WPF DataGrid CustomSort para cada columna

Tengo un WGD DataGrid vinculado a un CollectionViewSource que encapsula un ObservableCollection. Este CollectionViewSource tiene dos objectives principales:

1) Agrupar cada elemento por una propiedad específica de T. Estoy usando un ValueConverter en GroupDescription para obtener el comportamiento de agrupación que deseo.

2) Ordenar la grilla por a) principalmente el nombre del grupo (como se define arriba) yb) los ítems individuales del grupo. Estoy logrando esto adjuntando un IComparer personalizado a la propiedad ‘CustomSort’ de CollectionViewSource.

Esto funciona muy bien en su mayor parte, sin embargo, tan pronto como se hace clic en un encabezado de columna, la lógica de clasificación se anula. No quiero deshabilitar la clasificación, sin embargo, me preguntaba si era posible asignar un orden de clasificación personalizado para una columna específica.

Para aclarar un poco las cosas, supongamos que un usuario hace clic en ‘Columna A’: por el momento, la lógica de ordenamiento encapsulada por mi CustomSorter se anula y DataGrid ahora está ordenada por esa propiedad. En lugar de ordenar por la propiedad seleccionada, me gustaría invertir la lógica de CustomSorter.

Creé un par de propiedades adjuntas que manejan este problema. ¡Espero que esto sea útil para alguien!

Primero, una interfaz simple para su comparador direccionalizado. Esto extiende IComparer pero nos da una propiedad más (SortDirection). Su implementación debería usar esto para determinar el orden correcto de los elementos (que de otro modo se habrían perdido).

public interface ICustomSorter : IComparer { ListSortDirection SortDirection { get; set; } } 

El siguiente es el comportamiento adjunto: hace dos cosas: 1) le dice a la grilla que use una lógica de ordenamiento personalizada (AllowCustomSort = true) yb) nos da la capacidad de establecer esta lógica a nivel de columna.

 public class CustomSortBehaviour { public static readonly DependencyProperty CustomSorterProperty = DependencyProperty.RegisterAttached("CustomSorter", typeof(ICustomSorter), typeof(CustomSortBehavior)); public static ICustomSorter GetCustomSorter(DataGridColumn gridColumn) { return (ICustomSorter)gridColumn.GetValue(CustomSorterProperty); } public static void SetCustomSorter(DataGridColumn gridColumn, ICustomSorter value) { gridColumn.SetValue(CustomSorterProperty, value); } public static readonly DependencyProperty AllowCustomSortProperty = DependencyProperty.RegisterAttached("AllowCustomSort", typeof(bool), typeof(CustomSortBehavior), new UIPropertyMetadata(false, OnAllowCustomSortChanged)); public static bool GetAllowCustomSort(DataGrid grid) { return (bool)grid.GetValue(AllowCustomSortProperty); } public static void SetAllowCustomSort(DataGrid grid, bool value) { grid.SetValue(AllowCustomSortProperty, value); } private static void OnAllowCustomSortChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var existing = d as DataGrid; if (existing == null) return; var oldAllow = (bool)e.OldValue; var newAllow = (bool)e.NewValue; if (!oldAllow && newAllow) { existing.Sorting += HandleCustomSorting; } else { existing.Sorting -= HandleCustomSorting; } } private static void HandleCustomSorting(object sender, DataGridSortingEventArgs e) { var dataGrid = sender as DataGrid; if (dataGrid == null || !GetAllowCustomSort(dataGrid)) return; var listColView = dataGrid.ItemsSource as ListCollectionView; if (listColView == null) throw new Exception("The DataGrid's ItemsSource property must be of type, ListCollectionView"); // Sanity check var sorter = GetCustomSorter(e.Column); if (sorter == null) return; // The guts. e.Handled = true; var direction = (e.Column.SortDirection != ListSortDirection.Ascending) ? ListSortDirection.Ascending : ListSortDirection.Descending; e.Column.SortDirection = sorter.SortDirection = direction; listColView.CustomSort = sorter; } } 

Para usarlo, implemente un ICustomComparer (con un constructor sin parámetros) y en su XAML:

          

La respuesta dada por trilson86 es excelente. Sin embargo, el tercer parámetro en las dos declaraciones DependencyProperty es incorrecto. En lugar de DataGrid y DataGridColumn, deberían ser CustomSortBehaviour, como tal:

 public static readonly DependencyProperty AllowCustomSortProperty = DependencyProperty.RegisterAttached("AllowCustomSort", typeof(bool), typeof(CustomSortBehaviour), // <- Here new UIPropertyMetadata(false, OnAllowCustomSortChanged)); public static readonly DependencyProperty CustomSorterProperty = DependencyProperty.RegisterAttached("CustomSorter", typeof(ICustomSorter), typeof(CustomSortBehaviour)); // <- Here 

Seguí recibiendo una advertencia de que la propiedad AllowCustomSort ya estaba registrada. Una pequeña investigación me llevó a la respuesta aquí .

En cualquier caso, es una respuesta excelente, así que gracias.

Aquí hay una manera:

 using System; using System.Collections; using System.ComponentModel; using System.Windows; using System.Windows.Controls; using System.Windows.Data; public static class DataGridSort { public static readonly DependencyProperty ComparerProperty = DependencyProperty.RegisterAttached( "Comparer", typeof(IComparer), typeof(DataGridSort), new PropertyMetadata( default(IComparer), OnComparerChanged)); private static readonly DependencyProperty ColumnComparerProperty = DependencyProperty.RegisterAttached( "ColumnComparer", typeof(ColumnComparer), typeof(DataGridSort), new PropertyMetadata(default(ColumnComparer))); private static readonly DependencyProperty PreviousComparerProperty = DependencyProperty.RegisterAttached( "PreviousComparer", typeof(IComparer), typeof(DataGridSort), new PropertyMetadata(default(IComparer))); public static readonly DependencyProperty UseCustomSortProperty = DependencyProperty.RegisterAttached( "UseCustomSort", typeof(bool), typeof(DataGridSort), new PropertyMetadata(default(bool), OnUseCustomSortChanged)); public static void SetComparer(DataGridColumn element, IComparer value) { element.SetValue(ComparerProperty, value); } public static IComparer GetComparer(DataGridColumn element) { return (IComparer)element.GetValue(ComparerProperty); } public static void SetUseCustomSort(DependencyObject element, bool value) { element.SetValue(UseCustomSortProperty, value); } public static bool GetUseCustomSort(DependencyObject element) { return (bool)element.GetValue(UseCustomSortProperty); } private static void OnComparerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var column = (DataGridColumn)d; var columnComparer = new ColumnComparer((IComparer)e.NewValue, column); column.SetValue(ColumnComparerProperty, columnComparer); } private static void OnUseCustomSortChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var dataGrid = (DataGrid)d; if ((bool)e.NewValue) { WeakEventManager.AddHandler(dataGrid, nameof(dataGrid.Sorting), OnDataGridSorting); } else { WeakEventManager.RemoveHandler(dataGrid, nameof(dataGrid.Sorting), OnDataGridSorting); } } private static void OnDataGridSorting(object sender, DataGridSortingEventArgs e) { var column = e.Column; var columnComparer = (ColumnComparer)column.GetValue(ColumnComparerProperty); var dataGrid = (DataGrid)sender; var view = CollectionViewSource.GetDefaultView(dataGrid.ItemsSource) as ListCollectionView; if (view == null) { return; } if (columnComparer == null) { view.CustomSort = (IComparer)dataGrid.GetValue(PreviousComparerProperty); } else { if (!(view.CustomSort is ColumnComparer)) { dataGrid.SetValue(PreviousComparerProperty, view.CustomSort); } switch (column.SortDirection) { case ListSortDirection.Ascending: column.SortDirection = ListSortDirection.Descending; view.CustomSort = columnComparer.Descending; break; case null: case ListSortDirection.Descending: column.SortDirection = ListSortDirection.Ascending; view.CustomSort = columnComparer.Ascending; break; default: throw new ArgumentOutOfRangeException(); } e.Handled = true; } } private class ColumnComparer : IComparer { private readonly IComparer valueComparer; private readonly DataGridColumn column; private readonly InvertedComparer inverted; public ColumnComparer(IComparer valueComparer, DataGridColumn column) { this.valueComparer = valueComparer; this.column = column; inverted = new InvertedComparer(this); } public IComparer Ascending => this; public IComparer Descending => inverted; int IComparer.Compare(object x, object y) { if (x == y) { return 0; } if (x == null) { return -1; } if (y == null) { return 1; } // this can perhaps be a bit slow // Not adding caching yet. var xProp = x.GetType().GetProperty(column.SortMemberPath); var xValue = xProp.GetValue(x); var yProp = x.GetType().GetProperty(column.SortMemberPath); var yValue = yProp.GetValue(y); return valueComparer.Compare(xValue, yValue); } private class InvertedComparer : IComparer { private readonly IComparer comparer; public InvertedComparer(IComparer comparer) { this.comparer = comparer; } public int Compare(object x, object y) { return comparer.Compare(y, x); } } } } 

Uso:

       

Lo hice anulando el evento OnSorting e implementándolo yo mismo.

http://msdn.microsoft.com/en-us/library/system.windows.controls.datagrid.onsorting.aspx

Lo que básicamente significaba re clasificar el ListCollectionView.

Lo siento, no es una respuesta demasiado profunda.

Cambié la respuesta de @ trilson86 para que solo necesites una clase de clasificador personalizada para toda DataGrid.

Primero la interfaz:

 public interface ICustomSorter : IComparer { ListSortDirection SortDirection { get; set; } string SortMemberPath { get; set; } } 

A continuación, la clase Bevaviour, que define CustomSorterProperty de tal manera que puede usarlo directamente en DataGrid, no en DateGridRow. En HandleCustomSorting (), la propiedad SortMemberPath del CustomSorter está llena con el valor real de la columna clicada, puede usar este valor en su CustomSorter para ordenar en la columna deseada.

 public class CustomSortBehaviour { #region Fields and Constants public static readonly DependencyProperty CustomSorterProperty = DependencyProperty.RegisterAttached("CustomSorter", typeof (ICustomSorter), typeof (CustomSortBehaviour)); public static readonly DependencyProperty AllowCustomSortProperty = DependencyProperty.RegisterAttached("AllowCustomSort", typeof (bool), typeof (CustomSortBehaviour), new UIPropertyMetadata(false, OnAllowCustomSortChanged)); #endregion #region public Methods public static bool GetAllowCustomSort(DataGrid grid) { return (bool) grid.GetValue(AllowCustomSortProperty); } public static ICustomSorter GetCustomSorter(DataGrid grid) { return (ICustomSorter)grid.GetValue(CustomSorterProperty); } public static void SetAllowCustomSort(DataGrid grid, bool value) { grid.SetValue(AllowCustomSortProperty, value); } public static void SetCustomSorter(DataGrid grid, ICustomSorter value) { grid.SetValue(CustomSorterProperty, value); } #endregion #region nonpublic Methods private static void HandleCustomSorting(object sender, DataGridSortingEventArgs e) { var dataGrid = sender as DataGrid; if (dataGrid == null || !GetAllowCustomSort(dataGrid)) { return; } var listColView = dataGrid.ItemsSource as ListCollectionView; if (listColView == null) { throw new Exception("The DataGrid's ItemsSource property must be of type, ListCollectionView"); } // Sanity check var sorter = GetCustomSorter(dataGrid); if (sorter == null) { return; } // The guts. e.Handled = true; var direction = (e.Column.SortDirection != ListSortDirection.Ascending) ? ListSortDirection.Ascending : ListSortDirection.Descending; e.Column.SortDirection = sorter.SortDirection = direction; sorter.SortMemberPath = e.Column.SortMemberPath; listColView.CustomSort = sorter; } private static void OnAllowCustomSortChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var existing = d as DataGrid; if (existing == null) { return; } var oldAllow = (bool) e.OldValue; var newAllow = (bool) e.NewValue; if (!oldAllow && newAllow) { existing.Sorting += HandleCustomSorting; } else { existing.Sorting -= HandleCustomSorting; } } #endregion } 

Puedes usarlo en XAML así:

               

aquí hay algo de extensión para @ trilson86 ICustomeSorter

que hacen que el Clasificador sea más genérico para usar

NumericComparer basado en este recurso

http://www.codeproject.com/Articles/11016/Numeric-String-Sort-in-C

 public class GenericNumericComparer : ICustomSorter { private PropertyInfo _propertyInfo; private Type _objectType; public string SortMemberPath { get; set; } private readonly NumericComparer _comparer = new NumericComparer(); public Type ObjectType { get { return _objectType; } set { _objectType = value; if (_objectType != null) _propertyInfo = ObjectType.GetProperty(SortMemberPath); } } private int CompareHelper(object x, object y) { if (_propertyInfo != null) { var value1 = _propertyInfo.GetValue(x); var value2 = _propertyInfo.GetValue(y); return _comparer.Compare(value1, value2); } return 0; } public int Compare(object x, object y) { var i = CompareHelper(x, y); if (SortDirection == ListSortDirection.Ascending) return i; return i*-1; } public ListSortDirection SortDirection { get; set; } } 

Esta respuesta es muy similar a la solución de trilson86, se basó en ella, pero representa SortMemberPath de manera tal que los valores pasados ​​a su comparador son los valores reales de la columna, en lugar de las filas. Esto facilita una reutilización mucho mayor en sus clasificadores. Además, elimina la necesidad de una interfaz de clasificación personalizada por completo.

DataGridSortBehavior.cs

 using System; using System.Collections; using System.ComponentModel; using System.Reflection; using System.Windows; using System.Windows.Controls; using System.Windows.Data; namespace YourNamespace { public class DataGridSortBehavior { public static IComparer GetSorter(DataGridColumn column) { return (IComparer)column.GetValue(SorterProperty); } public static void SetSorter(DataGridColumn column, IComparer value) { column.SetValue(SorterProperty, value); } public static bool GetAllowCustomSort(DataGrid grid) { return (bool)grid.GetValue(AllowCustomSortProperty); } public static void SetAllowCustomSort(DataGrid grid, bool value) { grid.SetValue(AllowCustomSortProperty, value); } public static readonly DependencyProperty SorterProperty = DependencyProperty.RegisterAttached("Sorter", typeof(IComparer), typeof(DataGridSortBehavior)); public static readonly DependencyProperty AllowCustomSortProperty = DependencyProperty.RegisterAttached("AllowCustomSort", typeof(bool), typeof(DataGridSortBehavior), new UIPropertyMetadata(false, OnAllowCustomSortChanged)); private static void OnAllowCustomSortChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { var grid = (DataGrid)obj; bool oldAllow = (bool)e.OldValue; bool newAllow = (bool)e.NewValue; if (!oldAllow && newAllow) { grid.Sorting += HandleCustomSorting; } else { grid.Sorting -= HandleCustomSorting; } } public static bool ApplySort(DataGrid grid, DataGridColumn column) { IComparer sorter = GetSorter(column); if (sorter == null) { return false; } var listCollectionView = CollectionViewSource.GetDefaultView(grid.ItemsSource) as ListCollectionView; if (listCollectionView == null) { throw new Exception("The ICollectionView associated with the DataGrid must be of type, ListCollectionView"); } listCollectionView.CustomSort = new DataGridSortComparer(sorter, column.SortDirection ?? ListSortDirection.Ascending, column.SortMemberPath); return true; } private static void HandleCustomSorting(object sender, DataGridSortingEventArgs e) { IComparer sorter = GetSorter(e.Column); if (sorter == null) { return; } var grid = (DataGrid)sender; e.Column.SortDirection = e.Column.SortDirection == ListSortDirection.Ascending ? ListSortDirection.Descending : ListSortDirection.Ascending; if (ApplySort(grid, e.Column)) { e.Handled = true; } } private class DataGridSortComparer : IComparer { private IComparer comparer; private ListSortDirection sortDirection; private string propertyName; private PropertyInfo property; public DataGridSortComparer(IComparer comparer, ListSortDirection sortDirection, string propertyName) { this.comparer = comparer; this.sortDirection = sortDirection; this.propertyName = propertyName; } public int Compare(object x, object y) { PropertyInfo property = this.property ?? (this.property = x.GetType().GetProperty(propertyName)); object value1 = property.GetValue(x); object value2 = property.GetValue(y); int result = comparer.Compare(value1, value2); if (sortDirection == ListSortDirection.Descending) { result = -result; } return result; } } } } 

Tu Xaml

Esto también debería ser similar a la solución de trilson86:

         

DataGrid CustomSorting

       

Admite propiedades anidadas

puede usar esto en caso de que agregue las columnas de forma programática.

 dg_show.Items.SortDescriptions.Add(new System.ComponentModel.SortDescription("val1", System.ComponentModel.ListSortDirection.Descending)); 

“val1” aquí es la ruta de enlace para la columna que agregamos, y también puede usar otra línea como segundo orden. como éste .

  dg_show.Items.SortDescriptions.Add(new System.ComponentModel.SortDescription("val2", System.ComponentModel.ListSortDirection.Ascending));