WPFアプリで、DataGridコントロールやListViewコントロールにソートして表示されているデータが修正されたときに、再ソートしてデータが正しい順序で表示されるようにする方法を解説する。
対象:.NET 4.5以降
WPFのDataGridコントロールやListViewコントロールなど(いずれもSystem.Windows.Controls名前空間)に表示しているデータをソート/フィルタリング/グルーピングするには、CollectionViewSourceクラス(System.Windows.Data名前空間)を利用すればよい(「.NET TIPS:WPF:DataGridやListViewなどにデータをソートして表示するには?[XAML、C#、VB]」を参照)。ところが1つ困ったことがある。表示している元データをプログラムから変更したときには、表示位置が変わらず、ソート/フィルタリング/グルーピングが崩れてしまうのだ。これは簡単に解決できないものだろうか? 本稿では、.NET Framework 4.5の新機能を使ってこの問題に対処する方法を説明する。
なお、本稿のプログラミングには、無償のVisual Studio Express 2012 for Windows Desktop(以降、VS 2012)を使用した。Visual Studio 2013でも手順は同じである。
「.NET TIPS:WPF:DataGridやListViewなどにデータをソートして表示するには?[XAML、C#、VB]」で作成したプログラムを準備していただきたい。以降では、これをベースにして説明する。
ソートが設定してあると、DataGridコントロール上でエンドユーザーが直接編集/追加した場合や、プログラムからデータを追加した場合は、表示されているデータが自動的に適切な場所に移動し、ソート順が保たれていた。
ところが、プログラムからデータを変更した場合には、表示されているデータが移動しない。そのためソート順が崩れてしまう(次の画像)。
 ベースとなるプログラムで、プログラムから元データを変更した(Windows 7)
ベースとなるプログラムで、プログラムから元データを変更した(Windows 7)ICollectionViewLiveShapingインターフェース(System.ComponentModel名前空間)を使えばよい(.NET Framework 4.5の新機能)。
ただし、.NET Framework 4.5でICollectionViewLiveShapingインターフェースを実装しているのは、ListCollectionViewクラス/BindingListCollectionViewクラス(以上2つはSystem.Windows.Data名前空間)/ItemCollectionクラス(System.Windows.Controls名前空間)の3つである。上記のベースとなるプログラムにおいて、元データはObservableCollectionクラスであり、そこから得られる既定のビューはListCollectionViewクラスであるので、このICollectionViewLiveShapingインターフェースの機能を利用できる。
コーディングとしては、既定のビューのIsLiveSortingプロパティをtrueにするだけである(次のコード)。
// 画面が表示されるとき、データを画面にセットする
private void Window_Loaded(object sender, RoutedEventArgs e)
{
  // 既定のビューを取り出してセットする
  var view = CollectionViewSource.GetDefaultView(_data);
  this.RootGrid.DataContext = view;
  // 既定のビューにソートを指定する
  view.SortDescriptions.Add(
    new System.ComponentModel.SortDescription(
          "Value",
          System.ComponentModel.ListSortDirection.Ascending)
        );
  // DataGridコントロールのヘッダーにソートの印(三角のマーク)を表示する
  this.DataGrid1.Columns[1].SortDirection = System.ComponentModel.ListSortDirection.Ascending;
  // ここまでが、ベースとなるプログラムの記述
  // LiveShapingを有効にする
  // viewの実体が分からないときは、ICollectionViewLiveShapingインターフェースが実装されていることと、
  // CanChangeLiveSortingプロパティの値がtrueであるかをチェックしてから有効にすること
  var liveShaping = view as System.ComponentModel.ICollectionViewLiveShaping;
  if (liveShaping != null && liveShaping.CanChangeLiveSorting)
    liveShaping.IsLiveSorting = true;
  // IsLiveSortingプロパティをtrueに変更できることが確定しているなら、次の1行で済む
  //(view as System.ComponentModel.ICollectionViewLiveShaping).IsLiveSorting = true;
}
' 画面が表示されるとき、データを画面にセットする
Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs)
  ' 既定のビューを取り出してセットする
  Dim view = CollectionViewSource.GetDefaultView(_data)
  Me.RootGrid.DataContext = view
  ' 既定のビューにソートを指定する
  view.SortDescriptions.Add(
    New System.ComponentModel.SortDescription(
          "Value",
          System.ComponentModel.ListSortDirection.Ascending)
        )
  ' DataGridコントロールのヘッダーにソートの印(三角のマーク)を表示する
  Me.DataGrid1.Columns(1).SortDirection _
    = System.ComponentModel.ListSortDirection.Ascending
  ' ここまでが、ベースとなるプログラムの記述
  ' LiveShapingを有効にする
  ' viewの実体が分からないときは、ICollectionViewLiveShapingインターフェースが実装されていることと、
  ' CanChangeLiveSortingプロパティの値がTrueであるかをチェックしてから有効にすること
  Dim liveShaping = TryCast(view, System.ComponentModel.ICollectionViewLiveShaping)
  If (liveShaping IsNot Nothing AndAlso liveShaping.CanChangeLiveSorting) Then
    liveShaping.IsLiveSorting = True
  End If
  ' IsLiveSortingプロパティをtrueに変更できることが確定しているなら、次の1行で済む
  'DirectCast(view, System.ComponentModel.ICollectionViewLiveShaping).IsLiveSorting = True
End Sub
これで実行してみると、次の画像のようにソート順が維持される。なお、同様にして、フィルタリング/グルーピングを維持させることもできる。
 IsLiveSortingプロパティをtrueに変更して、プログラムから元データを変更した(Windows 7)
IsLiveSortingプロパティをtrueに変更して、プログラムから元データを変更した(Windows 7)利用可能バージョン:.NET Framework 4.5以降
カテゴリ:WPF 処理対象:DataGridコントロール、ListViewコントロール
使用ライブラリ:ICollectionViewLiveShapingインターフェース(System.ComponentModel名前空間)
関連TIPS:WPF:DataGridやListViewなどにデータをソートして表示するには?[XAML、C#、VB]
Copyright© Digital Advantage Corp. All Rights Reserved.