对绑定到绑定列表的 DataGridView 中的自定义列进行排序
本文关键字:绑定 排序 自定义 DataGridView 列表 | 更新日期: 2023-09-27 17:56:29
我有一个数据绑定到 BindingList 的 DataGridView。我的 DataGridView 还包含几个我添加的自定义列。这些不是数据绑定的,而是基于我的 BindingList 中的项目生成的(即:我的 BindingList 中类型 A 的项目具有 B 类型的属性;我的自定义列显示 B.Name(编辑:在这种情况下,"Name"是类 B 的属性,因此在 BindingList 中的项目中找不到由列表示的属性))。
我需要能够对 DataGridView 中的所有列进行排序。DataGridView 有两种排序方法:Sort(IComparer)和 Sort(DataGridViewColumn,ListSortDirection)。我使用第二个对数据绑定列进行排序,但是当用于非数据绑定列时,它当然会引发异常。如果数据源不为 null,则第一种方法将引发异常。
因此,据我所知,DataGridView 的内置排序方法都不起作用。我还能如何根据我的自定义列对网格进行排序?
编辑:
我目前所做的是按照此处看到的说明处理对列标题的单击:http://msdn.microsoft.com/en-us/library/system.windows.forms.datagridview.columnheadermouseclick.aspx
问题出现在以下行上:
dataGridView1.Sort(newColumn, direction);
当 newColumn 在我的绑定列表中保存对象的一个属性时,事情会很好用。但是为了对我的一个自定义列进行排序,我将不得不完全避免这一行,并找到一些其他方法来基于该列对数据网格进行排序。这是否意味着必须构建自己的排序函数?这似乎是一种巨大的痛苦。
最终编辑/答案:我想不出一种方法来做到这一点,同时仍然使用 DataGridView 中内置的排序机制。如果我站在你的立场上,我可能会将每列的排序模式更改为"编程",然后自己处理"列标题鼠标单击"。此时,您应该在修改后的 BindingList 类中调用一个排序方法,该方法将根据单击的列根据需要执行排序。这样可以避免使用 DGV 的 Sort 方法,而是直接对基础列表进行排序。
完整的话语 位于评论部分。 原答案紧随其后:
编辑:由于对问题的一些困惑和随后的讨论,我在这个答案的评论中有一个新的建议。 我将保留我发布的原始答案,以便我们可以参考它。
我最近不得不这样做 - 我不会撒谎,这是一个真正的痛苦。 我确实想出了一个解决方案(在SO的一些朋友的帮助下),所以在这里。 我创建了一个新的基于 IComparer 的接口,允许您指定列和方向。 我这样做只是因为我需要我的排序代码尽可能通用 - 我有两个网格需要像这样排序,而且我不想维护两倍的代码。 这是界面,非常简单:
public interface IByColumnComparer : IComparer
{
string SortColumn { get; set; }
bool SortDescending { get; set; }
}
显然,如果您不担心保持通用(您可能应该),那么这不是绝对必要的。 然后,我构建了一个基于 BindingList<> 的新类。 这允许我覆盖排序代码,并在逐列的基础上提供我自己的IByColumnComparer,这允许我所需的灵活性。 看看这个:
public class SortableGenericCollection<T> : BindingList<T>
{
IByColumnComparer GenericComparer = null;
public SortableGenericCollection(IByColumnComparer SortingComparer)
{
GenericComparer = SortingComparer;
}
protected override bool SupportsSortingCore
{
get
{
return true;
}
}
protected override bool IsSortedCore
{
get
{
for (int i = 0; i < Items.Count - 1; ++i)
{
T lhs = Items[i];
T rhs = Items[i + 1];
PropertyDescriptor property = SortPropertyCore;
if (property != null)
{
object lhsValue = lhs == null ? null :
property.GetValue(lhs);
object rhsValue = rhs == null ? null :
property.GetValue(rhs);
int result;
if (lhsValue == null)
{
result = -1;
}
else if (rhsValue == null)
{
result = 1;
}
else
{
result = GenericComparer.Compare(lhs, rhs);
}
if (result >= 0)
{
return false;
}
}
}
return true;
}
}
private ListSortDirection sortDirection;
protected override ListSortDirection SortDirectionCore
{
get
{
return sortDirection;
}
}
private PropertyDescriptor sortProperty;
protected override PropertyDescriptor SortPropertyCore
{
get
{
return sortProperty;
}
}
protected override void ApplySortCore(PropertyDescriptor prop,
ListSortDirection direction)
{
sortProperty = prop;
sortDirection = direction;
GenericComparer.SortColumn = prop.Name;
GenericComparer.SortDescending = direction == ListSortDirection.Descending ? true : false;
List<T> list = (List<T>)Items;
list.Sort(delegate(T lhs, T rhs)
{
if (sortProperty != null)
{
object lhsValue = lhs == null ? null :
sortProperty.GetValue(lhs);
object rhsValue = rhs == null ? null :
sortProperty.GetValue(rhs);
int result;
if (lhsValue == null)
{
result = -1;
}
else if (rhsValue == null)
{
result = 1;
}
else
{
result = GenericComparer.Compare(lhs, rhs);
}
return result;
}
else
{
return 0;
}
});
}
protected override void RemoveSortCore()
{
sortDirection = ListSortDirection.Ascending;
sortProperty = null;
}
}
现在,正如您在 ApplySortCore 方法中看到的那样,我直接从 DataGridView 接收列和方向 - 这意味着我不是以编程方式调用它。 这听起来不像您想要执行的操作,但是如果需要以编程方式调用此代码并传递相应的IByColumnComparer,则可以轻松修改此代码。 我向您展示所有这些的目的是让您了解如何修改排序算法,这非常有用。
特别感谢@MartinhoFernandes关于使这个类更通用的建议。