2011年05月22日

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

   Clip to Evernote   このエントリーをはてなブックマークに追加      
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)

   Clip to Evernote   このエントリーをはてなブックマークに追加      
前回の続きです。前回までで、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)

   Clip to Evernote   このエントリーをはてなブックマークに追加      
■はじめに

これまで、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)

   Clip to Evernote   このエントリーをはてなブックマークに追加      
それでは、前回作成した 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)

   Clip to Evernote   このエントリーをはてなブックマークに追加      
前回の続きです。

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)

   Clip to Evernote   このエントリーをはてなブックマークに追加      
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)

2011年04月25日

WPF:マウスカーソルが、特定のコントロールの上にあるかどうかを知る

   Clip to Evernote   このエントリーをはてなブックマークに追加      
WPFで、マウスカーソルが、特定のコントロールの上にあるかどうかを知る方法がわからなくてかなり悩みました。

MSDNライブラリでいろいろと調べて、VisualTreeHelper.HitTestを使えばよいことは分かったのですが、
何も表示されていないImageコントロール上に、マウスカーソルがあるかどうかをHitTestで調べようとしましたが、上手くいきません。
何も表示されていない=そこに存在していないと同じなのだから、
操作はできないということなのだと思います。

そういえば、前にも似たようなことで悩んだ記憶があります。

結局、Borderの子要素として、Imageを配置し、Borderの色をWhiteに設定すれば、
HitTesをが利用できることがわかりました。


でも、ちょっといまいちな方法だなー

それと、このメソッド Silverlightには無いんですね...
代わりに、FindElementsInHostCoordinates() メソッド が使えそうですが、 試していません。
FindElementsInHostCoordinates()メソッドは、WPFにはありません...   
Posted by gushwell at 23:00Comments(2)TrackBack(0)