Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to establish communication/pass data between two ViewModels

I'm aware this question has already been asked numerous times, and I've read many answers referring to it. Nevertheless, none of them fulfilled my requirements, and hence I need some advice.

The application I'm developing at the current moment looks like this: http://screenshooter.net/100101493/smrkpog

It's a WPF MVVM application based on MVVM Light Toolkit. The area with a grid is a ListBox + Canvas. The items (let's call them 'Neumes') consist again of a ListBox + Canvas each and are dynamically created by a user. Every Neume is composed of 'Elements' which are rendered as the red shapes.

When the user double-clicks a Neume, a new window opens. It is supposed to enable editing (translate, resize, rotate) of the Elements the selected Neume is made of. What I'd like to do is to pass my SelectedNeume (with a list of Elements) stored in my MainViewModel to the ViewModel of my newly created window. I have a few ideas on how to achieve this:

  1. Skip the new ViewModel entirely and set MainViewModel as the DataContext of the new window,
  2. Create a 'MainViewModel' field in the new ViewModel and pass it there when a new window is opened,
  3. Use Messaging/Mediator pattern provided by MVVM Light Toolkit.

I'm looking for a solution that is both simple and neat. I'd be most happy to make use of option 3., but I'm a relative newbie as far as WPF is concerned, and don't know how to start really. Also, I'm afraid there might be a problem with the fact that the new window is opened when a double-click event on the main ListBox is fired, since I couldn't find a way to bind a command to the ListBoxItem neither in its Style nor DataTemplate (the event trigger/EventToCommand solution didn't work for me).

This is the event:

    private void ListBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
    {
        var item = ((FrameworkElement)e.OriginalSource).DataContext as Neume;
        if (item != null)
        {
            var view2 = new EditWindow();
            view2.Owner = this;
            view2.SetDataContext();
            view2.ShowDialog();
        }
    }  

The SetDataContext() method currently takes care of binding the DataContext according to the 1. option:

public void SetDataContext()
    {
        if(this.Owner != null)
            DataContext = this.Owner.DataContext;
    }

My question is: Which option would you recommend? Are there any problems with either 1. or 2.? Possible memory leaks?

like image 686
SG_90 Avatar asked Aug 06 '14 11:08

SG_90


People also ask

How do you communicate between two ViewModels?

There are two ways to pass information between View Models: Implement the ISupportParameter interface to pass parameters between View Models: Passing Data Between ViewModels (ISupportParameter). Use the Messenger service: Layer Communication. Messenger.

How do I share data between two ViewModels?

I decided to do it the traditional way: 1) define a thread-safe singleton 2) in Application_Startup, instantiate the singleton and all the ViewModels (at least the VM's that need to share info with each other), then save references to all the VM's in the Singleton.

Can we pass view to ViewModel?

ViewModel as the bridge between the View and the Model. TL;DR: We can pass parameters to our ViewModel, use it as a data holder, also to share data between Fragments, and to persist its state across process recreation. This is part of a multi-part series regarding Advanced ViewModels on Android.

Can a view have multiple ViewModels WPF?

No that is fine; each object should be a ViewModel in its own right.


1 Answers

Personally I would go with option 3.

The messaging mechanism keeps your viewmodels separated from each other and once you work through one example, you'll see it is quite easy.

Personally I like to add a message broker class with static methods for each message type I want to send, this helps me centralise changes - but essentially you've got a send and receive. You can send what you want and if something wants to receive it they can.

MVVM Light is a great framework for this.

Send:

GalaSoft.MvvmLight.Messaging.Messenger.Send<LoginSuccessMessage>(new LoginSuccessMessage() { UserName = user });

Receive, in my target View Model constructor:

this.MessengerInstance.Register<LoginSuccessMessage>(this, this.OnLoginSuccessMessage);

Handler in the target View Model:

    private async void OnLoginSuccessMessage(LoginSuccessMessage message)
    {
        this.CurrentUserName = message.UserName;
        this.MoveToState(ApplicationViewModelState.Active);

        await Task.Delay(5000);
        this.MoveToState(ApplicationViewModelState.Idle);
    }

In this example, I am sending a user ID as a property on the message class:

public class LoginSuccessMessage : MessageBase
{

    private string _UserName;

    public string UserName
    {
        get
        {
            return this._UserName;
        }

        set
        {
            this._UserName = value;
        }
    }
}

Replace that property with whatever you want be that a collection or complex object.

like image 194
kidshaw Avatar answered Oct 31 '22 00:10

kidshaw