May 29, 2012 08:43 by
Scott
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.