In my xaml I bind a ObservableCollection<City> Cities to it
and the SelectedItem is SelectedCity
and sometimes when the mouse is over a item, I cannot select it
My ListBox looks like this:
<ListBox ItemsSource="{Binding Model.Cities}" SelectedItem="{Binding Model.SelectedCity}" HorizontalAlignment="Left" Height="468" Margin="10,136,0,0" VerticalAlignment="Top" Width="877">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="150" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Name}" />
<TextBlock Grid.Column="1" Text="{Binding Plz}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
and in My Model the code looks like this:
class CitiesModel: MyObservableCollection<City>
{
public ObservableCollection<City> Cities
{
get
{
return _cities;
}
}
private ObservableCollection<City> _cities;
private City _selectedCity;
public City SelectedCity
{
get
{
return _selectedCity;
}
set
{
_selectedCity = value;
RaisePropertyChanged("SelectedCity");
}
}
Can someone explain, why i sometimes cannot select an item?
You mean once you select one, you cannot select a different one.
Your problem is that if you have in the setter.
Remove it.
if(_selectedCity != value)
{
City is an object.
If you compare an object to another then unless you have provided an Equals method, they will be equal if of the same type.
Hence, once you select a City then any other City is equal.
Related
I would like to make a 2-columns table arrangement and to add buttons to the table at runtime.
what I did is defining nested StackPanels similar to this.
<StackPanel MinWidth="500" MaxWidth="800" MaxHeight="400" HorizontalAlignment="Center">
<TextBlock HorizontalAlignment="Center" Foreground="Black" Margin="0,0,0,5" FontSize="20">Some Title</TextBlock>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button Content="" MinWidth="100" MinHeight="100" Margin="10,0,0,10"></Button>
<Button Content="" MinWidth="100" MinHeight="100" Margin="10,0,0,10"></Button>
</StackPanel>
</StackPanel>
Is this a correct starting, or there is a better and easier arrangement?
You could use a model-view implementation with ListView and bind it to a collection of items which have the respective handler for the button:
WPF:
<ListView ItemsSource={Binding MyItems}>
<ListView.View>
<GridView>
<GridViewColumn Header="Name" DisplayMemberBinding={Binding Name}></GridViewColumn>
<GridViewColumn Header="Button">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Command={Binding ButtonPress}>Click me</Button>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
You can also set the ItemsSource inside the .cs file for your view as well, otherwise use a ViewModel class to handle your view and create a ObservableCollection<MyItemWrapper> property which will hold all the table items.
ViewModel:
public class MyViewModel
{
private ObservableCollection<MyItemWrapper> _myItems;
public MyViewModel()
{
_myItems = new ObservableCollection<MyItemWrapper>();
//// add your initial items
}
public ObservableCollection<MyItemWrapper> MyItems
{
get { return _myItems; }
}
}
View:
public partial class MyView : UserControl
{
public MyView(MyViewModel viewModel)
{
DataContext = viewModel;
InitializeComponents()
}
}
MyItem and MyItemWrapper
public class MyItem
{
public string Name { get; set; }
public object Data { get; set; }
}
public class MyItemWrapper
{
private MyItem _item;
public MyItemWrapper(MyItem item)
{
_item = item;
ButtonPress = new DelegateCommand<object>(OnButtonPress);
}
public string Name
{
get { return _item.Name; }
}
public DelegateCommand<object> ButtonPress { get; private set; }
private void OnButtonPress(object args)
{
System.Diagnostics.Debug.WriteLine("Button pressed for: " + Name);
}
}
This will ultimately be able to add/remove items at runtime by using MyItems inside the view model and have your list view always update automatically.
I got it working by using WrapPanel, and I will share what I have did, Thanks for Vlad for binding part:
<ListBox ItemsSource="{Binding CartItemC.CartItems}" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel x:Name="wrapPanel" MaxWidth="300" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<WrapPanel MaxWidth="300">
<Button Command="{Binding ButtonPress}">Click me</Button>
</WrapPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You mentioned you want it dynamically, well you don't want to use StackPanel as you would end up maintaining the width and height of the controls inside it. Given you want the controls to automatically resize its own size.
The best solution is to use Grid if you really want to make a 2 column table arrangement.
Take a look at this example.
Sample code
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="28" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="200" />
</Grid.ColumnDefinitions>
Excuse the wordy title, I'm having trouble with a succinct description. If I could come up with one, I could probably Google the right answer!
I am binding my DataGrid to an ObservableCollection of properties that themselves have properties. My grid is populated just fine, but when I edit the grid the changes are not getting back to my model.
I have an ObservableCollection
Normally, you'd just have some properties of MarriedCoupleRow, but I actually have something slightly more complicated. Each MarriedCoupleRow has some propties (Male, Female) which in turn expose properties (Height, Weight, Information). It's this Information that can be edited. Again, I can populate the grid just fine, but the setter property of Information is not hit when you edit the cell and tab off (or leave).
I'd appreciate any pointers or references, including how to better word my title!
Here's the simple code:
public class XMLDemoViewModel : ViewModelBase
{
public XMLDemoViewModel()
{
_rows = new ObservableCollection<MarriedCoupleRow>();
// create some data....
for (uint i = 0; i < 2;i++)
{
MarriedCoupleRow row = new MarriedCoupleRow();
row.Male = new HumanData();
row.Male.Height = (70 + i*5).ToString();
row.Male.Weight = 150+(i*30+1);
row.Male.Information = row.Male.Height + " " + row.Male.Weight;
row.Female = new HumanData();
row.Female.Height = (60 +i*3).ToString();
row.Female.Weight = 120+(i*10+5);
row.Female.Information = row.Female.Height + " " + row.Female.Weight;
_rows.Add(row);
}
}
#region Fields
private ObservableCollection<MarriedCoupleRow> _rows = null;
#endregion Fields
#region Properties
public ObservableCollection<MarriedCoupleRow> Rows
{
get
{
return _rows;
}
}
#endregion Properties
#region Commands
#endregion Commands
#region Private Methods
#endregion Private Methods
}
public class MarriedCoupleRow : ViewModelBase
{
private HumanData _Male = null;
public HumanData Male
{
get { return _Male; }
set
{
if (value != _Male)
{
_Male = value;
OnPropertyChanged("Male");
}
}
}
public HumanData Female { get; set; }
}
public class HumanData : INotifyPropertyChanged
{
public string Height { get; set; }
public uint Weight { get; set; }
private string _friendlyName;
public string Information
{
get
{
return _friendlyName;
}
set
{
if (_friendlyName != value)
{
_friendlyName = value;
OnPropertyChanged("Information");
}
}
}
}
And here's the XAML:
<Window x:Class="XMLDemo.Views.XMLDemoView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="XMLDemoView" Height="600" Width="1152">
<DockPanel>
<StackPanel Orientation="Horizontal" Height="120" DockPanel.Dock="Bottom">
<StackPanel Orientation="Horizontal">
<GroupBox Width="249" BorderThickness="2" Height="90">
<GroupBox.Header>
<TextBlock FontSize="12" FontWeight="Bold">Control</TextBlock>
</GroupBox.Header>
<Grid Height="64" Width="223">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*" />
<ColumnDefinition Width="0.5*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0.5*"/>
<RowDefinition Height="0.5*"/>
</Grid.RowDefinitions>
</Grid>
</GroupBox>
</StackPanel>
</StackPanel>
<DataGrid ItemsSource="{Binding Path=Rows, UpdateSourceTrigger=PropertyChanged}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.HeaderTemplate >
<DataTemplate>
<Grid ShowGridLines="True">
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Text="Male" />
</Grid>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBox Text="{Binding Male.Height}" IsEnabled="False" Grid.Row="0"></TextBox>
<TextBox Text="{Binding Male.Weight}" IsEnabled="False" Grid.Row="1"></TextBox>
<TextBox Text="{Binding Male.Information, Mode=TwoWay}" Grid.Row="2"></TextBox>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn>
<DataGridTemplateColumn.HeaderTemplate >
<DataTemplate>
<Grid ShowGridLines="True">
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Text="Female" />
</Grid>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid >
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBox Text="{Binding Female.Height}" IsEnabled="False" Grid.Row="0"></TextBox>
<TextBox Text="{Binding Female.Weight}" IsEnabled="False" Grid.Row="1"></TextBox>
<TextBox Text="{Binding Female.Information}" Grid.Row="2"></TextBox>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</DockPanel>
Your TextBox.Text bindings need to be set to updatesource on propertychanged.
<TextBox Text="{Binding Male.Information, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Grid.Row="2"></TextBox>
There must be something goofy going on with LostFocus which is the default. I tested this using your code.
I Like your post. As I seen from the coding you are using Complex property binding with DataGrid. Also complex property changes won't be reflect in DataGrid.
However you can achieve this requirement in sample level. I will try to make the sample and let you know.
Regards,
Riyaj Ahamed I
In a Tabbed interface application i'm trying to pass the TabItem Header string or the object contained in the selected TabItem to the view model to use it to
publish an event like this:
In the View (xaml):
<TabControl x:Name="MyTC"
prism:RegionManager.RegionName="{x:Static inf:RegionNames.MainRegion}"
SelectedItem="{Binding Path=TabControlSelectedItem,UpdateSourceTrigger=PropertyChanged,Mode=Twoway}"
Cursor="Hand"
Grid.Row="0"
Grid.Column="1">
<TabControl.ItemTemplate>
<DataTemplate>
<!--DataContext="{Binding ElementName=MyTC, Path=SelectedItem}"-->
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Margin="3"
Text="{Binding Path=DataContext.DataContext.HeaderInfo, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TabItem}}}"
/>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<i:InvokeCommandAction Command="{Binding HeaderClickCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
In View Model
//***********************************************************
//Constructor
public ShellWindowViewModel(IEventAggregator eventAggregator)
{
this.eventAggregator = eventAggregator;
this.HeaderClickCommand = new DelegateCommand(OnHeaderClick);
}
//SelectedItem Binding
private object tabControlSelectedItem;
public object TabControlSelectedItem
{
get { return tabControlSelectedItem; }
set
{
if (tabControlSelectedItem != value)
{
tabControlSelectedItem = value;
OnPropertyChanged("TabControlSelectedItem");
}
}
}
//*****************************************************************************************************
//this handler publish the Payload "SelectedSubsystem" for whoever subscribe to this event
private void OnHeaderClick()
{
//EA for communication between Modules not within Modules
string TabHeader = (TabControlSelectedItem as TabItem).Header.ToString();
eventAggregator.GetEvent<SubsystemIDSelectedEvent>().Publish(TabHeader);
}
but there is something wrong because when i click the TabItem nothing happen and when i inserted a breakpoint # TabControlSelectedItem
property i found it contain the view namespace.i want the TabControlSelectedItem to get the selected Tab header string or the object in
the selected tab item.
Your help is very much appreciated.
I just tried something similar and it works fine.
Here is my xaml
<TabControl ItemsSource="{Binding Matches}"
SelectedItem="{Binding SelectedRecipe}">
<TabControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="{Binding Path=Date}" />
<TextBlock Grid.Column="1"
TextAlignment="Center"
Text="{Binding Path=Name}" />
</Grid>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
And then in my viewmodel
public object SelectedRecipe
{
get
{
return _selectedRecipe;
}
set
{
_selectedRecipe = value;
OnNotifyPropertyChanged("SelectedRecipe");
}
}
I'm pretty new to XAML and to databinding, so hopefully this will be an easy question to someone out there...
I'm trying to build a TreeView using data binding that has a structure similar to this:
Category
Item Name
Item Name
Category
Item Name
So far, so good--I can do that. Next, I want to be able to expand the Item node and show details about that item. This works fine when I build the tree manually in the code-behind, but I want to use data binding instead. I ALMOST have this working, but as it is now, either I can't select the node OR I can't get the node to behave like a TreeViewItem where it shows just the item name when it is collapsed and can expand to show more--in my case a custom defined grid with data on it.
Here's a simplified sample of my XAML code:
<DataTemplate x:Key="ItemDataTemplate">
<TreeViewItem Header="{Binding Path=ItemName}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Price" />
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=Price}"/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="Description" />
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Path=Description}"/>
<TextBlock Grid.Row="2" Grid.Column="0" Text="Qty on Hand" />
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Path=Qty}"/>
</Grid>
</TreeViewItem>
</DataTemplate>
<HierarchicalDataTemplate x:Key="CategoryDataTemplate" ItemsSource="{Binding Path=Categories}" ItemTemplate="{StaticResource ItemDataTemplate}">
<TextBlock Text="{Binding Path=CategoryName}"/>
</HierarchicalDataTemplate>
Based on this simplified example, the object that was defined in the code-behind would be something like this:
public class Category
{
public ObservableCollection<Item> Items {get; private set;}
public string CategoryName { get; set; }
//constructor and methods here
}
public class Item
{
public string ItemName {get; private set;}
public decimal Price { get; private set; }
public string Description {get; private set; }
public int Qty {get; set;}
//constructor and methods here
}
After reading this question and answer on stackoverflow, I can see that you cannot have your template be a TreeViewItem if you want to select the node. I tried getting rid of the TreeViewItem tag and going straight to the Grid which allows me to select the node, but now I can't collapse the node down to just the ItemName (header text).
I feel like it's darn close... what am I missing?
So, what you want is to make an Item look like it has more child nodes, even though its only "child" is itself, presented with more info?
Well, the most correct way of doing that might be to use an expander in ItemDataTemplate, like so:
<DataTemplate x:Key="ItemDataTemplate">
<Expander Header="{Binding Path=ItemName}" >
<Grid>
<Grid.ColumnDefinitions>
...
<Grid.RowDefinitions>
...
<TextBlock Grid.Row="0" Grid.Column="0" Text="Price" />
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=Price}"/>
<TextBlock ...
</Grid>
</TreeViewItem>
</DataTemplate>
The expander doesn't look exactly like a standard TreeViewItem, but that can be fixed by making your own Template for the Expander.
Now, a slightly more hacky way of doing it would be to have a list of Items in the Item class, where each Item exposes itself as the only list element. Then, ItemDataTemplate would need to be a HierarchicalDataTemplate which referenced another, more detailed, "ItemDetailsTemlate":
public class Item
{
...
public List<Item> InfoWrapper
{
get { return new List<Item>() { this }; }
}
}
Xaml:
<DataTemplate x:Key="ItemDetailsTemplate">
<Grid>
<Grid.ColumnDefinitions>
...
<Grid.RowDefinitions>
...
<TextBlock Grid.Row="0" Grid.Column="0" Text="Price" />
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=Price}"/>
<TextBlock ...
</Grid>
</DataTemplate>
<HierarchicalDataTemplate x:Key="ItemDataTemplate" ItemsSource="{Binding Path=InfoWrapper}" ItemTemplate="{StaticResource ItemDetailsTemplate}" >
<TextBlock Text="{Binding Path=ItemName}"/>
</HierarchicalDataTemplate>
I am trying to work MVVM. I created a model with 3 properties (ModelClass), and in my view model i have an observable collection of ModelClasses. In the view i bind a list to the observable collection in my VM. the list is a template of 3 textboxes.
how can i bind the textbox to a specific property in my model?
say the model class is called Person and has name, age and country as properties, the view model has a collection of persons, the view has a list bound to the person list (itemssource..). i don't know how to make a textbox in list bound to name, age and country of the person model.
As per your query,i build the sample app and it's perfectly working for me.Details are furnished below.
PersonViewModel.cs
internal class PersonViewModel{
public ObservableCollection<PersonModel> PersonCollection { get; set; }
public PersonViewModel()
{
//This data will load as the default person from the model attached to the view
PersonCollection = new ObservableCollection<PersonModel>();
PersonCollection.Add(new PersonModel { Name = "John", Age= 24, Country = "Canada"});
PersonCollection.Add(new PersonModel { Name = "David", Age = 25, Country = "United States"});
PersonCollection.Add(new PersonModel { Name = "Prajin", Age = 28, Country = "Japan"});
}
}
PersonView.xaml
<Window x:Class="MVVM.PersonView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MVVM Demostration" Height="297" Width="480"
xmlns:local="clr-namespace:MVVM.ViewModel">
<Window.DataContext>
<local:PersonViewModel />
</Window.DataContext>
<Grid Margin="10">
<ListBox ItemsSource="{Binding PersonCollection}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Path=Name}" Margin="5" Grid.Column="0" FontSize="14" />
<TextBlock Text="{Binding Path=Age}" Margin="5" Grid.Column="1" FontSize="14" />
<TextBlock Text="{Binding Path=Country}" Margin="5" Grid.Column="2" FontSize="14"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Person.cs
internal class PersonModel : System.ComponentModel.INotifyPropertyChanged
{
private string name;
public string Name
{
get { return name; }
set
{
name = value;
OnPropertyChanged("Name");
}
}
private int age;
public int Age
{
get { return age; }
set
{
age = value;
OnPropertyChanged("Age");
}
}
private string country;
public string Country
{
get { return country; }
set
{
country = value;
OnPropertyChanged("Country");
}
}
#region INotifyPropertyChanged Members
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
#endregion
}
You need to change the ItemTemplate of the list to bind the the properties:
<ListView ItemsSource="{Binding PersonsList}" >
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" VerticalAlignment="Center" FontSize="14" />
<TextBlock Text="{Binding Path=Age}" VerticalAlignment="Center" FontSize="14" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Create a ListBox and bind your collection of objects to its ItemsSource. Here, Persons is the collection from your ViewModel.
<ListBox ItemsSource="{Binding Persons}" />
Then create a DataTemplate for your object type.
<DataTemplate DataType="{x:Type local:Person} >
<TextBox Text="{Binding Name}"/>
<TextBox Text="{Binding Age}"/>
etc...
</DataTemplate>
Where "local" is an xml namespace alias for the namespace containing the Person type.
Put this DataTemplate in your window's resources (Window.Resources).