I have a problem with the SelectedItem in my ComboBox.
<ComboBox Name="cbxSalesPeriods"
ItemsSource="{Binding SalesPeriods}"
DisplayMemberPath="displayPeriod"
SelectedItem="{Binding SelectedSalesPeriod}"
SelectedValuePath="displayPeriod"
IsSynchronizedWithCurrentItem="True"/>
If I open the ComboBox, I see the values.
If I select an item, the selected Item won't be shown.
Has anybody an idea?
In my ViewModel I have these two properties:
public ObservableCollection<SalesPeriodVM> SalesPeriods { get; private set; }
private SalesPeriodVM selectedSalesPeriod;
public SalesPeriodVM SelectedSalesPeriod
{
get { return selectedSalesPeriod; }
set
{
if (selectedSalesPeriod != value)
{
selectedSalesPeriod = value;
RaisePropertyChanged("SelectedSalesPeriod");
}
}
}
These are a few properties from the class :
public SalesPeriodVO Vo
{
get { return period; }
}
public int Year
{
get { return period.Year; }
set
{
if (period.Year != value)
{
period.Year = value;
RaisePropertyChanged("Year");
}
}
}
public int Month
{
get { return period.Month; }
set
{
if (period.Month != value)
{
period.Month = value;
RaisePropertyChanged("Month");
}
}
}
public string displayPeriod {
get
{
return this.ToString();
}
}
public override string ToString()
{
return String.Format("{0:D2}.{1}", Month, Year);
}
EDIT:
The Following happens If I remove the Property DisplayMemberPath:
You seem to be unnecessarily setting properties on your ComboBox. You can remove the DisplayMemberPath and SelectedValuePath properties which have different uses. It might be an idea for you to take a look at the Difference between SelectedItem, SelectedValue and SelectedValuePath post here for an explanation of these properties. Try this:
<ComboBox Name="cbxSalesPeriods"
ItemsSource="{Binding SalesPeriods}"
SelectedItem="{Binding SelectedSalesPeriod}"
IsSynchronizedWithCurrentItem="True"/>
Furthermore, it is pointless using your displayPeriod property, as the WPF Framework would call the ToString method automatically for objects that it needs to display that don't have a DataTemplate set up for them explicitly.
UPDATE >>>
As I can't see all of your code, I cannot tell you what you are doing wrong. Instead, all I can do is to provide you with a complete working example of how to achieve what you want. I've removed the pointless displayPeriod property and also your SalesPeriodVO property from your class as I know nothing about it... maybe that is the cause of your problem??. Try this:
public class SalesPeriodV
{
private int month, year;
public int Year
{
get { return year; }
set
{
if (year != value)
{
year = value;
NotifyPropertyChanged("Year");
}
}
}
public int Month
{
get { return month; }
set
{
if (month != value)
{
month = value;
NotifyPropertyChanged("Month");
}
}
}
public override string ToString()
{
return String.Format("{0:D2}.{1}", Month, Year);
}
public virtual event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged(params string[] propertyNames)
{
if (PropertyChanged != null)
{
foreach (string propertyName in propertyNames) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
PropertyChanged(this, new PropertyChangedEventArgs("HasError"));
}
}
}
Then I added two properties into the view model:
private ObservableCollection<SalesPeriodV> salesPeriods = new ObservableCollection<SalesPeriodV>();
public ObservableCollection<SalesPeriodV> SalesPeriods
{
get { return salesPeriods; }
set { salesPeriods = value; NotifyPropertyChanged("SalesPeriods"); }
}
private SalesPeriodV selectedItem = new SalesPeriodV();
public SalesPeriodV SelectedItem
{
get { return selectedItem; }
set { selectedItem = value; NotifyPropertyChanged("SelectedItem"); }
}
Then initialised the collection with your values:
SalesPeriods.Add(new SalesPeriodV() { Month = 3, Year = 2013 } );
SalesPeriods.Add(new SalesPeriodV() { Month = 4, Year = 2013 } );
And then data bound only these two properties to a ComboBox:
<ComboBox ItemsSource="{Binding SalesPeriods}" SelectedItem="{Binding SelectedItem}" />
That's it... that's all you need for a perfectly working example. You should see that the display of the items comes from the ToString method without your displayPeriod property. Hopefully, you can work out your mistakes from this code example.
I had a similar problem where the SelectedItem-binding did not update when I selected something in the combobox. My problem was that I had to set UpdateSourceTrigger=PropertyChanged for the binding.
<ComboBox ItemsSource="{Binding SalesPeriods}"
SelectedItem="{Binding SelectedItem, UpdateSourceTrigger=PropertyChanged}" />
<!-- xaml code-->
<Grid>
<ComboBox Name="cmbData" SelectedItem="{Binding SelectedstudentInfo, Mode=OneWayToSource}" HorizontalAlignment="Left" Margin="225,150,0,0" VerticalAlignment="Top" Width="120" DisplayMemberPath="name" SelectedValuePath="id" SelectedIndex="0" />
<Button VerticalAlignment="Center" Margin="0,0,150,0" Height="40" Width="70" Click="Button_Click">OK</Button>
</Grid>
//student Class
public class Student
{
public int Id { set; get; }
public string name { set; get; }
}
//set 2 properties in MainWindow.xaml.cs Class
public ObservableCollection<Student> studentInfo { set; get; }
public Student SelectedstudentInfo { set; get; }
//MainWindow.xaml.cs Constructor
public MainWindow()
{
InitializeComponent();
bindCombo();
this.DataContext = this;
cmbData.ItemsSource = studentInfo;
}
//method to bind cobobox or you can fetch data from database in MainWindow.xaml.cs
public void bindCombo()
{
ObservableCollection<Student> studentList = new ObservableCollection<Student>();
studentList.Add(new Student { Id=0 ,name="==Select=="});
studentList.Add(new Student { Id = 1, name = "zoyeb" });
studentList.Add(new Student { Id = 2, name = "siddiq" });
studentList.Add(new Student { Id = 3, name = "James" });
studentInfo=studentList;
}
//button click to get selected student MainWindow.xaml.cs
private void Button_Click(object sender, RoutedEventArgs e)
{
Student student = SelectedstudentInfo;
if(student.Id ==0)
{
MessageBox.Show("select name from dropdown");
}
else
{
MessageBox.Show("Name :"+student.name + "Id :"+student.Id);
}
}
Related
I have an ObservableCollection<UsageItem> MyItems; :
EDIT :
public class UsageItem : INotifyPropertyChanged
{
string _Name;
public string Name
{
get
{
return _Name;
}
set
{
if (_Name != value)
{
_Name = value;
RaisePropertyChanged("Name");
}
}
}
int _ChargesLeft;
public int ChargesLeft
{
get
{
return _ChargesLeft;
}
protected set
{
if (_ChargesLeft != value)
{
_ChargesLeft = value;
RaisePropertyChanged("ChargesLeft");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string prop)
{
if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(prop)); }
}
public Consummable(string name, int charges)
{
this.Name = name;
this.ChargesLeft = charges;
}
public override string DisplayName
{
get
{
if (ChargesLeft > 1)
{
return Name + " " + ChargesLeft + "charges";
}
else
{
return Name + " " + ChargesLeft + "charge";
}
}
}
public override void use(Hero hero)
{
if(this.ChargesLeft >= 1)
{
this.ChargesLeft--;
}else{
//Will remove this UsageItem from my ObservableCollection
...
}
}
}
I've bound my Collection to a ListBox :
<ListBox x:Name="listBoxBackPack" ItemsSource="{Binding MyItems}" SelectedItem="{Binding SelectedItem}" DisplayMemberPath="DisplayName"/>
I have a button that when clicked call the use function on my SelectedItem, it will decrease the value my ChargesLeft Field or remove the object.
If I click the button while my ChargesLeft value is <= 1 it will remove my Item from my Collection, and my ListBox will be updated as expected, no problem.
But If I use an item with a ChargesLeft >= 1, then I will decrease my field and so in consequence change my DisplayName.
As I still change my Collection I would expect a CollectionChanged Event to fire and my ListBox to be refreshed and display the new DisplayValue of the item, but it does not happen, the ListBox keep displaying the old DisplayValue.
At this moment if I manually do a Listbox.Items.Refresh() (Binded it on a button for testing) it will refresh and correctly display the right DisplayValue, but I can't call this function from my ViewModel, as it has no knowledge of my ListBox
How can I do to automatically force the refresh of my ListBox when I modify an item ?
CollectionChanged is raised when you add or delete an item or clear the whole collection. Your UsageItem class needs to implement INotifyPropertyChanged itself to track changes in collection items.
https://msdn.microsoft.com/en-us/library/ms668604(v=vs.110).aspx
Note
To fully support transferring data values from binding source objects to binding targets, each object in your collection that supports bindable properties must implement an appropriate property changed notification mechanism such as the INotifyPropertyChanged interface.
Update
See a working example (I used Prism but that doesn't matter):
UsageItem.cs:
public class UsageItem : INotifyPropertyChanged
{
string _Name;
public string Name
{
get
{
return _Name;
}
set
{
if (_Name != value)
{
_Name = value;
RaisePropertyChanged("Name");
RaisePropertyChanged("DisplayName");
}
}
}
int _ChargesLeft;
public int ChargesLeft
{
get
{
return _ChargesLeft;
}
set
{
if (_ChargesLeft != value)
{
_ChargesLeft = value;
RaisePropertyChanged("ChargesLeft");
RaisePropertyChanged("DisplayName");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string prop)
{
if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(prop)); }
}
public UsageItem(string name, int charges)
{
this.Name = name;
this.ChargesLeft = charges;
}
public string DisplayName
{
get
{
if (ChargesLeft > 1)
{
return Name + " " + ChargesLeft + "charges";
}
else
{
return Name + " " + ChargesLeft + "charge";
}
}
}
}
MainViewModel:
public class MainWindowViewModel : BindableBase
{
private string _title = "Prism Unity Application";
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
public MainWindowViewModel()
{
Items = new ObservableCollection<UsageItem>();
Items.Add(new UsageItem("Bob", 1));
Items.Add(new UsageItem("Bill", 2));
Items.Add(new UsageItem("Joe", 3));
CurrentItem = Items[0];
UseCommand = new DelegateCommand(Use);
}
public ObservableCollection<UsageItem> Items { get; private set; }
private UsageItem _currentItem;
public UsageItem CurrentItem
{
get { return _currentItem; }
set { SetProperty(ref _currentItem, value); }
}
public DelegateCommand UseCommand { get; private set; }
private void Use()
{
if (CurrentItem.ChargesLeft >= 1)
{
CurrentItem.ChargesLeft--;
}
else
{
//Will remove this UsageItem from my ObservableCollection
Items.Remove(CurrentItem);
CurrentItem = Items[0];
}
}
}
MainWindow.xaml:
<Window x:Class="UsageLeft.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
Title="{Binding Title}"
Width="525"
Height="350"
prism:ViewModelLocator.AutoWireViewModel="True">
<StackPanel>
<ListBox ItemsSource="{Binding Items}" SelectedItem="{Binding CurrentItem}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DisplayName}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Command="{Binding UseCommand}" Content="Use" />
</StackPanel>
</Window>
My ComboBox looks like this:
<ComboBox Name="cbMDLName" Style="{StaticResource ComoboBox}"
DisplayMemberPath="Name" ItemsSource="{Binding Path=MDLCollection}"
SelectionChanged="cbMDLName_SelectionChanged" Grid.Row="0" Grid.Column="1"/>
The DataContext is set from the code behind file.
this.DataContext = this.ManagerRoot.MDLM;
The ItemSource of the ComboBox is set to ObservableCollection<MDL> MDLCollection {get;set;}.
MDL.cs
public class MDL : INotifyPropertyChanged
{
public AdditionManager AdditionManager { get; set; }
private string _name;
public string Name
{
get { return this._name; }
set
{
this._name = value;
this.OnPropertyChanged("Name");
}
}
private int _index;
public int Index
{
get { return this._index; }
set
{
this._index = value;
}
}
}
Now I want to get the index from the selected entry. How can I do that?
You may try this.
int selectedValue = (int)cbMDLName.SelectedValue;
I am trying to bind my ViewModel to my ComboBox. I have ViewModel class defined like this:
class ViewModel
{
public ViewModel()
{
this.Car= "VW";
}
public string Car{ get; set; }
}
I set this ViewModel as DataContext in Window_Load like:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = new CarModel();
}
Then in my xaml, I do this to bind my ComboBox to this ViewModel. I want to show the "VW" as selected by default in my ComboBox:
<ComboBox Name="cbCar" SelectedItem="{Binding Car, UpdateSourceTrigger=PropertyChanged}">
<ComboBoxItem Tag="Mazda">Mazda</ComboBoxItem>
<ComboBoxItem Tag="VW">VW</ComboBoxItem>
<ComboBoxItem Tag="Audi">Audi</ComboBoxItem>
</ComboBox>
I have 2 questions:
How do I set default value selected in Combo Box to "VW" (once form loads, it should show "VW" in combo box).
Instead of setting ComboBoxItems like above in xaml, how to I set it in my ViewModel and then load these in ComboBox?
Thanks,
UPDATE:
So far, I manage to implement this but I get error as below in the ViewModel c-tor:
namespace MyData
{
class ViewModel
{
public ViewModel()
{
this.Make = "";
this.Year = 1;
this.DefaultCar = "VW"; //this is where I get error 'No string allowed'
}
public IEnumerable<Car> Cars
{
get
{
var cars = new Car[] { new Car{Model="Mazda"}, new Car{Model="VW"}, new Car{Model="Audi"} };
DefaultCar = cars.FirstOrDefault(car => car.Model == "VW");
}
}
public string Make { get; set; }
public int Year { get; set; }
public Car DefaultCar { get; set; }
}
class Car
{
public string Model { get; set; }
}
}
As you are going to implement MVVM it will be a lot better if you start to think in objects to represent Cars in your application:
public class ViewModel
{
public Car SelectedCar{ get; set; }
public ObservableCollection<Car> Cars{
get {
var cars = new ObservableCollection(YOUR_DATA_STORE.Cars.ToList());
SelectedCar = cars.FirstOrDefault(car=>car.Model == "VW");
return cars;
}
}
}
public class Car
{
public string Model {get;set;}
public string Make { get; set; }
public int Year { get; set; }
}
Your Xaml:
<ComboBox SelectedItem="{Binding SelectedCar}", ItemsSource="{Binding Cars}"
UpdateSourceTrigger=PropertyChanged}"/>
Default Value:
If you set viewModel.Car = "VW", then it should auto-select that item in the combo box.
For this to work you will need to either implement INotifyPropertyChanged or set Car before you set DataContext.
INotifyPropertyChanged implementation might look like:
class ViewModel : INotifyPropertyChanged
{
private string car;
public ViewModel()
{
this.Car = "VW";
this.Cars = new ObservableCollection<string>() { "Mazda", "VW", "Audi" };
}
public string Car
{
get
{
return this.car;
}
set
{
if (this.car != value)
{
this.car = value;
OnPropertyChanged();
}
}
}
public ObservableCollection<string> Cars { get; }
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
2.
Bind ItemsSource and SelectedItem.
<ComboBox ItemsSource="{Binding Cars}"
SelectedItem="{Binding Car, Mode=TwoWay}">
</ComboBox>
You can also set ComboBox.ItemTemplate if your source or view is more complex than just displaying a string:
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</ComboBox.ItemTemplate>
In the view model just add a list property:
public ObservableCollection<string> Cars { get; set; }
It doesn't have to be ObservableCollection but that type will auto-update the UI whenever you change the collection.
I am using WPF, MVVM and DevExpress GridControl. There are two panels in my MainWindow.xaml. Panle1 has Grid and Panel2 has Textbox. I want that if i select an item from Grid in Panel1 it's name should display in that Panle2 Textbox. Iwrote Code but it is not working. Can you Please help me to solve this?
*In NameModel From Models Folder I wrote:*
private NameModelClass _selectedCustomer;
public NameModelClass SelectedCustomer
{
get { return _selectedCustomer; }
set
{
if (_selectedCustomer != value)
{
_selectedCustomer = value;
LastName = value.LastName;
OnPropertyChanged("SelectedCustomer");
}
}
public List<Namess> ListPerson { get; set; }
void CreateList()
{
ListPerson = new List<Namess>();
for (int i = 0; i < 10; i++)
{
ListPerson.Add(new Namess(i));
}
}
public class Namess
{
public Namess(int i)
{
FirstName = "FirstName" + i;
LastName = "LastName" + i;
Age = i * 10;
}
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
}
In MianWindow.xaml I wrote:
<dxdo:LayoutPanel Caption="Grid" Caption="Panel1" x:Name="abc1">
<Grid>
<dxg:GridControl x:Name="grid" Height="233" ItemsSource="{Binding ListPerson}" AutoGenerateColumns="AddNew" HorizontalAlignment="Left" VerticalAlignment="Top" SelectedItem="{Binding SelectedNames}">
<dxg:GridControl.View>
<dxg:TableView ShowTotalSummary="True"/>
</dxg:GridControl.View>
</dxg:GridControl>
</Grid>
</dxdo:LayoutPanel>
<dxdo:LayoutPanel Caption="Panel2" x:Name="abc1">
<TextBox Width="166" Background="White" Height="33" HorizontalAlignment="Right" VerticalAlignment="Bottom" Text="{Binding Path=LastName}"/>
</dxdo:LayoutPanel>
I am new to MVVM and c#. I f my query is not clear to you please ask me. Thank you.
I do it this way:
private Namess _selectedCustomer;
public Namess SelectedCustomer
{
get { return _selectedCustomer; }
set
{
if (_selectedCustomer != value)
{
_selectedCustomer = value;
OnPropertyChanged("SelectedCustomer");
}
}
public List<Namess> ListPerson { get; set; }
void CreateList()
{
ListPerson = new List<Namess>();
for (int i = 0; i < 10; i++)
{
ListPerson.Add(new Namess(i));
}
}
public class Namess : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
public Namess(int i)
{
FirstName = "FirstName" + i;
LastName = "LastName" + i;
Age = i * 10;
}
public string FirstName { get; set; }
private string _lastName;
public string LastName
{
get
{
return _lastName;
}
set
{
if(value==_lastName)
return;
_lastName=value;
OnPropertyChanged("LastName");
}
}
public int Age { get; set; }
}
}
and in your view:
<dxdo:LayoutPanel Caption="Grid" Caption="Panel1" x:Name="abc1">
<Grid>
<dxg:GridControl x:Name="grid" Height="233" ItemsSource="{Binding ListPerson}" AutoGenerateColumns="AddNew" HorizontalAlignment="Left" VerticalAlignment="Top" SelectedItem="{Binding SelectedNames,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
<dxg:GridControl.View>
<dxg:TableView ShowTotalSummary="True"/>
</dxg:GridControl.View>
</dxg:GridControl>
</Grid>
</dxdo:LayoutPanel>
<dxdo:LayoutPanel Caption="Panel2" x:Name="abc1">
<TextBox Width="166" Background="White" Height="33" HorizontalAlignment="Right" VerticalAlignment="Bottom" Text="{Binding Path=SelectedCustomer.LastName,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"/>
</dxdo:LayoutPanel>
Bsically I changed the type of SelectedCustomer to one of the collection of items. In the view you can set the binding of your TextBox directly to a property of the SelectedCustomer.
It looks like you forgot to raise the INPC (INotifyPropertyChanged) event for the "LastName" string.
So try this (changed is in the setter below):
public NameModelClass SelectedCustomer
{
get { return _selectedCustomer; }
set
{
if (_selectedCustomer != value)
{
_selectedCustomer = value;
LastName = value.LastName;
OnPropertyChanged("SelectedCustomer");
OnPropertyChanged("LastName"); //<-- new
}
}
}
You have to send out INPCs so that the binding knows to update to the new value. The displayed binding won't "grab" the new value for LastName unles you raise that event.
Have you tried:
SelectedItem="{Binding SelectedNames, Mode=TwoWay}"
After looking at it more, your main Namess Class could do with implementing INotifyPropertyChanged
With each property raising the property changed event when it ahem changes.
Also using an observable collection so when you add and remove items it also raises changes.
That way, the notification change system receives the notify of property changes to change the view accordingly via bindings.
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
}