In my previous blog post I introduced data binding. It worked, but there was no mechanism for updating. Updating comes in two flavors, and these are often confused by folks new to databinding:
The first case arises because most of the time you are not the only user of your program – other users may be connected to the same data and while you are looking at your data someone else may change it. You want to see that change immediately. The canonical example is this: you work at a bookstore and someone calls and asks whether you have a particular book in stock. You check and say yes, you have one left. While they are deciding whether to purchase it, another employee has sold that book. The quantity-on-hand just dropped from one (plenty of books for your customer) to zero (oops). You really would like to see that change reflected in the UI.
To accomplish this first kind of update, you will make your data class implement INotifyPropertyChanged. Returning to the previous posting, we had an Employee class. We will have that class implement INotifyPropertyChanged. It does so like this,
class Employee : INotifyPropertyChanged { private string name; public string Name { get { return name; } set { name = value; OnPropertyChanged(); } } private string title; public string Title { get { return title; } set { title = value; OnPropertyChanged(); } } public Employee( string name, string title ) { Name = name; Title = title; } public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged( [CallerMemberName] string caller = "" ) { if ( PropertyChanged != null ) { PropertyChanged( this, new PropertyChangedEventArgs( caller ) ); } } }
First, we mark the class to implement the interface. The interface calls for the existence of a single Event: PropertyChanged. We add a helper method that checks to see if anyone has registered with INotifyPropertyChanged (which all the UIElements do) and if it is not null, then it raises the PropertyChanged event passing in the name of the calling property (using the new attribute [CallerMemberName]).
In the setter of each property we call OnPropertyChanged to inform the UI that the property has been updated.
Let’s add a button to the XAML so that we can simulate the property being changed by another user. Here is what our XAML looks like,
<StackPanel Name="xDisplay" Margin="50"> <StackPanel Orientation="Horizontal"> <TextBlock Text="Name:" /> <TextBlock Margin="5,0,0,0" Text="{Binding Name}" /> </StackPanel> <StackPanel Orientation="Horizontal"> <TextBlock Text="Title:" /> <TextBlock Margin="5,0,0,0" Text="{Binding Title}" /> </StackPanel> <Button Content="Change" Click="Button_Click_1" /> </StackPanel>
When we navigate to the page, we’ll create an instance of Employee.
private Employee emp; protected override void OnNavigatedTo(NavigationEventArgs e) { emp = new Employee( "Joe", "QA" ); xDisplay.DataContext = emp; }
When the button is clicked we’ll modify the employee’s name and title,
private void Button_Click_1( object sender, RoutedEventArgs e ) { emp.Name = "Jesse"; emp.Title = "Evangelist"; }
Run the application. It initially comes up with Joe in QA, but click the button and it is changed to Jesse, Evangelist. The change in the display reflects a change to the underlying data.
That was scenario #1. In scenario #2 we’d like the user to be able to change the display and have that change persisted in the underlying data. To do this, we’ll add a second stack panel, just like the first, except that the name and title will be in TextBoxes instead of TextBlocks. More important, our binding will no longer be one-way, but rather two-way. This is accomplished with the mode attribute. There are three modes in databinding:
Clearly it is Two Way that we want now, and you can see this in our modified XAML,
<StackPanel Name="xDisplay" Margin="50"> <StackPanel Orientation="Horizontal"> <TextBlock Text="Name:" /> <TextBlock Margin="5,0,0,0" Text="{Binding Name}" /> </StackPanel> <StackPanel Orientation="Horizontal"> <TextBlock Text="Title:" /> <TextBlock Margin="5,0,0,0" Text="{Binding Title}" /> </StackPanel> <StackPanel Orientation="Horizontal"> <TextBlock Text="Name:" /> <TextBox Margin="5,0,0,0" Text="{Binding Name, Mode=TwoWay}" /> </StackPanel> <StackPanel Orientation="Horizontal"> <TextBlock Text="Title:" /> <TextBox Margin="5,0,0,0" Text="{Binding Title, Mode=TwoWay}" /> </StackPanel> <Button Content="Change" Click="Button_Click_1" /> </StackPanel>
There are no changes to the code to handle the two way binding; the binding mechanism will take care of that for you. Thus, if you run this again, you can modify the name and title in the TextBoxes, and the changes will be immediately reflected in the TextBlocks above. What is happening is that the user is changing the data, that is being stored back into the underlying data and because of INotifyPropertyChanged the UI is being updated to reflect that data.
Cool, eh?
Here’s something else that is cool; this is exactly how it worked in Silverlight, WPF and Windows Phone.
If you are eager to start building your first Windows 8 app, make sure to check Telerik Windows 8 UI controls.
Jesse Liberty has three decades of experience writing and delivering software projects. He is the author of 2 dozen books and has been a Distinguished Software Engineer for AT&T and a VP for Information Services for Citibank and a Software Architect for PBS. You can read more on his personal blog or follow him on twitter