WPF - MVVM - ComboBox SelectedItem - c#

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
}

Related

Wpf two way binding in listbox

trying to do two way binding in the listbox unfortunately does not work.
Here's the xaml code:
<ListBox Margin="19,0,21,149" ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding Path=Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="{Binding Path=Age}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
here's the viewmodel and person code:
public class ViewModel
{
public ObservableCollection<Person> Items
{
get
{
return new ObservableCollection<Person>
{
new Person { Name = "P1", Age = 1 },
new Person { Name = "P2", Age = 2 }
};
}
}
}
public class Person : INotifyPropertyChanged
{
public string _name;
public int Age { get; set; }
public string Name
{
get
{
return _name;
}
set
{
if (value != _name)
{
_name = value;
OnPropertyChanged("Name");
}
}
}
private void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
and MainWindow:
public MainWindow()
{
InitializeComponent();
model = new ViewModel();
this.DataContext = model;
}
I do not know what is wrong, trying to bind the property "Name", but it does not work. Please, let me know what may be wrong.
It was enough to change:
public class ViewModel
{
ObservableCollection<Person> _items = new ObservableCollection<Person>
{
new Person { Name = "P1", Age = 1 },
new Person { Name = "P2", Age = 2 }
};
public ObservableCollection<Person> Items
{
get
{
return _items;
}
}
}

SelectedItem has been changed when show view

I am going to create a ComboBox with availability of add item manually by user in WPF. So I have created some code like this:
My View code:
<ComboBox SelectedItem="{Binding Path=SelectedItem}" ItemsSource="{Binding ItemsSource}" Text="{Binding Path=InputText, UpdateSourceTrigger=LostFocus}" IsEditable="True"/>
My ViewModel code:
public class ViewModel : INotifyPropertyChanged
{
private string selectedIndex;
private string inputText;
public event PropertyChangedEventHandler PropertyChanged;
public string InputText
{
get { return inputText; }
set { inputText = value; OnPropertyChanged(); CheckAndInsertIfValid(); }
}
public string SelectedItem
{
get { return selectedIndex; }
set { selectedIndex = value; OnPropertyChanged(); }
}
public ObservableCollection<string> ItemsSource { get; set; }
public ViewModel()
{
ItemsSource = new ObservableCollection<string>()
{
"0", "1", "2", "3", "4" ,"5"
};
SelectedItem = ItemsSource[3];
}
public virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private void CheckAndInsertIfValid()
{
if (InputText != "Some Values" && !ItemsSource.Contains(InputText))
ItemsSource.Add(InputText);
}
}
It works fine and user can add to ComboBox manually. But when view is showing to user SelectedItem will be "null" however I've set.
I don't know why SelectedItem is going to be null? And How can I prevent to change of SelectedItem?
The InputText property in your case doesn't seem necessary to me, you can get rid of it and bind directly to the SelectedItem property:
<ComboBox SelectedItem="{Binding Path=SelectedItem}" ItemsSource="{Binding ItemsSource,Mode=TwoWay}" Text="{Binding Path=SelectedItem, UpdateSourceTrigger=LostFocus}" IsEditable="True"/>
And change your VM accourdingly:
public class ViewModel : INotifyPropertyChanged
{
private string _selectedItem;
public event PropertyChangedEventHandler PropertyChanged;
public string SelectedItem
{
get { return _selectedItem; }
set { _selectedItem = value; OnPropertyChanged(); CheckAndInsertIfValid(); }
}
public ObservableCollection<string> ItemsSource { get; set; }
public ViewModel()
{
ItemsSource = new ObservableCollection<string>()
{
"0", "1", "2", "3", "4" ,"5"
};
SelectedItem = ItemsSource[3];
}
public virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private void CheckAndInsertIfValid()
{
if (SelectedItem != "Some Values" && !ItemsSource.Contains(SelectedItem))
ItemsSource.Add(SelectedItem);
}
}

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 Combobox binding to update multiple textbox

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>

Categories