Model-View-Controller Architecture for WPF: An Approach.
Do you know that disappointing feeling you get, when you learn a new technology, you start using it, just to discover that all the examples of the new technology across the internet don't amount to a practical real-life application?
The problem with conventional WPF examples
I am far from an expert in UI, especially windows forms UI, which makes me the perfect victim for bad examples. This is the feeling that I am getting with WPF right now. Where are the best practices? Its a very impressive technology, and it makes for some very entertaining parlor tricks. We see new sample interfaces that look like video games. We see a plethora of code samples for new tricks, but few of them form to any semblance of a practical windows application.
New Technology Causes new Confusion
The other thing we get with WPF is a very dangerous period of conceptual oblivion as we try to figure out where we should define the bindings, where should we define behavior, where to draw the line between presentation and logic etc.
One thing I am discovering (the hard way) is that if you just want to write a normal program, a lot of the classic design patterns are still very relevant with WPF.
The Danger of "Direct" DataBinding
DataBinding is where I got burnt.
Think about it: everywhere you look, you see examples of a WPF UI directly bound to a business object. Direct binding between the UI and a business object has some dangerous implications:
What happens when I type beyond this limit? As a default behavior, WPF swallows the exception! It turns out you need to write a custom binding object to display the exception to the user in a messagebox.
What is the recommended solution for databinding to objects? Your objects need to implement IDataErrorInfo. The gist of implementing IDataErrorInfo is this:
This code gives me a migraine headache. Why should my business objects EVER exist in an invalid state?
The recommended solution seems like an antipattern to me. Imagine that, all of your business objects implementing INotifyPropertyChanged, INotiftyCollectionChanged, IDataErrorInfo just so they can work with WPF?
But still, databinding is a very cool feature in WPF. How to leverage this without violating my model? I made a mistake, I was so focused on using new WPF tricks that I forgot how to build a good testable application.
My (current) Solution
MVC is a design pattern intended to solve the problem between separating a UI and the logic. These were the qualities I was looking to achieve:
The sample program is here. It is a very simple, practical windows program for adding/editing/removing blog posts. The main attraction is the architecture.
The Models
The model is simple. I have a Blog that contains a collection of BlogEntries, and they are both "Persistable" meaning they have an ID and a name.
The Blog class exposes a list of its BlogEntries, but it has specialized Add/Remove methods for modifying this list.
The Views
Since I needed the view to be mockable, I set up for an interface, such as IView.
Any view must have an associated controller, and be able to update its display with the latest data. I create a view for every aspect of the program. In this case, I have a view for BlogEntries, and a view for Blogs. These views generate events that are handled by the controllers. They use BlogEntryEventArgs as arguments for the events.
The Controllers
The controllers exist to handle generated events from the views, and this is where the real business logic begins. As you can see, there is an event handler for every event in the view. One noteworthy thing here is that the controller does not need to talk back to the view for updates because the view is directly bound (in a readonly fashion) to the model. That little feature is thanks to the WPF databinding power!
Finally, The UI
The UI is two windows, one for selecting blog entries and one for editing them. Each window implements a view. Upon window construction, each window registers a controller. Button callbacks from the UI generate events sent to the controller. The windows catch and display any exceptions, and the UI is automatically updated by the direct databinding to the model.
What about tests?
How to test UI interaction without a UI? Make a "mock" of the views, and test the controller. Since our views are dumb, we can effectively test the bulk of the application using a dummy view. For example:
Other Observations
Admittedly, this is yet but another simple example for a tricky concept. Does this architecture scale nicely as the complexity grows? Your mileage may indeed vary.
In my example, I have a view-per-model design, but
Download the Source Code here
The problem with conventional WPF examples
I am far from an expert in UI, especially windows forms UI, which makes me the perfect victim for bad examples. This is the feeling that I am getting with WPF right now. Where are the best practices? Its a very impressive technology, and it makes for some very entertaining parlor tricks. We see new sample interfaces that look like video games. We see a plethora of code samples for new tricks, but few of them form to any semblance of a practical windows application.
New Technology Causes new Confusion
The other thing we get with WPF is a very dangerous period of conceptual oblivion as we try to figure out where we should define the bindings, where should we define behavior, where to draw the line between presentation and logic etc.
- Should I use a ContentTemplate? Maybe a DataTemplate?
- Should I define my bindings declaratively, or should I do it manually in the codebehind? If I do it in the XAML, should I use the XAML notation, or the odd new binding syntax with the curly braces?
One thing I am discovering (the hard way) is that if you just want to write a normal program, a lot of the classic design patterns are still very relevant with WPF.
The Danger of "Direct" DataBinding
DataBinding is where I got burnt.
Think about it: everywhere you look, you see examples of a WPF UI directly bound to a business object. Direct binding between the UI and a business object has some dangerous implications:
- As I type/click etc... I am causing immediate changes to the business object. How do I undo them?
- Since the binding is two-way and immediate, does this mean I no longer need an "Apply" button anymore?
- What if I want to cancel what I was just doing?
What happens when I type beyond this limit? As a default behavior, WPF swallows the exception! It turns out you need to write a custom binding object to display the exception to the user in a messagebox.
What is the recommended solution for databinding to objects? Your objects need to implement IDataErrorInfo. The gist of implementing IDataErrorInfo is this:
public virtual string this[string propertyName] {
get {
//find the property that matches the string "propertyName"
//check the current state of the property, is it valid?
//if its valid, return string.Empty
//if its invalid, return a formatted error
}
This code gives me a migraine headache. Why should my business objects EVER exist in an invalid state?
The recommended solution seems like an antipattern to me. Imagine that, all of your business objects implementing INotifyPropertyChanged, INotiftyCollectionChanged, IDataErrorInfo just so they can work with WPF?
But still, databinding is a very cool feature in WPF. How to leverage this without violating my model? I made a mistake, I was so focused on using new WPF tricks that I forgot how to build a good testable application.
My (current) Solution
MVC is a design pattern intended to solve the problem between separating a UI and the logic. These were the qualities I was looking to achieve:
- "smart" business objects, they throw exceptions if you try to pass them anything invalid, or something goes wrong in an operation
- "dumb" WPF UI that is bound in a dynamic, readonly fashion to the objects
- The UI catches exceptions and shows them to the user in a messagebox
- A "controller" that receives gestures from the UI and performs logic on the model
- The controller should be very testable
- The UI should be very mockable
- The UI has no direct write access to anything in the business objects
The sample program is here. It is a very simple, practical windows program for adding/editing/removing blog posts. The main attraction is the architecture.
The Models
The model is simple. I have a Blog that contains a collection of BlogEntries, and they are both "Persistable" meaning they have an ID and a name.The Blog class exposes a list of its BlogEntries, but it has specialized Add/Remove methods for modifying this list.
The Views
Since I needed the view to be mockable, I set up for an interface, such as IView.Any view must have an associated controller, and be able to update its display with the latest data. I create a view for every aspect of the program. In this case, I have a view for BlogEntries, and a view for Blogs. These views generate events that are handled by the controllers. They use BlogEntryEventArgs as arguments for the events.
The Controllers
The controllers exist to handle generated events from the views, and this is where the real business logic begins. As you can see, there is an event handler for every event in the view. One noteworthy thing here is that the controller does not need to talk back to the view for updates because the view is directly bound (in a readonly fashion) to the model. That little feature is thanks to the WPF databinding power!Finally, The UI
The UI is two windows, one for selecting blog entries and one for editing them. Each window implements a view. Upon window construction, each window registers a controller. Button callbacks from the UI generate events sent to the controller. The windows catch and display any exceptions, and the UI is automatically updated by the direct databinding to the model.
What about tests?
How to test UI interaction without a UI? Make a "mock" of the views, and test the controller. Since our views are dumb, we can effectively test the bulk of the application using a dummy view. For example:
At this point, feel free to replace MockBlogEntryView with something from your favorite mocking API such as Rhino Mocks or TypeMock.NET if you are too lazy to define a dummy view.
private class MockBlogEntryView:IBlogEntryView{...}
///
/// isolate the controller for testing
///
[Test]
public void TestCreate()
{
Blog _blog = new DummyBlogDAO().GetByID(0);
MockBlogEntryView _mockBlogEntryView = new MockBlogEntryView();
BlogEntryController _blogEntryController = new BlogEntryController(_mockBlogEntryView);
BlogEntryEventArgs _blogEntryEventArgs = new BlogEntryEventArgs(null, "blahblahblah", "New post", _blog);
_mockBlogEntryView.ThrowCreateEvent(_blogEntryEventArgs);
}
Other Observations
Admittedly, this is yet but another simple example for a tricky concept. Does this architecture scale nicely as the complexity grows? Your mileage may indeed vary.
In my example, I have a view-per-model design, but
- There is no real correlation at all between the number of views you have and the number of "model" objects.
- There is no real correlation between the number of controllers you have and the number of views either. (In retrospect, perhaps I only needed one controller)
Download the Source Code here


6 Comments:
Nice post, Peter. In the "Dangers of Binding Directly to the Model" section, you raise three points of concern about having a two-way binding to the Model. You address one of them later by mention IDataErrorInfo, but here is my take on them:
I see no problem with having a two-way binding directly to the model. If you use IDataErrorInfo as the means of having a business object (BO) communicate an invalid value to the UI, and have the BO class track changes, there is no problem. For example, Rocky Lhotka's CSLA framework provides built-in undo/redo functionality. The UI should NOT know about such things, those are properly handled by the layer which must be undone/redone, so to speak.
Regarding the Apply button, I don't see why that's an issue. What does it mean to "Apply" changes? Save them to the db, usually. So, you can still have an Apply button. Most users don't think of "Apply" as meaning "push the edits I made into an in-memory business object" (HA!).
Regarding how to cancel what you were just doing, isn't that the same as asking how to perform an undo?
Thanks,
Josh
Josh,
Thanks for writing! As a newbie to WPF I've been scouring the internet searching for good WPF examples to guide me, your code and articles always seem to come up as the pick of the litter. We need more like you.
In regards to my IDataErrorInfo gripes, I am coming from another perspective outside of the CSLA.
I am using NHibernate for my persistence model, PostSharp for validation, and my own custom BOs.
Of course, a simple example like a "Name" string property is very easy to allow and flag as invalid if it is empty, but what if the property change triggers a complex chain of actions elsewhere, where an invalid value can potentially cause massive data corruption?
As a general rule, my BOs do not support invalid states, and they throw excpetions early and often. Maybe this is due to the complex nature of my current project, maybe it is due to the fact that I have a large team of developers and this is the only common design compromise we can agree to.
Should I have leveraged CSLA? Perhaps, I dont know how well it interfaces with NHibernate as a persistence mechanism, but NHibernate pays off big time in terms of object persistence.
Does apply mean "commit to the database"? Does apply mean "commit to the core objects"? It depends. Much like nested transactions, we may need to manipulate multiple views and alter the business objects in many screens before we are ready to commit all of our updates to the database. Youre right, I dont have a good "undo" feature, but I do have a good two-phase-commit design, which is acceptable for my users at the moment.
As I said at the end of the post, there are so many right ways...
You raise some interesting points here. I have never used NHibernate, so I can't give a worthwhile reply. I'll be interested to see what others, who have WPF and NHibernate experience, have to say about this.
Thanks,
Josh
With all the "new technology confusion" and redundant ways of completing a given task have you considered writing a MVP framework restricted to PresentationCore?
Also what about dependency object call backs instead of that pesky INotifiyPropertyChanged?
Thanks,
Rob FireGarden
With all the "new technology confusion" and redundant ways of completing a given task have you considered writing a MVP framework restricted to PresentationCore?
--I don't think I'm comfortable enough with WPF yet to start writing my own framework, maybe someday with some more experience :)
Also what about dependency object call backs instead of that pesky INotifiyPropertyChanged?
--INotifyPropertyChanged is a necessary evil: the controls have dependency properties that are bound to properties in the objects. The objects need to generate notification events via the INotifyPropertyChanged interface, the controls change with the objects.
If you use postsharp, INotifyPropertyChanged is just an attribute, which the syntax is much less pesky with the same semantic result
-p
I also don't think the binding happens directly to the model. Having a controller that handles all of the changes to the model is great. It also makes it much easier to unit test. Take a look at my blog on WPF and MVC (http://shellewell.spaces.live.com/blog/cns!90F64416E91D2F8C!119.entry). I have already revised my working samples since my blog entry to make my views inherit from user control. I will either update my blog later, or add an additional entry.
Post a Comment
<< Home