Damian
Damian

Reputation: 119

How to share multiple ObservableCollections with multiple ViewModels?

I am writing a program with 4 Views: CompanyView, MembersView, WeeksView and ReportsView. Each has a corresponding ViewModel and Model. I have used PRISM BindableBase to create the ViewModels. The data binds correctly.

Navigation is performed via top buttons in the MainWindow, with any View able to be selected at any time.

Container.RegisterTypeForNavigation<CompanyView>("CompanyView");
Container.RegisterTypeForNavigation<MembersView>("MembersView");
Container.RegisterTypeForNavigation<WeeksView>("WeeksView");
Container.RegisterTypeForNavigation<ReportsView>("ReportsView");

The problem is that WeeksViewModel must also have access to the Members ObservableCollection. And the ReportsViewModel must also have access to the Company Object, Members ObservableCollection and the Weeks ObservableCollection.

I am not sure how to implement this. How can I easily share the data between the ViewModels?

I have tried using the PRISM IEventAggregator to Publish the ObservableCollections when they are updated, and this works, however the View must first be accessed before it can listen to the Event. If the user has not clicked on "Weeks" view before, the updated MembersCollection wont reach the WeeksView. Could I pre-initialise the Views? How would I do this?

I followed the MVVM Made Simple with Prism - Webinar (https://www.youtube.com/watch?v=ZfBy2nfykqY) and discovered the same problem written in the comments. Brian Lagunas suggests the only way to fix this is via Navigation Parameters:

I have a small problem with the UpdateEvent passing the message from ViewAViewModel to ViewBViewModel. It doesn't seem to work if I have not visited ViewB, I assume this is because the ViewBViewModel has not yet been instantiated until the view for it has been loaded at least once.

Anybody got any ideas on this, I assume instantiating all viewmodels before they're needed is a bad idea, so how can you get default information into a viewmodel from other viewmodels before it's been instantiated?

Brian Lagunas: The only way to do it is to pass that information as a parameter when you navigate to ViewBViewModel.

I have considered using Navigation Parameters, but it seems you must know from WHERE to WHERE you are navigating. E.G. From MembersView > WeeksView, pass the Members Collection as a Parameter. But users can naviagate in any order, including directly to WeeksView on program load. e.g. How does the WeeksView get the Members Collection if they came from the Company View? Company does not know about Members Collection.

I am open to other ideas, I have researched far and wide and am completely stuck :-(

Thanks very much for your ideas and help! Kind Regards, Damian

Upvotes: 2

Views: 1583

Answers (2)

Haukinger
Haukinger

Reputation: 10863

I'd suggest to make the Company, ObservableCollection<Week> and ObservableCollection<Member> come from one or more services.

The WeeksViewModel then receives ObservableCollection<Week> and ObservableCollection<Member> from the service(s) and processes the data for consumption by the WeeksView. The ReportsViewModel processes Company, ObservableCollection<Week> and ObservableCollection<Member> for consumption by the ReportsView... If someone adds a Member, he does that through the service possessing the ObservableCollection<Member>, and all view models that received the ObservableCollection<Member> from that service, will get a notification and update accordingly.

There's no need to pass data around between view models, because the view models do not own the data. All they ever do is transforming data from some data source for a view to display, and the other way round convert user actions to calls to a service that modifies the data.

The moment you have view models that own data, you get methods like UpdateData on the view models, and ultimately you end up with unintelligible spaghetti code of UpdateThis and UpdateThat or one mm8-style mega view model that's used by all views. You don't want that.

Upvotes: -2

mm8
mm8

Reputation: 169150

The problem is that WeeksViewModel must also have access to the Members ObservableCollection. And the ReportsViewModel must also have access to the Company Object, Members ObservableCollection and the Weeks ObservableCollection.

I am not sure how to implement this. How can I easily share the data between the ViewModels?

You could use a single view model with properties for each specific view model and the common properties to be shared across two or more view models, e.g.:

class MainViewModel
{
    public CompanyViewModel CompanyViewModel { get; set; }
    public MembersViewModel MembersViewModel { get; set; }
    public WeeksViewModel WeeksViewModel { get; set; }

    public ObservableCollection<Members> Members { get; set; }
}

Then each of the child views can inherit the DataContext of the parent window and bind to any property they want across all view model types.

The other option would be to pass information back and forth between the view models, either using direct references, an event aggregator or a shared service.

But it seems kind of pointless to desperately try to separate the logic between the view models if they still must access properties of each other. So I would probably go with the first approach.

Upvotes: 3

Related Questions