読者です 読者をやめる 読者になる 読者になる

C# + WPFで、DataGridにボタンを表示し、選択行を操作する

C# WPF

引き続き、Microsoft.TeamFoundation.MVVM名前空間を使って作る、WPFアプリの話です。

WPFのDataGridにボタンを表示し、そのボタンを押すと選択行に対して

  • 行データの更新
  • 行データを元にした、メッセージボックスの表示や子Windowの表示

を行うようなアプリを試してみました。

 
2015/12/10 追記 ここから

公式Blogに以下の記事が掲載されましたので、Microsoft.TeamFoundation.MVVMの使用前に記事を確認してみてください。
Microsoft.TeamFoundation.MVVM 名前空間の利用について - Visual Studio サポート チーム blog - Site Home - MSDN Blogs

2015/12/10 追記 ここまで

 

環境

 

作成イメージ

親Window

f:id:thinkAmi:20140823062235p:plain

 

呼ばれる子Window

数量加算ボタンを押した後に、Windowボタンを押した結果

f:id:thinkAmi:20140823062334p:plain

 

親Window

View

DataGridButtonMainView.xamlに実装します。

 

Microsoft.TeamFoundation.MVVM名前空間を使った子Windowの表示設定

前回と同じく、Window要素に必要なものを追加します。

<Window x:Class="DataGridMVVM.DataGridButtonMainView"
        ...
        xmlns:local="clr-namespace:DataGridMVVM"
        xmlns:mvvm="clr-namespace:Microsoft.TeamFoundation.MVVM;assembly=Microsoft.TeamFoundation.Controls"
        mvvm:MVVMSupport.ViewModel="{Binding}"
        ...>
    <Window.DataContext>
        <local:DataGridButtonMainViewModel/>
    </Window.DataContext>
    <Window.Resources>
        <mvvm:RegisterWindow x:Key="SubWindowKey" Type="local:DataGridButtonSubView"/>
    </Window.Resources>
</Window>

 

DataGridのデータソースと現在行へのデータバインディング設定

データソースはItemsSourceに、現在行はSelectedItemに、それぞれ設定します。

実際には、上記のDataContextに指定したViewModelのプロパティをPathに設定します。
参考:Get selected row item in DataGrid WPF - Stack Overflow

<DataGrid
    ItemsSource="{Binding UpdateSourceTrigger=PropertyChanged, Path=DataGrid}"
    SelectedItem="{Binding Path=CurrentRowItem}">
</DataGrid>

 

DataGridのボタン

DataGridの各行にボタンを表示するには、 <DataGridTemplateColumn>を使います。
参考:[C#, WPF] datagrid にボタン配置 - パイナップルの芯

 
Commandへのデータバインディングはいつもと同じですが、DataGrid内では

  • Pathに、DataContext.<ViewModelのプロパティ>
  • RelativeSourceに、{RelativeSource AncestorType={x:Type DataGrid}}}

と指定することが異なります。
参考:mvvm - Firing a RelayCommand from a button inside a WPF DataGrid - Stack Overflow

<DataGridTemplateColumn>
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <Button Command="{Binding Path=DataContext.ShowWindowCommand,
                RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" .../>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

 
なお、RelativeSourceについては、以下が参考になりました。

ちなみに、参考先にはFindAncestorの記述がありますが、手元のインテリセンスには表示されてこなかったので、XAMLには書いてありません。

 

ViewModel

DataGridButtonMainViewModel.csに実装します。

 
特に変わったところはなく、

  • Microsoft.TeamFoundation.MVVM.ViewModelBaseを継承
  • Viewのデータバインディングで指定したプロパティを記述
    • DataGrid内ButtonのCommand向けの、public ICommand ShowWindowCommandプロパティ
    • RelayCommandで指定するprivate void ExecuteShowMessageCommand()メソッド
    • ItemSource向けの、public ObservableCollection<Order> DataGridプロパティ
    • SelectedItem向けの、public Order CurrentRowItemプロパティ

を実装します。

また、Execute<hoge>Command()メソッドは3種類ありますが、それぞれ以下のコードを実装してあります。

子Window表示

親Windowのデータは、子WindowのViewModelのプロパティへ渡しました。

private void ExecuteShowWindowCommand()
{
    var vm = new DataGridButtonSubViewModel();
    vm.ItemName = CurrentRowItem.ItemName;
    vm.Quantity = CurrentRowItem.Quantity;
    WindowDisplayService.ShowDialog("SubWindowKey", vm);
}

 

MessageBox表示
private void ExecuteShowMessageCommand()
{
    if (CurrentRowItem == null) return;

    //  CurrentRowItemはOrder型であり、Order型でToString()メソッドをオーバーライドしてある
    MessageBoxService.Show(CurrentRowItem.ToString());
}

 

現在行の数量を加算
private void ExecuteAddQuantityCommand()
{
    if (CurrentRowItem == null) return;

    //  Model(Order型)のメソッドで加算
    CurrentRowItem.AddQuantity();
}

 

Model

Order.csに

  • DataGridのItemsSourceSelectedItemで必要になるプロパティ
  • INotifyPropertyChangedインタフェースの実装
  • ViewModelから呼ばれるAddQuantity()メソッドや、ToString()メソッドのオーバーライド

を実装します。

特に変わったことはないので、コードは省略します。

 

子Window

DataGridButtonSubView.xamlや DataGridButtonSubViewModel.csなどに実装しますが、特に変わったことはないので、コードは省略します。

 

ソースコード

GitHubにあげておきました。
CSharp-Sample/MVVMApp/DataGridMVVM at master · thinkAmi/CSharp-Sample