29
2014,Jul
4 Comments
Respond
W

Using Caliburn Micro with Universal Windows app – Design time data

in Universal Apps, Windows 8, Windows Phone , by qmatteoq

One of the most powerful features in XAML is design time data support. Let’s say that you have an application that displays some news retrieved from a RSS feed using a ListView or a GridView control. If you try to edit the visual layout of your application using Blend or the Visual Studio designer, you won’t be able to get a preview of the final result: since data is retrieved from a remote service, the ListView will look empty, since the designer isn’t able to load the real data. This scenario makes hard for a designer to have a real preview of the final result or to edit some elements (like the ItemTemplate of the ListView).

Design data is the solution to this problem: it’s basically a way to create fake data, that is loaded only when the View is displayed in Blend or in the Visual Studio designer. Let’s see how to manage this scenario using Caliburn Micro.

The sample app: a simple news reader

As a sample, we’re going to develop a simple news reader, that will read and parse the RSS feed of this blog. The real application will display the real posts from the blog, while the designer will display some fake posts, just to give to designer an idea of the content that will displayed in the page. First, let’s start to set up the real application, using the conventions we’ve learned in the previous posts. The application will have just one View, that will display a list of posts using a ListView control.

First, let’s define a simple class that will map some of the information about the post that we want to display in the page:

public class FeedItem
{
    public string Title { get; set; }
    public DateTimeOffset PublishDate { get; set; }
    public string Description { get; set; }
}

Then, we need to download the RSS feed from the web and convert the XML into a list of FeedItem objects. For this purpose, we’re going to create a separate class, that will take care of processing the RSS feed: we’re going to define a method that will take, as input, the URL of the RSS feed and will return, as output, a collection of FeedItem objects. Here is how the interface of the class looks like:

public interface IFeedService
{
    Task<IEnumerable<FeedItem>> GetNews(string feedUrl);
}

To implement it, we’re going to use one of the new 8.1 APIs, called SyndicationClient: it’s a special HttpClient implementation that is able to download the RSS feed and to automatically parse it, so that you can work with classes and objects instead of raw XML. Here is the implementation:

public class FeedService: IFeedService
{
    public async Task<IEnumerable<FeedItem>> GetNews(string feedUrl)
    {
        SyndicationClient client = new SyndicationClient();
        SyndicationFeed feed = await client.RetrieveFeedAsync(new Uri(feedUrl, UriKind.Absolute));
        List<FeedItem> feedItems = feed.Items.Select(x => new FeedItem
        {
            Title = x.Title.Text,
            Description = x.Summary.Text,
            PublishDate = x.PublishedDate
        }).ToList();

        return feedItems;
    }
}

We use the RetrieveFeedAsync() method, which takes in input the URL of the RSS feed (which is passed as parameter of the GetNews() method), and it will give you in return a SyndicationFeed object, which stores all the information about the feed. What’s interesting for us is the Items collection, which contains all the news that are included in the feed (in our case, the list of all the posts published in this blog). Using LINQ, we convert it into a collection of FeedItem objects, by grabbing the title, the description and the published date of the post.

Now we need to use this class in the ViewModel that is connected to our page. The easiest way would be to simply create a new instance of the FeedService class and to call the the GetNews() method, like in the following sample:

public class MainPageViewModel : Screen
{

    public MainPageViewModel()
    {
    }

    private List<FeedItem> _news;

    public List<FeedItem> News
    {
        get { return _news; }
        set
        {
            _news = value;
            NotifyOfPropertyChange(() => News);
        }
    }

    protected override async void OnActivate()
    {
        IFeedService feedService = new FeedService();
        IEnumerable<FeedItem> items = await feedService.GetNews("feeds.feedburner.com/qmatteoq_eng");
        News = items.ToList();
    }
}

However, this approach has some cons. Let’s say that, due to some changes in the requirements, you need to swap the FeedService() class with another implementation, by keeping the IFeedService interface as a base ground. With this approach, you would have to go in every ViewModel in which you’re using the FeedService class and replace the implementation. But wait, there’s a better way to do that: using the dependency injection container provided by Caliburn Micro!

Let’s move to the App.xaml.cs file and, in the Configure() method, let’s register the FeedService, like we did for the different ViewModels:

protected override void Configure()
{
    container = new WinRTContainer();

    container.RegisterWinRTServices();

    container.PerRequest<MainPageViewModel>();
    container.PerRequest<IFeedService, FeedService>();
}

The only difference is that, this time, since we have an interface that describes our class, we use another approach: instead of registering just the class (by calling the PerRequest<T>() method), we bind the interface with the concrete implementation, by using the PerRequest<T,Y>() method. Now, to get a reference to the FeedService class in our ViewModel, we simply need to add a new parameter in the constructor, which type is IFeedService, like in the following sample:

public class MainPageViewModel : Screen
{
    private readonly IFeedService _feedService;

    public MainPageViewModel(IFeedService feedService)
    {
        _feedService = feedService;
    }

    private List<FeedItem> _news;

    public List<FeedItem> News
    {
        get { return _news; }
        set
        {
            _news = value;
            NotifyOfPropertyChange(() => News);
        }
    }

    protected override async void OnActivate()
    {
        IEnumerable<FeedItem> items = await _feedService.GetNews("feeds.feedburner.com/qmatteoq_eng");
        News = items.ToList();
    }
}

We’ve simply added a parameter in the constructor which type is IFeedService: the dependency injection container will take care of resolving the parameter at runtime, by injecting the real implementation we’ve defined in the App class (in our case, FeedService). This way, if we need to use a different implementation of the IFeedService class, we simpy have to go back to the App class and change the Configure() method we’ve previously defined, instead of having to change the code in every ViewModel that needs to use this class. Now we can simply define our View using a ListView control, to display the posts retireved by the FeedService:

<ListView ItemsSource="{Binding Path=News}">
    <ListView.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="{Binding Path=Title}" Style="{StaticResource TitleTextBlockStyle}" />
                <TextBlock Text="{Binding Path=PublishDate}" Style="{StaticResource BodyTextBlockStyle}"></TextBlock>
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

Now if you run the app (it doesn’t matter if the Windows 8.1 or the Windows Phone 8.1 version) you should see the lists of posts recently published on this blog, downloaded from the following RSS feed: feeds.feedburner.com/qmatteoq_eng

spacer

Using design data

If you try to open your project in Blend or you enable the Visual Studio designer, you’ll notice that the page will be empty: the real FeedService isn’t executed during design time, so you don’t get a chance to see the list of posts. The purpose of our work will be to display a list of fake posts, in case the View is displayed inside the designer.

One thing you should have noticed, while working with Caliburn, is that you don’t get Intellisense while you’re dealing with binding, unlike when you work with other toolkits like MVVM Lights. This happens because you’re not explicitly setting the DataContext of the page: it’s automatically resolved at runtime, by using the specfic Caliburn naming convention. The result is that, when you’re writing the XAML, the View doesn’t know which is the ViewModel that is connected to it. This is a consequence of the design data requirement: we need to tell to the View which is the connected ViewModel, so that it can enable Intellisense and that it can provide design data for us.

Here is how to do it:

<Page
    x:Class="DesignData.Views.MainPageView"
    xmlns="schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:DesignData"
    xmlns:d="schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:micro="using:Caliburn.Micro"
    xmlns:viewModels="using:DesignData.ViewModels"
    mc:Ignorable="d"
    d:DataContext="{d:DesignInstance Type=viewModels:MainPageViewModel, IsDesignTimeCreatable=True}"
    micro:Bind.AtDesignTime="True"
   >

    <Grid>
        <ListView ItemsSource="{Binding Path=News}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <StackPanel>
                        <TextBlock Text="{Binding Path=Title}" Style="{StaticResource TitleTextBlockStyle}" />
                        <TextBlock Text="{Binding Path=PublishDate}" Style="{StaticResource BodyTextBlockStyle}"></TextBlock>
                    </StackPanel>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>
</Page>

First we’ve defined which is the DataContext of the page, but we did it in a different way, by adding the prefix d: this way, we’re telling to the XAML compiler that this value should be used only at design time, while at runtime the application will continue to use the standard DataContext (in our case, the ViewModel assigned by Caliburn). With this definition, basically we’re manually defining what Caliburn automatically does at runtime: we’re telling to the current View that the DataContext class to use is called MainPageViewModel and that supports design time data.

Then, we need to use a special Caliburn attached property, that is able to perform the binding for us at design timed: it’s called Bind.AtDesignTime and its value has to be set to True. Please note that, to get this code working, we need to define two XAML namespaces: the first one (called viewModels in the sample) which points to the project’s folder that contains all our ViewModels (in our case, using:DesignData.ViewModels, where DesignData is the name of the project); the second one (called micro in the sample) it’s the default Caliburn one, which is using:Caliburn.Micro.

After this setup, one thing you’ll immediately notice is that you’ll get Intellisense up and running: if you try to setup a new binding with a control, Visual Studio will propose you one of the properties that have been declared in the ViewModel.

Now it’s time to define the design time data: one important thing to highlight is that this feature, to work properly in Caliburn, requires a parameterless constructor. In our case, it will be different than the standard one (which, instead, contains a parameter which type is IFeedService): in this empty constructor we’re going to set up the fake data, by filling the News collection (the one displayed in the ListView control) with a list of fake posts, like in the following sample.

public class MainPageViewModel : Screen
{
    private readonly IFeedService _feedService;

    public MainPageViewModel(IFeedService feedService)
    {
        _feedService = feedService;
    }

    public MainPageViewModel()
    {
        if (Execute.InDesignMode)
        {
            News = new List<FeedItem>
            {
                new FeedItem
                {
                    Title = "First news",
                    Description = "First news",
                    PublishDate = new DateTimeOffset(DateTime.Now)
                },
                new FeedItem
                {
                    Title = "Second news",
                    Description = "Second news",
                    PublishDate = new DateTimeOffset(DateTime.Now)
                },
                new FeedItem
                {
                    Title = "Third news",
                    Description = "Third news",
                    PublishDate = new DateTimeOffset(DateTime.Now)
                }
            };
        }
    }

    private List<FeedItem> _news;

    public List<FeedItem> News
    {
        get { return _news; }
        set
        {
            _news = value;
            NotifyOfPropertyChange(() => News);
        }
    }

    protected override async void OnActivate()
    {
        IEnumerable<FeedItem> items = await _feedService.GetNews("feeds.feedburner.com/qmatteoq_eng");
        News = items.ToList();
    }
}

As you can see, the main code of the ViewModel is the same: we have one constructor, that takes care of initializing the IFeedService object; we have a property called News, which contains a list of FeedItem objects; in the OnActivate() method (that is invoked when the view is displayed) we use the FeedService class to retrieve the list of posts and to display it in the View.

The difference is that now we have a constructor without parameters, in which we initialize a list of fake posts: we manually create some FeedItem objects and we add them to the News collection. Of course, we want to do that only if the View is displayed in the designer: we don’t want to display fake data when the app is running on the phone. For this purpose, we can use a Caliburn class called Execute, which provides some useful properties and methods: for our scenario we can use a property called InDesignMode, which is a simple boolean that is set to true in case the View is displayed inside the designer. We’re goint to add fake data to the News collection only if the value of this property is set to true.

Now, build your project and try to open the designer: you’ll notice that the View won’t be empty anymore, but it will display the fake data we’ve provided.

spacer

Wrapping up

As usual, I’ve commited all the samples described in this post in my GitHub project (https://github.com/qmatteoq/CaliburnMicro-UniversalApp-Demo), inside the folder called DesignData. Have fun!

Tagged with: Caliburn • Windows 8 • Windows Phone  
14
2014,Jul
4 Comments
Respond
W

Using Caliburn Micro with Universal Windows app – Messages

in Windows 8, Windows Phone , by qmatteoq

When you’re developing an application using the MVVM pattern, one of the most common needs is to create a communication channel between two ViewModels or a ViewModel and a View: you need to exchange some data between the two actors, but you don’t have an easy way because everything is decoupled.

Messages are the perfect solution for such scenarios: they’re basically objects, that a class (like a ViewModel) can send using a special messenger; any other class can subscribe to receive such kind of messages and perform an operation when it happens. With this approach, we are able to maintain the fundamental concept of MVVM, which is “separation of concerns”. When a class sends a message, it doesn’t know who will be the receiver: it’s the class that wants to receive the message that needs to act and prepare itself to receive messages.

In Caliburn Micro, this scenario is achieved using one of the built-in services, called EventAggregator: it’s our postman and we’re going to use it to publish and to receive messages.

Let’s see how it works and how to use it in a Universal Windows app.

Sending a message

As a sample scenario, we’re going to develop a simple application with two pages: a main page and a detail page. The detail page will offer a button that will send a message containing some text; the main page will subscribe itself to receive this message and it will display the text on the screen, using a TextBlock control.

I won’t go into the details needed to setup the application and to connect the ViewModel’s properties and actions to the UI: you can see my previous posts, where I explained the basic concepts of a Caliburn Micro application.

The first step to use the EventAggregator is to add a reference to it in our ViewModel: in the same way we did in the previous post for the NavigationService, we need to add a parameter in the constructor of the ViewModel’s class, which type will be IEventAggregator.

public class DetailPageViewModel: Screen
{
    private readonly IEventAggregator _eventAggregator;

    public DetailPageViewModel(IEventAggregator eventAggregator)
    {
        _eventAggregator = eventAggregator;
    }
}

Now that we have a reference to the EventAggregator class, we can manage the button’s click: we’re going to define a method in the ViewModel, that will be invoked when the button is clicked. The purpose of this method is to send a message:

public async void SendMessage()
{
    string message = "This is a simple message";
    await _eventAggregator.PublishOnUIThreadAsync(message);
}

In this sample the message is simply a text: we send a string object by passing it as parameter of the method PublishOnUIThreadAsync() offered by the EventAggregator class. There are multiple ways to send a message: we’ll deal with them later. For the moment, it’s important to know that the PublishOnUIThreadAsync() method simply takes the object passed as parameter and sends it as a message on the UI thread. This means that the receiver class will receive it on the same thread that manages the user interface of the application: it’s perfect for our scenario, since we’re simply going to display the text on the UI.

Receiving a message

There are two steps to register a class to receive a message: the first one requires to use, again, the EventAggregator class. As a consequence, you’ll need to add an IEventAggregator parameter also in the receiver’s ViewModel constructor. The difference is that, this time, we’re going to call the Subscribe() method, that will communicate to our postman that the current class wants to receive messages.

public class MainPageViewModel: Screen
{
    public MainPageViewModel(IEventAggregator eventAggregator)
    {
        eventAggregator.Subscribe(this);
    }
}

As a parameter of the Subscribe() method you need to pass which class is going to subscribe to receive messages: since, in this case, it’s the ViewModel itself, we simply pass the value this, that is a reference to the current class.

The second step is to implement the IHandle<T> interface, where T is the type of message we want to receive: by doing so, you’ll be forced to implemented the Handle() method, which will receive as input the message.

public class MainPageViewModel: Screen, IHandle<string>
{
    private string _text;

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

    public MainPageViewModel(IEventAggregator eventAggregator)
    {
        eventAggregator.Subscribe(this);
    }

    public void Handle(string message)
    {
        Text = message;
    }
}

Here is a complete sample of the MainPageViewModel class: first, we’ve implemented the IHandle<string> interface, since we want to receive the message sent by the DetailViewModel class, which is a string. Then, we’ve created a method called Handle(), which receives as parameter a string, since it’s the type of message we expect: now we are free to manage the message as we prefer, according to our needs. In the sample, we simply take the string and we assign it to a property called Text, which is connected to a TextBlock control in the View.

If we try this simple application, we’ll see that, if we tap on the button to send the message that we’ve placed in the detail page and then we go back to the main page, the text stored in the message will be successfully displayed on the screen.

Sending and receiving complex messages

In the previous sample we’ve simply sent a string as a message. However, there are situations when using a base type can be too generic: for example, we could have multiple ViewModel registered to receive string messages, but we want that a particular message is received only by a specific ViewModel.

In this case, the solution is easy: the EventAggregator can send not only basic types as messages, but also complex objects. Let’s try to implement the same scenario, but with a different approach: instead of sending a simple string, we’re going to send an object that will store a string.

First, we need to add a new class in our application, that can act as a message. We’re going to call it SimpleMessage:

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

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

It’s a simple class, which exposes a string parameter called Text, which is initialized using the constructor. The next step is to send the message, using the same approach we’ve seen before with the EventAggregator class and the PublishOnUIThreadAsync() method: the only difference is that, this time, instead of passing as parametere a simple string, we’re going to send a SimpleMessage object.

public async void SendComplexMessage()
{
    SimpleMessage message = new SimpleMessage("This is a complex message");
    await _eventAggregator.PublishOnUIThreadAsync(message);
}

Now, in the MainPageViewModel, we need to subscribe to receive the SimpleMessage by simply implementing the IHandle<T> interface in the proper way, like in the following sample:

public class MainPageViewModel: Screen, IHandle<SimpleMessage>
{
    private string _text;

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

    public MainPageViewModel(IEventAggregator eventAggregator)
    {
        _navigationService = navigationService;

        eventAggregator.Subscribe(this);
    }

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

Nothing special to mention: it’s the same approach we’ve used before, the only difference is that this time the Handle() method will receive a real object (which type is SimpleMessage) instead of a simple type.

Sending and receiving messages to the code behind

Another common scenario is the communication between a ViewModel and a View: there are, in fact, certain operations that require direct access to the controls, like starting an animation or invoking a method that is exposed only in code behind. A way to solve this scenarios is using behavior, but sometimes they can be complex to define: sending messages is much easier.

Sending and receiving messages in the code behind is the same we’ve seen before with ViewModels: we send messages using the EventAggregator class and we receive them by implementing the IHandle<T> interface. The only difference is that, in the code behind, we can’t add an IEventAggregator paramter to the constructor: dependency injection works fine only for ViewModels. The solution is to manually interact with the Caliburn container, to explicity ask for an EventAggregator object: to achieve this result, we first need to do a change in the App class since, by default, the container is declared as a private variable, so we can’t use it in another class.

public sealed partial class App
{
   public WinRTContainer container { get; private set; }

   public App()
   {
       InitializeComponent();
   }
}

We’ve simply changed the container’s type from private to public and we’ve turned it into a property. Now, from every class, we can access to the container in the following way:

WinRTContainer container = (Application.Current as App).container;

To explicity ask for an instance of a class registred in the container, we need to use the GetInstance<T> method. Let’s see that we want to receive the SimpleMessage object we’ve sent before in the code behind of the MainPage View. Here is how we can do it:

public sealed partial class MainPageView : Page, IHandle<SimpleMessage>
{
    public MainPageView()
    {
        this.InitializeComponent();

        this.NavigationCacheMode = NavigationCacheMode.Required;

        WinRTContainer container = (Application.Current as App).container;

        IEventAggregator eventAggregator = container.GetInstance<IEventAggregator>();

        eventAggregator.Subscribe(this);
    }

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

As you can see, there aren’t big differences with the previous approach: the class implements the IHandle<SimpleMessage> interface and, as a consequence, it defines the Handle() method which receives, as parameter, a SimpleMessage object. The only difference is that we set up the EventAggregator in another way: after we’ve obtained a reference to the WinRTContainer object, we ask for the EventAggregator instance registered in the container by calling the GetInstance<IEventAggregator>()  method. Then, we proceed as usual, by calling the Subscribe() method passing this as parameter, since we want the actual code behind class to be able to receive messages.

Managing messages in a background thread

One of the new features added in Caliburn Micro 2.0 is the support to send messages in a background thread. This scenario is useful if the receiver class needs to perform intensive operation when the message is received: to avoid impacting on the UI, we can handle the message in a separate thread. Sending a message in a background thread is really easy: just use the PublishOnBackgroundThread() method offered by the EventAggregator class, like in the following sample:

public void SendMessageInBackground()
{
    SimpleMessageInBackground message = new SimpleMessageInBackground("This is a message handled in a background thread");
    _eventAggregator.PublishOnBackgroundThread(message);
}

 

Then, in the receiver class, we’ll handle it in the same way we did in the previous samples: we implement the IHandle<T> interface and we manage the Handle() method in the class. The only difference is that, this time, the Handle() method will be executed on the background thread: we need to remember that, if we need to interact with the View (for example, by changing a control’s property) we need to use the Dispatcher, which takes care of redirecting the operation to the UI thread; otherwise, we will get a cross-thread access exception.

Take a look at the following sample:

public sealed partial class MainPageView : Page, IHandle<SimpleMessageInBackground>
{
    public MainPageView()
    {
        this.InitializeComponent();

        this.NavigationCacheMode = NavigationCacheMode.Required;

        WinRTContainer container = (Application.Current as App).container;

        IEventAggregator eventAggregator = container.GetInstance<IEventAggregator>();

        eventAggregator.Subscribe(this);
    }

    public void Handle(SimpleMessageInBackground message)
    {
        Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
        {
            MessageContent.Text = message.Text;
        });
    }
}

This sample is similar to the previous one: in a code behind class we register to receive a message which type is SimpleMessageInBackground. The final result is the same: we display the content of the message in a TextBlock control called MessageContent. The difference, this time, is that we do it using the RunAsync() method of the Dispatcher class, since we’re interacting with a control in the XAML but the message is being handled in a background thread.

That’s all!

In this post we’ve covered all the basic concepts about sending and receiving messages in a MVVM application built with Caliburn Micro 2.0. The last week I’ve decided to publish all the samples connected to this series of post about Caliburn Micro and Universal Windows app on GitHub: the repository is available on https://github.com/qmatteoq/CaliburnMicro-UniversalApp-Demo. You’ll find in the solution, together will all the samples of the previous posts, also a new one about messages.

Tagged with: Caliburn • Windows 8 • Windows Phone  
7
2014,Jul
0 Comments
Respond
W

Porting a Windows Phone 8.0 application to Caliburn Micro 2.0

in Windows 8, Windows Phone , by qmatteoq

In my everyday job at Funambol I work on an application called OneMediaHub, which is the Windows Phone client for the cloud services offered by the company. The application is built using the MVVM pattern and Caliburn Micro as a framework. Now that the development cycle of the new version of the client is completed, one of my goal before starting working on the new features for the next version is to upgrade Caliburn Micro from the previous version (1.5.3) to the most recent one (2.0).

However, as I discovered by my self, the procedure isn’t so straight forward: Caliburn Micro is a big improvemenet compared to the previous version, but it also includes many breaking changes. In this post I’ll detail the most important ones I had to face during the migration.

Changes in bootstrapper

The boostrapper is the base class that replaces the App one and that takes care of initializing the app, among all the Caliburn services and conventions.

There are two important changes in the boostrapper with Caliburn Micro 2.0:

  • In the previous release, the bootstrapper’s class had to inherit from the PhoneBootstrapper one. Now, instead, the base class has been renamed to PhoneBootstrapperBase.
  • Now, in the constructor of the bootstrapper’s class, you need to call the Initialize() method, while in the previous version it wasn’t required.

Here is a full working initialization of the bootstrapper for Caliburn Micro 2.0:

public class CaliburnBootStrapper : PhoneBootstrapperBase
{
    public OneMediaHubBootStrapper()
    {
        this.Initialize();
    }
}

Thanks to Matteo Tumiat that pointed me in the right direction: without calling the Initialize() method, the app was stuck in the loading phase, without any warning or exception.

Changes in EventAggregator

EventAggregator is the class used to manage messages (which are, in the end, simple objects) that can be sent and received from different classes (like two ViewModels or a ViewModel and a View). This approach helps to create a communication channel between different classes while keeping alive, at the same time, the fundamental MVVM concept of “separation of concerns”. In fact, when a class sends a message, it doesn’t know who’s going to receive it: it’s the receiver class that will simply register itself to handle specific kind of messages.

To send a message in Caliburn Micro 1.5 we used the Publish() method of the IEventAggregator class, that simply dispatched the

gipoco.com is neither affiliated with the authors of this page nor responsible for its contents. This is a safe-cache copy of the original web site.