amputek
amputek

Reputation: 59

MVVM Best Practices: communication between view models

My simplified program structure looks like this:

public class Manager
{
    public Item MyItem { get; set; }

    public void Recalculate(){ ... } 
}

public class Item
{
    public string SomeProperty { get; set; }
}

public class ManagerViewModel
{
    public Manager Model { get; set; }

    public ItemViewModel MyItem { get; set; }
}


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

    public string SomeProperty
    {
        get => Model.SomeProperty;
        set 
        { 
            Model.SomeProperty = value;
            RaisePropertyChanged("SomeProperty");
        }
    }
}

When SomeProperty gets changed in ItemViewModel, I want Recalculate() to get triggered inside Manager.

Do I:

A) Have a PropertyChangedListener inside ManagerViewModel which listens for Property changes inside it's MyItem, and then tells it's Model to Recalculate()

B) Allow ItemViewModel to have access to Manager, so it can manually tell Manager to run Recalculate()

..

(B) seems kind of anti-pattern-ish... shouldn't each ViewModel only really be concerned with it's own Model? (A) has it's own issues -- I need to use this sort of 'Recalculation' structure a lot, and it seems having these PropertyChangedListeners all over the place is kind of messy. I realise there's a few different ways of going about this, but I'm just wondering what the 'best practice' is in this case.

Upvotes: 2

Views: 8208

Answers (2)

amputek
amputek

Reputation: 59

As confirmed by Ed Plunkett, 'option A' is the best approach, as it separates the concerns of the ViewModels and Models.

ItemViewModel is only concerned with it's own Model, and it just notifies whoever's listening that it's properties have been changed.

ManagerViewModel listens for changes inside ItemViewModel, and then executes Recalculate() inside it's own model.

//Models

public class Manager
{
    public Item MyItem { get; set; }

    public void Recalculate(){ ... } 
}

public class Item
{
    public string SomeProperty { get; set; }
}

//ViewModels

public class ManagerViewModel
{
    public Manager Model { get; set; }

    public ItemViewModel MyItem { get; set; }

    public ManagerViewModel()
    {
        //Listen for property changes inside MyItem
        MyItem.PropertyChanged += ItemPropertyChanged;
    }

    //Whenever a Property gets updated inside MyItem, run Recalculate() inside the Manager Model 
    private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        Model.Recalculate();
    }
}


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

    public string SomeProperty
    {
        get => Model.SomeProperty;
        set 
        { 
            Model.SomeProperty = value;
            RaisePropertyChanged("SomeProperty");
        }
    }
}

Upvotes: 2

Michael Puckett II
Michael Puckett II

Reputation: 6749

There's nothing wrong with making your Model(s) observable by adding INotifyPropertyChanged to them also. Then you can listen to the models you're bound to. In many projects I prefer to have a static data set layer, which publishes the list of models from a store and those models are all observable. This means they can be bound to and since the store is the same source any ViewModels etc can bind to them and become updated system wide. Looks like you're on the right track so don't second guess yourself. Making things observable, downright to the model, isn't bad or considered bad practice. I personally prefer it.

Upvotes: 1

Related Questions