New Line of Business Features in Silverlight 5–Ancestor RelativeSource Binding
This is the second post in a series covering new line of business features in Silverlight 5.
- Implicit Data Templates
- Ancestor RelativeSource Binding
- Debugging Bindings in XAML
- Elevated Trust Enhancements
- Using pInvoke
- Running with Elevated Trust In-Browser
- Creating Multiple Windows in Out-of-Browser Applications
- Vector Printing
- Custom Markup Extensions
In this post I’ll provide an overview of the new ancestor relativesource binding functionality which simplifies the process of binding child properties to parent (or ancestor) properties.
Ancestor Relative Source Binding
If you've ever wanted to bind a child object property to a parent object property but realized you couldn't do it easily in XAML then you'll be happy to know that Silverlight 5 adds support for ancestor relativesource binding. This new type of binding allows a child to bind to a parent (or an ancestor higher-up in the hierarchy) using the new RelativeSource binding. Additional properties such as AncestorType and AncestorLevel can also be used along with RelativeSource to determine the type of parent object to look for as well as how many levels up it is from the child.
RelativeSource binding is especially useful when working with data templates where a child in the template needs access to data exposed by the parent. Listing 1 shows an example of how ancestor relativesourece bindings can be used.
<navigation:Page xmlns:types="clr-namespace:Silverlight5Features.Model"> <navigation:Page.Resources> <types:AncestorBindingViewModel x:Key="ViewModel" /> <DataTemplate DataType="types:Person"> <StackPanel Orientation="Horizontal"> <ComboBox ItemsSource="{Binding DataContext.States, RelativeSource={RelativeSource AncestorType=ListBox,AncestorLevel=1}}" Width="100" Height="20" /> <TextBlock Margin="5,0,0,0" Text="{Binding LastName}" /> </StackPanel> </DataTemplate> </navigation:Page.Resources> <Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource ViewModel}}"> <ListBox ItemsSource="{Binding People}" /> </Grid> </navigation:Page>
Listing 1. Using the RelativeSource binding to allow a child to access the value of a parent's property.
Looking at the code in Listing 1 you'll notice that the data template contains a ComboBox that is used to show states and a TextBlock control that handles rendering the LastName property of a Person type. The problem is that at runtime the ComboBox in the data template will only have access to the Person object's properties (since the Person object becomes the data context as each item of the Listbox is bound). That's a problem since the ComboBox in the data template needs to display states.
To allow the ComboBox control's ItemSource property to bind to the parent control's DataContext.States property (the view model’s States property in other words – see Listing 2 for a simple example), an ancestor relativesource binding is used. This is done by setting the RelativeSource property of the binding to {RelativeSource AncestorType=ListBox, AncestorLevel=1}. As the ComboBox is bound, it'll get its ItemsSource value from the ListBox control's DataContext and then navigate to the States property.
public class AncestorBindingViewModel : INotifyPropertyChanged { private List<string> _States; private List<Person> _People; public List<string> States { get { return _States; } set { if (value != _States) { _States = value; OnPropertyChanged("States"); } } } public List<Person> People { get { return _People; } set { if (value != _People) { _People = value; OnPropertyChanged("People"); } } } public AncestorBindingViewModel() { States = new List<string> {"Arizona", "Utah", "California"}; People = new List<Person> { new Person {FirstName = "John", LastName = "Doe"}, new Person {FirstName = "Jane", LastName = "Doe"} }; } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string prop) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(prop)); } } }
Listing 2. The AncesorBindingViewModel code. This shows a simple example of creating a view model class.
Although there are other workarounds to this type of binding challenge in XAML, ancestor relativesource binding adds important functionality that has been missing in previous releases of Silverlight.