BindingList<T>.Sort() 表现得像 List<T>.Sort()

Posted

技术标签:

【中文标题】BindingList<T>.Sort() 表现得像 List<T>.Sort()【英文标题】:BindingList<T>.Sort() to behave like a List<T>.Sort() 【发布时间】:2010-11-07 00:39:45 【问题描述】:

我正在尝试编写一个可用于我的应用程序的 SortableBindingList。我发现很多关于如何实现基本排序支持的讨论,以便 BindingList 在 DataGridView 或其他一些绑定控件的上下文中使用时进行排序,包括来自 *** 的这篇文章:DataGridView sort and e.g. BindingList<T> in .NET

这一切都非常有帮助,我已经实现了代码,经过测试等,一切正常,但在我的特殊情况下,我需要能够支持对 Sort() 的简单调用,并让该调用使用默认 IComparable.CompareTo() 进行排序,而不是调用 ApplySortCore(PropertyDescriptor, ListSortDirection)。

原因是因为我有相当多的代码依赖于 Sort() 调用,因为这个特定的类最初继承自 List,最近被更改为 BindingList。

具体来说,我有一个名为 VariableCode 的类和一个名为 VariableCodeList 的集合类。 VariableCode 实现了 IComparable 并且其中的逻辑基于几个属性等......

public class VariableCode : ...  IComparable ...

    public int CompareTo(object p_Target)
    
        int output = 0;
        //some interesting stuff here
        return output;
    


public class VariableCodeList : SortableBindingList<VariableCode>

    public void Sort()
    
        //This is where I need help
        //  How do I sort this list using the IComparable
        //  logic from the class above?
    

我在 Sort() 中重新利用 ApplySortCore 方法进行了几次失败的尝试,但一直阻碍我的是 ApplySortCore 期望 PropertyDescriptor 进行排序,我不知道如何让它使用 IComparable.CompareTo() 逻辑。

有人能指出正确的方向吗?

非常感谢。


编辑:这是基于 Marc 的响应的最终代码,以供将来参考。

  /// <summary>
  /// Sorts using the default IComparer of T
  /// </summary>
  public void Sort()
  
     sort(null, null);
  
  public void Sort(IComparer<T> p_Comparer)
  
     sort(p_Comparer, null);
  
  public void Sort(Comparison<T> p_Comparison)
  
     sort(null, p_Comparison);
  
  private void sort(IComparer<T> p_Comparer, Comparison<T> p_Comparison)
  

     m_SortProperty = null;
     m_SortDirection = ListSortDirection.Ascending;

     //Extract items and sort separately
     List<T> sortList = new List<T>();
     this.ForEach(item => sortList.Add(item));//Extension method for this call
     if (p_Comparison == null)
     
        sortList.Sort(p_Comparer);
     //if
     else
     
        sortList.Sort(p_Comparison);
     //else

     //Disable notifications, rebuild, and re-enable notifications
     bool oldRaise = RaiseListChangedEvents;
     RaiseListChangedEvents = false;
     try
     
        ClearItems();
        sortList.ForEach(item => this.Add(item));
     
     finally
     
        RaiseListChangedEvents = oldRaise;
        ResetBindings();
     

  

【问题讨论】:

【参考方案1】:

模拟一个属性只是为了进行排序可能是矫枉过正。 首先要看的是Comparer&lt;T&gt;.Default。然而,事实证明,最简单的做法是:

将数据提取到List&lt;T&gt; 或类似文件中 对提取的数据进行排序 禁用通知 重新加载数据 重新启用通知 发送“重置”消息

顺便说一句,您也应该在现有排序期间禁用通知。

public void Sort() 
    // TODO: clear your "sort" variables (prop/order)

    T[] arr = new T[Count];
    CopyTo(arr, 0);
    Array.Sort(arr);
    bool oldRaise = RaiseListChangedEvents;
    RaiseListChangedEvents = false; // <=== oops, added!
    try 
        ClearItems();
        foreach (T item in arr) 
            Add(item);
        
     finally 
        RaiseListChangedEvents = oldRaise;
        ResetBindings();
        

【讨论】:

很好,马克。谢谢你。我一直在想我错过了一些东西,并且在某处会有一些内置的支持,而不仅仅是执行简单的解决方案。通知的好点,以及。我最终在内部使用 List 而不是 Array,这样我也可以支持灵活的基于委托的排序(就像 List 类支持的那样)。使用最终代码更新帖子。【参考方案2】:

我遇到了同样的问题,这篇文章帮我解决了!

由于我将这个解决方案(基于 Marc 和 Paul 的代码)实现为扩展并添加了两个简单的排序方法,因此我想与您分享:

public static void SortAscending<T, P>(this BindingList<T> bindingList, Func<T, P> sortProperty)
    
        bindingList.Sort(null, (a, b) => ((IComparable<P>)sortProperty(a)).CompareTo(sortProperty(b)));
    
    public static void SortDescending<T, P>(this BindingList<T> bindingList, Func<T, P> sortProperty)
    
        bindingList.Sort(null, (a, b) => ((IComparable<P>)sortProperty(b)).CompareTo(sortProperty(a)));
    
    public static void Sort<T>(this BindingList<T> bindingList)
    
        bindingList.Sort(null, null);
    
    public static void Sort<T>(this BindingList<T> bindingList, IComparer<T> comparer)
    
        bindingList.Sort(comparer, null);
    
    public static void Sort<T>(this BindingList<T> bindingList, Comparison<T> comparison)
    
        bindingList.Sort(null, comparison);
    
    private static void Sort<T>(this BindingList<T> bindingList, IComparer<T> p_Comparer, Comparison<T> p_Comparison)
    

       //Extract items and sort separately
        List<T> sortList = new List<T>();
        bindingList.ForEach(item => sortList.Add(item));//Extension method for this call
        if (p_Comparison == null)
        
            sortList.Sort(p_Comparer);
        //if
        else
        
            sortList.Sort(p_Comparison);
        //else

        //Disable notifications, rebuild, and re-enable notifications
        bool oldRaise = bindingList.RaiseListChangedEvents;
        bindingList.RaiseListChangedEvents = false;
        try
        
        bindingList.Clear();
        sortList.ForEach(item => bindingList.Add(item));
        
        finally
        
        bindingList.RaiseListChangedEvents = oldRaise;
        bindingList.ResetBindings();
        

    

    public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
    
        if (source == null) throw new ArgumentNullException("source");
        if (action == null) throw new ArgumentNullException("action");

        foreach (T item in source)
        
            action(item);
        
    

希望这有帮助。

【讨论】:

非常好,干净且易于使用。我个人更喜欢从扩展方法返回 BindingList。

以上是关于BindingList<T>.Sort() 表现得像 List<T>.Sort()的主要内容,如果未能解决你的问题,请参考以下文章

是啥导致 BindingList<T> 中的 ListChangedType.ItemMoved ListChange 事件?

List<T> vs BindingList<T> 优点/缺点

BindingList<> ListChanged 事件

交换 BindingList<SomeClass> 元素需要很多时间,为啥会这样,我该怎么办?

从 BindingList 中删除重复项

当 BindingList 中的现有项目发生更改时,Listbox 拒绝更新