I higly appreciate Matthias' solution for its simplicity and beauty.
However, while this gives excellent results for low data volumes, when working with large data volumes the performance is not so good, due to reflection.
I ran a test with a collection of simple data objects, counting 100000 elements. Sorting by an integer type property took around 1 min. The implementation I'm going to further detail changed this to ~200ms.
The basic idea is to benefit strongly typed comparison, while keeping the ApplySortCore method generic. The following replaces the generic comparison delegate with a call to a specific comparer, implemented in a derived class:
New in SortableBindingList<T>:
protected abstract Comparison<T> GetComparer(PropertyDescriptor prop);
ApplySortCore changes to:
protected override void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction)
{
List<T> itemsList = (List<T>)this.Items;
if (prop.PropertyType.GetInterface("IComparable") != null)
{
Comparison<T> comparer = GetComparer(prop);
itemsList.Sort(comparer);
if (direction == ListSortDirection.Descending)
{
itemsList.Reverse();
}
}
isSortedValue = true;
sortPropertyValue = prop;
sortDirectionValue = direction;
}
Now, in the derived class one have to implement comparers for each sortable property:
class MyBindingList:SortableBindingList<DataObject>
{
protected override Comparison<DataObject> GetComparer(PropertyDescriptor prop)
{
Comparison<DataObject> comparer;
switch (prop.Name)
{
case "MyIntProperty":
comparer = new Comparison<DataObject>(delegate(DataObject x, DataObject y)
{
if (x != null)
if (y != null)
return (x.MyIntProperty.CompareTo(y.MyIntProperty));
else
return 1;
else if (y != null)
return -1;
else
return 0;
});
break;
// Implement comparers for other sortable properties here.
}
return comparer;
}
}
}
This variant requires a little bit more code but, if performance is an issue, I think it worths the effort.