Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Adding Multicolumn-sorting to ListViewSortManager

0.00/5 (No votes)
5 Dec 2007 1  
Easily add multicolumn sorting to your ListView controls

Introduction

A long time ago (back in 2002), Eddie Velazquez published a little class to easily add column sorting to ListView controls. The original article is here.

Several people (myself among them) requested some other features, especially the ability to sort by multiple columns at the same time. Although he fixed some bugs, Eddie did not have the time to add this bigger feature. One day I really needed it for a project and instead of waiting, I decided to roll up my sleeves and do it.

I had wanted to post it here for everyone else to benefit, but I had not given myself the time to write the article. But finally, here it is.

The most important feature I added to Eddie's version is multicolumn sorting. But I also added:

  • Currency and IP address sorting. The IP sorter was taken from a comment by Eddie in the original article.
  • Fixed a bug with the SortEnabled property
  • ListViewBottomItem class for items that should always stay at the bottom (e.g. a total row).

To sort on several columns at once, click the first column, then hold down the Ctrl key and click on additional columns.

The demo project included with this article is for Visual Studio .NET 2003 and .NET 1.1, so it can reach a broader audience. I have used this class also with Visual Studio 2005 and .NET 2.0 and everything works fine as is.

Using the Code

I have tried to keep my version backward compatible with Eddie's, and I have succeeded in 99% of the cases. Using ListViewSortManager is very easy. You just have to create an instance in your constructor, set the list view and column sorters, and that's it. In the example below, I have marked in bold the required changes:

using Intelectix.Windows.Forms;

class MyForm()
{
    ListView lstNames;
    ListViewSortManager sortManager;

    public MyForm()
    {
        InitializeComponent();

        sortManager = new ListViewSortManager(lstNames,  new Type[]
            {
                typeof(ListViewTextSort),
                typeof(ListViewTextSort),
                typeof(ListViewInt32Sort),
                typeof(ListViewDateSort)
            });
    }
}

Simple, isn't it? If you want to specify initial sorting to the list, specify the column index in the third parameter of the constructor, and the sort order in the fourth parameter:

sortManager = new ListViewSortManager(lstNames,  new Type[]
    {
        typeof(ListViewTextSort),
        typeof(ListViewTextSort),
        typeof(ListViewInt32Sort),
        typeof(ListViewDateSort)
    }, 1, SortOrder.Descending);  // Sort descending by second column

Of course, you will have to add the ListViewSortManager.cs file to your project.

Adding Items to the ListView

If you are adding a large quantity of items to the listview, either use ListView.Items.AddRange or disable ListViewSortManager. If you add one by one, the list view will be sorted after each item and you will see a performance hit.

lstNames.BeginUpdate();
lstNames.Items.Clear();
sortManager.SortEnabled = false;

for(int i = 0; i < 1000; i++)
{
    ListViewItem item = new ListViewItem();
        .
        .
        .
    lstNames.Items.Add(item);
}

sortManager.SortEnabled = true;
lstNames.EndUpdate();

Creating your Own Comparers

I have provided comparers for:

  • Text (ListViewTextSort)
  • Case insensitive text (ListViewTextCaseInsensitiveSort)
  • Date (ListViewDateSort)
  • Integers (ListViewInt32Sort, ListViewInt64Sort)
  • Double (ListViewDoubleSort)
  • Currency (ListViewCurrencySort)
  • Percentage (ListViewPercentSort)
  • IP addresses (ListViewIPSort)

If you want to sort on something different, implementing your own comparer is very easy (I've made it easier, but a little different than Eddie's version). You just need to implement the IListViewSorter interface, and compare two items. Below you can see the code for ListViewCurrencySort:

public class ListViewCurrencySort : IListViewSorter
{
    public int Compare(String lhs, String rhs)
    {
        decimal result =
           decimal.Parse(lhs, NumberStyles.Currency, CultureInfo.CurrentCulture) -
           decimal.Parse(rhs, NumberStyles.Currency, CultureInfo.CurrentCulture);

        if(result > 0)
            return 1;
        else if(result < 0)
            return -1;
        else
            return 0;
    }
}

Other Improvements

There is a ListViewBottomItem, that when sorted, will always go at the very bottom. This is useful for rows containing totals. It is created and used like a regular ListViewItem (some constructors are missing, though).

How Multicolumn Sorting Works

Basically, to support sorting by multiple columns, the ListViewSortManager keeps a list of the indexes of sorted columns.

When the user clicks a column:

  • If the Control key is not down, the listView_ColumnClick method checks to see if the same column is already the first in the sequence, and if so, just toggles the order. Otherwise, the whole sort list is cleared and then the column added.
  • If the Control key is down, the clicked column is added at the end of the sort sequence. If it was at the end before, its sort order is toggled. If it appeared somewhere in the middle of the sequence, it is removed from there and added at the end.

The sequence contains the indexes of the columns in the order they should be sorted. If the index is negative, it means the column should be sorted in a descending manner.

Coming Soon

One thing I have always wanted to add is designer support. I visualize it like this:

  • In the designer, add a component to your form.
  • Each listview gets SortEnabled, SortColumn, and SortOrder properties which you can set.
  • In the column designer, each column gets a Sorter property.

I just hope the "Soon" in this section's title is soon enough. :)

If you have any doubts, comments, or improvements, feel free to use the message board below so everyone can benefit.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here