BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

Windows Presentation Foundation
.NET 4.0+

WPF Data Display Controls - DataGrid - Sorting

The one hundred and eleventh part of the Windows Presentation Foundation Fundamentals tutorial continues the examination of the DataGrid control. This article explains how data can be sorted.

DataGrid

In recent articles we've started to examine the DataGrid control. This complex WPF control allows you to organise collections into a grid of rows and columns. So far, we've seen the basics of creating a grid and the types of column that are available. In this article we'll add to the earlier ones by looking at sorting the data in a grid.

To demonstrate how to order one or more columns, we'll extend the example code that we created in the previous article. The sample project generated a window that displayed the details of the planets in the solar system. If you do not have the example project, you can download it using the link at the top of the article, "WPF Data Display Controls - DataGrid - Column Types".

User Sorting

Unless you've disabled the feature, the user can sort the data in a DataGrid by clicking the column headers. A single click of the header of an unsorted column sorts the entire data set into ascending order, according to the data in the column. Clicking a second time reverses the order and a third click removes the sorting altogether. Holding the Shift key whilst clicking column headers adds them to the sort. For example, you could click the header of the "Category" column in the sample project, then hold Shift and click the "Name" column's header. This would sort the rows by category first, then sort any planets with matching categories by planet name.

Try running the program any sorting the grid by one or more columns. You might notice that the "More" and "Moons" columns do not seem to sort correctly. We'll fix this later.

Sorting Using XAML

Often you will want to set the initial order of the data in a data grid. You can do this with pure XAML, rather than sorting the information in code before it is data bound. To do so, you simply add a collection view to the XAML, using a CollectionViewSource object. The view is bound to the underlying collection, marked up with SortDescription elements and given a unique key. The DataGrid control is then bound to the view, instead of the original data.

Let's set up a collection view so that the data grid is sorted by planet name on launching. We'll need to reference the SortDirection class from the XAML. This is found in the System.ComponentModel namespace, so declare the following alias within the Window tag.

xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"

We can now add the collection view source as a resource for the Window. It is bound directly to the data context, as this holds a simple collection of Planets.

<Window.Resources>
    <CollectionViewSource x:Key="SortedPlanets" Source="{Binding}">
        <CollectionViewSource.SortDescriptions>
            <scm:SortDescription PropertyName="Name" Direction="Ascending" />
        </CollectionViewSource.SortDescriptions>
    </CollectionViewSource>
</Window.Resources>

Finally, modify the ItemsSource attribute of the DataGrid so that it is bound to the view, instead of the data context:

<DataGrid ItemsSource="{Binding Source={StaticResource SortedPlanets}}"
          AutoGenerateColumns="False" Hyperlink.Click="DataGrid_Click">

Run the program to see the results. The planets will be shown in name order. You will still be able to change the ordering by clicking column headers.

Disabling Sorting

You can disable the sorting feature to prevent the user from changing the order of the data in a grid. If you want to completely disable sorting, set the DataGrid's CanUserSortColumns property to false, as follows:

<DataGrid ItemsSource="{Binding Source={StaticResource SortedPlanets}}"
          AutoGenerateColumns="False" Hyperlink.Click="DataGrid_Click"
          CanUserSortColumns="False">

With the above change, clicking any of the column headers has no effect. However, the sorting that was applied by the collection view is still applied.

Sometimes you will want to permit sorting for some columns whilst disabling it for others. This is achieved by setting the CanUserSortColumns property for the DataGrid to true, or omitting the attribute altogether, and setting the CanUserSort property of the unsortable columns to false.

To demonstrate, remove the CanUserSortColumns declaration from the DataGrid element, then change the Name column's XAML, as follows.

<DataGridTextColumn Binding="{Binding Name}" Header="Planet" Width="80"
                    Foreground="Blue" FontWeight="Bold" CanUserSort="False"/>

Run the program again to see the results. It will no longer be possible to sort the Name column.

Setting the Sort Member

At the beginning of this article we saw that two columns so not sort correctly. The "More" column appears to be sortable but when you try, the information is not ordered as expected. This is because the underlying data is held in Uri objects. It is these that are being sorted, not the visible text. The "Moons" column is not sortable because it is not bound to a property. It uses a data template to show the data, so it is not possible to determine how to order the information.

Both problems can be solved by setting the sort members for the columns. A sort member specifies the property to sort. If you set this to the Name property for the "More" column, the data will be sorted by the visible text. For the Moons column, we can specify the Moons property, which is an integer.

You set the sort member using the SortMemberPath property of the column. To demonstrate, replace the XAML for the last two columns with the code shown below:

<DataGridHyperlinkColumn Binding="{Binding MoreInfoUri}" Header="More" Width="80"
                         ContentBinding="{Binding Name}" SortMemberPath="Name" />
<DataGridTemplateColumn Header="Moons" Width="50" SortMemberPath="Moons">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Moons}" />
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
    <DataGridTemplateColumn.CellEditingTemplate>
        <DataTemplate>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition Width="Auto"/>
                </Grid.ColumnDefinitions>
                <TextBlock Text="{Binding Moons}" />
                <ScrollBar Grid.Column="1" Minimum="0" Maximum="100" SmallChange="1"
                            Orientation="Vertical"
                            Value="{Binding Moons,UpdateSourceTrigger=PropertyChanged}"
                            RenderTransformOrigin="0.5,0.5">
                    <ScrollBar.RenderTransform>
                        <ScaleTransform ScaleY="-1"/>
                    </ScrollBar.RenderTransform>
                </ScrollBar>
            </Grid>
        </DataTemplate>
    </DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>

Run the program and attempt to sort the grid using the "More" and "Moons" columns. They should now operate as expected.

11 November 2014