Xceed DataGrid for WPF v7.2 Documentation
Part 4: Templating
Welcome to Xceed DataGrid, Editors, and 3D Views for WPF v7.2 > Xceed DataGrid for WPF > Tutorial: Creating a Custom Theme > Part 4: Templating

     

If you're like me, you find templates to be a little daunting at times. Luckily, all the templates you will need are included (not in the custom-theme project, but we'll get to that) and are just waiting to be pasted into the new theme.

It helps to think of the templates that we will be adding as "overrides" since the new templates that we will be adding have the same names as the default templates in the base-xaml file. The base-xaml file, which contains the default templates, is located in the common-theme folder located under the Xceed DataGrid for WPF installation folder in the Themes\Common folder. Each view has its own base file, which contains the element templates for the specific view. Since we are only dealing with the TableView, you only need to open the TableView.GridElementTemplates.xaml file. Remember in the Styles part when I talked about one XAML file being included in the resources of another? Well the TableView.GridElementTemplates.xaml file is included in the resources of the TableView.Office2007Tutorial.xaml file.

All the templates that will be overridden need (OK, not really "need", but it is the suggested place) to be placed in the "Templates specific to this View/Theme/ColorScheme" section of the TableView.Office2007Tutorial.xaml file.

The list of elements to template was provided in part 2, but for simplicity's sake, it has been copied over to this part as well (see Table 1). Each template will be included for those of you who prefer to just cut-and-paste code.

ShowTable 1: Elements to template

Element to template What we will be changing
RowSelector Custom separator lines and change the background when the mouse is pressed
ColumnManagerCell Change the background when the mouse is pressed
Custom separator lines
Change the color of the sort glyph

So, without further ado, let's modify some templates.

Retrieving the Templates

In the TableView.GridElementTemplates.xaml file, locate the RowSelector templates by searching for "TEMPLATE: RowSelector" (remember the naming conventions?. You will notice that there are already 2 templates for the RowSelector, so copy both and place them in the templates section of the TableView.Office2007Tutorial.xaml file. Next, locate the ColumnManagerCell template and copy it over to the custom-theme project. Search for "TEMPLATE: ColumnManagerCell" to locate it, copy it, and add it to the same location as the RowSelector templates.

The next step is to change the name of the namespace mapping from "markup" and "local" to "xcdg" so that the elements and their members can be accessed from the custom-theme project. Make sure that you only replace the namespaces in the templates imported from the TableView.GridElementTemplates.xaml file.

"local" is used by default for your custom-theme's xmlns attribute. Make sure that you do not replace it with "xcdg".

Templating Elements

  1. The first element for which we will be providing a template for is the RowSelector element. "Why not do a style?" you ask? Good question. The RowSelector template includes the resizer thumb, which allows rows to be resized. A style is pretty much limited to the appearance of an element, and we need to modify the appearance of the separator lines, which will have an impact on the resizer thumb. This creates the need for a template since the visual structure and behavior of the RowSelector element will be modified. Ready? Here we go:

    1. Locate the template in the TableView.Office2007Tutorial.xaml file by searching for "TEMPLATE: RowSelector

    2. Give the parent visual element a name so we can access it later on. In this case, the first border in the grid (not DataGridControl, the other kind) and we will name it "mainBorder"

    3. Add a trigger for IsPressed in the ControlTemplate.Triggers to invert the brush when the row selector is pressed. 

    4. Create new separator line by:
         i.     adding a new Border at the bottom of the grid;
         ii.    giving it a width and height of 1 pixel;
         iii.   a left and right margin of 4 pixels;
         iiii.  and vertically align it to the bottom. 

      This border will also be added to the second RowSelector template.

      View the RowSelector template

      XAML
      Copy Code
      <!==**************************
         * TEMPLATE: RowSelector
         ************************** ==>
       <ControlTemplate x:Key="tableViewRowSelectorTemplate"
                         TargetType="xcdg:RowSelector">
           <!== This Grid is used to position the resizer Thumb over the RowSelector ==>
           <Grid>
             <Border x:Name="mainBorder"
                     Background="{TemplateBinding Background}"
                     BorderBrush="{TemplateBinding BorderBrush}"
                     BorderThickness="{TemplateBinding BorderThickness}"
                     Padding="{TemplateBinding Padding}">
                 <ContentPresenter x:Name="contentPresenter"
                                   Content="{TemplateBinding Content}"
                                   ContentTemplate="{TemplateBinding ContentTemplate}"
                                   ContentTemplateSelector="{TemplateBinding ContentTemplateSelector}"
                                   HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                   VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
             </Border>
             <!== Thumb that is used to resize the Row. ==>
             <Thumb x:Name="PART_RowResizerThumb"
                     Style="{StaticResource invisibleThumbStyle}"
                     VerticalAlignment="Bottom" />
       <Border VerticalAlignment="Bottom"
                   Height="1"
                   Background="#9AC6FF"
                   Margin="4,0,4,0"/> Language Filtered Section XML
           </Grid>
           <ControlTemplate.Triggers>
             <Trigger Property="IsPressed"
                       Value="True">
                 <Setter TargetName="mainBorder"
                         Property="Background"
                         Value="{StaticResource rowSelectorPressedBrush}"/>
             </Trigger>
           </ControlTemplate.Triggers>
       </ControlTemplate>

       

      Required RowSelector Brushes

      XAML
      Copy Code
      <Color x:Key="mainGradientColor">
           #C4DDFF
       </Color>
      <LinearGradientBrush x:Key="rowSelectorPressedBrush"
                             StartPoint="0,0.5"
                             EndPoint="1,0.5">
           <GradientStop Color="#FFFFFF"
                         Offset="1" />
           <GradientStop Color="{StaticResource mainGradientColor}"
                         Offset="0" />
       </LinearGradientBrush>

       

2. The next template (don"t worry, it"s the last one) is for the ColumnManagerCell element. In this template, we will change the background when the mouse is pressed, the separator lines, as well as the glyph that is displayed when the column-manager cell"s associated column is sorted.

As we did for the second RowSelector template, we want to add a "Pressed" effect on the ColumnManagerCell; therefore, we will have to provide the parent visual element with a different brush when it receives a mouse-down (IsPressed). Let"s break this part into step:

    1. Locate the ColumnManagerCell template by searching for "TEMPLATE: ColumnManagerCell".
    2. Give the parent visual element a name so we can access it later on. In this case, the first border in the grid (not DataGridControl) and we will name it "mainBorder"
    3. Add a trigger for IsPressed in the ControlTemplate.Triggers to invert the brush when the column-manager cell is pressed.

      Now that we have a snazzy new pressed effect, we will change the separator lines. As previously stated, the same RowSelector-style vs. template logic applies here. The new separator lines will be created by a Border that:
    1. has a right horizontal alignment and a 1-pixel width;
    2. a background set to #9AC6FF;
    3. and the top and bottom margins set to 4 pixels.

    This new border is added at the bottom of the Grid (again, not DataGridControl).

    Lastly, we need to change the color of the glyph that is displayed in a column-manager cell when a column is sorted. In the ColumnManagerCell template there is a ContentPresenter named "sortGlyphPresenter". Locate it and change its foreground color to #9AC6FF.

    View the ColumnManagerCell template

    XAML
    Copy Code
    <!==**************************
       * TEMPLATE: ColumnManagerCell
       ************************** ==>
    <ControlTemplate x:Key="tableViewColumnManagerCellTemplate"
                     TargetType="xcdg:ColumnManagerCell">
      <!== We don't use the base Cell Template for the ColumnManagerCell
           because it need more elements to function properly:
           a Thumb named "PART_ColumnResizerThumb" that is used to
           resize the ColumnManagerCells's ParentColumn.Width and a glyph
           that represents the Column's SortDirection. ==>
      <!== This Grid is used to position the resizer Thumb
           over the ColumnManagerCell ==>
      <Grid>
        <Border x:Name="mainBorder"
                Background="{TemplateBinding Background}"
                BorderBrush="{TemplateBinding BorderBrush}"
                BorderThickness="{TemplateBinding BorderThickness}"
                Padding="{TemplateBinding Padding}">
          <!== This StackPanel is used to lay out the ContentPresenter and the
               Sort Glyph (when present). ==>
          <StackPanel Orientation="Horizontal">
            <!== Same ContentPresenter as in the base Cell Template. ==>
            <ContentPresenter Content="{xcdg:CellContentBinding}"
                  ContentTemplate="{TemplateBinding ContentTemplate}"
                  ContentTemplateSelector="{TemplateBinding ContentTemplateSelector}"
                  HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                  VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
            <!== ContentPresenter that is used to display the sort glyph.
                 We explicitly set its Content property to Null to prevent
                 the XAML parser from implicitely setting it to its
                 TemplatedParent's Content. ==>
            <ContentPresenter x:Name="sortGlyphPresenter"
                              Content="{x:Null}"
                              ContentTemplate="{x:Null}"
                              TextElement.Foreground="#9AC6FF"/>
          </StackPanel>
        </Border>
        <!== Thumb that is used to resize the Column. ==>
        <Thumb x:Name="PART_ColumnResizerThumb"
               Style="{StaticResource invisibleThumbStyle}"
               HorizontalAlignment="Right" />
          <Border HorizontalAlignment="Right"
                  Width="1"
                  Background="#9AC6FF"
                  Margin="0,4,0,4"/>
      </Grid>
        <ControlTemplate.Triggers>
          <Trigger Property="IsPressed" Value="True">
            <Setter TargetName="mainBorder" Property="Background">
              <Setter.Value>
                <LinearGradientBrush
                           StartPoint="0.5,0"
                           EndPoint="0.5,1">
                  <GradientStop Color="#FFFFFF"
                                Offset="1" />
                  <GradientStop Color="#C4DDFF"
                                Offset="0" />
                </LinearGradientBrush>
              </Setter.Value>
            </Setter>
          </Trigger>
          <!== The following 2 triggers allow to display the appropriate Sort Glyph
               depending on the ParentColumn's SortDirection. ==>
          <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self},
                                         Path=ParentColumn.SortDirection}"
                       Value="Ascending">
            <Setter TargetName="sortGlyphPresenter"
                    Property="ContentPresenter.ContentTemplate"
                    Value="{xcdg:ViewBinding AscendingSortGlyph}"/>
            <Setter TargetName="sortGlyphPresenter"
                    Property="Margin"
                    Value="6,0,0,0"/>
          </DataTrigger>
          <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self},
                                         Path=ParentColumn.SortDirection}"
                       Value="Descending">
            <Setter TargetName="sortGlyphPresenter"
                    Property="ContentPresenter.ContentTemplate"
                    Value="{xcdg:ViewBinding DescendingSortGlyph}"/>
            <Setter TargetName="sortGlyphPresenter"
                    Property="Margin"
                    Value="6,0,0,0"/>
          </DataTrigger>
          <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self},
                                         Path=ParentColumn.HasFixedWidth}"
                       Value="True">
            <Setter TargetName="PART_ColumnResizerThumb"
                    Property="IsEnabled"
                    Value="False"/>
          </DataTrigger>
          <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self},
                                         Path=ParentColumn.Visible}"
                       Value="False">
            <Setter Property="Visibility"
                    Value="Collapsed"/>
          </DataTrigger>
        </ControlTemplate.Triggers>
      </ControlTemplate>
      

You're done. Nothing left to do but use your custom theme!