Silverlight 5 Beta Rough Notes–Implicit Data Templates

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;

image

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;

image

(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.