Sorting MultiColumn ListView Windows Forms Control


Figure 1 - ListView Sorted on Column 2  



If you want to sort on the first column of a ListView, simply click the Sorting in the properties Window and choose Ascending or Descending as a value.  If you want to do sorting in all the columns of a ListView, you need to write custom sorting using the ListViewItemSorter property. This property allows you to assign the property to an instance of a custom class that implements the IComparer interface.  The design of the MultiColumnSort ListView is shown below:

 

Figure 2 - UML Design of Implementing the MulitColumnSortListView reverse engineered using the WithClass 2000 UML Tool

In our example, the ListView utilizies a class called ColumnSorter which tells the ListView how to sort a particular column when a column is clicked.  The sequence of events that implements sorting is shown below:

Figure 3 - UML Sequence diagram (created using the WithClass 2000)

As shown in figure 3,  The user clicks on the Column and this triggers the column click event handler in the form.  The Event handler calls Sort on the ListView which internally calls TheColumnSorter to determine how each ListViewItem (Row)  is compared for sorting.

Below is the event handler in the form for clicking on a column in the ListView:

private void listView1_ColumnClick(object sender, System.Windows.Forms.ColumnClickEventArgs e)
{
// Tell the Column Sorter which column you want to sort on based on the column that was clicked by the user
TheColumnSorter.CurrentColumn = e.Column;
// Call Sort on the ListView
listView1.Sort();
}

To utilize TheColumnSorter in the ListView, the ListView must assign the TheColumnSorter to its ListViewItemSorter some where in the initialization of the form (such as the constructor) as shown in the line of code below:

listView1.ListViewItemSorter = TheColumnSorter;

The instance of the ColumnSorter class, TheColumnSorter, implements the IComparer interface to compare two ListViewItems.  In order to implement this interface, you need to create a Compare method that takes two objects and returns an integer that indicates whether the two compared items are either greater than, less than, or equal to each other.  Below is a table of the integers returned based on these comparisons:

For the Compare method shown below, the comparisons in Table 1 determine what value should be returned in the Compare method.

 int Compare(object a, object b)

Comparison Integer returned
object a > object b positive number,  e.g.  1
object a < object b negative number, e.g. -1
object a == object b 0

Table 1 - Compare Method return values based on comparisons

Below is the ColumnSorter class used to sort two ListViewItems.  The Sort method of the ListView will use this method for all of its sorting comparisons:
public class
ColumnSorter : IComparer
{
public int
CurrentColumn = 0;
public int Compare(object x, object
y)
{
ListViewItem rowA = (ListViewItem)x;
ListViewItem rowB = (ListViewItem)y;
return
String.Compare(rowA.SubItems[CurrentColumn].Text,
rowB.SubItems[CurrentColumn].Text);
}
public
ColumnSorter()
{
}
}
}

Note: that the comparison is performed using the String Compare Method.  This method will return 1 for string1 > string2,  -1 for string 1 < string2, and 0 for string1 = string2.  This string compare functionality jives with what we need to do with the Compare method in ColumnSorter, so we can just return the string compare method's results.  The CurrentColumn property determines which column it is we want to make the comparison on by indexing into the appropriate SubItem.