Sorting Listview on Multiple Columns

You can download the demo application with detailed comments from here ListViewSortDemo.zip (47.56 kb)

A little while ago I was working on a project where I needed to display the contents of a given folder. When sorting the columns it was requested that the application behaved similar to windows explorer. Sorting on the name column in ascending order would group the folders followed by the files, and sorting in descending order would list the files first then folders.

Ascending Sorting in Windows Explorer
Ascending Sorting in Windows Explorer

 Descending Sorting in Windows Explorer
Descending Sorting in Windows Explorer

I realised it was worth investing some time in creating a reusable lisvtiew sorting class which can sort multiple columns. 

Sorting a listview can be achieved by creating a class that inherits from the ICompare interface.  I'm not going to go into the technicalities of ICompare here, rather I am going to present the features of the class I have written.  It is sufficient to say though that the comparison of string values is not case sensitive, so 'hello world' will be considered the same as 'HeLlO WorLd'. 

While looking through the code below bear in mind that this is only one example of how it can be done, the code is open to scrutiny and opinion of all.

Listiview Sorter Features

Multiple column sort

A sort can be performed containing upto three columns (personally I have not yet found the need to sort on more than a couple of columns as yet).  This is achieved by three properties;   SortAppendColumn, SortColumn and SortPrependColumn. 

  • SortColumn.  This is column clicked on the listview by the user.
  • SortAppendColumn.  When a column index is specified for the SortAppendColumn the listview is sorted by this column before the actual clicked column.  An index of -1 indicates no column
  • SortPrependColumn.  When a column index is specified for the SortPrependColumn the listview is sorted by this column after the actual clicked column.  An index of -1 indicates no column

So the idea is, if the user clicks on the header of column 1 you can specify a sortprependcolumn, for example column 3.  What the class will do while sorting is simply append the contents of the relevant row in column 3 to column 1 before a compare.  This is assuming a textual comparison is being made.  This is all made clearer in the accompanying demo application.

For those of you more interested in the technical here is the code from the Compare method that analyses the properties and sets up the text to be compared;

   1:          ListViewItem listviewX, listviewY;
   2:   
   3:          // Cast the  objects to be compared to ListViewItem objects
   4:          listviewX =  (ListViewItem)x;
   5:          listviewY = (ListViewItem)y;
   6:   
   7:          StringBuilder XCompare = new StringBuilder();
   8:          StringBuilder  YCompare = new StringBuilder(); 
   9:   
  10:          // Compare the two  items
  11:          if (this.SortPrependColumn> -1 )
  12:          {
  13:               XCompare.Append(listviewX.SubItems[this.SortPrependColumn].Text);
  14:               YCompare.Append(listviewY.SubItems[this.SortPrependColumn].Text); 
  15:           }
  16:   
  17:          XCompare.Append  (listviewX.SubItems[MainSortColumn].Text);
  18:           YCompare.Append(listviewY.SubItems[MainSortColumn].Text);
  19:   
  20:           if (this.SortAppendColumn > -1)
  21:          {
  22:               XCompare.Append(listviewX.SubItems[this.SortAppendColumn].Text);
  23:               YCompare.Append(listviewY.SubItems[this.SortAppendColumn].Text); 
  24:           }

Textual or Numeric Comparison

When numbers are sorted in text format the result looks something like this;

1
14
2
26
3
37
4
...

Some developers tend to work around this feature by padding the number to a specified length with zeros like so;

0001
0002
0010
0014
...

and putting the padded numbers into an extra hidden column.  When the column containing the original numbers is clicked, the sorting is actually performed on the hidden column.

One of the properties you can set on the sorter class is whether a textual or numeric sort should be performed.  A numeric sort will avoid the above issues and sort number values in the expected logical way.  This was actually a new feature I implemented while writing this article, you never stop learning. 

Using the Class

At the top of your form declare an instance of the ListviewColumnSorter class.

   1:  ListViewColumnSorter lvwColumnSorter = new ListViewColumnSorter();

Inside your form constructor assign the class to the listview.

   1:      // Assign the column sorter
   2:      listView1.ListViewItemSorter = lvwColumnSorter;

In the ColumnClick event set the class properties and call the listview sort method.
First we check if we are changing the sort order on the previously selected column or on a different column.
Next, if the clicked column is index 2 we tell it to sort on column index 3 then column index 2. This mimics the windows explorer functionality mentioned earlier of grouping the folders and files when sorting.
Lastly, if column index 4 was clicked then we are sorting a number column so the compare type is set to numeric.

   1:  private void listView1_ColumnClick(object sender, ColumnClickEventArgs e)
   2:   
   3:   
   4:  {
   5:      // Determine if clicked column is already the column that is being sorted.
   6:      if (e.Column == lvwColumnSorter.SortColumn)
   7:      {
   8:          // Reverse the current sort direction for this column.
   9:          if (lvwColumnSorter.Order == SortOrder.Ascending)
  10:          {
  11:              lvwColumnSorter.Order = SortOrder.Descending;
  12:          }
  13:          else
  14:          {
  15:              lvwColumnSorter.Order = SortOrder.Ascending;
  16:          }
  17:      }
  18:      else
  19:      {
  20:          // Set the column number that is to be sorted; default to ascending.
  21:          lvwColumnSorter.SortColumn = e.Column;
  22:          lvwColumnSorter.Order = SortOrder.Ascending;
  23:      }
  24:      
  25:      if (lvwColumnSorter.SortColumn == 2)
  26:          lvwColumnSorter.SortPrependColumn = 3;
  27:             
  28:      if (lvwColumnSorter.SortColumn == 4)
  29:          lvwColumnSorter.CompareAs = SorterCompareType.Numeric;
  30:      else
  31:          lvwColumnSorter.CompareAs = SorterCompareType.Textual ;
  32:   
  33:      // Perform the sort with these new sort options.
  34:      listView1.Sort();
  35:  }

You can download the demo application with detailed comments from here ListViewSortDemo.zip (47.56 kb)


Silverlight - This weeks 7 day wonder

I love learning new stuff, which causes many hours of internet surfing when I should really be doing something else.  My mum used to call them 7 day wonders.

This weeks big thing for me is Silverlight 2.  I have been looking for tutorials on writing games using Silverlight and found some very interesting sites.  Here are a few that I found did focus on games.

The first one is Bluerosegames.com.  If you search for 'Silverlight game tutorial' on google you will currently find it at the no. 1 position.  There is a series of tutorials as listed below

I have read through the bluerosegames tutorials and highly recommend them.  They are written in C# and focus on object oriented methods.  During the tutorials the author takes you through the beginnings of creating an asteriod game.  Excellent for getting you started.

dieAjax have a mini series on creating a shooter game.

For more general Silverlight  tutorials I recommend visiting Microsoft's own Silverlight.net. or searching google for pages of lists of tutorials.