Wednesday, November 26, 2008

DataGrid custom sorting

As you know DataGrid can sort your items by default when column headers are clicked.
You can however implement your own custom sorting.

When you bind your DataGrid instance to a source object, beside other things it will also look to see if your object implements ICollectionView interface. If it does, it will use it when you click the column headers, if it doesn't it will create and use an internal ICollectionView implementation.

The definition for ICollectionView in MSDN is
"Enables collections to have the functionalities of current record management, custom sorting, filtering, and grouping."
From my investigation, the DataGrid control will only use the custom sorting from your ICollectionView implementation.

To implement custom sorting you need to implement:
1. CanSort property:
bool CanSort { get; }

2. SortDescriptions property:
SortDescriptionCollection SortDescriptions { get; }

The DataGrid control will first call the CanSort property of your ICollectionView implementation. If it returns true it will then call SortDescriptions to get the SortDescriptionCollection collection to use when sorting is triggered.

The
SortDescriptionCollection is a collection of SortDescription objects. When you click on a column header to sort the items by the column, what DataGrid does is to add a new SortDescription object to the SortDescriptionCollection. If you click again on another column the previous SortDescription object is removed from the SortDescriptionCollection and a new SortDescription object is added to the SortDescriptionCollection collection.
Practically, the
SortDescriptionCollection is the representation of the sorting glyph icons you see drawn on the column headers.

A SortDescription object is an object with 2 properties: a property name and sorting direction.
The property name is the name of the property to sort the list by.

That being said, let's write a simple implementation of a simple data source (a list of integers) for a DataGrid control which also implements ICollectionView.

public class MyDataSource : List, ICollectionView
{
SortDescriptionCollection sortDescColl = new MySortDescriptionCollection();
bool CanSort
{
get
{
return true; // this indicates the DataGrid control that we have a valid SortDescriptionCollection returned by SortDescriptions
}
}

SortDescriptionCollection SortDescriptions
{
get
{
return sortDescColl;
}
}
}

We create a new class MySortDescriptionCollection derived from SortDescriptionCollection just to override the InserItem to catch when new columns should get sorted.

public class MySortDescriptionCollection : SortDescriptionCollection
{
    protected override void InsertItem(int index, SortDescription item)
{
// new column was clicked to be sorted
// here we should operate the changes to the
MyDataSource collection
}
}

MySortDescriptionCollection will need to notify MyDataSource somehow about the sorting.
You can use do this with a public event for example. Have the MySortDescriptionCollection to expose a public event for example.