Sort a ListView control using all of its columns in C#

Sort a ListView control

You can make the ListView sort by setting its Sorting property to Ascending or Descending, but it takes some extra work to sort a ListView control using all of its columns. Normally the control only sorts on its items, not their sub-items. It won’t even use the sub-items to break ties when the items have the same text. This example makes it use the both the items and sub-items.

To modify the control’s sorting behavior, you must create a class that implements the IComparer interface. (For instructions on implementing an interface, see the post Implement an interface in C#.)

The key to this class is the Compare function, which takes as parameters two ListViewItem objects to compare and returns -1, 0, or 1 to indicate whether the first should be considered less than, equal to, or greater than the second. The ListViewAllColumnComparer class shown in the following code implements IComparer.

// Compares two ListView items using all of their columns.
public class ListViewAllColumnComparer : IComparer
{
    private SortOrder SortOrder;

    public ListViewAllColumnComparer(SortOrder sort_order)
    {
        SortOrder = sort_order;
    }

    // Compare two ListViewItems.
    public int Compare(object object_x, object object_y)
    {
        // Get the objects as ListViewItems.
        ListViewItem item_x = object_x as ListViewItem;
        ListViewItem item_y = object_y as ListViewItem;

        // Loop through the sub-items.
        for (int i = 0; i < item_x.SubItems.Count; i++)
        {
            // If item_y is out of sub-items,
            // then it comes first.
            if (item_y.SubItems.Count <= i) return 1;

            // Get the sub-items.
            string string_x = item_x.SubItems[i].Text;
            string string_y = item_y.SubItems[i].Text;

            // Compare them.
            int result = CompareValues(string_x, string_y);

            if (result != 0) return result;
        }

        // If we get here, we have an exact match.
        return 0;
    }

    // Compare two items. If they look like
    // numbers or dates, compare them as such.
    private int CompareValues(string string_x, string string_y)
    {
        // Compare them.
        int result;
        double double_x, double_y;
        if (double.TryParse(string_x, out double_x) &&
            double.TryParse(string_y, out double_y))
        {
            // Treat as a number.
            result = double_x.CompareTo(double_y);
        }
        else
        {
            DateTime date_x, date_y;
            if (DateTime.TryParse(string_x, out date_x) &&
                DateTime.TryParse(string_y, out date_y))
            {
                // Treat as a date.
                result = date_x.CompareTo(date_y);
            }
            else
            {
                // Treat as a string.
                result = string_x.CompareTo(string_y);
            }
        }

        // Return the correct result depending on whether
        // we're sorting ascending or descending.
        if (SortOrder == SortOrder.Ascending)
        {
            return result;
        }
        else
        {
            return -result;
        }
    }
}

The class uses private variable SortOrder to keep track of its sort order. This value is set by the class’s constructor.

The Compare method loops through the sub-items held by the two ListView items and uses the CompareValues method to compare them. When it finds two values that are different, CompareValues returns -1 or 1 and the Compare method returns that value. If CompareValues returns 0 to indicate that the values are equal, the Compare method keeps examining sub-items.

The CompareValues method first tries to compare the values as doubles and dates. For example, as strings “9” comes after “100” but as numbers 9 comes before 100. Similarly as strings “10/10/2010” comes before “1/1/2009” but as dates 10/10/2010 comes after 1/1/2009. Comparing the values as doubles and dates lets the method sort values more meaningfully so they make sense to the user. If the values aren’t both numbers or dates, the code treats them as strings.

The following code shows how the main program sorts ascending or descending.

// Sort ascending.
private void radAscending_Click(object sender, EventArgs e)
{
    // Create a comparer.
    lvwBooks.ListViewItemSorter =
        new ListViewAllColumnComparer(SortOrder.Ascending);

    // Sort.
    lvwBooks.Sort();
}

// Sort descending.
private void radDescending_Click(object sender, EventArgs e)
{
    // Create a comparer.
    lvwBooks.ListViewItemSorter =
        new ListViewAllColumnComparer(SortOrder.Descending);

    // Sort.
    lvwBooks.Sort();
}

When you click the Ascending radio button, the program creates a new ListViewAllColumnComparer object, passing its constructor the Ascending parameter to make the comparer sort items in ascending order. It then sets the ListView control’s ListViewItemSorter property to the comparer. The control automatically rearranges its items appropriately.

When you click the Descending radio button, the program performs the same steps but it uses a descending comparer.


Download Example   Follow me on Twitter   RSS feed


This entry was posted in controls, user interface and tagged , , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *