2012-08-16 19 views
8

Tengo un WGD DataGrid, y lo tengo para que pueda ordenarlo haciendo clic en los encabezados de las columnas. Funciona, pero es inestable. ¿Cómo hago para que haga una clasificación estable?¿Cómo ordenar un DataGrid usando una clasificación estable?

Con esto quiero decir, si tengo esta tabla:

Class | Student | Grade 
----------------------------- 
Art  | James  | A 
Art  | Amy  | B 
Art  | Charlie | A 
Science | James  | D 
Science | Amy  | A 
Science | Charlie | C 
History | James  | B 
History | Amy  | A 
History | Charlie | C 

Si ordenar por estudiante, que funciona como era de esperar:

Class | Student | Grade 
----------------------------- 
Art  | Amy  | B 
Science | Amy  | A 
History | Amy  | A 
Art  | Charlie | A 
Science | Charlie | C 
History | Charlie | C 
Art  | James  | A 
Science | James  | D 
History | James  | B 

Pero si ahora ordenar por clase :

Class | Student | Grade 
----------------------------- 
Art  | James  | A 
Art  | Amy  | B 
Art  | Charlie | A 
History | James  | B 
History | Amy  | A 
History | Charlie | C 
Science | James  | D 
Science | Amy  | A 
Science | Charlie | C 

Se destruye el orden de los alumnos (clasificación inestable). Lo que quiero es la clasificación estable, donde se conserva el orden:

Class | Student | Grade 
----------------------------- 
Art  | Amy  | B 
Art  | Charlie | A 
Art  | James  | A 
History | Amy  | A 
History | Charlie | C 
History | James  | B 
Science | Amy  | A 
Science | Charlie | C 
Science | James  | D 

parece que debería funciona de esta manera predeterminada, o al menos ser un conmutador. ¿Alguien tiene alguna sugerencia? La idea de @Eirik de hacer clic de mayúsculas funciona, y eso muestra que el comportamiento está presente. Sin embargo, lo que realmente me gustaría es que funcione así sin ningún modificador. No debería ser una causa de "ordenar por esto, luego esto, luego esto", debería ser el caso de cambiar el algoritmo por uno diferente.

ver esto: http://en.wikipedia.org/wiki/Sorting_algorithm#Stability

+0

¿Está buscando una forma de hacer esto en el código subyacente, o esperando que alguien sepa cómo hacer que el comportamiento por clic de cambio sea el comportamiento predeterminado? – Grubsnik

+0

@Grubsnik Esperaba que alguien supiera cómo cambiar el algoritmo de ordenamiento, porque creo que el enfoque de cambio de clic es un gran truco. Sin embargo, hace el trabajo. – TarkaDaal

Respuesta

1

he logrado obtener una clasificación estable utilizando un comparador de encargo, pero siente un poco como un gran truco ...

utilizo propiedad CustomSort de ListCollectionView para establecer mi Comparer costumbre, que me necesita para pasar el recopilación de datos al crear una instancia.

private void Sorting(IEnumerable collection) 
{ 
    var view = CollectionViewSource.GetDefaultView(collection) as ListCollectionView; 

    if (view != null) 
    { 
     view.CustomSort = new StableComparer(collection); 
    } 
} 

En mi costumbre Comparer, utilizo la colección durante el método de comparación sólo para el repliegue de los artículos índices cuando la comparación periódica devuelve un cero (que son los mismos o tienen el mismo valor).

public class StableComparer : IComparer 
{ 
    public IEnumerable Collection { get; set; } 

    public StableComparer(IEnumerable collection) 
    { 
     Collection = collection; 
    } 

    public int Compare(object x, object y) 
    { 
     IComparable x_Comparable = x as IComparable; 
     IComparable y_Comparable = y as IComparable; 

     if (x_Comparable != null && y_Comparable != null) 
     { 
      var comparison = x_Comparable.CompareTo(y_Comparable); 

      // A zero value means x and y are equivalent for sorting, and they could 
      // be rearranged by an unstable sorting algorithm 
      if (comparison == 0 && Collection != null) 
      { 
       // IndexOf is an extension method for IEnumerable (not included) 
       var x_Index = Collection.IndexOf(x); 
       var y_Index = Collection.IndexOf(y); 

       // By comparing their indexes in the original collection, we get to 
       // preserve their relative order 
       if (x_Index != -1 && y_Index != -1) 
        comparison = x_Index.CompareTo(y_Index); 
      } 

      return comparison; 
     } 

     return 0; 
    } 
} 

Todavía estoy probando esto, así que no puede garantizar que esto funcionaría todo el tiempo ... Un problema sería mantener la propiedad de colección dentro de la Comparer actualizado, por ejemplo. O apoyando dos direcciones de clasificación (trabajar en ello ahora, no debería ser difícil). O revisando cómo funciona esto, en términos de rendimiento.

Pero creo que la idea es clara; aunque hacky, como dije.

8

Usted debe ser capaz de ordenar por varias columnas manteniendo pulsada la tecla de mayúsculas al hacer clic en las columnas. Intente hacer clic en la columna de la clase y luego mantenga presionada la tecla Mayús y haga clic en la columna del alumno.

Aquí es una solución para la adición de la clasificación en el código detrás:

private void myDataGridPreviewMouseDown(object sender, MouseButtonEventArgs e) 
{ 
    DependencyObject dep = (DependencyObject)e.OriginalSource; 

    while ((dep != null) && !(dep is DataGridColumnHeader)) 
    { 
     dep = VisualTreeHelper.GetParent(dep); 
    } 

    if (dep == null) 
     return; 

    if (dep is DataGridColumnHeader) 
    { 
     DataGridColumnHeader columnHeader = dep as DataGridColumnHeader; 

     ICollectionView view = CollectionViewSource.GetDefaultView((sender as DataGrid).ItemsSource); 

     if (columnHeader.Content.Equals("Class") || columnHeader.Content.Equals("Student")) 
     { 
      view.SortDescriptions.Clear(); 
      view.SortDescriptions.Add(new SortDescription("Class", ListSortDirection.Ascending)); 
      view.SortDescriptions.Add(new SortDescription("Student", ListSortDirection.Ascending)); 
     } 
    } 
} 

Para que esto funcione hay que desactivar la clasificación estándar. Una forma de hacer esto es para detener el evento de clasificación, así:

private void myDataGridSorting(object sender, DataGridSortingEventArgs e) 
{ 
    e.Handled = true; 
} 

Editar: Después de leer el comentario de hbarck leí tu pregunta de nuevo, y parece que me perdí algunas partes. Si cambia este código:

if (columnHeader.Content.Equals("Class") || columnHeader.Content.Equals("Student")) 
{ 
    view.SortDescriptions.Clear(); 
    view.SortDescriptions.Add(new SortDescription("Class", ListSortDirection.Ascending)); 
    view.SortDescriptions.Add(new SortDescription("Student", ListSortDirection.Ascending)); 
} 

a esto:

if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) 
{ 
    view.SortDescriptions.Clear(); 
} 

view.SortDescriptions.Insert(0, new SortDescription(columnHeader.Content.ToString(), ListSortDirection.Ascending)); 

tendrá clasificación estable. Haga clic en Estudiante para ordenar por Estudiante, luego haga clic en Clase para ordenar por Clase, Estudiante. Si mantiene presionado ctrl al hacer clic en borrar la ordenación anterior antes de ordenar por la columna que se hizo clic.

+0

+1. Esto es genial, gracias! Es útil a corto plazo. Sin embargo, no quiero marcarlo como la respuesta aún. Idealmente, me gustaría este comportamiento por defecto (por lo tanto, sin mantener shift). Voy a aclarar la pregunta. – TarkaDaal

+1

@TarkaDaal ¿cómo "eliminarás" la clasificación anterior? Digamos que quiere ordenar por Clase y Estudiante, pero luego quiere ordenar por Clase y Grado. Debería implementar alguna forma de configurar una nueva columna para ordenarla primero, por ejemplo, mantener presionada la tecla shift mientras hace clic ...;) – Eirik

+0

No es necesario. Si desea ordenar por Clase, entonces Grado, simplemente haga clic en Clase, luego Grado. El orden de Estudiante no importa. No se trata de ordenar por varias columnas, se trata de preservar el orden anterior siempre que sea posible. – TarkaDaal

Cuestiones relacionadas