In a MVVM based application if you decide to delegate all the communication to the infrastructure using a broker, so to respect the single responsibility principle, you end up dealing with problems that in a much more coupled environment would never arise.

Imagine the following scenario: As a user when I create e new order I need to choose the customer that owns the order so to successfully associate the order with the given customer.

In “programming” language the above translates to: when the user creates a new order and want to choose a customer we need to open a search dialog to let the user search for the customer and then return the chosen customer to the calling view.

I have highlighted the keywords in bold, we have 2 different contexts that needs to communicate but we do not want to have a direct relation between the 2 contexts, in other words: we do not want to open the dialog from the “create new order view model” but we want to delegate all the communication to the broker.

There are several options to achieve the same behavior but we think that the most important thing to avoid is to try to achieve the same “blocking” behavior that a dialog gives us because this prevents to move to a different UX without changing the implementation.

2-way messaging

The first viable approach is to use 2 messages where the first one is to request the start of the selection process and the second one is delivered by the selection process to send back the selection result(s):

class SelectCustomer
{
    public object RequestToken{ get; set; }
}

class CustomerSelected
{
    public object RequestToken{ get; set; }
    public IEnumerable<Customer> Selection{ get; set; }
}

We need to introduce the concept of token so that the receiver when receives the response can determine if the response if for its own request or should be discarded because is someone else request:

class OrderViewModel
{
    IMessageBroker broker
    object requestToken = null;

    public OrderViewModel( IMessageBroker broker )
    {
        this.broker = broker;
        this.broker.Subscribe<CustomerSelected>( this, ( s, m ) => 
        {
            if( m.RequestToken == this.requestToken )
            {
                //do something with the selection
            }
        } );
    }

    void Select()
    {
        this.requestToken = new object();
        this.broker.Broadcast( this, new SelectCustomer() );
    }
}

The benefit of this approach is that someone else can be interested in the selection and we do not have to do anything to plug “that” someone else into the process.

Message callback

an easier approach is to use one single message:

class SelectCustomer
{
    public object RequestToken{ get; set; }
    public Action<IEnumerable<Customer>> Callback{ get; set }
}

so that we do not have to deal with tokens to identify responses:

class OrderViewModel
{
    IMessageBroker broker

    public OrderViewModel( IMessageBroker broker )
    {
        this.broker = broker;
    }

    void Select()
    {
        var msg = new SelectCustomer()
        {
            Callback = results => 
            {
                //do something interesting with the results
            }
        };
        this.broker.Broadcast( this,  );
    }
}

in this case when the selection process is finished the view model that handles the search/selection simply invokes the callback with the selection results.

Dedicated services

The last approach is to wrap the above logic into a custom component, such as an ISelectionService<T>, that can be something like:

interface ISelectionService<T>
{
    Task<IEnumerable<T>> Search();
    Task<IEnumerable<T>> Search( String query );
}

where the fact that we return a Task can be handy because can be used in conjunction with the async/await keywords and perfectly fit the async nature of the message broker broadcasting engine.

Last edited Jun 16, 2013 at 7:52 AM by topics, version 5

Comments

0v3rCl0ck Feb 12, 2014 at 6:24 PM 
Sorry, I omit to tell you my ideas:

1 - In a tabbed application one idea could be to unsubscribe to all the messages (that you don't want to be alerted) of the previous active tab, and subscribe the messages on the tab that will become active.

2 - In a multi windows application, the idea is similar to 1, but listening the activate/deactivate window event, and do the same subscribe/unsubscribe like idea 1

But what about if you have a couple of master-detail in the same view/window/tab?

Do you think there could be a generic way to handle all this cases?

0v3rCl0ck Feb 12, 2014 at 6:08 PM 
And if instead different views wants to communicate each other creating a group of views let's say a context, to avoid a broadcasted message to change all the views in the application, but just the one that are in the same context, how do you think is a good way to resolve this?

For example let's think about a simple master detail application, a view containing the master rows, and a view containing the detail panel. The master broadcast MasterSelectedMessage when the user select a master row. For some reason I would like to decouple those views, so I can reuse the master view for another kind of detail view and all the detail views know how to subscribe and act on MasterSelectedMessage:

MasterView - MasterViewModel
DetailView - DetailViewModel

MasterSelectedMessage

...and another detail view that can respond to the same master selected message:

AlternativeDetailView - AlternativeDetailViewModel

then think about to have a couple of master-detail visible somewhere, in the same view, or different tab, or again different widows to support multiple monitors:

context1[
<MasterView>
<DetailView>
]

context2[
<MasterView>
<AlternativeDetailView>
]

when the master broadcast the message MasterSelectedMessage, both viewmodels DetailViewModel and AlternativeDetailViewModel, receive the message and change the detail panel according to the selectedItem present in the message, but actually I want to group one MasterViewModel with the DetailViewModel and another MasterViewModel with the AlternatvieDetailViewModel, so they can act separately. In other words, if I select a master row of context1, I want that the message is consumed just by the DetailView of the context1

Is there any token or context in the radical ui composition implementation?