tags:

views:

96

answers:

3

Consider an application which is implemented using MVVM, in which the user inputs data.
When the user selects "open file", "new file" etc., the application asks the user if he wants to save the data to a file before invoking the requested command. However, if the user did not change any data since the last save, the application should skip that question and continue directly to invoking the command. Note that only properties that ultimately belong to the model should change the status. For example if the user changed the selected element in a list (which changes the VM) it doesn't require saving.

How would you implement such requirement?
Manually raising an event whenever some property in the VM is changed seems tedious and error-prone.

Thanks, Elad

+1  A: 

With MVVM (generally), every class needs to implement INotifyPropertyChanged, and every property setter should trigger a property changed event:

public class Objective : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private string _text;

    public string Text
    {
        get { return _text; }
        set
        {
            if (_text != value)
            {
                _text = value;
                FirePropertyChanged("Text");
            }
        }
    }

    private void FirePropertyChanged(string s)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(s));
        }
    }
}

Yes: it's error prone and even with some clever usage of lambda's the compiler support isn't great. But that's how MVVM needs to be done, and you can see why on most introductions to MVVM.

In order to answer your question, however, i'd recommend that you just add some custom code to your FirePropertyChanged method:

    public bool HasChanged { get; set; }

    private void FirePropertyChanged(string s)
    {
        HasChanged = true;
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(s));
        }
    }

And you'd need to remember to set HasChanged to false after you've finished reading the object from a file or database.

10 points if you can tell me why HasChanged doesn't fire PropertyChanged, despite what I said above. This means, of course, that you shouldn't use HasChanged from XAML

edit: There's a great sample of this kind of thing here: http://follesoe.no/silverlight/divelog/

Rob Fonseca-Ensor
Rob - did you declare HasChanged as public for a reason? surely it wouldn't need to be visible outside the scope of this VM?
IanR
Wasn't making any assumptions about which bit of code would check the HasChanged property and decide to present a "DO YOU WANT TO SAVE" dialog.
Rob Fonseca-Ensor
A: 

How about using PostSharp?

I'd create 2 attributes:

1. RaisePropertyChanged: INotifyPropertyChanged with postsharp

2. And second similar one that only sets the HasChanged flag:

public class MainWindowViewModel : ViewModel
{
    [RaisePropertyChanged]
    public string Message { get; set; }

    [RaisePropertyChanged]
    [SetsHasChanged]
    public string DataThatCausesModifyDialog { get; set; }

    // ...
}
Pawel Lesnikowski
Did you have good experience with PostSharp? I've found it to have quite large performance hit, plus it didn't work under certain circumstances.
Elad
I had positive experience with PostSharp. There is a preformance hit during compilation, as it needs to inject additional code by decompiling the assembly (it was improved in 1.5 version). I think it's worth using.
Pawel Lesnikowski
A: 

Yaa, fine I am getting the PropertyChangedEvent, but I have another problem in activating/displaying another View on this event.

Naresh Goradara