Title: Sort a ListView using the column you click in C#
You can sort a ListView using the column you click, but it's a lot more work than simply sorting the ListView's items. To sort using the items, you can simply set the control's Sorting property to Ascending or Descending. Unfortunately, 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.)
To modify the control's sorting behavior, you must create a class that implements the IComparer interface. The key to this class is the Compare method. This method takes as parameters two ListViewItems to compare and should return -1 if the first should come before the second in the sort order, 0 if the items are equal, and 1 if the first item should come after the second.
The following code shows the ListViewComparer class used in this example.
// Compares two ListView items based on a selected column.
public class ListViewComparer : System.Collections.IComparer
{
private int ColumnNumber;
private SortOrder SortOrder;
public ListViewComparer(int column_number,
SortOrder sort_order)
{
ColumnNumber = column_number;
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;
// Get the corresponding sub-item values.
string string_x;
if (item_x.SubItems.Count <= ColumnNumber)
{
string_x = "";
}
else
{
string_x = item_x.SubItems[ColumnNumber].Text;
}
string string_y;
if (item_y.SubItems.Count <= ColumnNumber)
{
string_y = "";
}
else
{
string_y = item_y.SubItems[ColumnNumber].Text;
}
// 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 variables ColumnNumber and SortOrder to keep track of the column on which it should sort and whether it should sort ascending or descending. These values are set by the class's constructor.
The Compare method compares the appropriate sub-items to determine how two items should be ordered. It needs to do a little checking to guard against the case when either of the items doesn't have the sub-item that the object is trying to compare. (For example, if you're comparing the fourth sub-item but one of the items only has two sub-items.)
The code also tries to convert the values into doubles and dates to see if it should sort the values using those data types. 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 values as numbers or dates makes them more sensible for users.
The example program uses the following code to sort when you click on a ListView column.
// The column we are currently using for sorting.
private ColumnHeader SortingColumn = null;
// Sort on this column.
private void lvwBooks_ColumnClick(object sender,
ColumnClickEventArgs e)
{
// Get the new sorting column.
ColumnHeader new_sorting_column = lvwBooks.Columns[e.Column];
// Figure out the new sorting order.
System.Windows.Forms.SortOrder sort_order;
if (SortingColumn == null)
{
// New column. Sort ascending.
sort_order = SortOrder.Ascending;
}
else
{
// See if this is the same column.
if (new_sorting_column == SortingColumn)
{
// Same column. Switch the sort order.
if (SortingColumn.Text.StartsWith("> "))
{
sort_order = SortOrder.Descending;
}
else
{
sort_order = SortOrder.Ascending;
}
}
else
{
// New column. Sort ascending.
sort_order = SortOrder.Ascending;
}
// Remove the old sort indicator.
SortingColumn.Text = SortingColumn.Text.Substring(2);
}
// Display the new sort order.
SortingColumn = new_sorting_column;
if (sort_order == SortOrder.Ascending)
{
SortingColumn.Text = "> " + SortingColumn.Text;
}
else
{
SortingColumn.Text = "< " + SortingColumn.Text;
}
// Create a comparer.
lvwBooks.ListViewItemSorter =
new ListViewComparer(e.Column, sort_order);
// Sort.
lvwBooks.Sort();
}
The SortingColumn variable keeps track of the column that the program is currently sorting.
When the user clicks a column header, the program figures out which column was clicked. It then determines the new sorting order it should use. If the new column is the same as the old one, the program switches the sort order. Otherwise it sorts ascending.
The program adds "> " to the column header when it sorts ascending and "< " when it sorts descending. It uses these characters to determine the current sort order.
After it decides on the new sort order, the program removes the sorting indicator ("> " or "< ") from the current sort column and adds it to the new sort column.
Finally, the program creates a new ListViewItemSorter object, passing its constructor the appropriate column number and sort order. It finishes by calling the ListView control's Sort method to make it resort its data.
Download the example to experiment with it and to see additional details.
|