devxlogo

Flexible and Powerful Data Binding with WPF, Part 2

Flexible and Powerful Data Binding with WPF, Part 2

ll applications are dependent on data in some form and most developers find themselves writing reams of data access code. The first article in this series discussed how to control the data binding mechanisms of WPF programmatically. Along with a programmatic interface, WPF also provides a declarative data binding interface, which is the topic of this article.

XAML Binding

It’s possible to set up a binding using only XAML. The XAML parser understands how to parse and instantiate many of the WPF framework classes, but doesn’t know how to work with non-WPF classes unless they’re registered with the parser. Therefore, the first step in using a custom data source is to associate an XML namespace with a CLR namespace. The following XAML snippet shows how to set up an xmlns “sys” namespace for the System namespace in mscorlib and a “local” namespace for a custom assembly:

   

Next, you need to create an instance of the type and store it in a discoverable location. In C#/Visual Basic, you would create a variable and instantiate the type. In XAML, you have to put the instance in a resources section. The XAML parser instantiates your tTeype and adds the instance to the resource dictionary. You provide a key for the resource so that you can retrieve the instance later:

       ABC 123 DEF 456      

Now you can set up the binding in the main part of the page and employ the Source property for the configuration. Set the Source property to refer to an instance of an object, in this case the sample string declared in the Page.Resources section shown previously. Bind the first TextBlock directly to the string and use a Path for the second TextBlock to show the second Char in the String.Chars array:

      

The next example shows comparable XAML that binds to the Employee object:

   

Relative Source Binding

It’s not necessary (or desirable) to name every element in a WPF logical tree, which can pose a problem when binding to other elements. Use the ElementName property to specify a named element as the source. There are times when you won’t know the element name but you’ll want to bind to it anyway.

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

Sometimes you just want to say; “Bind to my parent StackPanel” or “Bind to the previous item in this list.” Another example is when you want to bind to a different property of the same element. In other words, you need a way to configure the element binding without requiring an element name?and XAML provides one. Use the RelativeSource property when you need an element binding, but can’t use the ElementName property.

There are four modes available for RelativeSource bindings, but only two are relevant to this article:

  • Self: Reference any property on the current object. The next example shows some XAML that binds the TextBox Background property to the Text property on the same TextBox. Note that the example nests one markup extension within another. Run the application and try typing “Red” or “#FF336688” in the text box to see the background color change:
   
  • FindAncestor: This binding walks the tree of parent elements looking for a specific type of ancestor element. You must specify the AncestorType (a DockPanel in the following example), to create the binding successfully. Use the AncestorLevel to skip over parent levels. For example, setting AncestorLevel=2 will skip one generation. This example binds the TextBox Background attribute to its grandparent DockPanel (so it will be blue):
                      
Author’s Note: The two RelativeSource modes not relevant to this article are TemplatedParent and PreviousData.

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

Binding Direction

When you configure a binding, you can specify the direction that the data flows. A common binding direction is from the source to the target. This is implemented with the BindingMode.OneWay enumeration setting. In a OneWay binding, the target property gets updated when the binding source changes. Remember that change notification happens only if the data source object notifies WPF about the changes. See the “Change Notification” section of the first article in this series for details.

OneWay binding is common when the target is a non-editable element such as a TextBlock or Label. Because users can’t change the value, it’s not necessary for the data to flow back to the source:

   

In contrast, in a BindingMode.TwoWay binding, the target property gets updated when the source property changes, and vice versa. TwoWay binding is common when the target is an edit control such as Slider or TextBox.

If you don’t specify a value for the Mode property, the type of binding direction you get depends on the type author preferences. Each dependency property has a FrameworkPropertyMetadataOptions enumeration flag set. If you’ve set the BindsTwoWayByDefault value then the default is TwoWay, otherwise it will be OneWay. For a TextBox, the default Mode is TwoWay, but most other controls are OneWay by default.

In a BindingMode.OneTime binding, the target property gets updated once, when the system establishes the binding. The WPF dependency property system disconnects the binding after it sets the initial value, and the system does not forward further changes. Usually you’ll use this mode to reduce overhead when you know the value won’t change in the source property.

In a BindingMode.OneWayToSource binding the data flows from the target property to the source property. This is another single-direction binding; no data flows back to the target. OneWayToSource exists to solve one specific problem in the WPF binding world. Data target properties are always dependency properties. There are no exceptions to this rule.

Occasionally you’ll run across an element that has a property that you want to use as a target, but you discover that it wasn’t implemented as a dependency property. The Run element is a classic example of this problem. A Run is a subsection of a FlowDocument. Run elements let you break a big document into manageable sections and apply formatting, or modify the Run text from your code. Here’s an example:

                                   Intro Text                         Lorem Ipsum                        

In the code behind, suppose you change the Run as shown here.

   // C#:   changeMeRun.Text =      DateTime.Now.ToLongTimeString();   ' VB   changeMeRun.Text = _     DateTime.Now.ToLongTimeString()

Now imagine that you want to bind a TextBox to the Run element so that it updates when you change the text programmatically. Sorry?you can’t do that with a OneWay or TwoWay binding. Because Run.Text is not a dependency property, you’ll have to use a OneWayToSource binding instead. Here’s some XAML that demonstrates this binding:

   

Binding Updates

TwoWay bindings exist for a good reason?to push modified data back to the data source. But in certain application scenarios, it’s not wise to update the data immediately. For example, perhaps you have a slow connection back to the data server, and your design documents state that the changes should be batched. For these situations, WPF provides the UpdateSourceTrigger enumeration, which controls the immediacy of the update, granting you better control over the trigger condition. Setting the UpdateSourceTrigger property to PropertyChanged provides immediate gratification and causes the framework to update the data source whenever the user changes the target property:

   

But setting the UpdateSourceTrigger property to a different event, such as LostFocus, delays the update until the focus leaves the bound control. Setting the UpdateSourceTrigger to Explicit divorces it from any event, and forces you to write some code to update the source. The following code shows how to update an Explicit binding.

   // C#:   private void UpdateButton_Click(object sender,       System.Windows.RoutedEventArgs e)   {      // get the elements binding expression      BindingExpression be =         TextBox3.GetBindingExpression(TextBox.         TextProperty);      // update the bound source      be.UpdateSource();   }   ' VB   Private Sub UpdateButton_Click( _      ByVal sender As Object, _      ByVal e As RoutedEventArgs)         ' get the elements binding expression      Dim be As BindingExpression = _        TextBox3.GetBindingExpression( _        TextBox.TextProperty)      ' update the bound source      be.UpdateSource()   End Sub

For most elements the default UpdateSourceTrigger behavior is to update the data source when the target control data is changed. The TextBox control is one of the few elements that updates its source data on the LostFocus event.

Binding Errors

Setting up a binding requires getting the settings and properties correct in your XAML. One simple typo can turn your intended binding into an unfathomable mess for the binding class, which begs the question: What happens if you have an invalid binding configuration?

Instead of throwing an exception and bringing your application to a screeching halt, WPF ignores the error, displays the rest of your UI correctly, and quietly dumps the error information to the trace listener.

At first glance this seems an unreasonable choice by Microsoft, but further examination shows that it’s not so impractical after all. Often a designer sets the binding in XAML using Expression Blend long before there’s a valid data context in place for the binding. Working in an environment that is throwing exceptions while configuring the XAML would be a joyless experience.

As an example, the following XAML is invalid because I’ve misspelled the FirstName property:

   

The typo causes a binding error that produces the following information in the Visual Studio output window:

   System.Windows.Data Error: 35:   BindingExpression path error: 'FirstNam' property      not found on 'object' ''Employee' (HashCode=18307385)'.    BindingExpression:Path=FirstNam;    DataItem='Employee' (HashCode=18307385); target      element is 'TextBlock' (Name=''); target property     is 'Text' (type 'String')

If you want more control over the trace process, you can set up a custom TraceOutput, which lets you dump trace output to external files, filter WPF namespaces for reporting, and control message verbosity flags.

Even if you configure your binding correctly, you can still end up with problems. You could have a valid binding, but somehow get incorrect data shown in the data target. WPF provides the PresentationTraceSources.TraceLevel attached property (introduced in .NET 3.5) to help in this situation. Enabling the TraceLevel switch causes WPF to dump detailed debugging information to the trace window. The first step in using this property is to add the correct xmlns attribute to your XAML:

   

Next, use the attached property as shown below:

   

Check out Listing 1 for an example of the debug trace that is sent to the Output window.

Validation

The potential for getting corrupt input data is higher when implementing a TwoWay binding. Good UI design attempts to reduce input errors by narrowing input choices and guiding the user through a well-defined set of controls. The accumulation of experience in UI design has shown that limiting the user to a small set of items in a ListBox is a better option than allowing the user to input ad hoc text. Fortunately, the WPF binding engine implements a validation system that simplifies the validation process.

A reasonable separation of concerns for validation looks like this:

  • The business class provides the validation rules and enforces them.
  • The UI provides feedback to the user supplying visual cues indicating which controls are invalid. If the business class offers explanatory information regarding the error, the UI should also show these details to the end user. For a thorough discussion of object binding in Windows Forms, see Deborah Kurata's Mar/Apr 2006 CoDe Magazine article "Object Binding Tips and Tricks."

Business Class Validation

Deciding when you should validate an object is a subject open to frequent debate. Experts all agree that a business object must validate any inbound data and notify the client code when the data is unacceptable. Where they disagree is in how and when to do this. There is one school that states: "Always throw an exception when incoming data is invalid." While this rule is simple to understand it doesn't always fit well in data-binding or multi-tier application scenarios. There are lots of alternate architecture choices, but for this article I'll look at only two: the "Throw Exception" implementation, and the IDataErrorInfo implementation.

The Throw Exception model is so simple it doesn't require much explanation. If the incoming property value is invalid, throw an exception. That's really all there is to this model.

The IDataErrorInfo model requires only slightly more work. The IDataErrorInfo interface has been part of .NET for years. Originally intended to return a list of errors to a Windows Forms UI. WPF 3.5 added support for this interface so you can use it to report error details to your WPF UI too. The IDataErrorInfo interface is simple to implement. Create a read-only Error property that returns an error string and an Indexer that returns a specific error by name.

   // C#:   string IDataErrorInfo.Error   {      get{         return _errorString;      }   }      string IDataErrorInfo.this[string propertyName]   {      get{         return (string)_errorDetails[propertyName];      }   }

In Visual Basic:

   Public ReadOnly Property [Error]() As String _      Implements IDataErrorInfo.Error      Get         Return _errorString      End Get   End Property      Default Public ReadOnly Property Item _      (ByVal propertyName As String) As String _      Implements IDataErrorInfo.Item      Get         Return _errorDetails(propertyName)      End Get   End Property

You can see the full implementation of the IDataErrorInfo interface in Listing 2.

Validation Feedback in the UI

It's not enough for the business class to reject the data. You have to inform your users of the problem and show the errors in the user interface too. Once again Microsoft provides the answer with two properties on the Binding class: ValidatesOnExceptions and ValidatesOnDataErrors.

When you set ValidatesOnExceptions to true, WPF monitors any attempt to update the source property, and when an exception is thrown, WPF draws a red border over the outline of the offending bound element. WPF draws the error border in a special area, called the Adorner layer, which is the last layer drawn (highest Z-Order) by the WPF renderer. Therefore the red border (or your custom error template if you have one), is always superimposed on the normal UI:

?
Figure 1. Red Exception Border Example: The default validation in WPF shows a simple red border.
   

In Figure 1 you can see what the Red Exception Border looks like in the UI.

The Red Border is simple?but very plain. Admit it. You were thinking that the Red Border was lame. You've seen the ASP.NET or Windows Forms error providers and you know that there are better looking error icons available. Even though WPF provides only this simple error template, you're not stuck with it. The WPF UI is based on a templating model, so you can transform any UI control instantly to a radical new appearance using a template. That's the solution to the red border crisis; create a replacement UI with an error template.

Build an Error Template

You typically define control templates as part of a style. For Error Templates, the template is part of the Validation.ErrorTemplate setter. The following XAML shows the first few lines of the style. The style applies only to text boxes; within the Validation.ErrorTemplate setter there is a ControlTemplate section:

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist