WPFAQS – Templates in WPF/XAML

WPFAQS – is a series on WPF frequently asked questions (FAQS). Today we will discuss about different templates, like “ControlTemplate”, “DataTemplate”, “HierarchicalDataTemplate” and “ItemsPanelTemplate”, in WPF and their usage.

WPFAQS(003) – What are the different types of Templates in WPF/XAML, explain with examples

In the last WPFAQ, I discussed the logical and visual trees. In this post I will expand my discussion on different types of templates and how they are related to visual trees. All the templates In WPF all derived from the abstract FrameworkTemplate class. ControlTemplate is a visual tree of a UI controls such as a Button or ListView/GridView control while DataTemplate is used to visualize the model/business object in terms of UI components defined in the DataTemplate or the business objects render themselves to the visual tree defined in the DataTemplate. For its example please refer to my [Posting] where enums are rendered to some combobox items or so. HierarchicalDataTemplate is also a DataTemplate but, as the name suggests, is suitable for hierarchal/recursive data structures like Trees or the model based on composite design pattern. ItemsControl and its derived classes like Comboboxes and ListBoxes create Layout panels called ItemsPanelTemplate for controlling the layout of their respective children. You may consider overriding it in order to change the default behavior of their layout. Here is the class diagram that shows their relationships with each other :


And, here is an example XAML, that shows DataTemplates as well as ItemsPanelTemplate that I took it from an example application “LegoDraw” that I am planning to post in the near future:

   1:  <UserControl x:Class="Shams.Wpf.LegoDraw.Controls.LegoDrawBoard"
   2:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:      xmlns:local="clr-namespace:Shams.Wpf.LegoDraw.Controls"
   5:      xmlns:model="clr-namespace:Shams.Wpf.LegoDraw.Model"         
   6:      HorizontalAlignment ="Stretch"
   7:      HorizontalContentAlignment ="Stretch"
   8:      VerticalAlignment ="Stretch"
   9:      VerticalContentAlignment ="Stretch"
  10:      local:LegoDrawBoard.TotalColors="9"
  11:      x:Name="This">
  13:      <UserControl.Resources>
  14:          <ResourceDictionary>
  15:              <DataTemplate DataType="{x:Type model:CellWidget}">
  16:                  <Grid Width="Auto" Height="Auto">
  17:                      <Border x:Name="PART_Border" 
  18:                              BorderThickness="4" 
  19:                              Background="{Binding Path=Background}"
  20:                              BorderBrush="{Binding Path=BorderBrush}" 
  22:                              MouseLeftButtonDown="PART_Border_MouseLeftButtonDown"
  23:                              MouseMove="PART_Border_MouseMove"
  24:                              MouseLeftButtonUp="PART_Border_MouseLeftButtonUp" />
  25:                  </Grid>
  26:                  <DataTemplate.Triggers>
  27:                      <DataTrigger Binding="{Binding Path=CellId}" Value="{x:Null}">
  28:                          <Setter TargetName="PART_Border" Property="BorderBrush" Value="Transparent" />
  29:                      </DataTrigger>
  30:                      <DataTrigger Binding="{Binding Path=CellId}" Value="0">
  31:                          <Setter TargetName="PART_Border" Property="BorderBrush" Value="Yellow" />
  32:                      </DataTrigger>
  33:                      <DataTrigger Binding="{Binding Path=CellId}" Value="1">
  34:                          <Setter TargetName="PART_Border" Property="BorderBrush" Value="Cyan" />
  35:                      </DataTrigger>
  36:                      <DataTrigger Binding="{Binding Path=CellId}" Value="2">
  37:                          <Setter TargetName="PART_Border" Property="BorderBrush" Value="Red" />
  38:                      </DataTrigger>
  39:                      <DataTrigger Binding="{Binding Path=CellId}" Value="3">
  40:                          <Setter TargetName="PART_Border" Property="BorderBrush" Value="Blue" />
  41:                      </DataTrigger>
  42:                      <DataTrigger Binding="{Binding Path=CellId}" Value="4">
  43:                          <Setter TargetName="PART_Border" Property="BorderBrush" Value="Purple" />
  44:                      </DataTrigger>
  45:                      <DataTrigger Binding="{Binding Path=CellId}" Value="5">
  46:                          <Setter TargetName="PART_Border" Property="BorderBrush" Value="SpringGreen" />
  47:                      </DataTrigger>
  48:                      <DataTrigger Binding="{Binding Path=CellId}" Value="6">
  49:                          <Setter TargetName="PART_Border" Property="BorderBrush" Value="Pink" />
  50:                      </DataTrigger>
  51:                      <DataTrigger Binding="{Binding Path=CellId}" Value="7">
  52:                          <Setter TargetName="PART_Border" Property="BorderBrush" Value="Orange" />
  53:                      </DataTrigger>
  54:                      <DataTrigger Binding="{Binding Path=CellId}" Value="8">
  55:                          <Setter TargetName="PART_Border" Property="BorderBrush" Value="SteelBlue" />
  56:                      </DataTrigger>
  57:                  </DataTemplate.Triggers>
  58:              </DataTemplate>
  60:              <DataTemplate x:Key ="GridRowTemplate">
  61:                  <ItemsControl ItemsSource ="{Binding Path=CellsRow}">
  62:                      <ItemsControl.ItemsPanel>
  63:                          <ItemsPanelTemplate>
  64:                              <UniformGrid Columns ="1"/>
  65:                          </ItemsPanelTemplate>
  66:                      </ItemsControl.ItemsPanel>
  67:                  </ItemsControl>
  68:              </DataTemplate>
  69:          </ResourceDictionary>
  70:      </UserControl.Resources>
  72:      <Grid x:Name="MainGrid" >
  73:          <Grid.DataContext>
  74:              <Binding ElementName="This" Path="MainGridWidget" />
  75:          </Grid.DataContext>
  76:          <Border BorderBrush="Silver" BorderThickness="2" >
  77:              <ItemsControl x:Name ="LegoDrawItemsControl" 
  78:                            ItemTemplate ="{StaticResource GridRowTemplate}" 
  79:                            ItemsSource ="{Binding Path=GridRows}" >
  80:                  <ItemsControl.ItemsPanel>
  81:                      <ItemsPanelTemplate>
  82:                          <UniformGrid Rows ="1"/>
  83:                      </ItemsPanelTemplate>
  84:                  </ItemsControl.ItemsPanel>
  85:              </ItemsControl>
  86:          </Border>
  87:      </Grid>
  88:  </UserControl>

If you look at lines 64 and 82  above, you will see that we override the default “ItemsPanelTemplate” with the “UniformGrid” panel. Also have a look at the DataTemplate part line-15, where the Binding is a business object “CellWidget” and will be rendered as Border UI at run time.

I hope you now have a clear understanding of the templates, if not then pass on me your question for more clarification on them. That's all for now and look forward to your feed-back. enjoy :)


If you enjoyed reading this blog, leave your valuable feedback and consider subscribing to the RSS feed. You can also subscribe to it by email. Also, you can follow me on Twitter. Thank you!

Add comment