WPF Simplified Part 14: INotifyPropertyChanged and ObservableCollection
May 4, 2010 3 Comments
When data-binding, we want changes to the source (data bound property/collection) to be reflected in the UI. Fortunately WPF gives us some easy ways to keep the UI updated of data changes.
Consider the simple XAML,
<Grid> <Grid.RowDefinitions><RowDefinition /><RowDefinition /></Grid.RowDefinitions> <!-- Data bound text block --> <TextBlock x:Name="MyTextBlock" Height="30" Width="110" Text="{Binding Name}" /> <!-- Button to change to bound property --> <Button Grid.Row="1" Height="30" Width="110" Content="Update" Click="MyButton_Click" /> </Grid>
And the simple class,
public class Person { public string Name { get; set; } }
We’ll data bind like this,
Person person; public Window1() { InitializeComponent(); person = new Person() { Name = "John" }; // Data bind Window1 to the person object // Or we could choose to data bind to the TextBlock directly, // this.MyTextBlock.DataContext = person; this.DataContext = person; } private void MyButton_Click(object sender, RoutedEventArgs e) { // Change the data bound object's data bound property person.Name = "Locke"; }
When you click the button, nothing happens, the UI isn’t notified that the bound property has changed. There are two ways to fix this,
1. Implement INotifyPropertyChanged:
Implement the Person class like this,
public class Person : INotifyPropertyChanged { private string name; public string Name { get { return name; } set { name = value; NotifyPropertyChanged("Name"); } } public event PropertyChangedEventHandler PropertyChanged; protected void NotifyPropertyChanged(string name) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(name)); } }
Now clicking the button updates the UI and the TextBlock changes to “Locke”. See here for best practices for implementing INotifyPropertyChanged.
2. Change the property to a Dependency Property:
Implement the property (Name) backed by a dependency property (NameProperty) instead of backed by a field (name) as we previously did. Note that Person class will have to be derived from DependencyObject in this case.
public class Person : System.Windows.DependencyObject { public static DependencyProperty NameProperty
= DependencyProperty.Register("Name", typeof(string), typeof(Person));
public string Name { get { return (string)GetValue(NameProperty); } set { SetValue(NameProperty, value); } } }
We have looked at how data bound properties can update the UI, let’s also look at collections, consider that XAML,
<Grid> <Grid.RowDefinitions><RowDefinition /><RowDefinition /></Grid.RowDefinitions> <!-- Data bound list view –> <ListView x:Name="MyListView" DisplayMemberPath="Name" /> <!-- Button to change the bound collection --> <Button Grid.Row="1" Height="30" Width="80" Content="Change" Click="Button_Click" /> </Grid>
and the Person class,
public class Person { public string Name { get; set; } }
Let’s data bind a collection of Person to the ListView, like this,
List<Person> people = new List<Person>()
public Window1() { InitializeComponent(); people.Add(new Person() { Name = "John" }); people.Add(new Person() { Name = "Jack" }); // Data bind to the listview, this.MyListView.ItemsSource = people; }
private void Button_Click(object sender, RoutedEventArgs e) { // Change the data bound collection people.Add(new Person { Name = "Jacob" }); }
When we click the button, the UI doesn’t update. This is because the collection List<> doesn’t implement INotifyPropertyChanged and INotifyCollectionChanged. Luckily, the ObservableCollection does, so simply changing people to an ObservableCollection instance does the trick.
ObservableCollection<Person> people = new ObservableCollection<Person>();
Pingback: WPF Simplified Series « I.Net
This is a good explanation using a simple example. Concise and easy to follow.
Thanks…
John
Good one man nice explanation.
Its hard to find some good solutions like this.
Keep Rocking!!!