2009-06-30 24 views
20

Estoy intentando escribir una SortableBindingList que pueda usar para mi aplicación. He encontrado mucha discusión acerca de cómo implementar el soporte básico de clasificación para que el BindingList clasificará cuando se utiliza en el contexto de un DataGridView o algún otro control enlazado incluyendo este puesto de stackoverflow:
DataGridView sort and e.g. BindingList<T> in .NETBindingList <T> .Sort() se comporta como una lista <T> .Sort()

Todo esto es muy útil y he implementado el código, probado, etc. y todo está funcionando, pero en mi situación particular, necesito poder admitir una simple llamada a Sort() y hacer que esa llamada use el valor predeterminado IComparable.CompareTo() para hacer la clasificación, en lugar de hacer una llamada a ApplySortCore (PropertyDescriptor, ListSortDirection).

La razón es porque tengo una gran cantidad de código que depende de la llamada Sort() porque esta clase en particular se heredó originalmente de List y recientemente se cambió para ser una BindingList.

Así que específicamente, tengo una clase llamada VariableCode y una clase de colección llamada VariableCodeList. VariableCode implementa la lógica IComparable y allí es moderadamente compleja basada en varias propiedades, 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? 
    } 
} 

He hecho varios intentos fallidos en el método de reutilización ApplySortCore en el tipo(), pero lo que sigue frustrando es que ApplySortCore espera que un PropertyDescriptor haga su clasificación y no puedo encontrar la forma de conseguir que use la lógica IComparable.CompareTo().

¿Puede alguien señalarme en la dirección correcta?

Muchas gracias.


EDITAR: Este es el código final basado en la respuesta de Marc para referencia futura.

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

    } 

Respuesta

16

Emular una propiedad para hacer el tipo es probablemente demasiado. Lo primero a mirar es Comparer<T>.Default. Podría, sin embargo, resulta que la cosa más fácil de hacer es:

  • extraer los datos en List<T> o similares
  • ordenar los datos extraídos
  • desactivar las notificaciones
  • cargar los datos
  • vuelva a habilitar las notificaciones
  • enviar un "reset" mensaje

Por cierto, también deberías deshabilitar las notificaciones durante tu clasificación existente.

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. Gracias. Seguía pensando que me estaba perdiendo algo y que habría algo de apoyo integrado en alguna parte, en lugar de solo ejecutar la solución simple. Buen punto en las notificaciones, también. Terminé usando una lista internamente en lugar de una matriz para poder admitir la ordenación flexible basada en delegados, así (como la clase List es compatible). La publicación se actualiza con el código final. –

9

Tuve el mismo problema y esta publicación me ayudó a resolverlo.

Como he implementado esta solución (en base a Marc y el código de Pablo) como una extensión y añadió dos métodos de clasificación simples, me gustaría compartirlo con ustedes:

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

espero que esto sea útil.

+1

Muy bonito, limpio y fácil de usar. Personalmente, preferí devolver BindingList del método de extensión. – PhilHoy