基本
TriggerAction
画面遷移は基本的にTriggerActionを使います。その名の通り、何かしらの引き金によって発動するアクションです。TriggerActionを発動させる方法はいくつかありますが、イベントで発動させる方法とViewModelから発動させる方法を覚えておけば困らないでしょう。前者はEventTrigger(System.Windows.Interactivityの機能)、後者はMessenger(Livetの機能)を使います。
EventTrigger
こちらは、例えばダイアログ表示ではなくても、Livetのメソッド直接バインディング機能などを使うときに多用することになるでしょう。<Button Content="Push me" VerticalAlignment="Center" HorizontalAlignment="Center" Width="100" Height="30" > <i:Interaction.Triggers> <i:EventTrigger EventName="Click"> <l:LivetCallMethodAction MethodTarget="{Binding}" MethodName="ButtonPushed" /> </i:EventTrigger> </i:Interaction.Triggers> </Button>
Messenger
LivetにはMessengerという機能があります。ViewModelはMessengerというインスタンスを持っており、MessengerでInteractionMessageクラスのメッセージを送信すると、それをViewで受け取れる(=TriggerActionとして動作する)というものです。標準のWPFにはこのようにViewModelからViewに何かを働きかける機能がかなり少なく、Livet以外のMVVMライブラリでもたいてい似たような機能は実装されているようです。
<i:Interaction.Triggers> <l:InteractionMessageTrigger Messenger="{Binding Messenger}" MessageKey="MethodCaller"> <l:LivetCallMethodAction MethodTarget="{Binding}" MethodName="MessageRaised" /> </l:InteractionMessageTrigger> </i:Interaction.Triggers>
ちなみに、ViewModelではこのように呼び出します。
Messenger.Raise(new InteractionMessage("MethodCaller"));
InteractionMessage
上記のMessengerの項でも少し出てきましたが、MessengerとはViewModelからViewに情報を受け渡すものです。上記の例ではRaiseしかしていないのでMessengerKeyしか渡しませんでしたが、例えばダイアログボックスならば表示テキストやキャプション、アイコンの種類などを伝える必要があるでしょう。そのような場合は、InteractionMessageを継承した派生クラスを作ってパラメーターを持たせるような仕様になっています。それに対応して、TriggerActionの派生クラスであるInteractionMessageActionを継承した派生クラスも作成し、そこで独自のInteractionMessageを受け取れば良いです。Messengerの例
というわけで、まずはMessengerを使ってダイアログボックスを表示する例を示します。<i:Interaction.Triggers> <l:InteractionMessageTrigger Messenger="{Binding Messenger}" MessageKey="MessageBox"> <l:InformationDialogInteractionMessageAction /> </l:InteractionMessageTrigger> </i:Interaction.Triggers>
Messenger.Raise(new InformationMessage("Message Text", "Caption", MessageBoxImage.Information, "MessageBox"));
InformataionDialogInteractionMessageActionは単一ボタン(=OKボタンのみ)のメッセージボックスを表示するTriggerActionです。
これに対応するInteractionMessageはInformationMessageで、これにメッセージ内容、キャプション内容、アイコン、メッセージキーを含めてMessengerに乗せることでダイアログボックスを表示させることができます。
EventTriggerの例
ところで、ボタンが押されたらそのままメッセージボックスを表示したくなることがあります。ですが、上記のMessengerの例を見ているとViewModelからViewにメッセージを送る必要があるので、ボタン(View)—(メソッド呼び出し)→Messenger発動(ViewModel)—(Messenger)→ダイアログ表示(View)といった具合にViewとViewModelを往復する必要が出てきてしまいます。これではあまりスマートではありません。ですがそこは流石Livet、ちゃんと方法が用意されています。
InteractionMessageActionクラスにはDirectInteractionMessageプロパティが用意されており、ここに直接InteractionMessageを渡してあげればいい…と思いきや、ここに渡すのはDirectInteractionMessageクラスです。これについては細かくは後述します。
DirectInteractionMessageにはMessageプロパティがあり、こちらにInteractionMessageを渡せばいいです。
さらに、それぞれのクラスのDirectInteractionMessageプロパティ、MessageプロパティはContentPropertyに設定されているので、プロパティを明示しなくても設定できるのでネストが深くなるのも最小限に抑えることができます。
<Button Content="Push me" VerticalAlignment="Center" HorizontalAlignment="Center" Width="100" Height="30" > <i:Interaction.Triggers> <i:EventTrigger EventName="Click" > <l:InformationDialogInteractionMessageAction> <l:DirectInteractionMessage> <l:InformationMessage Caption="Caption" Text="Message text" Image="Information" /> </l:DirectInteractionMessage> </l:InformationDialogInteractionMessageAction> </i:EventTrigger> </i:Interaction.Triggers> </Button>
ダイアログの戻り値
さて、上記は単一ボタンのダイアログを表示していたので戻り値は特に必要ありませんでした。ですが、例えば「はい」「いいえ」で聞くダイアログなどは戻り値を知る必要があります。Messengerの例
実はこの戻り値はMessengerが持つようにできています。ですので、Messengerの例は特に上記の例とほぼ同じコードになります。<i:Interaction.Triggers> <l:InteractionMessageTrigger Messenger="{Binding Messenger}" MessageKey="MessageBox"> <l:ConfirmationDialogInteractionMessageAction /> </l:InteractionMessageTrigger> </i:Interaction.Triggers>
var msg = new ConfirmationMessage("Is this a MessageBox?", "Caption", MessageBoxImage.Question, MessageBoxButton.YesNoCancel, "MessageBox"); Messenger.Raise(msg); if(msg.Response == null) { // Cancel clicked } if(msg.Response.Value) { // Yes clicked } else { // No clicked }
EventTriggerの例
さて、ここでまた問題が発生します。当然、ダイアログの戻り値はソフトウェアの制御側(=ViewModel)で受け取りたいですが、EventTriggerで直接TriggerActionを呼び出してしまった場合、ViewModelは介在しないのでそのMessageを受け取るところがありません。ですがそこは流石Livet、ちゃんと方法が用意されています。
先ほど無駄に挟んでいたように見えたDirectInteractionMessageクラスですが、実は答えはここにあります。これがCallbackMethodTarget/CallbackMethodNameプロパティ及びCallbackCommandプロパティを持っていて、ここでViewModelのメソッドやコマンドをバインディングすることによってViewModelが戻り値を受け取ることができるようになります。
<Button Content="Push me" VerticalAlignment="Center" HorizontalAlignment="Center" Width="100" Height="30" > <i:Interaction.Triggers> <i:EventTrigger EventName="Click" > <l:ConfirmationDialogInteractionMessageAction> <l:DirectInteractionMessage CallbackMethodTarget="{Binding}" CallbackMethodName="MessageBoxClosed"> <l:ConfirmationMessage Caption="Is this a MessageBox?" Text="Message text" Image="Question" Button="YesNoCancel" /> </l:DirectInteractionMessage> </l:ConfirmationDialogInteractionMessageAction> </i:EventTrigger> </i:Interaction.Triggers> </Button>
public void MessageBoxClosed(ConfirmationMessage msg) { if(msg.Response == null) { // Cancel clicked } if(msg.Response.Value) { // Yes clicked } else { // No clicked } }
クラス名対応表
さて、Messaengerとかはもうわかっているからいいよという人はここだけ見てください。用途 | TriggerAction | InteractionMessage | 備考 |
---|---|---|---|
メッセージボックス(単一ボタン) | InformationDialogInteractionMessageAction | InformationMessage | |
メッセージボックス(複数ボタン) | ConfirmationDialogInteractionMessageAction | ConfirmationMessage | |
ファイルを開くダイアログ | OpenFileDialogInteractionMessageAction | OpeningFileSelectionMessage | |
ファイルを保存ダイアログ | SaveFileDialogInteractionMessageAction | SavingFileSelectionMessage | |
フォルダ選択ダイアログ | FolderBrowserDialogInteractionMessageAction | FolderSelectionMessage | Livet.Extensionsが必要 |
任意のウィンドウ | TransitionInteractionMessageAction | TransitionMessage |
最後の任意のウィンドウ表示はなかなかに曲者かと思います。特に、これで開くウィンドウに対応するViewModelをどうしたらいいのか、というところでとても悩むことになるかと思います。
まあその辺はまたの機会に。
0 件のコメント:
コメントを投稿