How to Reset DataGrid Sorting in WPF

Practical ways to reset WPF DataGrid sorting, including explicit clearing, Sorting-event control, CollectionView handling, and a reusable Behavior.

WPF DataGrid provides built-in sorting, but some business workflows require an explicit operation to restore the initial unsorted state.
This article organizes practical reset strategies for single-column and multi-column sorting scenarios.

Overview

This article covers the following approaches for resetting WPF DataGrid sorting.

Prerequisites / Environment

Problem

In WPF DataGrid, production requirements often include a command that resets current sorting back to the initial state.
Default user interaction alone may not provide a consistent reset timing across screens.

Cause / Background

In WPF DataGrid, Shift + column-header click is a default operation for adding multi-column sorting.
It is not a built-in shortcut to clear sorting.

For this reason, explicit reset logic is required when an application needs deterministic unsorted behavior.

Solution

The reset strategy should be selected by architecture and UX requirements.

Implementation

Explicitly clear sorting in code

The most direct implementation clears both data-level sort descriptors and visual sort arrows.

using System.Windows.Controls;

public static class DataGridSortHelper
{
    public static void ClearDataGridSort(DataGrid dataGrid)
    {
        if (dataGrid == null) return;

        // Clear data-level sort descriptors.
        dataGrid.Items.SortDescriptions.Clear();

        // Clear header arrows.
        foreach (var column in dataGrid.Columns)
        {
            column.SortDirection = null;
        }

        // Refresh the view.
        dataGrid.Items.Refresh();
    }
}

This approach is effective for full reset commands and keeps UI indicators synchronized with actual sort state.

Auto-reset on third click with custom sorting behavior

To implement Ascending -> Descending -> Unsorted, intercept the Sorting event at the transition after descending.
The three states appear as follows.

Three DataGrid controls side by side. The left one shows an ascending arrow on the Name column with rows in name order, the middle one a descending arrow with the reverse order, and the right one has no arrow and shows the original data order.
The three states rendered over the same data. In the unsorted state on the right, both the column's SortDescription and the header arrow are removed. Because this example sorts on a single column, clearing it returns the rows to the order of the underlying collection; when several columns are sorted, the remaining descriptors still apply and the original order is not necessarily restored.

XAML

<DataGrid x:Name="MyDataGrid"
          Sorting="DataGrid_Sorting" />

This event hook allows custom handling before the default sorting pipeline completes.
When columns such as DataGridTemplateColumn are used, SortMemberPath should be set explicitly for columns that need sort-reset handling.

C#

using System.ComponentModel;
using System.Linq;
using System.Windows.Controls;

private void DataGrid_Sorting(object sender, DataGridSortingEventArgs e)
{
    if (sender is not DataGrid dataGrid) return;

    if (e.Column.SortDirection == ListSortDirection.Descending)
    {
        e.Handled = true; // Cancel default sorting behavior.

        // Remove only the sort descriptor for the target column.
        var target = dataGrid.Items.SortDescriptions
            .FirstOrDefault(sd => sd.PropertyName == e.Column.SortMemberPath);

        if (!string.IsNullOrEmpty(target.PropertyName))
        {
            dataGrid.Items.SortDescriptions.Remove(target);
        }

        e.Column.SortDirection = null;
        dataGrid.Items.Refresh();
    }
}

This logic removes only the clicked column from sorting, so existing sort conditions on other columns can remain intact.

Reset from ViewModel using ICollectionView

In MVVM, sorting should generally be controlled in the view model layer instead of manipulating DataGrid directly.

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Data;

public class SampleViewModel
{
    public ObservableCollection<RowItem> Items { get; } = new();
    public ICollectionView ItemsView { get; }

    public SampleViewModel()
    {
        ItemsView = CollectionViewSource.GetDefaultView(Items);
    }

    public void ClearSort()
    {
        ItemsView.SortDescriptions.Clear();
        ItemsView.Refresh();
    }
}

public class RowItem
{
    public string Name { get; set; } = "";
    public int Value { get; set; }
}

Managing SortDescriptions in ItemsView improves testability and keeps view logic thin.

<DataGrid ItemsSource="{Binding ItemsView}" />

With this binding, sorting reset responsibility remains in ViewModel commands.

Build a reusable Behavior class

When the same tri-state rule is required in multiple screens, encapsulate the logic as a Behavior (Microsoft.Xaml.Behaviors.Wpf).

using Microsoft.Xaml.Behaviors;
using System.ComponentModel;
using System.Linq;
using System.Windows.Controls;

public class TriStateSortBehavior : Behavior<DataGrid>
{
    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.Sorting += OnSorting;
    }

    protected override void OnDetaching()
    {
        AssociatedObject.Sorting -= OnSorting;
        base.OnDetaching();
    }

    private void OnSorting(object sender, DataGridSortingEventArgs e)
    {
        if (sender is not DataGrid grid) return;

        if (e.Column.SortDirection == ListSortDirection.Descending)
        {
            e.Handled = true;

            var sd = grid.Items.SortDescriptions
                .FirstOrDefault(x => x.PropertyName == e.Column.SortMemberPath);

            if (!string.IsNullOrEmpty(sd.PropertyName))
            {
                grid.Items.SortDescriptions.Remove(sd);
            }

            e.Column.SortDirection = null;
            grid.Items.Refresh();
        }
    }
}

This design reduces duplicated event handlers and centralizes behavior-level customization.

<Window
    xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
    xmlns:local="clr-namespace:YourApp.Behaviors">
    <DataGrid>
        <i:Interaction.Behaviors>
            <local:TriStateSortBehavior />
        </i:Interaction.Behaviors>
    </DataGrid>
</Window>

XAML usage remains compact, which helps apply identical sorting rules consistently across screens.

Notes

Alternatives / Comparison

Method Advantages Disadvantages Best fit
Explicit clear (SortDescriptions.Clear + SortDirection = null) Simple implementation and fast adoption. Requires direct DataGrid reference and lowers MVVM purity. Screen-level commands that always perform full reset.
Tri-state with Sorting event Provides consistent Ascending -> Descending -> Unsorted UX. Event logic can become complex with column-specific requirements. Header-driven interaction where reset should be available by click sequence.
ICollectionView in ViewModel Better testability and minimal UI dependency. Requires clear view-model responsibility boundaries. Strict MVVM implementations with command-based reset.
Behavior-based reuse Easy to roll out across multiple screens and reduces duplicate code. Needs extension design for per-screen differences. Shared sorting policy across many DataGrid instances.

Summary

WPF DataGrid sorting reset should be selected by requirement scope and architecture.

For simple requirements, a helper method is sufficient.
For shared screen behavior, Behavior-based design is more maintainable.
For strict MVVM, ICollectionView-centric control is the most practical option.