備忘録的なSomething

素敵なAnything

【C#,WPF,MVVM】DataGridとかで SelectedItems をコマンドで使う簡単な方法

DataGrid, ListView, ListBox などの ItemsControl で複数選択したとき、選択結果は SelectedItems に入っていますが、残念なことにこのプロパティは Binding 非対応です。
バインディングしたい場合は IsSelected プロパティを用意するなど面倒な手順が必要になりますが、実はコマンドで取得するだけなら簡単にできます。

やり方:

  • DataGrid 等には x:Name をつけておきます。
        <DataGrid x:Name="dataGridName"
                  ItemsSource="{Binding Items}" />
  • 使用するコマンドの CommandParameter に、DataGrid 等の SelectedItems を渡します。
        <Button Content="選択"
                Command="{Binding SelectCommand}"
                CommandParameter="{Binding ElementName=dataGridName, Path=SelectedItems}" />
  • VMのコマンド実装では、parameter を受け取って、SelectedItems の型である IList にキャストしてから、中身を項目の型にキャストして使います。
    (以下はコマンド実装に ReactiveProperty の ReactiveCommand を使用し、各項目の型は string である場合の例です)
    なお、IList はusing System.Collections;で使用できます。(なぜか Ctrl+. を押しても出てこない・・・)
            SelectCommand.Subscribe(parameter =>
            {
                var selectedItems = ((IList)parameter).Cast<string>().ToArray();
                // 以下、selectedItems を使う処理
            });

サンプルコード

このサンプルでは、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>
        <DataGrid x:Name="dataGridName"
                  ItemsSource="{Binding Items}"
                  AutoGenerateColumns="False">
            <DataGrid.Columns>
                <DataGridTextColumn Header="世界" Binding="{Binding}" />
            </DataGrid.Columns>
        </DataGrid>
        <Button Content="選択"
                Command="{Binding SelectCommand}"
                CommandParameter="{Binding ElementName=dataGridName, Path=SelectedItems}" />
    </StackPanel>
</Window>

VM

    public class MainWindowViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public List<string> Items { get; } = new List<string>() { "Asgard", "Alfheim", "Jotunheim", "Midgard", "Muspelheim", "Nidavellir", "Niflheim", "Svartalfheim", "Vanaheim", };
        public ReactiveCommand SelectCommand { get; } = new ReactiveCommand();

        // コンストラクタ
        public MainWindowViewModel()
        {
            SelectCommand.Subscribe(parameter =>
            {
                var selectedItems = ((IList)parameter).Cast<string>().ToArray();
                if (selectedItems.Any())
                {
                    MessageBox.Show($"あなたは {string.Join(" と ", selectedItems)} を選択しました。");
                }
            });
        }
    }

実行結果