I'm using a BindingList<T>
in my Windows Forms that contains a list of "IComparable<Contact>
" Contact-objects. Now I'd like the user to be able to sort by any column displayed in the grid.
There is a way described on MSDN online which shows how to implement a custom collection based on BindingList<T>
which allows sorting. But isn't there a Sort-event or something that could be caught in the DataGridView (or, even nicer, on the BindingSource) to sort the underlying collection using custom code?
I don't really like the way described by MSDN. The other way I could easily apply a LINQ query to the collection.
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.