Introduction
I have decided to kill two birds with one stone in getting to grips both with .Net and Pocket PC development, by writing a project in C# using the .NET Compact Framework.
For this project I need a sortable list view control; no problem I thought - I'll use the one in the .Net Framework. This is when you realise that a lot of the methods and properties in the documentation do not have the magic words "Supported by the .NET Compact Framework", including the ListView.Sort method. So, I decided to roll my own, called SortListView. There may be other similar classes out there, but I treated this as a learning exercise.
As it turned out, this was quite a good choice, as it also required some interop with unmanaged code, something else I am hoping to learn more about.
How to use the SortListView
class
I have tried to make using the SortListView
class as simple as possible.
- In the form designer, drag a
ListView
control onto the form, and position and size it as desired. This step is useful purely to set up the Size and Location properties of the ListView
control. If you prefer, you can enter these manually into the code.
- In the Form class constructor, create an
AdrianStanley.Windows.Forms.SortListView
control, and a AdrianStanley.Windows.Forms.SortColumnHeader
control for each column you want in the ListView
, and add them to the ListView object's Columns collection.
- Make sure the
ListView
control View
property is set to System.Windows.Forms.View.Details
.
- For each
SortColumnHeader
object, set its ColumnHeaderSorter
property. This determines how items in the column will sort. The property should be set to an instance of a class derived from AdrianStanley.Windows.Forms.SortComparer
. I have provided a number of such classes (see SortComparer.cs), for sorting strings, integers, doubles, and dates.
- Add items to the
ListView
control as normal ListViewItem
objects.
- Build the unmanaged dll, ControlEx.dll, for your target platform, and add the dll to the main .Net project. The download includes a build of this dll for Pocket PC 2003.
That should be it. Initially items in the ListView
are unsorted. When you first click on a column header, the items in that column will be sorted in ascending order, and a little upwards pointing arrow will be displayed next to the text in the column header. When you click on the column header again, the arrow changes to point downwards, and the column is sorted in descending order.
Example Project
To demonstrate how simple this is, here is a complete C# Windows project that creates a sorting ListView with three columns.
using System;
using System.Globalization;
using System.Windows.Forms;
using AdrianStanley.Windows.Forms;
namespace SortListViewDemo
{
public class SortListViewForm : System.Windows.Forms.Form
{
private AdrianStanley.Windows.Forms.SortListView m_sortListView;
private AdrianStanley.Windows.Forms.SortColumnHeader m_colhdrName;
private AdrianStanley.Windows.Forms.SortColumnHeader m_colhdrAmount;
private AdrianStanley.Windows.Forms.SortColumnHeader m_colhdrDate;
public SortListViewForm()
{
m_sortListView = new AdrianStanley.Windows.Forms.SortListView();
m_colhdrName = new AdrianStanley.Windows.Forms.SortColumnHeader();
m_colhdrDate = new AdrianStanley.Windows.Forms.SortColumnHeader();
m_colhdrAmount=new AdrianStanley.Windows.Forms.SortColumnHeader();
m_sortListView.Columns.Add(m_colhdrName);
m_sortListView.Columns.Add(m_colhdrDate);
m_sortListView.Columns.Add(m_colhdrAmount);
m_sortListView.Size = new System.Drawing.Size(248, 272);
m_sortListView.View = System.Windows.Forms.View.Details;
m_colhdrName.Text = "Name";
m_colhdrName.Width = 100;
m_colhdrDate.Text = "Date";
m_colhdrDate.Width = 80;
m_colhdrAmount.Text = "Amount";
m_colhdrAmount.Width = 60;
m_colhdrAmount.TextAlign = HorizontalAlignment.Right;
Controls.Add(m_sortListView);
Text = "SortListView Demo";
m_colhdrName.ColumnHeaderSorter = new ComparerString();
m_colhdrDate.ColumnHeaderSorter = new ComparerStringAsDateTime();
m_colhdrAmount.ColumnHeaderSorter = new ComparerStringAsDouble();
m_sortListView.BeginUpdate();
AddItem("Green, David", new DateTime(2001, 12, 31), 100.23);
AddItem("Brown, Tom", new DateTime(2000, 4, 3), 56.98);
AddItem("Pink, Jane", new DateTime(1996, 6, 15), 653.18);
AddItem("White, Sarah", new DateTime(2006, 8, 23), 345.22);
AddItem("Grey, Richard", new DateTime(2003, 9, 18), 27.74);
m_sortListView.EndUpdate();
}
private void AddItem(string name, DateTime date, double amount)
{
string[] displayInfo = new string[3];
displayInfo[0] = name;
displayInfo[1] = date.ToString("d");
NumberFormatInfo nfi = new NumberFormatInfo();
nfi.NumberDecimalDigits = 2;
displayInfo[2] = amount.ToString("N", nfi);
m_sortListView.Items.Add(new ListViewItem(displayInfo));
}
protected override void Dispose( bool disposing )
{
m_sortListView.Dispose();
base.Dispose( disposing );
}
static void Main()
{
Application.Run(new SortListViewForm());
}
}
}
Persisting State
The SortListView
class can persist state if desired. The persisted state consists of the index of the current sort column, and the sort order for each column. The persistance mechanism uses the OpenNETCF.Win32
namespace, available from http://www.opennetcf.org/. The persistance mechanism is disabled by default, as not everyone will have the OpenNETCF.Win32
namespace. To enable the persistance mechanism, define the preprocessor constant PERSIST_SORTLISTVIEW
in the project settings, set the SortListView.PersistKeyName
property to the key to be used in the registry for persisting values, and then call the LoadState
and SaveState
methods to respectively load the state from the registry, and to save the state to the registry.
I recommend that a value of the form "SOFTWARE\CompanyName\ProductName\ListViewName" is used for the SortListView.PersistKeyName
property.
The Main Classes
There are three main classes involved in this project. I have used the XML comments feature of the .Net development environment to document the classes, so for detailed information I suggest you read that - preferably after generating the HTML files (Tools, Build Comment Web Pages...).
SortListView
The SortListView
class represents a Windows list view control, which displays a collection of sorted items in a details view. The SortListView
class extends the existing .Net Compact Framework ListView class, when it is used in a details view, with ListView.View
set to View.Details
. In details view, the ListView
control has a column header. The SortListView
class will automatically sort the items in the ListView control when a column header is clicked.
When a column header is clicked for the first time, the items in the column are sorted in ascending order. The column is now the sorted column; the ordering of items in the other columns follow the ordering of the items in the clicked column. Clicking the column header again will sort the items in the column in descending order. A sort arrow is shown in the column header to show the direction of the sort - an upwards pointing arrow for ascending order, and a downwards pointing arrow for descending order. By default the sort arrow is shown to the right of any text in the column header. If the column header text is aligned to the right then the sort arrow will be shown to the left of the text.
Each column has its own sort order, and its own comparer for determining how items in the column should be sorted. The sort order is defined as one of the values in the enum AdrianStanley.Windows.Forms.SortOrder
, and the comparer is defined as an instance of a class derived from AdrianStanley.Windows.Forms.SortComparer
. The AdrianStanley.Windows.Forms
namespace defines some existing comparers for sorting string
, int
, short
, long
, double
and DateTime
objects.
The SortListView
class requires unmanaged code (contained in ControlEx.dll) to show the sort arrow within the column header.
SortColumnHeader
The SortColumnHeader
class sisplays a single column in a SortListView
control, and represents a column header in a AdrianStanley.Windows.Forms.SortListView
control.
A column header is an item in a SortListView
control that contains heading text, and a sort order. SortColumnHeader
objects can be added to a SortListView
using the Add
method of the base ListView.ColumnHeaderCollection
class.
SortColumnHeader
provides the Sorting
property to set the sort order for the column, and the SortComparer
property to set the comparer to be used to sort items in the column.
SortComparer
The SortComparer
class is used in conjunction with theSortListView.Sort
method, and provides a way to customize the sort order of items in a SortListView
.
The class is marked as abstract, as it acts as a base class for derived classes which implement the Compare
method.
Several classes derived from SortComparer
are provided:
ComparerString
. Compares alphabetic strings.
CompareStringAsShort
. Compares shorts represented as strings.
CompareStringAsInt
. Compares integers represented as strings.
CompareStringAsLong
. Compares longs represented as strings.
CompareStringAsDouble
. Compares doubles represented as strings.
CompareStringAsDateTime
. Compares dates represented as strings.