2009-06-30 5 views
20

Sto tentando di scrivere un SortableBindingList che posso utilizzare per la mia applicazione. Ho trovato un sacco di discussione su come implementare il supporto di base di ordinamento in modo che il BindingList ordinerà quando usato nel contesto di un DataGridView o qualche altro controllo associato compreso questo post da StackOverflow:
DataGridView sort and e.g. BindingList<T> in .NETBindingList <T> .Sort() per comportarsi come un elenco <T> .Sort()

Tutto ciò è molto utile e ho implementato il codice, testato, ecc. e tutto funziona, ma nella mia situazione particolare, devo essere in grado di supportare una semplice chiamata a Sort() e fare in modo che quella chiamata usi l'IComparable.CompareTo() predefinito per fare l'ordinamento, anziché effettuare una chiamata a ApplySortCore (PropertyDescriptor, ListSortDirection).

Il motivo è perché ho un bel po 'di codice che dipende dalla chiamata Sort() perché questa particolare classe originariamente ereditata da List e recentemente è stata modificata per essere una BindingList.

In modo specifico, ho una classe chiamata VariableCode e una classe di raccolta chiamata VariableCodeList. VariableCode implementa IComparable e la logica in là è moderatamente complessa basata su diverse proprietà, ecc ...

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

Ho fatto un paio di tentativi falliti a riproporre il metodo ApplySortCore in sort(), ma ciò che mantiene contrastare io sono che ApplySortCore si aspetta che un PropertyDescriptor faccia il suo ordinamento e non riesco a capire come ottenerlo per usare la logica IComparable.CompareTo().

Qualcuno può indicarmi la giusta direzione?

Molte grazie.


MODIFICA: questo è il codice finale basato sulla risposta di Marc per riferimento futuro.

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

    } 

risposta

16

L'emulazione di una proprietà solo per eseguire l'ordinamento è probabilmente eccessiva. La prima cosa da guardare è Comparer<T>.Default. Potrebbe, tuttavia, risultare che la cosa più facile da fare è:

  • estrarre i dati in List<T> o simile
  • ordinare i dati estratti
  • disabilitare le notifiche
  • ricaricare i dati
  • ri-abilitare le notifiche
  • inviare un messaggio "reset"

btw, dovresti disattivare anche le notifiche durante il tuo ordinamento esistente.

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

Nice, Marc. Grazie. Continuavo a pensare che mi mancasse qualcosa e che ci sarebbe stato un supporto integrato da qualche parte, piuttosto che solo l'esecuzione della soluzione semplice. Buon punto anche per le notifiche. Mi sono ritrovato a utilizzare internamente un elenco anziché un array in modo da poter supportare anche l'ordinamento basato sui delegati flessibile (come i supporti di classe Elenco). Post viene aggiornato con il codice finale. –

9

Ho avuto lo stesso problema e questo post mi ha aiutato a risolverlo!

Come ho implementato questa soluzione (sulla base di Marc e il codice di Paul) come estensione e aggiunto due semplici metodi di ordinamento, vorrei condividere con voi:

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

Spero che questo è utile.

+1

Molto bello, pulito e facile da usare. Personalmente ho preferito restituire BindingList dal metodo di estensione. – PhilHoy