2020年5月4日月曜日

WPFでViewModelからWindowをCloseさせる

View層からのCloseは、this.Close()とかでOKだけど、WPFなのでViewModel層から画面を閉じたいことって結構あります。
今回は「Windowがアクティブじゃない場合」についても考慮しています。

1.引数としてWindowを受け取る

コマンドのバインド時にCommandParameterにWindowを渡します。
例)画面Load時のイベントにWindowを渡す場合

<i:Interaction.Triggers>
  <i:EventTrigger EventName="Loaded">
  <i:InvokeCommandAction
      Command="{Binding LoadedCommand}"
      CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"/>
  </i:EventTrigger>
</i:Interaction.Triggers>


ViewModelは
private void Loaded(Window window)
{
  ・・・
  window.close()
}


のように画面を閉じることができます。
個人的にはViewModelでControl触るのはあまり好きじゃないなぁ

2.Closeメッセージを送る

こっちの方がMVVMぽいかな。Livetを使用しています。
XAML側にCLOSEメッセージ受信用のトリガーを書きます

<i:Interaction.Triggers>
  <l:InteractionMessageTrigger MessageKey="Close" Messenger="{Binding Messenger, Mode=OneWay}">
  <l:WindowInteractionMessageAction InvokeActionOnlyWhenWindowIsActive="False"/>
  </l:InteractionMessageTrigger>
</i:Interaction.Triggers>

private void Loaded(Window window)
{
  ・・・
  Messenger.Raise(new WindowActionMessage(WindowAction.Close, "Close"));
}
※メッセージを使用する場合、Windowがアクティブではない場合、デフォルトでは処理が動きません。
通常の「閉じるボタンクリック」などでは問題ないですが「処理が終わったら画面を閉じる」のような処理の場合は、Windowが前面に出ていない可能性が結構あると思います。
InvokeActionOnlyWhenWindowIsActive="False"
を設定することで、アクティブ状態でなくてもメッセージを受け取ることができます。