Recently Glenn Block asked some questions of the community concerning what support Microsoft should offer for the MVVM design pattern in WPF/Silverlight. I’d like to answer that question here, but in a round-about manner. I’m going to use this as an opportunity to talk about the origins of Caliburn.
About two years ago I entered an article in a contest on dotnetslackers about a prototype framework I had developed several months earlier during my free time. The sample application for the article is manufactured, but the framework was built for a real application I was working on then. At that time, I was writing a GUI test runner for NSpecify framework. I had been doing WPF development for over a year, though not for paying contracts (I built some really cool samples such as a WF designer, an interactive fiction viewer/editor, a partial implementation of the Office 2007 ribbon and a multi player online game) and was using this project to experiment with some new architectural ideas. I had long since become frustrated with the typical code-behind model and had been looking for ways to move away from it. Being heavily influenced by John Gossman’s article on MVVM, I began making greater use of Commands to enable databinding of UI to actions.
Here’s the progression I went through and suspect that many WPF developers can identify with:
- Started by manually wiring events and handling them in the code-behind.
- Factored logic out of the code-behind and into some sort of presentation class (Presenter, VM, etc). Event handlers became a pass-through to methods on the presentation class.
- Moved away from events and towards commands. Created custom commands that hung off the presentation class and were wired to the UI through databinding.
- Got tired of creating lots of commands after about 5 minutes and implemented a DelegatingCommand pattern to enable databinding on the view side, with a pass-through to a standard method on the presentation class.
- Got tired of instantiating and wiring delegating commands after about 15 minutes and created a fluent interface dedicated to the task. The code looked something like this:
public DelegatingCommand Save
{
get { return Execute.AsCommand(DoSave).If(CanDoSave).Async(); }
}
In fact, you could see the implementation of this in some early versions of Caliburn.
To answer Glenn’s first question:
Where does the platform help? Where does it hinder?
Even though the fluent interface is a pretty efficient way of binding models and views, it is still fairly limited in its capabilities. With commands you are limited to elements that implement ICommandSource only. So, if you want to have code execute on a ListBox.SelectionChanged event, you have to revert back to events and code-behind. You could try and rig something up by using InputBindings/CommandBindings which exist on all UI elements, but using gestures to simulate control-specific events is kludgey. Furthermore, InputBindings/CommandBindings have limitations due to the fact that they either are not DependencyObjects or don’t implement DependencyProperties and cannot be bound as easily, thus you have to use some sort of static class mechanism. Furthermore, commands can only have one parameter and RoutedCommands are terribly confusing and IMHO an overly complex solution to the problem. All of this is a bit shady and quite round-about in general. This leads me to a rather simple answer to Glenn’s second question:
What can we do in the platform to make life easier?
What we really want to do is bind the view directly to the methods themselves. It would be nice if we had a mechanism by which we could bind any .NET event, routed event, gesture or command with one consistent syntax.
Caliburn’s implementation of RoutedMessaging and Actions is my attempt to solve this very problem in the OSS space. Actions was the first feature I developed for Caliburn (although there are quite a few more now and many more to come) and it remains one of the most used. Here’s a snippet from one of Caliburn’s sample apps, demonstrating some of the syntax:
<Button Content="Example 1"
Message.Attach="Divide(left.Text, right.Text) : DivideResult.Text" />
<Button Content="Example 2"
Message.Attach="[Event Click] = [Action Divide(left.Text, right.Text) : DivideResult.Text]" />
Both these snippets do the same thing, the second is just more explicit about the details. Here’s what all this does:
- Wire up to the “Click” event (Caliburn determines this to be the default in Ex1)
- Send an “Action Message” whenever the “Click” event fires (again Caliburn knows that Actions are default in Ex1)
- When the action executes, take the Text properties of the controls with names “left” and “right” and pass those values as parameters to the “Divide” action (a method on a class).
- Perform type conversion on the parameters.
- Take the return value of the action and databind it back to the Text property of the control with its name set to “DivideResult”
This demonstrates binding directly to methods from events. But you can also trigger actions based on an ICommandSource’s command execution:
Message.Attach="[CommandSource] = [Action Divide(left.Text, right.Text) : DivideResult.Text]"
Or by gesture:
Message.Attach="[Gesture MouseAction: LeftClick, Modifiers: Control] = [Action Divide(left.Text, right.Text) : DivideResult.Text]"
The “trigger” aspect of Caliburn is completely pluggable. In fact, the ICommandSource trigger implementation was a user submitted extension, much thanks to Marco Amendola. Additionally, you can plug the right part of the expression. So instead of just triggering actions, you can also trigger commands (or anything you wish to extend it too):
Message.Attach="[Event SelectionChanged] = [ContainerCommand ShowMessage(title.Text, message.Text)]"
Caliburn can resolve commands from the container (ContainerCommand) , resource dictionaries (ResoureceCommand) or through databinding (BoundCommand) and the command’s trigger doesn’t have to be an ICommandSource nor is it limited to one parameter. In fact, commands in Caliburn are built on top of actions. Which means they inherit a host of other features as well, such as ASP.NET MVC-style rescues and filters (which are extensible) and extensible ActionResults (called IExecutableResult in Caliburn). You can also decorate your action/command with an [Async] attribute and Caliburn will call it on a background thread, and automatically marshal the callback onto the UI thread (you even have control over the background task’s progress and cancellation).
As a developer, it was really important to me that I have a consistent way of wiring user interactions to my model. Furthermore, I wanted the same syntax for both WPF and Silverlight (and yes, that took a lot of work in SL2). This brings me to a key point, whatever Microsoft builds, the public API should be the same for both WPF and Silverlight.
What role do developers / designers play in your application of ViewModel?
I think that the only way to have a strong UI is through iterative development. This means constant communication between the development team and the client, but also constant communication between the developers and the designers. Keep in mind that a View and its ViewModel are a highly cohesive unit. This means that a designer is going influence what your ViewModel looks like. What I would really like is a way for Views to be more easily bound to their ViewModels. Christopher and I have often talked about how nice it would be for Caliburn to have a Cider/Expression plugin that let you wire views directly to the ViewModel (both properties and methods). And that definitely describes something I would like to see from Microsoft in this area. The next phase in designer/developer interaction in WPF and Silverlight is a ViewModel rather than code-behind centric tooling experience.
Posted
05-18-2009 10:51 AM
by
Rob Eisenberg