【问题标题】:How to sort BindingList<T>?如何对 BindingList<T> 进行排序?
【发布时间】:2017-02-04 08:34:10
【问题描述】:

我有数千个 MyClass 对象存储在 BindingList&lt;MyClass&gt; 中。我想按日期属性MyClass.dt对它们进行排序。

类 BindingList 不支持直接排序。我如何对BindingList&lt;T&gt; 进行排序而不复制所有对象?我需要按升序对它们进行排序,请按降序排序。

我不需要SortableBindingList 中描述的特殊类BindingList.Sort() to behave like a List.Sort()。我正在用一到两行代码寻找简短的解决方案。

【问题讨论】:

标签: c# winforms


【解决方案1】:

Linq 可以工作。

var sortedListInstance = new BindingList<MyClass>(unsortedListInstance.OrderBy(x => x.dt).ToList());

请记住,您会得到排序列表的浅表副本,而不是 MyClass 的重复实例。

不要忘记在代码文件的顶部包含命名空间System.Linq

【讨论】:

  • 请注意,这是您第一次创建列表时的绝佳解决方案。稍后插入项目时,您可能希望确保在开始时将它们 insert 放在正确的索引处。
  • 我绑定到这个绑定列表的事件会消失吗?
【解决方案2】:

在 BindingList 上实现排序的一种快速方法是使用 constructor that takes a backing IList< T > 作为其参数。您可以使用List&lt;T&gt; 作为支持并获得其Sort 功能。

根据documentation

使用此 BindingList 创建一个由 list 支持的 BindingList,从而确保对 list 的更改反映在 BindingList 中。

如果您的 MyClass 定义为:

internal class MyClass
{
    public MyClass(string name, Int32 num)
    {
        this.Name = name;
        this.Num = num;
    }
    public string Name {get; set;}
    public Int32 Num {get; set;}
}

然后你可以做这样的事情在Num字段上排序。

private List<MyClass> backing;
private BindingList<MyClass> bl;

    private void InitializeBindingList()
        {
            backing = new List<MyClass>();
            bl = new BindingList<MyClass>(backing);
            bl.Add(new MyClass("a", 32));
            bl.Add(new MyClass("b", 23));
            bl.Add(new MyClass("c", 11));
            bl.Add(new MyClass("d", 34));
            bl.Add(new MyClass("e", 53));
        }

    private void SortBindingList()
        {
            backing.Sort((MyClass X, MyClass Y) => X.Num.CompareTo(Y.Num));
            // tell the bindinglist to raise a list change event so that 
            // bound controls reflect the new item order
            bl.ResetBindings();
        }
    }

您需要在对支持列表进行排序后调用BindingList.ResetBindings 方法来通知任何绑定的控件BindingList 已更改并更新控件。

【讨论】:

  • 将项目添加到后备列表的问题是BindingList.ResetBindings 不会为TItemPropertyChanged 事件添加事件处理程序。令人沮丧的是,只有 BindingList(IList&lt;T&gt;) 构造函数会这样做。
  • @Dai,我不明白你的意思。如果我修改上面的示例以在 MyClass 上实现 INotifyPropertyChanged,则对基础项目的更改将在 DGV 中响应,而无需任何其他操作。您能否提供更多关于您的使用案例的详细信息?
  • 如果您调用backing.Add( new MyClass(..) ) 而不是bl.Add( new MyClass(...) ),那么即使您在将ResetBindings 添加到backing 之后调用ResetBindings,属性更改事件处理也将不起作用。
  • @dai,感谢您的澄清,除了排序技巧,我没有想到要返回原始列表。
【解决方案3】:
//Convert it to a data table, then the Automatic will work.
DataGridView.DataSource = ConvertToDataTable(MyList).DefaultView;

public DataTable ConvertToDataTable(IBindingList list)
{
   DataTable dt = new DataTable();

   if (list.Count > 0)
   {
      Type typ = list[0].GetType();

      PropertyInfo[] arrProps = typ.GetProperties();

      foreach (PropertyInfo pi in arrProps)
      {

         Type colType = pi.PropertyType;
         if (colType.IsGenericType)
         {
            colType = colType.GetGenericArguments()[0];
         }

         dt.Columns.Add(pi.Name, colType);
      }

      foreach (object obj in list)
      {
         DataRow dr = dt.NewRow();

         foreach (PropertyInfo pi in arrProps)
         {
            if (pi.GetValue(obj, null) == null)
               dr[pi.Name] = DBNull.Value;
            else
               dr[pi.Name] = pi.GetValue(obj, null);
         }

         dt.Rows.Add(dr);
      }
   }
   return dt;
}

【讨论】:

  • 排序在哪里?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-13
  • 1970-01-01
相关资源
最近更新 更多