I was writing a thin client application where I had to implement some efficiency-oriented business logic, and encountered the problem, that the generic collection of custom data objects can’t be sorted in the DataGridView by simply clicking on the column headers. I started to google up solutions, but either they were for ASP, or they were incomplete, some of them were not usable at all. I ended up using a solution provided on MSDN, but it was far from a complete, bugless solution.
The framework has an own generic abstract class for this situation, called BindingList. The class itself is pretty straightforward, but the code here requires minimun knowledge of the System.ComponentModel and System.Reflection namespace.
Public Class SortableBindingList(Of T) Inherits BindingList(Of T) Private _isSorted As Boolean Private _sortDirection As ListSortDirection Private _sortProperty As PropertyDescriptor 'This override shows the binded object, that our list supports sorting Protected Overrides ReadOnly Property SupportsSortingCore() As Boolean Get Return True End Get End Property 'And that it can sort bi-directional Protected Overrides ReadOnly Property SortDirectionCore() As ListSortDirection Get Return _sortDirection End Get End Property 'And that it can sort by T typed object's properties Protected Overloads Overrides ReadOnly Property SortPropertyCore() As PropertyDescriptor Get Return _sortProperty End Get End Property 'This is the method, what gets called when the sort event occurs in the bound object Protected Overloads Overrides Sub ApplySortCore(ByVal prop As PropertyDescriptor, ByVal direction As ListSortDirection) Dim items As List(Of T) = TryCast(Me.Items, List(Of T)) If items IsNot Nothing Then Dim pc As New PropertyComparer(Of T)(prop.Name, direction) items.Sort(pc) _isSorted = True _sortDirection = direction _sortProperty = prop Else _isSorted = False End If OnListChanged(New ListChangedEventArgs(ListChangedType.Reset, -1)) End Sub 'This shows if our list is already sorted or not Protected Overloads Overrides ReadOnly Property IsSortedCore() As Boolean Get Return _isSorted End Get End Property 'Removing the sort Protected Overrides Sub RemoveSortCore() _isSorted = False End Sub Sub New(ByVal list As ICollection(Of T)) MyBase.New(list) End Sub End Class
The list uses a simple property comparer; the default comparer to compare two properties. It is good for most cases; of course you have to have the correct property type declaration.
Public Class PropertyComparer(Of T) Implements IComparer(Of T) Private _property As PropertyInfo Private _sortDirection As ListSortDirection Public Sub New(ByVal sortProperty As String, ByVal sortDirection As ListSortDirection) _property = GetType(T).GetProperty(sortProperty) Me._sortDirection = sortDirection End Sub Public Function Compare(ByVal x As T, ByVal y As T) As Integer Implements IComparer(Of T).Compare Dim valueX As Object = _property.GetValue(x, Nothing) Dim valueY As Object = _property.GetValue(y, Nothing) If _sortDirection = ListSortDirection.Ascending Then Return Comparer.[Default].Compare(valueX, valueY) Return Comparer.[Default].Compare(valueY, valueX) End Function End Class
Comments
Great Post
I've been developing a vb.net / MS SQL application and I'm in the process of converting it to a 3 tier application. It's currently a 2.5 tier application. I was looking for a way to sort unbound datagrid views and your solutions was perfect. Thanks so much.
Sincerely
Thanx indeed!
Thanx indeed!
JUST GREAT!!
Hi Geri! this is really fantastic! Im using LINQ2SQL and returning a list of entities made it impossible to enable sorting on my datagridview. Im thankful I came across your post here. And just in case anyone's interested Im posting the working C# version below. The only thing I changed in the SortableBindingList constructor is changed the parameter from ICollection<T> to IList<T> - the former threw an error
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Collections;
using System.Reflection;
public class SortableBindingList<T> : BindingList<T> {
private bool _isSorted;
private ListSortDirection _sortDirection;
private PropertyDescriptor _sortProperty;
//This override shows the binded object, that our list supports sorting
protected override bool SupportsSortingCore {
get { return true; }
}
//And that it can sort bi-directional
protected override ListSortDirection SortDirectionCore {
get { return _sortDirection; }
}
//And that it can sort by T typed object's properties
protected override PropertyDescriptor SortPropertyCore {
get { return _sortProperty; }
}
//This is the method, what gets called when the sort event occurs in the bound object
protected override void ApplySortCore( PropertyDescriptor prop, ListSortDirection direction ) {
List<T> items = this.Items as List<T>;
if (items != null) {
PropertyComparer<T> pc = new PropertyComparer<T>( prop.Name, direction );
items.Sort( pc );
_isSorted = true;
_sortDirection = direction;
_sortProperty = prop;
} else {
_isSorted = false;
}
OnListChanged( new ListChangedEventArgs( ListChangedType.Reset, -1 ) );
}
//This shows if our list is already sorted or not
protected override bool IsSortedCore {
get { return _isSorted; }
}
//Removing the sort
protected override void RemoveSortCore() {
_isSorted = false;
}
public SortableBindingList( IList<T> list )
: base( list ) {
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Collections;
using System.Reflection;
public class PropertyComparer<T> : IComparer<T> {
private PropertyInfo _property;
private ListSortDirection _sortDirection;
public PropertyComparer( string sortProperty, ListSortDirection sortDirection ) {
_property = typeof( T ).GetProperty( sortProperty );
this._sortDirection = sortDirection;
}
public int Compare( T x, T y ) {
object valueX = _property.GetValue( x, null );
object valueY = _property.GetValue( y, null );
if (_sortDirection == ListSortDirection.Ascending) return Comparer.Default.Compare( valueX, valueY );
return Comparer.Default.Compare( valueY, valueX );
}
}
cheers!
how to instatiate with a list
I see here
that I can make a binding list from a list in the constructor new
dim lc as new List( of Customer)
dim blc as new BindingList(Of Customer)(lc)
dataGridView1.DataSource = blc
But I cant get it to work with the above sortable binding list
Any ideas on how to convert, instantiate, or create the above sortable binding list
like I can the bindinglist by passing a list as a parameter to the constructor?