WP7 Contrib – the last messenger

As you may already be aware the WP7C uses MVVM Light as its preferred MVVM framework. Laurent has a great implementation of a Mediator style pattern that he has aptly name the Messenger. What I really like about this is the simplicity when it comes to implementing the baked in messages and how you can extend the IMessenger interface that Laurent has included in MVVM Light. In general these generic messages cater for the majority of problems. However, there is a case that is not particularly well catered for. What follows is focused on WP7 however, this messenger component could be recompiled under your favourite SL build and used in your MVVM Light application today.

Before we start to roll you are going to make sure you have all the bits installed

WP7 Dev tools ISO download thanks JaimeR

MVVM Light Toolkit

WP7 Contrib

Now if you are using some sort of DI component, at the moment my favourite is funq (Ninject will always have a place in my heart) you may have found yourself in a situation where by you want to pass data from one VM to another, however the VM you want to pass the data to has not yet been created. There are various ways around this like not using DI and newing up all your VM during start-up so that this is no longer an issue. But, remember we are running under WP7 and performance is never going to be what it’s like in the browser. We also want to take advantage of DI and all the goodness that it brings to the party.

Which came first, the chicken or the egg ??

An inherent problem with using Silverlight navigation and DI and having your VM’s lazily loaded. When you navigate to a page its not until this moment in time that your VM is constructed, therefore we hit this timing problem where the VM that is loaded up and trying to send a message to a VM which has yet to be loaded. What we first started out with was an implementation that was not so polished in that we would have a Register and Send Message being defined in the VM making the call; a Register Message defined in the receiving VM; and a callback Message in the receiving VM.

The way in which this works would be something like :-

  1. VM SearchPeople Registers for Message CurrentlySelectedPerson
  2. In response to an interaction in the View (i.e. selecting an item in a list box). The associated VM SearchPeople Sends Message CurrentlySelectedPerson from within a INPC Property
  3. In response to an interaction in the View (i.e. tapping an item in a list box). The associated VM SearchPeople ‘s RelayCommand navigates to a new page Person Details
  4. VM Person Details is constructed by our DI framework
  5. VM Person Details constructor Registers for the Message CurrentlySelectedPerson
  6. VM Person Details constructor fires a CallBack for the MessageCurrentlySelectedPerson
  7. VM SearchPeople received the Message CurrentlySelectedPerson
  8. VM SearchPeople Send the Message CurrentlySelectedPerson
  9. VM PersonDetails received the Message CurrentlySelectedPerson

This mechanism has to deal with the timing issue of the second VM not being loaded before the message is sent from the first, we end up in a situation where the first message never works and the subsequent messages always work. Therefore, by providing a callback in the second and sending the message from here to the first VM we solve the problem, but its not as elegant as it could be. The main issue here is that we are forced to send our messages twice due to the fact that our recipient VM has yet to be created and we need to give it a poke.

What becomes apparent is that the callback mechanism is not built for this type of work and we need a more structured and direct messaging mechanism. I had a chat with Ollie and he came up with a great messaging implementation. The rest of this post is concerned with how to use this Replay Messenger in your app, Ollie has a great post coming where he is going to go into more detailed post on the implementation details.

Laurent has provided an interface called IMessenger which provides developers with the ability to extend the and create our own Messages, nice… Let’s make a start on building out a simple implementation. If you have all the bits installed lets make a start. First off what is this app we are going to build, well is a simple app which has some sort of people list and we can select a person from the list and navigate to a more detailed page. Not the most exciting of apps but I want to keep the context fairly striaght forward so that we can concentrate on the problem and the solution rather than being distracted by other stuff. Saying that you are going too meet some other interface from WP7Contrib, ILogManager, ILog and INavigation Service. Over on the right you can see an screen shot showing the strcuture of the sample which can be found in the Spikes dir of the WP7Contrib.

My intentions below are not to walk you through every step we have some planned detailed posts and maybe even some screencasts to illustrate this points, therefore I am assumming that you know about the message classes availavble to you in MVVM Light Messaging and I am also assuming that you understand how to uses a DI framework.

Over on the right there is a screen grab of the solution if you want to fire this up then you can find it in the Spikes dir of the WP7Contrib. I have a model class called Person this is going to hold our data;2 pages main page and child page; 2 corresponding View Models main VM and child VM; in messages I have a base message and something called the Selected Person Message; a custom Base VM for WP7 which subclasses the MVVM Light VM Base; and finally I have a BootStrapper for my DI bits.

So first off what we need to do is take a look at the Selected Person Message as this is the message which is a wrapper for our data that we want to pass from the Main VM to the Child VM.

namespace ReplayLastMessageMessenger.ViewModel.Messages
{
    using System;

    using ReplayLastMessageMessenger.Model;

    /// <summary>
    /// The currently selected search result message.
    /// </summary>
    public class SelectedPersonMessage : BaseMessage
    {
        #region Constants and Fields

        /// <summary>
        /// The person.
        /// </summary>
        private readonly Person person;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="SelectedPersonMessage"/> class.
        /// </summary>
        /// <param name="person">
        /// The person.
        /// </param>
        public SelectedPersonMessage(Person person)
        {
            this.person = person;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SelectedPersonMessage"/> class.
        /// </summary>
        /// <param name="exception">
        /// The exception.
        /// </param>
        public SelectedPersonMessage(Exception exception)
        {
            this.Exception = exception;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets person.
        /// </summary>
        public Person Person
        {
            get
            {
                return this.person;
            }
        }

        #endregion
    }
}

The code above is boiler plate making the implementation clean and repeatable, the code above is the message that we use for passing Property data between our VM’s.

That’s the message built.

Remember, what we have in our view is a list of items, and in our case a list of people and when you select an item from the list we navigate from this page to a details page, and we do this by sending a message that contains the data of the currently selected item from the list.

/// <summary>
/// Gets or sets CurrentlySelectedPerson.
/// </summary>
public Person CurrentlySelectedPerson
{
    get
    {
        return this.currentlySelectedPerson;
    }

    set
    {
        this.SetPropertyAndNotify(ref this.currentlySelectedPerson, value, "CurrentlySelectedPerson");
        this.NavigationService.Navigate(new Uri("/View/ChildPage.xaml", UriKind.Relative));
        this.MessengerInstance.Send(new SelectedPersonMessage(this.currentlySelectedPerson));
    }
}

Note, in the above code the Messenger is only responsible for sending message, its the Navigation Service that is responsible for the navigation from the list page to the details page and the details page displays the details of the data item selected from the list, therefore the Child VM will need to know the currently selected person. I will cover off the Navigation Service interface in a future post.

In order to do this we need to inject the IMessenger into each VM from our bootstrapper. I am using funq for this so the code below is specific to funq however it’s the same drill register your wire ups with the container and then resolve them to an interface, This will provide a way of firing Registering and Sending those Currently Selected Person messages.

public class BootStrapper : IDisposable
{
    #region Constants and Fields

    /// <summary>
    /// The disposed.
    /// </summary>
    private bool disposed;

    #endregion

    #region Constructors and Destructors

    /// <summary>
    /// Initializes a new instance of the <see cref="BootStrapper"/> class.
    /// </summary>
    public BootStrapper()
    {
        this.Container = new Container();

        this.ConfigureContainer();
    }

    #endregion

    #region Properties

    /// <summary>
    /// Gets Container.
    /// </summary>
    public Container Container { get; private set; }

    #endregion

    #region Implemented Interfaces

    #region IDisposable

    /// <summary>
    /// The dispose.
    /// </summary>
    public void Dispose()
    {
        if (this.Container != null)
        {
            this.Container.Dispose();
            this.Container = null;
        }
    }

    #endregion

    #endregion

    #region Methods

    /// <summary>
    /// The configure container.
    /// </summary>
    private void ConfigureContainer()
    {
        this.Container.Register<ILogManager>(c => new LoggingService());

        this.Container.Register<ILog>(c => this.Container.Resolve<ILogManager>());

        this.Container.Register<IMessenger>(c => new LastMessageReplayMessenger());

        this.Container.Register<INavigationService>(
            c => new ApplicationFrameNavigationService(((App)Application.Current).RootFrame));

        this.Container.Register(
            c => new MainViewModel(c.Resolve<INavigationService>(), c.Resolve<IMessenger>(), c.Resolve<ILog>()));

        this.Container.Register(
            c => new ChildViewModel(c.Resolve<INavigationService>(), c.Resolve<IMessenger>(), c.Resolve<ILog>()));
    }

    #endregion
}

There are a couple of interfaces here that I am injecting into my VM’s these include the Navigation Service, the log manager and the log all of which we will talk about in future posts.

Again, Laurent has done a great job by providing a property in the View Model Base called Messenger Instance which returns the instance of the messenger we injected in our bootstrapper, nice. There is a small nugget here as well to help with performance, as the registration of the message does not need to happen on the UI thread we can wrap this up like so.

The Main VM looks like this.

public MainViewModel(INavigationService navigationService, IMessenger messenger, ILog log)
    : base(navigationService, messenger, log)
{
    ThreadPool.QueueUserWorkItem(state =>
    {
        // messaging
        this.MessengerInstance.Register<NotificationMessageAction<Person>>(
            this, message => message.Execute(this.CurrentlySelectedPerson));
    });
}

Now in the constructor of the Child VM its far sweeter code instead of the crufty callback and force a message in order to get the data we simply register for the replay message we are interested in, and provide a method to deal with the received message.

public ChildViewModel(INavigationService navigationService, IMessenger messenger, ILog log)
    : base(navigationService, messenger, log)
{
    ThreadPool.QueueUserWorkItem(state =>
    {
        this.MessengerInstance.Register<SelectedPersonMessage>(this, this.OnReceiveMessage);
    });
}

The Xaml in the Main View looks like this.

<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel"
        Grid.Row="1"
        Margin="12,0,12,0">
    <ListBox ItemsSource="{Binding Path=Persons}"
                SelectedItem="{Binding Path=CurrentlySelectedPerson, Mode=TwoWay}"
                ItemTemplate="{StaticResource PersonsDataTemplate}" />
</Grid>

The Xaml in the Child View looks like this.

<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel"
        Grid.Row="1"
        Margin="12,0,12,0">
    <Grid.RowDefinitions>
        <RowDefinition Height="90" />
        <RowDefinition Height="513" />
    </Grid.RowDefinitions>
    <StackPanel Orientation="Vertical">
        <TextBlock Text="{Binding CurrentlySelectedPerson.LastName}"
                    FontFamily="{StaticResource PhoneFontFamilySemiLight}"
                    FontSize="{StaticResource PhoneFontSizeMediumLarge}" />
        <TextBlock Text="{Binding CurrentlySelectedPerson.FirstName}"
                    FontFamily="{StaticResource PhoneFontFamilySemiLight}"
                    FontSize="{StaticResource PhoneFontSizeMedium}" />
    </StackPanel>
    <Border Grid.Row="1"
            BorderThickness="2">
        <Image Source="{Binding CurrentlySelectedPerson.ProfileImage}" />
    </Border>
</Grid>
</Grid>


Just to recap our steps were :-

  1. Define the Message which wraps up the data
  2. Add the IMessenger interface as a parameter in the VM constructor
  3. Register the IMessenger interface with the Last Message Replay Messenger
  4. In the originator register the type of message data you’re interested in with the Notification Message Action
  5. In the VM which is interested in the message register for the Message defined in step 1
  6. Provide a method that deals with the received messages and setups up the data context of the View

You can find out more information on the WP7Contrib and find out what people are saying on twitter #WP7Contrib.

As i mentioned at the start if you are already using MVVM Light today then you can take advantage of the Last Replay Message. Ollie is going to follow up with a more detail post on the implementation details.

Be Sociable, Share!

3 Responses to “WP7 Contrib – the last messenger”

  1. Hey Fox sorry about that my bad.

    I have not updated the source on codeplex for this spike if you get latest now you should be good.

    Let me know how you get on. Are you using DI in your implementation ?? Do you have any thoughts on other messages that you may find helpful ??

    rich
  2. Hello again. Thank you for the fixing.

    Im using DI in my APP (using Unity but Funq looks good too) but implementing it on the Locator (Your implementation on the bootstrapper like prism looks good, Im going to try it now). The messages itself, I have to improve my messages on an APP and this is the way to do that.

    Keep doing this good work :)

    Fox
  3. […] WP7 Contrib – the last messenger […]


Leave a Reply