This morning I woke up to find a rather curious question posted to the WPF Forum. A fellow by the name of Waseem Sadiq is binding a ContentPresenter directly to a business object (i.e. not to properties on the object) and using the ContentPresenter’s ContentTemplateSelector to load the appropriate data template at runtime.
I think the basic idea is that an entity in his system which is not “started” will be displayed as a “Start” button, and an entity which has already “started” will be displayed as a “Stop” button. Each button’s Command is set to a custom command to “start” or “stop” the entity, and the button’s CommandParameter is bound to the entity itself. Binding the CommandParameter property to the entity makes it easy to access it when the command is executed. For more details on his situation, read the post here.
The problem with the situation is that when a bound business object has a property modified, the property change notification is not noticed by the ContentPresenter which displays that object. The business object class implements INotifyPropertyChanged, but since the ContentPresenter is bound directly to the object (and not bound to a property on the object) its PropertyChanged event is irrelevant to the Binding.
Suppose that an entity which has “started” is suddenly “stopped” by some part of the application. Let’s say that the object’s IsRunning property is set from true to false. Since the ContentPresenter does not notice any property changes on the object, the entity’s button will continue to display “Stop” instead of changing to display “Start.” That’s a bug in the application (not the platform). The million dollar question is: how can one fix it?
The answer I came up with was to wrap the business object in a wrapper class which exposes it as a property. That wrapper class also implements INotifyPropertyChanged and raises its PropertyChanged event when the wrapped business object’s PropertyChanged is raised. You then create a value converter which takes in a business object and emits a wrapper around it. The value converter is then used in the DataContext binding of a top-level panel in the business object’s DataTemplate. Thus, all elements in the DataTemplate should bind to the public property on the wrapper which exposes the actual business object to display.
Here’s the source code for my demo app: Displaying Changes to Business Objects (change the file extension from .DOC to .ZIP then decompress). The demo app displays Foo objects (of course) and also a Button for each Foo. If the Foo object’s Bar property is even, that Foo’s button reads “Make Odd.” If it’s Bar property is odd, the button reads “Make Even.” Clicking a Foo’s button will execute a command which modifies the Bar property on that Foo instance. The wrapper class in the demo is called BusinessObjectHolder.