Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
Markdown Monster - The Markdown Editor for Windows

Fumblings and Thoughts on WPF DataBinding


:P
On this page:

So I spent the evening today experimenting with databinding in WPF. Overall I'm very excited about that data binding capabilities in WPF which finally has produced a powerful and reasonably user friendly way to do data binding of just about any type to anything else. WPF makes this easily available courtesy of dependency properties which can automatically manage their change tracking and two-way updating of bindings.

FWIW, what I'm writing about here is sort of along the 101 getting started path but I like to put my thoughts down in my own words  and document my experiments steps and mistakes mostly for myself. This sort of thing always helps me understand technology a little better as well as helping me dig a little deeper than I would if were just to move on - after all I'm not doing anything real with WPF at the moment - it's all for learning experience. As it turns out there's lots of good WPF content out on the Web and so the good news is that when one gets stuck (frequently) help is often only a search or two away.  Anyway, just thought I'd give a disclaimer for my fumbling here <s>..

Dependency Properties

One of the first "huh?" moments with WPF for me was dependency properties. Dependency properties are STATIC properties of a type that provide the basis for what normally is considered proeprties. A static property for dynamic values? Yup; rather than exposing properties directly, WPF 'publishes' properties and stores the data internally in it's own optimized format. The reason for is to save resources. WPF classes have a boatload of properties and most will never get set so the static default values are used. But it's also that it allows for properties to provide an update notification mechanism - when a dependency property is updated its INotifyPropertyChanged interface members are called which allows other objects to attach to change notifications.  It's pretty cool to see to bind one property (say the width of the form) to a another property (say a TextBox text value) and see the data updated as the form is resized without having to attach any objects or write any code. It just works. This makes it possible to do some pretty cool things without writing any code as you can simply hook up databindings between two objects and have changing one automatically affect the other. This is an important concept in WPF and it internally uses this mechanism for many operations.

Behind the scenes dependency properties require a bit of hookup to implement if you end up creating your own: You have a static property for the actual dependency property, and registration code that bascially registers the dependency property with the type. Finally you generally implement an actual property that wrapps the appropriate GetValue() and SetValue() methods used to get and set the dependency property. For the developer consumer, this is mostly transparent - the properties act just like other properties, except having to know what's a dependency property and what's not <s>... and implementing dependency properties is probably mostly left to designing custom controls or specific UI based components.

DataBinding

The syntax for databindings is a bit obtuse at first, but after getting used to the syntax it's all fairly logical. Databinding works in a number of different scenarios - both for simple databinding which basically binds a single value to a single other value, such as binding a textbox text field to a value from a data source (Datafile, XML or object). Or there's binding support for data context binding which is essentially list binding where typically a parent control like a list control of some sort binds its ItemsSource property to a datasource and then binds the actual values via a DataTemplate. DataTemplates are very similar to ASP.NET item templates where you can layout for each item in the list. The individual items then bind typically to a data path in the context - a field, an XPATH expression or an object property (yay!!!).

As I mentioned the syntax isn't super intuitive and takes some getting used to but it works reasonably well.

For example to bind the Width property to a fixed control of the page you might do something like this:

Width="{Binding ElementName=lstPhotos, Path=ActualWidth}"

This is essentially binding two dependency properties together so if the width of the list changes so does the width of the inner child (actually this is kind of a contrived example, since that's easily achieved automatically, but it demonstrates the point).

You can also bind simple values like a plain .NET object:

<TextBox Text="{Binding ElementName=Customer, Path=LastName}" />

This binds a Customer object - which is a plain CLR object - and bind to its lastname property. But unless the Customer object implements INotifyPropertyChanged the binding is only one way - if it does the binding automatically is two-way and changes in the TextBox automatically go back to the LastName field. INotifyChange is

There is also support for ValidationRules that can be applied - both in code and via XAML, but this process unfortunately is a bit more involved. I'll come back to customization aspects in a minute.

List Binding

The above is simple databinding which is basically binding a property to a datasource's path. Above the ElementName used is explicit and points at the data source directly. List binding extends this mechanism by providing a listbased ItemsSource property to which a datasource can be bound, and then using DataTemplates that can reference the individual data items. ItemsSource essentially creates an underlying DefaultDataView which is a collection that provides many useful features like current item positioning and movement, sorting, filtering and grouping of the view. The actual individual item binding then uses Binding syntax similar to the simple databinding.

Here's an example of a List control binding photos, from a Photolist XML file from my online photo album:

 

<Window.Resources>    
   <XmlDataProvider IsAsynchronous="True" 
                 IsInitialLoadEnabled="True"                                               
                 Source="http://www.west-wind.com/rick/photoalbum/hoodriver2006/photoalbum.xml" 
                 XPath="/PhotoAlbum/Photos/Photo" x:Key="PhotoSource"/>
</Window.Resources>
...<ListBox x:Name="lstPhotos" ItemsSource="{Binding Source={StaticResource PhotoSource},IsAsync=True }">
  <ListBox.ItemTemplate>
      <DataTemplate>
          <StackPanel Margin="0,5,0,5">
              <StackPanel Orientation="Horizontal"  VerticalAlignment="Top">                        
                 <Image Source="{Binding XPath=ImageUrl}" Width="150"/> 
<TextBlock Text="{Binding XPath=Notes}" TextWrapping="Wrap" Width="200" Margin="10,0,0,0" /> </StackPanel> </StackPanel> </DataTemplate> </ListView.ItemTemplate> </ListBox>

Here the binding is associated with a data source - in this case an XML document from my Web Server. The data source is bound using XPath binding and referencing and the datatemplate then simply references individual Xml nodes in the XPATH document. As you can see the individual bindings work the same way as the individual simple bindings worked before - except here the path is pointing at the data source.

There are a few really nice features built into this kind of databinding mechanism: The data can be loaded asynchronously which is great for data coming from the Web and it's fully automatic. No code to make this happen - the data source manages both the async retrieval of the data and async updating of the data source. You can also fire events that can provide feedback on the load operation but it takes a little more work to do this.

If you're binding to ICollection based components you get more control yet.The data source for those data sources are exposed through a view that has the notion of a current item, navigation and filtering. Any data source is assigned a view (ICollectionView based typically) and this view provides the actual navigation and support features. This is very nice because you can with very little effort filter and reorder the view without having to actually change the underlying data source! And again most of this can actually be accomplished without code in XAML or with literally a few lines of code in CodeBehind.

I've had a bit of a hard time (still do actually) understanding how data sources are mapped to bindings. There are StaticResource bindings, there are bindings based on ElementNames (ie. any named control or item in the XAML document) and there are explicit DataContext assignments. In order to assign a plain property defined in CodeBehind the only way I could get that to work is by assigning it explicitly to the data context:

public PhotoViewer()
{
    
    InitializeComponent();

    this.CustomerRef.Company = "East Wind";
    this.DataContext = this.CustomerRef;

}

At that point the CustomerRef object can be used for databinding just by specifying a path anywhere:

<Button Click="btnClick" Content="{Binding  Path=Company}">               

You can also declare object references as resources:

<WPFAnimation:Customer x:Key="CustomerRef"   />

which actually creates an instance of the class. This works fine if you only want to reference the object in XAML, but makes it more cumbersome if you need to access it in codebehind:

CustomerRef cust = this.FindName("CustomerRef") as Customer;

The really tricky part with all these permutations is to find the one that will work with your bindings... <s>

The simple Stuff is super easy - non-standard or even slighlty uncommon Scenarios: not so much!

Ok, so WPF addresses many basic databinding scenarios very well. And by basic I don't mean basic in terms of simplistic but more in terms of straight ahead binding: Taking a direct unmodified value and binding it to another property for example. If on the other hand you need to format that value in some way that the WPF designers haven't provided support for the story changes pretty quickly.

The simple List example, is a good one. The XML for that list contains an image file name, rather than an Image path for example. In my first pass to get this example to work for my self I found myself modifying the XML file to put a full image url into the XML. But the actual data I'm working with comes from my Web site and can't be changed quite so easily. So rather than a fully qualified URL it contains only a filename. The URL is known by the calling application, but it's not exposed as part of the data source.

So it's not trivial to build a composite databinding value. There's no explicit way to specify combine say the base path of a property on the form PLUS the XPath value of the filename.

<Image Source="{Binding XPath=FileName }" Width="150"/>

To be honest I'm not sure what that could possibly look like IF it was possible, but possibly special FormatStringConverter would be handy. Doesn't exist and I don't think it would work in this scenario anyway.

So the workaround for this is not difficult but requires creating a custom ValueConverter. And registering it with the XAML. And hooking up the binding properly in the page. Before you know it you have a lot of code in a lot of places for this. For example, to do the above combination of a base path plus the file name I created a MultiValueConverter class:

 

public class ImageUrlConverter : IMultiValueConverter, IValueConverter 
{
    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {            
       // *** Hard coded originally for testing - not used        return "http://www.west-wind.com/rick/photoalbum/hoodriver2006/" + (string)value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new Exception("The method or operation is not implemented.");
    }

    #endregion

    #region IMultiValueConverter Members

    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        string Url = (string)values[1] + "tb_" + (string)values[0];

        if (targetType == typeof(string))
            return Url;
            
        return new ImageSourceConverter().ConvertFromString(Url);
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new Exception("The method or operation is not implemented.");
    }

    #endregion
}

The implementation is pretty simple to be sure. The first is a single value conversion and it's basically passed a single value and the expected result type. The multi-value version passes in an array of values.

What's not so nice is all the syntax required in XAML to actually use the converter. First in the Resources section:

<Window.Resources>
<WPFAnimation:ImageUrlConverter x:Key="ImageUrlConverter" />
</Window.Resources>

I also had to register the WPFAnimation namespace with the page:

<Window x:Class="WPFAnimation.PhotoViewer"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:WPFAnimation="clr-namespace:WPFAnimation" 
    Title="PhotoViewer" Height="700" Width="500" 
    x:Name="frmPhotoViewer"
    Loaded="Window_Loaded"
    >

To actually use the converter the following code is used inside fo the ListBox - it replaces the <Image> tag from the sample above.

<Image Stretch="Uniform" Width="150" HorizontalAlignment="Left" x:Name="imgImage">
  <Image.Source>
      <MultiBinding Converter="{StaticResource ImageUrlConverter}">
          <Binding Mode="OneWay" XPath="FileName" />                                                                        
          <Binding ElementName="frmPhotoViewer"  Path="BasePath" />
      </MultiBinding>
  </Image.Source>                    
</Image>

So this binding basically creates two binding values and passes them as parameters which are then picked up by the value converter. Note that you can see both XPath binding and binding to a simple property of the Window object to get those two values.

Phew, it works (after many false starts) but it's not exactly what I call intuitive.

DataBinding Errors and Validation Rules

One thing that seems quite odd with databinding is that if databinding fails - a value can't be bound or even if the data source is pointing at an invalid object altoghether, no exceptions are raised. In fact, there's no automatic way to capture any error information.

IMPORTANT: When you're debugging this is a real pain as you get no indication of why something failed: Either the app crashes with a generic error which has no information regarding what went wrong (Target of an Invokation Reflection error) or the binding simply doesn't work with no indication of what went wrong.

For debugging there's a little thing you can easily miss:

Binding error information is displayed in Debug mode in Visual Studio and dumped to the Output Window.

This is easily the best piece of information you can have in debugging binding errors. Unfortunately it's not quite intuitive and I stumbled upon this by accident yesterday after cursing the process. <s> I'm still cursing because the whole process of capturing binding errors and even validation errors is very messy and convoluted.

If you need to trap error information on binding results you can implement custom ValidationRules. Like the ValueConverters these things are separate classes that you need to create and they require the same sort of registration in the XAML page and then hooking up in XAML code. This gives you fine grained control, but it's not very user friendly to set up and deal with. The validation rules are basically fired before values are bound back but that still doesn't help if there's some sort of formatting error that makes the  You get decent control but it's very low level and requires lots of plumbing to get hooked up. For an excellent discussion of this topic check out Paul Stovell's CodeProject article.

Unless I'm missing something though all of the discussion I've seen deals with manual validation errors which is fine - in fact that can potentially be handled differently in the business layer, but the part I didn't see is how to show error information for internal binding errors that WPF generates when doing the value conversions - say you type a non-numeric into a text box bound to an integer for example. <shrug> This one will require some more research.

Mixed Bag

Clearly a lot of thought went into the databinding mechanics of WPF and there's a lot of stuff to really like in it. But there are a number things that could be a lot easier from the markup perspective or if not that at least be vastly improved by providing a bunch of default behaviors for many common scenarios. I'd think that a more generic data formatting mechansim - even if it's only one way has to be a vast improvement over having to create ValueConverters for even many simple conversions. Making validation and error handling easier certainly would be nice preferrably with some mechanism to fire aggregated error information to a unified method somewhere so that the client code can decide what to do with it or whether to pass it on to a business layer. This seems like a real nightmare to do right now.

It's interesting stuff to be sure, but a tedious affair to wade through <s>...

As so many things with WPF it's not so much the logical hurdles it's the conceptual ones. Over the last week I've been looking at a ton of WPF code from other people and different approaches to handling layout and trigger mechanisms and yeah I feel like I'm starting all over at CS 101 (or CDS 101) <s>...

Posted in .NET  WPF  

The Voices of Reason


 

# DotNetSlackers: Fumblings and Thoughts on WPF DataBinding


Rick Strahl's Web Log
July 16, 2007

# Rick Strahl's Web Log


Nathan
June 03, 2008

# re: Fumblings and Thoughts on WPF DataBinding

I think it should be </ListBox.ItemTemplate> not </ListView.ItemTemplate>

Mark Nijhof
July 12, 2008

# re: Fumblings and Thoughts on WPF DataBinding

Hi Rick,

First of all thanks for your post about databinding, very helpfull.

I found a way to get the databinding to work when saving a FlowDocument to XPS. I created a post on my blog about it, you may find it interesting: http://blog.fohjin.com/2008/07/saving-wpf-xaml-flowdocument-to-xps.html
If in the meantime you found another/better way of doing this I am all ears :)

-Mark

Simon-john Roberts
March 26, 2010

# re: Fumblings and Thoughts on WPF DataBinding

How time flies. I remember when this was fresh, Can it really be two years?

Thanks again for the heads up on Binding.

And seriously? I loved your link to "Underwear goes inside the pants" http://is.gd/aX7Ua.

Surfs up!

Jayant Kulkarni
April 13, 2010

# re: Fumblings and Thoughts on WPF DataBinding

I would like to know when data templates are initialized? While debugging, I observed that data templates are not initialized in the InitializeComponent call. This is causing the binding failure in my code when I attach event handler from the xaml. The issue gets fixed when the event handler is attached from code behind.
To elaborate, I have a textbox in the user control and in the xaml, I am attaching event handler to the text_changed event. This works fine with proper data binding till the user control is outside the data template. As soon as I place this control in data template the binding fails. When I attach event handler from the code behind it works well.
I observed that when control is in data template, the constructor of the control is called from some external code. When the control is not in data template, the constructor of the control is called from the constructor of the container i.e. window.
Does this mean that data binding happens when constructor is executed and when any event is raised the binding breaks.

Any clues?

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024