DataGrid や ListView などの ItemsControl に対して、検索結果等で絞り込んで表示したいときは、CollectionViewSource のフィルタリング機能が使用できます。
やり方:
- VMに CollectionViewSource のプロパティを定義します。
public CollectionViewSource ItemsView { get; } = new CollectionViewSource();
- xaml では、DataGrid 等の ItemsSrouce に CollectionViewSource の View プロパティをバインドします。
<DataGrid ItemsSource="{Binding ItemsView.View}" />
- VMのコンストラクタ等で、CollectionViewSource の Source と View.Filter を設定します。
View.Filter には、object(Source に入れたアイテムの型にキャスト可)を受け取って、表示するかどうかをboolで返す処理を指定します。
(以下では関数を作成して指定しています)
// コンストラクタ public MainWindowViewModel() { ItemsView.Source = new[] { "アイテム1", "アイテム2", "アイテム3", }; ItemsView.View.Filter = ItemsViewFilter; } // フィルタリング関数 private bool ItemsViewFilter(object obj) { var item = obj as string; if (表示する条件) { return true; } else { return false; } }
- 検索ボタンが押されたタイミングなど、フィルタリングを実行するときはビューの Refresh() 関数を呼びます。
ItemsView.View.Refresh();
以下の記事で紹介した方法と組み合わせると、Enterキーを押したときにフィルタリングをかけることもできます。
【C#,WPF,MVVM】TextBox上でEnterキーが押されたときにコマンドを実行する - 備忘録的なSomething
サンプルコード
このサンプルでは、string のリストから、入力した部分文字列を含む項目を絞り込んで表示します。
MVVMフレームワークとして ReactiveProperty を使用しています。
xaml(Margin等の指定は省略)
<Window x:Class="WpfTestApp.Windows.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" SizeToContent="WidthAndHeight" ResizeMode="NoResize"> <StackPanel> <TextBox Text="{Binding SearchText.Value}" /> <Button Content="検索" Command="{Binding SearchCommand}" /> <DataGrid ItemsSource="{Binding ItemsView.View}" AutoGenerateColumns="False" IsSynchronizedWithCurrentItem="False"> <DataGrid.Columns> <DataGridTextColumn Header="世界" Binding="{Binding}" /> </DataGrid.Columns> </DataGrid> </StackPanel> </Window>
IsSynchronizedWithCurrentItem
は、デフォルトの True だとなぜか最初の項目が初期選択されてしまうので、嫌な場合は False を指定します。
VM
public class MainWindowViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; public ReactivePropertySlim<string> SearchText { get; } = new ReactivePropertySlim<string>(); public ReactiveCommand SearchCommand { get; } = new ReactiveCommand(); public CollectionViewSource ItemsView { get; } = new CollectionViewSource(); // コンストラクタ public MainWindowViewModel() { ItemsView.Source = new[] { "Asgard", "Alfheim", "Jotunheim", "Midgard", "Muspelheim", "Nidavellir", "Niflheim", "Svartalfheim", "Vanaheim", }; ItemsView.View.Filter = ItemsViewFilter; SearchCommand.Subscribe(() => ItemsView.View.Refresh()); } // フィルタリング関数 private bool ItemsViewFilter(object obj) { if (string.IsNullOrEmpty(SearchText.Value)) { return true; } var item = obj as string; return item.Contains(SearchText.Value); } }
実行結果
↓(「gard」が含まれる世界を検索)
更新履歴
- 2021/11/30 IsSynchronizedWithCurrentItem について追記