2011年06月01日

MVVM:コードビハインドに記述しても良いと思う

Messengerを理解するために自作してみた(1)-(3)
Messenger+Behaviorを理解するために自作してみた(1)-(3)

でとりあえず、コードビハインドにコードを記述せずに、ViewModelからViewを操作する
方法については理解したつもりだけれど、
それでも、今の僕の経験と知識では、コードビハインドに次のようなコードを書いても、
良いんじゃないかなーと思ってます。


こう主張すると、

「コードビハインドにView以外のロジックが入り込んでしまう」
「コードビハインドとViewModelとの蜜結合が起こってしまう」

という意見が出てくるのはわかっています。
しかし、Messengerを使うやり方よりも、こちらの方が、簡単だし、直観的で理解しやすいと思います。

Commandを実装する必要もないから、誰もがわかる普通のメソッドを定義すれば良いだけです。
そして、(XAMLも含めた)コード量も絶対にこちらの方が少ないと思われるます。
開発効率もそれほど変わらないように思います。

ユニットテストでは、ここでいうExecuteメソッドをテストすれば良いと考えます。
ダイアログ表示などは、UIテストでやると割り切っていいのではないでしょうか。
いままでもそうしてきたのだし、その方が自然だと思います。


ViewModelは、以下の3つをやることに専念して、ユーザとの対話は、コードビハインドで
やれば良いと考えます。

・データバインド
・データ検証
・Modelに記述したロジックの仲介

じゃあ、ダイアログを表示する必要が無い場合は、どうするのか?
これも、イベントハンドラで、ViewModelのメソッド呼べばよいと思います。
あとから、その前後でViewの処理をしたくなったら、イベントハンドラにViewのコード
を追加すればいいだけだから、簡単に対応できます。

ViewModelが起点となるView操作はどうか?
これも、ViewMdelのメソッドから戻り値をもらえばいいだけだから、なにも特別な仕組みはいりません。
場合によっては、デリゲート渡してCallbackしてもらっても良いかもしれません


Viewだけで閉じた処理を記述するのも、もちろん、コードビハインドで書けば良いと思います。
わざわざ、ViewModeを経由する必要はないと思います。 必要ならば、添付ビヘイビアを定義すれば良いです。
ただ、業務ロジックに直結するようなものは、添付ビヘイビアは向かない(と思います)。

上記のようにすれば、

・ViewModelの複雑さが軽減されます。
・XAMLも、若干すっきりします。
・XAMLとコードビハインドと、ViewModelとが、ちょうど良い按配でコードが分散してくれます。


ただ、ボタンの有効無効の制御の一貫性を保つのが面倒というのはあるかもしれませんが、
大した問題ではないと思います。
それに、常に有効にしておいて、ボタンを押したときにチェックしても良いんじゃないかと 思います。
無効にしてしまうと、なぜ無効なのかがユーザに理解されないこともありますし...

異論があるのは十分理解しているけど、これが現時点の僕の考えです。
将来この考えが変わる可能性は大いにあります。
それには、

・MVVMフレームワークがVisula Studioに標準として組み込まれる。
・フレームワークが複雑さが隠ぺいしてくれる。
・MVVMデザイナーがVisual Studioに搭載され、XAMLを直接編集しなくてもよくなる。
・これらにより、そのメリットを実感できる。

といったことが実現した時だと思っています。

もちろん、ViewModelを経由した書き方を否定するものではありませんし、
スキルのある開発グループでは、Command + Messenger + Behavior での
開発を採用すればよいと思います。
  

Posted by gushwell at 20:58Comments(7)TrackBack(0)

2011年05月22日

MVVM:Messenger + Behaviorを理解するために自作してみた(3)

MVVM:Messengerを理解するために自作してみた(1)
MVVM:Messengerを理解するために自作してみた(2)
MVVM:Messengerを理解するために自作してみた(3)
MVVM:Messenger + Behaviorを理解するために自作してみた(1)
MVVM:Messenger + Behaviorを理解するために自作してみた(2)

これまでは、Messengerオブジェクトを利用するときには、Defaultプロパティを使用していましたが、
いわゆるシングルトンオブジェクトと同じなので、ひとつのMessengerオブジェクトが、いくつものViewと
ViewModelの仲介をすることになります。

これだと、不都合な状況というのが生まれる可能性があります。
例えば、複数のViewが同時に表示されている場合を考えると、あるViewModelから、特定のViewにメッセージを
送りたいのに、ほかのViewにもメッセージが送られてしまうことになります。

シングルトンオブジェクトを止めて、ViewModel毎に、Messengerが生成すればいいのですが、
どうやって、このMessengerオブジェクトをビヘイビアで参照したら良いのかが問題になります。

これを解決するのが、データバインドの機能です。
XAMLのビヘイビアを記述する時に、データバインドで、Messengerオブジェクトとビヘイビアを
関連付けてあげればよいのです。

どういったXAMLになるのかは、後で説明するとして、まずは、ViewModelの変更からお見せします。


Messengerプロパティを追加し、コンストラクタで、Messengerのインスタンスを生成し、
このプロパティにセットしています。
HelloAction メソッドでは、Messenger.Default.Send ではなく、Messengerプロパティ経由で、
Sendメソッドを呼び出していいます。

では、XAMLの例をお見せします。


バインドさせているのは次に示す行です。


この Messengerは、ViewModel に定義したプロパティの名前となります。
このSourceObjectは、my:ActionCollection 配下の、各Actionで共有されます。

つまり、この例だと、

DialogBoxMessageというメッセージが、SourceObjectで指定した Messenger から送られてきたら、
DialogBoxActionを起動する

ということになります。

これを実現するには、さらにいくつかのクラスを、変更する必要があります。

まずは、IVewActionインターフェースと、その具象クラスです。


Messenger.Defaultというグローバルなオブジェクトではなく、引数で渡されたMessengerに、
メッセージとその対応する処理を登録するようにしています。

つぎに、ActionCollection クラスです。ここに SourceObject プロパティを追加するために、
継承元をCollection から Freezable に変更しました。
その関係で、Collection もプロパティとして保持するように変更しています。


MessengerBehavior も変更します。ここは、変更が必要無いかなと思ったのですが、
RegisterAll メソッドを呼び出すタイミングを変更する必要があったので、以下のように
変更しました。


Loadedイベントで、RegisterAllメソッドを呼ぶように変更しています。
これで、完成です。

これまでに掲載したコード(過去の一連の記事も含む)をコピーし、ひとつのプロジェクトとすれば
正しくビルドできると思います。興味のある方は、是非、実際に動かしてみることを
お勧めします。

ちなみに、Messengerクラスそのものの変更はしていないので、Defaultプロパティを使った方法でも
コーディング可能です。

ところで、OnActionsPropertyChanged って,このサンプルコードだと2回呼ばれるんですよね。
なんで、1回じゃないんだろう。
  
Posted by gushwell at 23:30Comments(2)TrackBack(0)

2011年05月19日

MVVM:Messenger + Behaviorを理解するために自作してみた(2)

前回の続きです。前回までで、Messenger + Behavior の準備はできました。
つまり、フレームワーク側が出来たということです。

では、この簡易フレームワークをつかって、実際に、ViewModelからメッセージを
送り、Viewを操作してみます。
以下のようなプログラム(以前示したものと同じ)を作成します。


MessengerSample02MessengerSample03



今回は、Viewのコードビハインドには、初期状態のまま、何もコードを追加しません。

■ XAMLの実装

サンプルプログラムのXAMLを示します。


<my:MessengerBehavior.Actions> 要素が、添付ビヘイビアの記述部分です。
通常の添付プロパティのように、Window要素の属性として、


と書きたいところですが、設定するオブジェクトがコレクションであるため、
上記のような記述にしています。

こうすれば、MainWindowクラスのコードビハインドには、何も記述することなく、
ViewModellから、Viewの機能である、MessageBoxを表示できるようになります。

■ ViewModekの実装

ViewModelも掲載しておきましょう。これは、以前示したものと変更はありません。


■ まとめ

ViewModelで、Messenger経由でメッセージを送ると、Messengerが、そのメッセージに
関連付けされたActionを呼び出してくれます。
今回の例では、メッセージとActionの関連付けは、IViewActionの具象クラスが行っています。
そして、XAMLで、MessengerBehavior添付プロパティにActionオブジェクトのコレクションを
指定することで、Viewのコードビヘイビアに何も記述することなく、ViewModelから、
Actionを呼び出せるようになります。

ブラックボックスだった、Messenger + Behavior がどんな仕組みで動いているかの
すこしは理解の助けにはなったのでは、と思います。

僕が作った、Messengerクラスは、グローバルオブジェクトを使い、ViewとViewModel両方から、
簡単にアクセスできるようにしましたが、次回は、データバインドの機能を使い、
グローバルオブジェクトではなく、明示的にインスタンス化したオブジェクトを利用するように
変更してみます。

※言葉だけで理解するのは難しいと思うので、是非、一連の記事で示したコードを、実際に
 Visual Studioで動かしてもらうと、理解が深まると思います。

続く...
  
Posted by gushwell at 22:45Comments(0)TrackBack(0)

2011年05月16日

MVVM:Messenger + Behaviorを理解するために自作してみた(1)

■はじめに

これまで、3回に亘って、Messengerクラスを自作することで、MVVMにおける
Messengerについて理解を深めようとしてきたわけですが、今回は、
「Messenger + Behavior」を使い、ViewModelからViewを操作する方法について考
えてみます。

XAMLにビヘイビアを記述することで、Viewのコードビハインドに、何も記述せずに、
ViewModelからViewを操作できるようになるらしいので、オレオレBehavior を実装して、
これを確かめてみようと思います。

この記事の趣旨は、 MVVMフレームワークがどうやって、Messenger + Behavior を
実現しているかを、理解することなので、当然、Expression Blendに付属している
System.Windows.Interactivity も利用しません。
最低限のものを自作してみます。後処理などは実装していないし、まあ、かなりの手抜きですが...

「Messenger + Behavior」でビヘイビアを利用する目的は、コードビハインドに何も記述することなく、
Viewの動作をXAMLで指定できるようにすることです。
これまでは、Messageを受け取ったViewのコードビハインドで、メッセージに対応するコードを
記述していましたが、これをXAMLで記述できるようにするわけです。
なお、ViewModel側は、前回示したものと変更はありません。Messengerを使って、
メッセージをSendします。

■Actionの実装

まずは、前回までコードビハインドで記述していたMessageBoxの表示をクラスとし
て独立させます。基本的に、このクラスは汎用性のあるものにします。
ここでは、面倒なので、前回までのロジックをそのまま流用します。


IViewAction というインターフェースを定義し、具象クラスの DialogBoxAction
で実装します。
Registerメソッドが呼ばれると、Messageとデリゲートの対応付けを行います。
recipienttとは受取人といった意味があり、Messageを受け取るオブジェクトが渡ります。
通常、WPFの場合は、Windowオブジェクトになります。

この例では、Registerメソッドは、
「recipientがDialogBoxMessageを受け取ったら、ShowMessageメソッドを呼び出す」
ようにお願いているわけです。

このIViewActionの具象クラスには、OpenFileDialogを表示するActionとか、
画面を遷移するActionとかいろんなものが考えられます。

今回は、メッセージとActionを1対1に対応させたため、新たなActionを定義する際は、
それに対応したMessageクラスも定義する必要があります。

Blendに付属しているSystem.Windows.Interactivityでは、この2つも分離し、XAMLで指定できるように
しているようですが、今回は、そこまではやりません。

このIViewActionインターフェースを実装したクラスは、アプリケーションを開発するたびに
定義していたのでは開発効率が悪いですから、あらかじめ汎用的なものを作成しておくのが大前提です。
通常は、フレームワークが用意しておくべきものですね。
足りないものだけを、アプリケーションプログラマーが用意することになるのだと思います。

■ActionCollectionの実装

では、次に、ActionCollectionの定義です。このクラスは、複数の IViewActionを保持で
きるようにするためのものです。


Collection ジェネリッククラスを継承して、独自のコレクションを定義しています。
追加するメソッドは、RegisterAllメソッドです。
このメソッドは、コレクションに格納されている IViewActionオブジェクトのRegisterメソッド(前述したメソッド)を
呼び出します。

このクラスは、XAMLを記述する際に使われます。以下のような記述をします。



■ MessengerBehavior の実装

最後に、上記XAMLにある MessengerBehaviorクラスを実装します。
このクラスは、添付ビヘイビアとして定義します。これは、先ほど定義した
ActionCollentionを保持する添付プロパティ Actions を持っています。


このクラスをビヘイビアと言ってよいのか迷うところです。
このクラスの中では、どこにも、イベントをフックしている箇所はありませんし、
単なる添付プロパティといったほうが良いのかもしれません。

まあ、仕組みを理解することが目的なので、このあたりは深く考えないことにします。

OnActionsPropertyChangedメソッドが呼ばれた時には、IViewActionの具象クラスは、
インスタンス化済みで、そのコレクションが、e.NewValueで参照できます。
このコレクション(ActionCollection) の RegisterAllメソッドを呼び出して、メッセージとActionを
対応付けています。

これで、準備は完了です。

長くなったので、続きはまた今度。

※ここで示したコードは、理解を助けるためのものなので、実際に利用することはお薦めしません。
  
Posted by gushwell at 21:35Comments(0)TrackBack(1)

2011年05月12日

MVVM:Messengerを理解するために自作してみた(3)

それでは、前回作成した Messengerクラスを使い、ViewModelからViewにメッセージを送ってみます。

以下のような簡単なプログラムを作成してみます。

MessengerSample02 MessengerSample03


TextBoxに文字列を入れ、ボタンを押すとダイアログが表示されるという単純なものです。

まずは、Windowクラスです。


Messengerクラスには、Default静的プロパティがあります。このプロパティを使い、
Messengerクラスのインスタンスを取得し、Registerメソッドを呼び出しています。
この例では、DialogBoxMessage と ShowMessage を関連付けしています。

これは、「DialogBoxMessageが送られたら、ShowMessageを呼び出してね」とMessengerクラスに依頼 していることになります。

次に、ViewModelクラスの定義です。ここで、RelayCommand を使っていますが、これも自作すると
記事がながくなるので、http://msdn.microsoft.com/ja-jp/magazine/dd419663.aspx
で紹介されているものをそのまま使います、


Helloコマンドで実際に起動されるメソッドが、HelloActionメソッドです。
ここでMessengerクラスをつかい、DialogBoxMessageのメッセージを送っています。
これで、Viewに定義した ShowMessageメソッドを起動できます。
ShowMessageの結果は、Resultプロパティに格納されますので、これを見て、
その後の処理をしています。

ViewModelから見た場合、どんなダイアログを表示するのかには関与しません。
その実装は、DialogBoxMessageに関連付けされたメソッドの実装次第です。
WPFが標準で用意しているMessageBoxでなくてもかまわないわけです。それは、Viewの仕事になります。
これで、ある種の抽象化が行われていることになります。


最後にXAMLです。


今回は、DialogBoxMessageというメッセージしか定義しませんでしたが、
いろんなメッセージクラスを定義することで、ViewModelからViewを操作することが可能になります。

ところで、「ShowMessageメソッドって、たぶん、どれも同じようなコードになるよね」ってことで、
これを共通化して、添付ビヘイビア内に閉じ込めてしまえば、XAMLに、すこし記述を追加するだけで、
コードビハインドには、何も記述する必要がなくなるはずです。

ということで、次回は、「Messenger + Behavior」の話題に移って行きます。


コードは、GW中に書いたのですが、記事はまだ書きかけ状態中なので、次回は来週かな。   
Posted by gushwell at 23:30Comments(0)TrackBack(0)

2011年05月11日

MVVM:Messengerを理解するために自作してみた(2)

前回の続きです。

Messageクラスが定義できたので、次は、Messengerクラスの定義です。
このMessengerクラスには、2つのメソッドを定義します。ひとつは、Register。
もうひとつは、Send メソッドです。

Registerメソッドは、View側で利用するもので、メッセージの型情報と「デリゲート」を
関連付けるものです。
この「デリゲート」は、ViewModelから、Messengerを経由して間接的に呼び出されます。

Sendメソッドは、ViewModelで利用されます。
このSendメソッドは、引数で渡されたMessageオブジェクトに対応したデリゲートを
呼び出す機能を持ちます。
この対応付けは、先ほどのRegisterメソッドで対応付けしたものです。

では、Messengerクラスの実装をしてみます。


ActionInfoというprivateなクラスを定義し、このオブジェクトを格納するListを用意しています。
Registerメソッドでは、ActionInfoのインスタンスを生成し、そこに情報をセットし、
Addメソッドでリストに追加します。
つまり、「XXXというMessegaeが送られたら、YYYYというデリゲートを呼び出してね」と
お願いするためのメソッドが、Registerメソッドです。
繰り返しますが、Registerメソッドは、Viewが利用するメッドになります。

Sendメソッドは、ViewModelが呼び出すメソッドです。
このメソッドは、引数で渡されたMessageに対応するActionInfoをリストから探し出し、
見つかったActionInfoのDelegateを呼び出しています。

なお、Messengerクラスは、ViewとViewModelの両方から同じインスタンスにアクセスする
必要があるため、Default静的プロパティを用意して、ViewとViewModelの両方から簡単にアクセス
できるようにしています。


これで、Messengerクラスとそれに関連するクラスの定義ができました。

次は、これをViewとViewModelでどのように利用するのかを示そうと思いますが、
続きは、次回に。
  
Posted by gushwell at 22:40Comments(0)TrackBack(0)

2011年05月09日

MVVM:Messengerを理解するために自作してみた(1)

MVVMを勉強していたら、Messengerというものが出てきました。
これって何だろうか。
.NET Frameworkのクラスじゃなくて、MVVM Light Toolなどのフレームワークで
用意されているもので、ViewModelからViewを操作する時に利用するもののようです。
要は、ViewModelとViewの仲を取り持つ仲介者クラスです。

と、ここまでは分かりましたが、それ以上のこと(特にどういう仕組みなのか)が良くわかりません。
そこで、理解を深めるために、MVVM Light Toolの外部インターフェイスを参考に
Messengerクラスをちょっと自作してみました。

とはいっても、ものすごい手抜きなので、これを実際に使うわけにはいかないと思うし、
MVVM Light Toolのソースコードは一切みていないので、僕の理解が間違っている可能性は
ありますが、まあ、Messengerを理解を助けるにはなるんじゃないかと思います。

では、オレオレ Messengerとそれに関連するクラスのコードを示します。

まずは、「メッセージ」の基底クラス。名前は、VmMessageとします。
この「メッセージ」は、MessageBoxを表示するメッセージとか、画面遷移するメッセージなど、
ViewModelからViewへメッセージを送るためのクラスです。ViewModelとView間の
データ受け渡し用としても利用されます。

ViewModelからViewへの通知をする際に、このMessageクラスが使われます。

このクラスは、各Messegeクラスの基底クラスになり、Senderプロパティを持っています。

では、これから派生したDialogBoxMessageを定義してみます。
これは、「MessageBox.Showを呼び出してね」というメッセージを表しています。

本当ならば、MessageBoxOptions や MessageBoxImageなどもプロパティとして用意すべきですが、
今回は省略します。
このクラスは、ViewModelから呼び出されます。たとえば、こんなコードになります。


Messengerクラスを経由して、DialogBoxMessageメッセージをViewに送ることで、
View側で、MessageBoxを表示できるようにしているわけです。

どうして、こんな面倒なことをやるかと言えば、ダイアログを表示するという処理は、Viewの役割であり、
ViewModelから直接呼び出すことは、その役割分担を崩すことになってしまうからです。

そもそも、「ViewModelは、Viewを知らない」が大前提なので、直接Viewのメソッドを呼び出すわけには
いかないという事情があります。

こうすることで、ViewModelとViewの結合度を低くすることができます。
結合度が低ければ、片方の修正が、もう片方に影響を与える度合いが低くなります。

次に、Messengerクラスの実装ですが、続きは、また今度。   
Posted by gushwell at 20:38Comments(0)TrackBack(1)