WPF DataGrid: Stock and Template Columns

Overview

The DataGrid uses a set of DataGridColumns to describe how to display its data just like a GridView has a set of GridViewColumns to describe its data. In my first post, the sample I used auto-generated the columns for you but this time I want to go over how to create the columns manually.

Column Types

Currently in our WPF DataGrid we have four available stock columns and a template column:

· DataGridTextColumn

· DataGridCheckBoxColumn

· DataGridComboBoxColumn

· DataGridHyperlinkColumn

· DataGridTemplateColumn

Each column type corresponds to the UIElement that it will show in each of the cells in that column. The DataGridTemplateColumn allows you to customize the template with a custom UIElement or tree of Elements for each of the cells in the column. Template columns derive from DataGridColumn, whereas stock columns derive from DataGridBoundColumn which derives from DataGridColumn.

Why the difference in inherited classes? The main difference is that a DataGridBoundColumn includes the property Binding, which is a convenience property for mapping a data item property in the ItemsSource to its corresponding set of cells in the column. Here is an example:

<dg:DataGridTextColumn Binding="{Binding Path=FirstName}" />

 

The concept is basically the same as setting the DisplayMemberBinding property on a GridViewColumn of a GridView. I also talked about this in my post, Dissecting the Visual Layout. For a DataGridTemplateColumn, since you are free to set the template to whatever you like, we cannot automatically figure out where you would like to place the binding. Because of that, you are required to setup the binding yourself and therefore the class does not derive from DataGridBoundColumn.

Stock Columns and styling

For stock columns or DataGridBoundColumns, the main properties you will likely use are the Binding property, which I describe above, and the Header property. Header is just like GridViewColumn.Header, which displays the text on the DataGridColumnHeader of that column.

<dg:DataGridTextColumn Header="First Name" Binding="{Binding Path=FirstName}" />

 

Styling a stock column can be accomplished with these particular properties:

· CellStyle

· EditingElementStyle

· ElementStyle

Recall in my post, Dissecting the Visual Layout, when the DataGridCell is created its Content property is set after asking DataGridColumn to generate the visual tree. Well, depending if you are in an editing state or not, the column will generate a different UIElement for each state. For a DataGridTextColumn in an editing state, a TextBox is created. In a non-editing state, a TextBlock is created. For a DataGridComboBoxColumn in an editing state, a ComboBox is created, while in a non-editing state, a TextBlock is created.

With that in mind, CellStyle is the style for the overall DataGridCell (which is a ContentControl and items container), EditingElementStyle is the style for the DataGridCell’s Content generated during the editing state and ElementStyle is the style for the DataGridCell’s Content generated during the non-editing state. Here is an example:       

<dg:DataGridTextColumn Header="First Name" Binding="{Binding Path=FirstName}">

  <dg:DataGridTextColumn.CellStyle>

    <Style TargetType="{x:Type dg:DataGridCell}">

      <Setter Property="Foreground" Value="Blue" />

      <Setter Property="FontWeight" Value="Bold" />

    </Style>

  </dg:DataGridTextColumn.CellStyle>

  <dg:DataGridTextColumn.EditingElementStyle>

  <Style TargetType="TextBox">

      <Setter Property="Foreground" Value="Red" />

    </Style>

  </dg:DataGridTextColumn.EditingElementStyle>

  <dg:DataGridTextColumn.ElementStyle>

    <Style TargetType="TextBlock">

      <Setter Property="Foreground" Value="LightBlue" />

    </Style>

  </dg:DataGridTextColumn.ElementStyle>

</dg:DataGridTextColumn>

 

You should note that the Bold FontWeight will be applied to both the editing and non-editing element while the Foreground property set in CellStyle will be overwritten by the Foreground property set by each of the element styles.

Template Columns

For DataGridTemplateColumns, the Header and CellStyle property still apply but there isn’t a Binding, EditingElementStyle, or ElementStyle property. Instead, you have the properties, CellTemplate and CellEditingTemplate. DataGridCell’s Content generation is still the same; it’s just that it uses these two cell templates to generate the content instead. Here is an example:

<dg:DataGridTemplateColumn Header="First Name">

  <dg:DataGridTemplateColumn.CellTemplate>

    <DataTemplate>

      <Button Content="{Binding Path=FirstName}" />

    </DataTemplate>

  </dg:DataGridTemplateColumn.CellTemplate>

  <dg:DataGridTemplateColumn.CellEditingTemplate>

    <DataTemplate>

      <TextBox Text="{Binding Path=FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />

    </DataTemplate>

  </dg:DataGridTemplateColumn.CellEditingTemplate>

</dg:DataGridTemplateColumn>

 

In a non-editing state a Button is created as the UIElement for the cell and is bound to the FirstName property of my data source. In an editing state a TextBox is created as the UIElement. Having an editable Button in a DataGrid may not be a common scenario but I did want to show both CellTemplate and CellEditingTemplate. If you do not want to make it editable, one thing you can do is just not declare the CellEditingTemplate. Notice in both templates I setup the binding myself since I do not have a Binding like DataGridBoundColumns.

Important: Also notice that in CellEditingTemplate I had to declare the binding with Mode set to TwoWay and UpdateSourceTrigger set to PropertyChanged. This is another consequence of not getting the binding utilities that DataGridBoundColumns provide. In a DataGridBoundColumn, when cells are edited and committed, the DataGrid will take care of updating the sources for you. In the case of the template column, you will have to update the source yourself. This issue is only for a commit scenario. The DataGrid will still be able to rollback on a cancel command.

Sizing Columns

Column sizing uses a special class called DataGridLength that has sizing properties specifically for a row and column scenario. The types of widths are as follows:

· Pixel

· SizeToCells

· SizeToHeader

· Auto

· Star

 Pixel uses absolute sizing on the column width, SizeToCells sizes the column width to the largest cell, SizeToHeader sizes the column width to the header width, Auto sizes the column width to either the largest cell or the header width whichever is larger, and Star sizing follow the proportional sizing like a Grid panel. See the final example below for usage of the different widths.

Putting it all together

So with all that, I have updated the sample from my first post on DataGrid with what I discuss here. Instead of auto-generating the columns, I’ve explicitly declared each column and I’ve setup different widths for you to get an idea of how that works. You can also double-click on the column header grippers to change the width to Auto. This functionality is similar to Windows Explorer. I’ve also created a template column that displays an Image UIElement when in non-edit mode and a CombBox UIElement when in editing mode. Notice the differences in how I setup the data bindings between the template column versus the bound columns. I’ve also added a DataGridCell style and DataGridRow style that updates the BorderBrush and BorderThickness when in edit mode. You can download the sample here.

<Style x:Key="defaultCellStyle" TargetType="{x:Type dg:DataGridCell}">

  <Style.Triggers>

      <Trigger Property="IsEditing" Value="True">

          <Setter Property="BorderBrush" Value="Red" />

          <Setter Property="BorderThickness" Value="2" />

      </Trigger>

  </Style.Triggers>

</Style>

<dg:DataGrid AutoGenerateColumns="False" CellStyle="{StaticResource defaultCellStyle}" …>

  <dg:DataGrid.Columns>

      <dg:DataGridTextColumn Width="130" Header="First Name" Binding="{Binding Path=FirstName}" />

      <dg:DataGridTextColumn Width="Auto" Header="Last Name" Binding="{Binding Path=LastName}" />

      <dg:DataGridCheckBoxColumn Width="SizeToCells" Header="Likes Cake" Binding="{Binding Path=LikesCake}" />

      <dg:DataGridComboBoxColumn Width="200" Header="Cake" SelectedItemBinding="{Binding Path=Cake}">

          <dg:DataGridComboBoxColumn.ItemsSource>

                 <col:ArrayList>

                       <sys:String>Chocolate</sys:String>

                       <sys:String>Vanilla</sys:String>

                 </col:ArrayList>

          </dg:DataGridComboBoxColumn.ItemsSource>

      </dg:DataGridComboBoxColumn>

      <dg:DataGridHyperlinkColumn Width="SizeToHeader" Header="Homepage" Binding="{Binding Path=Homepage}" />

      <dg:DataGridTemplateColumn MaxWidth="250" Header="Picture">

          <dg:DataGridTemplateColumn.CellTemplate>

                 <DataTemplate>

                       <Image Source="{Binding Path=Picture}" />

                 </DataTemplate>

          </dg:DataGridTemplateColumn.CellTemplate>

          <dg:DataGridTemplateColumn.CellEditingTemplate>

                 <DataTemplate>

                       <ComboBox SelectedItem="{Binding Path=Picture, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">

                             <ComboBox.ItemsSource>

                                   <col:ArrayList>

                                     <sys:String>Assets\Autumn Leaves.jpg</sys:String>

                                     <sys:String>Assets\Butterfly.JPG</sys:String>

                                     <sys:String>Assets\Green Sea Turtle.jpg</sys:String>

                                   </col:ArrayList>

                             </ComboBox.ItemsSource>

                       </ComboBox>

                 </DataTemplate>

          </dg:DataGridTemplateColumn.CellEditingTemplate>

      </dg:DataGridTemplateColumn>

  </dg:DataGrid.Columns>

</dg:DataGrid>

 

More Related Material:

Dissecting the Visual Layout

Working with DataGridComboBoxColumn (Part1)

Working with DataGridComboBoxColumn (Part2)

Overview of the editing features in the DataGrid

Other Samples:

ScrollViewer with ToolTip

Custom sorting, column selection, single-click editing

Tri-state sorting

 

DataGrid_V1_StockAndTemplateColumnsSample.zip