如何为WPF DataGrid列设置默认的降序排序方向?

7
我有一个带有可排序列的WPF DataGrid。我不想预先按任何特定列对网格进行排序。当用户首次单击列标题时,我只希望默认排序方向为降序而不是升序。
无论是CollectionViewSource的SortDescription.Direction还是DataGridTextColumns的SortDirection属性都不影响更改排序列时的默认排序方向。它总是选择第一次单击列标题时的升序。
99%的时间需要降序,而在用户工作流程中频繁切换列,因此这会增加很多不必要的单击。如果有XAML解决方案,我非常希望使用它,但如果必要,也可以采用代码技巧。
1个回答

9
看起来您不能不对排序处理程序进行轻微干预就完成它,因为DataGrid默认的排序是这样开始的:
ListSortDirection direction = ListSortDirection.Ascending;
ListSortDirection? sortDirection = column.SortDirection;
if (sortDirection.HasValue && sortDirection.Value == ListSortDirection.Ascending)
    direction = ListSortDirection.Descending;

只有当列在之前排序过且该排序是升序的情况下,它才会将其翻转为降序。但是通过小小的技巧,你可以实现你想要的效果。首先订阅DataGrid.Sorting事件,在那里:
private void OnSorting(object sender, DataGridSortingEventArgs e) {
    if (e.Column.SortDirection == null)
         e.Column.SortDirection = ListSortDirection.Ascending;
    e.Handled = false;
}

基本上,如果还没有排序 - 您将其切换为 Ascending 并将其传递给 DataGrid 的默认排序(通过将 e.Handled 设置为 false)。在排序开始时,它会为您翻转到 Descending,这正是您想要的。
您可以通过使用附加属性在 XAML 中完成此操作,如下所示:
public static class DataGridExtensions {        
    public static readonly DependencyProperty SortDescProperty = DependencyProperty.RegisterAttached(
        "SortDesc", typeof (bool), typeof (DataGridExtensions), new PropertyMetadata(false, OnSortDescChanged));

    private static void OnSortDescChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
        var grid = d as DataGrid;
        if (grid != null) {
            grid.Sorting += (source, args) => {
                if (args.Column.SortDirection == null) {
                    // here we check an attached property value of target column
                    var sortDesc = (bool) args.Column.GetValue(DataGridExtensions.SortDescProperty);
                    if (sortDesc) {
                        args.Column.SortDirection = ListSortDirection.Ascending;
                    }
                }
            };
        }
    }

    public static void SetSortDesc(DependencyObject element, bool value) {
        element.SetValue(SortDescProperty, value);
    }

    public static bool GetSortDesc(DependencyObject element) {
        return (bool) element.GetValue(SortDescProperty);
    }
}

然后在你的 XAML 中:
<DataGrid x:Name="dg" AutoGenerateColumns="False" ItemsSource="{Binding Items}" local:DataGridExtensions.SortDesc="True">
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Value}"
                            Header="Value"
                            local:DataGridExtensions.SortDesc="True" />
    </DataGrid.Columns>
</DataGrid>

所以基本上,您可以使用SortDesc = true标记DataGrid本身,以订阅排序事件,然后仅标记需要按降序排序的列。如果有逻辑来确定这一点,还可以将SortDesc绑定到您的模型。

我也想到了那个解决方案,它可以正常工作。可惜我不能在 XAML 中指定它,在绑定时很难从排序事件返回到基础绑定数据,以确定是否仅对某些列按此方式工作。 - DannyMeister
@DannyMeister 我已经更新了我的答案,并附上了一个帮助器属性,这将允许您完全在XAML中完成此操作。 - Evk
+1 这是一个很好的答案。两件事情:1)您可能还需要根据 e.NewValue 订阅/取消订阅 Sorting 事件处理程序,这意味着将 lambda 移动到命名的静态方法中。2)为什么在代码中设置 Ascending?难道不应该是 Descending 吗? - Drew Noakes
关于我的第二点,我现在明白了:它会欺骗现有的排序处理代码,让它认为该列已经按升序排列,然后翻转它。不过我并不能百分之百确定这样不会对数据进行双重排序。 - Drew Noakes
@DrewNoakes 你可以尝试在排序处理程序中设置断点,如果它被调用一次 - 我期望它确实已经排序了一次。 - Evk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接