Data-Binding Sources: The Three Alternatives
March 21, 2006
It's Spring in NYC?
I hope yesterday's discussion of a digital clock program using data binding didn't imply that there was only one way to implement a property that successfully serves as a source in a data binding. There are at least three ways to do it.
A brief overview: In WPF, a data binding links two properties of two objects. One is considered the "target" of the data binding and the other is the "source." There are several different "modes" of linking these two properties together, but the most common (and generally the default) is BindingMode.OneWay. In this type of binding, whenever the source property changes, the target property is updated to reflect the new value.
Obviously there must exist some kind of mechanism for the source object to notify the outside world when the bound property has changed. Veteran .NET programmers will immediately shout out "Events!" and that's certainly true. In pre-WPF days, such an event to signal a change in a property is given the name of the property with the word Changed appended.
That approach still works in WPF. Here is an alternative ClockTicker.cs file that you can slip into yesterday's DigitalClock project and the clock will work just the same. This alternative ClockTicker class has a property named DateTime and an event named DateTimeChanged. The code in WPF responsible for implementing data bindings knows enough to look around (via reflection) for an event named DateTimeChanged when the source property is named DateTime. Notice that this is a really old-fashioned looking event because it's based on the EventHandler delegate. Nothing new about it.
The approach I showed yesterday (here's the original ClockTicker.cs file for your convenience) implements the INotifyPropertyChanged interface and has an event named PropertyChanged, which identifies the particular event that's changed with a string. This is a good approach to use if you have a bunch of properties that might change because you only need one event for all of them.
The third approach is more "modern" and more "WPFish," and involves backing the property with an object of type DependencyProperty. Here's yet another alternative ClockTicker.cs file that you use in the DigitalClock project. A class that defines dependency properties must derive from DependencyObject. The dependency property gets the same name as the CLR property but with the word Property appended. The DateTime property is then implemented with calls to GetValue and SetValue defined by DependencyObject. (Complete details on defining dependency properties are in Chapter 8 of my forthcoming book Applications = Code + Markup).
What's interesting in the dependency property version of ClockTicker is the simplicity of the TimerOnTick handler. Rather than fire a new event, this one simply sets the DateTime property to its new value. That's enough to trigger activity within the data binding itself to update the target.
Which approach is the best? Although defining dependency properties is a PITA, it's really the way to go. Consider that target properties of data bindings are required to be backed by dependency properties, and the choice is obvious.