我BindingList
在我的Windows窗体中使用了一个包含" IComparable
"联系人对象列表的窗体.现在我希望用户能够按网格中显示的任何列进行排序.
在线MSDN上有一种描述如何实现基于BindingList
允许排序的自定义集合的方法.但是,是否有一个Sort-event或者某些东西可以在DataGridView中捕获(或者,甚至更好,在BindingSource上)以使用自定义代码对底层集合进行排序?
我真的不喜欢MSDN描述的方式.另一种方法是我可以轻松地将LINQ查询应用于集合.
我用谷歌搜索并尝试了更多的时间......
到目前为止,.NET中没有内置方法.您必须基于实现自定义类BindingList
.自定义数据绑定,第2部分(MSDN)中描述了一种方法.我最终生成了一个不同的ApplySortCore
-method 实现,以提供一个与项目无关的实现.
protected override void ApplySortCore(PropertyDescriptor property, ListSortDirection direction) { ListitemsList = (List )this.Items; if(property.PropertyType.GetInterface("IComparable") != null) { itemsList.Sort(new Comparison (delegate(T x, T y) { // Compare x to y if x is not null. If x is, but y isn't, we compare y // to x and reverse the result. If both are null, they're equal. if(property.GetValue(x) != null) return ((IComparable)property.GetValue(x)).CompareTo(property.GetValue(y)) * (direction == ListSortDirection.Descending ? -1 : 1); else if(property.GetValue(y) != null) return ((IComparable)property.GetValue(y)).CompareTo(property.GetValue(x)) * (direction == ListSortDirection.Descending ? 1 : -1); else return 0; })); } isSorted = true; sortProperty = property; sortDirection = direction; }
使用这个,您可以按任何实现的成员进行排序IComparable
.
我非常欣赏Matthias的简洁和美丽的解决方案.
然而,虽然这为低数据量提供了出色的结果,但在处理大数据量时,由于反射,性能不是很好.
我用一组简单的数据对象运行测试,计算100000个元素.按整数类型属性排序大约需要1分钟.我将进一步细节的实现将此更改为~200ms.
基本思想是使强类型比较受益,同时保持ApplySortCore方法的通用性.以下内容将通用比较委托替换为对派生类中实现的特定比较器的调用:
SortableBindingList
protected abstract ComparisonGetComparer(PropertyDescriptor prop);
ApplySortCore更改为:
protected override void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction) { ListitemsList = (List )this.Items; if (prop.PropertyType.GetInterface("IComparable") != null) { Comparison comparer = GetComparer(prop); itemsList.Sort(comparer); if (direction == ListSortDirection.Descending) { itemsList.Reverse(); } } isSortedValue = true; sortPropertyValue = prop; sortDirectionValue = direction; }
现在,在派生类中,必须为每个可排序属性实现比较器:
class MyBindingList:SortableBindingList{ protected override Comparison GetComparer(PropertyDescriptor prop) { Comparison comparer; switch (prop.Name) { case "MyIntProperty": comparer = new Comparison (delegate(DataObject x, DataObject y) { if (x != null) if (y != null) return (x.MyIntProperty.CompareTo(y.MyIntProperty)); else return 1; else if (y != null) return -1; else return 0; }); break; // Implement comparers for other sortable properties here. } return comparer; } } }
这种变体需要更多的代码,但是,如果性能是一个问题,我认为值得付出努力.
我知道所有这些答案在撰写时都很好.可能他们仍然是.我正在寻找类似的东西,并找到了一个替代解决方案,将任何列表或集合转换为可排序BindingList
.
以下是重要的片段(下面分享了完整示例的链接):
void Main() { DataGridView dgv = new DataGridView(); dgv.DataSource = new ObservableCollection(Person.GetAll()).ToBindingList(); }
此解决方案使用Entity Framework库中提供的扩展方法.因此,在继续进行之前,请考虑以下事项:
如果你不想使用Entity Framework,那很好,这个解决方案也没有使用它.我们只是使用他们开发的扩展方法.EntityFramework.dll的大小为5 MB.如果它在Petabytes时代对你来说太大了,请随意从上面的链接中提取方法及其依赖项.
如果您正在使用(或想要使用)Entity Framework(> = v6.0),那么您无需担心.只需安装Entity Framework Nuget包即可开始使用.
我在这里上传了LINQPad代码示例.
下载示例,使用LINQPad打开它并点击F4.
你应该看到红色的EntityFramework.dll.从这个位置下载DLL .浏览并添加引用.
单击确定.点击F5.
如您所见,您可以通过单击DataGridView控件上的列标题对所有四列不同数据类型进行排序.
那些没有LINQPad的人,仍然可以下载查询并用记事本打开它,看看完整的样本.