ViewModel

Silverlight4の制限はあるものの、MVVMの実装において重要な責務を担うコマンドバインディングが実現できることが解ったので、次はデータバインディングが実現できるかどうか検証してみよう。
といってもデータバインディング機構そのものはWindowsPhone7でも積極的に使用されており、これがあるからこそMVVMで作ろうということになる訳だ。

ViewModel

ViewModelはMVVMの中核となるコンポーネントであり、アプリケーションのプレゼンテーションロジックを担当する。
こんな重要なViewModelだが構造自体はそれ程複雑ではなく、その殆どは以下の機能を実装することになる。

    • ビュー(画面)とバインディングするデータを内部に持つ
    • 要件/非要件ロジックを起動するコマンドを持ち公開する
    • モデル、ビュー双方のデータを同期する
    • アプリケーションの状態を管理する
    • データの妥当性を検証する(バリデーション)

このうち幾つかの機能は.NET/Silverlightが自動/半自動勝手にやってくれるため、私たちは個々の仕様に合わせた部分の実装を行えば良い訳だ。

ViewModelの実装

WPF/Silverlight/WindowsPhone7で使うことのできるViewModelの実装はたくさんあって、どれを使えばよく分らない。逆に言えばどれもその本質部分は変わらないということでもある。

代表的なViewModelの実装の例(Prism4.0より)

    public class ViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void RaisePropertyChanged(Expression> propertyExpresssion)
        {
            var propertyName = PropertySupport.ExtractPropertyName(propertyExpresssion);
            this.RaisePropertyChanged(propertyName);
        }
        protected virtual void RaisePropertyChanged(string propertyName)
        {
            var handler = this.PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        protected void ExecuteOnUIThread(Action action)
        {
            var dispatcher = Deployment.Current.Dispatcher;

            if (dispatcher.CheckAccess())
            {
                action();
            }
            else
            {
                dispatcher.BeginInvoke(action);
            }
        }
    }

これは基本クラスであり、実際には仕様に合わせてプロパティ、コマンドを追加した継承クラスを作るのが普通だ。

ポイントはRaisePropertyChanged(string)を起動するために、実際にメンバから呼ぶであろうRaisePropertyChanged(Expression>)だ。 Prism4が提供するこのユーティリティクラスのおかけで、今までは

RaisePropertyChanged("FooProperty");

とプロパティの名前を指定して通知していたのが、

RaisePropertyChanged(() => FooProperty);

等と式で型を記述できるようになることだ。型を直接指定することでリファクタリングの対象となる訳で個別に修正する必要がなくなる。素晴らしい。

ちなみに同クラスのExecuteOnUIThreadメソッドは非同期で処理を実行するために有用なメソッドである。Silverlightも他の.NETプラットホームと同様にUIスレッドは同期しなくてはならないため、他のスレッドからUIにアクセスするような処理はこのメソッドを介する必要がある。(AndroidプラットホームのrunOnUIthreadと同じ用途だ)