tri*_*n86 14 .net c# wpf datagrid collectionviewsource
我有一个绑定到CollectionViewSource的WPF DataGrid,它封装了一个ObservableCollection.此CollectionViewSource有两个主要目标:
1)按T的特定属性对每个项目进行分组.我在GroupDescription中使用ValueConverter来获取我想要的分组行为.
2)通过a)主要对组名称(如上定义)和b)各个组项目对网格进行排序.我通过将自定义IComparer附加到CollectionViewSource的'CustomSort'属性来实现此目的.
这在大多数情况下都很有用,但是只要单击列标题,就会覆盖排序逻辑.我不想禁用排序,但是我想知道是否可以为特定列分配自定义排序顺序?
为了使事情更加清晰,假设用户单击"ColumnA" - 此时,我的CustomSorter封装的排序逻辑被覆盖,DataGrid现在按该属性排序.我宁愿颠倒CustomSorter的逻辑,而不是按所选属性进行排序.
tri*_*n86 27
我创建了几个附加属性来处理这个问题.我希望这对某人派上用场!
首先 - 定向比较器的简单接口.这扩展了IComparer,但又为我们提供了一个属性(SortDirection).您的实现应该使用它来确定元素的正确排序(否则会丢失).
public interface ICustomSorter : IComparer
{
ListSortDirection SortDirection { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
接下来是附加行为 - 这做了两件事: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;
}
}
Run Code Online (Sandbox Code Playgroud)
要使用它,请在XAML中实现ICustomComparer(带无参数构造函数):
<UserControl.Resources>
<converters:MyComparer x:Key="MyComparer"/>
<!-- add more if you need them -->
</UserControl.Resources>
<DataGrid behaviours:CustomSortBehaviour.AllowCustomSort="True" ItemsSource="{Binding MyListCollectionView}">
<DataGrid.Columns>
<DataGridTextColumn Header="Test" Binding="{Binding MyValue}" behaviours:CustomSortBehaviour.CustomSorter="{StaticResource MyComparer}" />
</DataGrid.Columns>
</DataGrid>
Run Code Online (Sandbox Code Playgroud)
这个答案与 trilson86 的解决方案非常相似——它是基于它的——但它的解释SortMemberPath方式是传递给比较器的值是列的实际值,而不是行的值。这有利于您的分拣机更好地重复使用。此外,它完全消除了对自定义排序界面的需要。
DataGridSortBehavior.csusing 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;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
这看起来也应该类似于 trilson86 的解决方案:
<UserControl.Resources>
<converters:MyComparer x:Key="MyComparer"/>
</UserControl.Resources>
<DataGrid behaviours:DataGridSortBehavior.AllowCustomSort="True" ItemsSource="{Binding MyListCollectionView}">
<DataGrid.Columns>
<DataGridTextColumn Header="Test" Binding="{Binding MyValue}" behaviours:DataGridSortBehavior.Sorter="{StaticResource MyComparer}" />
</DataGrid.Columns>
</DataGrid>
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
我一直收到一条警告,指出 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<DataGrid, DataGridSortingEventArgs>.AddHandler(dataGrid, nameof(dataGrid.Sorting), OnDataGridSorting);
}
else
{
WeakEventManager<DataGrid, DataGridSortingEventArgs>.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);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
<DataGrid AutoGenerateColumns="False"
ItemsSource="{Binding DataItems}"
local:DataGridSort.UseCustomSort="True">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Key}"
Header="Key"
local:DataGridSort.Comparer="{x:Static local:StringLengthComparer.Default}" />
<DataGridTextColumn Binding="{Binding Value}" Header="Value" />
</DataGrid.Columns>
</DataGrid>
Run Code Online (Sandbox Code Playgroud)