A while back I needed to solve the problem of making all of the DataGridView controls in our application sortable, but without needing to implement a custom sort in every single form. The problem is that the DataGridView, for reasons unknown to me, does not include default sorting. The only way I could find to get automatic sorting was if the data source was a DataSet or if the columns and rows are added manually rather than using a data source. I toyed with the idea of manually adding columns and rows, but that did not last long as I realized that would be entirely too tedious and prone to break. I was also not able to use a DataSet as my data source.

Through some research, I did find that when bound to a BindingList, the DataGridView checks the BindingList.SupportsSortingCore property. This is normally false, but if you create your own BindingList (using inheritance) and override the sort-specific properties, you can implement sorting and the DataGridView will take care of the rest.

Let me briefly explain how the DataGridView sorting works in relation to the BindingList. The DataGridView calls the BindingList.ApplySortCore method, passing a PropertyDescriptor for the sort column and the sort direction. Internally, you’ll use the List<T>.Sort() method to handle the sort algorithm, but it is up to you for the item comparison logic. The DataGridView will then display the sorted data along with a sorting glyph indicating the sort direction. The sorting glyph has no relation with the data; it is simply tracking internally by the DataGridView.

This leaves you with two options regarding the implementation of the sorting. You can accomplish sorting using some generic code that tries to compare the properties using the IComparable interface for the property value. Alternatively, you can create a custom sorter for every type of object that you might have sorted in a grid and provide that custom sorter to the BindingList. Let me explain the benefits and drawbacks of each method.

Generic Sort Comparer
  • Benefits
  • Reusable
  • Automatically covers any new data types
  • Drawbacks
  • Does not compare types that do not implement IComparable
  • Not as flexible for complex sorting algorithm
Type-Specific Sort Comparer
  • Benefits
  • Supports advanced sorting and comparison of complex data types
  • Supports sorting of types which do not implement IComparable
  • Drawbacks
  • Must implement a new sort comparer for each data type to be sorted
  • Must maintain the sort comparer as the class changes

I ultimately decided to use a combination of the two methods. To start with, small data sets that don’t need any complex sorting just use the generic sort comparer, which is the default sort comparer in my custom BindingList. However, I do provide the ability for the BindingList to be provided an ISortComparer<T> to be used in place of the generic sort comparer. For large data sets or complex comparisons, I provide the BindingList with a custom sort comparer and performance improves tremendously.

I’ve uploaded the source files for the concepts mentioned above to gist.github.com.