0
votes

Imagine a WPF application with one main view representing a window that can be opened multiple times. This main view has several child views.

The messenger could send messages between the viewmodels of the main view and its child views' viewmodels.

Can the messenger be made to have a restricted scope so that if you had two main views open, interacting with one would message only that view's child views?

Is there another way to share an "identity" between the parent and child view models in a way that honours MVVM?

2

2 Answers

1
votes

You can always choose between passing default IMessenger which will have static, application domain scope or creating new instance of messenger:

var domainWideMessenger = GalaSoft.MvvmLight.Messaging.Messenger.Default;
var localMessenger = new Messenger();

First approach is useful when you don't want to control scope of messenger. You can treat it as a central hub. "local" messengers are good for communications within VM or within some container.

Why this might be better then having tokens?! When you have advanced application with messaging built on tokens at some point you'll face difficulties handling them (picking the right one). Especially when it comes to dependency injection.

In your case you'll have new instance of Messenger per MainView which will be pushed down to all its child views and view models. To sync data and communicate between multiple instances of MainView use static Messenger from MVVM Light.

1
votes

You could use the concept of "tokens" to achieve this effect.

The IMessenger interface has overloads of Register and Send that accept an object to restrict which registrants receive a message. If a message is sent with a token, the only objects that will see the message are those that registered for the message with the same token. Here, "same" means object equality, so you can use any object for the token that has sensible equality semantics and makes sense to you, i.e. a GUID, integer, or string.

As an example, consider the following objects:

public static class MessengerHelper
{
    public static IMessenger Messenger { get { return GalaSoft.MvvmLight.Messaging.Messenger.Default; } }
    public static object Group1Token { get { return 1; } }
    public static object Group2Token { get { return 2; } }
}

public class FooChild
{
    object token;

    public FooChild(object token)
    {
        this.token = token;
        MessengerHelper.Messenger.Register<IFooMessage>(this, token, HandleFooMessage);
    }

    void HandleFooMessage(IFooMessage fooMessage)
    {
        Console.WriteLine("FooChild got the message, token = " + (token ?? "(null)"));
    }
}

public class FooParent
{
    FooChild[] children;

    public FooParent()
    {
        children = new [] { 
            new FooChild(MessengerHelper.Group1Token),
            new FooChild(MessengerHelper.Group2Token),
            new FooChild(null)
        };
    }

    public void SendFooMessage(IFooMessage fooMessage, object token)
    {
        MessengerHelper.Messenger.Send(fooMessage, token);
    }
}

Then if you create the parent and send a message with the given tokens:

FooParent parent = new FooParent();
parent.SendFooMessage(new FooMessage(), MessengerHelper.Group1Token);
parent.SendFooMessage(new FooMessage(), MessengerHelper.Group2Token);

You'll get the following output:

FooChild got the message, token = 1

FooChild got the message, token = 2

In your case, you'll want each main view models to have their own token, and pass their token to their child view models.