【WPF】リファクタリングで学ぶMVVM、Prism。その⑤ ～ 編集機能周り ～

# はじめに
下記の記事の続きとなります。

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

この記事は、編集に関わる機能のリファクタリングを行っていきます。

# リファクタリングの対象
"編集内容を確定"ボタンの処理などをリファクタリングしていきます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/48623/91e47c9a-ac1c-9ce1-e1a9-bcf61f540396.png)

リファクタリングの対象ソースコードはこちら。

```xml:MainView.xaml
<Button Click="fixBtnClick">編集内容を確定</Button>
```

```xml:MainView.xaml
<igWPF:XamDataGrid x:Name="xamDataGrid" RecordActivated="XamDataGrid_RecordActivated" 
 ...省略... />
```

```csharp:MainWindow.xaml.cs
        private void fixBtnClick(object sender, RoutedEventArgs e)
        {
            SampleData editData = this.xamPropGrid.SelectedObject as SampleData;
            List<SampleData> records = this.xamDataGrid.DataSource as List<SampleData>;
            SampleData targetData = records.First(data => data.Id == editData.Id);

            // 編集内容を反映
            targetData.Age = editData.Age;
            targetData.Name = editData.Name;

            // XamDataGridに反映
            this.xamDataGrid.DataSource = null;
            this.xamDataGrid.DataSource = records;

            // データソース反映時に選択行が解除されるため、再設定
            this.xamDataGrid.SelectedDataItem = targetData;

        }

        private void XamDataGrid_RecordActivated(object sender, Infragistics.Windows.DataPresenter.Events.RecordActivatedEventArgs e)
        {
            // 選択行のオブジェクトをXamPropertyGridに表示
            xamPropGrid.SelectedObject = this.xamDataGrid.ActiveDataItem;
        }
```

## コードリファクタリング
## 処理詳細を整理
今回も、まずは、編集機能が行っている処理を整理します。

**編集内容の表示**
1.XamDataGridの選択行に応じて、編集内容を表示する。

**編集内容の確定**
1. "編集内容を確定"ボタンがクリックされたらfixBtnClickメソッドの処理を実行する。
2. XamDataGridからデータソースを取得する。
3. XamDataGridの選択中のレコードを取得する。
4. 編集したIDと一致するIDを持つレコードをデータソースから取得する。
5. 4で取得したレコードの年齢(Age)、名前(Name)に編集内容を反映する。
6. 更新したデータソースを再反映させる。
これらをViewから処理移管していきましょう。
ただし、今回は単純移管ではなく、"編集内容を確定"ボタンは削除して、バインディング機能を使いXamPropertyGridでの編集内容をXamDataGridに反映するようにします。

## 1.XamDataGridの選択行とXamPropertyGridの編集対象オブジェクトをバインディングで解決する。
XamPropetyGridの編集対象(SelectedObject)ですが、バインディングでXamDataGridの選択行を直接みるようにします。
こうすることで、コードビハインドでXamDataGridの選択行が変更なった場合にイベントで処理していた処理も不要となります。

```xml:MainView.xaml
<ig:XamPropertyGrid  x:Name="xamPropGrid" SelectedObject="{Binding ElementName=xamDataGrid, Path=ActiveDataItem}" />
```

## 2.XamPropertyGridの編集内容を反映する。
さて、編集内容の反映は"編集内容の反映"ボタンクリックにより、プログラムで処理していましたが、今回のリファクタリングでは趣向をかえ、SampleDataにバインディング通知機能(ViewModel⇒View)を追加することにより、値が変更されたらリアルタイムでXamDataGridのデータに反映されるようにします。

手順としては、
1.SampleDataにINotifyPropertyChangedを実装する。

```csharp:SampleData.cs
    public class SampleData : INotifyPropertyChanged
    {
        /// <summary>
        /// プロパティ値の変更をクライアントに通知する。
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// PropertyChanged イベント を発生させる。
        /// </summary>
        /// <param name="propertyName">変更されたプロパティ名</param>
        protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
```

2.ViewModel⇒Viewに変更通知を行いたいプロパティのSetより、OnPropertyChanged()メソッドをコールする。

```csharp:SampleData.cs
        private string _Name;
        public string Name
        {
            get { return _Name; }
            set { _Name = value; OnPropertyChanged(); }
        }

        private int _Age;
        public int Age
        {
            get { return _Age; }
            set { _Age = value; OnPropertyChanged(); }
        }
```

ここまで実装すると、XamPropertyGridの編集内容がXamDataGridにバインディングされ即座に反映されるようになります。
![Capture2.gif](https://qiita-image-store.s3.amazonaws.com/0/48623/4e9160d7-906d-1bfc-2bee-0c189a8e475f.gif)


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

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

## まとめ
この記事で、ついにMainWindow.xaml.csからロジックが消え去りました！
次は、MainWindow.xamlをユーザーコントロールという観点でリファクタリングしてきます。