4
votes

I have a WPF DataGrid with sortable columns. I do not want to pre-sort the grid on any particular column. I just want the default sort direction when a user first clicks on a column header to be descending rather than ascending.

Neither SortDescription.Direction on a CollectionViewSource nor the SortDirection property of by DataGridTextColumns influence the default sort direction when changing the sort column. It always chooses ascending on first click of a column header.

99% of the time it needs to be descending and switching columns in the user workflow is frequent, so this is adding a lot of unnecessary clicks. I would highly prefer a XAML solution if there is one, but will resort to code trickery on events if necessary.

1

1 Answers

5
votes

It seems you cannot do it without minor intervention into sorting handlers, because default sorting done by DataGrid starts like this:

ListSortDirection direction = ListSortDirection.Ascending;
ListSortDirection? sortDirection = column.SortDirection;
if (sortDirection.HasValue && sortDirection.Value == ListSortDirection.Ascending)
    direction = ListSortDirection.Descending;

So only if column was sorted before, and that sort was Ascending - it will flip it to Descending. However with tiny hack you can achieve what you want. First subscribe to DataGrid.Sorting event, and there:

private void OnSorting(object sender, DataGridSortingEventArgs e) {
    if (e.Column.SortDirection == null)
         e.Column.SortDirection = ListSortDirection.Ascending;
    e.Handled = false;
}

So basically if there was no sort yet - you switch it to Ascending and pass it to default sorting of DataGrid (by setting e.Handled to false). At the beginning of sorting it will flip that to Descending for you, which is what you want.

You can do that in xaml with a help of attached property, like this:

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);
    }
}

Then in your 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>

So basically you mark DataGrid itself with SortDesc=true, to subscribe to sorting event, and then you mark only that columns you need to be sorted desc. You can also bind SortDesc to your model if logic to determine this is there.