Note: these are early notes based on some initial experiments with the Silverlight 5 beta, apply a pinch of salt to what you read.
One of the powerful new features around templating in the Silverlight 5 beta is the ability to produce a DataTemplate that will be implicitly associated with a particular data type.
For example, if I have these 2 simple types Person and Vehicle;
public class Person { public string FirstName { get; set; } public string LastName { get; set; } public int Age { get; set; } } public class Vehicle { public string Type { get; set; } public int Wheels { get; set; } }
then I can define implicit templates for them by writing templates such as these;
<UserControl.Resources> <DataTemplate DataType="local:Person"> <StackPanel> <TextBlock Text="{Binding FirstName}" /> <TextBlock Text="{Binding LastName}" /> <TextBlock Text="{Binding Age}" /> </StackPanel> </DataTemplate> <DataTemplate DataType="local:Vehicle"> <StackPanel> <TextBlock Text="{Binding Type}" /> <TextBlock Text="{Binding Wheels}" /> </StackPanel> </DataTemplate> </UserControl.Resources>
where I have not specified a Key for these resources but have, instead, specified a DataType and that’s enough for Silverlight to figure it out.
If I have a scenario like this one where I have a ListBox bound to a set of Items;
<Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <ListBox ItemsSource="{Binding Items}"> </ListBox> <Button Command="{Binding AddPerson}" Content="Add Person" Grid.Row="1" /> <Button Command="{Binding AddVehicle}" Content="Add Vehicle" Grid.Row="2" /> </Grid>
with a DataContext providing a view model like this one;
public class ViewModel { public ViewModel() { this.Items = new ObservableCollection<object>(); this.AddPerson = new SimpleCommand(() => { this.Items.Add( new Person() { FirstName = "TestFirst", LastName = "TestLast", Age = 22 }); }); this.AddVehicle = new SimpleCommand(() => { this.Items.Add( new Vehicle() { Type = "Car", Wheels = 4 }); }); } public ObservableCollection<object> Items { get; set; } public ICommand AddPerson { get; set; } public ICommand AddVehicle { get; set; } }
then whenever I add a Person to the ListBox the runtime will find the right implicit template to display the Person and if I add a Vehicle to the ListBox then the runtime will do the right thing there too;
and, if for example I was to make my ViewModel implement property change notification and then bind up a new property called SelectedItem to my ListBox then I can bring in a ContentPresenter and it will also make use of the implicit template as in;
<UserControl.Resources> <DataTemplate DataType="local:Person"> <StackPanel> <TextBlock Text="{Binding FirstName}" /> <TextBlock Text="{Binding LastName}" /> <TextBlock Text="{Binding Age}" /> </StackPanel> </DataTemplate> <DataTemplate DataType="local:Vehicle"> <StackPanel> <TextBlock Text="{Binding Type}" /> <TextBlock Text="{Binding Wheels}" /> </StackPanel> </DataTemplate> </UserControl.Resources> <UserControl.DataContext> <local:ViewModel /> </UserControl.DataContext> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <ListBox ItemsSource="{Binding Items}" SelectedValue="{Binding SelectedItem,Mode=TwoWay}" /> <Button Command="{Binding AddPerson}" Content="Add Person" Grid.Row="1" /> <Button Command="{Binding AddVehicle}" Content="Add Vehicle" Grid.Row="2" /> <ContentPresenter Grid.Column="1" Content="{Binding SelectedItem}" /> </Grid>
and so then both the ListBox on the left and the ContentPresenter on the right are using implicit templates to display content;
(as an aside, I also tried this with a ContentPresenter inside a Tooltip and it didn’t work for me so far in the beta).
Naturally, you can override these implicit templates so if I want a different template for my ContentPresenter I can simply add an implicit template that is nearer to the ContentPresenter in the hierarchy of resource resolution as in;
<UserControl.Resources> <DataTemplate DataType="local:Person"> <StackPanel> <TextBlock Text="{Binding FirstName}" /> <TextBlock Text="{Binding LastName}" /> <TextBlock Text="{Binding Age}" /> </StackPanel> </DataTemplate> <DataTemplate DataType="local:Vehicle"> <StackPanel> <TextBlock Text="{Binding Type}" /> <TextBlock Text="{Binding Wheels}" /> </StackPanel> </DataTemplate> </UserControl.Resources> <UserControl.DataContext> <local:ViewModel /> </UserControl.DataContext> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <ListBox ItemsSource="{Binding Items}" SelectedValue="{Binding SelectedItem,Mode=TwoWay}" /> <Button Command="{Binding AddPerson}" Content="Add Person" Grid.Row="1" /> <Button Command="{Binding AddVehicle}" Content="Add Vehicle" Grid.Row="2" /> <Grid Grid.Column="1"> <Grid.Resources> <DataTemplate DataType="local:Vehicle"> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding Type}" /> <TextBlock Text="{Binding Wheels}" /> </StackPanel> </DataTemplate> </Grid.Resources> <ContentPresenter Grid.Column="1" Content="{Binding SelectedItem}"/> </Grid> </Grid>
and, naturally, you can also mix/match this implicit approach with the explicit approach that you’d use in Silverlight 4 today.
I think this is a pretty powerful addition to the Silverlight 5 binding/templating abilities and it’ll be interesting to see what other folks and frameworks do with it.
As a final note, I’m not sure at the time of writing whether there’s anything in Visual Studio 2010 Sp1 or in the Blend Preview for Silverlight 5 that deals with implicit templates – I’ve not seen anything just yet.