Wpf gridview selected Item - c#

I have ListView with GridView inside view of ListView and ListView item source is specified. I dont seem to find how can. I get SelectedItem of GridView or SelectedItem changed.
<ListView Grid.Row="4" Margin="0,250,0,0" ItemsSource="{Binding TestBinding}" SelectedItem="{Binding Path=selectedItem}" IsSynchronizedWithCurrentItem="True" HorizontalAlignment="Left" SelectionChanged="ListView_SelectionChanged">
<ListView.View>
<GridView AllowsColumnReorder="False" >
<GridViewColumn Header="Test" DisplayMemberBinding="{Binding Path=Test1}" Width="100" />
<GridViewColumn Header="Test2" DisplayMemberBinding="{Binding Path=Test2}" Width="130" />
</GridView>
</ListView.View>
</ListView>

Here is my code and it works fine:
public partial class MainWindow : Window, INotifyPropertyChanged, INotifyPropertyChanging
{
public class MyObj
{
public string Test1 { get; set; }
public string Test2 { get; set; }
}
public MainWindow()
{
InitializeComponent();
TestBinding = new ObservableCollection<MyObj>();
for (int i = 0; i < 5; i++)
{
TestBinding.Add(new MyObj() { Test1 = "sdasads", Test2 = "sdsasa" });
}
DataContext = this;
}
#region TestBinding
private ObservableCollection<MyObj> _testBinding;
public ObservableCollection<MyObj> TestBinding
{
get
{
return _testBinding;
}
set
{
if (_testBinding != value)
{
NotifyPropertyChanging("TestBinding");
_testBinding = value;
NotifyPropertyChanged("TestBinding");
}
}
}
#endregion
#region selectedItem
private MyObj _selectedItem;
public MyObj selectedItem
{
get
{
return _selectedItem;
}
set
{
if (_selectedItem != value)
{
NotifyPropertyChanging("selectedItem");
_selectedItem = value;
NotifyPropertyChanged("selectedItem");
}
}
}
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
// Used to notify the page that a data context property changed
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
#region INotifyPropertyChanging Members
public event PropertyChangingEventHandler PropertyChanging;
// Used to notify the data context that a data context property is about to change
protected void NotifyPropertyChanging(string propertyName)
{
if (PropertyChanging != null)
{
PropertyChanging(this, new PropertyChangingEventArgs(propertyName));
}
}
#endregion
}

Related

Background / Foreground Capability in Custom ListView Items

I have the following functioning code that binds GridViewColumns to data from a custom class:
<ListView Name="lv">
<ListView.View>
<GridView>
<GridViewColumn Header="First" DisplayMemberBinding="{Binding lvi.firstName}"/>
<GridViewColumn Header="Last" DisplayMemberBinding="{Binding lvi.lastName}"/>
</GridView>
</ListView.View>
</ListView>
public class LVItemBox {
public LVItem lvi { get; set; }
}
public class LVItem : INotifyPropertyChanged {
private string _firstName;
private string _lastName;
public string firstName {
get { return _firstName; }
set { SetField(ref _firstName, value); }
}
public string lastName {
get { return _lastName; }
set { SetField(ref _lastName, value); }
}
public event PropertyChangedEventHandler PropertyChanged;
public virtual void OnPropertyChanged(string propertyName) {PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
public bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null) { if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
}
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
LVItem lvi1 = new LVItem {firstName = "John", lastName = "Doe"};
LVItem lvi2 = new LVItem {firstName = "Jane", lastName = "Smith"};
lv.Items.Add(new LVItemBox {lvi = lvi1});
lv.Items.Add(new LVItemBox {lvi = lvi2});
}
}
My dilemma is that I want background / foreground Brush capability within LVItemBox, however if I make LVItemBox extend Control, changing Background/Foreground has no effect:
public class LVItemBox : Control {
public LVItem lvi { get; set; } // data displays
}
...
...
private void changeBackground(object sender, EventArgs e) {
LVItemBox lvib = (LVItemBox)lv.Items[0];
lvib.Background = Brushes.Black; // doesn't work
}
Furthermore, if I extend ListViewItem instead of Control I can get the background change to work, but the data bindings no longer work.
public class LVItemBox : ListViewItem {
public LVItem lvi { get; set; } // data doesn't display
}
...
...
private void changeBackground(object sender, EventArgs e) {
LVItemBox lvib = (LVItemBox)lv.Items[0];
lvib.Background = Brushes.Black; // works
}
Any idea how I can get foreground / background capability within LVItemBox?
Inheriting from Control works if you add the following ItemContainerStyle to your XAML:
<ListView Name="lv">
<ListView.View>
<GridView>
<GridViewColumn Header="First" DisplayMemberBinding="{Binding lvi.firstName}"/>
<GridViewColumn Header="Last" DisplayMemberBinding="{Binding lvi.lastName}"/>
</GridView>
</ListView.View>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Background" Value="{Binding Background}" />
</Style>
</ListView.ItemContainerStyle>
</ListView>

Implement Visibility Property to a custom class

I've created a class that I need to have Visibility property like other UI controls. It looks like this:
More extended code:
xaml:
<ListBox x:Name="itemsHolder" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Surname}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Code behind:
public ObservableCollection<MyClass > myVM= new ObservableCollection<MyClass>();
public class MyClass : Control //FrameworkElement
{
public string Name { get; set; }
public string Surname { get; set; }
}
...
MyClass my1 = new MyClass();
my1.Name = "Test";
my1.Surname = "Test2";
myVM.Add(my1);
itemsHolder.ItemsSource = myVm;
...
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
foreach(MyClass item in itemsHolder.Items)
{
if(!item.Name.Contains((sender as TextBox).Text))
{
item.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
}
else
{
item.Visibility = Windows.UI.Xaml.Visibility.Visible;
}
}
}
What I try to do is something like a filter(search) and I want to hide items. Just adding a property to the class also does not work.
You are very close... to get this working your class MyClass must implement INotifyPropertyChanged, I use the base class bindable which makes implementing INotifyPropertyChanged much simpler.
Below is the answer
xaml:
<ListBox Grid.Row="1" x:Name="itemsHolder" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Visibility="{Binding IsVisible}">
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Surname}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
code:
public ObservableCollection<MyClass > myVM= new ObservableCollection<MyClass>();
public class Bindable:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class MyClass : Bindable {
private string _Name;
public string Name {
get { return _Name; }
set
{
if (_Name != value)
{
_Name = value;
OnPropertyChanged();
}
}
}
private string _Surname;
public string Surname
{
get { return _Surname; }
set{
if (_Surname != value)
{
_Surname = value;
OnPropertyChanged();
}
}
}
public Visibility _IsVisible;
public Visibility IsVisible {
get { return _IsVisible; }
set {
if (_IsVisible != value)
{
_IsVisible = value;
OnPropertyChanged();
}
}
}
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
foreach(MyClass item in itemsHolder.Items)
{
if(!item.Name.Contains((sender as TextBox).Text))
{
item.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
}
else
{
item.Visibility = Windows.UI.Xaml.Visibility.Visible;
}
}
}

How to bind button to Listbox item

ViewModel
public class MainWindowViewModel:BindableBase
{
public IRelayCommand MyCommand { get; protected set; }
private void CreateCommand()
{
this.MyCommand = new RelayCommand(MyCommandExecuted, CanExecuteMyCommand);
}
private void MyCommandExecuted(object obj)
{
MessageBox.Show("Command Executed");
}
private bool CanExecuteMyCommand(object obj)
{
return true; // The value is based on Selected Item
}
}
XAML
<ListBox
x:Name="myListBox"
ItemsSource="{Binding Path=MyClass}"
<ListBox.ItemTemplate>
<DataTemplate>
<Expander Header="{Binding Path=HeaderName}" IsExpanded="True">
<StackPanel>
<DataGrid
x:Name="dataGrid"
AutoGenerateColumns="False"
ItemsSource="{Binding Path=RowVal}" SelectedItem="{Binding CurrentItem}"/>
</StackPanel>
</Expander>
</DataTemplate>
</ListBox.ItemTemplate>
<Button Content="Select"
Command="{Binding Path=MyCommand }"
CommandParameter="{Binding ElementName=myListBox,Path=SelectedItem}"/>
DataClass
public class DataClass
{
public string HeaderName { get; set; }
public object RowVal { get; set; }
public ObservableCollection<DataGridColumn> ColumnCollection { get; set;}
private object currentItem;
public object CurrentItem
{
get
{
return currentItem;
}
set
{
currentItem = value;
}
}
}
How can I bind my button to Listbox item which is CurrentItem in DataClass ?
I created a complete example to show how I would do it. You would have to bind the parent element to SelectedItem as well, to keep track of when that item changes. Since the SelectedItem is public in your child class as well you can access that when your command triggers in your main view model.
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<ListView ItemsSource="{Binding Parents}" SelectedItem="{Binding SelectedParent}">
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type local:Parent}">
<DataGrid ItemsSource="{Binding Children}" SelectedItem="{Binding SelectedChild}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Grid.Row="1" Width="70" Content="Click me" Height="25" Command="{Binding MyCommand}" />
i.e. in DoWork, you can get the child from the parent via its public property.
public sealed class WindowViewModel : INotifyPropertyChanged
{
private readonly ObservableCollection<Parent> parents;
private readonly ICommand myCommand;
private Parent selectedParent;
public WindowViewModel()
{
parents = new ObservableCollection<Parent>
{
new Parent{ Name = "P1"},
new Parent{ Name = "P2"}
};
myCommand = new DelegateCommand(DoWork);
}
private void DoWork()
{
var selectedChild = SelectedParent == null ? null : SelectedParent.SelectedChild;
}
public Parent SelectedParent
{
get { return selectedParent; }
set
{
if (selectedParent == value)
return;
selectedParent = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<Parent> Parents
{
get { return parents; }
}
public ICommand MyCommand
{
get { return myCommand; }
}
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
With the basic setup of Data models
public class Parent : INotifyPropertyChanged
{
private ObservableCollection<Child> children;
private Child m_SelectedChild;
public Parent()
{
children = new ObservableCollection<Child>
{
new Child {Name = "C1"},
new Child {Name = "C2"}
};
}
public string Name { get; set; }
public ObservableCollection<Child> Children
{
get { return children; }
}
public Child SelectedChild
{
get { return m_SelectedChild; }
set
{
if (m_SelectedChild == value)
return;
m_SelectedChild = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Child
{
public string Name { get; set; }
}
Another solution, if you are more interested of the Child item in your WindowViewModel is to change the relative source of where the binding should occur, in your DataGrid. i.e., the binding would look like this instead:
<DataGrid ItemsSource="{Binding Children}"
SelectedItem="{Binding DataContext.SelectedChild, RelativeSource={RelativeSource AncestorType={x:Type ListView}}}" />
and then move the Property from Parent to WindowViewModel. With that you would be able to trigger changes to your button command when the child element changes for any of the Parent elements.
I think you want to pass the CurrentItem to the MyCommand as CommandParameter right?
Then you only have to:
CommandParameter="{Binding CurrentItem, UpdateSourceTrigger=PropertyChanged}"
Try this :
CommandParameter="{Binding ElementName=myListBox,Path=SelectedItem.CurrentItem}"

Correct Binding in WPF (Collection inside Collection) on Tab and Datagrid

I would like to have on several tabs different Datagrids but I have problems with the correct Binding.
Each TabEntry has a Collection of DataGridEntry.
The Tab Items are displayed (Tab1 and Tab2) but the Binding for the DataGridEntries is not correct.
TabEntry.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace TabControlTest
{
public class TabEntry : INotifyPropertyChanged
{
public TabEntry()
{
DataGridEntries = new ObservableCollection<DataGridEntry>();
}
public string Description { get; set; }
public ObservableCollection<DataGridEntry> DataGridEntries{get;set;}
public event PropertyChangedEventHandler PropertyChanged;
}
public class DataGridEntry : INotifyPropertyChanged
{
public string Description { get; set; }
public string Value { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
}
}
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
ObservableCollection<TabEntry> Tabs = new ObservableCollection<TabEntry>();
TabEntry tab1 = new TabEntry();
tab1.Description = "Tab1";
DataGridEntry data1 = new DataGridEntry();
data1.Description = "Tab1 Description 1";
data1.Value = "Tab1 Value 1";
DataGridEntry data2 = new DataGridEntry();
data2.Description = "Tab1 Description 2";
data2.Value = "Tab1 Value 2";
tab1.DataGridEntries.Add(data1);
tab1.DataGridEntries.Add(data2);
TabEntry tab2 = new TabEntry();
tab2.Description = "Tab2";
DataGridEntry data3 = new DataGridEntry();
data1.Description = "Tab2 Description 1";
data1.Value = "Tab1 Value 1";
DataGridEntry data4 = new DataGridEntry();
data2.Description = "Tab2 Description 2";
data2.Value = "Tab1 Value 2";
tab2.DataGridEntries.Add(data3);
tab2.DataGridEntries.Add(data4);
Tabs.Add(tab1);
Tabs.Add(tab2);
this.DataContext = Tabs;
InitializeComponent();
}
}
Tabs is a collection with tabEntries (class TabEntry)
Each TabEntry has a collection with DataGridEntries(class DataGridEntry)
How do I bind to these collections correctly in xaml?
<Window x:Class="TabControlTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TabControlTest"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TabControl x:Name="tabControl1" ItemsSource="{Binding}" TabStripPlacement="Left" HorizontalAlignment="Left" Height="242" Margin="10,10,0,0" VerticalAlignment="Top" Width="358">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Description}"></TextBlock>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<DataGrid x:Name="dataGrid1" ItemsSource="{Binding DataGridEntries}">
<DataGrid.Columns>
<DataGridTextColumn Header="Description" Binding="{Binding Description}"></DataGridTextColumn>
<DataGridTextColumn Header="Value" Binding="{Binding Value}"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</Window>
I have the following output:
The content is not correctly mapped to the TabControl and DataGrid.
The Datagrid on Tab2 is blank
Here a cleaner implementation of what you are trying to do
the Xaml
<Window x:Class="WpfApplication34.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfApplication34="clr-namespace:WpfApplication34"
Title="MainWindow" Height="350" Width="525" DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Grid>
<TabControl x:Name="tabControl1" ItemsSource="{Binding Tabs}" TabStripPlacement="Left" HorizontalAlignment="Left" Height="242" Margin="10,10,0,0" VerticalAlignment="Top" Width="358">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Description}" ></TextBlock>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<DataGrid x:Name="dataGrid1" ItemsSource="{Binding DataGridEntries}">
<DataGrid.Columns>
<DataGridTextColumn Header="Description" Binding="{Binding Desription}"></DataGridTextColumn>
<DataGridTextColumn Header="Value" Binding="{Binding Value}"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</Grid>
and the codeBehind:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private ObservableCollection<TabEntry> _tabs ;
public ObservableCollection<TabEntry> Tabs
{
get
{
return _tabs;
}
set
{
if (_tabs == value)
{
return;
}
_tabs = value;
OnPropertyChanged();
}
}
public MainWindow()
{
InitializeComponent();
Tabs = new ObservableCollection<TabEntry>()
{
new TabEntry()
{
Description = "Tab1",
DataGridEntries = new ObservableCollection<DataGridEntry>()
{
new DataGridEntry()
{
Description = "Tab1 Description 1",
Value = "Tab1 Value 1"
},
new DataGridEntry()
{
Description = "Tab1 Description 2",
Value = "Tab1 Value 2"
}
}
},
new TabEntry()
{
Description = "Tab2",
DataGridEntries = new ObservableCollection<DataGridEntry>()
{
new DataGridEntry()
{
Description = "Tab2 Description 1",
Value = "Tab2 Value 1"
},
new DataGridEntry()
{
Description = "Tab2 Description 2",
Value = "Tab2 Value 2"
}
}
}
};
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class TabEntry : INotifyPropertyChanged
{
private String _description;
public String Description
{
get
{
return _description;
}
set
{
if (_description == value)
{
return;
}
_description = value;
OnPropertyChanged();
}
}
public ObservableCollection<DataGridEntry> DataGridEntries { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class DataGridEntry : INotifyPropertyChanged
{
private String _description;
public String Description
{
get
{
return _description;
}
set
{
if (_description == value)
{
return;
}
_description = value;
OnPropertyChanged();
}
}
private String _value;
public String Value
{
get
{
return _value;
}
set
{
if (_value == value)
{
return;
}
_value = 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));
}
}
Your member DataGridEntries must be a property to be used in binding:
public class TabEntry : INotifyPropertyChanged
{
public TabEntry()
{
DataGridEntries = new ObservableCollection<DataGridEntry>();
}
public string Description { get; set; }
public ObservableCollection<DataGridEntry> DataGridEntries { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
}
Side note: Just implementing INotifyPropertyChanged is not enought - you also need to call the event in the getter methods. Either implement the getters or use e.g. Fody...

WPF - MVVM - ComboBox SelectedItem

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
}

Categories