When I assign a List
to DataSource
of a DataGridView
, when I click on column headers, nothing happens and sorting doesn
How Sorting works in a data-bound DataGridView
When you click on a column header in a data-bound DataGridView
which its automatic sorting is enabled, first it checks if the list behind of the DataSource
property is IBindingList, then using SupportsSorting checks if the list supports sorting. Then it calls ApplySort method to sort the list.
When you use a DataTable as data source of the grid, the list behind the data source is actually a DataView which implements IBindingList
that supports sorting.
To have automatic support for sorting in a DataGridView
, the list should implement IBindingList
and its members which are related to sort.
Enable sorting in a BindingList<T>
To have typed list implementation of IBindingList
which also supports sorting, a good option is deriving from BindingList<T>. It implements IBindingList
but it doesn't support sorting by default. You can override it's methods and properties which are related to sorting: SupportsSortingCore
, IsSortedCore
, SortPropertyCore
, SortDirectionCore
and ApplySortCore
.
Existing Implementations
There are some implementations around:
SortableBindingList<T> implementation which is used in Entity Framework.
SortableSearchableList<T>
which is published in an MSDN article
If you are using Entity Framework, ToBindingList method of the Local property of DbSet<T>
returns a sortable BindingList<T>
.
Here is an implementation which is borrowed from Microsoft internal implementations with some small changes:
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;
public class SortableBindingList<T> : BindingList<T>
{
private bool _isSorted;
private ListSortDirection _sortDirection;
private PropertyDescriptor _sortProperty;
public SortableBindingList(List<T> list)
: base(list)
{
}
protected override void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction)
{
if (PropertyComparer.CanSort(prop.PropertyType))
{
((List<T>)Items).Sort(new PropertyComparer(prop, direction));
_sortDirection = direction;
_sortProperty = prop;
_isSorted = true;
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
}
}
protected override void RemoveSortCore()
{
_isSorted = false;
_sortProperty = null;
}
protected override bool IsSortedCore
{
get { return _isSorted; }
}
protected override ListSortDirection SortDirectionCore
{
get { return _sortDirection; }
}
protected override PropertyDescriptor SortPropertyCore
{
get { return _sortProperty; }
}
protected override bool SupportsSortingCore
{
get { return true; }
}
internal class PropertyComparer : Comparer<T>
{
private readonly IComparer _comparer;
private readonly ListSortDirection _direction;
private readonly PropertyDescriptor _prop;
private readonly bool _useToString;
public PropertyComparer(PropertyDescriptor prop, ListSortDirection direction)
{
if (!prop.ComponentType.IsAssignableFrom(typeof(T)))
{
throw new MissingMemberException(typeof(T).Name, prop.Name);
}
Debug.Assert(CanSort(prop.PropertyType), "Cannot use PropertyComparer unless it can be compared by IComparable or ToString");
_prop = prop;
_direction = direction;
if (CanSortWithIComparable(prop.PropertyType))
{
var property = typeof(Comparer<>).MakeGenericType(new[] { prop.PropertyType }).GetTypeInfo().GetDeclaredProperty("Default");
_comparer = (IComparer)property.GetValue(null, null);
_useToString = false;
}
else
{
Debug.Assert(
CanSortWithToString(prop.PropertyType),
"Cannot use PropertyComparer unless it can be compared by IComparable or ToString");
_comparer = StringComparer.CurrentCultureIgnoreCase;
_useToString = true;
}
}
public override int Compare(T left, T right)
{
var leftValue = _prop.GetValue(left);
var rightValue = _prop.GetValue(right);
if (_useToString)
{
leftValue = leftValue != null ? leftValue.ToString() : null;
rightValue = rightValue != null ? rightValue.ToString() : null;
}
return _direction == ListSortDirection.Ascending
? _comparer.Compare(leftValue, rightValue)
: _comparer.Compare(rightValue, leftValue);
}
public static bool CanSort(Type type)
{
return CanSortWithToString(type) || CanSortWithIComparable(type);
}
private static bool CanSortWithIComparable(Type type)
{
return type.GetInterface("IComparable") != null ||
(type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>));
}
private static bool CanSortWithToString(Type type)
{
return type.Equals(typeof(XNode)) || type.IsSubclassOf(typeof(XNode));
}
}
}
public static class EnumerableExtensions
{
public static BindingList<T> ToSortableBindingList<T>(this IEnumerable<T> source)
{
return new SortableBindingList<T>(source.ToList());
}
}
Example
private void Form1_Load(object sender, EventArgs e)
{
var list = Enumerable.Range(1, 10)
.Select(x => new { A = x, B = $"x" })
.ToSortableBindingList();
dataGridView1.DataSource = list;
}