DataTemplates and DataTriggers in WPF

One of the cool features of WPF is the way we can define a UI based upon the data type used.

For example, assuming a very simple view model

public class PersonViewModel : ReactiveObject
{
   private string firstName;
   private string lastName;
   private int age;

   public string FirstName
   {
      get { return firstName; }
      set { this.RaiseAndSetIfChanged(ref firstName, value); }
   }

   public string LastName
   {
      get { return lastName; }
      set { this.RaiseAndSetIfChanged(ref lastName, value); }
   }

   public int Age
   {
      get { return age; }
      set { this.RaiseAndSetIfChanged(ref age, value); }
   }
}

We might have a parent view model returning a PersonViewModel type or maybe an ItemsControl that has an ObservableCollection of PersonViewModel types. We can handle the bindings in the standard way but we can also associate a UI with a data type using DataTemplates.

A simple example of this is seem with the following code

<ResourceDictionary>
   <Model:PersonViewModel x:Key="model"/>
</ResourceDictionary>

<Button Content="{Binding Source={StaticResource model}}"/>

With the above the button content will display the namespace.objecttype, i.e. MyTestApp.PersonViewModel which is of little use, but if we create a DataTemplate as per the following XAML, we get something more useable

<DataTemplate DataType="{x:Type ta:PersonViewModel}">
   <TextBlock>                    
      <TextBlock.Text>
         <MultiBinding StringFormat="{}{0} {1} aged {2}">
            <Binding Path="FirstName" />
            <Binding Path="LastName" />
            <Binding Path="Age" />
         </MultiBinding>
      </TextBlock.Text>
   </TextBlock>
</DataTemplate>

Now our button will display the “FirstName LastName aged Age” text, where obviously FirstName, LastName and Age are taken from our view model.

Note: You can use a DataTemplate against any ContentControl, so we can see this template used on a button, a label or other control that expects/handles a ContentControl. Whereas the likes of a TextBlock’s Text property expects a string, so this will not work there.

The DataTemplate is also used in the ItemTemplate of a ListBox (for example)

<ListBox ItemsSource="{Binding People}" />

Using the above, where People is an ObservableCollection property. The ItemTemplate of the ListBox uses the DataTemplate previously defined. Alternatively we can create the DataTemplate within the ItemTemplate as per

<ListBox ItemsSource="{Binding People}">
   <ListBox.ItemTemplate>
      <DataTemplate>
         <TextBlock>
            <TextBlock.Text>
               <MultiBinding StringFormat="{}{0} {1} aged {2}">
                  <Binding Path="FirstName" />
                  <Binding Path="LastName" />
                  <Binding Path="Age" />
               </MultiBinding>
            </TextBlock.Text>
         </TextBlock>
      </DataTemplate>
   </ListBox.ItemTemplate>
</ListBox>

Along with DataTemplate we can define DataTemplate Triggers, for example

<DataTemplate DataType="{x:Type ta:PersonViewModel}">
   <TextBlock x:Name="text">                    
      <TextBlock.Text>
         <MultiBinding StringFormat="{}{0} {1} aged {2}">
            <Binding Path="FirstName" />
            <Binding Path="LastName" />
            <Binding Path="Age" />
         </MultiBinding>
      </TextBlock.Text>
   </TextBlock>
   <DataTemplate.Triggers>
      <Trigger SourceName="text" Property="IsMouseOver" Value="True">
         <Setter TargetName="text" Property="Background" Value="GreenYellow" />
      </Trigger>
   </DataTemplate.Triggers>
</DataTemplate>

So now anything use the DataTemplate will also apply the trigger code to the UI view of this data.