<Warning>Long post follows</Warning>
Lately there is a lot of momentum and interesting conversation around Model-View-View-Model. There’s several good resources out there that discuss the basics of the pattern, who the actors are that are involved and what role the play. I’ll let those speak for themselves, including John Gossman’s great post here, Martin Fowler’s post on the more general PresentationModel pattern and more recently Josh Smith’s MSDN article, Rob Eisenberg’s new series, and Ward Bell’s posts which touch on some of the deeper complexities involved.
In many of these discussions (not including the posts I referred to) it seems like one aspect of ViewModel get’s lost, which is “Why use it in the first place?” This leads to a lot of debate around details of implementation including one item in particular which is whether or not code in the code-behind is an anti-pattern. Building off of the stage Phil set in his great LOD post, I’ll say ViewModel is not a code-counting exercise.
John says in his post, “Model/View/ViewModel is a variation of Model/View/Controller (MVC) that is tailored for modern UI development platforms where the View is the responsibility of a designer rather than a classic developer. The designer is generally a more graphical, artistic focused person, and does less classic coding than a traditional developer.”
Martin then states in this Bliki that “The essence of a Presentation Model is of a fully self-contained class that represents all the data and behavior of the UI window, but without any of the controls used to render that UI on the screen. A view then simply projects the state of the presentation model onto the glass.” Martin then adds “It's useful for allowing you to test without the UI, support for some form of multiple view and a separation of concerns which may make it easier to develop the user interface.”
From John’s perspective, it is a pattern that enables a UX designer (non-developer) to be responsible for the UI. From Martin’s perspective it’s about separation-of-concerns and testability. So which one is right? I say both. Also notice neither one said anything about having zero-code in the code-behind. Instead they spoke about maintainability.
So what is the spirit of MVVM then? I say it’s building UIs that utilize platform enhancements in WPF and Silverlight to provide good separation between UI and business logic in order to make those UIs easier to maintain by developers and designers.
When I say designer, I don’t mean only the stereotypical guy/gal who wears a black sweater :-). It may also be a developer who is using Blend/Cider to define the UI, which is likely, as we all know hand coding a ton of XAML is anything but fun.
Why have I called out WPF and Silverlight? Because the pattern is specific and solely on the specific data binding, data templates, attached properties and command implementations found in the platform. Presentation Model on the other hand is entirely general and places no such specificity. I would not for example recommend ViewModel as an appropriate pattern for ASP.NET or Winforms, but I would however recommend PresentationModel, in addition to MVP, etc.
They key here (and which I want to touch on) is the last part, “easier to maintain by developers and designers.”. This means (if you buy my definition) that the chosen implementation should consider both aspects of the equation, that is what does it do for the developer and what does it do for the designer. Obviously tradeoffs have to be made, but the key is finding a healthy balance, reducing the negative impact to both.
Which brings us to the code counting question. Does having zero code in the code-behind make the UI easier to maintain? I say it depends on what kind of code you are talking about:
If the code is UI level business logic which does not relate directly to the rendering of the view, such as determining whether or not certain elements are visible, or enabled, then I agree, this kind of code is a separate concern and should not appear in the View at all and be within the ViewModel. I would include in this the XAML, as the XAML is still part of the view. Why shouldn’t this logic be there? Because it makes the UI difficult to maintain by creating a tight coupling between the UI and the logic. It makes it difficult to test, and is also very brittle and likely to break as the UI evolves in response to changing requirements.
On the other side of the coin are UI-specific concerns, I would include in this setting enabled state, visibility or starting/stopping animations as well as even logic that relates to which model data a specific UI element binds to. That may sound contradictory to my previous statement but it is not. What I mean is, the ViewModel should contain the logic to determine whether something is visible or not, and the View should act on that knowledge to then hide the element. Adding this logic to the ViewModel would introduce tight coupling between the ViewModel and the View, this time from the ViewModel side.
Now being that it is a UI specific concern, the next question is how should it be handled, as code in the code-behind or in XAML? And for that answer I would say it depends on the specific concern itself, as well as whether or not there is a UX designer who handles the UI. Even still, I would look at an approach that best meets the needs of both developers and designers.
Let’s take data binding as an example. If I am working with a dedicated UX designer then bindings in XAML is the prevalent technique that our tooling provides for data binding. Now it does negatively impact the development team to some degree in that due to XAML’s very loose nature it is very easy to break and difficult to verify and test. However there’s no other option for the designer other than forcing them to write code, which is how should we say less than optimal.
If there was no UX designer present however, I would see no issue with putting that binding definition in code, even in the code-behind. As developers are used to writing code, not markup, and the developer IDE provides much richer support for developers in code. Now that being said, the API for defining bindings is not really well-suited for doing them in code, but OSS solutions like “Fluent Silverlight” help bridge that gap.
A different example relates to animations. The user clicks a Save button on the Edit Order screen which requires some UI cue to the user such as an hourglass while the order is saving, and another cue once the Order has saved. The question is where should this logic live? In the XAML or in the code? You could handle this in the XAML by using triggers, attached behaviors and the like. In the case of Silverlight, you don’t yet have triggers yet baked in, but you could look to using those included with Blend.
Now although you “could”, I wouldn’t, though you might feel different. The reason goes back to maintainability, having such logic in the view means once again it is easy to break and difficult to test (How do I know it actually works?). Instead, I would opt in this case to separate into two parts, the pure visual part and the logic. The pure visual would be the hour glass animation, and the saved completed screen which could likely be purely defined in XAML. However, the logic part which determines to show the hour glass while saving is in progress would rest in the code. I would achieve this by adding OnSave,OnSaveCompleted methods to the view which would both contain the logic to actually display the elements.
The ViewModel would get injected with either an interface to the view, or with a set of delegates it could call to once save begins and completes. Yes, I did say I’d put code in the code-behind! That is ok because it is code that relates entirely to a view-level concern. Also I would argue that putting the one line of code in the View, only minimally impacts the designer. They can still verify that the animations works. On the plus side it means that we’re remove one more potential piece of brittle, un-testable logic. Also it improves debugging as a developer can put a break-point on the view methods to and see whether or not they are even getting called, additionally it ensures that an exception is thrown if the animation in question is removed.
The last thing I want to touch on relates to invoking commands, and usage of command parameters through element binding, vs using bound properties on the ViewModel. The user selects an item in the Order list, and double clicks. Do we a) Use element binding and have clicking on the “Open Order” button invoke the OpenOrder command passing in the currently selected order as a parameter, or b) Set the orders list selected item to bind to a SelectedOrder ViewModel property and then have a parameterless OpenOrder command which uses the SelectedOrder order property on the VM itself.
I would argue B over A, and why relates back to the maintainability question. If I use element binding, again I make the view harder to maintain. In this case, I haven’t coupled it to the VM, but I have coupled one part of the View to another part of the View. The impact here is on future refactorings of the View. If move around elements in the view such that the relative binding is broken, I broke my UI, if I use the second approach however, I can re-factor / replace elements all day long without breaking any relative bindings.
Yes, I’ve added properties to the ViewModel, but those properties are easy to discover, and result in UIs that are less prone to break over time. I should also add that using approach B works just as well for a designer, as they can easily bind to the properties on the VM for the parameters and parameterless commands.
In conclusion, the spirit of MVVM is about producing maintainable UIs for the developer and the designer, not UIs that have zero code. I would recommend folks who are developing using the MVVM pattern to focus less on whether or not there is code in the code behind, and rather focus on the type of code and whether it improves / or hinders the maintainability of the UI. Sometimes having a bit of code in the code behind actually goes a long way toward improving it.
What are your thoughts on this topic? I’d love to hear them.