Silverlight 5 Tidbits–Data binding
Published on Tuesday, April 26, 2011 12:30:00 PM UTC in Programming
Edit 2011-12-11: This article is compatible with the final version of Silverlight 5 (5.0.61118.0).
This post is part of a mini series about Silverlight 5:
- Implicit Data Templates
- Multi-Column Layouts
- Markup Extensions
- Multiple Windows
- Trusted Applications
- Incremental Search
- Data binding
We've seen one of the new features regarding data binding in the first post of this series already: implicit data templates. But wait, there's more in the box! This post looks at some of the minor (yet still important) improvements in Silverlight's data binding features as well as what will change in the tools.
The situation so far
Although providing a pretty powerful binding engine, Silverlight up to version 4 unfortunately is missing some of the more sophisticated features of WPF. Especially if you're into certain patterns like MVVM the lack of some features can be a painful thing to work around, and all of a sudden you find yourself writing loads of code just to avoid that last bit of code behind.
Another painful limitation you most likely ran into already as a Silverlight developer is when your data bindings are not working. You get some clues from the output window in Visual Studio, but at the moment there is no way to debug binding errors gracefully.
What's new in Silverlight 5
The next version of Silverlight will both add missing features to the data binding engine as well as dramatically improve tooling support for it. The former will bring Silverlight on par with WPF in some details, and surprisingly the latter one is a detail where it outruns its bigger brother and offers a new level of comfort not even WPF developers are used to.
FindAncestor binding
The problem around this missing relative source binding mode often involves items controls and data templates. Let's say your view model exposes a collection of items that is used to bind the "ItemsSource" property of your items control. Then you're often in the situation that you want to e.g. bind a command that is exposed on your view model in the data template for the items (think "Delete" button). This is a real problem because there is no easy, built-in way from the data context of the item back to the context of the items control, so people like Colin Eberhardt and Kiener have come up with clever ways to work around this limitation.
In Silverlight 5, the FindAncestor mode that was missing until now has been added for this. To demonstrate how it works let's create a simple view model and data item:
public class MyViewModel
{
public ObservableCollection<Item> Items
{
get;
private set;
}
public RelayCommand<Item> DeleteCommand
{
get;
private set;
}
public MyViewModel()
{
DeleteCommand = new RelayCommand<Item>(item =>
MessageBox.Show("Deleted item: " + item.Name));
Items = new ObservableCollection<Item>
{
new Item("First"),
new Item("Second"),
new Item("Third")
};
}
}
public class Item
{
public string Name
{
get;
set;
}
public Item(string name)
{
Name = name;
}
}
This is exactly the setup we've just talked about: a collection of data items and a command exposed on the view model. Here is the corresponding XAML part that will work in Silverlight 5:
<ListBox ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Content="Delete"
Command="{Binding DataContext.DeleteCommand,
RelativeSource={RelativeSource FindAncestor,
AncestorType=ListBox,
AncestorLevel=1}}"
CommandParameter="{Binding}"/>
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
What happens here? The list box binds its "ItemsSource" property to the exposed collection of items on the view model. That means the data context of the list box actually is the view model.
In the data template for the items, we use both the item's data context (to bind the command parameter as well as the "Text" property of the text block) as well as the new FindAncestor mode to go back from the button to the nearest list box, and grab "DataContext.DeleteCommand" to bind the button's "Command" property.
The above sample syntax can be further simplified, because I included some superfluous elements to demonstrate the complete syntax. For example, as soon as you specify either "AncestorType" or "AncestorLevel", the mode is implicitly set to "FindAncestor", so you can omit that. In addition the "AncestorLevel" enables you to skip elements in the tree when the runtime searches for the target, which is helpful if you have nested elements of the same type and want to bind to an outer one. A value of 1 like here just says: use the nearest list box up in the tree you can find, which is the default behavior anyway. So a simplified version of the binding would be:
Command="{Binding DataContext.DeleteCommand,
RelativeSource={RelativeSource AncestorType=ListBox}}"
Binding of style setters
This is another frequently requested feature in the Silverlight forums that has been missing so far, and once again there have been workarounds to add a similar feature to Silverlight, for example this one by David Anson.
Silverlight 5 now natively supports this feature. The following example shows a style for list box items that binds the background color to an "IsSelected" property of the data context; in addition it uses a value converter to convert this property value to a brush, to demonstrate that this also is supported.
<UserControl.Resources>
<local:SelectionToBrushConverter x:Key="SelectionToBrushConverter" />
<Style x:Key="ListBoxItemStyle"
TargetType="ListBoxItem">
<Setter Property="Background"
Value="{Binding IsSelected,
Converter={StaticResource SelectionToBrushConverter}}" />
</Style>
</UserControl.Resources>
Binding to dynamic types
This is a really cool new feature that allows binding of types which are not known at design time, for example in scenarios where you have highly dynamic data (the documentation lists Xml data with a schema that is not known until runtime as an example).
Instead of creating an own example, let me point you to a blog post by David Cathue who happened to write about this feature just a few hours ago here.
Debugging data binding
This is a truly great new feature in Silverlight 5. Let's simply jump right into a sample to see how it works. Let's assume for a moment that in the style setter sample above I had set up the binding for a path of "Selected" instead of the actual property name which is "IsSelected". When I ran my application now I would see that the background color of my list box items is not adjusted correctly, but I wouldn't receive any errors either.
Experienced developers will now start looking for errors in the output window of Visual Studio. In cases like these the problem will be pretty easy to track down, and the error message is really helpful. But in more complex scenarios this can quickly turn into a tedious trial and error session where you start to place random break points in your data items or write debug output statements. Luckily, in Silverlight 5 this situation changes for the better:
Wait a minute... did Visual Studio just break on a break point in XAML? That's right! You can now set break points on your bindings and inspect the involved binding objects closely. Just bring up the "Locals" window and you will see the "BindingState":
There are lots of interesting things to see here. For example, "FinalSource" contains the element that ultimately was used as source in the binding. Inspecting this node will fairly quickly help realize the wrong name of the property was the problem. The "Binding" node lets you analyze if the binding is indeed set up as you wanted it to be, and the "BindingExpression" can be used to drill down to the target element involved in the binding, in this case the list box item. Finally, the "LastCompletedStage" and "Action" show you what the binding engine currently wants to do.
Summary
The community has been very active over the last years and created alternatives for most of the missing features in Silverlight. But it's still good to see that every new release closes some of the gaps and makes using these workarounds unnecessary.
The debugging of data bindings on the other hand is completely new and something that I think will result in another productivity boost in debugging situations. A lot of people complain about XAML and how some of the usual features (e.g. regarding Intellisense) is not on par with "normal" code, so it's really cool to see that these aspects of modern programming also start to catch up.
Tags: Silverlight · Silverlight 5