Better User and Developer Experiences – From Windows Forms to WPF with MVVM: Part 4, Data Binding
Now that I’ve demonstrated how WPF can be written in the same manner, using the same methods as prior windowing frameworks, it’s time to explain why this is a bad idea. To do this effectively, I’m going to discuss a few things that WPF introduces, and explain how they should change the way all of us approach building user interfaces. One thing I do want to mention – this series is not meant to be a full tutorial on WPF, rather an explanation of how to use features in WPF effectively.
The first new concept in WPF I’ll introduce is the new, more powerful, more flexible model for Data Binding.
Windows Presentation Foundation provides an entirely new form of Data Binding than previous data binding technologies. Data Binding existed in Windows Forms, but was limited to being bound to pretty specific data sources. Many developers have used data bound controls prior to WPF, but the way data binding works in WPF has changed. The data binding support in WPF adds the potential to use completely different programming models than we used previously.
So lets start with explaining data binding, in general terms. Data binding is a general purpose technique that lets you take two (or potentially more than two) separate elements, and “bind†them together so that their values stay synchronized. Typically, a control in the user interface (like a text box or a label) is bound to a property on some object containing your business logic.
The advantages of this should be obvious pretty quickly, provided this works correctly. However, previous attempts at data binding have all fallen short when it came time to use them in practical, real-world problems. I believe this comes down to the inability of most data binding frameworks to handle everything required. For data binding to truly be effective, a binding framework should provide the following.
- An easy way to bind user interface elements to data sources
- A clean way to specify a data source for an entire collection of objects
- A consistent way to specify bindings, no matter what is being bound
- Allow flexibility in what triggers updates
- An effective means of validation of values
- The means to do conversion of the data through the binding itself
Most frameworks for data binding have provided some, but not all, of the features available above. I’m going to address the above bullet points, and show how WPF provides for clean data binding. We’ll start with the first three points:
- An easy way to bind user interface elements to data sources
- A clean way to specify a data source for an entire collection of objects
- A consistent way to specify bindings, no matter what is being bound
Nearly all user interface items in WPF derive from FrameworkElement. Whenever you create a Window or a UserControl in WPF, you have the option of specifying any object as the Framework Element’s DataContext. The DataContext property specifies “the data context for an element when it participates in data bindingâ€. Whenever an object has a DataContext specified, you can use WPF’s data binding to bind directly to properties defined on that object.
There is a very important line in the documentation for DataContext:
This dependency property inherits property values. If there are child elements without other values for DataContext established through local values or styles, then the property system will set the value to be the DataContext value of the nearest parent element with this value assigned.
What this means is that you don’t have to specify the DataContext for every element in your Window or UserControl. You can specify the DataContext for a UserControl, and, by default, every control inside of the UserControl will automatically use that same DataContext for data binding! This is incredibly powerful.
For example, in both our Windows Forms and WPF (version 1) applications, we had a UserControl defined for displaying information about our Feed object. Whenever we’d update our Feed object, we would write a bunch of code triggered by setting our property to update our UI, such as:
// .. Called when Feed changes to update the user interface this.textBoxTitle.Text = this.Feed.Title; this.textBoxLink.Text = this.Feed.Link.AbsoluteUri; this.textBoxDescription.Text = this.Feed.Description;
By using data binding, we can completely eliminate this code. Instead of doing this, we could, for example, just set the DataContext of our UserControl to the Feed object itself. Once we’ve done that, we can just specify, in our XAML (which can be done in the designer), which property in our DataContext which we want to bind to our individual TextBox controls:
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Path=Feed.Title, Mode=OneWay}" /> <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Path=Feed.Link.AbsoluteUri, Mode=OneWay}" /> <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Path=Feed.Description, Mode=OneWay}"/>
Now, all of a sudden, we have full, up to date user interface controls, and we can completely eliminate two entire methods (ResetFeed and ClearFeed) from our code! Less code means less to maintain, less to test, and less to worry about. Our TextBox instances don’t have a DataContext specified, so they automatically inherit the DataContext of the UserControl (since that is the first parent element with a DataContext provided). They then bind directly to the property of the object in the DataContext.
This solves our first three bullet points – we can bind to any object (*see note below) using declarative syntax (in XAML), and we can specify the object one time, at a high level such as a Window or UserControl, via FrameworkElement.DataContext.
Let’s move on to the next two points:
- Allow flexibility in what triggers updates
- An effective means of validation of values
Windows Presentation Foundation provides the means to handle this, as well. Again, with no changes to our code, we can declaratively add support for both of the above bullet points.
First, what triggers an update to the binding can be specified, in XAML, by providing an UpdateSourceTrigger. This lets us say, explicitly, that we want the property to update every time we type a letter into a TextBox (by setting the trigger to “PropertyChangedâ€), or only when the control loses focus (“LostFocusâ€). In addition, UpdateSourceTrigger lets us explicitly handle property updates, even though this is not as commonly used.
Validation can be handled, and again specified declaratively by using XAML, in multiple ways. I do not want to cover Validation here in detail, but it is discussed in detail on MSDN. One or more ValidationRules can be attached to a Binding, allowing for great flexibility in data validation.
Let’s look at our final point:
- The means to do conversion of the data through the binding itself
Using WPF, we have the ability to bind objects of any type to a control, even if the type wouldn’t normally (directly) be editable or displayable within a control. WPF allows you to specify a converter to use to convert from the source type to the editing type, as well as handling the conversion back. This is done via IValueConverter. By specifying your own value converter, you can bind, via both OneWay or TwoWay binding, any control to any type of object. For example, above, we bound our TextBox to “Feed.Link.AbsoluteUriâ€, which is a string. However, if the AbsoluteUri property didn’t exist, we could write a converter to convert from a Uri to a string, and use that in our data binding. This would allow us to bind directly to “Feed.Linkâ€, as follows:
<TextBox Grid.Row="1" Grid.Column="1"> <TextBox.Text> <Binding Path="Feed.Link" Mode="OneWay"> <Binding.Converter> <RssMVVM:StringToUriConverter /> </Binding.Converter> </Binding> </TextBox.Text> </TextBox>
This is critical to the flexibility of WPF’s data binding – we’re now directly binding a string (the TextBox.Text property) to a custom class (Uri). We can now data bind to any type of object, which allows us to do very complex operations. For example, we can now display different images for Enum values (by having a converter that converts from the Enum value to an image).
To summarize, the data binding in Windows Presentation Foundation provides us with a simple, clean, powerful, and flexible means of displaying our properties inside our user interfaces, as well as automatically updating our properties when the user edits values within controls in our user interfaces. In addition, it does it in a way that’s completely declarative, with no code required.
* Although you can bind to any object, there are certain types of objects that work better. In particular, any object that implements INotifyPropertyChanged or derives from DependencyObject will allow for TwoWay binding to work properly. Collections should implement INotifyCollectionChanged or IBindingList in order to allow for proper TwoWay binding, as well. This isn’t a requirement, but it is worth mentioning, since it has a large impact on usability.
The statement:
What this means is that you don’t have to specify the DataContext for every element in your Window or UserControl. You can specify the DataContext for a UserControl, and, by default, every control inside of the UserControl will automatically use that same DataContext for data binding! This is incredibly powerful.
In my expreience is not true for and the s in the control.
Kevin:
As long as the individual controls within the UserControl don’t override the DataContext, it’s inherited from it’s parent. Where have you seen differently? If you aren’t seeing this behavior, it’s because something inside the UserControl is explicitly setting its DataContext, “breaking” the inheritance through the tree.
-Reed
Really one of the best articles. Explains everything that I searched for weeks in – just ONE PLACE in a very clean, deep and efficient manner.
Nabil,
Thank you for the feedback – I’m glad it was helpful!
-Reed
Binding by using magic strings like “Feed.ThisWillNeverChange”? Classy.
Stuart,
I do agree – using property names as strings for binding is less than ideal. This is probably one of the few things I don’t love about data binding in WPF (and Silverlight). That being said, it’s a small tradeoff I’d happily pay for the flexibility this brings, in this case.
-Reed
It’s good to get a fresh way of looknig at it.
hands down the BEST article to understand WPF + MVVM.
Deserves a re-read just to feel the simplicity + awesomeness.
Thanks Reed
Thanks for the feedback, Yogesh! I’m happy to hear that it helped.
I’ve been reading books and learning WPF MVVM for more than a week but I haven’t learned anything until I found your page. Very useful indeed from someone who used to code in WinForms.
Thanks Reed