WPF/MVVM/C#/Prism5.0 ViewModelを少し便利に-BindableBaseとDelegateCommand-

#WPFでMVVMは難しい

残念なことに、WPFでMVVMパターンを適用する際には、.NET標準だけ使うとなると、綺麗でわかりやすく保守が容易なコードが書けません。
書けないような基盤しかないのです。
なので、PrismなどのMVVM基盤ライブラリが必要となります。
https://msdn.microsoft.com/ja-jp/library/gg406140.aspx

他にも様々なライブラリが公開されていますが、MS謹製ということで今回はPrismを利用しようと思います。
Prismを利用することで得られるメリットを公開します。
以下の予定です。

1. BindableBase/DelegateCommand ～ViewModelの基盤～
2. ErrorsContaier ～便利なエラー通知～
3. ViewModelLocationProvider ～ViewとViewModelを自動で関連付け～
4. Regionってなんなのさ ～Viewの配置をお手軽に～
5. IModuleとUnity ～UIでDI～
6. DIPパターンの恩恵 ～MSBuildで並列ビルド～ 

本稿は上記1の記事になります。

※Visual Studio 2013 Community Editionで実験しています。

#Prismのインストール

Nuget Package Manager Consoleにて、以下のように打ちます。

`Install-Package Prism`

そうすると、以下のようにモジュール参照が追加になります。

![Image.png](https://qiita-image-store.s3.amazonaws.com/0/12790/4d2d63bd-cb10-c638-3de2-16e42c5d60f8.png)


package.configは以下のようになります。
```xml:package.config
<?xml version=" 1.0" encoding=" utf-8"?>
<packages>
  <package id=" CommonServiceLocator" version=" 1.2" targetFramework=" net45" />
  <package id=" Prism" version=" 5.0.0" targetFramework=" net45" />
  <package id=" Prism.Composition" version=" 5.0.0" targetFramework=" net45" />
  <package id=" Prism.Interactivity" version=" 5.0.0" targetFramework=" net45" />
  <package id=" Prism.Mvvm" version=" 1.0.0" targetFramework=" net45" />
  <package id=" Prism.PubSubEvents" version=" 1.0.0" targetFramework=" net45" />
</packages>
```

たくさんあって混乱しそうですが、とりあえず今はこのまま！
ビルドすると、出力パスにはこんなにたくさんのDLLやフォルダが！！
![Image.png](https://qiita-image-store.s3.amazonaws.com/0/12790/6f6e88f5-6ca2-0933-1573-c606f9258be6.png)

Prismのサイズは、多言語Resource含めて約1MBです。
これを多いと思うか否かは環境次第ですが、訳もわからずDLLが増えるのは嫌ですよね。

#使ってみる

UIはこんな感じを想定しています。ボタンを押したら下に答えが出るようなアプリです。
（かずきさんのアプリを参考しています。）
https://code.msdn.microsoft.com/MVVM-Light-toolkitMessenger-0ec2e5c4


![Image.png](https://qiita-image-store.s3.amazonaws.com/0/12790/f0f1faa9-579c-ad3e-d748-3bd25a792de6.png)



XAMLはこんな感じです。

```xaml:CalcView.xaml
<Window x :Class="KStore.Calc._1.CalcView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x ="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d ="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc ="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local ="clr-namespace:KStore.Calc._1"
        mc:Ignorable ="d"
        Title="Calc" Height ="350" Width="525">
    <Grid >
        <Grid.RowDefinitions>
            <RowDefinition Height ="120*"/>
            <RowDefinition Height ="200*"/>
        </Grid.RowDefinitions>
        <Grid Name ="InputRegion" Grid.Row="0">
            <Grid.RowDefinitions>
                <RowDefinition Height ="30*"/>
                <RowDefinition Height ="30*"/>
                <RowDefinition Height ="30*"/>
                <RowDefinition Height ="30*"/>
            </Grid.RowDefinitions>
            <TextBox Grid.Row ="0" Text="{ Binding LeftValue}" Name="LeftValue" VerticalContentAlignment="Center" TextAlignment="Center" />
            <TextBlock Grid.Row ="1"  Text ="+" HorizontalAlignment="Center" VerticalAlignment="Center"/>
            <TextBox Grid.Row ="2" Text="{ Binding RightValue}" Name="RightValue" VerticalContentAlignment="Center" TextAlignment="Center" />
            <Button Grid.Row ="3" Name="CalcButton" Content="=" Command ="{Binding CalcCommand , Mode=OneWay}" />
        </Grid>
        <Grid Name ="OutputRegion" Grid.Row="1">
            <TextBlock Name ="Answer" Text="{ Binding AnswerValue}" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="48pt"/>
        </Grid>
    </Grid >
</Window>
```


ViewModelとViewの紐付けは、今は下記のようになります。今回はベタでいきます。わかりやすいですけどね。

```csharp:CalcView.xaml.cs
using System.Windows;

namespace KStore.Calc._1
{   
    public partial class CalcView : Window
    {
        public CalcView()
        {
            InitializeComponent();
            this.DataContext = new CalcViewModel(); // これが紐付けって行。
        }
    }
}
```

ViewModelは以下のようになります。ここでBindableBaseとDelagateCommandを使用しています。

```csharp:CalcViewModel.cs
using KStore.Calc._1.Model;
using Microsoft.Practices.Prism.Commands;
using Microsoft.Practices.Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace KStore.Calc._1
{
    public class CalcViewModel : BindableBase
    {
        private string _leftValue;
        public string LeftValue
        {
            get { return _leftValue; }
            set { this .SetProperty(ref this._leftValue, value); }
        }
       
        private string _rightValue;
        public string RightValue
        {
            get { return _rightValue; }
            set { this .SetProperty(ref this._rightValue, value); }
        }

        private string _answerValue;
        public string AnswerValue
        {
            get { return _answerValue; }
            set { this .SetProperty(ref this._answerValue, value); }
        }

        private ICommand calcCommand;

        public ICommand CalcCommand
        {
            get { return this.calcCommand ?? ( this.calcCommand = new DelegateCommand(CalcExecute, CanCalcExecute)); }
        }

        private bool CanCalcExecute()
        {
            return true ;
        }

        private void CalcExecute()
        {        
            AnswerValue = IntToString( Calculation.Sum(StringToInt(LeftValue), StringToInt(RightValue)));
        }

        private int StringToInt(string src)
        {
            int ret = 0;
            if( int .TryParse(src, out ret) )
            {
                return ret;
            }
            throw new ArgumentException( "src" + src);
        }

        private string IntToString(int src)
        {
            return src.ToString();
        }

    }

}
```

考え方は単純で、

`INotifyPropertyChanged`

使いたくないんですよね。
使っちゃうと、文字列でプロパティを表現しないといけません。

下記のようなメソッドを用意してあげて、

```csharp:notifypropertychangedexample.cs
private void NotifyPropertyChanged(string info)
{
    if (PropertyChanged != null)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(info));
    }
}

public string RightValue
{
     get { return _rightValue; }
     set
     { 
         this .(ref this._rightValue, value); 
         NotifyPropertyChanged("RightValue"); // <-- これがダルい。
     }
}
```

プロパティのセッターで、文字列指定が必要になります。。
プロパティ名が変わってもVisual Studioのリファクタ機能で追従してくれない。
実行時にバグがわかると。
これではC#のような静的言語の旨味も半減です。
コンパイルを活用したいんですよね。せっかくコンパイルするんだから。


**これを回避してくれるのが、BindableBase。**
https://msdn.microsoft.com/en-us/library/microsoft.practices.prism.mvvm.bindablebase%28v=pandp.50%29.aspx
説明少なっ！

BindableBaseを継承すると、上記サンプルのように、

`set { this .SetProperty(ref this._leftValue, value); }`

これで良いということになります。
これだけでもPrism使う価値がある！と思う。　ちなみに昔は

`NotificationObject`

っていう名前だったんですよね。
BindableBaseのほうがわかりやすくて良い。

つづいて、**DelegateCommand**です。
https://msdn.microsoft.com/en-us/library/microsoft.practices.prism.commands.delegatecommand%28v=pandp.50%29.aspx
これは、もしかしたら不要かも。

このサンプルレベルだと恩恵を受けますが、ある程度の規模の開発になって、ViewやViewModelが超複雑になる場合、
Commandの処理とViewModelを切り離したくなる時がきます。
DelegateCommandだと、ViewModelに直接delegate用メソッドを定義しないとなりません。
ViewやViewModelは、UserControlで切り出して小分けで作るのは、キツイ場合があります。
全部一緒だと何かと都合が良い。UIってそうなんですよね。Class分割しすぎると生産性落とします。
だけどViewModelにロジックを書きたくない。
ただのViewのデータ部分を反映する箱にしておきたい。そんな場合に対応できないです。

そうゆう場合は、ICommandを直接実装してもいいんじゃないかなと考えます。

```csharp:Commands.cs
public class CalcCommand_SeparatedViewModel : ICommand
{
	CalcViewModel _parentViewModel;

	public CalcCommand_SeparatedViewModel(CalcViewModel parentViewModel)
	{
		_parentViewModel = parentViewModel;
	}

	public bool CanExecute(object parameter)
	{
		return true ;
	}

	public event EventHandler CanExecuteChanged;

	public void Execute(object parameter)
	{
		_parentViewModel.AnswerValue = Util.IntToString(Calculation .Sum(Util.StringToInt(_parentViewModel.LeftValue), Util.StringToInt(_parentViewModel.RightValue)));
	}
}
```

ViewModelでの定義は以下のようになります。

```csharp:CalcViewModel.cs
private ICommand _calcCommand_SeparetedViewModel;

public ICommand CalcCommand_SeparetedViewModel
{
	get { return this._calcCommand_SeparetedViewModel ?? ( this._calcCommand_SeparetedViewModel = new CalcCommand_SeparatedViewModel (this)); }
}
```

#まとめ
という訳で、Prism5.0使うなら
**BindableBaseは活用しよう！**
*DelegateCommandは用途に応じて活用しよう！*
ということでした。

続いて、ErrorsContainerも試してみます。