如何按显示的转换值,而不是绑定的源属性值对DataGridTextColumn进行排序?

时间:2016-10-11 18:21:18

标签: wpf mvvm data-binding datagrid wpfdatagrid

如何按显示的,转换后的值,而不是绑定的源属性值对WPF DataGridTextColumn进行排序?现在它在行视图模型中按整数值排序,而不是由Converter返回的显示文本。我使用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的意义不大。

3 个答案:

答案 0 :(得分:4)

听起来你想要查看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

不幸的是,此方法仅适用于viewmodel值,如果您使用转换器,则您希望将这些值公开为viewmodel属性,然后将它们提供给SortDescriptions。据我所知,即使在用户排序模式下,DataGrid也不支持这种情况。

如果您想讨论这个问题,请随时放入我们的#wpf room并抓住任何房主,如果我不能帮助他们,他们将能够提供帮助已经。

答案 1 :(得分:4)

不幸的是,这不是一项微不足道的任务。正如@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中执行此操作。

答案 2 :(得分:0)

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

  1. 收听DataGrid的Sorting事件
  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...