9

I'm getting confused; I thought I understood INotifyPropertyChanged.

I have a small WPF app with a frontend MainWindow class, a viewmodel in the middle and a model at the back.

The model in my case is the Simulator class.

The SimulatorViewModel is nearly transparent and just interfaces properties between MainWindow and Simulator.

Simulator implements INotifyPropertyChanged and each property setter in Simulator calls the RaisePropertyChanged method:

private string serialNumber;
public string SerialNumber
{
    get { return serialNumber; }
    set
    {
        serialNumber = value;
        RaisePropertyChanged("SerialNumber");
    }
}

public event PropertyChangedEventHandler PropertyChanged;

public void RaisePropertyChanged(string propName)  
{  
    if (this.PropertyChanged != null)  
    {  
        this.PropertyChanged(this, new PropertyChangedEventArgs(propName));  
    }  
}    

In the xaml, I have a TextBox with binding like this:

Text="{Binding Path=SerialNumber, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"

and the DataContext is the SimulatorViewModel (but see my comment about changing the DataContext to the model)

The ViewModel just passes the properties through:

public string SerialNumber  
{  
    get { return Simulator.SerialNumber; }  
    set { Simulator.SerialNumber = value; }  
}  

Programmatic updates to the property SerialNumber in Simulator are not propagating up to the UI although curiously the initial value, set in the Simulator constructor, is getting there.

If I break on the SerialNumber setter in the Simulator and follow into RaisePropertyChanged, I find that PropertyChanged is null and so the event is not propagating upwards to the GUI.

Which leads me to several questions:

  1. What exactly should be hooking into the PropertyChanged event? I'm looking to be more specific than just "WPF". What's the link between that event and the Binding statement in the xaml?

  2. Why is the initial property value getting up to the UI at startup, but not subsequently?

  3. Am I right to have Simulator (the model) implementing INotifyPropertyChanged, or should it be the ViewModel doing that? If the ViewModel does it, then programmatic changes in the model don't trigger PropertyChanged, so I'm unclear on the correct pattern. I realise my ViewModel is virtually redundant, but that's due to the simplicity of the project; a more complex one would make the ViewModel concept work harder. My understanding is that the ViewModel is the place to interface my unit tests.

ocodo
  • 29,401
  • 18
  • 105
  • 117
Alan
  • 215
  • 1
  • 2
  • 10
  • Could you include the code that is doing the updates to the SerialNumber property? – devdigital Feb 11 '11 at 10:30
  • @devdigital - SerialNumber = GenerateSerialNumber(); - I don't think that's the problem. But I have just discovered that if I set the DataContext to Simulator (the model), it all works. But doesn't that mean that a ViewModel is irrelevant? – Alan Feb 11 '11 at 10:35
  • what does your SimulatorViewModel look like? From what I can see your Simulator class has taken the role of the ViewModel. So currently your SimulatorViewModel is redundant. How are you propagating Simulator notifications to the SimulatorViewModel? – Goran Feb 11 '11 at 10:49
  • @Goran: I've put an example SimulatorViewModel property in the code above for clarity. Yes, the viewmodel does seem irrelevant in a simple case like this as I said, but in a more complex case it would be useful. I think you are right about propagating Simulator notifications to the SimulatorViewModel. Is it the case that the DataContext initiates wiring up the PropertyChanged event? That would explain a lot. But all this just seems to be leading to the question "what purpose does a ViewModel actually serve?" – Alan Feb 11 '11 at 11:02

2 Answers2

7
  1. The problem is that you're raising PropertyChanged on your model, but your view is bound to ViewModel. So your View subscribes only to ViewModel events (not Model's) - that is why textbox is not updated - because it never receives PropertyChanged event. One of possible solutions is to listen in ViewModel for Model PropertyChanged events and raise same event on ViewModel accordingly.

  2. Initial value is being propagated because your setters/getters in ViewModel are correct, the problem is in events.

  3. Yes, you are correct (see my #1), you should raise PropertyChanged on your ViewModel because View is bound to it. These events will not be triggered automatically after changes in Model, so you should listen for Model.PropertyChanged in ViewModel.

Simplest dirty fix to understand the idea:

SimulatorViewModel(Simulator model)
{
    // this will re-raise Model's events on ViewModel
    // VM should implement INotifyPropertyChanged
    // method OnPropertyChanged should raise INPC for a given property
    model.PropertyChanged += (sender, args) => this.OnPropertyChanged(args.PropertyName);
}
ocodo
  • 29,401
  • 18
  • 105
  • 117
Snowbear
  • 16,924
  • 3
  • 43
  • 67
  • Yes, that all makes sense, thanks. But this ViewModel thing is begining to bother me. I'm reflecting all my properties through view model (with extra code) and as you say, I may need to monitor model changes in the viewmodel with an event (with extra code): I'm beginning to doubt the need for a ViewModel, despite my assertion that it would be useful in a more complex project. What kind of stuff should really go in the viewmodel that really justifies the need for an intermediate layer? – Alan Feb 11 '11 at 11:36
  • Any properties or presentation logic that are appropriate for your view, but wouldn't make sense in your model would go in your view model. For example, you may want to display the Age of a user in your view, but your model only has a DateOfBirth property. You could use a framework such as CSLA to build rich models that you can bind to directly from your view to save the model->view model sync overhead. – devdigital Feb 11 '11 at 12:32
  • @Alan, ViewModels may have a lot of different use-cases. Most common use-case for me is when VM is supposed to be an editor of a model. In this case VM will contain some logic like Save, Rollback changes made in editor, display progress bar while saving, etc... This is the logic which makes a lot of sense in context of an editor, but no sense for a Model itself. – Snowbear Feb 11 '11 at 13:45
  • 1
    In WPF applications, you pretty much always need a view model class. But you don't necessarily need a separate data model class. It becomes useful once you need to separate out functionality into reusable and testable pieces, but if you don't need that, having a view model class that implements property-change notification and encapsulates the data model is fine. – Robert Rossney Feb 11 '11 at 20:01
  • While I agree WPF has a lot less friction when using MVVM especially because that's what the community has adopted I do think WPF can be used just as easy as WinForms to do everything-in-the-code-behind style apps regardless of whether it's a good idea to do so or not. The question of whether to use layering or using different UI patterns is really independent of WPF or any particular UI framework and should be understood and answered independently. – jpierson Nov 17 '12 at 19:06
1
  1. Always make sure that the instance of the class which has been set as Data-context should implement INotifyPropertyChanged
  2. In your case Simulator implements INotifyPropertyChanged but SimulatorViewModel is set as DataContext, in this case the UI will come to know only if there are changes in the SimulatorViewModel but not in the Simulator.

What you can do is that:

You can expose some events or delegates in the Simulator class and hook to some methods in SimulatorViewModel class. Then when ever values in Simulator class changes, invoke the event or the delegate that will get Execute on SimulatorViewModel, now you can get the updated values from the simulator and update the properties in SimulatorViewModel. Make sure that you use different properties in both the class

Kumareshan
  • 1,311
  • 8
  • 8