MVVM パターンで ViewModel から VIEW を操作したいが、疎結合にしたいので VM で VIEW のインスタンスを持つことは避けたいという話をよく聞く、これに関する解決策として以下のようなものがあげられる。
1) IxxxView なるインターフェースを作成して VIEW に実装し VM 作成時に VIEW のインスタンスを IxxxView として渡す。
VIEW の全体ではなく限定された機能の一部を公開するということで VIEWと VM の疎結合は保たれ、IxxxView のスタブを作ることで単体テストも可能になる。
一方で、やってはいけないことだが IxxxView を VIEW のクラスにキャストしてしまえば丸見えになってしまう、VIEW のインスタンスを渡しているので、疎結合という VIEW 提供者の希望は開発の最終段のあわただしさの中で、VIEW・VMの再設計よりも手軽なのでなし崩しにされる可能性がある。
2)VIEW のインスタンスや VM のインスタンスも知っている仲介者を立てて、メッセージのやり取りを行う。
メッセージの受け側(VIEW でも VM でも可能)が仲介者にコールバック関数を登録して、メッセージの送り側が仲介者にコールバック関数を呼ぶことを依頼する。
メッセージの受け側はイベントとして実装することも可能だ。
メッセージなので限定された機能の一部を公開するということになり VIEWと VM の疎結合は保たれ、仲介者にダミーのメッセージの受け側を登録することで単体テストも可能になる。
イベントと同じことなので仲介者は疎結合ではない、非常に便利なのだがきちんと管理しないと混乱のもとになり MVVM パターンを使用したメリットがなくなる。
3)VIEW にコントローラを実装し公開する VM は Binding されたコントローラを介して VIEW を操作する。
限定された機能の一部をコントローラとして公開するので VIEWと VM の疎結合は保たれ、コントローラ のスタブを作ることで単体テストも可能になる。
一方で、VIEW も VM もコントローラに依存する、非常に便利なのだがきちんと管理しないとなんでもコントローラに登録すればいいという方向に流れ、依存性が大きく膨らむ可能性がある。
各方法とも欠点がありこれをこのまま使うのには問題があると考える方もいるだろう。
それでは、WPF には VIEW を操作する機構がないのであろうか?
それは存在し既に WPF のコントロールに組み込まれている。
RoutedUICommnad がそれである。
TextBox は ApplicationCommands.Cut などを受け取って実行するように CommandBinding されている。
RoutedUICommnad を呼ぶのには一つの問題がある、CommandTarget で VIEW を指定しないと、キーボードフォーカスがあるコントロールを起点にイベントと同じようにトンネルとバブルが発生する、通常は問題ないかそのほうが好都合なことが多い。
そうでないケースの場合は CommandTarget を指定する必要ができやはり VIEW のインスタンスを IInputElement という Interface で渡さなければいけない。
1)のケースに帰着するのではないかという意見も出るかもしれない。
しかしそうでないケースの場合として紹介したいのが VIEW に ICommand を作成することだ。
VIEW に Command を実装することが WPF の機構として問題がないのであれば積極的に使うべきである。
VIEW に ICommand を作成して CanExecute Excecute を実装する。
VIEW から VM には Binding を OneWayToSource で通知する。
VM は必要に応じて CanExecute Excecute を実行できる。
MVVM パターンで VM から VIEW を操作するには、VIEW に RoutedUICommnad か ICommand を実装することを推奨する。