Click here to Skip to main content
15,884,473 members
Articles / Web Development / HTML
Tip/Trick

Built-in Automatic Sorting for WPF ListView Items

Rate me:
Please Sign up or sign in to vote.
5.00/5 (3 votes)
14 Feb 2015MIT1 min read 25.5K   483   4   1
A custom ListView class that uses reflection to sort columns based on the DisplayMemberBinding's bound data type

Introduction

For most LOB (Line Of Business) applications, a ListView is a common control used to display rows of data in a grid like format. In WinForms, the ListView is very easy to sort data based on a data type. In WPF however, this has been a sore spot for many developers because the WPF version of the control's built in sorting mechanism is based on simple string comparisons. The default ListView control also does not allow you to manipulate GridRowColumns so easily. This makes a custom sorting solution difficult in WPF.

Background

The solution I provide here is a small extended ListView class called ListViewSorter that "bakes-in" custom sorting logic on any bound types that subscribe to the IComparable interface (i.e. can be compared).

Using the Code

To use the ListViewSorter class, simply import the XML namespace containing the class into a Window or UserControl. Then use the control as you would the default in ListView control. Make sure you create GridViewColumns and set their DisplayMemberBinding attributes to a property of a view model. Run the program and click the headers to see the sorting in action.

ListViewSorter.xaml.cs:

C#
public partial class ListViewSorter : ListView
{
    private CustomSorter _customSorter = new CustomSorter();
    private ListSortDirection _sortDirection = ListSortDirection.Ascending;

    public ListViewSorter()
    {
        InitializeComponent();
    }

    private void GridViewColumnHeaderClickedHandler(object sender, RoutedEventArgs e)
    {
        GridViewColumnHeader columnHeader = e.OriginalSource as GridViewColumnHeader;
        if (columnHeader == null)
            return;
        Sort(columnHeader);
    }

    private void Sort(GridViewColumnHeader columnHeader)
    {
        Binding binding = columnHeader.Column.DisplayMemberBinding as Binding;
        if (binding != null)
        {
            ICollectionView dataView = CollectionViewSource.GetDefaultView(this.ItemsSource);
            ListCollectionView view = (ListCollectionView)dataView;
            _customSorter.SortPropertyName = binding.Path.Path;;
            view.CustomSort = _customSorter;
        }
    }

    public class CustomSorter : IComparer
    {
        private Dictionary<string, ListSortDirection>_dictOfSortDirections =
            new Dictionary<string, ListSortDirection>();     

        private string _sortPropertyName;
        public string SortPropertyName
        {
            get { return _sortPropertyName; }
            set
            {
                _sortPropertyName = value;
                if (!_dictOfSortDirections.ContainsKey(_sortPropertyName))
                {
                    _dictOfSortDirections.Add(_sortPropertyName, ListSortDirection.Ascending);
                }
                // Alternate sort directions inside the dictionary
                _dictOfSortDirections[_sortPropertyName] =
                    (_dictOfSortDirections[_sortPropertyName] == ListSortDirection.Ascending) ?
                        ListSortDirection.Descending : ListSortDirection.Ascending;
            }
        }

        public int Compare(object x, object y)
        {
            PropertyInfo pi = x.GetType().GetProperty(_sortPropertyName);
            if (pi != null)
            {
                object value1 = pi.GetValue(x);
                object value2 = pi.GetValue(y);

                bool valuesAreNotSortable = (!(value1 is IComparable) || !(value2 is IComparable));
                if (valuesAreNotSortable)
                    return 0;

                ListSortDirection dir = _dictOfSortDirections[_sortPropertyName];

                if (dir == ListSortDirection.Ascending)
                    return ((IComparable)value1).CompareTo(value2);
                else
                    return ((IComparable)value2).CompareTo(value1);
            }
            return 0;
        }
    }
}

ListViewSorter.xaml:

XML
<ListView x:Class="Controls.ListViewSorter"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      mc:Ignorable="d" 
      d:DesignHeight="300"
      GridViewColumnHeader.Click="GridViewColumnHeaderClickedHandler">

<ListView.Resources>
    <Style TargetType="{x:Type GridViewColumnHeader}">
        <Setter Property="HorizontalContentAlignment"
                Value="Left" />
    </Style>
</ListView.Resources>

</ListView>

MainWindow.xaml:

XML
<Window x:Class="ListViewSorter.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:controls="clr-namespace:Controls"
    Title="MainWindow"
    Height="212"
    Width="447"
    WindowStartupLocation="CenterScreen">
    
    <Grid>
        <controls:ListViewSorter ItemsSource="{Binding Rows}" FontSize="14">
            <ListView.View>
                <GridView>
                    <GridViewColumn Width="100" Header="Name" DisplayMemberBinding="{Binding Name}" />
                    <GridViewColumn Width="100" Header="Account ID" 
            DisplayMemberBinding="{Binding AccountId}"  />
                    <GridViewColumn Width="100" Header="Weight" 
            DisplayMemberBinding="{Binding Weight, StringFormat=F2}"  />
                    <GridViewColumn Width="100" Header="DOB" 
            DisplayMemberBinding="{Binding DOB, StringFormat={}{0:MM/dd/yyyy}}" />
                </GridView>
            </ListView.View>
        </controls:ListViewSorter>
    </Grid>
</Window>

Points of Interest

Please let me know if you find any bugs or add any features, happy coding!

History

  • 02/14/2015 - Initial version

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Questionhow to use this in xamarin.forms Pin
Member 1134575216-Jun-15 4:46
Member 1134575216-Jun-15 4:46 

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

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