Presenting the SortableBindingList<T>

If you are databinding your custom objects (in a Bindinglist of <T>) to a DataGridView you will notice that the users can’t sort the rows by clicking on the columnheaders… Unlike an unbound DataGridView, the SortCompare event is not raised. Here is a class that uses IComparer to implement a BindingList that supports Sorting:

Please read the follow up article to find the updated source code.

Using this SortableBindingList is as easy as:

public Form1()
{
 InitializeComponent();

 SortableBindingList<person> persons = new SortableBindingList<person>();
 persons.Add(new Person(1, "timvw", new DateTime(1980, 04, 30)));
 persons.Add(new Person(2, "John Doe", DateTime.Now));

 this.dataGridView1.AutoGenerateColumns = false;
 this.ColumnId.DataPropertyName = "Id";
 this.ColumnName.DataPropertyName = "Name";
 this.ColumnBirthday.DataPropertyName = "Birthday";
 this.dataGridView1.DataSource = persons;
}



the sortablebindinglist at work

Feel free to download the source and demoproject: SortableBindingList.zip.

Edit: You can find the latest implementation at BeTimvwFramework, a project where i will keep classes that i find interesting.

47 Comments.

  1. The code got mangled and doesn’t compile as is. Can you fix this (or post the .zip)? Looks like a very good start of what I need.

  2. Apparently there was a } too much in the delegate… Anyway, as promised, i’ve added a link to the source (and a demo project).

  3. Your example works, but since I already have my own collection, I added another constructor:

    public SortableBindingList(IList list)
        : base(list)
    {
    }

    This compiles but when I click on column header it blows up in the delegete on this line:

    List itemsList = this.Items as List;

    because it could not cast and itemsList was null.

    So I changed this line to:

    List itemsList = new List(Items);

    This compiles, I click on column, sort triangle appears, but it does not sort!

    I can’t figure out why…

  4. Well, i don’t know why, but these days my implementation does have a constructor that accepts an existing collection… I’ve uploaded an updated version of the source with an example of the added constructor:

    public SortableBindingList(IEnumerable<T> enumerable)
     : base(new List<T>(enumerable))
    {
    }
    
  5. That did it! I guess it was

    foreach (T t in enumerable)
    {
    this.Add(t);
    }
    which I was missing before in my constructor. One way or another it works

    Very handy class, thanks

  6. Thanks for the class – works great.

  7. I have used the above example in my own project with the effect that by DataGridView’s columns now sort, but the column order is no longer as specified in the form designer.

    For some reason, populating the SortableBindingList is reordering the DataGridView’s columns so they are in alphabetical order

  8. I solved my problem, I was binding the SortableBindingList directly to the DataGridView instead of setting up a BindingSource in-between.

  9. How would I implement a multi-column (sub) sort. It looks like ApplySortCore(…) only return the PropertyDescriptor of the column that was clicked.

    Thanks

  10. Hi Tim

    I have already tried your code, it works very good, but one isuue you might not probably noticed, if I have on column with all same data, when I click this column to sort, what I expect should change nothing, but each time datagridview change the order of records. any suggestion?

    THanks

  11. @Malcolm:

    This is because the implementation always raises a ListChanged event… You could probably add a little tweak that does not do that…

  12. Tim,

    Great class. It is a beautiful, elegant solution to allow sorting of objects in a DataGridView.

    Groetjes,
    Toño

  13. Hi Tim

    This is interesting, I look up msdn, here is some comment:”This method uses Array..::.Sort, which uses the QuickSort algorithm. This implementation performs an unstable sort; that is, if two elements are equal, their order might not be preserved. In contrast, a stable sort preserves the order of elements that are equal.”

    that means our Sort() has changed the order of binding list, even I don’t raise ListChanged event this time, but later on, when datagridview needs refresh, all records’ order will be changed. I have not found the solution so far. :-)

  14. Hi!

    Your base class already implements the required constructors, just add

    public SortableBindingList() : base() { }

    public SortableBindingList(List list) : base(list) { }

    to initialize with an existing List.

  15. greate class!

    It would be interesting to know why so simple functionality isn’t implemented in standart NET framework by default (e.g. in BindingSource class)?
    Is it in-depth intention?

  16. Thanks a lot!
    It’s very helpful.

  17. thanks man!

    I’m going to implement your excellent peace of code with this other one:
    http://www.codeproject.com/KB/cs/ObjectBindingSource.aspx

    if anyone is interested drop me a line on plurk (link on nick)

  18. Tahir Hussain

    Thanks Tim… your code helped me a lot.

  19. Hi,,

    Is there any we can make this as a stable sort rather than a unstable sort…??

  20. Thanks, it is simple and elegant.
    MS should take this one into the framework.

  21. You Rock man!
    We use a binding source on all our db objects and this code will make it easy to implement sorting in all our apps.

  22. Very nice, thank you.

  23. I would suggest also the following method, in order that newly added items are automatically sorted:
    protected override void InsertItem(int index, T item)
    {
    if (comparer != null && index == Count)
    {
    List itemsList = (List)this.Items;

    index = itemsList.BinarySearch(item, comparer);
    if (index < 0)
    {
    index = ~index;
    }
    }
    base.InsertItem(index, item);
    }

  24. Thanks Tim,

    Best of luck for all your future articles and all things. Thanks once again.

    regards
    Karthick

  25. Thank you Tim
    Been looking for this a lot until i stumbled onto your article. Great job
    Regards
    Hal

  26. good stuff man, works brilliantly. Even sorts time correctly

  27. Thanks so much, works like a charm!

  28. :grin: Works beautifully, THANKS! (After hours of battling with other attempts)

    Regards
    Cedric

  29. Big Thanks Tim!

    You saved me a lot of coding with this example.

  30. Super Thanks Tim!

    Works brilliantly.

  31. Amaging man!!!!!!!

  32. public static class SortableDataSourceExtension
    {
    public static IList ToSortable(this IList list)
    {
    return new SortableBindingList(list);
    }
    }

  33. But with less than T greater than T where needed to make it generic. your phorum ate my generics!

  34. How would you modify this to allow a comparer for images (ie. red/green/yellow circles that you would like grouped after a list of test in the gridview are run)?

  35. Awesome for the most part, although I’m running into an issue. When a user clicks a column in which the data is bound to a child table’s column I get a null reference exception. “Object Reference Not Set To An Instance Of An Object.” (Using Linq) And when I go back and try to save the employee it will throw the same error. It’s something to do with not being able to handle the child table.

  36. Awesome for the most part, although I’m running into an issue. When a user clicks a column in which the data is bound to a child table’s column I get a null reference exception. “Object Reference Not Set To An Instance Of An Object.” (Using Linq) It’s something to do with not being able to handle the child table.

  37. Thanks man, very useful!

  38. Hi Tim,
    Thank you so much. This works great for me. Do I need to add a copyright reference or give you credit beyond a comment in the header?

    Thanks again

  39. Hi, no need to add a reference as per: http://www.timvw.be/terms-of-use/

  40. This works great for me as well. Thank you so much.

  41. Works very well out-of-the-box!
    Thanks :)

  42. It’s working quite well.

  43. Thank you very much!

  44. Thank you! Works great for me, too.