Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Access Data between ViewModels?

I'm currently working on a project, and for the sake of simplifying explanations, let's say that there are two tabs in a TabControl...

In one tab, you add folders to a ListBox.

In the other tab, there is a ListBox that displays all of the items in all of the folders you have added.

Each tab is a ViewModel (this is to simplify code, as dumping all the code in one ViewModel makes it hard to read and understand).

In order for this program to work, both ViewModels need to access the list of items: one because it has to display them, and the other because it has to add to them.

I am having trouble figuring how to do this. I initially thought that sharing data is bad and this shouldn't have occurred in the first place, but then I realised that I can't think of any other way to do this.

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

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


2 Answers

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

Each tab is a ViewModel (this is to simplify code, as dumping all the code in one ViewModel makes it hard to read and understand).

This is the right approach. Here is some pseudo code which describes how your example application scheme should look 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's 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's 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. Every View will automatically be updated because it references the same Model.

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

Try to avoid static classes/Singletons with the data that could be accessed from anywhere in your application because this breaks the Encapsulation principle.

like image 137
EuKa Avatar answered Apr 19 '26 11:04

EuKa


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

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 like;

ApplicationState.Instance.SharedString = "hello from VM1"

and read it from another view model like;

Debug.WriteLine(ApplicationState.Instance.SharedString)

You can take a look at this link to learn more about singletons

you can even make your ApplicationState singleton an ObservableObject and bind to its properties from your views like;

Value="{Binding SharedString, Source={x:Static ApplicationState.Instance}}"
like image 32
Hasan Avatar answered Apr 19 '26 09:04

Hasan



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!