Update Controls and INotifyPropertyChanged: never the two shall meet (but if they have to, here’s how)
I recently corresponded with a new Update Controls user. He brought up some excellent points about INotifyPropertyChanged and Update Controls. The short answer is: don't mix them. Unfortunately, reality is not so cut and dried.
We started with some easy questions:
Should my view model implement INotifyPropertyChanged [when I'm using Update Controls]?
No, your view model should not implement INotifyPropertyChanged. When you call ForView.Wrap(), Update Controls implements it for you.
So ForView.Wrap() completely replaces the "{u:Update}" syntax?
Absolutely, it does. It is more Blend friendly.
But do I still need to define my Independent's using Ctrl+D, G?
Yes, the underlying dependency tracking is still exactly the same. It still requires Independent sentries, which Ctrl+D, G generates for you.
And then the hard questions began:
If I exposed properties of an object that implements INotifyPropertyChanged (for example, CslaDataProvider.IsBusy) via my ViewModel, do I need to do anything special? Or would it be so simple as:
public bool IsBusy { get { return _dp.IsBusy; } }
Unfortunately, ForView.Wrap() hides the INotifyPropertyChanged implementation of the root object or any of its properties. So simply passing through the property access would not be sufficient. Neither does the dependency tracking mechanism recognize INotifyPropertyChanged. So when the event is fired, Update Controls does not know to forward that along to the view.
If your class already implements INotifyPropertyChanged and you want to add Independent sentries too, that's fine.
public class Customer : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private string _name; private Independent _indName = new Independent(); public string Name { get { _indName.OnGet(); return _name; } set { _indName.OnSet(); _name = value; FirePropertyChanged("Name"); } } private void FirePropertyChanged(string name) { if (PropertyChanged != null) PropertyChanged.Invoke(this, new PropertyChangedEventArgs(name)); } }
The INotifyPropertyChanged implementation will be completely ignored by Update Controls, and the Independent will be completely ignored by legacy code.
Or, if you have a legacy object and you completely control the access to its properties, you can wrap it.
public class CustomerWrapper { private Customer _customer; private Independent _indCustomerName = new Independent(); public CustomerWrapper(Customer customer) { _customer = customer; } public string Name { get { _indCustomerName.OnGet(); return _customer.Name; } set { _indCustomerName.OnSet(); _customer.Name = value; } } }
But if someone else could change the controlled property without going through your code, you have to handle INotifyPropertyChanged yourself.
public class CustomerWrapper { private Customer _customer; private Independent _indCustomerName = new Independent(); public CustomerWrapper(Customer customer) { _customer = customer; _customer.PropertyChanged += (sender, e) => { if (e.PropertyName == "Name") _indCustomerName.OnSet(); }; } public string Name { get { _indCustomerName.OnGet(); return _customer.Name; } set { /*_indCustomerName.OnSet(); Not needed anymore. */ _customer.Name = value; } } }
When the event fires, you record that the property has been set. I'd love to generate a wrapper for you (something like ForViewModel.Wrap()), but I can't think of a way of doing that without hiding the underlying object properties. Maybe with some Ayende mojo I could pull it off, but I'm hoping that I never have to.