Diederik Krols

The XAML Brewer

A minimalistic template for an editable WPF DataGrid

By default, a WPF DataGrid operates in its birthday suit, with no decorations at all. For your end user this is not intuitive, so you should provide some fig-leafs here and there. In my humble opinion, at least two rows should be easily identifiable in any editable grid:

  • the 'new' row (generally at the bottom of the grid), and
  • the row that is currently being edited.

Both rows deserve their own template. As you certainly know, you can get very far with WPF templating. In this article however I'll go for the minimalistic approach and display

  • an asterisk in front of the 'new' row, and
  • a pencil in front of the row in edit mode.

An asterisk for the 'New' row

This is what we're trying to achieve:

We will get this result by applying a data template to the row's header. So start with a data template in XAML, somewhere in a resource element:

<DataTemplate x:Key="AsteriskTemplate" >

    <TextBlock

       Margin="0"

       HorizontalAlignment="Center"

       VerticalAlignment="Center"

       ToolTip="New"

       Text="*">

    </TextBlock>

</DataTemplate>

 

This template should be applied when loading the row, so hook an event handler to LoadingRow, also in XAML:

<toolkit:DataGrid

   x:Name="DriversDataGrid"

   ItemsSource="{Binding Source={x:Static local:FormulaOneDriver.GetAll}}"

   CommandManager.PreviewExecuted="DriversDataGrid_PreviewDeleteCommandHandler"

   LoadingRow="DriversDataGrid_LoadingRow"

   RowEditEnding="DriversDataGrid_RowEditEnding"

 

Here's the C# code for the event handler:

/// <summary>

/// Apply Asterisk DataTemplate to New Row

/// </summary>

/// <param name="sender">Sender of the event: the DataGrid.</param>

/// <param name="e">Event arguments.</param>

private void DriversDataGrid_LoadingRow(object sender, DataGridRowEventArgs e)

{

    if (e.Row.Item == CollectionView.NewItemPlaceholder)

    {

        e.Row.HeaderTemplate = (DataTemplate)DriversDataGrid.FindResource("AsteriskTemplate");

        e.Row.UpdateLayout();

    }

}

A pencil for the 'Edit' row

This is what we're trying to achieve: 

Again we define the look of the row header through a data template:

<DataTemplate x:Key="PencilTemplate" >

    <Image Source="/DataGridSample;component/pencil.png"

          Margin="0"

          Height="11" Width="11"

          HorizontalAlignment="Center" VerticalAlignment="Center"

           />

</DataTemplate>

 

The template is applied when we enter edit-mode. So you should handle the BeginningEdit event. When leaving edit mode, don't forget to revert to the default template, through the RowEditEnding event:

<toolkit:DataGrid

   x:Name="DriversDataGrid"

   ItemsSource="{Binding Source={x:Static local:FormulaOneDriver.GetAll}}"

   CommandManager.PreviewExecuted="DriversDataGrid_PreviewDeleteCommandHandler"

   LoadingRow="DriversDataGrid_LoadingRow"

   BeginningEdit="DriversDataGrid_BeginningEdit"

   RowEditEnding="DriversDataGrid_RowEditEnding"

 

Here's the event handler to apply the template:

/// <summary>

/// A row goes in edit mode. So display the pencil in its header.

/// </summary>

/// <param name="sender">Sender of the event: the DataGrid.</param>

/// <param name="e">Event arguments.</param>

private void DriversDataGrid_BeginningEdit(object sender, DataGridBeginningEditEventArgs e)

{

    e.Row.HeaderTemplate = (DataTemplate)DriversDataGrid.FindResource("PencilTemplate");

    e.Row.UpdateLayout();

}

 

And here's how to remove the template:

/// <summary>

/// A row leaves edit mode: persist it, and revert to the default header.

/// </summary>

/// <param name="sender">Sender of the event: the DataGrid.</param>

/// <param name="e">Event arguments.</param>

private void DriversDataGrid_RowEditEnding(object sender, DataGridRowEditEndingEventArgs e)

{

    [...]

    e.Row.HeaderTemplate = null;

}

Comments are closed