如何按显示的转换值而不是绑定源 属性 值对 DataGridTextColumn 进行排序?
How to sort DataGridTextColumn by displayed, converted value, not bound source property value?
如何按显示、转换的值而不是绑定源 属性 值对 WPF DataGridTextColumn 进行排序?现在它按行视图模型中的整数值排序,而不是显示转换器返回的文本。我使用 MVVM。
这是应要求提供的示例。然而,这是一般性问题。我可以将 MmsClass.Name 放在 class 中代表该行。但我需要在所有地方进行适当的排序,而不仅仅是这里。
Class 一行:
public class MaintenanceDataItem
{
public MaintenanceDataItem(int classId, Type objectType, object value, IEnumerable<MmsData> rows)
{
ClassId = classId;
TypeOfObject = objectType;
Value = value;
ObjectIds = new List<int>();
MmsDataRows = rows;
}
public int ClassId { get; private set; }
// rest of the properrties omitted
}
转换器:
public class MmsClassToNameConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
MmsClass mmsClass;
if (MmsClasses.Instance.TryGetValue((int) value, out mmsClass))
{
return mmsClass.Name;
}
return value.ToString();
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
return value.Equals(true) ? parameter : Binding.DoNothing;
}
}
xaml 中的列:
<DataGridTextColumn Header="{StaticResource ResourceKey=MmsStrCondClass}" Binding="{Binding ClassId, Converter={StaticResource mmsclasstonameconverter}}" Width="*">
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}"
BasedOn="{StaticResource {x:Type TextBlock}}">
<Setter Property="TextWrapping" Value="NoWrap" />
<Setter Property="TextTrimming" Value="CharacterEllipsis"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
我真的以为默认排序会显示值。如果不容易解决,使用转换器对 datagridcolumn 没有太大意义。
听起来你想调查一下 CollectionViewSource.SortDescriptions。
下面是一个示例:
XAML
<Window.Resources>
<CollectionViewSource x:Key="Fruits" Source="{Binding Source={x:Static local:MainWindowViewModel.Fruits}}">
<CollectionViewSource.SortDescriptions>
<ComponentModel:SortDescription Direction="Ascending" PropertyName="Length"/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
</Window.Resources>
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Source={StaticResource Fruits}}">
<DataGrid.Columns>
<DataGridTextColumn Header="Fruit Name" Binding="{Binding}" />
<DataGridTextColumn Header="Name Length" Binding="{Binding Length}" />
</DataGrid.Columns>
</DataGrid>
其中 MainWindowViewModel.Fruits 简单定义为:public static string[] Fruits { get; } = {"Apples", "Bananas", "Grapes", "Oranges", "Kiwis"};
结果
这会自动生成:
不幸的是,此方法仅适用于 viewmodel 值,如果您使用的是转换器,您可能希望将这些值公开为 viewmodel 属性,然后将它们提供给 SortDescriptions。据我所知,即使在用户排序模式下,DataGrid 也不支持这种情况。
如果您想讨论这个问题,请随时加入我们的 #wpf room 并联系任何房主,如果我不在,他们会提供帮助。
不幸的是,这不是一项微不足道的任务。正如@Maverik 正确指出的那样,DataGrid
对基础数据进行排序,而不是转换器吐出的内容。为此,您需要 Sort
自己。首先创建一个 class 和一个 属性 来使用您的自定义排序器,另一个来定义要在给定列上使用的排序器:
public static ICustomSorter GetCustomSorter(DependencyObject obj)
{
return (ICustomSorter)obj.GetValue(CustomSorterProperty);
}
public static void SetCustomSorter(DependencyObject obj, ICustomSorter value)
{
obj.SetValue(CustomSorterProperty, value);
}
// Using a DependencyProperty as the backing store for CustomSorter. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CustomSorterProperty =
DependencyProperty.RegisterAttached("CustomSorter", typeof(ICustomSorter), typeof(CustomSortBehavior), new PropertyMetadata(null));
public static bool GetAllowCustomSort(DependencyObject obj)
{
return (bool)obj.GetValue(AllowCustomSortProperty);
}
public static void SetAllowCustomSort(DependencyObject obj, bool value)
{
obj.SetValue(AllowCustomSortProperty, value);
}
// Using a DependencyProperty as the backing store for AllowCustomSort. This enables animation, styling, binding, etc...
public static readonly DependencyProperty AllowCustomSortProperty =
DependencyProperty.RegisterAttached("AllowCustomSort", typeof(bool), typeof(CustomSortBehavior), new PropertyMetadata(false, AllowCustomSortChanged));
ICustomSorter
是一个非常简单的界面:
public interface ICustomSorter : IComparer
{
ListSortDirection SortDirection { get; set; }
string SortMemberPath { get; set; }
}
现在您需要实现自定义排序 "AllowCustomSort":
private static void AllowCustomSortChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DataGrid control = d as DataGrid;
{
var oldAllow = (bool)e.OldValue;
var newAllow = (bool)e.NewValue;
if (!oldAllow && newAllow)
{
control.Sorting += HandleCustomSorting;
}
else
{
control.Sorting -= HandleCustomSorting;
}
}
}
private static void HandleCustomSorting(object sender, DataGridSortingEventArgs e)
{
//Check if we should even be using custom sorting
DataGrid dataGrid = sender as DataGrid;
if (dataGrid != null && GetAllowCustomSort(dataGrid))
{
//Make sure we have a source we can sort
ListCollectionView itemsSource = dataGrid.ItemsSource as ListCollectionView;
if (itemsSource != null)
{
ICustomSorter columnSorter = GetCustomSorter(e.Column);
//Only do our own sort if a sorter was defined
if (columnSorter != null)
{
ListSortDirection nextSortDirection = e.Column.SortDirection == ListSortDirection.Ascending ?
ListSortDirection.Descending :
ListSortDirection.Ascending;
e.Column.SortDirection = columnSorter.SortDirection = nextSortDirection;
columnSorter.SortMemberPath = e.Column.SortMemberPath;
itemsSource.CustomSort = columnSorter;
//We've handled the sort, don't let the DataGrid mess with us
e.Handled = true;
}
}
}
}
这只是连接 Sorting
事件,然后通过调用提供的 ICustomSorter
对集合进行排序来处理它。
在您的 XAML 中,您创建了已实现 ICustomSorter
的实例并使用附加属性,如下所示:
<DataGridTextColumn Header="Column1" Binding="{Binding Column1, Converter={StaticResource Column1Converter}}" IsReadOnly="True"
util:CustomSortBehavior.CustomSorter="{StaticResource Column1Comparer}"/>
这很痛苦,您必须对所有转换后的值进行自定义排序,但它确实允许您在 DataGrid
中执行此操作。
超级简单的工作解决方案:
- 监听 DataGrid 的排序事件
- 触发事件时,验证列
- 按您喜欢的方式对列表进行排序
- 将排序后的新集合放回 DataGrid 的 ItemsSource
- 标记事件已处理,因此不会应用基本排序
示例:
cs
/// <summary>
/// catch DataGrid's sorting event and
/// sort AllRows by employee, than by planit order
/// </summary>
private void MainDataGrid_Sorting(object sender, DataGridSortingEventArgs e)
{
if (e.Column.Header is string header && header.Equals(Properties.Resources.EmployeeName))
{
// AllRows is a property binded to my DataGrid's ItemsSource
AllRows = programaItems.ToList().OrderBy(item => item.SelectedWorkOrder.WorkOrderResource.EmployeeName).ThenBy(item => item.SelectedWorkOrder.WorkOrderResource.PlanitSetupOrder);
// flag event handled
e.Handled = true;
}
}
xaml
<DataGrid ... Sorting="MainDataGrid_Sorting">
<DataGrid.Columns...
如何按显示、转换的值而不是绑定源 属性 值对 WPF DataGridTextColumn 进行排序?现在它按行视图模型中的整数值排序,而不是显示转换器返回的文本。我使用 MVVM。
这是应要求提供的示例。然而,这是一般性问题。我可以将 MmsClass.Name 放在 class 中代表该行。但我需要在所有地方进行适当的排序,而不仅仅是这里。
Class 一行:
public class MaintenanceDataItem
{
public MaintenanceDataItem(int classId, Type objectType, object value, IEnumerable<MmsData> rows)
{
ClassId = classId;
TypeOfObject = objectType;
Value = value;
ObjectIds = new List<int>();
MmsDataRows = rows;
}
public int ClassId { get; private set; }
// rest of the properrties omitted
}
转换器:
public class MmsClassToNameConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
MmsClass mmsClass;
if (MmsClasses.Instance.TryGetValue((int) value, out mmsClass))
{
return mmsClass.Name;
}
return value.ToString();
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
return value.Equals(true) ? parameter : Binding.DoNothing;
}
}
xaml 中的列:
<DataGridTextColumn Header="{StaticResource ResourceKey=MmsStrCondClass}" Binding="{Binding ClassId, Converter={StaticResource mmsclasstonameconverter}}" Width="*">
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}"
BasedOn="{StaticResource {x:Type TextBlock}}">
<Setter Property="TextWrapping" Value="NoWrap" />
<Setter Property="TextTrimming" Value="CharacterEllipsis"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
我真的以为默认排序会显示值。如果不容易解决,使用转换器对 datagridcolumn 没有太大意义。
听起来你想调查一下 CollectionViewSource.SortDescriptions。
下面是一个示例:
XAML
<Window.Resources>
<CollectionViewSource x:Key="Fruits" Source="{Binding Source={x:Static local:MainWindowViewModel.Fruits}}">
<CollectionViewSource.SortDescriptions>
<ComponentModel:SortDescription Direction="Ascending" PropertyName="Length"/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
</Window.Resources>
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Source={StaticResource Fruits}}">
<DataGrid.Columns>
<DataGridTextColumn Header="Fruit Name" Binding="{Binding}" />
<DataGridTextColumn Header="Name Length" Binding="{Binding Length}" />
</DataGrid.Columns>
</DataGrid>
其中 MainWindowViewModel.Fruits 简单定义为:public static string[] Fruits { get; } = {"Apples", "Bananas", "Grapes", "Oranges", "Kiwis"};
结果
这会自动生成:
不幸的是,此方法仅适用于 viewmodel 值,如果您使用的是转换器,您可能希望将这些值公开为 viewmodel 属性,然后将它们提供给 SortDescriptions。据我所知,即使在用户排序模式下,DataGrid 也不支持这种情况。
如果您想讨论这个问题,请随时加入我们的 #wpf room 并联系任何房主,如果我不在,他们会提供帮助。
不幸的是,这不是一项微不足道的任务。正如@Maverik 正确指出的那样,DataGrid
对基础数据进行排序,而不是转换器吐出的内容。为此,您需要 Sort
自己。首先创建一个 class 和一个 属性 来使用您的自定义排序器,另一个来定义要在给定列上使用的排序器:
public static ICustomSorter GetCustomSorter(DependencyObject obj)
{
return (ICustomSorter)obj.GetValue(CustomSorterProperty);
}
public static void SetCustomSorter(DependencyObject obj, ICustomSorter value)
{
obj.SetValue(CustomSorterProperty, value);
}
// Using a DependencyProperty as the backing store for CustomSorter. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CustomSorterProperty =
DependencyProperty.RegisterAttached("CustomSorter", typeof(ICustomSorter), typeof(CustomSortBehavior), new PropertyMetadata(null));
public static bool GetAllowCustomSort(DependencyObject obj)
{
return (bool)obj.GetValue(AllowCustomSortProperty);
}
public static void SetAllowCustomSort(DependencyObject obj, bool value)
{
obj.SetValue(AllowCustomSortProperty, value);
}
// Using a DependencyProperty as the backing store for AllowCustomSort. This enables animation, styling, binding, etc...
public static readonly DependencyProperty AllowCustomSortProperty =
DependencyProperty.RegisterAttached("AllowCustomSort", typeof(bool), typeof(CustomSortBehavior), new PropertyMetadata(false, AllowCustomSortChanged));
ICustomSorter
是一个非常简单的界面:
public interface ICustomSorter : IComparer
{
ListSortDirection SortDirection { get; set; }
string SortMemberPath { get; set; }
}
现在您需要实现自定义排序 "AllowCustomSort":
private static void AllowCustomSortChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DataGrid control = d as DataGrid;
{
var oldAllow = (bool)e.OldValue;
var newAllow = (bool)e.NewValue;
if (!oldAllow && newAllow)
{
control.Sorting += HandleCustomSorting;
}
else
{
control.Sorting -= HandleCustomSorting;
}
}
}
private static void HandleCustomSorting(object sender, DataGridSortingEventArgs e)
{
//Check if we should even be using custom sorting
DataGrid dataGrid = sender as DataGrid;
if (dataGrid != null && GetAllowCustomSort(dataGrid))
{
//Make sure we have a source we can sort
ListCollectionView itemsSource = dataGrid.ItemsSource as ListCollectionView;
if (itemsSource != null)
{
ICustomSorter columnSorter = GetCustomSorter(e.Column);
//Only do our own sort if a sorter was defined
if (columnSorter != null)
{
ListSortDirection nextSortDirection = e.Column.SortDirection == ListSortDirection.Ascending ?
ListSortDirection.Descending :
ListSortDirection.Ascending;
e.Column.SortDirection = columnSorter.SortDirection = nextSortDirection;
columnSorter.SortMemberPath = e.Column.SortMemberPath;
itemsSource.CustomSort = columnSorter;
//We've handled the sort, don't let the DataGrid mess with us
e.Handled = true;
}
}
}
}
这只是连接 Sorting
事件,然后通过调用提供的 ICustomSorter
对集合进行排序来处理它。
在您的 XAML 中,您创建了已实现 ICustomSorter
的实例并使用附加属性,如下所示:
<DataGridTextColumn Header="Column1" Binding="{Binding Column1, Converter={StaticResource Column1Converter}}" IsReadOnly="True"
util:CustomSortBehavior.CustomSorter="{StaticResource Column1Comparer}"/>
这很痛苦,您必须对所有转换后的值进行自定义排序,但它确实允许您在 DataGrid
中执行此操作。
超级简单的工作解决方案:
- 监听 DataGrid 的排序事件
- 触发事件时,验证列
- 按您喜欢的方式对列表进行排序
- 将排序后的新集合放回 DataGrid 的 ItemsSource
- 标记事件已处理,因此不会应用基本排序
示例:
cs
/// <summary>
/// catch DataGrid's sorting event and
/// sort AllRows by employee, than by planit order
/// </summary>
private void MainDataGrid_Sorting(object sender, DataGridSortingEventArgs e)
{
if (e.Column.Header is string header && header.Equals(Properties.Resources.EmployeeName))
{
// AllRows is a property binded to my DataGrid's ItemsSource
AllRows = programaItems.ToList().OrderBy(item => item.SelectedWorkOrder.WorkOrderResource.EmployeeName).ThenBy(item => item.SelectedWorkOrder.WorkOrderResource.PlanitSetupOrder);
// flag event handled
e.Handled = true;
}
}
xaml
<DataGrid ... Sorting="MainDataGrid_Sorting">
<DataGrid.Columns...