如何按显示的、转换的值、未绑定源属性值对DataGridTextColumn进行排序?

问题描述 投票:0回答:3

如何按显示的、转换的值、未绑定的源属性值对 WPF DataGridTextColumn 进行排序?现在它按行视图模型中的整数值排序,而不是显示转换器返回的文本。我使用 MVVM。

这是根据要求提供的示例。然而,这是一般性问题。我可以将 MmsClass.Name 放在代表行的类中。但我需要在任何地方进行适当的排序,而不仅仅是这里。

连续班级:

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 就没多大意义。

wpf mvvm data-binding datagrid
3个回答
6
投票

不幸的是,这不是一项微不足道的任务。正如 @Maverik 正确指出的那样,

DataGrid
对基础数据进行排序,而不是转换器吐出的内容。为此,您需要自己
Sort
。首先创建一个类,其中一个属性用于使用自定义排序器,另一个用于定义要在给定列上使用的排序器:

    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
中执行此操作。


3
投票

听起来您想研究一下 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"};

结果

这会自动生成:

Sorted Data Screenshot

不幸的是,此方法仅适用于视图模型值,如果您使用转换器,您可能希望将这些值公开为视图模型属性,然后将它们输入到 SortDescriptions 中。据我所知,即使在用户排序模式下,DataGrid 也不支持这种情况。

如果您想讨论这个问题,请随时来到我们的#wpf 房间,并联系任何房间主人,如果我不在的话,他们将能够提供帮助。


0
投票

超级简单的工作解决方案:

  1. 聆听 DataGrid 的排序事件
  2. 当事件被触发时,验证该列
  3. 按照您喜欢的方式对列表进行排序
  4. 将排序后的新集合放回DataGrid的ItemsSource
  5. 已处理标记事件,因此不会应用基本排序

示例:

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...
© www.soinside.com 2019 - 2024. All rights reserved.