WPF Toolkit DataGrid, Part II – Custom styling

This post is part of the WPF Toolkit DataGrid series. Here is a list with the complete set of blog posts:

Introduction

In Part I we went through basic DataGrid setup creating a sample application that showcased the usage of DataGridColumns. In this part we will dive into more advanced concepts. It is my ambition that you get a good understanding of how to setup a DataGrid to do whatever you wish without restrictions. In this part we will focus on restyling the DataGrid, DataGridColumnHeaders and DataGridRowHeaders.

Roadmap

  1. Visual breakdown of styles and layout
  2. Styling the DataGrid
  3. Styling DataGridColumnHeaders
  4. Styling DataGridRowHeaders
  5. Creating a new template for the SelectAllButton

Download sample source code

Here you can find the source code for this article.

Visual breakdown of layout and styles

Before we start digging dipper into DataGrid’s functionalities and restyling let’s take a look on what WPF Toolkit uses to build it. It is essential that you understand what each class is responsible for in order to find which styles you need to replace so that you can change the looks of a specific region of the DataGrid.

First let’s look at the DataGrid from a layout perspective. Along with the DataGrid this are the main components used to display the information inside of it:

These classes are the ones used by the DataGrid to render itself. They are the objects that end up on the application’s visual tree:

  • DataGridColumnHeadersPresenter: this control will go through the DataGridColumns collection of the DataGrid and create a DataGridColumnHeader for each;
  • DataGridColumnHeader: picks up the information in its DataGridColumn and renders it as a header on the corresponding column position;
  • DataGridRowsPresenter: the ItemsControl that iterates the ItemsSource creating a DataGridRow for each item;
  • DataGridRow: represents an item of ItemsSource;
  • DataGridRowHeader: this is a Button that lays on the beginning of each DataGridRow. It allows the user to select the row, resize it, and can be used to show information – for instance, on the current operation being performed over the selected row or still to provide error information;
  • DataGridCellsPresenter: this is the ItemsControl used to render each cell, based on the column bindings, for the DataGridRow bounded item;
  • DataGridCell: this is the control used to render a specific value of an Item. Essentially it is made of two templates, one to display the item and one for editing it;
  • DataGridDetailsPresenter: this is an extension of the DataGridRow used to show further information about a row than the one displayed by the cells. It is a panel that is normally laid bellow the cells. On WPF Toolkit’s DataGrid, by default, this panel is only shown when the user selects a row.

On the following image you can see where each of this controls are laid. Take some time to get to know what component is responsible for what part of the DataGrid.

Starting from the top, in the DataGrid, we will have a DataGridColumnHeadersPresenter used to present all DataGridColumnHeaders, there will be one header per bounded column. Each column header has two grippers. Inside the DataGrid along with the DataGridColumnHeadersPresenter there will be a DataGridRowsPresenter which will contain all our rows. The DataGridRow will contain a DataGridRowHeader and a DataGridCellsPresenter which contains all the DataGridCells. Also part of the DataGridRow, although not shown in the screenshot, is the DataGridDetailsPresenter, which can be used to present further details on the selected row.

If you use Snoop to browse the visual tree of our sample from Part I you will see the following (I have marked in red the items you should pay more attention to):

Side note: Snoop is a great tool for analyzing how a WPF application is structured. If you do not have this tool on you arsenal, stop reading, go to their web site, download it and install it. This is a must have if you are developing WPF applications.

You can see each of the classes we looked at above in the visual tree of our WPF sample application. Take some time to assimilate the structure used before proceeding.

By now you should have a solid understanding about the structure of the DataGrid. You should be able to easily spot each component along with which class is responsible for rendering it. This is the foundation for our next job – restyling the WPF Toolkit’s DataGrid.

Styling the DataGrid

Since the DataGrid is divided in several parts and each has individual styles, our ControlTemplate for it will not change that much, most of it will be based on the one that comes with WPF Toolkit.

The DataGrid style has five major components:

  • Select all button: this is the button on the top left corner of the DataGrid that is used to select all rows;
  • PART_ColumnHeadersPresenter: this is the ItemsControl used to present all of our DataGridColumnHeaders;
  • PART_ScrollContentPresenter: the main area of the DataGrid where all the DataGridRows will be rendered;
  • PART_VerticalScrollBar: renders the vertical scroll bar of our ScrollViewer;
  • PART_HorizontalScrollBar: renders the horizontal scroll bar of our ScrollViewer.

I have taken a screenshot from Snoop in order to show you how these parts fit together on visual tree:

In the last sample we worked with on Part I, you may have notice that the vertical scroll bar does not occupy the full height of the DataGrid. In our new style, we will make it fill that empty space. This is just a minor tweak in the default DataGrid ControlTemplate.

Since you may want to reuse this new style just add a new resource dictionary to your solution where you can place the new DataGrid style – in the sample code I have named it ‘DataGrid.Generic.xaml’ and placed it under the Themes folder.

The change we are planning to make is a small one, so we can reuse the style defined within WPF Toolkit, you just have to copy the DataGrid style from the ‘Generic.xaml’ file on WPF Toolkit and add it to your new dictionary. Find the definition of the vertical scroll bar in the ControlTemplate part of the style. Change Grid.Row to 0 and give it a Grid.RowSpan of 2, as shown below:

(…)
<ScrollBar Name="PART_VerticalScrollBar"
           Grid.Row="0" Grid.RowSpan="2" Grid.Column="2" Orientation="Vertical"
           Maximum="{TemplateBinding ScrollableHeight}"
           ViewportSize="{TemplateBinding ViewportHeight}"
           Value="{Binding Path=VerticalOffset, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"
           Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>
(…)

Along with extending the scroll bar you can also style your DataGrid by setting some nice colors. To give your DataGrid a cooler look you can start by creating a series of brushes:

<!-- Generic brushes -->
<SolidColorBrush x:Key="DefaultControlBorderBrush" Color="#FF688CAF"/>
<SolidColorBrush x:Key="DefaultControlBackgroundBrush" Color="#FFE3F1FE"/>
<SolidColorBrush x:Key="DefaultControlForegroundBrush" Color="#FF10257F"/>

<!-- DataGrid related brushes -->
<SolidColorBrush x:Key="GridLineColorBrush" Color="#FFCEDFF6" />
<LinearGradientBrush x:Key="AlternateRowBackgroundBrush"
                     StartPoint="0.5,0.0" EndPoint="0.5,1.0">
    <GradientStop Color="#FFFEFEFF" Offset="0"/>
    <GradientStop Color="#FFE4F0FC" Offset="1"/>
</LinearGradientBrush>
These brushes will then be used to set the following properties on the style:
(…)
<Setter Property="Background"
        Value="{StaticResource DefaultControlBackgroundBrush}"/>
<Setter Property="Foreground"
        Value="{StaticResource DefaultControlForegroundBrush}"/>
<Setter Property="BorderBrush"
        Value="{StaticResource DefaultControlBorderBrush}" />
<Setter Property="HorizontalGridLinesBrush"
        Value="{StaticResource GridLineColorBrush}" />
<Setter Property="VerticalGridLinesBrush"
        Value="{StaticResource GridLineColorBrush}" />
<Setter Property="AlternatingRowBackground"
        Value="{StaticResource AlternateRowBackgroundBrush}" />
<Setter Property="BorderThickness" Value="1" />
(…)

In the sample you will find all the style for the DataGrid. When you go through its ControlTemplate you will see most of the items described in ‘Visual breakdown of layout and styles’. This is how our DataGrid currently looks with the new style:

You can download a working sample by following this link.

Styling DataGridColumnHeaders

The next step will be to create custom styles for our DataGridColumnHeaders. In the class diagram above you can see that DataGridColumnHeaders extend ButtonBase, so when we style a DataGridColumnHeader we are actually creating a style for a button. We also need to keep in mind the two styles for the grippers – one for each side of the DataGridColumnHeader. The grippers will be used to resize the DataGridColumnHeader, thus they need to exist as controls the user can interact with!

Let’s start by creating the style for the grippers, both left and right, since they will be referenced as static resources by the DataGridColumnHeader style.

<!-- DataGridColumnHeader Right Gripper Style -->
<Style x:Key="ColumnHeaderRightGripperStyle" TargetType="{x:Type Thumb}">
    <Setter Property="Width" Value="8"/>
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="Cursor" Value="SizeWE"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Thumb}">
                <Border Padding="{TemplateBinding Padding}"
                        Background="{TemplateBinding Background}">
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
<!-- DataGridColumnHeader Left Gripper Style -->
<Style x:Key="ColumnHeaderLeftGripperStyle" 
       BasedOn="{StaticResource ColumnHeaderRightGripperStyle}" 
       TargetType="{x:Type Thumb}">
    <Setter Property="Width" Value="8"/>
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="Cursor" Value="SizeWE"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Thumb}">
                <Border Name="Border"
                        Padding="{TemplateBinding Padding}" 
                        Background="{TemplateBinding Background}">
                    <Canvas>
                        <Line RenderOptions.EdgeMode="Aliased" Stroke="#88B0E4"
                              X1="7" Y1="{Binding ElementName=Border, Path=ActualHeight}"
                              X2="7" Y2="0"/>
                    </Canvas>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

As you may have noticed we are drawing a line on the left gripper but not drawing it on the right gripper. If we were to draw lines on both sides the end result would be a thicker line. Instead, on the right gripper, we just reserve some space so that the user will have somewhere to click and drag to resize the column.

Now that you have created the grippers you can go ahead and create the style for the DataGridColumnHeader. In this style we will be adding a nice blue gradient background and a highlight gradient that gets displayed when the user put his mouse over a column header:

<!-- DataGridColumnHeader Style -->
<Style x:Key="ColumnHeaderStyle" TargetType="{x:Type WpfToolkit:DataGridColumnHeader}">
    <Setter Property="VerticalContentAlignment" Value="Center" />
    <Setter Property="Background" Value="{StaticResource HeaderBackgroundBrush}"/>
    <Setter Property="BorderBrush" Value="{StaticResource HeaderBorderBrush}" />
    <Setter Property="BorderThickness" Value="0,1,0,1" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type WpfToolkit:DataGridColumnHeader}">
                <Grid>
                    <WpfToolkit:DataGridHeaderBorder
                        x:Name="headerBorder"
                        SortDirection="{TemplateBinding SortDirection}"
                        IsHovered="{TemplateBinding IsMouseOver}"
                        IsPressed="{TemplateBinding IsPressed}"
                        IsClickable="{TemplateBinding CanUserSort}"
                        Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}"
                        Padding ="{TemplateBinding Padding}"
                        SeparatorVisibility="{TemplateBinding SeparatorVisibility}"
                        SeparatorBrush="{TemplateBinding SeparatorBrush}">
                        <Border BorderBrush="{StaticResource HeaderInnerBorderBrush}" 
                                BorderThickness="0,1,0,0">
                            <TextBlock
                                Text="{Binding}" Margin="4,0,4,0"
                                SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" />
                        </Border>
                    </WpfToolkit:DataGridHeaderBorder>

                    <Thumb x:Name="PART_LeftHeaderGripper"
                           HorizontalAlignment="Left"
                           Style="{StaticResource ColumnHeaderRightGripperStyle}"/>
                    <Thumb x:Name="PART_RightHeaderGripper"
                           HorizontalAlignment="Right"
                           Style="{StaticResource ColumnHeaderLeftGripperStyle}"/>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter TargetName="headerBorder" Property="Background" 
                                Value="{StaticResource HeaderHighlightedBackgoundBrush}" />
                    </Trigger>
                    <Trigger Property="IsPressed" Value="True">
                        <Setter TargetName="headerBorder" Property="Background" 
                                Value="{StaticResource HeaderPressedBackgroundBrush}" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Go ahead now and create a ‘Brushes.xaml’ beside the ‘DataGrid.Generic.xaml’ resource dictionary. In here you will define all of the brushes used by the styles in the DataGrid. This will make it easier for you to reuse them on the remaining controls of your application.

<!-- Header related brushes -->
<SolidColorBrush x:Key="HeaderBorderBrush" Color="#88B0E4"/>
<SolidColorBrush x:Key="HeaderInnerBorderBrush" Color="#FFFFFF" />
<LinearGradientBrush x:Key="HeaderBackgroundBrush" 
                     StartPoint="0.5,0.0" EndPoint="0.5,1.0">
    <GradientStop Color="#FF98BFEB" Offset="0"/>
    <GradientStop Color="#FFB8D4F2" Offset="1"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HeaderHighlightedBackgoundBrush" 
                     StartPoint="0,0" EndPoint="0,1">
    <GradientStop Color="#FFA3CBF7" Offset="0"/>
    <GradientStop Color="#FFD9E6F9" Offset="1"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HeaderPressedBackgroundBrush" 
                     StartPoint="0,0" EndPoint="0,1">
    <GradientStop Color="#FFA3CBF7" Offset="1"/>
    <GradientStop Color="#FFD9E6F9" Offset="0"/>
</LinearGradientBrush>

In order for you to see the outcome of these new styles you will have to alter the DataGrid style by adding it a setter for the ColumnHeaderStyle property:

<Setter Property="ColumnHeaderStyle" Value="{StaticResource ColumnHeaderStyle}"/>

In the following picture you can see how our DataGrid is actually looking after restyling the DataGridColumnHeaders:

Here are our fancy looking DataGridColumnHeaders. Notice that the ‘Player Name’ column was purposely highlighted when the screenshot was taken.

You can download a working sample of the code presented here by following this link.

Styling DataGridRowHeaders

DataGridRowHeaders pretty much follow the DataGridColumnHeaders in the way they are implemented. So you have two grippers, one on top and another on bottom, and a style for header itself. In this case we will use the same style for both the grippers just to lay the required space for the user to interact with. The visual separator for the columns will be the control template’s border.

<!-- DataGridRowHeader Gripper -->
<Style x:Key="RowHeaderGripperStyle" TargetType="{x:Type Thumb}">
    <Setter Property="Height" Value="8"/>
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="Cursor" Value="SizeNS"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Thumb}">
                <Border Padding="{TemplateBinding Padding}"
                        Background="{TemplateBinding Background}"/>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

 

Now you can go ahead and include the style for the DataGridRowHeader. For this you will use most of the code from the DataGridColumnHeader and the corresponding brushes. We want to keep the look and feel of both header types consistent.

<!-- DataGridRowHeader Style -->
<Style x:Key="{x:Type WpfToolkit:DataGridRowHeader}"
       TargetType="{x:Type WpfToolkit:DataGridRowHeader}">
    <Setter Property="Background" Value="{StaticResource HeaderBackgroundBrush}" />
    <Setter Property="BorderBrush" Value="{StaticResource HeaderBorderBrush}" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type WpfToolkit:DataGridRowHeader}">
                <Grid>
                    <WpfToolkit:DataGridHeaderBorder 
                        x:Name="headerBorder"
                        IsSelected="{TemplateBinding IsRowSelected}"
                        IsHovered ="{TemplateBinding IsMouseOver}"
                        IsPressed="{TemplateBinding IsPressed}"
                        Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="1,0,1,1"
                        Padding ="{TemplateBinding Padding}"
                        Orientation="Horizontal"
                        SeparatorVisibility="{TemplateBinding SeparatorVisibility}"
                        SeparatorBrush="{TemplateBinding SeparatorBrush}">
                        <Border BorderBrush="{StaticResource HeaderInnerBorderBrush}"
                                BorderThickness="0,1,0,0">
                            <StackPanel Orientation="Horizontal">
                                <ContentPresenter
                                    SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                    VerticalAlignment="Center"/>
                                <Control
                                    SnapsToDevicePixels="false"
                                    Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type WpfToolkit:DataGridRow}}, Path=(Validation.HasError), Converter={StaticResource bool2VisibilityConverter}}"
                                    Template="{Binding RelativeSource={RelativeSource AncestorType={x:Type WpfToolkit:DataGridRow}}, Path=ValidationErrorTemplate}" />
                            </StackPanel>
                        </Border>
                    </WpfToolkit:DataGridHeaderBorder>
                    
                    <Thumb x:Name="PART_TopHeaderGripper"
                           VerticalAlignment="Top"
                           Style="{StaticResource RowHeaderGripperStyle}"/>
                    <Thumb x:Name="PART_BottomHeaderGripper"
                           VerticalAlignment="Bottom"
                           Style="{StaticResource RowHeaderGripperStyle}"/>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter TargetName="headerBorder" Property="Background" 
                                Value="{StaticResource HeaderHighlightedBackgoundBrush}" />
                    </Trigger>
                    <Trigger Property="IsPressed" Value="True">
                        <Setter TargetName="headerBorder" Property="Background" 
                                Value="{StaticResource HeaderPressedBackgroundBrush}" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

As you can see this style is pretty much the same as the one for the DataGridColumnHeader. After these changes this is how our application is looking:

It is looking pretty cool! To download the sample for this part of the tutorial just follow this link.

Creating a new template for the SelectAllButton

The only thing that is currently killing the look of our DataGrid is that top left corner button. This button is used to select all the rows on the DataGrid and its appearance can be altered by setting it on the DataGrid’s style. First you have to create the style for the button:

<!-- SelectAllButton ControlTemplate -->
<ControlTemplate x:Key="SelectAllButtonTemplate" TargetType="{x:Type Button}">
    <Grid>
        <Rectangle x:Name="Border" SnapsToDevicePixels="True"
                   Stroke="{StaticResource HeaderBorderBrush}"
                   Fill="{StaticResource HeaderBackgroundBrush}" />
        <Border SnapsToDevicePixels="True" Margin="1,1,1,0"
                BorderBrush="White" BorderThickness="0,1,0,0" />
        <Polygon x:Name="Arrow"
                 HorizontalAlignment="Right"
                 VerticalAlignment="Bottom"
                 Margin="8,8,3,3"
                 Opacity="0.15"
                 Fill="Black"
                 Stretch="Uniform"
                 Points="0,10 10,10 10,0" />
    </Grid>
    <ControlTemplate.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
            <Setter TargetName="Border" Property="Fill"
                    Value="{StaticResource HeaderHighlightedBackgoundBrush}" />
        </Trigger>
        <Trigger Property="IsPressed" Value="True">
            <Setter TargetName="Border" Property="Fill"
                    Value="{StaticResource HeaderPressedBackgroundBrush}" />
        </Trigger>
        <Trigger Property="IsEnabled" Value="False">
            <Setter TargetName="Arrow" Property="Visibility" Value="Collapsed" />
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

The only thing remaining now is to set this template on the corresponding button on the DataGrid style:

<!--Left Column Header Corner -->
(...)
<Button 
    Command="{x:Static WpfToolkit:DataGrid.SelectAllCommand}"
    Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type WpfToolkit:DataGrid}}, Path=CellsPanelHorizontalOffset}"
    Focusable="false"
    Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type WpfToolkit:DataGrid}}, Path=HeadersVisibility, Converter={x:Static WpfToolkit:DataGrid.HeadersVisibilityConverter}, ConverterParameter={x:Static WpfToolkit:DataGridHeadersVisibility.All}}" 
    Template="{StaticResource SelectAllButtonTemplate}"/>
(...)

 

Congrats! You have now restyled all of the relevant components of the DataGrid. This is the final screenshot of the sample developed on this blog post:

The final sample for this blog post can be downloaded by following this link.

End of Part II

In this part you have seen how to style the look of the DataGrid. You got to know a bit more about the structure used by WPF Toolkit to render its DataGrid and its data.

Don’t miss the next part of this series. We will look into more advanced stuff like styling cells, columns and creating custom DataGridColumns.

WPF: Styled Button does not display access key

When creating a new style for a button this is something that can easily fall through the cracks into production code.

The Problem

Imagine that you have a standard WPF button using its default template. Something like this:

<Button Content="C_ommit" Command="{Binding CommitCommand}"/>
<Button Content="_Cancel" Command="{Binding CancelCommand}"/>

You use the underscore to specify which access key should be used in conjunction with the Alt key to execute the command associated with the button. So when you press the Alt key you can see ‘o’ and ‘C’ underlined:

Until now all goes well. The trouble comes when you want to add a personalized style to your button. Now you want your buttons to have a fancy glass button style. FYI this style was created based on Martin Grayson’s “Creating a glass button: The complete tutorial” blog post. Moving on:

<Style x:Key="{x:Type Button}" TargetType="{x:Type ButtonBase}">
    <Setter Property="SnapsToDevicePixels" Value="True"/>
    <Setter Property="OverridesDefaultStyle" Value="True"/>
    <Setter Property="Foreground" Value="#FFDDDDDD"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Button}">
                <Border BorderBrush="{DynamicResource ControlBorderBrush}" 
                        BorderThickness="1,1,1,1" CornerRadius="4,4,4,4">
                    <Border 
                        Background="{DynamicResource ControlBackgroundBrush}"
                        BorderBrush="{DynamicResource ControlInnerBorderBrush}"
                        BorderThickness="1,1,1,1" CornerRadius="4,4,4,4"
                        x:Name="border">
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition/>
                                <RowDefinition/>
                            </Grid.RowDefinitions>
                            <Border 
                                Background="{DynamicResource ControlGlowBrush}"
                                x:Name="glow" 
                                Grid.RowSpan="2" CornerRadius="4,4,4,4" 
                                Opacity="0"/>
                            <ContentPresenter 
                                HorizontalAlignment="Center"
                                VerticalAlignment="Center"
                                Grid.RowSpan="2" Margin="4,4,4,4"/>
                            <Border 
                                Background="{DynamicResource ControlShineBrush}" 
                                Grid.RowSpan="1" CornerRadius="4,4,0,0" 
                                x:Name="shine"/>
                        </Grid>
                    </Border>
                </Border>
                <ControlTemplate.Triggers>
                    (...)
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

And suddenly the button starts to display the underscores as part of its content:

Obviously this should be taken care of when we created the style, but it is easy to forget if you create a style without using some base code. If you do not know the code base that well this is something that may end up consuming you a lot of precious time. Once you know how to fix this, this is something you will never, ever, forget! Yes, unfortunately, I am basing this on my personal experience!

The Solution

The solution to this problem could not be simpler:

(...)

<ContentPresenter 
    HorizontalAlignment="Center" VerticalAlignment="Center"
    Grid.RowSpan="2" Margin="4,4,4,4"
    RecognizesAccessKey="True"/>

(...)

You just need to check that your ControlTemplate has the RecognizesAccessKey property set to true.

This way, when you add underscores to the text content of a button they will be processed as access keys.

WPF Toolkit DataGrid, Part I – Introduction

Recently I had to recreate in WPF a Windows Forms panel that used DevExpress XtraGrid to present data in tabular form, keeping the same look and feel as it was part of an existing application. I Googled around to find out what different solutions exist out there for presenting data this way in WPF. After searching for a while and looking at solutions I found a few interesting ones:

I ended up using WPF Toolkit. The main reasons were: WPF Toolkit is free, open source and it was the one that allowed me greater flexibility. In this series of articles I intend to present you with all the functionality I managed to implement on my UserControl. For obvious reasons I will not disclose the code developed within our project but will provide working samples of the most relevant achievements in detail using a sample application.

To access WPF Toolkit’s DataGrid start by downloading and installing the latest release on your PC. You can download it from the link provided here.

Roadmap

In this article we will go through the following topics:

  • Hands on your First WPF DataGrid
  • DataGridColumn Hierarchy
  • Using DataGridColumns to enhance the DataGrid sample

Hands on your first WPF DataGrid

After installing WPF Toolkit we can start by creating a new WPF Application within Visual Studio and add the WPF Toolkit reference on it, which can be accessed through the ‘Add Reference’ .Net tab.

I believe that going through a live sample is the best way to adopt a new technology and as so we will dive directly into using the code instead of first going through a bunch of theory and only afterwards peaking on some code. Adding a DataGrid to a WPF application can be done quite easily:

<UserControl x:Class="SampleWpfDataGrid.PeopleView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:WpfToolkit="http://schemas.microsoft.com/wpf/2008/toolkit">
    <WpfToolkit:DataGrid
        ItemsSource="{Binding Path=PlayerAges}"
        HorizontalScrollBarVisibility="Hidden" SelectionMode="Extended">
    </WpfToolkit:DataGrid>
</UserControl>

We just need to instantiate a DataGrid object from the WPF Toolkit and bind it to a collection in order to display its data. In this example we use the AutoGenerateColumns feature of the DataGrid to generate the columns so that all the information in PlayerAges gets correctly displayed – note that this is enabled by default.

Basically what you see are two teams of people each containing 5 members. The grid will display a series of data regarding each team member’s age and its deviation towards the team players average age.

In the sample application I have separated the logic from the view in a way that the MVVM pattern can easily be used by specifying a DataTemplate for the ViewModel. There is a class which represents the ViewModel – TeamModel – and it contains a list of PlayerAge entries. Each row in the DataGrid represents a PlayerAge.  I then use a special row to represent the Team that I can use in conjunction with the IsExpanded plus IsSubItem properties to group the items on the list by teams. The DataGrid is added to the PeopleView UserControl that is used on the SampleWindow.

For now you can download a working sample of what you have seen so far from this link.

This is a straight forward example on how to use the WPF Toolkit’s DataGrid. It shows you how easy it is for you to present data and allow users to edit it. Obviously this alone is not of much use because you will want to style your grid and give it a set of behaviors according to whatever state your application is in. We will look on how to customize and give this DataGrid a nice look and feel along with some additional functionality.

DataGridColumn Hierarchy

Before going on with our sample lets have a look on how the WPF Toolkit DataGrid is structured in regard to the default columns types provided.

We have a DataGrid class to which we bind a data source and then use the DataGridColumn’s to specify how the data will be presented for each field according to that field’s type. There are several types of DataGridColumns available: DataGridHyperLinkColumn, DataGridCheckBoxColumn, DataGridTextColumn, DataGridComboBoxColumn and DataGridTemplateColumn. Most of these column types are self explanatory, we have columns for text, check boxes, etc. The Template Column is perhaps the one that may not be so clear, it is a column that can be used in conjunction with ControlTemplates to create whatever type of column you require, being it a Chart, a user control, etc.

Using DataGridColumns to enhance the DataGrid sample

Lets pick up our DataGrid sample project and create columns for all the fields we want to present. Since some fields will only be used for logic you must hide all other from the user. Only IsEnabled, Name, Age, Deviation, Category and DeviationPercentage fields will be visible.

The first thing you have to do so that you can control what columns are displayed is to set the AutoGenerateColumns to false on the DataGrid. Afterwards you can start adding columns and binding them to their corresponding fields. This is an example on how you can define your columns:

<WpfToolkit:DataGrid
    ItemsSource="{Binding Path=PlayerAges}"
    HorizontalScrollBarVisibility="Hidden" SelectionMode="Extended"
    CanUserAddRows="False" CanUserDeleteRows="False"
    CanUserResizeRows="False" CanUserSortColumns="False"
    AutoGenerateColumns="False"
    RowHeaderWidth="17" RowHeight="25">
    <WpfToolkit:DataGrid.Columns>
        <WpfToolkit:DataGridCheckBoxColumn
            Header="Enabled" Width=".5*"
            Binding="{Binding Path=IsEnabled}"/>
        <WpfToolkit:DataGridTextColumn
            Header="Player Name" Width="2*"
            Binding="{Binding Path=Name}"/>
        <WpfToolkit:DataGridTextColumn
            Header="Age" Width="1*"
            Binding="{Binding Path=Age}"/>
        <WpfToolkit:DataGridTextColumn
            Header="Deviation" Width="1*"
            Binding="{Binding Path=Deviation}"/>
        <WpfToolkit:DataGridComboBoxColumn
            Header="Category" Width="1*"
            ItemsSource="{StaticResource Categories}"
            SelectedValueBinding="{Binding Path=Category}"
            TextBinding="{Binding Path=Category}" />
        <WpfToolkit:DataGridTextColumn
            Header="Deviation Chart" Width="1*"
            Binding="{Binding Path=DeviationPercentage}"/>
    </WpfToolkit:DataGrid.Columns>
</WpfToolkit:DataGrid>

Notice that you started by tweaking a bit the DataGrid to conform with some of the required features. You have disabled a series of functionality mainly because there is no need to handle new rows input. Sorting does not make sense has groups are being used, etc – most of it is intuitively understandable. Notice that AutoGenerateColumns was set to false and that RowHeight was set to 25 so that when the user is picking something from the category combo box the row does not need to grow to allow the layout of the control – if you wish, just remove this value from the DataGrid and watch how picking something from the Category combo box now looks.

You have added a DataGridCheckBoxColumn and linked it to our IsEnabled field. Added a series of DataGridTextColumns binding them to Name, Age and Deviation along with a DataGridComboBoxColumn to bind to the player’s category. Finally, for now, notice how the Deviation Chart column was set to a DataGridTextColumn, this will be replaced by a DataGridTemplateColumn in the future when we get into styling the DataGrid.

Each column has been given a Header name and a Width according to star notation (as in WPF Grid control). Has you can see from the code, the Name column should be twice as large as, for instance, the Age column. This allows us to create user friendly layouts for our columns without too much effort. This is how the sample application is currently looking:

To see a working sample of this new layout please download this file.

End of Part I

You have given the first steps in understanding how to use the WPF Toolkit’s DataGrid in an application. You are now able to create simple DataGrids and specify columns to better control how your data gets presented to the user.

Much is there to find out about the WPF Toolkit DataGrid. Stay tuned for future blog posts has I will go through more complex features including a complete restyling of the DataGrid.