DataGridView - 通过单击列标题对通用列表进行排序 [英] DataGridView - Sort Generic Lists by Click on Column Headers
问题描述
当我将 List
分配给 DataGridView
的 DataSource
时,当我单击列标题时,没有任何反应并且排序不会不工作;但是,如果我使用 DataTable
作为数据源,则单击标题时排序效果很好.
When I assign a List<MyClass>
to DataSource
of a DataGridView
, when I click on column headers, nothing happens and sorting doesn't work; but if I use a DataTable
as data source, the sorting works perfect when a header is clicked.
问题是:当我点击列标题时,应该使用哪种集合类型来启用 DataGridView
中的排序,就像它与 DataTable
一起工作一样?
The question is: Which kind of collection types should be used to enable sorting in DataGridView
just like it works with DataTable
when I click on column header?
推荐答案
排序在数据绑定的 DataGridView 中的工作原理
当您单击启用了自动排序的数据绑定 DataGridView
中的列标题时,它首先检查 DataSource
属性后面的列表是否为 IBindingList
,然后使用 SupportsSorting
检查列表是否支持排序.然后它调用 ApplySort
方法对列表进行排序.
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.
当您使用 DataTable
作为网格的数据源,数据源后面的列表实际上是一个DataView
实现了支持排序的 IBindingList
.
要在 DataGridView
中自动支持排序,列表应实现 IBindingList
及其与排序相关的成员.
To have automatic support for sorting in a DataGridView
, the list should implement IBindingList
and its members which are related to sort.
在 BindingList 中启用排序
要具有也支持排序的 IBindingList
的类型化列表实现,一个不错的选择是从 BindingList
.它实现了 IBindingList
但它默认不支持排序.您可以覆盖与排序相关的方法和属性:SupportsSortingCore
、IsSortedCore
、SortPropertyCore
、SortDirectionCore
和 <代码>ApplySortCore.
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
.
现有实现
周围有一些实现:
T>
实体框架中使用的实现.
SortableBindingList<T>
implementation which is used in Entity Framework.
SortableSearchableList
发布在 MSDN 文章
如果您使用实体框架,ToBindingList
方法的 Local
DbSet
属性返回一个可排序的 BindingList
>.
If you are using Entity Framework, ToBindingList
method of the Local
property of DbSet<T>
returns a sortable BindingList<T>
.
这是从 Microsoft 内部实现中借用的一个实现,并做了一些小改动:
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());
}
}
示例
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;
}
这篇关于DataGridView - 通过单击列标题对通用列表进行排序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!