当我在DataGridView上点击列标题时,如何对一个DataBound列进行排序?

3

我认为这应该自动处理。我正在将一个DataGridView绑定到一个对象数组:

public class Entity {    
    public string Name { get; set; }     
    public int PrimaryKey { get; set; }
}

绑定网格:

public void BindGrid(Entity[] entities) {
    grdEntities.DataSource = entities;
}

当我点击“名称”列中的列标题时,即使SortMode设置为自动模式,也没有任何反应。而且列标题中的排序标志也消失了。
我已经尝试绑定到IBindingListIList,但并没有成功。
我希望有一个简单、优雅的解决方案来设置DataGridViewDataGridViewColumn上的属性,而不是必须创建一个新类来支持排序。我应该怎样做才能在DataBound的DataGridView上通过单击标题来支持按列排序?
2个回答

5

我创建了一个基于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;
  }
}

编辑 这应该提供有关如何基于上面的接口创建自己的 IComparer 的一些信息。 拥有基于此接口的自定义 IComparer 的优点是,您可以以不同的方式对某些列进行排序,而其他列则采用另一种方式进行排序(某些列可能是字符串,某些列可能是整数,某些列可能有关于置于顶部的特殊规则等)。以下是您的 IComparer 可能工作的示例:

public class MyGenericComparer : IByColumnComparer
{
  private string columnToCompare;
  private bool descending;

  public string SortColumn
  {
     get { return columnToCompare; }
     set { columnToCompare = value; }
  }

  public bool SortDescending
  {
     get { return descending; }
     set { descending = value; }
  }

  public MyGenericComparer(string column, bool descend)
  {
     columnToCompare = column;
     descending = descend;
  }

  public int Compare(object x, object y)
  {
     MyGenericObject firstObj = (MyGenericObject )x;
     MyGenericObject secondObj = (MyGenericObject )y;

     if (descending) 
     {
        MyGenericObject tmp = secondObj ;
        secondObj = firstObj ;
        firstObj = tmp;
     }

     if (columnToCompare == "StringColumn")
     {
        //Run code to compare strings, return the appropriate int
        //eg, "1" if firstObj was greater, "-1" is secondObj, "0" if equal
     }

     if (columnToCompare == "IntColumn")
     {
        //Run code to compare ints, return the appropriate int
        //eg, "1" if firstObj was greater, "-1" is secondObj, "0" if equal
     }
  }
}

然后,您只需使用比较器的实例创建列表即可!
public static MyGenericComparer GridComparer = new MyGenericComparer();
public static SortableGenericCollection<GenericObject> GridList = new SortableGenericCollection<GenericObject>(GridComparer);

你能否提供一个如何填充 SortableGenericCollection<T> 集合的示例?我尝试过了,但我不确定 SortingComparer 参数应该传入什么。 - Ian R. O'Brien

1

所以基本上在.NET中这不是自动完成的事情?我只是好奇SortMode枚举是用来做什么的;是为非绑定网格设计的,还是你总是需要添加自己的实现? - Ian R. O'Brien
如果您提供了一个扩展BindingList<T>并支持排序操作的数据源(更详细的文章请参见:http://msdn.microsoft.com/en-us/library/ms993236.aspx),那么它将自动完成。 - Sorin Comanescu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接