WPF:DataGridやListViewなどのデータを変更したときに自動的に再ソートするには?[C#、VB].NET TIPS

WPFアプリで、DataGridコントロールやListViewコントロールにソートして表示されているデータが修正されたときに、再ソートしてデータが正しい順序で表示されるようにする方法を解説する。

» 2014年10月28日 13時24分 公開
[山本康彦BluewaterSoft/Microsoft MVP for Windows Platform Development]
.NET TIPS
Insider.NET

 

「.NET TIPS」のインデックス

連載目次

対象:.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コントロール上でエンドユーザーが直接編集/追加した場合や、プログラムからデータを追加した場合は、表示されているデータが自動的に適切な場所に移動し、ソート順が保たれていた。

 ところが、プログラムからデータを変更した場合には、表示されているデータが移動しない。そのためソート順が崩れてしまう(次の画像)。

[VALUE]が[Green]の行を選択するとウィンドウ下部にそのインデックスと値が表示されるので、[VALUE]テキストボックスに「Yellow」を入力して、[Add/Renew]ボタンをクリック
[VALUE]が[Green]の行を選択するとウィンドウ下部にそのインデックスと値が表示されるので、[VALUE]テキストボックスに「Yellow」を入力して、[Add/Renew]ボタンをクリック
ベースとなるプログラムで、プログラムから元データを変更した(Windows 7) ベースとなるプログラムで、プログラムから元データを変更した(Windows 7)
最初は[VALUE]の昇順([Blue]→[Green]→[Red])にソートされている。その状態で、[Green]を[Yellow]に変更しようとしているところ(上)。
新しい値を入力し、画面右下の[Add/Renew]ボタンをクリックすると、コードビハインドのプログラムが元データを書き換える。表示は[Green]から[Yellow]に変わるが、ソートは行われず、表示位置は元のままだ(下)。
ヘッダーで[VALUE]の昇順になるようソートを指定しているのだから、[Yellow]に変わったデータは一番下に移動してほしいのだ。

データを変更したときに自動的にソートするには?

 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

データを変更したときに自動的にソートするするコード(上:C#、下:VB)
太字の部分を追加するだけである。
コメントにあるように、IsLiveSortingプロパティをtrueに変更できることが確実であるならば、1行だけで済む。
なお、このVBのコードでは、Visual Basic 2010から利用できるようになった「暗黙の行連結」を使用している。

 これで実行してみると、次の画像のようにソート順が維持される。なお、同様にして、フィルタリング/グルーピングを維持させることもできる。

IsLiveSortingプロパティをtrueに変更して、プログラムから元データを変更した(Windows 7) IsLiveSortingプロパティをtrueに変更して、プログラムから元データを変更した(Windows 7)
最初は[VALUE]の昇順([Blue]→[Green]→[Red])にソートされている。その状態で、[Green]を[Yellow]に変更しようとしているところ(上)。
画面右下の[Add/Renew]ボタンをクリックすると、コードビハインドのプログラムが元データを書き換える。表示内容とともに表示位置も自動的に変わり、ソート順が維持される(下)。

利用可能バージョン:.NET Framework 4.5以降
カテゴリ:WPF 処理対象:DataGridコントロール、ListViewコントロール
使用ライブラリ:ICollectionViewLiveShapingインターフェース(System.ComponentModel名前空間)
関連TIPS:WPF:DataGridやListViewなどにデータをソートして表示するには?[XAML、C#、VB]


「.NET TIPS」のインデックス

.NET TIPS

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。