span.sup { vertical-align:text-top; }

Data and WPF

Customize Data Display with Data Binding and WPF

Josh Smith

This article discusses:

  • WPF data binding
  • Data display and hierarchical data
  • Using templates
  • Input validation
This article uses the following technologies:
WPF, XAML, C#

Code download available at:AdvancedWPFDatabinding2008_07.exe(171 KB)

Contents

Binding in Code
Working with Templates
Working with an Inherited DataContext
Working with Collection Views
Working with Hierarchical Data
Using Many Controls to Display XML Data
Using Many Controls to Display Business Objects
One Control to Display an Entire Hierarchy
Using Hierarchical Data Templates
Working with User Input
Input Validation via ValidationRules
Displaying Validation Errors
Input Validation via IDataErrorInfo
Wrapping Up
Experiment in the virtual lab:

When the Windows® Presentation Foundation (WPF) first appeared on the .NET radar, most of the articles and demo applications touted its superb rendering engine and 3D capabilities. While fun to read and play with, such examples do not demonstrate the powerful real-world functionality of WPF. Most of us do not need to create applications with rotating video cubes that burst into fireworks when you click on them. Most of us earn a living by creating software to display and edit large amounts of complex business or scientific data.

The good news is that WPF offers excellent support for managing the display and editing of complex data. In the December 2007 edition of MSDN® Magazine, John Papa wrote "Data Binding in WPF" (msdn.microsoft.com/magazine/cc163299), which did a great job of explaining essential WPF data binding concepts. Here, I'll explore more advanced data binding scenarios, building on what John presented in the aforementioned Data Points column. At the end of the day, you will be aware of various ways to implement common data binding requirements seen in most line-of-business applications.

Binding in Code

One of the biggest changes that WPF introduces for desktop application developers is the extensive use of and support for declarative programming. WPF user interfaces and resources can be declared using Extensible Application Markup Language (XAML), an XML-based markup language. Most explanations of WPF data binding only show how to work with bindings in XAML. Since anything that is possible to do in XAML is also possible to achieve in code, it is important that professional WPF developers learn how to work with data binding programmatically as well as declaratively.

In many situations, it is more convenient and expedient to declare bindings in XAML. As systems become more complex and dynamic, it sometimes makes more sense to work with bindings in code. Before going further, let's first review some common classes and methods involved with programmatic data binding.

WPF elements inherit both the SetBinding and GetBinding­Expression methods, from either FrameworkElement or Framework­ContentElement. These are just convenience methods that call into methods with the same names in the BindingOperations utility class. The following code illustrates how to use the Binding­Operations class to bind the Text property of a textbox to a property on another object:

static void BindText(TextBox textBox, string property)
{
      DependencyProperty textProp = TextBox.TextProperty;
      if (!BindingOperations.IsDataBound(textBox, textProp))
      {
          Binding b = new Binding(property);
          BindingOperations.SetBinding(textBox, textProp, b);
      }
} 

You can easily unbind a property by using the code seen here:

static void UnbindText(TextBox textBox)
{
    DependencyProperty textProp = TextBox.TextProperty;
    if (BindingOperations.IsDataBound(textBox, textProp))
    {
        BindingOperations.ClearBinding(textBox, textProp);
    }
}

By clearing out the binding, you will also remove the bound value from the target property.

Declaring a data binding in XAML conceals some of the underlying details. When you start working with bindings in code, those details begin to come out of the woodwork. One of those details is the fact that the relationship between a binding source and target is actually maintained by an instance of the BindingExpression class, not Binding itself. The Binding class contains high-level information that multiple BindingExpressions can share, but the enforcement of a link between two bound properties comes from an underlying expression. The following code shows how you can use BindingExpression to programmatically check if the Text property of a textbox is being validated:

static bool IsTextValidated(TextBox textBox)
{
    DependencyProperty textProp = TextBox.TextProperty;

    var expr = textBox.GetBindingExpression(textProp);
    if (expr == null)
        return false;

    Binding b = expr.ParentBinding;
    return b.ValidationRules.Any();
} 

Since a BindingExpression does not know if it is being validated, you'll need to ask its parent binding. I'll examine input validation techniques later on.

Working with Templates

An effective user interface presents raw data in such a way that the user can intuitively discover meaningful information in it. This is the essence of data visualization. Data binding is just one piece of the data visualization puzzle. All but the most trivial WPF programs require a means to present data in a more powerful way than just binding one property on a control to one property on a data object. Real data objects have many related values, and those various values should aggregate into one cohesive visual representation. This is why WPF has data templates.

The System.Windows.DataTemplate class is just one form of template in WPF. In general, a template is like a cookie cutter that the WPF framework uses in order to create visual elements that aid in rendering objects that have no intrinsic visual representation. When an element attempts to display an object that does not have an intrinsic visual representation, such as a custom business object, you can tell the element how to render the object by giving it a DataTemplate.

The DataTemplate can generate as many visual elements as necessary to display the data object. Those elements use data bindings to display the data object's property values. If an element does not know how to display the object it is told to render, it simply calls the ToString method on it and displays the result in a TextBlock.

Suppose you have a simple class called FullName, which stores a person's name. You want to display a list of names and have each person's last name appear more prominently than the other parts of the name. To do this you could create a DataTemplate that describes how to render a FullName object. The code listed in Figure 1 shows the FullName class and the codebehind for a window that will display a list of names.

Figure 1 Display FullNames with a DataTemplate

public class FullName
{
    public string FirstName { get; set; }
    public char MiddleInitial { get; set; }
    public string LastName { get; set; }
}

public partial class WorkingWithTemplates : Window
{
    // This is the Window's constructor.
    public WorkingWithTemplates()
    {
        InitializeComponent();

        base.DataContext = new FullName[]
        {
            new FullName 
            { 
                FirstName = "Johann", 
                MiddleInitial = 'S', 
                LastName = "Bach" 
            },
            new FullName 
            { 
                FirstName = "Gustav",
                MiddleInitial = ' ',
                LastName = "Mahler" 
            },
            new FullName 
            { 
                FirstName = "Alfred", 
                MiddleInitial = 'G', 
                LastName = "Schnittke" 
            }
        };
    }
}

As seen in Figure 2, there is an ItemsControl in the window's XAML file. It creates a simple list of items that the user cannot select or remove. The ItemsControl has a DataTemplate assigned to its ItemTemplate property, with which it renders each FullName instance created in the window's constructor. You should notice how most of the TextBlock elements in the DataTemplate have their Text property bound to properties on the FullName object that they represent.

Figure 2 Display FullName Objects Using a DataTemplate

<!-- This displays the FullName objects. -->
<ItemsControl ItemsSource="{Binding Path=.}">
  <ItemsControl.ItemTemplate>
    <DataTemplate>
      <StackPanel Orientation="Horizontal">
        <TextBlock FontWeight="Bold" Text="{Binding LastName}" />
        <TextBlock Text=", " />
        <TextBlock Text="{Binding FirstName}" />
        <TextBlock Text=" " />
        <TextBlock Text="{Binding MiddleInitial}" />
      </StackPanel>
    </DataTemplate>
  </ItemsControl.ItemTemplate>
</ItemsControl>

When this demo app runs, it looks like Figure 3. By using a DataTemplate to render the name, it is easy to accentuate each person's last name because the corresponding TextBlock's FontWeight is bold. This simple example demonstrates the fundamental relationship between WPF data binding and templates. As I dig deeper into this topic, I will combine these features into more powerful ways of visualizing complex objects.

fig03.gif

Figure 3 FullNames Rendered by a DataTemplate

Working with an Inherited DataContext

Unless told otherwise, all bindings implicitly bind against an element's DataContext property. An element's DataContext references its data source, so to speak. There is something special to be aware of regarding how DataContext works. Once you understand this subtle aspect of DataContext, it vastly simplifies the design of complex data-bound user interfaces.

An element's DataContext property does not have to be set in order to reference a data source object. If the DataContext of an ancestor element in the element tree (technically, the logical tree) is given a value for its DataContext, the value will automatically be inherited by every descendant element in the user interface. In other words, if a window's DataContext is set to reference a Foo object, by default the DataContext of every element in the window will reference that same Foo object. You can easily give any element in the window a different DataContext value, which causes all of that element's descendant elements to inherit that new DataContext value. This is similar to an ambient property in Windows Forms.

In the previous section, I examined how to use DataTemplates to create visualizations of data objects. Elements created by the template in Figure 2 have their properties bound to properties of a FullName object. Those elements are implicitly binding to their DataContext. The DataContext of elements created by a Data­Template reference the data object for which the template is used, such as a FullName object.

There is no magic involved in the value inheritance of the DataContext property. It is leveraging the support for inherited dependency properties that is built-in to WPF. Any dependency property can be an inherited property, simply by specifying a flag in the metadata provided when registering that property with the dependency property system of WPF.

Another example of an inherited dependency property is Font­Size, which all elements have. If you set the FontSize dependency property on a window, by default all elements in that window will display their text at that size. The same infrastructure used to propagate the FontSize value down the element tree is what propagates the DataContext.

This usage of the term "inheritance" is different from its meaning in the object-oriented sense, where a subclass inherits the members of its parent class. Property value inheritance refers only to the propagation of values down the element tree at run time. Naturally, a class can inherit a dependency property that happens to support value inheritance, in the object-oriented sense.

Working with Collection Views

When WPF controls bind to a collection of data, they do not bind directly to the collection itself. Instead, they implicitly bind to a view automatically wrapped around that collection. The view implements the ICollectionView interface and can be one of several concrete implementations, such as ListCollectionView.

A collection view has several responsibilities. It keeps track of the current item in the collection, which usually translates to the active/selected item in a list control. Collection views also supply a generic means of sorting, filtering, and grouping items in a list. Multiple controls can bind to the same view around a collection so they are all coordinated with each other. The following code demonstrates some features of ICollectionView:

// Get the default view wrapped around the list of Customers.
ICollectionView view = CollectionViewSource.GetDefaultView(allCustomers);

// Get the Customer selected in the UI.
Customer selectedCustomer = view.CurrentItem as Customer;

// Set the selected Customer in the UI.
view.MoveCurrentTo(someOtherCustomer);

All list controls, such as listbox, combobox, and list view, must have their IsSynchronizedWithCurrentItem property set to true in order to remain synchronized with the collection view's Current­Item property. The abstract Selector class defines that property. If it is not set to true, selecting an item in the list control will not update the CurrentItem of the collection view, and assigning a new value to CurrentItem will not be reflected in that list control.

Working with Hierarchical Data

The real world is full of hierarchical data. A customer places multiple orders, a molecule is made of many atoms, a department consists of many employees, and a solar system contains a set of celestial bodies. You're undoubtedly familiar with this common master-detail arrangement.

WPF provides various ways to work with hierarchical data structures, each of which is appropriate for different situations. The choices essentially boil down to either using many controls to display data or displaying multiple levels of the data hierarchy in one control. Next, I am going to examine both of those approaches.

Using Many Controls to Display XML Data

A very common way to work with hierarchical data is to have a separate control display each level of the hierarchy. For example, suppose we have a system that represents customers, orders, and order details. In that situation, we might have a combobox to display customers, a listbox to display all of the selected customers' orders, and then an ItemsControl to display details about the selected order. This is a great way to display hierarchical data, and it's quite easy to implement in WPF.

Based on the scenario I described previously, Figure 4 shows a simplified sample of the data an application might deal with, wrapped up in the WPF XmlDataProvider component. A user interface similar to Figure 5 can display that data. Notice how the customers and orders are selectable, but the details about an order exist in a read-only list. This makes sense because a visual object should only be selectable if it affects the state of the application or if it is editable.

Figure 4 Customers, Orders, and OrderDetails XML Hierarchy

<XmlDataProvider x:Key="xmlData">
  <x:XData>
    <customers >
      <customer name="Customer 1">
        <order desc="Big Order">
          <orderDetail product="Glue" quantity="21" />
          <orderDetail product="Fudge" quantity="32" />
          </order>
          <order desc="Little Order">
            <orderDetail product="Ham" quantity="1" />
            <orderDetail product="Yarn" quantity="2" />
          </order>
        </customer>
        <customer name="Customer 2">
          <order desc="First Order">
            <orderDetail product="Mousetrap" quantity="4" />
          </order>
        </customer>
      </customers>
    </x:XData>
</XmlDataProvider>

fig05.gif

Figure 5 One Way to Display the XML Data

The XAML in Figure 6 describes how to use those various controls to display the hierarchical data just shown. This window requires no code; it exists entirely in XAML.

Figure 6 XAML to Bind Hierarchical XML Data to a UI

<Grid DataContext=
    "{Binding Source={StaticResource xmlData},
    XPath=customers/customer}"
    Margin="4" 
  >
  <Grid.RowDefinitions>
    <RowDefinition Height="Auto" />
    <RowDefinition />
    <RowDefinition />
  </Grid.RowDefinitions>

  <!-- CUSTOMERS -->
  <DockPanel Grid.Row="0">
    <TextBlock DockPanel.Dock="Top" FontWeight="Bold" Text="Customers" />
    <ComboBox
      IsSynchronizedWithCurrentItem="True"
      ItemsSource="{Binding}"
      >
      <ComboBox.ItemTemplate>
        <DataTemplate>
          <TextBlock Text="{Binding XPath=@name}" />
        </DataTemplate>
      </ComboBox.ItemTemplate>
    </ComboBox>
  </DockPanel>

  <!-- ORDERS -->
  <DockPanel Grid.Row="1">
    <TextBlock DockPanel.Dock="Top" FontWeight="Bold" Text="Orders" />
    <ListBox
      x:Name="orderSelector" 
      DataContext="{Binding Path=CurrentItem}"
      IsSynchronizedWithCurrentItem="True" 
      ItemsSource="{Binding XPath=order}"
      >
      <ListBox.ItemTemplate>
        <DataTemplate>
          <TextBlock Text="{Binding XPath=@desc}" />
        </DataTemplate>
      </ListBox.ItemTemplate>
    </ListBox>
  </DockPanel>

  <!-- ORDER DETAILS -->
  <DockPanel Grid.Row="2">
    <TextBlock DockPanel.Dock="Top" FontWeight="Bold" 
      Text="Order Details" />
    <ItemsControl 
      DataContext=
         "{Binding ElementName=orderSelector, Path=SelectedItem}"
      ItemsSource="{Binding XPath=orderDetail}">
      <ItemsControl.ItemTemplate>
        <DataTemplate>
          <TextBlock>
            <Run>Product:</Run>
            <TextBlock Text="{Binding XPath=@product}" />
            <Run>(</Run>
            <TextBlock Text="{Binding XPath=@quantity}" />
            <Run>)</Run>
          </TextBlock>
        </DataTemplate>
      </ItemsControl.ItemTemplate>
    </ItemsControl>
  </DockPanel>
</Grid>

Notice the extensive use of short XPath queries to tell WPF where to get the bound values. The Binding class exposes an XPath property to which you can assign any XPath query supported by the XmlNode.SelectNodes method. Under the hood, WPF uses that method in order to execute XPath queries. Unfortunately, this means that since XmlNode.SelectNodes does not currently support the use of XPath functions, WPF data binding does not support them either.

The combobox of customers and the listbox of orders both bind to the resultant node set of the XPath query performed by the root Grid's DataContext binding. The listbox's DataContext will automatically return the CurrentItem of the collection view wrapped around the collection of XmlNodes generated for the Grid's DataContext. In other words, the DataContext of the listbox is the currently selected Customer. Since that listbox's ItemsSource is implicitly bound to its own DataContext (because no other source was specified), and its ItemsSource binding executes an XPath query to get the <order> elements from the DataContext, the ItemsSource is effectively bound to the selected customer's list of orders.

Bear in mind that when binding to XML data you are actually binding to the objects created by a call to XmlNode.SelectNodes. If you are not careful, you can end up with multiple controls binding to logically equivalent, but physically distinct, sets of XmlNodes. This is due to the fact that each call to XmlNode.SelectNodes generates a new set of XmlNodes, even if you pass the same XPath query to the same XmlNode each time. That is a concern specific to XML data binding, so when binding to business objects you can blissfully ignore it.

Using Many Controls to Display Business Objects

Now suppose you wanted to bind to the same data as in the previous example, but the data exists as business objects instead of XML. How would that change the way you bind to the various levels of the data hierarchy? How similar or different would the technique be?

The code in Figure 7 shows the simple classes used to create business objects that store the data to which we will bind. These classes form the same logical schema as the XML data used in the previous section.

Figure 7 Classes to Create Hierarchy of Business Objects

public class Customer
{
    public string Name { get; set; }
    public List<Order> Orders { get; set; }

    public override string ToString()
    {
        return this.Name;
    }
}

public class Order
{
    public string Desc { get; set; }
    public List<OrderDetail> OrderDetails { get; set; }

    public override string ToString()
    {
        return this.Desc;
    }
}

public class OrderDetail
{
    public string Product { get; set; }
    public int Quantity { get; set; }
}

The XAML of the window that displays these objects is in Figure 8. It is very similar to the XAML seen in Figure 6, but there are some important differences worth noting. Something not seen in the XAML is that the window's constructor creates the data objects and sets the DataContext, instead of the XAML referencing it as a resource. Notice that none of the controls have their Data­Context explicitly set. They all inherit the same DataContext, which is a List<Customer> instance.

Figure 8 XAML to Bind Hierarchical Business Objects into a UI

<Grid Margin="4">
  <Grid.RowDefinitions>
    <RowDefinition Height="Auto" />
    <RowDefinition />
    <RowDefinition />
  </Grid.RowDefinitions>

   <!-- CUSTOMERS -->
   <DockPanel Grid.Row="0">
     <TextBlock DockPanel.Dock="Top" FontWeight="Bold" Text="Customers"
     />
     <ComboBox 
       IsSynchronizedWithCurrentItem="True" 
       ItemsSource="{Binding Path=.}" 
       />
   </DockPanel>

   <!-- ORDERS -->
   <DockPanel Grid.Row="1">
     <TextBlock DockPanel.Dock="Top" FontWeight="Bold" Text="Orders" />
     <ListBox 
       IsSynchronizedWithCurrentItem="True" 
       ItemsSource="{Binding Path=CurrentItem.Orders}" 
       />
   </DockPanel>

   <!-- ORDER DETAILS -->
   <DockPanel Grid.Row="2">
     <TextBlock DockPanel.Dock="Top" FontWeight="Bold" 
        Text="Order Details" />
     <ItemsControl
       ItemsSource="{Binding Path=CurrentItem.Orders.CurrentItem.
       OrderDetails}"
       >
       <ItemsControl.ItemTemplate>
         <DataTemplate>
           <TextBlock>
             <Run>Product:</Run>
             <TextBlock Text="{Binding Path=Product}" />
             <Run>(</Run>
             <TextBlock Text="{Binding Path=Quantity}" />
             <Run>)</Run>
           </TextBlock>
         </DataTemplate>
       </ItemsControl.ItemTemplate>
     </ItemsControl>
   </DockPanel>
</Grid>

Another significant difference when binding to business objects instead of XML is that the ItemsControl that hosts the order details does not need to bind to the SelectedItem of the order's listbox. That approach was necessary in the XML binding scenario because there is no generic way to reference the current item of a list whose items come from a local XPath query.

When binding to business objects instead of XML, it is trivial to bind against nested levels of selected items. The ItemsControl's ItemsSource binding utilizes this convenient feature by specifying CurrentItem in the binding path twice: once for the selected customer, again for the selected order. The CurrentItem property is a member of the underlying ICollectionView wrapped around the data source, as discussed previously in the article.

There is one more point of interest regarding a difference between how the XML and business object approaches work. Since the XML example binds to XmlElements, you must provide DataTemplates to explain how to render the customers and orders. When binding to custom business objects, you can avoid this overhead by simply overriding the ToString method of the Customer and Order classes and allowing WPF to display the output of that method for those objects. This trick only suffices for objects that can have simple textual representations. It might not make sense to use this convenient technique when dealing with complex data objects.

One Control to Display an Entire Hierarchy

Until now, you have only seen ways to display hierarchical data by showing each level of the hierarchy in separate controls. It is often useful and necessary to show all levels of a hierarchical data structure within the same control. The canonical example of this approach is the TreeView control, which supports the display and traversal of arbitrary levels of nested data.

You can populate the WPF TreeView with items in one of two ways. One option is manually adding the items, in either code or XAML, and the other option is to create them via data binding.

The XAML that follows shows how one can manually add some TreeViewItems to a TreeView in XAML:

<TreeView>
  <TreeViewItem Header="Item 1">
    <TreeViewItem Header="Sub-Item 1" />
    <TreeViewItem Header="Sub-Item 2" />
  </TreeViewItem>
  <TreeViewItem Header="Item 2" />
</TreeView>

The manual technique of creating items in a TreeView makes sense for situations where the control will always display a small, static set of items. When you need to display large amounts of data that can vary over time, it becomes necessary to use a more dynamic approach. At that point, you have two options. You can write code that walks over a data structure, creates TreeViewItems based on the data objects it finds, and then adds those items to the TreeView. Alternatively, you can take advantage of hierarchical data templates and let WPF do all of the work for you.

Using Hierarchical Data Templates

You can declaratively express how WPF should render hierarchical data via hierarchical data templates. The HierarchicalData­Template class is a tool that bridges the gap between a complex data structure and a visual representation of that data. It is very similar to a normal DataTemplate but also allows you to specify where a data object's child items come from. You can also provide the HierarchicalDataTemplate a template with which to render those child items.

Suppose you now want to display the data presented in Figure 7 within one TreeView control. The TreeView might look something like Figure 9. Implementing this involves the use of two Hierarchical­DataTemplates and one DataTemplate.

fig09.gif

Figure 9 Display Entire Data Hierarchy in a TreeView

The two hierarchical templates display Customer and Order objects. Since OrderDetail objects have no child items, you can render them with a non-hierarchical DataTemplate. The TreeView's ItemTemplate property uses the template for objects of type Customer, since Customers are the data objects contained at the root level in the TreeView. The XAML listed in Figure 10 shows how all the pieces of this puzzle fit together.

Figure 10 XAML Behind the TreeView Display

<Grid>
  <Grid.DataContext>
    <!-- 
    This sets the DataContext of the UI
    to a Customers returned by calling
    the static CreateCustomers method. 
    -->
    <ObjectDataProvider 
      xmlns:local="clr-namespace:VariousBindingExamples"
      ObjectType="{x:Type local:Customer}"
      MethodName="CreateCustomers"
      />
  </Grid.DataContext>

  <Grid.Resources>
    <!-- ORDER DETAIL TEMPLATE -->
    <DataTemplate x:Key="OrderDetailTemplate">
      <TextBlock>
        <Run>Product:</Run>
        <TextBlock Text="{Binding Path=Product}" />
        <Run>(</Run>
        <TextBlock Text="{Binding Path=Quantity}" />
        <Run>)</Run>
      </TextBlock>
    </DataTemplate>

    <!-- ORDER TEMPLATE -->
    <HierarchicalDataTemplate 
      x:Key="OrderTemplate"
      ItemsSource="{Binding Path=OrderDetails}"
      ItemTemplate="{StaticResource OrderDetailTemplate}"
      >
      <TextBlock Text="{Binding Path=Desc}" />
    </HierarchicalDataTemplate>

    <!-- CUSTOMER TEMPLATE -->
    <HierarchicalDataTemplate 
      x:Key="CustomerTemplate"
      ItemsSource="{Binding Path=Orders}"
      ItemTemplate="{StaticResource OrderTemplate}"
      >
      <TextBlock Text="{Binding Path=Name}" />
    </HierarchicalDataTemplate>
  </Grid.Resources>

  <TreeView
    ItemsSource="{Binding Path=.}"
    ItemTemplate="{StaticResource CustomerTemplate}"
    />

</Grid>

I assign a collection of Customer objects to the Data­Context of a Grid that contains the TreeView. This is possible to do in XAML by using the ObjectDataProvider, which is a convenient way of calling a method from XAML. Since the DataContext is inherited down the element tree, the TreeView's DataContext references that set of Customer objects. That is why we can give its ItemsSource property a binding of "{Binding Path=.}", which is a way of indicating that the ItemsSource property is bound to the TreeView's DataContext.

If you did not assign the TreeView's ItemTemplate property, the TreeView would only display the top-level Customer objects. Since WPF has no idea how to render a Customer, it would call ToString on each Customer and display that text for each item. It would have no way to figure out that each Customer has a list of Order objects associated with it, and that each Order has a list of OrderDetail objects. Since there is no way that WPF can magically understand your data schema, you must explain the schema to WPF so that it can render the data structure properly for you.

Explaining the structure and appearance of your data to WPF is where HierarchicalDataTemplates fit into the picture. The templates used in this demonstration contain very simple visual element trees, mostly just TextBlocks with a small amount of text in them. In a more complicated application, the templates could have interactive rotating 3D models, images, vector graphic drawings, complex UserControls, or any other WPF content to visualize the underlying data object.

It is important to note the order in which the templates are declared. You must declare a template before referencing it via the StaticResource extension. This is a requirement imposed by the XAML reader, and it applies to all resources, not just templates.

It is possible to reference the templates by using the Dynamic­Resource extension instead, in which case the lexical order of template declarations is irrelevant. However, using a Dynamic­Resource reference, as opposed to a StaticResource reference, carries along with it some runtime overhead because they monitor the resource system for changes. Since we do not replace the templates at run time, that overhead is unnecessary, so it is best to use StaticResource references and properly arrange the order of the template declarations.

Working with User Input

For most programs, displaying data is only half of the battle. The other big challenge is analyzing, accepting, and rejecting data entered by the user. In an ideal world, where all users always enter logical and accurate data, this would be a simple task. In the real world, however, this is not at all the case. Real users create typos, forget to enter required values, enter values in the wrong place, delete records that should not be deleted, add records that should not be added, and generally follow Murphy's Law whenever humanly possible.

It is our job, as developers and architects, to combat the inevitable erroneous and malicious input entered by our users. The WPF binding infrastructure has support for input validation. In the next few sections of this article, I'll examine how to make use of the WPF support for validation, as well as how to display validation error messages to the user.

Input Validation via ValidationRules

The first version of WPF, which was part of the Microsoft® .NET Framework 3.0, had only limited support for input validation. The Binding class has a ValidationRules property, which can store any number of ValidationRule-derived classes. Each of those rules can contain some logic that tests to see if the bound value is valid.

Back then, WPF only came with one ValidationRule subclass, called ExceptionValidationRule. Developers could add that rule to a binding's ValidationRules and it would catch exceptions thrown during updates made to the data source, allowing the UI to display the exception's error message. The usefulness of this approach to input validation is debatable, considering that the bedrock of good user experience is to avoid unnecessarily revealing technical details to the user. The error messages in data parsing exceptions are generally too technical for most users, but I digress.

Suppose that you have a class that represents an era of time, such as the simple Era class seen here:

public class Era
{
    public DateTime StartDate { get; set; }
    public TimeSpan Duration { get; set; }
}

If you want to allow the user to edit the start date and duration of an era, you could use two textbox controls and bind their Text properties to the properties of an Era instance. Since the user could enter any text he wants into a textbox, you cannot be sure that the input text will be convertible to an instance of DateTime or TimeSpan. In this scenario, you can use the ExceptionValidationRule to report data conversion errors, and then display those conversion errors in the user interface. The XAML listed in Figure 11 demonstrates how to achieve this task.

Figure 11 A Simple Class that Represents an Era of Time

<!-- START DATE -->
<TextBlock Grid.Row="0">Start Date:</TextBlock>
<TextBox Grid.Row="1">
  <TextBox.Text>
    <Binding Path="StartDate" UpdateSourceTrigger="PropertyChanged">
      <Binding.ValidationRules>
        <ExceptionValidationRule />
      </Binding.ValidationRules>
    </Binding>
  </TextBox.Text>
</TextBox>

<!-- DURATION -->
<TextBlock Grid.Row="2">Duration:</TextBlock>
<TextBox 
  Grid.Row="3" 
  Text="{Binding 
         Path=Duration, 
         UpdateSourceTrigger=PropertyChanged, 
         ValidatesOnExceptions=True}" 
  />

Those two textboxes demonstrate the two ways that an ExceptionValidationRule can be added to the ValidationRules of a binding in XAML. The Start Date textbox uses the verbose property-element syntax to explicitly add the rule. The Duration textbox uses the shorthand syntax by simply setting the ValidatesOnExceptions property of the binding to true. Both bindings have their UpdateSourceTrigger property set to PropertyChanged so that the input is validated every time the textbox's Text property is given a new value, instead of waiting until the control loses focus. A screenshot of the program is seen in Figure 12.

fig12.gif

Figure 12 ExceptionValidationRule Displays Validation Errors

Displaying Validation Errors

As seen in Figure 13, the Duration textbox contains an invalid value. The string it contains is not convertible to a TimeSpan instance. The textbox's tooltip displays an error message, and a small red error icon appears on the right side of the control. This behavior does not happen automatically, but it is easy to implement and customize.

Figure 13 Render Input Validation Errors to the User

<!-- 
The template which renders a TextBox 
when it contains invalid data. 
-->
<ControlTemplate x:Key="TextBoxErrorTemplate">
  <DockPanel>
    <Ellipse 
      DockPanel.Dock="Right" 
      Margin="2,0"
      ToolTip="Contains invalid data"
      Width="10" Height="10"   
      >
      <Ellipse.Fill>
        <LinearGradientBrush>
          <GradientStop Color="#11FF1111" Offset="0" />
          <GradientStop Color="#FFFF0000" Offset="1" />
        </LinearGradientBrush>
      </Ellipse.Fill>
    </Ellipse>
    <!-- 
    This placeholder occupies where the TextBox will appear. 
    -->
    <AdornedElementPlaceholder />
  </DockPanel>
</ControlTemplate>

<!-- 
The Style applied to both TextBox controls in the UI.
-->
<Style TargetType="TextBox">
  <Setter Property="Margin" Value="4,4,10,4" />
  <Setter 
    Property="Validation.ErrorTemplate" 
    Value="{StaticResource TextBoxErrorTemplate}" 
    />
  <Style.Triggers>
    <Trigger Property="Validation.HasError" Value="True">
      <Setter Property="ToolTip">
        <Setter.Value>
          <Binding 
            Path="(Validation.Errors)[0].ErrorContent"
            RelativeSource="{x:Static RelativeSource.Self}"
            />
        </Setter.Value>
      </Setter>
    </Trigger>
  </Style.Triggers>
</Style>

The static Validation class forms a relationship between a control and any validation errors it contains by the use of some attached properties and static methods. You can reference those attached properties in XAML to create markup-only descriptions of how the user interface should present input validation errors to the user. The XAML in Figure 13 is responsible for explaining how to render input errors messages for the two textbox controls in the previous example.

The Style in Figure 13 targets all instances of a textbox in the UI. It applies three settings to a textbox. The first Setter affects the textbox's Margin property. The Margin property is set to a value that provides enough space to display the error icon on the right side.

The next Setter in the Style assigns the ControlTemplate used to render the textbox when it contains invalid data. It sets the attached Validation.ErrorTemplate property to the Control­Template declared above the Style. When the Validation class reports that the textbox has one or more validation errors, the textbox renders with that template. This is where the red error icon comes from, as seen in Figure 12.

The Style also contains a Trigger that monitors the attached Validation.HasError property on the textbox. When the Validation class sets the attached HasError property to true for the textbox, the Style's Trigger activates and assigns a tooltip to the textbox. The content of the tooltip is bound to the error message of the exception thrown when attempting to parse the textbox's text into an instance of the source property's data type.

Input Validation via IDataErrorInfo

With the introduction of the Microsoft .NET Framework 3.5, WPF support for input validation vastly improved. The Validation­Rule approach is useful for simple applications, but real-world applications deal with the complexity of real-world data and business rules. Encoding business rules into Validation­Rule objects not only ties that code to the WPF platform, but it also does not allow for business logic to exist where it belongs: in business objects!

Many applications have a business layer, where the complexity of processing business rules is contained in a set of business objects. When compiling against the Microsoft .NET Framework 3.5, you can make use of the IDataErrorInfo interface to have WPF ask business objects if they are in a valid state or not. This removes the need to place business logic in objects separate from the business layer, and it allows you to create UI platform-independent business objects. Since the IDataErrorInfo interface has been around for years, this also makes it much easier to reuse business objects from a legacy Windows Forms or ASP.NET application.

Suppose that you need to provide validation for an era beyond just ensuring that the user's text input is convertible to the source property's data type. It might make sense that an era's start date cannot be in the future, since we do not know about eras that have yet to exist. It might also make sense to require that an era last for at least one millisecond.

These types of rules are similar to the generic idea of business logic in that they are both examples of domain rules. It is best to implement domain rules in the objects that store their state: domain objects. The code listed in Figure 14 shows the SmartEra class, which exposes validation error messages via the IData­ErrorInfo interface.

Figure 14 IDataErrorInfo Exposing Validation Error Messages

public class SmartEra 
    : System.ComponentModel.IDataErrorInfo
{
    public DateTime StartDate { get; set; }
    public TimeSpan Duration { get; set; }

    #region IDataErrorInfo Members

    public string Error
    {
        get { return null; }
    }

    public string this[string property]
    {
        get 
        {
            string msg = null;
            switch (property)
            {
                case "StartDate":
                    if (DateTime.Now < this.StartDate)
                        msg = "Start date must be in the past.";
                    break;

                case "Duration":
                    if (this.Duration.Ticks == 0)
                        msg = "An era must have a duration.";
                    break;

                default:
                    throw new ArgumentException(
                        "Unrecognized property: " + property);
            }
            return msg;
        }
    }

    #endregion // IDataErrorInfo Members
}

Consuming the validation support of the SmartEra class from a WPF user interface is very simple. The only thing you have to do is tell the bindings that they should honor the IDataErrorInfo interface on the object to which they are bound. You can do this in one of two ways, as seen in Figure 15.

Figure 15 Consuming the Validation Logic

<!-- START DATE -->
<TextBlock Grid.Row="0">Start Date:</TextBlock>
<TextBox Grid.Row="1">
  <TextBox.Text>
    <Binding Path="StartDate" UpdateSourceTrigger="PropertyChanged">
      <Binding.ValidationRules>
        <ExceptionValidationRule />
        <DataErrorValidationRule />
      </Binding.ValidationRules>
    </Binding>
  </TextBox.Text>
</TextBox>

<!-- DURATION -->
<TextBlock Grid.Row="2">Duration:</TextBlock>
<TextBox 
  Grid.Row="3" 
  Text="{Binding 
         Path=Duration, 
         UpdateSourceTrigger=PropertyChanged, 
         ValidatesOnDataErrors=True,
         ValidatesOnExceptions=True}" 
  />

Similar to how you can add the ExceptionValidationRule explicitly or implicitly to a binding's ValidationRules collection, you can add the DataErrorValidationRule directly to the ValidationRules of a binding, or you can just set the ValidatesOnDataErrors property to true. Both approaches result in the same net effect; the binding system queries the data source's IDataErrorInfo interface for validation errors.

Wrapping Up

There is a reason why many developers say that their favorite feature of WPF is its rich support for data binding. The use of binding in WPF is so powerful and pervasive that it requires many software developers to recalibrate their thinking around the relationship between data and user interfaces. Many core features of WPF work together to support complex data-bound scenarios, such as templates, styles, and attached properties.

With relatively few lines of XAML, you can express your intentions for how to display a hierarchical data structure and how to validate user input. In advanced situations, you can tap into the full power of the binding system by accessing it programmatically. With such a powerful infrastructure at our disposal, the perennial goal of creating great user experiences and compelling data visualizations is finally within reach for developers creating modern business applications.

Josh Smith is passionate about using WPF to create great user experiences. He was awarded the Microsoft MVP title for his work in the WPF community. Josh works for Infragistics in the Experience Design Group. When he is not at a computer, he enjoys playing the piano, reading about history, and exploring New York City with his girlfriend. You can visit Josh's blog at joshsmithonwpf.wordpress.com.