13

How do I share data between multiple ViewModels ?

For example there is a class named Project in application .

    public class Project : ModelBase
{
    private string _projectName;

    public string ProjectName
    {
        get { return _projectName; }
        set
        {
            _projectName = value;
            RaisePropertyChanged(() => ProjectName);
        }
    }
}

In multiple ViewModels application should access ActiveProject.
What's the best way to share Project between ViewModels ?

  • Mediator Pattern ? (Messaging)
  • Static object
  • Singleton pattern (If yes how?)

I've used Messaging before but it needs much codding . For all ViewModels I've to create ActiveProject property and also have to register a messenger to update that.


I use MVVM Light framework.
Any code example would be appreciated.

Unforgiven
  • 1,999
  • 5
  • 19
  • 18
  • Is your `ActiveProject` created somewhere by one of the view models? If so, messaging would probably be the best (and it's not *that* verbose). Other option is to inject an `ActiveProject` to every view model that needs it while setting the `ActiveProject` lifetime to singleton in the IoC container of choice - third option you've suggested... But so far it's mostly guessing on my part. – Patryk Ćwiek May 13 '13 at 20:59
  • 2
    @TrustMe-I'maDoctor Why do we WPFers always overcomplicate everything? Wouldn't it be much easier to store that in the `App` class as a static property? – Federico Berasategui May 13 '13 at 21:01
  • @HighCore That's always an option... A static read-write property rubs me in a wrong way though, too easy to abuse. :) – Patryk Ćwiek May 13 '13 at 21:02
  • @Trustme-I'maDoctor Using DI techniques would be great. please give me an example or sample link related to my problem. – Unforgiven May 13 '13 at 21:18
  • @Unforgiven e.g. SimpleInjector automatically resolves the concrete types, but you can do `container.RegisterSingle(() => new Project());` and then just request a `Project activeProject` in the constructor. Other IoC containers also support named injection, so you can use that too. – Patryk Ćwiek May 13 '13 at 21:22

5 Answers5

7

I would create a ViewModel that acts as a parent to all the Project ViewModels. (Let's call it Solution)

The Solution ViewModel would have the property ActiveProject and an observable collection of Projects.

Emond
  • 50,210
  • 11
  • 84
  • 115
  • 2
    but then... if you have multiple viewmodels for one view, how do you bind this shared property? – inside Apr 09 '15 at 14:31
  • 1
    @inside - there is one top viewmodel, that assigned to the DataContext of the View. The other viewmodels are properties of the this top viewmodel and can be bound to by specifying the bindingpath relative to the top viewmodel – Emond Apr 09 '15 at 15:13
  • 3
    I solved it in a bit different way. I have multiple viewmodels that all share the same ViewModelBase class, in ViewModelBase I have Singleton of "Projects" list, then in my View I just specify binding path as childVM.Projects, so it doesn't matter which vm it is, they will all share the same instance of Projects. This allowed me to have multiple viewmodels per DataContext and no ambiguity is present. – inside Apr 09 '15 at 15:27
  • 5
    @inside _"Singleton - always the preferred solution"_, said no programmer ever :D – Gusdor Jul 28 '15 at 08:12
  • 2
    And? This is far too vague! – AndyUK Jun 06 '17 at 06:52
6

I would recommend the Mediator Pattern. I have used an EventAggregator for this type of messaging between VM's before and there is really not much to it.

Jacob
  • 3,598
  • 4
  • 35
  • 56
Kevin
  • 4,586
  • 23
  • 35
  • 2
    This should really be the accepted answer. In addition, it appears that people that are running into this anti-pattern are only building two-layered applications without the model as the third layer. – Scott Nimrod Oct 22 '14 at 12:34
4

Don't, don't. Don't use singletons this way in your MVVM application. In fact, the Project class should be a model for your ViewModels. Just pass it in vm's constructor. If you really need to share one instance of Project class in multiple vm's, then use factories and some type of cache when constructing view models. If your vm reguires some more information, just create special Model class which will derive from Project (or implement IProject), so you can easilly use interface segregation principle.

Lukáš Koten
  • 1,191
  • 10
  • 22
1

You could have a static collection which your view model populate before you navigate to the new view model. The target view model can then retrieve the data from within it's constructor.

For example ViewModel1 (VM1) will create a Project and populate it. VM1 will then put the Project into a shard, static, collection. VM1 will then navigate to another view model (VM2). In the constructor of VM2 you would go to the collection and retrieve the Project placed in there by VM1.

If you used a dictionary of key-value pairs it would also allow you to share other data between view models.

MotoSV
  • 2,348
  • 17
  • 27
1

Singleton will definitely help. To implement, if I had a class named User:

    private static User mInstance;

    private User () //constructor
    {
    }

    public static User Instance
    {
        get
        {
            if (mInstance == null)
                mInstance = new User();
            return mInstance;
        }
    }
Jeff
  • 972
  • 5
  • 11