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>
.
SortableBindingList
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;
}
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…