Click here to Skip to main content
Click here to Skip to main content
Go to top

WPF DataGrid: Using DataTemplates for auto-generated columns

, 4 Nov 2009
Rate this:
Please Sign up or sign in to vote.
An article on using templates with auto-generated columns in the WPF DataGrid.

Introduction

The DataGrid is a control for displaying tabular data. Setting AutoGenerateColumns to true will automatically generate a column for each field. While this is quite useful, it does not give much control about the generated column. This article shows how you can use a DataTemplate for automatically generated columns with an attached property.

Implementation

The DataGrid provides an event called AutoGeneratingColumn. This event is fired for each individual column that is generated. You can adjust or cancel the generated column in the event handler. By setting DataGridAutoGeneratingColumnEventArgs.Column, you can provide your own column.

There is already a column class with support for templates, called DataGridTemplateColumn. This column has no binding support like DataGridBoundColumn. The data context for DataGridTemplateColumn is DataRowView. In order to reuse data templates for multiple fields, I created a custom class, CustomDataGridTemplateColumn, that extends DataGridTemplateColumn and adds binding support.

The AutoGeneratingColumn event handler is listed below. In this event handler, a new DataGridTemplateColumn column is created when the current column name is found in the AutoGenerateColumnCollection.

public static void _grid_AutoGeneratingColumn(
    object sender, DataGridAutoGeneratingColumnEventArgs e)
{
    DataGrid grid = sender as DataGrid;
    if (grid == null) { return; }

    AutoGenerateColumnCollection coll = GetColumns(grid);

    foreach (AutoGenerateColumn col in coll)
    {
        if (e.PropertyName == col.Column)
        {
            CustomDataGridTemplateColumn templateColumn = 
                    new CustomDataGridTemplateColumn();
            templateColumn.Header = e.Column.Header;
            if (col.CellTemplate != null)
            {
                templateColumn.CellTemplate = col.CellTemplate;
            }
            if (col.CellEditingTemplate != null)
            {
                templateColumn.CellEditingTemplate = col.CellEditingTemplate;
            }
            if (col.Binding != null)
            {
                templateColumn.Binding = col.Binding;
            }

            templateColumn.SortMemberPath = e.Column.SortMemberPath;
            e.Column = templateColumn;
            return;
        }
    }

    return;
}

The AutoGenerateColumnCollection originates from the attached property GenerateTemplateColumn.Columns. AutoGenerateColumnCollection is a class that extends ObservableCollection<AutoGenerateColumn> and is used to store the column names and templates.

The AutoGenerateColumn class is listed below:

public class AutoGenerateColumn
{
    public string Column
    {
        get; set;
    }

    public DataTemplate CellTemplate
    {
        get; set;
    }

    public DataTemplate CellEditingTemplate
    {
        get; set;
    }

    public System.Windows.Data.BindingBase Binding
    {
        get; set;
    }
}

The AutoGeneratingColumn event handler is registered when the attached property changes. The PropertyChangedCallback for the attached property is shown below:

private static void OnColumnsChanged(
    DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    if (e.NewValue != null)
    {
       AutoGenerateColumnCollection coll = 
                e.NewValue as AutoGenerateColumnCollection;

        DataGrid grid = coll.Owner as DataGrid;
        if (grid != null)
        {
            grid.AutoGeneratingColumn += _grid_AutoGeneratingColumn;
        }
    }

    if (e.OldValue != null)
    {
        AutoGenerateColumnCollection coll = 
                e.OldValue as AutoGenerateColumnCollection;

        DataGrid grid = coll.Owner as DataGrid;
        if (grid != null)
        {
            grid.AutoGeneratingColumn -= _grid_AutoGeneratingColumn;
        }
    }
}

Using the code

To use the code in your own project, you will need to include AutoGenerateColumn.cs and CustomDataGridTemplateColumn.cs.

You can specify the templates using the attached property GenerateTemplateColumn.Columns. The XAML below (from the sample application) shows how to specify templates for a column:.

<toolkit:DataGrid x:Name="dataGrid1" AutoGenerateColumns="True">
    <local:GenerateTemplateColumn.Columns>
        <local:AutoGenerateColumn Column="ItemCreatedStamp"
            CellTemplate="{StaticResource TimeStampCellTemplate}"
            CellEditingTemplate="{StaticResource CellEditTemplate}"
            Binding="{Binding Path=ItemCreatedStamp}"/>
        <local:AutoGenerateColumn Column="ItemUpdatedStamp"
            CellTemplate="{StaticResource TimeStampCellTemplate}"        
            CellEditingTemplate="{StaticResource CellEditTemplate}"
            Binding="{Binding Path=ItemUpdatedStamp}"/>
        <local:AutoGenerateColumn Column="ItemLink"
            CellTemplate="{StaticResource HttpHyperCellTemplate}"
            CellEditingTemplate="{StaticResource CellEditTemplate}"
            Binding="{Binding Path=ItemLink}"/>
        <local:AutoGenerateColumn Column="ItemStatusId"
            CellTemplate="{StaticResource StatusCellTemplate}"
            CellEditingTemplate="{StaticResource CellEditTemplate}"
            Binding="{Binding Path=ItemStatusId}" />
    </local:GenerateTemplateColumn.Columns>
</toolkit:DataGrid>

A template can be as simple as:

<DataTemplate x:Key="CellTemplate">
    <TextBlock Text="{Binding}" BorderThickness="0" Padding="0" />
</DataTemplate>

This way, you can easily customize the look of auto-generated columns.

Sample application

To build the demo project, you will need SQLite.NET and the WPF Toolkit.

The sample application includes a sample SQLite3 database (database.db) to demonstrate the use of templates. In the sample application, you can run any query and open different SQLite databases. By using templates, you can make the output more useful.

To query data from the main table:

SELECT * FROM Item

Running this query will show columns where templates are used. For example, a template is used for showing clickable links.

Sample application extra functionality

To make the sample application a bit more useful, code to allow the user to copy the data from the DataGrid to the clipboard is included. The code therefore was based on the code found in the links listed below:

TextBox context menu in DataGrid edit modus

While using the DataGrid, I noticed that the context menu of the TextBox was not working correctly in edit modus. When you right click on a TextBox in edit mode, the context menu is shown, but the items are disabled, which prevents you from using it. The reason for this is that when opening the context menu, the focus changes and the DataGridCell goes out of edit modus.

The solution is based on WPF DataGrid Sample: Locking input to the row being edited. In the CellEditEnding event, you cancel the event when a context menu is open. In order to detect whether a context menu is open, you can use the ContextMenuOpening and ContextMenuClosing events. The TextBox inside the DataGridCell uses TextEditorContextMenu.OnContextMenuOpening, which prevents you from receiving the events. In order to catch the event, you can use a custom ContextMenu like shown below:

<ContextMenu x:Key="textBoxMenu">
    <MenuItem Command="ApplicationCommands.Cut">
        <MenuItem.Icon>
            <Image Source="Resources\cut.png"/>
        </MenuItem.Icon>
    </MenuItem>
    <MenuItem Command="ApplicationCommands.Copy">
        <MenuItem.Icon>
            <Image Source="Resources\copy.png"/>
        </MenuItem.Icon>
    </MenuItem>
    <MenuItem Command="ApplicationCommands.Paste">
        <MenuItem.Icon>
            <Image Source="Resources\paste.png"/>
        </MenuItem.Icon>
    </MenuItem>
</ContextMenu>

You can set this context menu in various ways, when using auto-generated columns directly on the TextBox in the template, or using a style. See _grid_AutoGeneratingColumn in the sample application for non-templated auto generated columns.

Points of interest

While using templates for auto-generated columns is useful, the templates are applied based on the column name. When querying different data with the same column names, this can lead to unintended results.

History

  • 4-11-2009: Initial article upload.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Ewout van der Linden

Netherlands Netherlands
No Biography provided

Comments and Discussions

 
GeneralTurning it more useful Pinmemberpaulosmasher6-Nov-09 2:57 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140926.1 | Last Updated 4 Nov 2009
Article Copyright 2009 by Ewout van der Linden
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid