Introduction
By default, there is no easy way of sorting a ListView
in WPF. While there is a sample available on MSDN on how to sort a ListView
, it isn’t very satisfying. The sample sorts the ListView
based on the header of the column instead of the actual DisplayMemberBinding
attribute. This is not an optimal solution, because column headers don’t necessarily contain text. Secondly, it could be that the column header differs from the actual property name. In addition to that, I assume that this isn’t working very well in a multi-language application either.
Background
I wanted a more generic solution to this issue. After playing around a while, I ended up with the solution shown below.
Requirements:
- The solution needs to be easy to be attached to a
ListView
without adding a lot of code each time. The new attached properties functionality from WPF, using the Ramora pattern solves this issue nicely.
- The sorting needed to be customizable. By using the
CustomSort
property of the ListCollectionView
, this could be accomplished easily.
Using the code
To create a custom comparer, you need to implement the abstract base class ListViewCustomComparer<>
and create your own comparer. In the sample application, I created a PersonComparer
:
public class PersonComparer : ListViewCustomComparer<Person>
{
public override int Compare(Person x, Person y)
{
try
{
String valueX = String.Empty, valueY = String.Empty;
switch (SortBy)
{
default:
case "FirstName":
valueX = x.FirstName;
valueY = y.FirstName;
break;
case "LastName":
valueX = x.LastName;
valueY = y.LastName;
break;
case "Age":
if (SortDirection.Equals(ListSortDirection.Ascending))
return x.Age.CompareTo(y.Age);
else
return (-1) * x.Age.CompareTo(y.Age);
}
if (SortDirection.Equals(ListSortDirection.Ascending))
return String.Compare(valueX, valueY);
else
return (-1) * String.Compare(valueX, valueY);
}
catch (Exception)
{
return 0;
}
}
The SortBy
value is the value of the DisplayMemberBinding
, which is set in the MainWindow.xaml markup code:
MainWindow.xaml:
<GridViewColumn Header=“FirstName“ Width=“100“ DisplayMemberBinding=“{Binding FirstName}“ />
<GridViewColumn Header=“LastName“ Width=“100“ DisplayMemberBinding=“{Binding LastName}“ />
lt;GridViewColumn Header=“Age“ Width=“100“ DisplayMemberBinding=“{Binding Age}“ />
In the MainWindow
itself, you need to add the local xml:ns and the data templates, which are used to display a different header with arrows on sorted columns. You also need to add the ListViewSorter.CustomListViewSorter
attribute to the ListView
XML element.
<Window x:Class="WpfListViewSorting.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfListViewSorting"
Title="WpfListViewSorting Demo" Height="300" Width="400">
<Window.Resources>
<DataTemplate x:Key="ListViewHeaderTemplateDescendingSorting">
<DockPanel>
<TextBlock Text="{Binding}"/>
<Path x:Name="arrow"
StrokeThickness = "1"
Fill = "gray"
Data = "M 5,10 L 15,10 L 10,5 L 5,10"/>
</DockPanel>
</DataTemplate>
<DataTemplate x:Key="ListViewHeaderTemplateAscendingSorting">
<DockPanel>
<TextBlock Text="{Binding }"/>
<Path x:Name="arrow"
StrokeThickness = "1"
Fill = "gray"
Data = "M 5,5 L 10,10 L 15,5 L 5,5"/>
</DockPanel>
</DataTemplate>
<DataTemplate x:Key="ListViewHeaderTemplateNoSorting">
<DockPanel>
<TextBlock Text="{Binding }"/>
</DockPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<ListView Margin="5"
VirtualizingStackPanel.IsVirtualizing="True"
IsSynchronizedWithCurrentItem="True"
local:ListViewSorter.CustomListViewSorter="WpfListViewSorting.PersonComparer"
x:Name="lstView" >
<ListView.View>
<GridView AllowsColumnReorder="True">
<GridViewColumn Header="FirstName" Width="100"
DisplayMemberBinding="{Binding FirstName}" />
<GridViewColumn Header="LastName" Width="100"
DisplayMemberBinding="{Binding LastName}" />
<GridViewColumn Header="Age" Width="100"
DisplayMemberBinding="{Binding Age}" />
</GridView>
</ListView.View>
</ListView >
</Grid>
</Window>
This is all the code that needs to be added to add sorting to a ListView
. For the next ListView
, you only need to add the local:ListViewSorter.CustomListViewSorter = "WpfListViewSorting.PersonComparer"
attribute to it to activate its working (if the bound object is the same; otherwise, create a new comparer). The ListViewSorter
will keep track of the previous sorting direction and sorting column in a Dictionary
, which holds a collection of ListViewSortItem
s.
Points of interest
None yet. Happy coding!
History
- 24-05-2008 - Modified the code sample to check for Visual Studio design mode, so it won't cause any errors.