PropertyChangedBase NotifyOfPropertyChange()

Topics: Bugs, UI Architecture
Jul 24, 2013 at 7:59 AM
Yesterday I finished porting a quite complex application to Caliburn, and started testing it. I had a nasty bug which took a while to find and solve, which is basically like this:
  • A background thread updates some properties in the VM.
  • Since the VM is thread-safe, read/write locks control the access to the previously mentioned properties.
  • As soon as I call NotifyOfPropertyChange() to notify the UI, the application immedaitely encounters a deadlock, because the code which notifies the property change currently owns a write lock on that same property.
  • I found the hard way that the NotifyOfPropertyChange raises the PropertyChanged event synchronously on the UI Thread, via Execute.OnUIThread(), hence the deadlock. My previous code just raised the event on the background thread, leaving the marshaling stuff to WPF. I guess WPF just let the UI thread read the refreshed properties after my background thread completed, so there's no deadlock.
Is there any particular reason NotifyOfPropertyChange raises the PropertyChanged event on the UI thread, when WPF does that itself (on its schedule obviously)?
Jul 24, 2013 at 8:15 AM
This behaviour is by design, and caused by the fact that WPF is the only platform supporting auto-marshalling for PropertyChanged events (which is a pity!).
Have a look at this request, you should be able to override the default CM behavior either on a per-class basis, or creating a custom base class for your Screens and Conductors.
Jul 24, 2013 at 8:50 AM
An #IF directive would have been enough to handle this case, or an optional parameter for NotifyOfPropertyChange(), to specify If Caliburn should do the marshaling or leave it to the current UI framework. Given the open architecture of Caliburn, I find this design choice a bit harsh.
Jul 24, 2013 at 9:21 AM
I agree with you (as stated in the referenced request).
Unfortunally, the #IF directive would require a CM rebuild from the sources, that's why I would have preferred an extension point (such as either a static delegate used to raise the property changed event, or even a static variable in the PropertyChangedBase class) to enable/disable this feature. Unfortunally, the current solution (overriding) is as much as I could achieve (and all in all, it is not so bad... except that you need to create a set of base classes or remember to override the method for each view-model). :)