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);
}
}
}
}
関連ページ
- WPF_Prism_BindableBase
- WPF_Prism_DelegateCommand
- WPF_Prism_InteractionRequest
- WPF_Prism_CompositeCommand
- WPF_Prism_ErrorsContainer
- WPF_Prism_InvokeCommandAction
- C#