How do I access data between ViewModels?

I am currently working on a project and for simplicity's sake, let's say there are two tabs in the TabControl ...

In one tab, you add folders to the ListBox.

The other tab has a ListBox that displays all the items in all the folders that you added.

Each tab represents a ViewModel (this simplifies the code, as dumping all the code in one ViewModel makes it difficult to read and understand).

For this program to work, both ViewModels need to access the list of items: one because it needs to display them, and the other because it needs to add to them.

I find it hard to figure out how to do this. Initially I thought that the data exchange was bad and it shouldn't have happened in the first place, but then I realized that I couldn't think of any other way to do this.

I'm new to MVVM (this is my first real application using MVVM) and started using it as I couldn't access data between classes and thought MVVM would somehow handle this problem, but here I am again.

I would appreciate it if someone could tell me how I can do this and maybe explain it with an example code. I am also open to suggestions and constructive criticism of my methods.

+3


source to share


6 answers


First of all, you must understand what a View is in MVVM. Look at this as a panel. A panel that can be embedded in a Window, TabControl, or even a ListBox. A panel that can also contain child panels. Basically, if your application is not just a simple input form, there is a good chance it will have more than one submission. Don't try to put everything in one View / ViewModel, because that will complicate things a lot later. You want to have a so called hierarchy of views and their respective ViewModels. There will be many Views / ViewModels, but they will be relatively simple and easy to maintain (here's a small example of switching between views using PRISM, but you can get the basic idea at https://youtu.be/ZfBy2nfykqY?t=45m34s ).

Each tab represents a ViewModel (this simplifies the code, as dumping all the code in one ViewModel makes it difficult to read and understand).

This is the correct approach. Here is some pseudo code that describes what your example application diagram looks like:

// MODEL:

public class Model
{
    ObservableCollection<Item> ListOfItems;
}

public class Item
{
}

// VIEWMODELS:

public class MainWindowViewModel
{
    Model Model { get; set; }

    Tab1ViewModel Tab1ViewModel { get; set; }
    Tab2ViewModel Tab2ViewModel { get; set; }

    public MainWindowViewModel()
    {
        Model = new Model();
        Tab1ViewModel = new Tab1ViewModel(Model);
        Tab2ViewModel = new Tab2ViewModel(Model);
    }
}

public class Tab1ViewModel
{
    ObservableCollection<ItemViewModel> ItemViewModels { get; set; } // Bind it to ListBox ItemsSource

    public Tab1ViewModel(Model m)
    {
        ItemViewModels = new ObservableCollection<ItemViewModel>();
        // Populate ItemViewModels and keep it in sync with ListOfItems model by subscribing to its CollectionChanged event.
    }
}

public class Tab2ViewModel
{
    ObservableCollection<ItemViewModel> ItemViewModels { get; set; } // Bind it to ListBox ItemsSource

    public Tab2ViewModel(Model m)
    {
        ItemViewModels = new ObservableCollection<ItemViewModel>();
        // Populate ItemViewModels and keep it in sync with ListOfItems model by subscribing to its CollectionChanged event.
    }

}

public class ItemViewModel
{
    Item Item { get; set; } // Model

    public ItemViewModel(Item item)
    {
        Item = item;
    }
}

      



Now you can display the same data in different views and perform different operations on it. Each view will automatically update as it references the same model.

You can also use EventAggregator or something similar to communicate between ViewModels.

Try to avoid static classes / singlets with data that can be accessed from anywhere in the application, as this violates the principle of encapsulation.

+3


source


You can have a singleton object and get / set its properties from anywhere.

Take a look at this example;

public sealed class ApplicationState
{
    private static readonly ApplicationState instance = new ApplicationState();

    static ApplicationState()
    {
    }

    private ApplicationState()
    {
    }

    public static ApplicationState Instance
    {
        get
        {
            return instance;
        }
    }


    public string SharedString {get;set;}
}

      

now you can set this SharedString property from anywhere:

ApplicationState.Instance.SharedString = "hello from VM1"

      



and read it from another view model like:

Debug.WriteLine(ApplicationState.Instance.SharedString)

      

You can watch this link to read more about singlons

you can even make your ApplicationState singleton an ObservableObject and bind it to your properties like:

Value="{Binding SharedString, Source={x:Static ApplicationState.Instance}}"

      

0


source


how dumping all the code in one ViewModel makes it hard to read and understand).

If you have a basic set of data to be used on a page, there is usually one view model per page. Unless each tab is a reusable unit that will be used elsewhere, having a virtual machine for each tab does not seem unnecessary to me.

difficult to read and understand

I ask you to distinguish, here are what comments are used to provide the logic of use.

My advice is to bring it to one virtual machine and solve this problem.

both ViewModels need to access the list of items:

You can put static properties in a program application and have virtual machine instances assigned to those statics. Then each virtual machine can access other VM data.

I've done this before when I need to access a virtual machine and I use a shared application as my access point.

I am new to MVVM

MVVM is just a three-tier data system for separating business logic (virtual machine) from objects (models) in terms of data. Don't get hung up trying to dogma MVVM.

0


source


Another approach would be to use a messaging system. For example, in PRISM you have IEventAggregator

to send messages inside your application. I think it IEventAggregator

is available as a separate dll.

It is really powerful and easy to use. You define a message, and a ViewModel that can add items sends an instance of that message (with an item argument). The Combo ViewModel can catch this message and add the item to its list.

One of the benefits is that both ViewModels don't need to know each other.

0


source


Use a single parent model that has links to the views you are viewing. I am assuming that in your viewmodel that the user selects, you are binding the view to an ObservableCollection.

In the parent view model, you can subscribe to change notification events on the ObservableCollection in the original view model, and then call a method in the second view model to populate the changes.

An example of using the ObservableCollection.CollectionChanged event: https://dotnetcodr.com/2015/05/29/getting-notified-when-collection-changes-with-observablecollection-in-c-net/

Another option, depending on the MVVM framework used, is to use the Messenging pattern to pass messages between disabled ViewModels - Use MVVM Light Messenger to pass values ​​between the View Model

0


source


Just use mvvm library like mvvm light, prism ... every mvvm library has a structured element to combine between view modes that you can use. - Don't write a new one. Don't reinvent the wheel. - Don't make an instance of the viewmodel parameter in another viewmodel. if you do it, it will be a pain in you.

0


source







All Articles