.NET & Funky Fresh

Syndication

News

  • <script type="text/javascript" src="http://ws.amazon.com/widgets/q?ServiceVersion=20070822&amp;MarketPlace=US&amp;ID=V20070822/US/bluspiconinc-20/8001/8b68bf4b-6724-40e7-99a5-a6decf6d8648"> </script>
Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
Caliburn: Using ActionMessages to enable MVC in WPF

The Caliburn framework is packed with a variety of features spread across three main assemblies.  In this post, I'll be discussing a few features available in Caliburn.dll.  I'll cover basic configuration of the framework and demonstrate how a WPF UI can communicate with a Controller/Presenter.

NOTE: I'm using the terms Controller, Presenter, MVC, MVP pretty loosely here.  The main point is that the framework has a number of facilities that make this style of development much easier.  The way in which you end up implementing the patterns is your own business.

The first thing you'll need to do to get things working is 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:

Now we need to make a view:

The two important things to note are Engine:Presenters.AddBinding and Engine:Views.AddMessageSource.  AddBinding 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.  PresenterBindings are directly related to ActionMessages.  The ActionMessage that is set with the AddMessageSource attached property has the following result:  When the Button is Clicked (Trigger) the Divide (Action) method on Calculator 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 AddBinding to ask a DI container to resolve the instance of the presenter for you.  I have provided adapters for Windsor, Spring.net and StructureMap (StructureMap is not quite complete but will work in this scenario).  You must use a custom configuration to turn on this functionality (discussed in my next post).  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! Sweet!  Need a callback instead/in addition to the return value?  The AsyncAction attribute lets you specify the name of a callback method (parameterless).

NOTE:  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.  This simplifies the use of ActionMessages.  (I tried hard with Caliburn not to box myself into one specific implementation of MVC/MVP because I found that in practice I would use both these techniques and others, depending on the specific situation.)

That just about sums up ActionMessages.  There's one more important thing you need to know.  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 forward messages on to its parent Window.

Everything I'm shown above in XAML can be done imperatively through the Engine static gateway.  You'll find that the static gateway mirrors the attached properties exactly.  I've thought about creating a C# dsl for hooking up the bindings in code.  It could use lambdas and such to create a nice compile-time checked version of the above.  It would then allow all bindings to be removed from XAML and the framework could just locate your bindings class, instantiate it and hook everything up.  This would also allow for Unit Testing of bindings.  I'm not sure if this is overkill or not and would like some feedback as to whether I should do something like this or take another direction.

Looking back, ActionMessages were the most important feature we used on the WPF LOB we recently finished because of the flexibility of communication between classes that it allowed.  I don't think I adequately represented them in the Caliburn sample (it overemphasizes the EventBroker which we very rarely used in practice), so I hope this clarifies things a bit more and maybe offers you some creative ideas for solving WPF problems in different ways.

The above sample has been added into the source along with several bug fixes and can be found at: http://bluespire.com/svn/Caliburn/trunk/.

NEXT POST: Custom Configuration, MarkupExtensions and Gestures Triggers


Posted 01-09-2008 10:14 PM by Rob Eisenberg
Filed under: , , , ,

[Advertisement]

Comments

DotNetKicks.com wrote Introduction to MVC for WPF
on 01-10-2008 12:06 AM

You've been kicked (a good thing) - Trackback from DotNetKicks.com

Christopher Bennage wrote re: Caliburn: Using ActionMessages to enable MVC in WPF
on 01-10-2008 9:39 AM

I like the idea of having autospecs for bindings, though I'm bit skeptical about the use of a DSL.  I'd like to experiment with that  and see though.

Rob Eisenberg wrote re: Caliburn: Using ActionMessages to enable MVC in WPF
on 01-14-2008 1:09 AM

For those who are having problems downloading the source from svn, I apologize.  I am working on a more permanent solution and will note  it on the blog when it is ready.  In the mean time I'm going to continue to blog about various features.

Kevin wrote re: Caliburn: Using ActionMessages to enable MVC in WPF
on 01-15-2008 2:25 PM

Hi Rob,

Thanks for sharing the code and your insight. I have one question:

Is EventBroker good for cross module communication, such as sync views in different modules? ActionMessage is a great idea with WPF bubbling for action handling within the logical tree. I suppose it is suitable for in-module composite UI development. I would like to have your thoughts.

Looking forward to your new posts,

Kevin

Rob Eisenberg wrote re: Caliburn: Using ActionMessages to enable MVC in WPF
on 01-16-2008 1:30 AM

@Kevin

EventBroker is geared towards cross module communication.  So it can be used as you have suggested.  I am however, pondering  (have begun spiking) rewriting my implementation of this pattern based on some recent blog posts by Glenn Block and Jeremy Miller.  My framework is a good start, but has quite a lot of room for improvement and this first version is still in flux.  Regarding ActionMessages, they are primarily geared towards in-module communication, but in addition to bubbling up the LogicalTree, they can bubble up the VisualTree.  So technically, they can bubble up to other modules as well.  Whether or not this is a good practice remains to be seen.

Kevin wrote re: Caliburn: Using ActionMessages to enable MVC in WPF
on 01-16-2008 1:50 PM

Thanks for the reply, Rob.

The more I look at the code, the more I appreciate the design and the code.

IMHO, EventBroker and ActionMessage are related to container and objects run-time hierarchy (would be logical tree or VisualTree). CAB has generic WorkItem as container and hierarchy abstraction. But I don't like WorkItem introduces complexity into design model (especially when WPF has bubble/tunnel mechanism in logical and visual tree) . I would like to have EventBroker at the top container level only(application level) and used for cross modules communication. Using ActionMessage for in-module communication or cross module communication if the modules form a hierarchy in terms of logical tree or visual tree. My 0.02 cents.

I found a threading issue when loading a module during run-time(drop the dlls into the Modules folder when shell is running)

Do you have a forum for this kind of discussion?

Thanks,

Kevin

Rob Eisenberg wrote re: Caliburn: Using ActionMessages to enable MVC in WPF
on 01-22-2008 7:06 PM

@Kevin

Thanks for the feedback!  Your ideas about how/when to use ActionMessage vs. EventBroker are in line with what I intended.  I don't have nested containers built-in yet and am still pondering what the best way to implement this is.  I have recently made some massive code refactoring, added some new features and fixed several bugs.  I'll be talking more about this soon.  Currently I am waiting for my project to be approved for hosting on tigris.  Once that goes through we'll have a place to manage releases, track bugs, request features, etc.  I'll be blogging about that when everything is in place and documenting more fully what caliburn still needs to have implemented/fixed before its really ready for prime time.

Ruurd Boeke wrote re: Caliburn: Using ActionMessages to enable MVC in WPF
on 01-24-2008 10:06 AM

Rob,

you mention this:

"This would also allow for Unit Testing of bindings.  I'm not sure if this is overkill or not and would like some feedback as to whether I should do something like this or take another direction."

I have just finished a big project where we used the Composite UIblock from microsoft as a MVC solution for our wpf application. I think your solution has the potential to be much much better.

The most important thing that bit us during the development was the unittesting. It is soooo hard to test bindings, but if you don't, u lose all flexibility in your datamodel. Therefor, everything done on testing bindings is great!

I would warn against creating another method of expressing the bindings though: xaml and it's bindings are very expressive and I have found it great to be able to place bindings decleratively.

Another direction would be (possibly unrelated to caliburn) to just instantiate the bindings and check for binding errors after setting the datacontext.

Rob Eisenberg wrote re: Caliburn: Using ActionMessages to enable MVC in WPF
on 01-24-2008 6:24 PM

@Ruurd

Thanks for the feedback!  My last WPF project had similar issues with bindings.  I would really like to come up with a solution for this, but I'm not sure what it will look like yet.  Keep the feedback coming and we'll hopefully be able to come up with something that will benefit everyone.

Daniel Petrik wrote re: Caliburn: Using ActionMessages to enable MVC in WPF
on 01-30-2008 4:40 AM

Has anybody idea why I can  not  set property of visual components in xaml window (for example Background color for TextBox) in design mode  in Visual Studio 2008 when <cal:Presenter.Binding>

       <local:Calculator />

</cal:Presenter.Binding>

is in xaml code of window. I try it first in my "own" sample, than also in Samples.ActionMesages. Only the way is temporary comment part "Presenter.Binding ", than it works.

The error message has caption "Properties Window" with text "Property values is not valid" and in details "Value cannot be null, Parameter name: value".

Rob Eisenberg wrote re: Caliburn: Using ActionMessages to enable MVC in WPF
on 02-03-2008 2:15 PM

@Daniel

I am not sure what is causing this problem just yet, but I will look into it.  Please feel free to post this as a bug on the project site.

Mike Eshva wrote re: Caliburn: Using ActionMessages to enable MVC in WPF
on 02-17-2008 4:20 PM

Thank you for Caliburn! I try to use it in our project. If it will fail I should change it to something else in the way. At the first glans it's pretty good. But information about how to use it is not enough.

Well today I have some concrete qustion. What if I need to ActionMassages for a control? Seems like it's not possible. I try:

           <ComboBox x:Name="aPnpName"

                     SelectedIndex="0">

               <ComboBox.ItemTemplate>

                   <DataTemplate>

                       <TextBlock Text="{Binding Path=Name, Mode=OneWay}" />

                   </DataTemplate>

               </ComboBox.ItemTemplate>

               <Engine:View.Messages>

                   <Engine:ActionMessage Trigger="SelectionChanged"

                                         Action="GetPnpItemList"

                                         Return="{Binding ElementName=aPnpItemList, Path=ItemsSource}">

                       <Engine:Parameter Value="{Binding ElementName=aPnpName, Path=SelectedItem.Row, Mode=OneWay}" />

                       <Engine:Parameter Value="{Binding ElementName=aRegionName, Path=SelectedItem.Row, Mode=OneWay}" />

                       <Engine:Parameter Value="{Binding ElementName=aPeriodName, Path=SelectedItem.Row, Mode=OneWay}" />

                   </Engine:ActionMessage>

                   <Engine:ActionMessage Trigger="MouseMove"

                                         Action="ClearMapDataSource"

                                         Return="{Binding ElementName=likeMap, Path=ItemsSource}" />

               </Engine:View.Messages>

           </ComboBox>

...and have a compilation error:

Error 1 The object 'Object' already has a child and cannot add 'ActionMessage'. 'Object' can accept only one child. Line 73 Position 22. D:\Projects\Trys\Caliburn studing\Caliburn studing\Window1.xaml 73 22 Caliburn studing

What if I need to call two different methods on the same Trigger (from two different controllers)?

Rob Eisenberg wrote re: Caliburn: Using ActionMessages to enable MVC in WPF
on 03-13-2008 9:07 AM

@Mike

In order to send multiple action messages you need to put them inside an x:Array element.  You can see an example of this in Samples.TodoListModule.TodoView.xaml.  

Currently there is no way to call methods on different controllers from the same element unless one of the controllers is related by a property path to the other.  In other words - if parentcontroller has a property called childcontroller , and the element is bound to parentcontroller, then you can do it with this:

Action="ChildController.MethodToCall"

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)