Note: these are early notes based on some initial experiments with the Silverlight 5 beta, apply a pinch of salt to what you read.
There’s another new tweak to the binding engine in Silverlight which now allows for a RelativeSource with a Mode of Ancestor which is something that existed in WPF but wasn’t there in Silverlight.
Let’s say I wanted to produce a data template for a ListBox – a simple scenario such as;
<Grid x:Name="LayoutRoot" Background="White"> <ListBox> <ListBox.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding}" FontSize="14" /> </DataTemplate> </ListBox.ItemTemplate> <ListBox.Items> <sys:String>One</sys:String> <sys:String>Two</sys:String> <sys:String>Three</sys:String> </ListBox.Items> </ListBox> </Grid>
and imagine that I want the selected ListBox item to be highlighted with a green rectangle or similar. Logically, it feels like I should be able to do something like this;
<Rectangle Visibility="{Binding IsSelected,Converter={StaticResource converter}}" Stroke="Green" StrokeThickness="1" RadiusX="3" RadiusY="3" />
but in Silverlight 4 I can’t really get anywhere near the IsSelected property of the ListBoxItem that ultimately will contain my content. Instead I need to go and visit the ItemContainerStyle of the ListBox and then re-template the ListBoxItem within there in order to make this work and it all feels a little cumbersome.
In Silverlight 5, I can get much closer. I know that this template is used in a ListBox so I know that I will end up contained in a ListBoxItem and so I should be able to make use of that as in;
<DataTemplate> <Grid> <Rectangle Visibility="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem}, Path=IsSelected, Converter={StaticResource converter}}" Stroke="Green" StrokeThickness="1" RadiusX="3" RadiusY="3" /> <TextBlock Text="{Binding}" FontSize="14" Margin="3" /> </Grid>
and that RelativeSource binding is going to searching up the hierarchy looking for a ListBoxItem and when it finds one it picks up its IsSelected property and lets me bind to it. Nice
There’s also the AncestorLevel property on the RelativeSource extension which allows you to skip the first N instances of the particular AncestorType that are found in the hierarchical tree “above” your usage of the extension just in case you want to pick out an ancestor at a particular level.
Similarly, I was working on a demo for MIX the other week and I had produced a UserControl. Onto this UserControl I’d added a dependency property called Rating and within the control I needed to be able to bind to that property. Here’s a snippet of XAML from that UserControl;
<UserControl Name="uc1"> <Image x:Name="ratingImage" Margin="8" Grid.Row="1" Stretch="Fill" Source="{Binding ElementName=uc1,Path=Rating, TargetNullValue={StaticResource imgSatUnsure}, Converter={StaticResource ratingImageConverter}}" /> </UserControl>
and so in this particular situation I wanted my Image to be able to bind to the Rating property on my UserControl and the most convenient way I found of doing that was to name the user control and use ElementName in order to find the source of my binding.
In Silverlight 5, I could have used RelativeSource as in;
<Image x:Name="ratingImage" Margin="8" Grid.Row="1" Stretch="Fill" Source="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=Rating, TargetNullValue={StaticResource imgSatUnsure}, Converter={StaticResource ratingImageConverter}}" />
and it would have saved me a tonne of energy as I came across a bug in going via the named route so, again, having another mechanism to reach up the hierarchy and find a particular element would be really useful.