WPF/MVVM/C#/Prism5.0 ViewModelLocationProvider ~ViewとViewModelを自動で関連付け~
※Qiitaにも投稿しています。
WPFでMVVMは難しい
残念なことに、WPFでMVVMパターンを適用する際には、.NET標準だけ使うとなると、綺麗でわかりやすく保守が容易なコードが書けません。 書けないような基盤しかないのです。 なので、PrismなどのMVVM基盤ライブラリが必要となります。 https://msdn.microsoft.com/ja-jp/library/gg406140.aspx
他にも様々なライブラリが公開されていますが、MS謹製ということで今回はPrismを利用しようと思います。 Prismを利用することで得られるメリットを公開します。 以下の予定です。
- BindableBase/DelegateCommand ~ViewModelの基盤~
- ErrorsContaier ~便利なエラー通知~
- ViewModelLocationProvider ~ViewとViewModelを自動で関連付け~
- Regionってなんなのさ ~Viewの配置をお手軽に~
- IModuleとUnity ~UIでDI~
- DIPパターンの恩恵 ~MSBuildで並列ビルド~
本稿は上記3の記事になります。
※Visual Studio 2013 Community Editionで実験しています。
ViewModelLocationProvider
これまでやってきたサンプルでは、ViewとViewModelの紐付けは以下のようになっています。
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に以下の2行を追加します。
<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
実行してみると以下の例外。
言われたとおりに、CalcViewにIViewを実装。これはほとんどマーカーインタフェースですね。 DataContextにViewModelを紐付けする行も削除しました。
using Microsoft.Practices.Prism.Mvvm; using System.Windows; namespace KStore.Calc._3 { public partial class CalcView : Window, IView // <-- このインターフェースを追加 { public CalcView() { InitializeComponent(); } } }
で、実行してみると・・・
バインドされていない。。。 何故か。
ViewModelLocationProviderのコードを見ると、 Viewの名前に加えて、"ViewModel"が必要みたいです。 今は、 CalcView -> CalcViewModelにしていたのですが、 CalcView -> CalcViewViewModel でないとダメみたいです。
うーん。Viewの名前は末尾に"View"にしないほうが良さそうですね。
気を取り直して、CalcViewViewModel にしてみて、実行します。
バインドされました! これを使うには、ViewModelの命名規則を必ず Viewの名前 + "ViewModel"とする必要があります。
上記はXAMLのみで完結する記載でしたが、XAMLで記載せず、コードビハインドで以下のように書いてもOKです。
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を使うには、Viewの名前とViewModelの名前に規則性を持たせる必要があります。 Viewの名前に "ViewModel"を末尾に加えます。 例: CalcView -> CalcViewViewModel
Viewの名前は末尾に"View"をつけないほうが良さそう。