13
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

MessengerパターンにPrismを使用する際に注意すること

Last updated at Posted at 2017-11-12

はじめに

私はWPFアプリを書く際、Messengerパターンの構築にPrismのEventAggregatorを使用するのですが、ちょっとハマったので共有します。

コード例

以下のコードは、私の環境ではうまく動作しません。

(Prism.Core以外に、ReactivePropertyを使用しています。)

・ViewModel

_MainWindowViewModel.cs
using System;
using Prism.Events;
using Reactive.Bindings;

namespace WpfApp1
{
    public class _MainWindowViewModel
    {
        // Viewへのリクエスト
        public EventAggregator HelloRequest { get; }
        
        // ボタンの動作
        public ReactiveCommand HelloCommand { get; }

        public _MainWindowViewModel()
        {
            HelloRequest = new EventAggregator();
            HelloCommand = new ReactiveCommand();

            // ボタン押下時、Viewへリクエストを発行する。
            HelloCommand.Subscribe(_ => HelloRequest.GetEvent<PubSubEvent>().Publish());
        }
    }
}

・Viewのコードビハインド

MainWindow.xaml.cs
using System.Windows;
using Prism.Events;

namespace WpfApp1
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            // ViewModelの初期設定
            var viewModel = new _MainWindowViewModel();
            DataContext = viewModel;

            // リクエストが発行されたら、MessageBoxを表示する
            var hello = "Hello, Prism!";
            viewModel.HelloRequest.GetEvent<PubSubEvent>().Subscribe(() => MessageBox.Show(hello));
        }
    }
}

・View

MainWindow.xaml
<Window x:Class="WpfApp1.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:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="200" Width="200">
    <Grid>
        <!-- メッセージを表示するボタン -->
        <Button Content="Hello"
                Command="{Binding HelloCommand}"/>
    </Grid>
</Window>

実行後、ボタンを押下してもメッセージが表示されません。

原因

知識不足により私には詳しく分からないのですが、ポイントはViewのコードビハインドに記述されたこの部分です。

MainWindow.xaml.cs
// リクエストが発行されたら、MessageBoxを表示する
var hello = "Hello, Prism!";  // ローカル変数にする
// ここのラムダ式がGCに回収される!
viewModel.HelloRequest.GetEvent<PubSubEvent>().Subscribe(() => MessageBox.Show(hello));

ここのコールバックのラムダ式がガベージコレクションにより回収されてしまいます。
その理由は、PrismのEventAggregatorがコールバックを弱参照しているからです。
これにより、イベント購読側でメモリリークは起きなくなりますが、上述のようなコードを書くと早すぎる回収が発生してしまうようです。
(ローカル変数helloではなく、文字列を直接指定すると正しく動作しました。ラムダ式の変数束縛に関係があるのでしょうか?)

対策

対策は簡単です。

MainWindow.xaml.cs
// リクエストが発行されたら、MessageBoxを表示する
var hello = "Hello, Prism!";
// 第2引数にtrueを指定する
viewModel.HelloRequest.GetEvent<PubSubEvent>().Subscribe(() => MessageBox.Show(hello), true);

上記のようにPubSubEvent.Subscribe()の第2引数にtrueを指定することにより、コールバックを強参照させることができます。
メモリリークには充分注意しましょう。

まとめ

PrismのEventAggregatorはコールバックを弱参照することを頭の片隅に覚えておきましょう。
以上です。

13
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?