I'm using the Xceed checkable combobox.
I need to set a few items as checked items:
<xctk:CheckComboBox x:Name="cbFileType" DisplayMemberPath="Name" SelectedMemberPath="IsChecked"></xctk:CheckComboBox>
C# code:
public partial class DataSelector : UserControl
{
public class BoolStringClass
{
public string Name { get; set; }
public bool IsChecked { get; set; }
}
public BackupDataSelector()
{
InitializeComponent();
cbFileType.Items.Add(new BoolStringClass { Name = ".jpg", IsChecked = true });
cbFileType.Items.Add(new BoolStringClass { Name = ".bmp", IsChecked = false });
}
}
But ".jpg" item is not checked:
How can I set ".jpg" as checked item?
You should add the items to be selected to the SelectedItems collection of the CheckComboBox:
cbFileType.Items.Add(new BoolStringClass { Name = ".jpg", IsChecked = true });
cbFileType.Items.Add(new BoolStringClass { Name = ".bmp", IsChecked = false });
foreach (var selectedItem in cbFileType.Items.OfType<BoolStringClass>().Where(x => x.IsChecked))
cbFileType.SelectedItems.Add(selectedItem);
The solution from user mm8 works, but that not the MVVM way...
From the docs:
https://xceed.com/wp-content/documentation/xceed-toolkit-plus-for-wpf/Xceed.Wpf.Toolkit~Xceed.Wpf.Toolkit.CheckComboBox.html
And
https://xceed.com/wp-content/documentation/xceed-toolkit-plus-for-wpf/Xceed.Wpf.Toolkit~Xceed.Wpf.Toolkit.Primitives.Selector~SelectedMemberPath.html
You'll need an ObservableCollection and a class holding the IsChecked and Item properties that fire the notifychanged event.
Below the working example: When you click the Set A and C button the items A and C will be checked as expected.
MainWindow.xaml.cs
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows;
namespace WpfApp6
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private ViewModel viewModel;
public MainWindow()
{
InitializeComponent();
viewModel = new ViewModel();
foreach (var item in new string[] { "A", "B", "C", "D"})
{
viewModel.CheckComboBoxItems.Add(new CheckComboBoxItems { IsChecked = false, Item = item});
}
DataContext = viewModel;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
foreach (var checkBoxToSet in viewModel.CheckComboBoxItems)
{
if (checkBoxToSet.Item.Equals("A") || checkBoxToSet.Item.Equals("C"))
{
checkBoxToSet.IsChecked = true;
}
}
}
}
public class ViewModel
{
public ObservableCollection<CheckComboBoxItems> CheckComboBoxItems { get; set; } = new ObservableCollection<CheckComboBoxItems>();
}
public class CheckComboBoxItems : INotifyPropertyChanged
{
private bool _isChecked;
private string _item;
public bool IsChecked
{
get
{
return _isChecked;
}
set
{
_isChecked = value;
NotifyPropertyChanged("IsChecked");
}
}
public string Item
{
get
{
return _item;
}
set
{
_item = value;
NotifyPropertyChanged("Item");
}
}
private void NotifyPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
MainWindow.xaml
<Window x:Class="WpfApp6.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp6"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<StackPanel>
<xctk:CheckComboBox ItemsSource="{Binding CheckComboBoxItems}" DisplayMemberPath="Item" SelectedMemberPath="IsChecked" Height="50" Width="150"/>
<Button Content="Set A und C" Width="150" Height="50" Margin="20" Click="Button_Click"/>
</StackPanel>
</Window>
Related
This question already has answers here:
INotifyPropertyChanged WPF
(3 answers)
Closed 1 year ago.
I know there is a lot of questions that can look like this, but i don't realy find anyone answer to my problem here and in another forums.
So, I'm relatively new to WPF and I'm testing data binding, but I'm getting a trouble that data don't get values updated when values of a ObservableCollection get changed.
I will put my exemple were.
Main.xaml
<Window x:Class="TestWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestWPF"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid x:Name="MyGrid">
</Grid>
</Window>
Main.xaml.cs
using System.Windows;
namespace TestWPF
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Stand stand = new Stand("Best Seller Stand");
stand.cars.Add(new Car()
{
ID = "1",
Brand = "BMW",
CarNumber = 165,
HaveRadio = true
});
stand.cars.Add(new Car()
{
ID = "2",
Brand = "Toyota",
CarNumber = 421,
HaveRadio = true
});
stand.cars.Add(new Car()
{
ID = "4",
Brand = "FIAT",
CarNumber = 312,
HaveRadio = false
});
stand.cars.Add(new Car()
{
ID = "3",
Brand = "Ferrari",
CarNumber = 12,
HaveRadio = true
});
MyGrid.Children.Add(stand.GetCatalog());
}
}
}
Car.cs
using System;
namespace TestWPF
{
public class Car : IComparable, IComparable<int>
{
public string ID { get; set; }
public string Brand { get; set; }
public int CarNumber { get; set; }
public bool HaveRadio { get; set; }
public void GerateRandomCarNumber()
{
CarNumber = new Random().Next(int.MinValue, int.MaxValue);
}
public int CompareTo(int other)
{
return CarNumber.CompareTo(other);
}
public int CompareTo(object obj)
{
Car other = null;
if (obj is Car)
other = obj as Car;
return CarNumber.CompareTo(other.CarNumber);
}
}
}
Stand.cs
using System.Collections.Generic;
using System.Linq;
namespace TestWPF
{
public class Stand
{
public Stand(string name)
{
Name = name;
}
public string Name { get; set; }
public SortedSet<Car> cars { get; set; } = new SortedSet<Car>();
public Car BestChoice
{
get
{
return cars.First();
}
}
public StandCatalog Catalog { get; set; } = null;
public StandCatalog GetCatalog()
{
if (Catalog == null)
Catalog = new StandCatalog(this);
return Catalog;
}
}
}
StandCatalog.xaml (UserControl)
<UserControl x:Class="TestWPF.StandCatalog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:TestWPF"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<StackPanel Orientation="Vertical">
<Label Name="StandName" Content="{Binding Model.Name}" Margin="10"/>
<Label Name="CarBrand" Content="{Binding Model.BestChoice.Brand}" Margin="10"/>
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding CatalogCar}">
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Binding="{Binding ID}"/>
<DataGridTextColumn Header="Brand" Binding="{Binding Brand}"/>
<DataGridTextColumn Header="Car Number" Binding="{Binding CarNumber}"/>
<DataGridCheckBoxColumn Header="Have Radio" Binding="{Binding HaveRadio}"/>
</DataGrid.Columns>
</DataGrid>
<Button Content="Gerate Random Number" Click="btn_GerateRandomNumber"/>
</StackPanel>
</UserControl>
StandCatalog.xaml.cs
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
namespace TestWPF
{
/// <summary>
/// Interaction logic for StandCatalog.xaml
/// </summary>
public partial class StandCatalog : UserControl
{
public Stand Model { get; init; }
public ObservableCollection<Car> CatalogCar { get; set; }
public StandCatalog()
{
InitializeComponent();
this.DataContext = this;
}
public StandCatalog(Stand model) : this()
{
Model = model;
CatalogCar = new ObservableCollection<Car>(Model.cars);
}
private void btn_GerateRandomNumber(object sender, RoutedEventArgs e)
{
foreach (var item in Model.cars)
{
item.GerateRandomCarNumber();
}
}
}
}
So I get this aplication:
But when I click on the button to gerate random number, the datagrid don't refresh and the label (Name="CarBrand") don't change either...
Doesn't data binding refresh the UI when the elements changed its value?
I know that the value changed because when i reorder the datagrid I get this:
Can anyone help me?
Another question, I'm using the class Stand as a Model of the StandCatalog (view/controller), what is the best way to use the SortedSet and the ObservableCollection together? Or should I use a SortedSet in the model?
Option 1: INotifyPropertyChanged
The Car class should implement the INotifyPropertyChanged interface to inform targets when a property changes.
public class Car : IComparable, IComparable<int>, INotifyPropertyChanged
{
private int _carNumber;
public string ID { get; set; }
public string Brand { get; set; }
public int CarNumber
{
get => _carNumber;
set
{
if (_carNumber == value) return;
_carNumber = value;
OnPropertyChanged();
}
}
public bool HaveRadio { get; set; }
public void GerateRandomCarNumber() { CarNumber = new Random().Next(int.MinValue, int.MaxValue); }
public int CompareTo(int other) { return CarNumber.CompareTo(other); }
public int CompareTo(object obj)
{
Car other = null;
if (obj is Car)
other = obj as Car;
return CarNumber.CompareTo(other.CarNumber);
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); }
}
Option 2: DependencyProperty
Define CarNumber property as a DependencyProperty. The infrastructure will handle the changes.
public class Car : DependencyObject, IComparable, IComparable<int>
{
public static readonly DependencyProperty
CarNumberProperty = DependencyProperty.Register("CarNumber", typeof(int), typeof(Car));
public string ID { get; set; }
public string Brand { get; set; }
public int CarNumber
{
get => (int)GetValue(CarNumberProperty);
set => SetValue(CarNumberProperty, value);
}
public bool HaveRadio { get; set; }
public void GerateRandomCarNumber() { CarNumber = new Random().Next(int.MinValue, int.MaxValue); }
public int CompareTo(int other) { return CarNumber.CompareTo(other); }
public int CompareTo(object obj)
{
Car other = null;
if (obj is Car)
other = obj as Car;
return CarNumber.CompareTo(other.CarNumber);
}
}
I have an ItemTemplateSelector which contains Multiple DataTemplates which have Different DataTypes.
I thus have multiple ItemSources based on Module Selected.
How to bind my ListView with multiple ItemSources based on the module selected?
Explanation:
1)ViewModel_A is my ItemSource and DataTemplateA is my DataTemplate when my Module A is Selected
2)ViewModel_B is my ItemSource DataTemplateB is my DataTemplate when my Module B is Selected
I tried Implementing a BaseViewModel and tried binding the BaseViewModel Type in my ItemSource But this doesn't allow the access of derived class properties.
How to Dynamically Select My ItemSource?
Step 1
First Create a UserControl which contains your ListView in your Xaml and two DependancyProperty for ItemSource and DataTemplate
DataList.Xaml
<UserControl
x:Class="MultipleDataTemplate.DataList"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<Grid>
<ListView ItemsSource="{x:Bind ItemsSource,Mode=OneWay}"></ListView>
</Grid>
</UserControl>
DataList.xaml.cs
public sealed partial class DataList : UserControl
{
public DataList()
{
this.InitializeComponent();
}
#region ItemsSource
public object ItemsSource
{
get { return (object)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(nameof(ItemsSource), typeof(object), typeof(DataList), new PropertyMetadata(null));
#endregion
#region ItemTemplate
public DataTemplate ItemTemplate
{
get { return (DataTemplate)GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
public static readonly DependencyProperty ItemTemplateProperty = DependencyProperty.Register(nameof(ItemTemplate), typeof(DataTemplate), typeof(DataList), new PropertyMetadata(null));
#endregion
}
Step 2
Now you can you this usercontrol with any multiple DataTemplate's and multiple itemsource as below
MainPage.xaml
<Page
x:Class="MultipleDataTemplate.Cars"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:MultipleDataTemplate">
<Page.Resources>
<DataTemplate x:Key="CarKey" x:DataType="controls:Car">
<Grid>
<TextBlock Text="{x:Bind carprop1}"></TextBlock>
<TextBlock Text="{x:Bind carprop2}"></TextBlock>
</Grid>
</DataTemplate>
<DataTemplate x:Key="BikeKey" x:DataType="controls:Bike">
<Grid>
<TextBlock Text="{x:Bind Bikeprop1}"></TextBlock>
<TextBlock Text="{x:Bind Bikeprop2}"></TextBlock>
</Grid>
</DataTemplate>
</Page.Resources>
<Grid>
<controls:DataList ItemsSource="{x:Bind ItemSource,Mode=OneWay}" ItemTemplate="{x:Bind ItemTemplate}"></controls:DataList>
<StackPanel>
<Button Content="Cars" Click="CarsClick"/>
<Button Content="Bike" Click="BikeClick"/>
</StackPanel>
</Grid>
</Page>
MainPage.xaml.cs
public sealed partial class Cars : Page, INotifyPropertyChanged
{
public object _ItemSource { get; set; }
public object ItemSource
{
get { return _ItemSource; }
set
{
_ItemSource = value;
this.OnPropertyChanged();
}
}
public DataTemplate _itemTemplate { get; set; }
public DataTemplate ItemTemplate
{
get { return _itemTemplate; }
set
{
_itemTemplate = value;
this.OnPropertyChanged();
}
}
public Cars()
{
this.InitializeComponent();
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
private void CarsClick(object sender, RoutedEventArgs e)
{
ItemSource = new List<Car>() { new Car() { carprop1 = "1", carprop2 = "2" } };
ItemTemplate = this.Resources["CarKey"] as DataTemplate;
}
private void BikeClick(object sender, RoutedEventArgs e)
{
ItemSource = new List<Bike>() { new Bike() { Bikeprop1 = "1", Bikeprop2 = "2" } };
ItemTemplate = this.Resources["BikeKey"] as DataTemplate;
}
}
public class Car
{
public string carprop1 { get; set; }
public string carprop2 { get; set; }
}
public class Bike
{
public string Bikeprop1 { get; set; }
public string Bikeprop2 { get; set; }
}
i have a class
class Names {
public int id get; set;};
public string name {get ; set};
public string til {set{
if (this.name == "me"){
return "This is me";
}
}
i have a list (ListNames) which contains Names added to it and binding with a combo box
<ComboBox SelectedValue="{Binding Path=id, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding
ListNames}" DisplayMemberPath="name" SelectedValuePath="id" />
every thing works
i want to display the "Tip" on another label field when user selects an item.
is it possible?
help!
I'm assuming you're using MVVM.
You need to create in your window's viewmodel, a property "CurrentName" of type "Names", binded to the ComboBox SelectedItem property.
This property must raise the NotifyPropertyChanged event.
Then, bind your label field, to this "CurrentName" property.
When the SelectedIem property will change on the combobox, your label field will then be updated.
Something like this:
Your Model:
public class Names
{
public int Id { get; set; }
public string Name { get; set; }
public string Tip {
get
{
return Name == "me" ? "this is me" : "";
}
}
}
Your ViewModel:
public class ViewModel : INotifyPropertyChanged
{
private ObservableCollection<Names> _namesList;
private Names _selectedName;
public ViewModel()
{
NamesList = new ObservableCollection<Names>(new List<Names>
{
new Names() {Id = 1, Name = "John"},
new Names() {Id = 2, Name = "Mary"}
});
}
public ObservableCollection<Names> NamesList
{
get { return _namesList; }
set
{
_namesList = value;
OnPropertyChanged();
}
}
public Names SelectedName
{
get { return _selectedName; }
set
{
_selectedName = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
And finally your View:
<Window x:Class="Combo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Combo="clr-namespace:Combo"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<Combo:ViewModel/>
</Window.DataContext>
<StackPanel>
<ComboBox ItemsSource="{Binding Path=NamesList}" DisplayMemberPath="Name"
SelectedValue="{Binding Path=SelectedName}"/>
<Label Content="{Binding Path=SelectedName.Name}"/>
</StackPanel>
I have ViewModel(implemented INotifyPropertyChanged) in the background and class Category which has only one property of type string. My ComboBox SelectedItem is bind to an instance of a Category. When i change the value of instance, SelectedItem is not being updated and Combobox is not changed.
EDIT: code
Combobox:
<ComboBox x:Name="categoryComboBox" Grid.Column="1" Grid.Row="3" Grid.ColumnSpan="2"
Margin="10" ItemsSource="{Binding Categories}"
DisplayMemberPath="Name" SelectedValue="{Binding NodeCategory, Mode=TwoWay}"/>
Property:
private Category _NodeCategory;
public Category NodeCategory
{
get
{
return _NodeCategory;
}
set
{
_NodeCategory = value;
OnPropertyChanged("NodeCategory");
}
}
[Serializable]
public class Category : INotifyPropertyChanged
{
private string _Name;
[XmlAttribute("Name")]
public string Name
{
get
{
return _Name;
}
set
{
_Name = value;
OnPropertyChanged("Name");
}
}
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
[field:NonSerialized]
public event PropertyChangedEventHandler PropertyChanged;
}
and what I am trying is: when I set
NodeCategory = some_list_of_other_objects.Category;
to have that item selected in Combobox with appropriate DisplayMemberPath
The category you are setting in this line -
NodeCategory = some_list_of_other_objects.Category;
and one present in your Categories collection(ItemsSource="{Binding Categories}") should be referring to same object. If they are not then SelectedItem won't work.
Solution 1 -
You can also try to use SelectedValuePath like this -
<ComboBox x:Name="categoryComboBox"
ItemsSource="{Binding Categories}"
DisplayMemberPath="Name"
SelectedValuePath="Name"
SelectedValue="{Binding NodeCategory, Mode=TwoWay}" />
and in code you can do something like this -
private string _NodeCategory;
public string NodeCategory
{
get
{
return _NodeCategory;
}
set
{
_NodeCategory = value;
OnPropertyChanged("NodeCategory");
}
}
and set selected item like this -
NodeCategory = some_list_of_other_objects.Category.Name;
and use selected value like this -
Category selectedCategory =
some_list_of_other_objects.FirstOrDefault(cat=> cat.Name == NodeCategory);
or
Category selectedCategory =
Categories.FirstOrDefault(cat=> cat.Name == NodeCategory);
Solution 2 -
Another possible solution can be -
NodeCategory =
Categories.FirstOrDefault(cat=> cat.Name == some_list_of_other_objects.Category.Name);
this way your NodeCategory property will have the reference of an object in Categories collection and SelectedItem will work.
Your XAML needs a couple of modifications but I think the real problem is with the code you have posted which I don't think is telling the full story.
For starters, your combobox ItemSource is bound to a property called Categories but you do not show how this property is coded or how your NodeCategory property is initially synced with the item.
Try using the following code and you will see that the selected item is kept in sync as the user changes the value in the combobox.
XAML
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<ComboBox x:Name="categoryComboBox"
Grid.Column="1"
Grid.Row="3"
Grid.ColumnSpan="2"
Margin="10"
ItemsSource="{Binding Categories}"
DisplayMemberPath="Name"
SelectedItem="{Binding NodeCategory}" />
<Label Content="{Binding NodeCategory.Name}" />
</StackPanel>
Code-behind
public partial class MainWindow : Window, INotifyPropertyChanged
{
private ObservableCollection<Category> _categories = new ObservableCollection<Category>
{
new Category { Name = "Squares"},
new Category { Name = "Triangles"},
new Category { Name = "Circles"},
};
public MainWindow()
{
InitializeComponent();
NodeCategory = _categories.First();
this.DataContext = this;
}
public IEnumerable<Category> Categories
{
get { return _categories; }
}
private Category _NodeCategory;
public Category NodeCategory
{
get
{
return _NodeCategory;
}
set
{
_NodeCategory = value;
OnPropertyChanged("NodeCategory");
}
}
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
[Serializable]
public class Category : INotifyPropertyChanged
{
private string _Name;
[XmlAttribute("Name")]
public string Name
{
get
{
return _Name;
}
set
{
_Name = value;
OnPropertyChanged("Name");
}
}
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
[field: NonSerialized]
public event PropertyChangedEventHandler PropertyChanged;
}
From my little example:
Note: This is setting just a string (or a category from another list), but the basics should be same here:
Basically this is done:
private void button1_Click(object sender, RoutedEventArgs e)
{
(this.DataContext as ComboBoxSampleViewModel).SelectCategory("Categorie 4");
}
Here is my XAML:
<Grid>
<ComboBox Height="23" HorizontalAlignment="Left" Margin="76,59,0,0"
Name="comboBox1" VerticalAlignment="Top" Width="120"
ItemsSource="{Binding List.Categories}"
DisplayMemberPath="Name"
SelectedValue="{Binding NodeCategory, Mode=TwoWay}" />
<Button Content="Button" Height="27" HorizontalAlignment="Left"
Margin="76,110,0,0" Name="button1" VerticalAlignment="Top"
Width="120" Click="button1_Click" />
</Grid>
and in the ViewModel of the Window
class ComboBoxSampleViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public CategoryList List { get; set; }
public ComboBoxSampleViewModel()
{
this.List = new CategoryList();
NodeCategory = List.Selected;
}
private ComboBoxSampleItemViewModel nodeCategory;
public ComboBoxSampleItemViewModel NodeCategory
{
get
{
return nodeCategory;
}
set
{
nodeCategory = value;
NotifyPropertyChanged("NodeCategory");
}
}
internal void SelectCategory(string p)
{
this.List.SelectByName(p);
this.NodeCategory = this.List.Selected;
}
}
With the help of this little class:
public class CategoryList
{
public ObservableCollection<ComboBoxSampleItemViewModel> Categories { get; set; }
public ComboBoxSampleItemViewModel Selected { get; set; }
public CategoryList()
{
Categories = new ObservableCollection<ComboBoxSampleItemViewModel>();
var cat1 = new ComboBoxSampleItemViewModel() { Name = "Categorie 1" };
var cat2 = new ComboBoxSampleItemViewModel() { Name = "Categorie 2" };
var cat3 = new ComboBoxSampleItemViewModel() { Name = "Categorie 3" };
var cat4 = new ComboBoxSampleItemViewModel() { Name = "Categorie 4" };
Categories.Add(cat1);
Categories.Add(cat2);
Categories.Add(cat3);
Categories.Add(cat4);
this.Selected = cat3;
}
internal void SelectByName(string p)
{
this.Selected = this.Categories.Where(s => s.Name.Equals(p)).FirstOrDefault();
}
}
And this Item ViewModel
public class ComboBoxSampleItemViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
private string name;
public string Name
{
get
{
return name;
}
set
{
name = value;
NotifyPropertyChanged("Name");
}
}
}
If Combobox is bound to object class of the View Model, while the SelectionBoxItem of the sender object (in the SelectionChanged in code behind) has not that type, it means it's still loading.
ComboBox combo = sender as ComboBox;
if (combo.SelectionBoxItem.GetType() == typeof(BindedClass))
{
// Not loading
}
What I'm trying to do is create a UserControl to which I can pass an Address object. It seems that when I pass Address="{Binding Path=Person.Address}" to the UserControl, the embedded TextBox is binding to Text="{Binding Path=Person.Address}" instead of Text="{Binding Path=Address.Summary}"
Am I going about this all wrong?
Here's a link to the project if you want to play with it: http://dl.dropbox.com/u/4220513/WpfApplication2.zip
Domain objects:
namespace WpfApplication2
{
public class Person
{
public String Name { get; set; }
public Address Address { get; set; }
}
public class Address
{
public String Street { get; set; }
public String City { get; set; }
public String Summary { get { return String.Format("{0}, {1}", Street, City); } }
}
}
MainWindow:
namespace WpfApplication2
{
public partial class MainWindow : Window
{
private readonly ViewModel vm;
public MainWindow()
{
InitializeComponent();
vm = new ViewModel();
DataContext = vm;
vm.Person = new Person()
{
Name = "Bob",
Address = new Address()
{
Street = "123 Main Street",
City = "Toronto",
},
};
}
}
public class ViewModel : INotifyPropertyChanged
{
private Person person;
public Person Person { get { return person; } set { person = value; NotifyPropertyChanged("Person"); } }
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication2"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<TextBlock Text="Name:" />
<TextBlock Text="{Binding Path=Person.Name}" />
<TextBlock Text="Address:" />
<local:AddressView Address="{Binding Path=Person.Address}" />
</StackPanel>
</Window>
UserControl:
namespace WpfApplication2
{
public partial class AddressView : UserControl
{
public AddressView()
{
InitializeComponent();
DataContext = this;
}
public Address Address
{
get { return (Address)GetValue(AddressProperty); }
set { SetValue(AddressProperty, value); }
}
public static readonly DependencyProperty AddressProperty =
DependencyProperty.Register("Address", typeof(Address), typeof(AddressView));
}
}
<UserControl x:Class="WpfApplication2.AddressView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TextBox Text="{Binding Path=Address.Summary}" IsReadOnly="True" />
</UserControl>
Error:
System.Windows.Data Error: 40 : BindingExpression path error: 'Person' property not found on 'object' ''AddressView' (Name='')'. BindingExpression:Path=Person.Address; DataItem='AddressView' (Name=''); target element is 'AddressView' (Name=''); target property is 'Address' (type 'Address')
In MainWindow.xaml :
<local:AddressView DataContext="{Binding Path=Person.Address}" />
and then in AddressView.xaml
<TextBox Text="{Binding Path=Summary, Mode=OneWay}" IsReadOnly="True" />
This displays the summary for me.