【WPF】リファクタリングで学ぶMVVM、Prism。その③ ～ 行追加ボタンのClick処理をリファクタリング ～

# はじめに
下記の記事の続きとなります。
[【WPF】リファクタリングで学ぶMVVM、Prism。その② ～ Model,ViewModelを作成 ～](https://qiita.com/furugen/items/da62d3364a2bdb59e04e)

この記事では、行追加ボタンのClick処理をリファクタリングしてきます。

# リファクタリングの対象
タイトルにもありますが、追加ボタンのイベント処理をCommandを利用して、ViewからViewModelに責務を移管します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/48623/536a45a4-9968-42c7-2e97-d810b33fb334.png)

ソースコードはこちらとなります。

```xml:MainView.xaml
<Button Grid.Column="0" Click="addBtnClick">行追加</Button>
```

```csharp:MainWindow.xaml.cs
private void addBtnClick(object sender, RoutedEventArgs e)
{
    // 行を追加する
    List<SampleData> records = this.xamDataGrid.DataSource as List<SampleData>;
    records.Add(new SampleData());
    this.xamDataGrid.DataSource = null;
    this.xamDataGrid.DataSource = records;
}
```

# リファクタリング
## 処理詳細を整理
まずは、addBtnClickが行っている処理を整理します。
1. 行追加ボタンがクリックされたらaddBtnClickメソッドの処理を実行する。
2. XamDataGridからデータソースを取得する。
3. 取得したデータソースに１レコード追加する。
4. 更新したデータソースを再反映させる。
これらをViewから処理移管していきましょう。

まず行追加ボタンのクリックをViewModelで感知するために、Commandの機能を利用します。
また、クリックされた後に、XamDataGridのデータソースを取得し再反映していますが、バインディングの機能を組み込めばViewModel/Modelからデータの操作が可能です。あとは、1レコードを追加するのはModelの責務となりますので、Modelにメソッドを用意したうえで、ViewModelからコールするようにしましょう。

## コードリファクタリング
### 1. 行追加ボタンのクリック時に呼びされるCommandを作成する。
まずは、ほぼ空のCommandクラスを作成しましょう。

```csharp:AddCommand.cs
public class AddCommand : ICommand
{
    public bool CanExecute(object parameter) { return true; }
    public event EventHandler CanExecuteChanged;
    public void Execute(object parameter)
    {

    }
}
```

ViewModelを操作できるようコンストラクタで対象のMainWindowViewModelを取得できるようにします。

```csharp:AddCommand.cs
    private MainWindowViewModel vm;
    public AddCommand(MainWindowViewModel viewModel)
    {
        this.vm = viewModel;
    }

```

Commandが実行された際に動作するExcuteメソッドで、ViewModel側で用意する行追加メソッドをコールしましょう。OnAddRecord()の詳細については、次の項目にて。

```csharp:AddCommand.cs
    public void Execute(object parameter)
    {
      this.vm.OnAddRecord();
    }
```

### 2. 行追加ボタンのクリックイベントを削除し、1.で作成したCommandをバインディングします。

ViewModelに作成したコマンドをプロパティとして保持し、コンストラクタで初期化します。

```csharp:MainWindowViewModel.cs
        public AddCommand AddCommand { get; set; }

        public MainWindowViewModel()
        {
            ...省略...
            // コマンドを追加
            this.AddCommand = new AddCommand(this);
        }
```

Viewの行追加ボタンをクリックイベントではなく、コマンドで処理するように変更します。先ほどViewModelに追加したAddCommandプロパティとバインディングしましょう。これで、行追加ボタンがクリックされた際に、行追加ボタンのクリック > AddCommand.Execute()が実行されるようになりました。

```xml:MainView.xaml
<Button Grid.Column="0" Command="{Binding AddCommand}">行追加</Button>
```

### 3. ViewModelの行追加メソッドでは、Modelが保持する社員情報追加メソッドをコールします。
コマンド実行時(AddCommand.Execute)から呼ばれるメソッドを実装しましょう。

```csharp:MainWindowViewModel.cs
        public void OnAddRecord()
        {
            // 社員情報のModelに社員情報を追加する。
            this.employeeServiceModel.AddEmployee();
        }
```

次に、Modelに社員情報を追加するメソッドを実装します。

```csharp:EmployeeServiceModel.cs
        public void AddEmployee()
        {
            this.Employees.Add(new SampleData());
        }
```

### 4. レコードが追加されない？　バインディングを見直し(ObserbabseCollection)を利用しよう。
これまでの手順を実施し、アプリケーションを起動して動作を確認してみましょう。

行追加ボタンをクリックしてもXamDataGridにレコードが追加されません。
![Capture11.gif](https://qiita-image-store.s3.amazonaws.com/0/48623/27c10bc2-de70-2250-4011-bb4f5b30da7a.gif)

デバッグでEmployeeServiceModel.AddEmployee()メソッドにブレークポイントを付与して確認しても、ちゃんと処理が行われていることを確認できます。

なぜでしょうか？　XamDataGridのバグ？（笑）
![image.png](https://qiita-image-store.s3.amazonaws.com/0/48623/d7278ea5-d118-d0d8-dfbf-8027065eace7.png)

実は、コレクションを管理しているEmployeeServiceModel.Employeesの型に問題があります。

```csharp:EmployeeServiceModel.cs
public List<SampleData> Employees { get; set; }
```

コレクションをバインディングで利用したい場合は、Listではなく、ObservableCollectionに変更しましょう。
ObservableCollectionはレコードの増減が発生した場合に、Viewへバインディング通知を行います。Listの場合は、バインディング通知が行われないため、レコードの追加が発生しない事象となっていました。

```csharp:EmployeeServiceModel.cs
public ObservableCollection<SampleData> Employees { get; set; }
```

```csharp:MainWindowViewModel.cs
public ObservableCollection<SampleData> EmployeeData { get; set; }
```

では、もう一度アプリケーションを起動してレコードが追加されることを確認してみましょう。
![Capture14.gif](https://qiita-image-store.s3.amazonaws.com/0/48623/c0959535-8a22-af73-32b6-3d7fd26deddc.gif)
OK!!

## リファクタリング前後のコード
今回のコードのリファクタリング前後はこちらになります。

実施前
https://github.com/furugen/WPF-Try-Refactoring/tree/edition2
実施後
https://github.com/furugen/WPF-Try-Refactoring/tree/edition3

## まとめ
MainWindow.xaml.csの行追加ボタンのClickに伴う処理を責務分割しました。
Command,ViewModel,Model,データバインディングの要素がありましたが如何でしょうか。
次は、行削除ボタンをリファクタリングしていきますが、基本的には行追加ボタンとやることは一緒ですね。

#次の記事

[【WPF】リファクタリングで学ぶMVVM、Prism。その④ ～ 行削除ボタンのClick処理をリファクタリング ～](https://qiita.com/furugen/items/130abe35b8981d92d5c0)