漫坊亭

社会の底辺プログラマ

Caliburn Micro Part 4: The Event Aggregator をやってみる。

Mindscape Blog » Blog Archive » Caliburn Micro Part 4: The Event Aggregator manbou404.hatenablog.com

要は、Event Aggregator の使い方。

以下は、前回のソリューションをつかう。
ShellViewとShellViewModelを空にしておく。

Step 1: Adding Another View and View-Model

  1. AppBootstrapperを、コンテナにMEFを使うように修正する。
    参照の追加で、System.ComponentModel.Compositionを追加する。
// <AppBootstrapper.cs>
using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using Caliburn.Micro;
using System.ComponentModel.Composition.Primitives;

namespace Tutorial
{
    public class AppBootstrapper : BootstrapperBase
    {
        private CompositionContainer container;

        protected override void Configure()
        {
            container = new CompositionContainer(
                new AggregateCatalog(
                    AssemblySource.Instance
                        .Select(x => new AssemblyCatalog(x))
                        .OfType<ComposablePartCatalog>()));

            CompositionBatch batch = new CompositionBatch();

            batch.AddExportedValue<IWindowManager>(new WindowManager());
            batch.AddExportedValue<IEventAggregator>(new EventAggregator());
            batch.AddExportedValue(container);

            container.Compose(batch);
        }

        protected override object GetInstance(Type serviceType, string key)
        {
            string contract = string.IsNullOrEmpty(key)
                            ? AttributedModelServices.GetContractName(serviceType) 
                            : key;
            var exports = container.GetExportedValues<object>(contract);

            if (exports.Count() > 0)
            {
                return exports.First();
            }

            throw new Exception(string.Format(
                    "Could not locate any instances of contract {0}.", contract));
        }

        public AppBootstrapper()
        {
            Initialize();
        }
        protected override void OnStartup(object sender, StartupEventArgs e)
        {
            DisplayRootViewFor<IShell>();
        }
    }
}
  1. ColorViewとColorViewModelを追加する。
<!--ColorView.xaml>
<UserControl x:Class="Tutorial.ColorView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300">
    
    <Grid Background="CornflowerBlue">
        
    </Grid>
    
</UserControl>
// <ColorViewModel.cs>
using System.ComponentModel.Composition;

namespace Tutorial
{
    [Export(typeof(ColorViewModel))]
    public class ColorViewModel
    {
    }
}
  1. ColorViewをShellViewとShellViewModelに追加する。
    先ほどのColorViewを、注入してもらいバインドする。
    Rectangleの背景色は、ViewのColorにバインドする。
<!--ShellView.xaml>
<UserControl x:Class="Tutorial.ShellView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
             xmlns:cal="http://www.caliburnproject.org">

    <Grid Width="300" Height="300" Background="LightBlue">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <ContentControl Name="ColorModel" Margin="10" />
        <Rectangle Grid.Column="1" Width="100" Height="100" Fill="{Binding Color}" />
    </Grid>

</UserControl>
// <ShellViewModel.cs>
using System.ComponentModel.Composition;
using System.Windows.Media;
using Caliburn.Micro;

namespace Tutorial
{
    [Export(typeof(IShell))]
    public class ShellViewModel : PropertyChangedBase, IShell
    {
        [ImportingConstructor]
        public ShellViewModel(ColorViewModel colorModel)
        {
            ColorModel = colorModel;
        }
 
        public ColorViewModel ColorModel { get; private set; }

        private SolidColorBrush _Color = new SolidColorBrush(Colors.Black);

        public SolidColorBrush Color
        {
            get { return _Color; }
            set
            {
                _Color = value;
                NotifyOfPropertyChange(() => Color);
            }
        }
    }
}

ここまでで、こんな感じ。
f:id:jfactory:20150626153814p:plain

オリジナルと違うところ

  • AppBootstrapperのコンストラクタで、Initializeを呼んでいる。
  • AppBootstrapperのOnStartupで、DisplayRootViewForを呼んでいる。
  • ShellViewModelの_Colorに、初期値:黒を入れている。

Step 2: Implementing the IHandle Interface

  1. ColorEventクラスを追加する。
// <ColorEvent.cs>
using System.Windows.Media;

namespace Tutorial
{
    public class ColorEvent
    {
        public ColorEvent(SolidColorBrush color)
        {
            Color = color;
        }

        public SolidColorBrush Color { get; private set; }
    }
}
  1. ShellViewModelに、購読するためのメソッドを追加
    (まだ呼ばれないよ!)
// <ShellViewModel.cs>
        public void Handle(ColorEvent message)
        {
            Color = message.Color;
        }

Step 3: Subscribe

クラス宣言に、IHandle<>を追加 コンストラクタインジェクションで、IEventAggregator をもらう。

// <ShellViewModel.cs>
    [Export(typeof(IShell))]
    public class ShellViewModel : PropertyChangedBase, IShell, IHandle<ColorEvent>
    {
        [ImportingConstructor]
        public ShellViewModel(ColorViewModel colorModel, IEventAggregator events)
        {
            ColorModel = colorModel;

            events.Subscribe(this);
        }

        // 以下略

Step 4: Publish

  1. ColorViewにラジオボタン追加
    <Grid Background="CornflowerBlue">
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <RadioButton Name="Red"   Content="Red"   Foreground="White" 
                                  VerticalAlignment="Center" HorizontalAlignment="Center" />
        <RadioButton Name="Green" Content="Green" Foreground="White" 
                                  VerticalAlignment="Center" HorizontalAlignment="Center" Grid.Row="1" />
        <RadioButton Name="Blue"  Content="Blue"  Foreground="White" 
                                  VerticalAlignment="Center" HorizontalAlignment="Center" Grid.Row="2" />
    </Grid>
  1. ColorViewModelに、イベントアグリゲータの初期化と、ラジオボタンイベントハンドラ追加
// <ColorViewModel.cs>
    [Export(typeof(ColorViewModel))]
    public class ColorViewModel
    {
        private readonly IEventAggregator _events;

        [ImportingConstructor]
        public ColorViewModel(IEventAggregator events) {
            _events = events;
        }

        public void Red() {
            _events.Publish(new ColorEvent(new SolidColorBrush(Colors.Red)), x => x());
        }

        public void Green() {
            _events.Publish(new ColorEvent(new SolidColorBrush(Colors.Green)), x => x());
        }

        public void Blue() {
            _events.Publish(new ColorEvent(new SolidColorBrush(Colors.Blue)), x => x());
        }
    }

こんな感じになった

f:id:jfactory:20150626155736p:plain

オリジナルと違うところ + ShellViewModelで購読するために、IHandleの追加 + ColorViewModelのIEventAggregatorのPublishメソッドの呼び出し方が変わっていた。

ソース一式