Our Devlicio.us Blogs

We briefly introduced actions in Pt. 1, but there is so much more to know. To begin our investigation, we’ll take our simple “Hello” example and see what it looks like when we explicitly create the actions rather than use conventions. Here’s the Xaml:

 

<UserControl x:Class="Caliburn.Micro.Hello.ShellView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
             xmlns:cal="http://www.caliburnproject.org">
    <StackPanel>
        <TextBox x:Name="Name" />
        <Button Content="Click Me">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="Click">
                    <cal:ActionMessage MethodName="SayHello" />
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </Button>
    </StackPanel>
</UserControl>

 

As you can see, the Actions feature leverages System.Windows.Interactivity for it’s trigger mechanism. This means that you can use anything that inherits from System.Windows.Interactivity.TriggerBase to trigger the sending of an ActionMessage.*  Perhaps the most common trigger is an EventTrigger, but you can create almost any kind of trigger imaginable or leverage some common triggers already created by the community.  ActionMessage is, of coarse, the Caliburn.Micro-specific part of this markup. It indicates that when the trigger occurs, we should send a message of “SayHello.”  So, why do I use the language “send a message” instead of “execute a method” when describing this functionality? That’s the interesting and powerful part.  ActionMessage bubbles through the Visual Tree searching for a target instance that can handle it. If a target is found, but does not have a “SayHello” method, the framework will continue to bubble until it finds one, throwing an exception if no “handler” is found.** This bubbling nature of ActionMessage comes in handy in a number of interesting scenarios, Master/Details being a key use case. Another important feature to note is Action guards. When a handler is found for the “SayHello” message, it will check to see if that class also has either a property or a method named “CanSayHello.” If you have a guard property and your class implements INotifyPropertyChanged, then the framework will observe changes in that property and re-evaluate the guard accordingly. We’ll discuss method guards in further detail below.

Now you’re probably wondering how to specify the target of an ActionMessage. Looking at the markup above, there’s no visible indication of what that target will be. So, where does that come from? Since we used a Model-First approach, when Caliburn.Micro (hereafter CM) created the view and bound it to the ViewModel using the ViewModelBinder, it set this up for us. Anything that goes through the ViewModelBinder will have its action target set automatically. But, you can set it yourself as well, using the attached property Action.Target. Setting this property positions an ActionMessage “handler” in the Visual Tree attached to the node on with you declare the property. It also sets the DataContext to the same value, since you often want these two things to be the same. However, you can vary the Action.Target from the DataContext if you like. Simply use the Action.TargetWithoutContext attached property instead. One nice thing about Action.Target is that you can set it to a System.String and CM will use that string to resolve an instance from the IoC container using the provided value as its key. This gives you a nice way of doing View-First MVVM if you so desire. If you want Action.Target set and you want Action/Binding Conventions applied as well, you can use the Bind.Model attached property in the same way.

Let’s see how we would apply this to achieve MVVM using a View-First technique (gasp!) Here’s how we would change our bootstrapper:

 

public class MefBootstrapper : Bootstrapper
{
    //same as before

    protected override void DisplayRootView()
    {
        Application.Current.RootVisual = new ShellView();
    }

    //same as before
}

 

Because we are using View-First, we’ve inherited from the non-generic Bootstrapper. The MEF configuration is the same as seen previously, so I have left that out for brevity’s sake. The only other thing that is changed is how the view gets created. In this scenario, we simply override DisplayRootView, instantiate the view ourselves and set it as the RootVisual (or call Show in the case of WPF). Next, we’ll slightly alter how we are exporting our ShellViewModel, by adding an explicitly named contract:

 

[Export("Shell", typeof(IShell))]
public class ShellViewModel : PropertyChangedBase, IShell
{
    //same as before
}

 

Finally, we will alter our view to pull in the VM and perform all bindings:

 

<UserControl x:Class="Caliburn.Micro.ViewFirst.ShellView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:cal="http://www.caliburnproject.org"
             cal:Bind.Model="Shell">
    <StackPanel>
        <TextBox x:Name="Name" />
        <Button x:Name="SayHello"
                Content="Click Me" />
    </StackPanel>
</UserControl>

 

Notice the use of the Bind.Model attached property. This resolves our VM by key from the IoC container, sets the Action.Target and DataContext and applies all conventions. I thought it would be nice to show how View-First development is fully supported with CM, but mainly I want to make clear the various ways that you can set targets for actions and the implications of using each technique. Here’s a summary of the available attached properties:

  • Action.Target – Sets both the Action.Target property and the DataContext property to the specified instance. String values are used to resolve an instance from the IoC container.
  • Action.TargetWithoutContext – Sets only the Action.Target property to the specified instance. String values are used to resolve an instance from the IoC container.
  • Bind.Model – View-First - Set’s the Action.Target and DataContext properties to the specified instance. Applies conventions to the view. String values are used to resolve an instance from the IoC container.
  • View.Model – ViewModel-First – Locates the view for the specified VM instance and injects it at the content site. Sets the VM to the Action.Target and the DataContext. Applies conventions to the view.

 

Now, let’s take a look at another interesting aspect of ActionMessage: Parameters. To see this in action, let’s switch back to our original ViewModel-First bootstrapper, etc. and begin by changing our ShellViewModel to look like this:

 

using System.ComponentModel.Composition;
using System.Windows;

[Export(typeof(IShell))]
public class ShellViewModel : IShell
{
    public bool CanSayHello(string name)
    {
        return !string.IsNullOrWhiteSpace(name);
    }

    public void SayHello(string name)
    {
        MessageBox.Show(string.Format("Hello {0}!", name));
    }
}

 

There are a few things to note here. First, we are now working with a completely POCO class; no INPC goop here. Second, we have added an input parameter to our SayHello method. Finally, we changed our CanSayHello property into a method with the same inputs as the action, but with a bool return type. Now, let’s have a look at the Xaml:

 

<UserControl x:Class="Caliburn.Micro.HelloParameters.ShellView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
             xmlns:cal="http://www.caliburnproject.org">
    <StackPanel>
        <TextBox x:Name="Name" />
        <Button Content="Click Me">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="Click">
                    <cal:ActionMessage MethodName="SayHello">
                        <cal:Parameter Value="{Binding ElementName=Name, Path=Text}" />
                    </cal:ActionMessage>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </Button>
    </StackPanel>
</UserControl>

 

Our markup now has one modification: We declared the parameter as part of the ActionMessage using an ElementName Binding. You can have any number of parameters you desire. Value is a DependencyProperty, so all the standard binding capabilities apply to parameters. Did I mention you can do all this in Blend?

 

ActionsInBlend

 

One thing that is nice about this is that every time the value of a parameter changes, we’ll call the guard method associated with the action(CanSayHello in this case) and use its result to update the UI that the ActionMessage is attached to. Go ahead and run the application. You’ll see that it behaves the same as in previous examples.

In addition to literal values and Binding Expressions, there are a number of helpful “special” values that you can use with parameters. These allow you a convenient way to access common contextual information:

  • $eventArgs – Passes the Trigger’s EventArgs or input parameter to your Action. Note: This will be null for guard methods since the trigger hasn’t actually occurred.
  • $dataContext – Passes the DataContext of the element that the ActionMessage is attached to. This is very useful in Master/Detail scenarios where the ActionMessage may bubble to a parent VM but needs to carry with it the child instance to be acted upon.
  • $source – The actual FrameworkElement that triggered the ActionMessage to be sent.

You must start the variable with a “$” but the name is treated in a case-insensitive way by CM.

 

Word to the Wise

Parameters are a convenience feature. They are very powerful and can help you out of some tricky spots, but they can be easily abused. Personally, I only use parameters in the simplest scenarios. One place where they have worked nicely for me is in login forms. Another scenario, as mentioned previously is Master/Detail operations.

 

Now, do you want to see something truly wicked? Change your Xaml back to this:

 

<UserControl x:Class="Caliburn.Micro.HelloParameters.ShellView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <StackPanel>
        <TextBox x:Name="Name" />
        <Button x:Name="SayHello" 
                Content="Click Me" />
    </StackPanel>
</UserControl>

 

Running the application will confirm for you that CM’s conventions even understand ActionMessage parameters. We’ll discuss conventions a lot more in the future, but you should be happy to know that these conventions are case-insensitive and can even detect the before-mentioned “special” values.

 

Now, lets look at a simple Master/Detail scenario that demonstrates ActionMessage bubbling, but let’s do it with a shorthand syntax that is designed to be more developer friendly. We’ll start by adding a simple new class named Model:

 

using System;

public class Model
{
    public Guid Id { get; set; }
}

 

And then we’ll change our ShellViewModel to this:

 

using System;
using System.ComponentModel.Composition;

[Export(typeof(IShell))]
public class ShellViewModel : IShell
{
    public BindableCollection<Model> Items { get; private set; }

    public ShellViewModel()
    {
        Items = new BindableCollection<Model>{
            new Model { Id = Guid.NewGuid() },
            new Model { Id = Guid.NewGuid() },
            new Model { Id = Guid.NewGuid() },
            new Model { Id = Guid.NewGuid() }
        };
    }

    public void Add()
    {
        Items.Add(new Model { Id = Guid.NewGuid() });
    }

    public void Remove(Model child)
    {
        Items.Remove(child);
    }
}

 

Now our shell has a collection of Model instances along with the ability to add or remove from the collection. Notice that the Remove method takes a single parameter of type Model. Now, let’s update the ShellView:

 

<UserControl x:Class="Caliburn.Micro.BubblingAction.ShellView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:cal="http://www.caliburnproject.org">
    <StackPanel>
        <ItemsControl x:Name="Items">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <Button Content="Remove"
                                cal:Message.Attach="Remove($dataContext)" />
                        <TextBlock Text="{Binding Id}" />
                    </StackPanel>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
        <Button Content="Add"
                cal:Message.Attach="Add" />
    </StackPanel>
</UserControl>

 

The first thing to notice is that we are using a more Xaml-developer-friendly mechanism for declaring our ActionMessages. The Message.Attach property is backed by a simple parser which takes its textual input and transforms it into the full Interaction.Trigger/ActionMessage that you’ve seen previously. If you work primarily in the Xaml editor and not in the designer, you’re going to like Message.Attach. Notice that neither Message.Attach declarations specify which event should send the message. If you leave off the event, the parser will use the ConventionManager to determine the default event to use for the trigger. In the case of Button, it’s Click. You can always be explicit of coarse. Here’s what the full syntax for our Remove message would look like if we were declaring everything:

 

<Button Content="Remove"
        cal:Message.Attach="[Event Click] = [Action Remove($dataContext)]" />

 

Suppose we were to re-write our parameterized SayHello action with the Message.Attach syntax. It would look like this:

 

<Button Content="Click Me"
        cal:Message.Attach="[Event Click] = [Action SayHello(Name.Text)]" />

 

But we could also leverage some smart defaults of the parser and do it like this:

 

<Button Content="Click Me"
        cal:Message.Attach="SayHello(Name)" />

 

You can specify literals as parameters as well and even declare multiple actions by separating them with a semicolon:

 

<Button Content="Let's Talk"
        cal:Message.Attach="[Event MouseEnter] = [Action Talk('Hello', Name.Text)];
                            [Event MouseLeave] = [Action Talk('Goodbye', Name.Text)]" />

 

WARNING: Those developers who ask me to expand this functionality into a full-blown expression parser will be taken out back and…dealt with. Message.Attach is not about cramming code into Xaml. It’s purpose is to provide a streamlined syntax for declaring when/what messages to send to the ViewModel. Please don’t abuse this.

 

If you haven’t already, run the application. Any doubts you had will hopefully be put to rest when you see that the message bubbling works as advertised :) Something else I would like to point out is that CM automatically performs type-conversion on parameters. So, for example, you can pump TextBox.Text into a System.Double parameter without any fear of a casting issue.

So, we’ve discussed using Interaction.Triggers with ActionMessage, including the use of Parameters with literals, element bindings*** and special values. We’ve discussed the various ways to set the action target depending on your needs/architectural style: Action.Target, Action.TargetWithoutContext, Bind.Model or View.Model. We also saw an example of the bubbling nature of ActionMessage and demoed it using the streamlined Message.Attach syntax. All along the way we’ve looked at various examples of conventions in action too. Now, there’s one final killer feature of ActionMessage we haven’t discussed yet…Coroutines. But, that will have to wait until next time.

 

*Currently, the full version of Caliburn is not based on System.Windows.Interactivity. Caliburn’s trigger mechanism was around long before Blend’s.  You may notice a shocking similarity in the markup. That said, Caliburn v2.0 will be migrated to use the Blend model in the near future.

**Actually, if no handler is found, before an exception is thrown, the framework will check the current DataContext to see if it has the requested method. This seamed like a reasonable fallback behavior.

***One important detail about ElementName Bindings that I didn’t mention…It doesn’t work with WP7 currently. Due to the fact that WP7 is based on a version of Silverlight 3 which had an incomplete implementation of DependencyObject/DependencyProperty, the infrastructure is not present to make this work in any sort of sane way. However, parameter literals and special values still work as described along with all the rest of the ActionMessage features.

Comments [0]

Some months back I blogged about “The Silver Lining,” a King’s Quest fan project that’s been in development for almost eight years.  I was upset because Activision, after acquiring Vivendi-Universal, decided to shutdown the project. Well, it seams that things have changed! Activision has granted Phoenix Online a full non-profit license to release all five episodes over the coarse of 2010. Thank you Activision! You did something really good. In about 20 minutes from now, Episode 1: What is Decreed Must Be, releases to web.  Here’s the list of episodes planned in the series:

Episode 1: What Is Decreed Must Be
Episode 2: Two Households
Episode 3: My Only Love Sprung From My Only Hate
Episode 4: ‘Tis In My Memory Locked, And You Yourself Shall Hold The Key Of It
Episode 5: A Thousand Times Goodnight

 

Can you tell I’m excited?! Counting down the minutes…

Comments [2]

In the last part we discussed the most basic configuration for Caliburn.Micro and demonstrated a couple of simple features related to Actions and Conventions. In this part, I would like to explore the Bootstrapper class a little more. Let’s begin by configuring our application to use an IoC container. We’ll use MEF for this example, but Caliburn.Micro will work well with any container. First, go ahead and grab the code from Part 1. We are going to use that as our starting point. Add two additional references: System.ComponentModel.Composition and System.ComponentModel.Composition.Initialization. Those are the assemblies that contain MEF’s functionality.* Now, let’s create a new Bootstrapper called MefBootstrapper. Use the following code:

 

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;
using System.Linq;

public class MefBootstrapper : Bootstrapper<IShell>
{
    private CompositionContainer container;

    protected override void Configure()
    {
        container = CompositionHost.Initialize(
            new AggregateCatalog(
                AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>()
                )
            );

        var batch = new CompositionBatch();

        batch.AddExportedValue<IWindowManager>(new WindowManager());
        batch.AddExportedValue<IEventAggregator>(new EventAggregator());
        batch.AddExportedValue(container);

        container.Compose(batch);
    }

    protected override object GetInstance(Type serviceType, string key)
    {
        string contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(serviceType) : key;
        var exports = container.GetExportedValues<object>(contract);

        if (exports.Count() > 0)
            return exports.First();

        throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract));
    }

    protected override IEnumerable<object> GetAllInstances(Type serviceType)
    {
        return container.GetExportedValues<object>(AttributedModelServices.GetContractName(serviceType));
    }

    protected override void BuildUp(object instance)
    {
        container.SatisfyImportsOnce(instance);
    }
}

 

That’s all the code to integrate MEF.  First, we override the Configure method of the Bootstrapper class. This gives us an opportunity to set up our IoC container as well as perform any other framework configuration we may want to do, such as customizing conventions. In this case, I’m taking advantage of Silverlight’s CompositionHost to setup the CompositionContainer. You can just instantiate the container directly if you are working with .NET. Then, I’m creating an AggregateCatalog and populating it with AssemblyCatalogs; one for each Assembly in AssemblySource.Instance. So, what is AssemblySoure.Instance? This is the place that Caliburn.Micro looks for Views. You can add assemblies to this at any time during your application to make them available to the framework, but there is also a special place to do it in the Bootstrapper. Simply override SelectAssemblies like this:

 

protected override IEnumerable<Assembly> SelectAssemblies()
{
    return new[] {
        Assembly.GetExecutingAssembly()
    };
}

 

All you have to do is return a list of searchable assemblies. By default, the base class returns the assembly that your Application exists in. So, if all your views are in the same assembly as your application, you don’t even need to worry about this. If you have multiple referenced assemblies that contain views, this is an extension point you need to remember. Also, if you are dynamically loading modules, you’ll need to make sure they get registered with your IoC container and the AssemblySoure.Instance when they are loaded.

After creating the container and providing it with the catalogs, I make sure to add a few Caliburn.Micro-specific services. The framework provides default implementations of both IWindowManager and IEventAggregator. Those are pieces that I’m likely to take dependencies on elsewhere, so I want them to be available for injection. I also register the container with itself (just a personal preference).

After we configure the container, we need to tell Caliburn.Micro how to use it.  That is the purpose of the three overrides that follow. “GetInstance” and “GetAllInstances” are required by the framework. “BuildUp” is optionally used to supply property dependencies to instances of IResult that are executed by the framework.

 

Word to the Wise

While Caliburn.Micro does provide ServiceLocator functionality through the Bootstrapper’s overrides and the IoC class, you should avoid using this directly in your application code. ServiceLocator is considered by many to be an anti-pattern. Pulling from a container tends to obscure the intent of the dependent code and can make testing more complicated. In future articles I will demonstrate at least one scenario where you may be tempted to access the ServiceLocator from a ViewModel. I’ll also demonstrate some solutions.**

 

Besides what is shown above, there are some other notable methods on the Bootstrapper. You can override OnStartup and OnExit to execute code when the application starts or shuts down respectively and OnUnhandledException to cleanup after any exception that wasn’t specifically handled by your application code.  The last override, DisplayRootView, is unique. Let’s look at how it is implemented in Bootstrapper<TRootModel>

 

protected override void DisplayRootView() 
{
    var viewModel = IoC.Get<TRootModel>();
#if SILVERLIGHT
    var view = ViewLocator.LocateForModel(viewModel, null, null);
    ViewModelBinder.Bind(viewModel, view, null);

    var activator = viewModel as IActivate;
    if (activator != null)
        activator.Activate();

    Application.Current.RootVisual = view;
#else
    new WindowManager().Show(viewModel);
#endif
}

 

The Silverlight version of this method resolves your root VM from the container, locates the view for it and binds the two together. It then makes sure to “activate” the VM if it implements the appropriate interface. The WPF version does the same thing by using the WindowManager class, more or less. DisplayRootView is basically a convenience implementation for model-first development. If you don’t like it, perhaps because you prefer view-first MVVM, then this is the method you want to override to change that behavior.

Now that you understand all about the Bootstrapper, let’s get our sample working. We need to add the IShell interface. In our case, it’s just a marker interface. But, in a real application, you would  have some significant shell-related functionality baked into this contract. Here’s the code:

 

public interface IShell
{
}

 

Now, we need to implement the interface and decorate our ShellViewModel with the appropriate MEF attributes:

 

[Export(typeof(IShell))]
public class ShellViewModel : PropertyChangedBase, IShell
{
   ...implementation is same as before...
}

 

That’s it! Your up and running with your bootstrapper configured for MEF.

 

*If you are using .NET, you will only need to reference System.ComponentModel.Composition.

**I’m quite guilty of this myself, but I’m trying to be more conscious of it.  I’m also excited to see that modern IoC containers as well as Caliburn.Micro provide some very nice ways to avoid this situation.

Comments [9]

In this tutorial we will learn a few of the basics of Caliburn.Micro. Let’s start by getting the framework.  Head on over to http://caliburnmicro.codeplex.com/SourceControl/list/changesets  Either use Mercurial to clone the repo or click on the link for the latest change set, then click on the download link. I recommend that you go ahead and get setup with TortoiseHG for Mercurial. You can read here and/or watch a free TekPub video here for information on that.  Once you have the source downloaded, navigate to the “src” folder. Open the “Caliburn.Micro.sln” Pess Ctrl-Shift-B (or use the Build menu) to build the solution in Visual Studio. Now that we have the framework and it’s been successfully built, let’s create a simple application.

Open Visual Studio and create a new Silverlight 4 Application called “Caliburn.Micro.Hello”. You don’t need a web site or test project. Add a reference to System.Windows.Interactivity.dll and Caliburn.Micro.dll. You can find them both in the \src\Caliburn.Micro.Silverlight\Bin\Release (or Debug) folder. Delete “MainPage.xaml” and clean up your “App.xaml.cs” so that it looks like this:

namespace Caliburn.Micro.Hello
{
    using System.Windows;

    public partial class App : Application
    {
        public App()
        {
            InitializeComponent();
        }
    }
}

Since Caliburn.Micro prefers a View-Model-First approach, let’s start there. Create your first VM and call it ShellViewModel. Use the following code for the implementation:

namespace Caliburn.Micro.Hello
{
    using System.Windows;

    public class ShellViewModel : PropertyChangedBase
    {
        string name;

        public string Name
        {
            get { return name; }
            set
            {
                name = value;
                NotifyOfPropertyChange(() => Name);
                NotifyOfPropertyChange(() => CanSayHello);
            }
        }

        public bool CanSayHello
        {
            get { return !string.IsNullOrWhiteSpace(Name); }
        }

        public void SayHello()
        {
            MessageBox.Show(string.Format("Hello {0}!", Name)); //Don't do this.
        }
    }
}

Notice that the ShellViewModel inherits from PropertyChangedBase. This is a base class that implements the infrastructure for property change notification and automatically performs UI thread marshalling. It will come in handy :)

Now that we have our VM, let’s create the bootstrapper that will configure the framework and tell it what to do. Create a new class named HelloBootstrapper. You can use this tiny bit of code:

namespace Caliburn.Micro.Hello
{
    public class HelloBootstrapper : Bootstrapper<ShellViewModel> {}
}

There are two Bootsrappers available in Caliburn.Micro. This version allows you to specify the type of “root view model” via the generic type. The “root view mdoel” is a VM that Caliburn.Micro will instantiate and use to show your application. Next, we need to place the HelloBootstrapper somewhere where it will be run at startup. To do that, change your App.xaml to match this:

<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:Caliburn.Micro.Hello"
             x:Class="Caliburn.Micro.Hello.App">
    <Application.Resources>
        <local:HelloBootstrapper x:Key="bootstrapper" />
    </Application.Resources>
</Application>

All we have to do is place a Caliburn.Micro bootstrapper in the Application.Resources and it will do the rest of the work.* Now, run the application. You should see something like this:

ViewNotFound

Caliburn.Micro creates the ShellViewModel, but doesn’t know how to render it. So, let’s create a view. Create a new Silverlight User Control named ShellView. Use the following xaml:

<UserControl x:Class="Caliburn.Micro.Hello.ShellView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <StackPanel>
        <TextBox x:Name="Name" />
        <Button x:Name="SayHello"
                Content="Click Me" />
    </StackPanel>
</UserControl>

Run the application again. You should now see the UI:

ViewFound

Typing something in the TextBox will enable the Button and clicking it will show a message:

ViweWithData

Caliburn.Micro uses a simple naming convention to locate Views for ViewModels.  Essentially, it takes the FullName and removes “Model” from it. So, given MyApp.ViewModels.MyViewModel, it would look for MyApp.Views.MyView. Looking at the View and ViewModel side-by-side, you can see that the TextBox with x:Name=”Name” is bound to the “Name” property on the VM. You can also see that the Button with x:Name=”SayHello” is bound to the method with the same name on the VM.  The “CanSayHello” property is guarding access to the “SayHello” action by disabling the Button. These are the basics of Caliburn.Micro’s ActionMessage and Conventions functionality. There’s much more to show. But, next time I want to show how we can integrate an IoC container such as MEF. This sample is attached below.

 

 

*All the functionality described in this article works identically for both Caliburn.Micro and Caliburn…with one exception. Caliburn does not have a Bootstrapper currently. At this time, you would inherit from CaliburnApplication and override CreateRootModel. In the coming months, Caliburn’s custom application class will be replaced by a bootstrapper mechanism similar to Caliburn.Micro.

Comments [14]

When my “Build Your Own MVVM Framework” talk was chosen for Mix10, I put a temporary hold on this series of blog posts. I wanted to dedicate significant time to working on a sample framework and demo that would make a top notch Mix presentation. After giving the talk, I received a ton of positive feedback on the techniques/framework that I had demonstrated. I was approached by several companies and individuals who expressed an interest in a more “official” version of what I had shown. That, coupled with the coming of Windows Phone 7, impressed upon me a need to have a more “lean and mean” framework. As a result, I’ve spent my spare time over the last six weeks building just that. My vision was to take 90% of Caliburn’s features and squash them into 10% of the code. I also hoped I could fit it in about 50k, give or take. I started with the Mix sample framework, then used an iterative process of integrating Caliburn v2 features, simplifying and refactoring. I continued along those lines until I had what I felt was a complete solution that mirrored the full version of Caliburn v2, but on a smaller scale. I call this new framework Calburn.Micro.

Caliburn.Micro consists of one ~50k assembly that builds for WPF4, SL4 and WP7*. It has a single dependency, System.Windows.Interactivity, which you are probably already using regularly in development. Caliburn.Micro is about 2,000 LOC total, so you can easily go through the whole codebase in an afternoon and hold it in your head. That’s less than 10% of the size of Caliburn v2, which is running around 27,000 LOC and is a lot harder to grasp in a short time. So, it looks like I was able to meat my code quantity and size goals.  But the best part is that I believe I was able to put something together that contains all of the features I consider most important in Caliburn. Here’s a brief list:

  • ActionMessages – The Action mechanism allows you to “bind” UI triggers, such as a Button’s “Click” event, to methods on your View-Model or Presenter. The mechanism allows for passing parameters to the method as well. Parameters can be databound to other FrameworkElements or can pass special values, such as the DataContext or EventArgs. All parameters are automatically type converted to the method’s signature. This mechanism also allows the “Action.Target” to vary independently of the DataContext and enables it to be declared at different points in the UI from the trigger. When a trigger occurs, the “message” bubbles through the element tree looking for an Action.Target (handler) that is capable of invoking the specified method. This is why we call them messages. The “bubbling” nature of Acton Messages is extremely powerful and very helpful especially in master/detail scenarios. In addition to invocation, the mechanism supports a “CanExecute” guard. If the Action has a corresponding Property or Method with the same name, but precede by the word “Can,” the invocation of the Action will be blocked and the UI will be disabled. Actions also support Coroutines (see below). That’s all fairly standard for existing Caliburn users, but we do have a few improvements in Caliburn.Micro that will be making their way into the larger framework. The Caliburn.Micro implementation of ActionMessages is built on System.Windows.Interactivity. This allows actions to be triggered by any TriggerBase developed by the community. Furthermore, Caliburn.Micro’s Actions have full design-time support in Blend. Code-centric developers will be happy to know that Caliburn.Micro supports a very terse syntax for declaring these ActionMessages through a special attached property called Message.Attach.
  • Action Conventions – Out of the box, we support a set of binding conventions around the ActionMessage feature. These conventions are based on x:Name. So, if you have a method called “Save” on your ViewModel and a Button named “Save” in your UI, we will automatically create an EventTrigger for the “Click” event and assign an ActionMessage for the “Save” method. Furthermore, we will inspect the method’s signature and properly construct the ActionMessage parameters. This mechanism can be turned off or customized. You can even change or add conventions for different controls. For example, you could make the convention event for Button “MouseMove” instead of “Click” if you really wanted.
  • Binding Conventions – We also support convention-based databinding. This too works with x:Name. If you have a property on your ViewModel with the same name as an element, we will attempt to databind them. Whereas the framework understands convention events for Actions, it additionally understands convention binding properties (which you can customize or extend). When a binding name match occurs, we then proceed through several steps to build up the binding (all of which are customizable), configuring such details as BindingMode, StringFormat, ValueConverter, Validation and UpdateSourceTrigger (works for SL TextBox and PasswordBox too). Finally, we support the addition of custom behaviors for certain scenarios. This allows us to detect whether we need to auto-generate a DataTemplate or wire both the ItemsSource and the SelectedItem of a Selector based on naming patterns.
  • Screens and Conductors – The Screen, ScreenConductor and ScreenCollection patterns enable model-based tracking of the active or current item, enforcing of screen lifecycles and elegant shutdown or shutdown cancellation in an application. Caliburn.Micro’s implementation of these patterns is an evolution of the one found in Caliburn and supports conducting any type of class, not just implementations of IScreen. These improvements are being introduced back into Caliburn. You’ll find that Caliburn.Micro’s screen implementation is quite thorough and even handles asynchronous shutdown scenarios with ease.
  • Event Aggregator – Coming in at about 75LOC, Caliburn.Micro’s EventAggregator is simple yet powerful. The aggregator follows a bus-style pub/sub model. You register a message handler with the aggregator, and it sends you any messages you are interested in. You declare your interest in a particular message type by implementing IHandle<TMessage>. References to handlers are held weakly and publication occurs on the UI thread.
  • Coroutines – Any action can optionally choose to return IResult or IEnumerable<IResult>, opening the door to a powerful approach to handling asynchronous programming. Furthermore, implementations of IResult have access to an execution context which tells them what ActionMessage they are executing for, what FrameworkElement triggered the messsage to be sent, what instance the ActionMessage was handled by (invoked on) and what the View is for that instance. Such contextual information enables a loosely-coupled, declarative mechanism by which a Presenter or View-Model can communicate with it’s View without needing to hold a reference to it at any time.
  • ViewLocator – For every ViewModel in your application, Caliburn.Micro has a basic strategy for locating the View that should render it. We do this based on naming conventions. For example, if your VM is called MyApplication.ViewModels.ShellViewModel, we will look for MyApplication.Views.ShellView. Additionally, we support multiple views over the same View-Model be attaching a View.Context in Xaml. So, given the same model as above, but with a View.Context=”Master” we would search for MyApplication.Views.Shell.Master. Of coarse, all this is customizable.
  • WindowManager – This service provides a View-Model-centric way of displaying Windows (ChildWindow in SL and Window in WPF). Simply pass it an instance of the VM and it will locate the view, wrap it in a Window if necessary, apply all conventions you have configured and show the window.
  • PropertyChangedBase and BindableCollection – What self respecting WPF/SL framework could go without a base implementation of INotifyPropertyChanged? The Caliburn.Micro implementation enables string and lambda-based change notification. It also ensures that all events are raised on the UI thread. BindableCollection is a simple collection that inherits from ObservableCollection<T>, but that ensures that all its events are raised on the UI thread as well.
  • Bootstrapper – What’s required to configure this framework and get it up and running? Not much. Simply inherit from Bootsrapper and add an instance of your custom bootstrapper to the Application’s ResourceDictionary. Done. If you want, you can override a few methods to plug in your own IoC container, declare what assemblies should be inspected for Views, etc. It’s pretty simple. You’ll see how that works in my next blog post.
  • Logging – Caliburn.Micro implements a basic logging abstraction. This is important in any serious framework that encourages Convention over Configuration. All the most important parts of the framework are covered with logging. Want to know what conventions are or are not being applied? Turn on logging. Want to know what actions are being executed? Turn on logging. Want to know what events are being published? Turn on logging. You get the picture.
  • MVVM and MVP – In case it isn’t obvious, this framework enables MVVM. MVVM isn’t hard on it’s own, but Caliburn.Micro strives to go beyond simply getting it done. We want to write elegant, testable, maintainable and extensible presentation layer code…and we want it to be easy to do so. That’s what this is about. If you prefer using Supervising Controller and PassiveView to MVVM, go right ahead. You’ll find that Caliburn.Micro can help you a lot, particularly it’s Screen/ScreenConductor implementation.*** If you are not interested in any of the goals I just mentioned, you’d best move along. This framework isn’t for you.

Just to be clear, this isn’t a toy framework. I’m using it on two Silverlight 4 applications that I’m building right now. The experience has been good and I haven’t had a need for anything beyond what it provides. As I said, I really focused on supporting the core and most commonly used features from Caliburn v2. In fact, Caliburn.Micro is going to be my default framework moving forward and I recommend that if you are starting a new project you begin with the Micro framework. I’ve been careful to keep the application developer API consistent with the full version of Caliburn. In fact, some of the improvements I made in Caliburn.Micro are going to be folded back into Caliburn v2 in the coming months. What’s the good part about that? You can start developing with Caliburn.Micro, then if you hit edge cases or have some other need to move to Caliburn, you will be able to do so with little or no changes in your application.**

In the coming months I’ll be blogging extensively about Caliburn.Micro. In fact, I’m planning to take you on a tour of the entire codebase. Keeping with the spirit of “Build Your Own…” I want developers to understand how this little framework works, inside and out. I’ve intentionally chosen Mercurial for source control, because I want developers to take ownership. While I’ve done some work to make the most important parts of the framework extensible, I’m hoping to see many Forks, each with their own customizations unique to their application’s needs.

 

*When compressed into a .xap, Caliburn.Micro is about 24k. Also, it can be built for WPF3.5 and SL3. Though the SL3 version will be short one feature at runtime (ActionMessage Parameters databound to Elements) due to unfinished work in the SL3 DependencyObject/DepencenyProperty system. That’s actually true for the current WP7 build as well.

**What are the differences between Caliburn and Caliburn.Micro? Caliburn has more features and supports a lot of edge cases. Also, the configuration and extensibility models are different. I’ll be talking about this in more detail in the future.

***The whole ScreenConductor piece is actually a Supervising Controller implementation…shshshsh…don’t tell anyone…but I almost always use a combination of MVVM and Supervising Controller (and other patterns). I like to use the best tool for the job.

Comments [9]

View our blog archives, visit us at devlicio.us.