Login | Register
My pages Projects Community openCollabNet

Introduction to Action Messages 

This article discusses code which can be found in the Samples.ActionMessages project.

Action messages are a mechanism enabled by Caliburn, designed to facilitate the communication between views and presenters in an MVP architecture.  The allow the developer to create POCOs (Plain Old CLR Classes) that encapsulate application logic and bind them (methods and properties) to the UI.  This greatly simplifies WPF development and enables TDD by moving important code out of the XAML code-behind and into the presenter.

Before you can begin working with Caliburn and Action Messages, you must configure the framework.  We'll be using the default configuration for now, so all that is required is to make your App constructor look like this:

Lets also add a class to serve as a simple presenter:

Notice that there is nothing special required of this class, such as inheriting from special base classes or implementing interfaces.  Now we need to make a view:

The two important things to note about the view are the us of cal:Presenter.Binding and cal:View.Messages.  Binding causes the instance of Calculator to be bound to the Window.  Effectively, it is set as the Window's DataContext and registered with the framework as being connected to the Window.  You can bind a presenter to any DependencyObject, although it must be a FrameworkElement to get DataContext functionality.  Presenter bindings are directly related to ActionMessages.  The ActionMessage that is set with the Messages attached property has the following result:  When the Button is Clicked (Trigger) the Divide (Action) method on Calculator instance will be called.  The Text values of the TextBoxes "leftSide" and "rightSide" will be passed into the method as parameters.  The return value of the method will be bound to the Text property of the "result" TextBox.

A couple of other important things to know:  Caliburn provides a ResolveExtension that can be used with Binding to ask a dependency injection container to resolve the instance of the presenter for you.  Caliburn provides adapters for Windsor, Spring.net, StructureMap and Unity.  You must use a custom configuration to turn on this functionality.  Also, WPF Controls have default events and value properties, also configurable; so you don't have to specify that the ActionMessage in the above example is triggered by a Click event because this is the default for a Button.  If we follow a few more conventions, we can further streamline the ActionMessage markup to this:

if we change our class to this:

If the message does not declare any parameters, and the action requires them, the framework will look for elements by name, based on the parameter names.  It will then query the found elements for their default property values (configurable) and supply those values to the method.  If it cannot find matches and the method only takes one parameter, it will attempt to bind that to the message sender's default property value.  If a message doesn't need to declare a return value or parameters, you can declare it with a simple string value:

If you play around with the sample long enough, you'll eventually put a zero in the second TextBox and throw a nasty exception.  You'd probably want to check for zero in reality, but you can handle exceptions like this:

You can also place an Action attribute on any method to declare custom exception handling for that method.  What if the method is a long running action?

The return value still binds! Need a callback instead of a return value?  The AsyncAction attribute lets you specify the name of a callback method.  The return values and callbacks are automatically marshaled back onto the UI thread for you.

Another, way to get the same functionality as all the above examples would be to give the presenter LeftSide, RightSide and Result properties bound to the UI along with a parameterless Divide method.  The design of Caliburn tries hard not to box the developer into one specific implementation of MVC/MVP because, in practice, both these techniques and others are often used in conjunction or depending on the specific situation.

There's one more important thing you need to know to get started with these features.  ActionMessages can bubble.  If, for example, a message was looking for an action called "DoSomething," but did not find it on the presenter, the message would bubble up the UI looking for a presenter until it found one with the appropriate action.  This is useful if a UserControl needs to have its own presenter, but also wants to pass messages on to its parent Window.

Everything shown above in XAML can be done imperatively through code by using the IPresenterManager and IViewManager services.  These can be obtained by by calling DI.Resolve<IPresenterManager>() and DI.Resolve<IViewManager>() respectively.