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