【WPF】ボタンクリックでDataGridのソートを初期化する


概要

DataGridの列ヘッダークリックでかけたソートを解除してデフォルトのソートにしたい。
並び替え操作が多く、何回かソートを繰り返した後、初期のソートに戻したいけど、
ユーザーに覚えていてもらって、ヘッダーを複数回クリックしてもらうわけにもいかない。

ソートをクリアするボタンを設置してそれをクリックすると初期化するようにする。

方法

  1. View側にDataGridに表示させたいViewModel側プロパティを中継するCollectionViewSourceを追加する
  2. DataGridのDataContextに1.のCollectionViewSourceを設定し、そのままItemSourceにBindingする
  3. ソートをクリアするボタンを設置してクリックイベントでDataGridのDataContextをnullにしてからCollectionViewSourceを再設定する

サンプルコード

Model

Employee.cs

    public class Employee
    {
        public int ID { get; set; }

        public string Name { get; set; }

        public string CompanyName { get; set; }

        public string ProjectName { get; set; }
    }

ViewModel

MainWindowViewModel.cs
using System.Collections.ObjectModel;
using System.Collections.Generic;

    public class MainWindowViewModel
    {

        public ObservableCollection<Employee> Employees { get; set; }

        public MainWindowViewModel()
        {
            this.Employees = new ObservableCollection<Employee>(
                new List<Employee>
                {
                    new Employee(){ID=1,Name="涼風青葉",CompanyName="EagleJump",ProjectName="PECO"},
                    new Employee(){ID=2,Name="八神コウ",CompanyName="EagleJump",ProjectName="FAIRIES STORY"},
                    new Employee(){ID=3,Name="社畜ちゃん",CompanyName="ブランクソフトウェア",ProjectName="*"}
                }
            );
        }
    }

View

初期化ボタンクリックでID順に並び替える例

xaml

MainWindow.xaml

<!-- WindowにComponentModelの参照の追加が必要-->
<Window x:Class="DataGridSortInitialization.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:ComponentModel="clr-namespace:System.ComponentModel;assembly=Windowsbase"
        xmlns:local="clr-namespace:DataGridSortInitialization"
        mc:Ignorable="d"
        Title="MainWindow" Height="370.161" Width="498.79">
    <Window.Resources>
        <CollectionViewSource x:Key="EmployeesViewSource"  Source="{Binding Employees}" >
            <CollectionViewSource.SortDescriptions>
                <!--デフォルトのソート-->
                <ComponentModel:SortDescription PropertyName="ID" Direction="Ascending" />
            </CollectionViewSource.SortDescriptions>
        </CollectionViewSource>
    </Window.Resources>
    <Grid>
        <DataGrid x:Name="EmployeesDataGrid" 
                  DataContext="{Binding Source={StaticResource EmployeesViewSource}}" 
                  ItemsSource="{Binding}" 
                  IsReadOnly="True"
                  CanUserAddRows="False"
                  CanUserDeleteRows="False"
                  Height="263" VerticalAlignment="Top" Margin="0,38,0,0">
        </DataGrid>
        <Button x:Name="ClearSortButton" Content="ソートの初期化" HorizontalAlignment="Left" VerticalAlignment="Top" 
                Width="112" Margin="351,6,0,0" Click="ClearSortButton_Click"/>
    </Grid>
</Window>

  • CollectionViewSourceのSortDerectionに定義することでデフォルトのソートを細かく設定できます。

コードビハインド

MainWindow.xaml.cs
using System.Linq;
using System.Windows;
using System.Windows.Data;

    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        private readonly string _viewSourceKey = "EmployeesViewSource";

        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = new MainWindowViewModel();
        }

        private void ClearSortButton_Click(object sender, RoutedEventArgs e)
        {
            // 各ColumnHeaderのSortDirectionをクリアする
            this.EmployeesDataGrid.Columns.ToList().ForEach(c => c.SortDirection = null);

            // DataGridのDataContextを再設定する
            this.EmployeesDataGrid.DataContext = null;
            this.EmployeesDataGrid.DataContext = this.FindResource(this._viewSourceKey) as CollectionViewSource;
        }
    }

動作例

まとめ

コードビハインドに少々の記述はありますが、CollectionViewSourceを間に差し込むことによって、ViewModelとView結合を疎に保ちつつソートの初期化を実現できました。

むしろViewModelをいじって何とかしようとすると途端に考えないといけないことが増えそうな気がします。

何か他にいい実現方法があればコメントをいただければ幸いです。