Flexible and Powerful Data Binding with WPF

hough ata binding is pervasive in Windows Presentation Foundation (WPF), it’s also different enough that you’ll need to rethink the way you use it in your applications. .NET 3.5 SP1, released in August 2008, is more than just a bug-and-performance fix service pack. It is a feature release service pack?meaning that in addition to the bug fixes, it also contains many new features. Upgrading to SP1 makes sense for WPF applications because the upgrade adds a significant number of essential updates. On the other hand, Microsoft did not release SP1 as a version release, e.g. 3.6, which complicates your deployment story.

The upgrade of WPF is full of surprises for seasoned .NET developers. It delivers a new UI composition model, a brand new XAML markup language, and the perplexing dependency property system. There is so much to learn, the question is: Where do you start? One good starting point is Wei-Meng Lee’s “An Overview of Windows Presentation Foundation” article (CoDe Magazine, Mar/Apr 2006). I have my own short list of favorite concepts to learn in WPF. Here are two.

  • First, learn about templates and their close cousins, styles. They are an astoundingly good way to design and render a user interface.
  • Second, invest some time in understanding the new data binding model. It’s time well spent.

Binding Frameworks
Data binding has been around in some form for years. Both Windows Forms and ASP.NET have binding implementations available. The motivation behind creating a binding framework is that it reduces the amount of code that the application developer must write. You rely on the teams at Microsoft to produce the mountains of code necessary to simplify your daily coding tasks.

WPF is a complete rethinking of how to construct a UI development platform. Since Microsoft started with a blank slate, it provided the WPF architects with the opportunity to engineer interesting ideas into the binding engine.

The central idea behind binding is simple. You “bind” some UI controls to a data object and then rely on the binding framework to push the data to the controls and ensure that changed data is saved back to the business object properties.

I’d define WPF data binding as the ability to register two items, a data source and a data target, with the binding framework. That’s as far as the developer’s responsibility goes. The binding framework is responsible for synchronizing the data between the two registered items and providing other services, such as validation and data conversion.

But that simple explanation hardly reveals the power of WPF binding. WPF is a complete rethinking of how to construct a UI development platform. Because Microsoft started with a blank slate, the WPF architects had the opportunity to engineer interesting ideas into their binding engine. Binding in WPF is pervasive and built into every corner of the system. It permits better abstraction of code and UI by allowing complete separation of UI design from business object code. Data templates are another unique idea within WPF. If you’re coming from a Windows Forms or ASP.NET background, you’ll find templates force you to change the way you think about developing and designing user interfaces.

Editor’s Note: This article was first published in the November/December 2008 issue of CoDe Magazine, and is reprinted here by permission.

Data Binding Collaborators
There are three main collaborators in the WPF data binding model: the framework, the data target, and the data source. Configuring a binding with the framework is simply a matter of telling the WPF dependency property system the details about the desired binding. You do this via the Binding markup extension class:

   

This short XAML (pronounced “zammel”) snippet is all you need to bind the Text property to the StreetAddress property.

Data Targets
You always set bindings are always set on a binding target, which is usually a WPF UI element?though it isn’t required to be a visual element.

The data target is the destination for the bound data. You have to specify both the target object and a target property. The target property must be a dependency property. Dependency properties are a core part of the WPF dependency property system. They enable exciting WPF features such as animation, styles, templates, and property inheritance. They also provide the glue for data binding.

Any type can serve as a data target as long as it provides one or more “dependency properties.” To find out which properties are eligible, here’s a simple LINQ query that returns all the dependency properties in the TextBlock class.

   // C#:   // using System.Windows.Controls   var q = from member   in typeof(TextBlock).GetFields      (BindingFlags.Public | BindingFlags.Static |       BindingFlags.FlattenHierarchy)   where member.FieldType ==        typeof(System.Windows.DependencyProperty)   orderby member.Name   select member.Name;   // Examples   // BackgroundProperty   // CursorProperty         ' Visual Basic:   ' using System.Windows.Controls   Dim q = From member In _    GetType(TextBlock).GetFields(BindingFlags.Public _   Or BindingFlags.Static _   Or BindingFlags.FlattenHierarchy) _   Where member.FieldType Is _ GetType(System.Windows.DependencyProperty) _   Order By member.Name _   Select member.Name   ' Examples   ' BackgroundProperty   ' CursorProperty
Author’s Note: If you run the “List of Dependency Properties” item in the included sample project, you’ll see a detailed list of available dependency properties for each framework element.

A variety of WPF elements can serve as data targets. Some are designed to show single values, such as the TextBlock and Slider elements. Other elements, such as ItemsControls, show lists of data. WPF includes a number of these list-friendly controls including ComboBox, ListBox, and TreeView.

Data Sources
One splendid aspect of the WPF data binding engine is that it can bind to nearly any data source: XML data, ADO.NET classes, LINQ queries, WPF elements, and any type that implements IEnumerable or IList.

As discussed in the preceding section, every binding must have a binding target and target property. But the binding isn’t complete until you also specify the data source and source property.

Sometimes you want to show a single value from a data source. It might be showing a font name in a label or an invoice date in a calendar control. Perhaps you’d like to bind the value of one control to the input of another. I’ll call this “single property” binding though there is no such official name in WPF. Single property binding is easy to do in WPF. Here is an example showing how to bind the Value property of a Slider control to the FontSize property of a TextBlock:

      

A binding source is usually a property on an object so you need to provide both the data source object and the data source property in your binding XAML. In the above example the ElementName attribute signifies that you want data from another element on the page and the Path signifies the appropriate property. A binding path can be a complex value, drilling into the member hierarchy, but in this case it’s just the name of the property.

Change Notification
Nearly any .NET property can serve as a data source. In the previous example the data source was the Value property on a Slider element, which returns a Double value. The framework loads the data into the data target on initialization. If you establish a two-way binding (details later in this article) the data will also flow back to the data source. The WPF dependency system can keep the data synchronized when the source property changes; however, this is not automatic. To accomplish the synchronization, the source property must implement some form of change notification?basically, your type must tell the framework that the underlying data has changed. There are a several ways to accomplish this in your data source:

  • INotifyPropertyChanged: Implement this .NET 2.0 interface and create the PropertyChanged event. In your property setters, raise the PropertyChanged event whenever the value changes. This is the preferred technique in WPF.
  • DependencyProperty: Derive from DependencyObject and create a DependencyProperty. All change notification is handled by the WPF dependency property system. A side effect of this approach is that you create a dependency on the WPF assemblies in your business class assembly.
  • xxxPropertyChanged events: In .NET 1.x you can create an event with the same name as your property (e.g. WidthChanged) and raise the event when the property (Width) changes. Microsoft made sure that this scenario works in WPF because it makes it possible to bind to older .NET types. While it is usable, you’ll find that INotifyPropertyChanged is easier to work with.

Listing 1 shows a sample business object that implements INotifyPropertyChanged, while Listing 2 shows an example of the DependencyProperty approach.

Data Context
Naturally, WPF is not limited to employing UI elements as data sources; it also offers the ability to bind to non-UI data. Every binding requires a data source object and a path to the source property. A data context lets the framework look for a source object further up the logical tree. All FrameworkElements and FrameworkContentElements have a DataContext property. Here’s an example:

   

What’s going on here? The Path property points to the Salary property but where is the XAML that specifies the data source? To answer that question, look at the following code.

   // C#:   // in the Loaded event procedure   var emp = new Employee();     emp.FirstName = "Johnny";     emp.LastName = "Appleseed";     emp.Salary = 15000M;   // sets the Datacontext for the Page   this.DataContext = emp;      ' Visual Basic:   ' in the Loaded event procedure   Dim emp = New Employee()     emp.FirstName = "Johnny"     emp.LastName = "Appleseed"     emp.Salary = 15000D   ' sets the Datacontext for the Page   Me.DataContext = emp

The preceding code sets the data context explicitly. The last line in each snippet sets the data context for the entire page. When the binding object can’t find a data context on the TextBlock, it walks the logical tree looking for a data context on a parent element. If none is found, the default error handling occurs. Because the page is a parent element, the binding object will use its data context in this case.

You can also apply different data contexts to elements on a page, as the next snippet shows:

   // C#:   // setting different data contexts   var emp1 = new Employee();   emp1.LastName = "Appleseed";   stackPanel1.DataContext = emp1;      emp2 = new Employee();   emp2.LastName = "Washington";   dockPanel1.DataContext = emp2;   ' Visual Basic:   ' setting different data contexts   Dim emp1 = New Employee()   emp1.LastName = "Appleseed"   stackPanel1.DataContext = emp1      Dim emp2 = New Employee()   emp2.LastName = "Washington"   dockPanel1.DataContext = emp2

The preceding code sets different data contexts for the StackPanel and DockPanel.

The Binding class constructor takes a Path string, meaning you can leave off the Path property entirely and use the shorter syntax shown here:

         

Binding Extension
The Binding class is the main actor in the data binding story. It has a simple and easy to understand API. Here are some of the common binding properties:

  • ElementName: The data source is a different element. You must specify the source element’s name specified, usually with the x:Name attribute.
  • Source: Provide a reference to a valid object source. A binding configured with the Source property takes precedence over any applied data context.
  • RelativeSource: The source is a property on a related object.
  • Path: Provide navigation instructions for finding the data source property. The simplest path is merely the name of a property, as in the example below:
  •                   
  • XPath: Provide XPath navigation instructions for finding the XML source node.

Suitable Data Sources
You can use any type as a data source as long as it has a public parameterless constructor and at least one public property. Here’s an example of binding to a System.String type. In the example below, both TextBlock elements have a Name, which makes them accessible in the *.cs or *.vb code-behind file. You set the TextBox (tb2) binding in the XAML file, and set the data context in the code behind:

                            

In the C# code-behind file:

   string sample;   public BindingToString()   {      InitializeComponent();      sample = "ABC DEF GHI JKL MNO PQR";      // assign directly to the Text property      tb1.Text = sample;      // register with the binding system      tb2.DataContext = sample;      // there must also be a binding in the XAML      // or here in code   }

The equivalent code in a Visual Basic code-behind file is:

   Private sample As String   Public Sub New()      InitializeComponent()      sample = "ABC DEF GHI JKL MNO PQR"      ' assign directly to the Text property      textBox1.Text = sample      ' register with the binding system      textBox2.DataContext = sample      ' there must also be a binding in the XAML      ' or here in code   End Sub

Storing Items in Resources
You can create bindings completely declaratively? a XAML-only binding; you’ll see shortly how to establish such bindings without writing any program code. However, to tackle that subject I first need to discuss XAML resources. The resource framework exists to provide easy access to reusable items within your XAML. I’ll start by discussing the XAML parser.

When the XAML parser encounters a WPF element in a XAML file, it creates an instance of the underlying type. This is possible because there is a one-to-one mapping between every element in the http://schemas.microsoft.com/winfx/2006/xaml/presentation namespace and an existing WPF class in a .NET assembly. Thus, the parser knows how to turn the

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Overview

Recent Articles: