Tag Archives: Event Aggregator

Caliburn Micro Part 4: The Event Aggregator

Welcome to the fourth part of this tutorial on the basics of Caliburn Micro, in the previous part we saw how actions worked. In this next part we will go through the basics of the Event Aggregator.

Description

The event aggregator is a service that allows View Models to communicate with each other using messages. A View Model can subscribe to as many messages as it wants and will be notified each time one of these messages is published by the same or a different View Model.

Step 1: New Components

In this example we will build a small application that changes the text in one View Model when buttons in another are pressed, we will build upon the application that we built in the past tutorials. In order to do this we will need two additional View Models, so lets start by creating the one containing the buttons. Create a new class, call it ButtonsViewModel and add code so that it looks as below.

public class ButtonsViewModel
{
    public void Button1()
    {
    }

    public void Button2()
    {
    }

    public void Button3()
    {
    }
}

Lets also make a View to go along with it as well called ButtonsView.

<UserControl x:Class="CaliburnMicroExample.ButtonsView"
             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" >
    <StackPanel Orientation="Vertical">
        <Button x:Name="Button1" Content="Button 1" />
        <Button x:Name="Button2" Content="Button 2" />
        <Button x:Name="Button3" Content="Button 2" />
    </StackPanel>
</UserControl>

Now that we have the buttons set up lets create the View Model that will display some text to the user. Create a new View Model and call it TextViewModel.

public class TextViewModel : PropertyChangedBase
{
    private string _text;

    public string Text
    {
        get { return _text; }
        set
        {
            _text = value;
            NotifyOfPropertyChange(() => Text);
        }
    }

    public TextViewModel()
    {
        Text = "No button pressed";
    }
}

Also create TextView to go along with it.

<UserControl x:Class="CaliburnMicroExample.TextView"
             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" >
    <Grid>
        <TextBlock x:Name="Text" VerticalAlignment="Center" />
    </Grid>
</UserControl>

Finally we need to update ShellViewModel and ShellView so that they contain the new View / View Model combos we have made. Start by changing ShellViewModel as below.

public class ShellViewModel
{
    public ButtonsViewModel ButtonsVM
    {
        get;
        set;
    }

    public TextViewModel TextVM
    {
        get;
        set;
    }

    public ShellViewModel()
    {
        ButtonsVM = new ButtonsViewModel();
        TextVM = new TextViewModel();
    }
}

Change ShellView as shown below.

<UserControl x:Class="CaliburnMicroExample.ShellView"
             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"
             xmlns:cal="http://www.caliburnproject.org"
             xmlns:local="clr-namespace:CaliburnMicroExample"
             mc:Ignorable="d" >
    <StackPanel Orientation="Horizontal">
        <local:ButtonsView cal:Bind.Model="{Binding ButtonsVM}" />
        <local:TextView cal:Bind.Model="{Binding TextVM}" />
    </StackPanel>
</UserControl>

If you run the program now you should see a small app with 3 buttons down the left hand side and some text displayed to the right.

Step 2: Implementing IHandle

We are going to be publishing messages from the ButtonsViewModel that will be picked up in the TextViewModel. To do this we need to make TextViewModel aware of the message that it is going to receive. Caliburn offers the IHandle interface which lets a View Model listen for a message of its choice, start by creating a new class and calling it ChangeTextMessage. This will act as the message that we send between our two View Models.

public class ChangeTextMessage
{
    public string Text
    {
        get;
        private set;
    }

    public ChangeTextMessage(string text)
    {
        Text = text;
    }
}

Now lets extend TextViewModel to support the IHandle interface, as you can see below once we received the message we set the text that is displayed to the text received in the message.

public class TextViewModel : PropertyChangedBase, IHandle<ChangeTextMessage>
{
    private string _text;

    public string Text
    {
        get { return _text; }
        set
        {
            _text = value;
            NotifyOfPropertyChange(() => Text);
        }
    }

    public TextViewModel()
    {
        Text = "No buttons pressed";
    }

    public void Handle(ChangeTextMessage message)
    {
        Text = message.Text;
    }
}

Step 3: Event Aggregator

Now that we have added the ability for TextViewModel to handle a message we need to start adding the capability for it to receive them and for ButtonsViewModel to send them. To do this we need to pass an instance of EventAggregator to each of our interested View Models, start by creating a new instance of it in the ShellViewModel constructor and passing it to the other two View Models.

public ShellViewModel()
{
    IEventAggregator events = new EventAggregator();

    ButtonsVM = new ButtonsViewModel(events);
    TextVM = new TextViewModel(events);
}

In TextViewModel save the passed in instance to a local variable.

private IEventAggregator _events;

public TextViewModel(IEventAggregator events)
{
    _events = events;

    Text = "No buttons pressed";
}

Do the same in ButtonsViewModel as well.

private IEventAggregator _events;

public ButtonsViewModel(IEventAggregator events)
{
    _events = events;
}

Step 4: Subscribe

The next step is to subscribe TextViewModel to the Caliburn message pipe, this is as simple as calling subscribe on the EventAggregator object that we passed in.

public TextViewModel(IEventAggregator events)
{
    _events = events;

    _events.Subscribe(this);

    Text = "No buttons pressed";
}

Step 5: Publish

The final thing we need to do is send a message every time a button is pressed in ButtonsViewModel, extend your code as shown below.

public void Button1()
{
    _events.PublishOnUIThread(new ChangeTextMessage("Button 1 Pressed"));
}

public void Button2()
{
    _events.PublishOnUIThread(new ChangeTextMessage("Button 2 Pressed"));
}

public void Button3()
{
    _events.PublishOnUIThread(new ChangeTextMessage("Button 3 Pressed"));
}

If you run the application now you should see that pressing buttons on the left changes the text on the right.

That’s it for this tutorial join us next time for a look at using Caliburn Micro in Windows Phone 8.1 and Windows Store projects.