views:

50

answers:

1

Looking for guidance on where to place code which is dependent upon changes to a property.

For example, I have a view model which is used to hold state for an applications settings

public SettingsViewModel(ISettingsRepository settings)
{
    _settings = settings;
    // ...
}

For each change to a settings property we have to persist this change to the repository, and on some properties, other properties are effected, so additional code is required.

I started off just adding this logic to the setter

public ProjectCollection Projects
{
    get { return _projects; }
    set
    {
        if (_projects == value) return;
        _projects = value;
        RaisePropertyChanged("Projects");

        // Extra work needed when collection of projects change
        _settings.SaveProjects(_projects);
        Startable = _projects != null && _projects.Count > 0;
    }
}

But then swapped over to wiring up the PropertyChanged event for INotifyPropertyChanged and removed the additional code out of the property setter

public SettingsViewModel(ISettingsRepository settings)
{
    _settings = settings;
    // ...
    PropertyChanged += onPropertyChanged;
}

void onPropertyChanged(object sender, PropertyChangedEventArgs e)
{
    switch (e.PropertyName)
    {
        case "Projects":
            _settings.SaveProjects(Projects);
            Startable = Projects != null && Projects.Count > 0;
            break;
        // ...
    }
}

public ProjectCollection Projects
{
    get { return _projects; }
    set
    {
        if (_projects == value) return;
        _projects = value;
        RaisePropertyChanged("Projects");
    }
}

Having the logic inside the setter means less code to write, less chance of making a mistake wiring up the wrong property name (unit test should pick this up though) and would be slightly faster, although probably insignificant.

Having the logic wired up to an event seems to be a more maintainable approach, so long as the methods are appropriately named it should be easier to follow the code, and means the setter isn't doing other work besides setting the property. I'm guessing it might also provide more flexibility, using the example above, if the requirements changed so that persisting of changes happened from another event e.g. "Save" button click instead property change, then the code should be easier to change.

I appreciate this is a subjective question, but I'm new to the MVVM pattern (although I guess it may hold true for any setter logic?) so am looking for other reasons before I settle on an approach. Thanks!

+2  A: 

When deciding what code to put where, remember this: a ViewModel is for presenting the data to the View. It can massage or manipulate the data a little to do so (within reason of course - changing a value to a color could be considered absolutely fine to do). The ViewModel doesn't know anything about the UI, and it doesn't know anything about saving data.

The property setters should be kept as simple as possible, and they should notify if anything relies on them (which mostly will be the case - the VM is used for binding). This means that your second example utilizing the OnPropertyChanged() event handler is a far better option than the first example.

However, it still knows too much for my liking. I would raise events which the Model can subscribe to, and let the Model do the work. The ViewModel should just say "hey, my data has changed, I don't care what you do about it but i've told you so do whatever you like". The Model can then either save instantly, or what till more changes have taken place before persisting the data.

slugster
Ah, that makes perfect sense! So instead of passing the ISettingsRepository to the ViewModel, there would be a SettingsModel (which knows about ISettingsRepository), and this model is passed to SettingsViewModel. So the view model just set the models property in its setter, and the model is responsible for persistence (or whatever). That's much better, thank you slugster!
Si
Yep, you got it. The Model can either read the values out of the VM (because the Model is allowed to know about the VM), or the VM can communicate the changed values as arguments in the event that is raised.
slugster