Make a ListView sort using the column you click in C#

You can make the ListView sort by setting its 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. (For instructions on implementing an interface, see Implement an interface in C#.) The key to this class is the Compare function. This function 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 ListViewComparer class shown in the following code compares two ListView items.

// Compares two ListView items based on a selected column.
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 function compares the appropriate sub-items to determine how the 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.

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.

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 then calls the ListView control's Sort method to make it resort its data.

   

 

What did you think of this article?




Trackbacks
  • No trackbacks exist for this post.
Comments

  • 8/23/2010 8:15 AM Billy wrote:
    This code, unlike most examples, actually works like a charm. Thanks much Rod. Microsoft should add this basic functinality into the ListView.
    Reply to this
  • 8/23/2010 8:34 AM Rod Stephens wrote:
    By "unlike most examples," I hope you mean other peoples' examples ;-)

    I agree that this would be at least a nice default behavior. I think that Microsoft decided that they couldn't always figure out how to sort a column because it's not always easy to tell if it contains a number, string, date, etc. So they let you do it very flexibly (but not too easily).
    Reply to this
  • 12/28/2012 12:07 PM Scott wrote:
    I tried several other "solutions" trying to implement this functionality, including several from Microsoft. This is the only one that worked. And because it worked, I could actually learn from it. Thanks!
    Reply to this
Leave a comment

Submitted comments are subject to moderation before being displayed.

 Name

 Email (will not be published)

 Website

Your comment is 0 characters limited to 3000 characters.