How to sort a ListView control by a column in Visual C#

Posted by bconlon on Geeks with Blogs See other posts from Geeks with Blogs or by bconlon
Published on Mon, 28 Feb 2011 17:55:45 GMT Indexed on 2011/02/28 23:26 UTC
Read the original article Hit count: 357

Filed under:

Microsoft provide an article of the same name (previously published as Q319401) and it shows a nice class 'ListViewColumnSorter ' for sorting a standard ListView when the user clicks the column header.

This is very useful for String values, however for Numeric or DateTime data it gives odd results. E.g. 100 would come before 99 in an ascending sort as the string compare sees 1 < 9. So my challenge was to allow other types to be sorted. This turned out to be fairly simple as I just needed to create an inner class in ListViewColumnSorter which extends the .Net CaseInsensitiveComparer class, and then use this as the ObjectCompare member's type.

Note: Ideally we would be able to use IComparer as the member's type, but the Compare method is not virtual in CaseInsensitiveComparer , so we have to create an exact type:

public class ListViewColumnSorter : IComparer
{
    private CaseInsensitiveComparer ObjectCompare;
    private MyComparer ObjectCompare;

    ... rest of Microsofts class implementation...
}

Here is my private inner comparer class, note the 'new int Compare' as Compare is not virtual, and also note we pass the values to the base compare as the correct type (e.g. Decimal, DateTime) so they compare correctly:

private class MyComparer : CaseInsensitiveComparer
{
    public new int Compare(object x, object y)
    {
        try
        {
            string s1 = x.ToString();
            string s2 = y.ToString();
 
            // check for a numeric column
            decimal n1, n2 = 0;
            if (Decimal.TryParse(s1, out n1) && Decimal.TryParse(s2, out n2))
                return base.Compare(n1, n2);
            else
            {
                // check for a date column
                DateTime d1, d2;
                if (DateTime.TryParse(s1, out d1) && DateTime.TryParse(s2, out d2))
                    return base.Compare(d1, d2);
            }
        }
        catch (ArgumentException) { }
 
        // just use base string compare
        return base.Compare(x, y);
    }
}

You could extend this for other types, even custom classes as long as they support ICompare.

Microsoft also have another article How to: Sort a GridView Column When a Header Is Clicked that shows this for WPF, which looks conceptually very similar. I need to test it out to see if it handles non-string types.

#

© Geeks with Blogs or respective owner