読者です 読者をやめる 読者になる 読者になる

Training for D-Day

ブログの内容は個人の見解であり、所属する企業を代表するものではありません。

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

qiita.com

※Qiitaにも投稿しています。

WPFでMVVMは難しい

残念なことに、WPFMVVMパターンを適用する際には、.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の紐付けは以下のようになっています。

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

実行してみると以下の例外。 Image.png

言われたとおりに、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();
        }
    }
}

で、実行してみると・・・

Image.png

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

ViewModelLocationProviderのコードを見ると、 Viewの名前に加えて、"ViewModel"が必要みたいです。 今は、 CalcView -> CalcViewModelにしていたのですが、 CalcView -> CalcViewViewModel でないとダメみたいです。

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

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

Image.png

バインドされました! これを使うには、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"をつけないほうが良さそう。