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


結論

LivetCask.Messagingが便利でした。

はじめに

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

  1. ViewModelでSystem.Windows.MessageBox.Showする
  2. PrismのInteractionRequestConfirmationを使用する
  3. PrismのIDialogServiceを使用する
  4. Messagerパターンで自作する
  5. etc

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

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

LivetCask.Messagingが便利

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

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

環境

自作サンプル

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" />
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

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を介さずに発火できます。戻り値を受け取る際はCallbackMethodNameCallbackMethodTargetを指定します。

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

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

ViewModel起点

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

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>
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

似たような使い方なので割愛(自作サンプル参照)

WindowInteractionMessageAction

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

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では下記メッセージが出て使用不可でした。