WPF databinding trick (part 1)

The last week, one of my colleague was doing a WPF training session and she ended up having a very strange behavior with the WPF databinding engine. I’ll describe a not well-known behavior of the WPF databinding engine and I’ll try to explain how its works under the scene.

Binding the Text property of a TextBlock to a collection of objects

This is something you’re not supposed to do, but you’ll see it actually works in an unexpected way. In the code-behind of our Window, we have the following C# code:

?View Code CSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
 
        Person person1 = new Person { Age = 45 };
        Person person2 = new Person { Age = 63 };
        Person person3 = new Person { Age = 71 };
 
        this.DataContext = new List<Person> { person1, person2, person3 };
    }
}
 
public class Person
{
    public int Age
    {
        get;
        set;
    }
}

The associated XAML looks like the following:

1
2
3
4
5
6
7
8
9
<Window x:Class="WpfApplicationDataContext.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        Title="MainWindow" Height="350" Width="525">
 
    <StackPanel>
        <TextBlock Margin="10" Text="{Binding Age}"/>
    </StackPanel>
</Window>

Here we’re doing something not usual: we’re binding the Text property of the TextBlock (which is of type string) to a collection of objects.

What do you think the Window will display ?

The first time I saw this code, I thought the Window will remain empty. The DataContext is set to a List and we’re trying to find an “Age” property on this collection: THAT CANNOT WORK.

Actually, it does…

What’s happening behind the scene ?

Let’s take a deep breath a take a look at what’s happening behind the scene to make this works.

1. A CollectionView is created

The first thing I was thinking while talking about the problem with my colleague was the use of a collection view. As you probably now, every time you create a binding to a collection property, the engine creates a collection view for you. This view will serve as an intermediate layer between your source and the target to manage selection, filtering and grouping. A good introduction to collection views is available here.

What is important to know is that collection views are shared along same objects instance. It means that when you use the CollectionViewSource.GetDefaultView() method, you’re either creating a default view for the collection (if no one has requested one before) or getting the default view (if it has been created by calling the same method before you).

To make sure I was right about my hypothesis, I added the following code in the C#:

?View Code CHSARP
1
2
3
var collectionView = CollectionViewSource.GetDefaultView(this.DataContext);
collectionView.MoveCurrentToNext();
this.MouseDoubleClick += (s, e) => collectionView.MoveCurrentToNext();

And the Window is now displaying the second item of the list:

So we’re definitively dealing with a collection view. The next question was, where the value comes from, I specified “Age” as binding path…

2. The CurrentItem property of ICollectionView

If you take a look at the documentation about ICollectionView, you’ll find this property:

So it looks like the Text property of my TextBlock is databound to this CurrentItem property while I explicitly set to the Age property…

3. The magic of the PropertyPathWorker class

Using Reflector, I looked for potential types using the ICollectionView.CurrentItem property. I found an interesting class: PropertyPathWorker.In the source code of the .Net framework, this type is defines as “the workhorse for CLR binding“.

In particular, take a look at this method:

?View Code CSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private void ReplaceItem(int k, object newO, object parent)
{
    // some code...
 
    view = CollectionViewSource.GetDefaultCollectionView(parent, TreeContext);
 
    // some more code...
 
    newO = view.CurrentItem;
    if (newO != null)
    {
        GetInfo(k, newO, ref svs);
        svs.collectionView = view;
    } 
 
    // and bam ! we're now using view.CurrentItem as source for our binding
}

So we’ve a special case when we’re creating a binding with a collection view: the CurrentItem property is automatically used and merge with the specificied path. In our case, it’s like creating a binding to CurrentItem.Age.

3. And voila !

Finally we’ve a lot going on within the engine to make this works. Of course the original binding was not something we would do in normal application but it was kind of cool doing the investigations to find out why it was working ! Hope you learn something cool about the databinding engine :-)

Next week I’ll try to write a similar article about another strange behavior you might have already seen…

 

2 thoughts on “WPF databinding trick (part 1)

  1. You can also explicitly bind to the current item in a collection view by setting your binding path to “/”. This can come in handy if there’s a property name collision between the collection type and the element type. For instance, if I wanted to bind to the current item’s ‘Count’ property (instead of the collection count), I would use “{Binding Path=/Count}”

  2. Mike,

    I was aware of the “/” syntax but this is not something I’ve used a lot. What is strange to me is the fact that a collection view is created while we’re not doing a binding to a collection property (Text is of type string and not IEnumerable…).

    Thanks for the feedback !

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>