Help us understand the problem. What is going on with this article?

ViewModel と相互作用処理

はじめに

オレオレ解釈の覚え書き その6

今回から ViewModel のお話です。ここでは相互作用処理についてまとめます。

本文

MVVM における相互作用処理とはユーザとの対話が発生する処理を指します。ダイアログを表示して Yes/No の選択を促す場合などがこれにあたります。こういった処理ではユーザの判断を待たなければなりませんが、直接 View にアクセスできない ViewModel はどのように実装すればよいでしょうか?

MVVM を学び始めたころの私は、このパターンで構築するにあたり、なんとしても ViewModel だけで処理を実現させようと躍起になっていました。当時は参考になる文献が少なく、思い通りに実装できない状態が続き、MVVM への苦手意識を持ったポイントにもなりました。

現在は強力なフレームワークがいくつも登場し、相互作用処理を容易に実現できる環境が整っています。Microsoft 製の Prism では InteractionRequest と InteractionRequestTrigger を組み合わせてこれを実現しています。InteractionRequest クラスには Raise メソッドが実装されており、これを実行するとインターフェースで定義された Raised イベントが発生します。このイベントを購読するためのトリガーが InteractionRequestTrigger になります。イベント引数に内包されるオブジェクトにメッセージを格納することで View に情報を伝播させます。

実装例を見てみましょう。ViewModel に InteractionRequest クラスのインスタンスを宣言し、View に InteractionRequestTrigger を配置してイベントを購読します。授受されるオブジェクトのクラスは Prism に用意されている Notification です。アクションには Notification の情報をダイアログにマッピングして表示する処理を実装し、View でトリガーに紐づければ完成です。

ViewModel
namespace TestApp.ViewModels
{
    public class MainWindowViewModel 
    {
        public InteractionRequest<Notification> MessageRequest { get; }
            = new InteractionRequest<Notification>();

        // このメソッドを呼び出せばダイアログを表示できる
        public void ShowMessage()
            => this.MessageRequest.Raise(new Notification { Title = "タイトル", Content = "メッセージ" });
    }
}
View(Action)
namespace TestApp.Views.Behaviors
{
    public class MessageAction : TriggerAction<DependencyObject>
    {
        protected override void Invoke(object parameter)
        {
            if (!(parameter is InteractionRequestedEventArgs args))
                return;
            if (!(args.Context is Notification context))
                return;

            var owner = this.AssociatedObject as Window ?? Window.GetWindow(this.AssociatedObject);
            var message = context.Content?.ToString() ?? string.Empty:
            MessageBox.Show(owner, message, context.Title, MessageBoxButton.OK, MessageBoxImage.Information);
        }
    }
}
View(Trigger)
<Window x:Class="TestApp.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
        xmlns:p="http://prismlibrary.com/"
        xmlns:behaviors="clr-namespace:TestApp.Views.Behaviors"
        p:ViewModelLocator.AutoWireViewModel="True">
    <i:Interaction.Triggers>
        <p:InteractionRequestTrigger SourceObject="{Binding MessageRequest, Mode=OneTime}">
            <behaviors:MessageAction/>
        </p:InteractionRequestTrigger>
    </i:Interaction.Triggers>
</Window>

上記で ViewModel は間接的に画面を操作していますが、View を直接参照していないため、独立性を保っています。Raised イベントをとらえて結果を返すようなテストロジックを組めば、View を介さずに ViewModel のロジックテストを実施できます。このように仲介者を挟んで双方が通信する方法は Messenger パターンと呼ばれたりもします。

おわりに

InteractionRequest を使った相互作用処理についてまとめました。
今回は Prism での実装でしたが、Livet などほかのフレームワークでは、これらのやり取りをさらにシンプルに実現したものもあります。それぞれ良さがあるので、自分の作り方にあった基盤を取り入れましょう。

実は今回ご紹介した方法は、執筆時点での最新版である Prism 7.2 で非推奨とされました。(使用できないわけではありません。)ただし、この考え方自体は有益であり、汎用性も高く、理解しておいて損はない内容だと思います。次回は新たに採用された相互作用処理の実現方法である DialogService についてまとめます。

kawasawa
C#Beginner / 尊敬するプログラマはMSのかずきさん(@okazuki) / ストアで自作アプリ公開してます
https://kawasawa.github.io/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした