3
votes

I have a strange problem.

I'm using the MVVM Light framework from GalaSoft and everything so far works fine. I'm using the messenger system to send messages between ViewModels just fine, until I tried to do the following:

I have a singleton class, GateKeeper, that sends messages.

This class is NOT a ViewModel and thus does not inherit from ViewModelBase.

If it sends a message it is not received anywhere.

I have tried the following:

  1. Letting GateKeeper inherit from ViewModeBase -> No success.
  2. Registering GateKeeper to receive messages, thus seeing if it would catch/receive the message that was actually sent from itself -> No Success.
  3. Changing GateKeeper from Singleton to normal instantiation -> no Success
  4. Creating a MVVM ViewModel that is not connected to a view, and letting it send messages, just like the GateKeeper -> no Success

All my viewModels that are connected to a view can send messages and they will be received.

It almost seems like a viewmodel must be "linked" to a view before the messenger works,but imo. that is a major limitation.

Below is the current, very simplified setup.

Calling ApplicationInitialize on GateKeeper does not trigger a message received on the mainviewmodel nor the GateKeeper class itselves.

I hope that someone has suggestions to this problem.

Thanks..

Example setup: MainViewModel constructor:

 public MainViewModel()
    {
        Messenger.Default.Register<LoadViewMessage>(this, (message) =>
        {
            if (message.Sender is GateKeeper) CurrentView = message.View;
            else if (message.Sender is LoginViewModel) CurrentView = message.View;
            else if (message.Sender is MenuItemBarViewModel) CurrentView = message.View;
        });

GateKeeper:

 public class GateKeeper : IGateKeeper
{
    private readonly IEmployeeService _employeeService;

    #region Implementation of IGateKeeper

    public void ApplicationInitialize()
    {
        Messenger.Default.Send<LoadViewMessage>(new LoadViewMessage(ObjectLocator.MainMapView), this);
    }

    public void LoginSucceeded(Employee employee)
    {
        //This is where we retrieve the available services for the current employee
        //TODO: add methods for retrieving service info from backend

        //Send a message that should make the mainview load the map into its currentview property
        Messenger.Default.Send(new LoadViewMessage(ObjectLocator.MainMapView), this);
    }

    #endregion

    public GateKeeper(IEmployeeService employeeService)
    {
        _employeeService = employeeService;

        //Test.. Is not triggered
        //Just used for debugging, thus nothing happens inhere.
        Messenger.Default.Register<LoadViewMessage>(this, (message) =>
        {
            if (message.Sender is GateKeeper) ;
            else if (message.Sender is LoginViewModel) ;
            else if (message.Sender is MenuItemBarViewModel);
        });
    }

Message class: LoadViewMessage

 public class LoadViewMessage : MessageBase
{
    public UserControl View { get; protected set; }

    public LoadViewMessage(UserControl view, object sender): base(sender)
    {
        View = view;
    }

    public LoadViewMessage(UserControl view):this(view, null){}
}

PS: ObjectLocator is a NinJect class that handles all instantiation of objects and their lifecycle

@UPDATE LBugnion (Creator of MVVM Light) pointed out that the problem lied in the send method, where i was actually using a overload of Send that takes a token.

@This will not work in my situation

Messenger.Default.Send(new LoadViewMessage(ObjectLocator.MainMapView), this);

@This WILL work

Messenger.Default.Send(new LoadViewMessage(ObjectLocator.MainMapView, this));

this was supposed to be passed to the loadViewMessage and NOT the Send method as a token

1
I don't know why your code doesn't work, but I can tell you it should. The messaging system is not tied to the ViewModel/View relationship.jv42
Have you debugged (breakpoints) the GateKeeper methods? For instance, you're using a non-default constructor (with parameter), is it called?jv42
Excatly. It should work, and also does when i use it in my other viewmodels that are used in views. I have debugged all and the constructor of the GateKeeper is called. Im clueless to why it does not work here.ThBlitz

1 Answers

4
votes

Your problem is on the Send method. You are using the overload of the method that takes a token as second parameter. You are passing "this" as token. It means that you are (probably by mistake) using the sender of the message as token.

If you are sending with a token, you also need to register the receiver with the same token (in that case, the exact same instance than the one used in the Send method). Since you didn't register with a token, the message is never sent by the Messenger, which is an optimization mechanism.

My guess is that you misunderstood the usage of the token in the Send method. Tokens are only here as a way to build a "private messenging network" if you want, where two objects can use the same token to register/send and establish a private communication.

In your case, if you want to send the Sender together with the message, you'll need to save the Sender in the message itself, which is what MessageBase and the derived classes do.

Hope this helps

Laurent