ViewModelからViewへのメッセージング手法

ダイアログ・アニメーション・画面遷移
2010/11/6
第60回codeseek勉強会
第2回日本C#ユーザー会勉強会
尾上 雅則(@ugaya40)
自己紹介
 尾上 雅則(おのうえ まさのり)
 フリーランスだったり、会社があったり。
 C#er
 はじめて覚えたプログラミング言語 = C#1.0
 もともとはサーバ側専門。たまにスマクラ Windows Forms
 2009/6月ごろWPFに目覚め、パッケージをリリース。イベント
駆動で開発した所、非常に苦労し、以後ほぼ一年間MVVMに夢中。
 VB.NET少々
 Silverlightってなんでしたっけ?
 Twitter : @ugaya40
 Blog : the sea of fertility – http://ugaya40.net
 Facebook : http://www.facebook.com/ugaya40
なぜこれらは難しい?
どういう時に、ViewModelからViewを触り
たくなる?
 代表的なパターン
 ダイアログ
 アニメーション
 画面遷移
ダイアログ
 ViewModelでMessageBox.Showする?
 自動単体テストが・・・
 ViewのコードビハインドからMessageBox.Showす
る
 コードビハインド・・・
 FileDialog系(Save/Open)はどうしよう?
 戻り値が必要なパターン
ダイアログ
 実は2種類ある。
 Viewが起点となって表示するダイアログ

実行確認系とか。
 ViewModelが起点となって表示するダイアログ

エラーダイアログ・終了前の保存確認とか
アニメーション
 ICommandの実行前後にアニメーションを挟みたい
場合が多々ある。
 コレクションの増減時にもアニメーションを挟みた
い場面が多々ある。
 View側で完結して欲しい処理
 ViewModelには関わらせたくない
 戻り値をViewModel側で処理したりする必要がない
画面遷移
 実は最大の鬼門
 パターンがたくさんある
 モーダルダイアログとしての遷移
 新しいウィンドウを別に表示
 Webページ的な遷移
 ナビゲーション

こいつはプログラミングモデルからして異なる。
共通する問題
 いずれも、イベント駆動開発ではコードビハインドで処
理してきたパターン
 イベント駆動開発をしていると、コードビハインドは
WPF/SLでは今までより増えるんです!


Windows Formsより、WPFを選んだのは、UIの表現力が増した
から!
ASP.NETより、Silverlightを選ぶのは、WEBよりリッチなUIを構
築したかったから!
 イベント駆動開発では、UIの見かけのみに関わるコードがコー
ドビハインドに爆発!
 イベント発生順依存のコードを保守するのは大変
 BlendだけでUIを開発できないのも痛い
 自動単体テストはどうしましょう?
 UI Automation系での自動単体テストは結局いつも実現さ
れてませんでした
考えられてきた対処法もろもろ
みんなの努力
 ViewModelからViewを操作したくなるシナリオで
は世界中の人がいろんな手法を考えてきました。
 代表的な手法
 インターフェース
 Viewにコマンド
 Messenger
みんなの努力1 – インターフェース
 Viewは特定のインターフェースを実装する
 ViewModelはViewのインスタンスを、インターフェースの実装とし
て持つ
 ViewModelはViewのコードビハインドに定義されたメソッドを、イ
ンターフェース経由で呼んでやるイメージ。
プロジェクト: 1-InterfaceView
みんなの努力1 – インターフェース
 長所
 一応疎結合は維持(たぶん。きっと。)
 インターフェースを実装したViewのスタブオブジェ
クトを使えば、自動単体テストも可能。
 短所
 ViewModelにて、インターフェースをViewのインス
タンスにキャストする事でViewを直接いぢり放題


きちんと管理しなければ、MVVMで開発している意味がな
くなる
「きちんと管理」がそもそもできるのなら、そもそも設計
パターンを使用する意味は・・・
 コードビハインドがある。
みんなの努力2 – Viewにコマンド
 Viewが、コードビハインドにてICommandを実装する
 ViewのXAMLにて、ViewModelのプロパティにView側で定義さ
れたICommandを渡してやる
 ViewModelは渡されたコマンドをExecute!
ダイアログ表示ロジック
などはここに記述
コードビハインドで定義した
ダイアログ表示用コマンド
Viewで定義された
コマンドを格納するプロパティ
ViewのXAMLで、ViewModelのプロパティにViewのコマンドを渡す
ViewModelは、渡されたコマンドを処理の中で自由に実行すれば良い。
Interfaceの時と同じことを、ViewModelがViewのインスタンスを持つこ
となく実現している
プロジェクト:2-ReverseCommand
みんなの努力2 – Viewにコマンド
 長所
 バインディング機構だけで疎結合を実現しているのは
素敵
 単体テスト問題なし
 短所
 コードビハインドがある
 ViewModel側がコマンドで乱雑になる
 戻り値を受け取るのが大変
みんなの努力3 – Messenger
 MVVM Light Toolkit
http://www.galasoft.ch/mvvm/getstarted/
 Messengerというアプローチ
 ViewModelは、Viewに何らかのアクションを起こし
て欲しいタイミングでメッセージ(実体はイベント)を
発信する。
コールバックは
Viewがメッセージに対応するアクションを終えた後に実行される
みんなの努力3 – Messenger
 Viewはイベントを監視し、送られてきたメッセージ
に対応するアクションを実装する
イベントの解除などは、補助ライブラリ側で責任を持つ必要がある
プロジェクト:3-SimpleMessenger
MVVM Light Toolkitを使って実装しました!
みんなの努力3 – Messenger
 長所
 一応疎結合は維持(たぶん。きっと。)
 単体テスト問題なし
 受け入れられている

最近ではWindows Phone 7 のサンプルが良くMVVM
Light Toolkitを使用して作られていた
 短所
 乱用しやすい

使ってみるとわかるが、ただのイベントの仕組みをラップ
しただけの仕組みのため、「きちんと管理」しないと
MVVMを使う意味が・・。
 「何も考えずに使うと」コードビハインドがある。
みんなの努力 – 共通した問題
 コードビハインドがある
 BlendだけでViewが作れない
 結局管理が難しい
 「きちんと管理」しないといけない=略
 やっぱり微妙にクールじゃない
XAMLで書けない部分を、C#で書く。
でもそれはコードビハインドに書くわけではないのです。
コードビハインドの抹殺 - ビヘイビア・トリ
ガー・アクション
 ビヘイビア・トリガー・アクションを用いる事で、
コードビハインドの抹殺が可能!
 ビヘイビア
 なんらからのイベント・ステートに基づいて、オブ
ジェクトの状態を遷移させるのが主な役割
 トリガー&アクション
 任意のトリガーを起点として、任意のアクションを実
行させられる。
 (インフラが整っていれば)C#どころか、XAMLを手
で一行も書かなくてもBlendのGUIで設定可能!
コードビハインドの抹殺 – ビヘイビア
 ビヘイビアとは、添付プロパティの仕組みを使って、
オブジェクト自体が持たない状態と動作を、オブ
ジェクトの外部から追加定義するもの。
コードビハインド抹殺 – ビヘイビア
 実はビヘイビアと言われるものは2種類ある
 .NET Frameworkだけで作成できるビヘイビア


通称 添付ビヘイビア(Attached Behavior)
実装に時間がかかる
 Blend SDK 付属アセンブリを使用するビヘイビア




ただビヘイビアと呼称
Blend SDKアセンブリ =
System.Windows.Interactivity.dll
基底クラスが定義されていて、実装が楽。
System.Windows.Interactivity.dllにあたるものを自前実
装する事もできるが、今後の標準化の方向などを考えると
無駄かと。
プロジェクト:4-SimpleBehaviors
@okazuki さんのブログの素敵なサンプルをぱくらせていただきました
http://d.hatena.ne.jp/okazuki/20100906/1283778339
コードビハインドの抹殺 – トリガー&アク
ション
 Trigger
 言葉通り。後述するアクションのトリガーとなる
 Action
 任意のトリガーによって起動されるアクション
Blend 定義済Trigger & Action
ChangePropertyAction
EventTrigger
DataTrigger
KeyTrigger
PropertyChangedTrigger
InvokeCommandAction
StoryboardCompletedTrigger
TimerTrigger
GotoStateAction
PlaySoundAction
LaunchUriOrFileAction
プロジェクト:SampleTriggerAndAction
コードビハインドの抹殺
 ビヘイビア & トリガーとアクションの使用はBlend
SDKが前提
 Blend SDKは、Expression Blendを持っていなくて
も単体で導入が可能!
 .NET Frameworkだけで使おうとすると
 EventTrigger以外のトリガーはスタイルやテンプレー
ト以外では使えない
 トリガーもアクションも独自定義できない

基底クラスがinternal コンストラクタ
(TriggerBase/TriggerAction)
 ビヘイビアの実装が非常に大変
Blend SDK
 Blend SDKを使うと.NET Frameworkだけでは不可能な、
トリガー&アクションの独自定義が可能
 しかも実装はむちゃくちゃ簡単です。詳しくはBlend SDK
のヘルプ参照
 Blend SDKには最初から膨大な数のビヘイビア・トリ
ガー・アクションが定義されている
 BlendでUIの開発を行っていると、標準で利用可能
 Blend SDKのライブラリは明示的にプロジェクトで参照
しなくても、Blendでプロジェクトを読み込むだけで勝
手に参照が追加される。
 Blend SDK を使用しないMVVMなど個人的には絵空事。
ビヘイビア・トリガー・アクションで出来る
事
 Viewが起点となる動作や、ViewModelのプロパ
ティの変化が起点となる動作ならば、結論からいえ
ば何でもできます
 UI開発者はBlend上で、一切のコードを書かず
(XAML含め)ビヘイビア・トリガー・アクションの
引数を指定可能。
ビヘイビア・トリガー・アクションで出来る
事
 なんでもできる、けど、だからこそ・・・
 開発者みんなが自由に作ったらやっぱり大変な事に・・。
 ビヘイビア・トリガー・アクションは定義済のものとして
インフラに組み込んでしまうべき。
 都合の良い事に、ビヘイビア・トリガー・アクションはビ
ジネスロジックのドメインと結合しにくい

つまり、再利用が容易
 ビヘイビア・トリガー・アクションは画面ごとに定義が必
要なものではありません!
Model呼び出しを伴わない様な処理はすべて処理できます。
コードビハインドの抹殺 – まとめ
 ビヘイビア・トリガー・アクションを使用する事で、
Model呼び出しを伴わないような処理は全てコード
ビハインドを使わずに処理できる。
 ViewModelのプロパティの変化を監視して、Viewを
遷移させることなども可能。
 Blend SDK使用が前提
 しかし戻り値を返せないため、ViewModelを介して
Modelと連携するような処理は苦手・・・
ViewModelを介して、Modelと連携するような処理はどうす
るか?
ViewModel起点アクションの処理
 MVVM Light ToolkitのMessengerを思い出してくだ
さい!
 Messengerがコードビハインドを使用するのは、メッ
セージに対応するアクションを記述する時だったはず
 もしメッセージに対応するアクションが、全てビヘイ
ビア・アクションに移動すると・・
Pattern & Practice Prismの採用方式です!
プロジェクト:MessagingAndBehaviors
Pattern & Practice Prismが採用している手法なので
Prismで実装してみました。
Messenger And Behaviors
 すいません。パターン名はオレオレです。
 ViewModelからMessageを送信(イベント発行)する
コードはほぼ一緒
Messenger And Behaviors
 しかしメッセージに対応するアクションはPrismでは
XAML(トリガー & アクション)で書きます。
 MVVM Lightの例ではコードビハインドに書いていました。
Messenger And Behaviors
 Messageに対応するアクションをTriggerActionに
移動する事でもう一つ利点が生まれました
 ViewModelは純粋な意味でメッセージを発信し、
Viewはメッセージに対応する好きな実装を
GUI(Blend)で選ぶ事が出来ます!
InteractionRequestTrigger
ChangePropertyAction
ダイアログを表示するアクション
GotoStateAction
好きなActionを選べます
InvokeCommandAction
バルーンを表示するアクション
Messenger And Behaviors
 今までのパターンのすべての長所を備え、かつ短所が少ない。
 現在・今後も主流となるように思えます!
 Pattern & Practice Prismも採用

これが最大の根拠
 MVVM Light Toolkitも、実はこの形を意識した実装がされてい
ます

しかし同時に、コードビハインドも使用できるよう設計されている
 Cinch / Caliburnなどの他のメジャーなMVVMライブラリも採
用しています
 現在のPrismは不完全なので注意が必要
実際の所、パターンの使い分け
ダイアログはどうなる?
 View起点ダイアログの場合
 トリガー&トリガーアクションを用いる事で、全て
コードビハインドなしの解決が可能
 ViewModel起点ダイアログの場合
 エラーダイアログや対応確認ダイアログの場合



ビヘイビア・トリガー・アクション用にいちいちプロパ
ティを用意する?
ビヘイビア・トリガー・アクションだけでは、まだまだ強
引な実装
だからこそ、Messenger
And Behavior
プロジェクト:7-DialogSample
アニメーションはどうなる?
 XAMLのみで完結できるアニメーションとビヘイビ
ア、トリガー、アクションの相性は抜群
 コードビハインドなしで、アニメーションを処理の
合間に挟み込める
プロジェクト:8-AnimationSample
画面遷移はどうなる
 基本的にはMessenger And Behaviorを使います。
 実はTODOです.
 引数を新規画面に引き渡す場合などを考慮すると、
Messenger And Behaviorを使うのがまず妥当に見えます
 しかし実際に表示するViewのクラスは、


VMが指示するんでしょうか?
メッセージを受け取ったViewが指定するんでしょうか?
 とりえあずダイアログと同じように実装は可能です.

Messageの中に、新規画面のViewModelを含める事にはなるで
しょう
悩ましい所はどんな基準で実装する?
まとめ。
MVVMでの実装に悩む時
 まずは問題となる機能が、表示のみの機能なのか、
Model呼び出しに影響を与える機能なのかを考える
 表示のみの機能
 例)


アニメーション
実行通知ダイアログなど
 → Blendだけで構築できるのが望ましい
 Model呼び出しに影響を与えうる機能
 例)


FileDialo系などの、戻り値をビジネスロジックで処理する必要の
あるダイアログ
画面遷移など
 →ViewModel経由、Modelとの連携が不可欠
表示のみの機能の実装指針
 ビヘイビア・トリガー・アクションで解決する
 典型的なパターンを一度インフラとして実装してしま
えば、新たにトリガー・アクションを実装する必要の
ある場合などほとんどない

ビヘイビア・トリガー・アクションは基本的にビジネスロ
ジックのドメインと非結合なため、インフラとして全社単
位・あるいはフレームワーク単位での管理が容易
 インフラ(ビヘイビア・トリガー・アクション)が
整っていれば・・・
 BlendだけでUI(View)の開発をする事が可能になる
 コードを触る必要がなくなる!
Model呼び出しに影響を与えうる機能の実装
指針
 Messenger And Behaviorsで解決する
 現在あるパターンの中で最も短所が少なく、長所が多
い方法
 こちらもインフラ化しやすい
 インフラが整っていれば・・・
 View側は、ViewModelから発されたメッセージに応
じて自由な表現をBlendだけで実現できる
MVVMでの実装は面倒(マゾい)?
 DelegateCommmandやRelayCommand、ビヘイ
ビア・トリガー・アクションはビジネスロジックの
ドメインに結合していません。
 インフラ化が容易という事。標準ライブラリで採用さ
れれば、まったく面倒ではなくなる。
 Blendの使用が前提ならば、むしろイベント駆動開発
よりはるかに楽です!
MVVMでの実装は面倒(マゾい)?
 ViewModelの記述が冗長?
 ViewModelは自動生成させましょう。

ViewModelを自動生成させるために、Modelにすべてのビ
ジネスロジックを載せましょう。
単純なデータ型のModel×。

詳しくは伊藤さんのセッションで!

 なによりもBlend使わなきゃ、WPF/SLの存在その
ものがマゾい
MVVMは業務ではまだ早い?
 はい。インフラが整備・統一されていない現状では
業務に採用するにはまだ早いです。
 DelegateCommandとRelayCommandすら統一され
ていない。

機能的には同じなのに。
 今後はPrismに合わせていくのが無難かも。
 Message And Behaviorsは各種メジャーMVVM補助
ライブラリではほぼ同じ形で実装されています

しかし統一されていないのです
私見 MVVMの将来
 私見ではMVVMはASP.NET MVCをWPF/SLの世界に持っ
てこようとしたもの。
 でもWEBシステムそのもののシンプルなアーキテクチャ上
の制約がWPF/SLにはない。

だからそのまま持ってこれなかった。
 Pattern & Practice
PrismはMVVMインフラにもなろう
としている。
 将来的には標準ライブラリに取り込まれるのでは?
 既存スキル・規模との兼ね合いを考えると、ASP.NETと
ASP.NET MVCみたいに、WPF(SL)とWPF(SL) MVVMみ
たいにテンプレートが別れていくのでは?