お手軽MVVMクッキング

前回エントリに引き続きMVVMをもう少し勉強していました。
Windows FormsでMVVMをやるぜ
この記事は、「MVVMの概念はだいたいわかったけど、実際どうコードにしていくの?」という人向けのレシピ的な雑文です。

1.Visual StudioWPFアプリケーションを作る
ツールは新しいほうが便利なのでVisual Studio 2013を。Express版もあります。
http://www.microsoft.com/visualstudio/jpn/downloads#d-2013-express

2.イベントハンドラは基本的に使わない
VBC#ではボタンのClickイベントに button1_Click() のようなイベントハンドラを対応付けていました。
WPFでもデザイン画面でボタンをダブルクリックすると自動でイベントハンドラを作ってくれますが、MVVMでは基本的にイベントハンドラを使いません。
画面の処理は画面クラス(MainWindow.xaml.cs)ではなく、ビューモデルというクラスに記述されます。

3.Microsoft.TeamFoundation.MVVM名前空間を参照する
一昔前は、MVVMはまだ設計方針のレベルでフレームワークのように整備されていなかったため、必要なクラスは自作するのが基本でした。
今はMicrosoftがMVVM向けの便利機能を用意してくれているのでそれを使うっきゃないでしょう。
プロジェクトの参照設定を右クリックして「参照の追加」を押し、「Microsoft.TeamFoundation.Controls」にチェックをつけます。
これでMicrosoft.TeamFoundation.MVVM名前空間が使用可能になりました。

3.ビューモデルを作る
Microsoft.TeamFoundation.MVVM.ViewModelBaseを継承したViewModelというクラスを作りましょう。
中身はコンストラクタと、いくつかのプロパティを持っています。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.TeamFoundation.MVVM;

namespace MvvmTest1
{
    class ViewModel : ViewModelBase
    {
        public ViewModel()
        {
            Name = "えむぶいぶいえむ";
        }

        #region プロパティ

        public String Name { get; set; }

        #endregion

    }
}


4.画面を作る
デザイナ画面で画面をつくります。
いきなり高度な画面を作ろうとすると挫折するので(^^;、シンプルにテキストボックス1個とボタン1個があればいいです。
MainWindow.xaml.csのコンストラクタに以下の一文を書き足してください。
これで画面(V)とビューモデル(VM)が結びつきます。

        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = new ViewModel();    //<==== これな
        }

XAMLエディタでテキストボックスのText属性を以下のように書き換えます。
これでテキストボックスのテキストにViewModel.Nameがバインドされたことになります。

<TextBox Width="100" Text="{Binding Name}" />
                    ~~~~~~~~~~~~~~~~~~~~~~

起動してみると、テキストボックスに「えむぶいぶいえむ」と表示されたでしょうか?

5.コマンドを作る
ビューモデルは結果出力用にいくつかの「プロパティ」を持っていますが、入力用にはいくつかの「コマンド」を持ちます。
ビューモデルのコマンドを外からたたくと、ビューモデルの中で処理をしてプロパティが書き換わり、それがデータバインディングで画面に反映される、という寸法です。
コマンドはViewModel内に以下のように実装します。

        #region コマンド

        ICommand _DoRename;
        public ICommand DoRename
        {
            get
            {
                return _DoRename != null ? _DoRename : _DoRename = new RelayCommand(delegate(object parameter)
                    {
                        Name = "MVVM";
                        RaisePropertyChanged("Name");
                    });
            }
        }

        #endregion

コマンドはICommand型のプロパティにすぎません。
本当はコマンド一つごとにクラスを一つつくる必要があるのですが、デリゲートを渡すとコマンドを作ってくれるRelayCommandという便利機能をMicrosoftが用意してくれているのでそれを使います。(ネットのサンプルではDelegateCommandと呼ばれたりもします)
↓こうすりゃいいじゃん、と思うかもしれませんが、そうするとデリゲート内でstaticでないプロパティを参照できないのでこうしています。

public ICommand DoRename = new RelayCommand(...);

RaisePropertyChanged()はViewModelBaseが提供するメソッドで、プロパティが変更されたことを外部に通知してくれます。これがあるからデータバインディングした画面が自動で反映される、超重要な処理です。
XAMLエディタでボタンのCommand属性を以下のように書き換えます。
これでボタンにViewModel.DoRenameがバインドされたことになります。

<Button Content="Button" ... Command="{Binding DoRename}" />
                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

ボタンを押すと、テキストボックスの値が変化したでしょうか?

自分の勉強した範囲はここまでです。
あとはWPFをさらに使い倒したり、ビューモデルからモデル(M)にアクセスしてファイルやDBを読み書きしてみたり、好きにしてください。
MVVMの利点として、画面と処理が明確に分離されているため分業しやすい、またビューモデルは所詮いくつかのプロパティを持つだけのクラスなのでユニットテストがしやすい、などがあるらしいですが知りません。
ボタンを押したとき確認ダイアログを出したい、というときなど「はて、どうすんべ?」と立ち止まったりするでしょう。そういうときはイベントハンドラを解禁するのもよいかもしれません。
あとFormsにあるけどWPFにないコントロールもあるので(プロパティグリッドとか)気を付けましょう。プロパティグリッドは「Extended WPF Toolkit」という無料ライブラリを使うと幸せになれます。
Extended WPF Toolkit
こちらからは以上です。