René Vogt
René Vogt

Reputation: 43876

Remember WPF DataGrid sort order

This is a continuation of this question.

1. Setup

I have a wpf window that contains a TabControl with dynamically created items. The ItemSource of this TabControl is bound to a list of Groups. These groups contain a list of elements that is displayed in a DataGrid on the TabPage.

XAML:

<Window x:Name="window" x:Class="TestWpf.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:vm="clr-namespace:TestWpf"
    Title="MainWindow" Height="350" Width="525">
    <TabControl x:Name="tabControl" BorderThickness="0" ItemsSource ="{Binding Groups, ElementName=window, NotifyOnSourceUpdated=True}">
        <TabControl.ItemTemplate>
            <DataTemplate DataType="{x:Type vm:Group}">
                <TextBlock Padding="2" Text="{Binding Name}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
            </DataTemplate>
        </TabControl.ItemTemplate>
        <TabControl.ContentTemplate>
            <DataTemplate x:Name="contentTemplate" DataType="{x:Type vm:Group}">
                <DataGrid x:Name="dgElements" ItemsSource="{Binding Elements, BindsDirectlyToSource=True}" DockPanel.Dock="Top" AutoGenerateColumns="False" >
                    <DataGrid.Columns>
                        <DataGridTextColumn x:Name="clmName" Header="Name" Binding="{Binding Name}" IsReadOnly="True" CanUserReorder="False" />
                    <DataGridTextColumn x:Name="clmDesc" Header="Description" Binding="{Binding Description}" IsReadOnly="True" CanUserReorder="False" />
                    </DataGrid.Columns>
                </DataGrid>
            </DataTemplate>
        </TabControl.ContentTemplate>
    </TabControl>
</Window>

This all works fine.

2. The Question

The DataGrid allows multi-column sort out of the box. But unfortunatly, if I switch between the tabpages, the sorting is lost.
I want that the multi-column sorting is maintained for each tabpage/datagrid.

It is possible to bind the SortDirection of DataGridTextColumn to a property, but that does not remember the sort order of the columns. And debugging I found that when I switch the tabpage, the SortDirection is reset before the new ItemSource is set, so I can't store this.

The question is: How can I keep the multi-column sort settings per tabpage/datagrid?

Upvotes: 3

Views: 4211

Answers (1)

Ren&#233; Vogt
Ren&#233; Vogt

Reputation: 43876

After reading a lot of articles only almost solving my problem, I finally found a solution.
I inherited my own class from DataGrid and stored the SortDescriptions per ItemSource in a Dictionary:

public class SortKeepingDataGrid : DataGrid
{
    // Dictionary to keep SortDescriptions per ItemSource
    private readonly Dictionary<object, List<SortDescription>> m_SortDescriptions =
        new Dictionary<object, List<SortDescription>>();

    protected override void OnSorting(DataGridSortingEventArgs eventArgs)
    {
        base.OnSorting(eventArgs);
        UpdateSorting();
    }
    protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
    {
        base.OnItemsSourceChanged(oldValue, newValue);

        ICollectionView view = CollectionViewSource.GetDefaultView(newValue);
        view.SortDescriptions.Clear();

        // reset SortDescriptions for new ItemSource
        if (m_SortDescriptions.ContainsKey(newValue))
            foreach (SortDescription sortDescription in m_SortDescriptions[newValue])
            {
                view.SortDescriptions.Add(sortDescription);

                // I need to tell the column its SortDirection,
                // otherwise it doesn't draw the triangle adornment
                DataGridColumn column = Columns.FirstOrDefault(c => c.SortMemberPath == sortDescription.PropertyName);
                if (column != null)
                    column.SortDirection = sortDescription.Direction;
            }
    }

    // Store SortDescriptions in dictionary
    private void UpdateSorting()
    {       
        ICollectionView view = CollectionViewSource.GetDefaultView(ItemsSource);
        m_SortDescriptions[ItemsSource] = new List<SortDescription>(view.SortDescriptions);
    }
}

So, basically, whenever the user changes the sorting, I call UpdateSorting and store the current SortDescriptions in the per ItemSource dictionary.
When the ItemSource has changed, I look up the SortDescriptions and reset them in the correct order.

The tricky part is finding the correct DataGridColumn to set its SortDirection. This is necessary to draw the triangled adornment. I here rely on the equality of SortMemberPath and PropertyName. Eventually a more generic approach could be necessary.

In the XAML I replaced the DataGrid with my SortKeepingDataGrid and now the sorting is stored per tabpage.

Since I could not find any other solution, maybe this will help others too.

Upvotes: 12

Related Questions