Presenting the SortableBindingList<T> (take two)
I’m in the progress of adding classes that i find interesting to the BeTimvwFramework project. The original implementation of my SortableBindingList relied on IComparable to implement ApplySortCore(PropertyDescriptor property, ListSortDirection direction). I received some good feedback and blogged about those improvements.
Because some of my classes only implement IComparable i needed support for this too. My first thought was to use Comparer
IComparer comparer = Comparer<t>.Default;
itemsList.Sort(delegate(T t1, T t2)
{
object property1 = prop.GetValue(t1);
object property2 = prop.GetValue(t2);
return reverse * comparer.Compare(property1, property2);
});
Obviously that didn’t work. The problem is that i received the default Comparer for T, instead of the Comparer for the type of the property. Anyway, with a bit of reflection i got access to that Comparer
Type comparablePropertyType = typeof(Comparer<>).MakeGenericType(property.PropertyType);
IComparer comparer = (IComparer)comparablePropertyType.InvokeMember("Default", BindingFlags.Static | BindingFlags.GetProperty | BindingFlags.Public, null, null, null);
After a bit of refactoring i ended up with a PropertyComparer which allowed me to implement the sorting as following
Type propertyType = property.PropertyType;
// this cache minimizes the cost of reflection in the PropertyComparer constructor
PropertyComparer<t> comparer;
if (!this.comparers.TryGetValue(propertyType, out comparer))
{
comparer = new PropertyComparer<t>(property, direction);
this.comparers.Add(propertyType, comparer);
}
else
{
comparer.SetListSortDirection(direction);
}
itemsList.Sort(comparer);
In order to write unittests, i needed a way to get instances of a PropertyDescriptor. This was achieved by using the TypeDescriptor as following
PropertyDescriptor GetPropertyDescriptor(object component, string propertyName)
{
PropertyDescriptorCollection propertyDescriptors = TypeDescriptor.GetProperties(component);
foreach (PropertyDescriptor propertyDescriptor in propertyDescriptors)
{
if (propertyDescriptor.Name == propertyName)
{
return propertyDescriptor;
}
}
throw new ArgumentException(string.Format("The property '{0}' was not found.", propertyName));
}
Feel free to download the updated demo application: sortablebindinglist.zip. There are even real applications that use this class, eg: VSTrac and MonoTorrent. If you’re interested in the unittests you’ll have to get the code at BeTimvwFramework.