Command Basics

Prerequisites

  1. Obtain and Build the Code
  2. Setting Up a Project
  3. Master Action Basics

Referenced Assemblies

  • Caliburn.Core
  • Caliburn.PresentationFramework
  • Microsoft.Practices.ServiceLocation

Minimum Configuration

Note: Configuration should be placed in the App.xaml.cs constructor for WPF or the App.xaml.cs Application_Startup for Silverlight.

CaliburnFramework
    .ConfigureCore()
    .WithPresentationFramework()
    .Start();
Note: As an alternative to manual configuration, you can inherit your application from CaliburnApplication.

Using Commands (Based on Samples - Commands)

Caliburn's commands provide a basic implementation of the command pattern. Silverleright, unlike WPF, does not have a built-in command mechanism. So, Caliburn provides a much needed feature. However, Caliburn's command implementation is much richer than the native WPF implementation, so WPF developers should find great benefit to using Caliburn's mechanism. Much of the richness of the command implementation stems from the fact that it is built on top of the actions system. Thus, Caliburn Commands inherit all the features of actions. This includes multiple input parameters, filters, asynchronous execution, IResult, etc. Caliburns commands can also exist in hierarchies. Let's take a look at how to use commands.
  • Create a new class called ShowMessageCommand and use the code below to implement it.
public class ShowMessageCommand
{
    [Preview("CanExecute")]
    public void Execute(string message)
    {
        MessageBox.Show(message);
    }
 
    public bool CanExecute(string message)
    {
        return !string.IsNullOrEmpty(message);
    }
}
  • Create another class called ShowTitledMessageCommand and use the following code to implement it:
public class ShowTitledMessageCommand
{
    [Preview("CanExecute")]
    public void Execute(string title, string message)
    {
        MessageBox.Show(message, title, MessageBoxButton.OK);
    }
 
    public bool CanExecute(string title, string message)
    {
        return !string.IsNullOrEmpty(title) && !string.IsNullOrEmpty(message);
    }
}

Note: It is not a good practice to call MessageBox.Show from a non-View class. Consider implementing a MessageBoxService.

We now have two commands that Caliburn can execute. Notice that they use the same attributes and filters as actions. The basic requirement for a Caliburn command is that it have a method named "Execute," although you can override this by applying the Command attribute to the class and specifying a different method to call. The execute method can have any number of parameters and can even have a return value. If you follow the convention Can{Execute}, then you don't even need to apply the Preview filter, Caliburn will add it for you. This makes it very easy to plug native WPF commands into Caliburn's command framework. Let's create UI for executing these commands next.
  • Use the following markup to implement your main Page (for Silverlight) or your Window (for WPF):

Silverlight
<UserControl x:Class="Commands.Page"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:Commands"
             xmlns:cm="clr-namespace:Caliburn.RoutedUIMessaging;assembly=Caliburn.RoutedUIMessaging"
             xmlns:ct="clr-namespace:Caliburn.RoutedUIMessaging.Triggers;assembly=Caliburn.RoutedUIMessaging"
             xmlns:cc="clr-namespace:Caliburn.Commands;assembly=Caliburn.Commands"
             Width="400"
             Height="300">
    <UserControl.Resources>
        <local:ShowTitledMessageCommand x:Key="ShowTitledMessage" />
    </UserControl.Resources>
 
    <StackPanel>
        <TextBox x:Name="title" />
        <TextBox x:Name="message" />
 
        <Button Content="Attached Container Command w/ 1 Parameter"
                cm:Message.Attach="ContainerCommand ShowMessage(message.Text)" />
 
        <Button Content="Attached Resource Command w/ 2 Parameters"
                cm:Message.Attach="ResourceCommand ShowTitledMessage(title.Text, message.Text)" />
 
        <Button Content="Triggers: Container Command With 1 Explicit Parameters">
            <cm:Message.Triggers>
                <ct:RoutedMessageTriggerCollection>
                    <ct:EventMessageTrigger EventName="Click">
                        <ct:EventMessageTrigger.Message>
                            <cc:CommandMessage Command="ShowMessage">
                                <cm:Parameter ElementName="message" 
                                              Path="Text" />
                            </cc:CommandMessage>
                        </ct:EventMessageTrigger.Message>
                    </ct:EventMessageTrigger>
                </ct:RoutedMessageTriggerCollection>
            </cm:Message.Triggers>
        </Button>
    </StackPanel>
</UserControl>

WPF
<Window x:Class="Commands.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:Commands"
        xmlns:cal="http://www.caliburnproject.org"
        Title="Window1"
        SizeToContent="Height">
    <Window.Resources>
        <local:ShowTitledMessageCommand x:Key="ShowTitledMessage" />
    </Window.Resources>
 
    <StackPanel>
        <TextBox x:Name="title" />
        <TextBox x:Name="message" />
 
        <Button Content="Attached Container Command w/ 1 Parameter" 
                cal:Message.Attach="ContainerCommand ShowMessage(message.Text)"/>
 
        <Button Content="Attached Resource Command w/ 2 Parameters"
                cal:Message.Attach="ResourceCommand ShowTitledMessage(title.Text, message.Text)" />
 
        <Button Content="Triggers: Container Command With 1 Explicit Parameters">
            <cal:Message.Triggers>
                <cal:RoutedMessageTriggerCollection>
                    <cal:EventMessageTrigger EventName="Click">
                        <cal:EventMessageTrigger.Message>
                            <cal:CommandMessage Command="{cal:Resolve Key=ShowMessage}">
                                <cal:Parameter Value="{Binding ElementName=message, Path=Text}"/>
                            </cal:CommandMessage>
                        </cal:EventMessageTrigger.Message>
                    </cal:EventMessageTrigger>
                </cal:RoutedMessageTriggerCollection>
            </cal:Message.Triggers>
        </Button>
    </StackPanel>
</Window>
  • Finally, make sure to add the following code to configure the commands. It should be placed in the App.xaml.cs constructor for WPF or the App.xaml.cs Application_Startup for Silverlight.

var container = new SimpleContainer();
 
CaliburnApplication
    .ConfigureCore(container)
    .WithPresentationFramework()
    .Start();
 
container.RegisterSingleton<ShowMessageCommand>("ShowMessage");

Run the application. Put various messages in the different text boxes. Try clicking the buttons. Notice that various buttons become disabled when certain boxes are empty. Let's see how this all works. Starting with the bottom most button, we have demonstrated the full syntax for wiring a command. This will not normally be necessary, unless you have multiple triggers wired to the same UI. But, it serves to demonstrate all the pieces involved in command processing. Triggers work as previously explained. Notice how we have used a CommandMessage instead of an ActionMessage though. You should use this message type to declare the command and the set of parameters (if any) that should be sent to it.

Note: The Command property should be set to an instance of a command. Normally this command would come from the dependency injection container, framework element resources, databinding, or it could be a static property on some class. WPF can use markup extensions for all these scenarios. Silverlight has some limitations though. Bindings and Static Resources still work, however there is no TypeExtension available, so statics cannot be used (not a good practice in my opinion anyways). Also, container resolution is not possible using the standard mechanism. To address this problem, in Silverlight, you can set the Command property to a string and that will be used to resolve from the container by Key. If you are using the SimpleContainer (the default), you can use a type's FullName to resolve by type.

Now, take a look at the other two buttons, which use the shortened syntax. In both cases, Caliburn is inferring the trigger type. We are then specifying whether the command should be retrieved from the container (ContainerCommand) or from the resources (ResourceCommand). You can also use databinding to get the command with BoundCommand. We then provide the resource key/container key/binding path and the set of parameters that should be passed to the command. You could optionally specify a return binding by the following the same pattern that the shortened syntax for ActionMessage uses.

Last edited May 5, 2010 at 8:36 PM by EisenbergEffect, version 18