对绑定到绑定列表的 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 中的自定义列进行排序

最终编辑/答案:我想不出一种方法来做到这一点,同时仍然使用 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关于使这个类更通用的建议。