WPF_Prism_InteractionRequest

InteractionRequest

InteractionRequestとは

InteractionRequestは、MVVMでViewModelからViewを操作(通信)する処理、いわゆる Messangerパターンを実現します。


Messanger パターンとは

MVVMではView層がViewModel層を参照しますが、逆向きの依存関係(ViewModel層からView層を見ること)はできません。

例えば、ViewModelの処理中にユーザへの問い合わせ(以降の処理を分岐させたい)場合に、ViewModelから入力ダイアログを表示するとViewへの依存を持つことになるため、MVVMのアンチパターンとなります。

この問題を解決するのが Messenger パターンです。


Messenger パターンはViewModelとViewの仲介を行います。


ViewModel ---> Messenger ---> View

InteractionRequest実装例

Request

RequestはViewModelからViewへ渡したいデータを内包するクラスとなります。


using Prism.Interactivity.InteractionRequest;
using System.Windows;

namespace WpfPrismMvvm
{
    public class MessageBoxRequest : Notification
    {
        // TitleはNotification.Titleを利用します。
        // メッセージ本文を格納します。
        public string Message { get; set; }

        // 実行結果の格納先
        public MessageBoxResult Result { get; set; }

        // MessageBoxの設定
        public MessageBoxButton Button { get; set; }
        public MessageBoxImage Image { get; set; }
        public MessageBoxResult DefaultButton { get; set; }
    }
}

Action

TriggerActionを継承して、ViewModelからViewを操作する内容を定義します。


using Prism.Interactivity.InteractionRequest;
using System.Windows;
using System.Windows.Interactivity;

namespace WpfPrismMvvm
{
    public class MessageBoxAction : TriggerAction<DependencyObject>
    {
        protected override void Invoke(object parameter)
        {
            // イベント引数を取得する。
            var args = parameter as InteractionRequestedEventArgs;
            if (args == null)
            {
                return;
            }

            // ContextのConfirmedに結果を格納する
            var confirmation = args.Context as MessageBoxRequest;
            if (confirmation != null)
            {
                confirmation.Result = MessageBox.Show(
                    confirmation.Message,
                    confirmation.Title,
                    confirmation.Button,
                    confirmation.Image,
                    confirmation.DefaultButton
                );
            }

            // コールバックを呼び出す
            args.Callback();
        }
    }
}

ViewModel

コマンド押下イベントからRequestを発行します。


using Prism.Commands;
using Prism.Interactivity.InteractionRequest;
using Prism.Mvvm;
using System.Windows;

namespace WpfPrismMvvm
{
    // PrismのMVVMのベースクラスとして使えるINotifyPropertyChangedの実装クラスを使う
    public class MainWindowViewModel : BindableBase
    {
        public InteractionRequest<MessageBoxRequest> OpenMessageBoxRequest { get; }        

        public DelegateCommand ButtonClickedCommand { get; private set; }

        /// <summary>
        /// コンストラクタでコマンドとリクエストを割り当てる。
        /// </summary>
        public MainWindowViewModel()
        {
            OpenMessageBoxRequest = new InteractionRequest<MessageBoxRequest>();

            ButtonClickedCommand = new DelegateCommand(ButtonClickedProcess, () => true);
        }

        private void ButtonClickedProcess()
        {
            // リクエストの作成
            var req = new MessageBoxRequest()
            {
                Title = "InteractionRequest",
                Message = "InteractionRequest!",
                Image = MessageBoxImage.Information,
                Button = MessageBoxButton.OK,
                DefaultButton = MessageBoxResult.OK
            };

            // Viewにリクエストを投げる。
            OpenMessageBoxRequest.Raise(req);

            // 同期処理であるため、リクエストが終了するまで待ち合わせる。
            System.Console.WriteLine(req.Result);
        }
    }
}

xaml

<prism:InteractionRequestTrigger>は、InteractionRequestのRaisedイベント発火によって発生するトリガを定義します。


<Window x:Class="WpfPrismMvvm.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        xmlns:prism="http://prismlibrary.com/"
        xmlns:l="clr-namespace:WpfPrismMvvm"
        mc:Ignorable="d"
        Title="MainWindow" Height="320" Width="480">
    <i:Interaction.Triggers>
        <prism:InteractionRequestTrigger SourceObject="{Binding OpenMessageBoxRequest, Mode=OneWay}">
            <l:MessageBoxAction/>
        </prism:InteractionRequestTrigger>
    </i:Interaction.Triggers>
    <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
        <Button Command="{Binding ButtonClickedCommand}" Content="Execute InteractionRequest" MinWidth="150" />
    </StackPanel>
</Window>


Prism.Interactivity.InteractionRequestの廃止

Prism 7.1 まではメッセージボックスやダイアログの表示に InteractionRequest と PopupWindowAction を使用していましたが、Prism 7.2 以降では IDialogService を使用するよう変更されました。

InteractionRequestを記述すると、廃止(非推奨)マーキングされコンパイル時に警告が表示されます。


InteractionRequestを自作する。

IDialogService が利用しにくい、過去の互換性を保ちたいといった場合には自作が必要です。


using System;

namespace WpfPrismMvvm
{
    /// <summary>
    /// リクエストのインターフェース
    /// </summary>
    public interface IAlternativeRequest
    {
    }

    /// <summary>
    /// InteractionRequestのイベントデータを格納する。
    /// </summary>
    public class AlternativeInteractionArgs : EventArgs
    {
        /// <summary>
        /// TriggerActionで参照するリクエストを格納する。
        /// </summary>
        public IAlternativeRequest Request { get; set; }

        /// <summary>
        /// TriggerActionで実行するコールバックを格納する。
        /// </summary>
        public Action Callback { get; set; }
    }

    /// <summary>
    /// AlternativeInteractionRequestのインターフェース
    /// </summary>
    public interface IAlternativeInteractionRequest : IAlternativeRequest
    {
        event EventHandler<AlternativeInteractionArgs> Raised;
    }

    /// <summary>
    /// InteractionRequestの代替処理
    /// </summary>
    /// <typeparam name="T">IAlternativeRequest</typeparam>
    public class AlternativeInteractionRequest<T> : IAlternativeInteractionRequest where T : IAlternativeRequest
    {
        public AlternativeInteractionRequest()
        {
        }

        public event EventHandler<AlternativeInteractionArgs> Raised;

        /// <summary>
        /// Viewにリクエストを投げる。
        /// </summary>
        /// <param name="request">リクエスト</param>
        public void Raise(T request)
        {
            Raise(request, n => { });
        }

        /// <summary>
        /// Viewにリクエストを投げる。
        /// </summary>
        /// <param name="request">リクエスト</param>
        /// <param name="callback">コールバック</param>
        public void Raise(T request, Action<T> callback)
        {
            if (Raised != null)
            {
                var args = new AlternativeInteractionArgs
                {
                    Request = request,
                    Callback = () =>
                    {
                        if (callback != null)
                        {
                            callback(request);
                        }
                    }
                };
                Raised(this, args);
            }
        }
    }
}


関連ページ