In this article we'll explore basic aspects of data binding, one of WPF's core features.
XAML data binding is a codeless (declarative) way of transferring information from one object to another. It's very helpful in a UI where data originating in one object controls a characteristic of (or is the source of data displayed in) another object. Data binding's main benefit is to greatly reduce (if not eliminate) the number of lines of procedural code you have to write in code behind class event handlers. Taken to its full extent, it's possible for a designer (a non-programmer) to define both the GUI's look and feel as well as its behavior within a design tool without resorting to programmer assistance.
Figure 1 shows a simple example. The window has a horizontal slide bar and a static text. Moving the track bar slider changes the size of the static text.
To synchronize the controls using code you have to implement six events. (Okay there's only one line in each.) You have to initialize the text size to the track bar's starting position in the Window's Open event; then you have to code the LineLeft, LineRight, Moved, PageLeft and PageRight to synchronize the text size with the slider position. Figure 2 shows the code and five of the six events.
Figure 3 shows an equivalent XAML data binding expression. You make the association between the controls by writing a single shorthand binding expression that links the TextSize
property value to its TrackBar data source.
The expression begins with a curly brace {
followed by the keyword Binding, followed by the name of the source object and a path expression that evaluates to the name of the property within the source containing the value. In case it's not clear to you, Figure 4 highlights the source and target objects. A longhand way of writing the same binding expression is shown in Figure 5. Here you can clearly see the role of the binding object.
Writing the binding expression causes the compiler to generate a Binding object that connects the properties of binding target objects with the data source.
Controlling Data Flow Direction
You can control the flow direction between a source and its bound objects. Flow can be:
- TwoWay (bi-directional): Value changes to the bound property are reflected in its source and changes to the source are reflected in the bound property.
- OneWay: Changes to the source are reflected in the bound property.
- OneTime: Value changes to the source are reflected in the bound property initially. Subsequent changes are ignored.
- OneWayToSource: Value changes to the bound property are reflected in its source. This is the inverse of OneWay
Default flow direction is property dependent. For user settable properties the default is TwoWay; everything else is OneWay.
I'll extend our example by adding a single line edit that allows me to directly enter a font size. In order to keep everything in sync all I need to do is bind it to the horizontal track bar. Figure 6 shows the new UI; Figure 7 shows its XAML.
Interestingly (although you should expect it) changes to dependency properties in code can be reflected among bound controls if the proper binding direction is set. To demonstrate, I'll add a couple command buttons whose clicked events will increment and decrement the font size. Since the TextSize
property by default is OneWay, I'll need to make it TwoWay to allow it to communicate its change to its source. Figure 8 shows the revised window and the new XAML (changes highlighted).
AcceptText( )
As we are all aware, changes to the contents of the edit control in a DataWindow are not reflected in the primary buffer until the user changes focus or your code calls AcceptText( )
. The Text property of the WPF TextBox (ancestor of the PB SLE) behaves the same way by default. In our case, if the user enters a new font size in the SingleLineEdit
, the size will not change until the user changes focus. However, there is an Enumeration called UpdateSourceTrigger
whose value allows you to control when the source is updated. Values are:
- Property Changed: Source is immediately updated.
- LostFocus: Source is updated when property changes and target loses focus.
- Explicit: Source can only be updated by a call in code.
Figure 9 shows the final change to the XAML that will show changes as the user is typing.
How Does It All Work?
One of the primary architectural philosophies used in building WPF was a preference for properties over methods or events. You can use properties declaratively in XAML. This allows you to easily specify intent (setting a property value) instead of action (calling a method). The mechanism supports a model-driven, or data-driven, system for displaying user interface content. This philosophy had the intended effect of creating more properties that you could bind to in order to better control the behavior of an application.
To support a system driven by properties, WPF needed a richer property system than what the CLR provides. A simple example of this richness is change notifications. In order to enable two-way binding, both sides of the bind need to support change notification. In order to have behavior tied to property values, you need to be notified when the property value changes. The Microsoft .NET Framework provides an optional interface, INotifyPropertyChange, that allows an object to publish change notifications.
WPF provides a richer property system, derived from the DependencyObject
type. The property system is truly a "dependency" property system in that it tracks dependencies between property expressions and automatically revalidates property values when dependencies change. In our example, the Static Text's FontSize
the system is automatically updated if any property it is dependent on changes.
References
- Windows Presentation Foundation Data Binding: Part 1
- WPF Architecture