Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

BindingList<T>.Sort() to behave like a List<T>.Sort()

I am attempting to write a SortableBindingList that I can use for my application. I have found lots of discussion about how to implement basic sorting support so that the BindingList will sort when used in the context of a DataGridView or some other bound control including this post from StackOverflow:
DataGridView sort and e.g. BindingList<T> in .NET

This is all very helpful and I have implemented the code, tested, etc. and it's all working, but in my particular situation, I need to be able to support a simple call to Sort() and have that call use the default IComparable.CompareTo() to do the sorting, rather than making a call to ApplySortCore(PropertyDescriptor, ListSortDirection).

The reason is because I have quite a great deal of code that's depending on the Sort() call because this particular class originally inherited from List and was recently changed to be a BindingList.

So specifically, I have a class called VariableCode and a collection class called VariableCodeList. VariableCode implements IComparable and the logic in there is moderately complex based on several properties, etc...

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?
    }
}

I've made a few failed attempts at repurposing the ApplySortCore method in the Sort(), but what keeps thwarting me is that the ApplySortCore expects a PropertyDescriptor to do its sort and I can't figure out how to get that to use the IComparable.CompareTo() logic.

Can someone point me in the right direction?

Many thanks.


EDIT: This is the final code based on Marc's response for future reference.

  /// <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();
     }

  }
like image 596
Paul Prewett Avatar asked Jun 30 '09 14:06

Paul Prewett


2 Answers

Emulating a property just to do the sort is probably overkill. The first thing to look at is Comparer<T>.Default. It might, however, turn out that the easiest thing to do is to:

  • extract the data into List<T> or similar
  • sort the extracted data
  • disable notifications
  • reload the data
  • re-enable notifications
  • send a "reset" message

btw, you should be disabling notifications during your existing sort, too.

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();     }     } 
like image 85
Marc Gravell Avatar answered Sep 29 '22 04:09

Marc Gravell


I had the same problem and this post helped me solve it!

As I implemented this solution (based on Marc's and Paul's code) as an extension and added two simple sort methods, I would like to share it with you:

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);         }     } 

Hope this is helpful.

like image 27
SolarX Avatar answered Sep 29 '22 04:09

SolarX