WPF/MVVM/C#/Prism5.0 ViewModelLocationProvider ～ViewとViewModelを自動で関連付け～

#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で並列ビルド～

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

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

#ViewModelLocationProvider


これまでやってきたサンプルでは、ViewとViewModelの紐付けは以下のようになっています。

```csharp:CalcView.xaml.cs
using Microsoft.Practices.Prism.Mvvm;
using System.Windows;

namespace KStore.Calc._2
{   
    public partial class CalcView : Window
    {
        public CalcView()
        {
            InitializeComponent();
            this.DataContext = new CalcViewModel();　// <-- ここで紐づけ
        }
    }
}
```

この
`this .DataContext = new CalcViewModel();`

を自動でやってくれるというちょっとオーバースペックな機能がPrismにはあります。
いや、オーバースペックではなく、今後必要になるのですが、まだあまり必要性は感じられません。
とにかく、やってみます。

CalcView.xamlに以下の２行を追加します。

```xml:CalcView.xaml
<Window x :Class="KStore.Calc._2.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._2"
        mc:Ignorable ="d"
        Title="Calc" Height ="350" Width="525"
        xmlns:prism="clr-namespace:Microsoft.Practices.Prism.Mvvm;assembly=Microsoft.Practices.Prism.Mvvm.Desktop"
        prism:ViewModelLocator.AutoWireViewModel ="True"
        >
</Window>
```

このままビルドすると、ビルドは通りますが、以下のように怒られます。

 **Your views must implement IView**    

実行してみると以下の例外。
![Image.png](https://qiita-image-store.s3.amazonaws.com/0/12790/24a188f8-fd92-9085-b1fd-1d300517860a.png)



言われたとおりに、CalcViewにIViewを実装。これはほとんどマーカーインタフェースですね。
DataContextにViewModelを紐付けする行も削除しました。

```csharp:CalcView.xaml.cs
using Microsoft.Practices.Prism.Mvvm;
using System.Windows;

namespace KStore.Calc._3
{   
    public partial class CalcView : Window, IView // <-- このインターフェースを追加
    {
        public CalcView()
        {
            InitializeComponent();
        }
    }
}
```

で、実行してみると・・・
![Image.png](https://qiita-image-store.s3.amazonaws.com/0/12790/c98b142d-e3cf-ce48-61ad-a7b45fa1f474.png)


バインドされていない。。。
何故か。

[ViewModelLocationProvider](http://compositewpf.codeplex.com/SourceControl/latest#V5/PrismLibrary/Prism.Mvvm/ViewModelLocationProvider.cs)のコードを見ると、
Viewの名前に加えて、"ViewModel"が必要みたいです。
今は、
CalcView -> CalcViewModelにしていたのですが、
CalcView -> CalcViewViewModel でないとダメみたいです。

うーん。Viewの名前は末尾に"View"にしないほうが良さそうですね。

気を取り直して、CalcViewViewModel にしてみて、実行します。

![Image.png](https://qiita-image-store.s3.amazonaws.com/0/12790/7ec19523-3dd3-0ba7-462a-de0c28fc3151.png)


バインドされました！
これを使うには、ViewModelの命名規則を必ず
Viewの名前 + "ViewModel"とする必要があります。

上記はXAMLのみで完結する記載でしたが、XAMLで記載せず、コードビハインドで以下のように書いてもOKです。

```csharp:CalcView.xaml.cs
using Microsoft.Practices.Prism.Mvvm;
using System.Windows;

namespace KStore.Calc._3
{   
    public partial class CalcView : Window, IView
    {
        public CalcView()
        {
            InitializeComponent();
            ViewModelLocationProvider.AutoWireViewModelChanged(this);
        }
    }
}
```

今回はあまり長くならずに済みました。

#まとめ
[ViewModelLocationProvider](https://msdn.microsoft.com/en-us/library/microsoft.practices.prism.mvvm.viewmodellocationprovider%28v=pandp.50%29.aspx)を使うには、Viewの名前とViewModelの名前に規則性を持たせる必要があります。
Viewの名前に "ViewModel"を末尾に加えます。
例:
 CalcView  -> CalcViewViewModel

Viewの名前は末尾に"View"をつけないほうが良さそう。