Prism.Wpf(MVVM)で気軽にMessageBoxをShowしたい

#結論

[LivetCask.Messaging][Livet]が便利でした。

#はじめに
Prism.Wpf(MVVM)で確認ダイアログを表示する方法として以下が考えられます。

1. ViewModelで[System.Windows.MessageBox.Show](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.messagebox?view=netframework-4.8)する
2. Prismの[InteractionRequest](https://github.com/PrismLibrary/Prism/blob/Prism.v6.3.0/Source/Wpf/Prism.Wpf/Interactivity/InteractionRequest/InteractionRequest.cs)で[Confirmation](https://github.com/PrismLibrary/Prism/blob/Prism.v6.3.0/Source/Wpf/Prism.Wpf/Interactivity/InteractionRequest/Confirmation.cs)を使用する
3. Prismの[IDialogService](https://github.com/PrismLibrary/Prism/blob/b8b95dbc5b7ed8100bba46a02c75f1d7e58287b5/Source/Wpf/Prism.Wpf/Services/Dialogs/IDialogService.cs)を使用する
4. [Messagerパターンで自作する](https://takamints.hatenablog.jp/entry/mvvm-messagebox)
5. etc

1.はViewModelで画面操作をするとは何事かとMVVM原理主義者に怒られるのであまりできません。
2.はSystem.Window.MessageBoxではないのですがPrismで既定で画面を用意してくれていましたが、IDialogServiceの導入により廃止予定です。
3.は自分でWindowベースで画面を用意しないといけません。

特に画面を装飾する必要がなく、ただただ見慣れた確認ダイアログを出したいと思っても面倒くさいのです。

# [LivetCask.Messaging][Livet]が便利
+ [WPF Prism episode: 18 ～ Livet が Prism に「IDisposable 呼び出し用」としてゲッツされた件 ～][]
+ [Livet v3.2.1 をリリースしました](https://blog.okazuki.jp/entry/2019/11/15/092200)

で存在に気付けました。ありがとうございます。

System.Window.MessageBoxをMessengerパターンで発火してくれるAction等々ありましたのでご紹介です。
詳細は[Github][Livet]参照願います。


# 環境
[自作サンプル](https://github.com/kwhrkzk/LivetCaskMessagingSample)

```C#:csproj抜粋
    <OutputType>WinExe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <UseWPF>true</UseWPF>
    <LangVersion>8.0</LangVersion>

    <PackageReference Include="LivetCask.Messaging" Version="3.2.1" />
    <PackageReference Include="prism.Unity" Version="7.2.0.1422" />
    <PackageReference Include="ReactiveProperty" Version="6.2.0" />
    <PackageReference Include="Unity" Version="5.11.3" />
```

```c#:xamlには以下の名前空間が書かれているとする
             xmlns:prism="http://prismlibrary.com/"
             xmlns:i="http://schemas.microsoft.com/xaml/behaviors" 
             xmlns:l="http://schemas.livet-mvvm.net/2011/wpf"
             prism:ViewModelLocator.AutoWireViewModel="True"
```


# Action列挙

| クラス名 | 説明(抜粋) |
|---|---|
| ConfirmationDialogInteractionMessageAction | 確認ダイアログを表示するアクションです。ConfirmationMessageに対応します。 |
| InformationDialogInteractionMessageAction | 情報ダイアログを表示するアクションです。InformationMessageに対応します。 |
| OpenFileDialogInteractionMessageAction | 「ファイルを開く」ダイアログを表示するアクションです。OpeningFileSelectionMessageに対応します。 |
| SaveFileDialogInteractionMessageAction | 「ファイルを保存する」ダイアログを表示するアクションです。SavingFileSelectionMessageに対応します。 |
| WindowInteractionMessageAction | Windowの最小化・最大化・閉じるを行うアクションです。WindowActionMessageに対応します。 |
| etc |  |


# ConfirmationDialogInteractionMessageAction 

```C#:xam例
        <Button Content="Livet Messenger: View Raise">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="Click">
                    <l:ConfirmationDialogInteractionMessageAction>
                        <l:DirectInteractionMessage CallbackMethodName="ViewMessager" CallbackMethodTarget="{Binding}">
                            <l:ConfirmationMessage Caption="title" Text="View Raise" />
                        </l:DirectInteractionMessage>
                    </l:ConfirmationDialogInteractionMessageAction>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </Button>
```

ViewModelを介さずに発火できます。戻り値を受け取る際は**CallbackMethodName**と**CallbackMethodTarget**を指定します。

```C#:ViewModel例
        public void ViewMessager(ConfirmationMessage message)
        {
            OutputMessage.Value = $"{DateTime.Now}: ViewMessager: {message.Response ?? false}";
        }
```

CallbackMethodNameに指定するメンバ関数はpublicにしないとダメでした。

## ViewModel起点
Viewで発火させるのではなくViewmodelからも発火できます。

```C#:xaml例
        <Button Command="{Binding ViewModelToViewMessagerCommand}" Content="Livet Messenger: ViewModel Raise">
            <i:Interaction.Triggers>
                <l:InteractionMessageTrigger MessageKey="ViewModelToViewMessager" Messenger="{Binding Messenger}">
                    <l:ConfirmationDialogInteractionMessageAction />
                </l:InteractionMessageTrigger>
            </i:Interaction.Triggers>
        </Button>
```

```C#:ViewModel例

        public InteractionMessenger Messenger { get; } = new InteractionMessenger();
        public ReactiveCommand ViewModelToViewMessagerCommand { get; } = new ReactiveCommand();

        public ~~ViewModel()
        {
            ViewModelToViewMessagerCommand.Subscribe(async _ => await ViewModelToViewMessagerAsync()).AddTo(DisposeCollection);
        }

        private async Task ViewModelToViewMessagerAsync()
        {
            var message = new ConfirmationMessage("ViewModel Raise", "title", "ViewModelToViewMessager")
            {
                Button = MessageBoxButton.YesNo,
            };

            await Messenger.RaiseAsync(message);

            OutputMessage.Value = $"{DateTime.Now}: ViewModelToViewMessagerAsync: {message.Response ?? false}";
        }
```


# InformationDialogInteractionMessageAction 
# OpenFileDialogInteractionMessageAction
# SaveFileDialogInteractionMessageAction 

似たような使い方なので割愛([自作サンプル](https://github.com/kwhrkzk/LivetCaskMessagingSample)参照)

# WindowInteractionMessageAction 

Windowクラスを介さずにWindowの最小化・最大化・閉じるを行えます。

```C#:xamlで閉じる例
            <Button Content="Window Interaction Message Close" Grid.Column="2">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="Click">
                        <l:WindowInteractionMessageAction>
                            <l:DirectInteractionMessage>
                                <l:WindowActionMessage Action="{x:Static l:WindowAction.Close}" />
                            </l:DirectInteractionMessage>
                        </l:WindowInteractionMessageAction>
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </Button>
```

# まとめ
車輪の再発明をしなくて良いのはとても助かります。
他にも便利そうなアクションやビヘイビアが揃っているので、～～したいなぁと思ったら覗いてみると良いかもしれません。

# おまけ
比較のために**InteractionRequest**を実装しようと思ったのですが
.NET Core 3.1では下記メッセージが出て使用不可でした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109302/bdbb596c-cbfe-a0c8-6b2c-654e9dd4a33a.png)





[Livet]:https://github.com/runceel/Livet
[WPF Prism episode: 18 ～ Livet が Prism に「IDisposable 呼び出し用」としてゲッツされた件 ～]:https://elf-mission.net/programming/wpf/episode18/