Best way to make WPF ListView/GridView sort on column-header clicking? Best way to make WPF ListView/GridView sort on column-header clicking? wpf wpf

Best way to make WPF ListView/GridView sort on column-header clicking?


I wrote a set of attached properties to automatically sort a GridView, you can check it out here. It doesn't handle the up/down arrow, but it could easily be added.

<ListView ItemsSource="{Binding Persons}"          IsSynchronizedWithCurrentItem="True"          util:GridViewSort.AutoSort="True">    <ListView.View>        <GridView>            <GridView.Columns>                <GridViewColumn Header="Name"                                DisplayMemberBinding="{Binding Name}"                                util:GridViewSort.PropertyName="Name"/>                <GridViewColumn Header="First name"                                DisplayMemberBinding="{Binding FirstName}"                                util:GridViewSort.PropertyName="FirstName"/>                <GridViewColumn Header="Date of birth"                                DisplayMemberBinding="{Binding DateOfBirth}"                                util:GridViewSort.PropertyName="DateOfBirth"/>            </GridView.Columns>        </GridView>    </ListView.View></ListView>


MSDN has an easy way to perform sorting on columns with up/down glyphs. The example isn't complete, though - they don't explain how to use the data templates for the glyphs. Below is what I got to work with my ListView. This works on .Net 4.

In your ListView, you have to specify an event handler to fire for a click on the GridViewColumnHeader. My ListView looks like this:

<ListView Name="results" GridViewColumnHeader.Click="results_Click">    <ListView.View>        <GridView>            <GridViewColumn DisplayMemberBinding="{Binding Path=ContactName}">                <GridViewColumn.Header>                    <GridViewColumnHeader Content="Contact Name" Padding="5,0,0,0" HorizontalContentAlignment="Left" MinWidth="150" Name="ContactName" />                </GridViewColumn.Header>            </GridViewColumn>            <GridViewColumn DisplayMemberBinding="{Binding Path=PrimaryPhone}">                <GridViewColumn.Header>                    <GridViewColumnHeader Content="Contact Number" Padding="5,0,0,0" HorizontalContentAlignment="Left" MinWidth="150" Name="PrimaryPhone"/>                </GridViewColumn.Header>            </GridViewColumn>        </GridView>    </ListView.View></ListView>

In your code behind, set up the code to handle the sorting:

// Global objectsBindingListCollectionView blcv;GridViewColumnHeader _lastHeaderClicked = null;ListSortDirection _lastDirection = ListSortDirection.Ascending;// Header click eventvoid results_Click(object sender, RoutedEventArgs e){    GridViewColumnHeader headerClicked =    e.OriginalSource as GridViewColumnHeader;    ListSortDirection direction;    if (headerClicked != null)    {    if (headerClicked.Role != GridViewColumnHeaderRole.Padding)    {        if (headerClicked != _lastHeaderClicked)        {            direction = ListSortDirection.Ascending;        }        else        {            if (_lastDirection == ListSortDirection.Ascending)            {                direction = ListSortDirection.Descending;            }            else            {                direction = ListSortDirection.Ascending;            }        }        string header = headerClicked.Column.Header as string;        Sort(header, direction);        if (direction == ListSortDirection.Ascending)        {            headerClicked.Column.HeaderTemplate =              Resources["HeaderTemplateArrowUp"] as DataTemplate;        }        else        {            headerClicked.Column.HeaderTemplate =              Resources["HeaderTemplateArrowDown"] as DataTemplate;        }        // Remove arrow from previously sorted header        if (_lastHeaderClicked != null && _lastHeaderClicked != headerClicked)        {            _lastHeaderClicked.Column.HeaderTemplate = null;        }        _lastHeaderClicked = headerClicked;        _lastDirection = direction;    }}// Sort codeprivate void Sort(string sortBy, ListSortDirection direction){    blcv.SortDescriptions.Clear();    SortDescription sd = new SortDescription(sortBy, direction);    blcv.SortDescriptions.Add(sd);    blcv.Refresh();}

And then in your XAML, you need to add two DataTemplates that you specified in the sorting method:

<DataTemplate x:Key="HeaderTemplateArrowUp">    <DockPanel LastChildFill="True" Width="{Binding ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type GridViewColumnHeader}}}">        <Path x:Name="arrowUp" StrokeThickness="1" Fill="Gray" Data="M 5,10 L 15,10 L 10,5 L 5,10" DockPanel.Dock="Right" Width="20" HorizontalAlignment="Right" Margin="5,0,5,0" SnapsToDevicePixels="True"/>        <TextBlock Text="{Binding }" />    </DockPanel></DataTemplate><DataTemplate x:Key="HeaderTemplateArrowDown">    <DockPanel LastChildFill="True" Width="{Binding ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type GridViewColumnHeader}}}">        <Path x:Name="arrowDown" StrokeThickness="1" Fill="Gray"  Data="M 5,5 L 10,10 L 15,5 L 5,5" DockPanel.Dock="Right" Width="20" HorizontalAlignment="Right" Margin="5,0,5,0" SnapsToDevicePixels="True"/>        <TextBlock Text="{Binding }" />    </DockPanel></DataTemplate>

Using the DockPanel with LastChildFill set to true will keep the glyph on the right of the header and let the label fill the rest of the space. I bound the DockPanel width to the ActualWidth of the GridViewColumnHeader because my columns have no width, which lets them autofit to the content. I did set MinWidths on the columns, though, so that the glyph doesn't cover up the column title. The TextBlock Text is set to an empty binding which displays the column name specified in the header.


It all depends really, if you're using the DataGrid from the WPF Toolkit then there is a built in sort, even a multi-column sort which is very useful. Check more out here:

Vincent Sibals Blog

Alternatively, if you're using a different control that doesn't support sorting, i'd recommend the following methods:

Li Gao's Custom Sorting

Followed by:

Li Gao's Faster Sorting