WPF DataContext and CurrentItem
DataBinding is one of the technologies I like the most.
Winforms did a fine job introducing the concept, but it was pretty easy to be stuck when trying to perform complex databinding. .NET 2.0 brought the concept of BindingSource, which eased the management of multiple "Current items" on a single form. You might have encountered this when creating multiple master/detail views on the same form. That was the case when you wanted to walk through a set of tables through their relations, say down to three levels.
WPF has a far more advanced DataBinding engine and introduces the concept of DataContext. There's the notion of a hierarchy of DataContexts and a databound control uses the nearest DataContext available.
An example is better than a thousand words. Here's a data source :
[code:xml]
<xmldataprovider x:key="ops" xpath="/data/level1">
<x:XData>
<data xmlns="">
<level1 name="1">
<level2 name="1-1">
<level3 name="test">Some Value</level3>
<level3>Yet an other value from level 3</level3>
</level2>
<level2 name="1-2">
<level3>Some other Value</level3>
<level3>Yet an other Value</level3>
</level2>
</level1>
<level1 name="2">
<level2 name="2-1">
<level3>Some Value</level3>
<level3>Yet an other value from level 3</level3>
</level2>
<level2 name="2-2">
<level3>Some other Value</level3>
<level3>Yet an other Value</level3>
</level2>
</level1>
</data>
</x:XData>
</xmldataprovider>[/code]
It's a three level hierarchy, and I want to display this data, by recursively selecting each level in a ListBox to see its content.
Now, let's bind this data to a list box, contained in a GroupBox to be a bit more readable :
[code:xml]
<GroupBox Header="Level 1" DataContext="{Binding Source={StaticResource ops}}">
<ListBox ItemsSource="{Binding}"
DisplayMemberPath="@name"
IsSynchronizedWithCurrentItem="True" />
</GroupBox>
[/code]
This performs a binding to the attribute "name" of the level1 node list. The IsSynchronizedWithCurrentItem tells the listbox to set the CurrentItem of the current DataContext, which can be used to fill the next ListBox for the level.
Now, to add a new DataContext level, let's add a new GroupBox, and a stack panel to have a nice layout :
[code:xml]<GroupBox Header="Level 1"
DataContext="{Binding Source={StaticResource ops}}">
<StackPanel Orientation="Horizontal">
<ListBox ItemsSource="{Binding}"
DisplayMemberPath="@name"
IsSynchronizedWithCurrentItem="True" />
<GroupBox Header="Level2"
DataContext="{Binding Path=CurrentItem}">
<ListBox ItemsSource="{Binding}"
DisplayMemberPath="@name"
IsSynchronizedWithCurrentItem="True" />
</GroupBox>
</StackPanel>
</GroupBox>
[/code]
Now, there is a new DataContext for any children of the second group box, and is having the CurrentItem of the upper DataContext as a root. This is fairly easy to do, so let's do this for the final level.
[code:xml]
<GroupBox Header="Level 1"
DataContext="{Binding Source={StaticResource ops}}">
<StackPanel Orientation="Horizontal">
<ListBox ItemsSource="{Binding}"
DisplayMemberPath="@name"
IsSynchronizedWithCurrentItem="True" />
<GroupBox Header="Level2"
DataContext="{Binding Path=CurrentItem}">
<StackPanel Orientation="Horizontal">
<ListBox ItemsSource="{Binding}"
DisplayMemberPath="@name"
IsSynchronizedWithCurrentItem="True" />
<GroupBox Header="Level3"
DataContext="{Binding Path=CurrentItem}">
<StackPanel Orientation="Horizontal">
<ListBox ItemsSource="{Binding}"
IsSynchronizedWithCurrentItem="True" />
<Label Content="{Binding Path=CurrentItem}" />
</StackPanel>
</GroupBox>
</StackPanel>
</GroupBox>
</StackPanel>
</GroupBox>
[/code]
Each time a new DataContext is set, a new CurrentItem is created. That kind of behavior was hard to reproduce using DataBinding with WinForms; WPF allows it only by using a simple declarative syntax. Easy and powerful.
Also there is a fine feature that came up when using this DataContext and CurrentItem : The CurrentItem "chain" for a specific path is -- when the data source does not change -- kept if you change the selection, and come back to that particular path. Pretty interesting.
Here is a working xaml sample of this post.
Did I say that a really like WPF already ? :)