每列的WPF DataGrid CustomSort

我有一个绑定到CollectionViewSource的WPF DataGrid,它封装了一个ObservableCollection。 此CollectionViewSource有两个主要目标:

1)按T的特定属性对每个项目进行分组。我在GroupDescription中使用ValueConverter来获取我想要的分组行为。

2)通过a)主要对组名称(如上定义)和b)各个组项目对网格进行排序。 我通过将自定义IComparer附加到CollectionViewSource的’CustomSort’属性来实现此目的。

这在大多数情况下都很有用,但是只要单击列标题,就会覆盖排序逻辑。 我不想禁用排序,但是我想知道是否可以为特定列分配自定义排序顺序?

为了使事情更清楚,假设用户单击“ColumnA” – 此时,我的CustomSorter封装的排序逻辑被覆盖,DataGrid现在按该属性排序。 我宁愿颠倒CustomSorter的逻辑,而不是按所选属性进行排序。

我创建了几个附加属性来处理这个问题。 我希望这对某人派上用场!

首先 – 定向比较器的简单接口。 这扩展了IComparer,但又为我们提供了一个属性(SortDirection)。 您的实现应该使用它来确定元素的正确排序(否则会丢失)。

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

接下来是附加行为 – 这做了两件事:1)告诉网格使用自定义排序逻辑(AllowCustomSort = true)和b)使我们能够在每列级别设置此逻辑。

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

要使用它,请在XAML中实现ICustomComparer(带无参数构造函数):

          

trilson86给出的答案非常好。 但是,两个DependencyProperty声明中的第三个参数不正确。 而不是DataGrid和DataGridColumn,它们应该是CustomSortBehaviour,因此:

 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 

我一直收到一条警告,说已经注册了AllowCustomSort属性。 一点点研究让我得到了答案。

无论如何,这是一个很好的答案,谢谢。

这是一种方式:

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

用法:

       

我通过覆盖OnSorting事件并自己实现它来做到这一点。

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

这基本上意味着重新排序ListCollectionView。

对不起它的答案不太深入。

我改变了@ trilson86的答案,因此整个DataGrid只需要一个自定义排序器类。

首先是界面:

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

接下来是Bevaviour类,它以这样的方式定义CustomSorterProperty,您可以直接在DataGrid上使用它,而不是在DateGridRow上。 在HandleCustomSorting()中,CustomSorter的属性SortMemberPath填充了单击列中的实际值,您可以在CustomSorter中使用此值对所需列进行排序。

 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 } 

您可以在XAML中使用它,如下所示:

               

这个答案与trilson86的解决方案非常相似 – 它基于它 – 但它以一种方式考虑SortMemberPath ,使得传递给比较器的值是列的实际值,而不是行。 这有助于更好地重复使用分拣机。 此外,它完全不需要自定义排序界面。

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

你的Xaml

这看起来应该与trilson86的解决方案类似:

         

这里是对@ trilson86 ICustomeSorter的一些延伸

使分拣机更通用

NumericComparer基于此资源

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

DataGrid CustomSorting

       

支持嵌套属性

如果以编程方式添加列,则可以使用它。

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

“val1”这里是我们添加的列的绑定路径,您也可以使用另一行作为第二种排序。 像这个 。

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