Composite Commands

Prerequisites

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

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 Composite Commands (Based on Samples - CompositeCommands)

Caliburn's commands allow developers to create a hierarchy of commands, where the execution of a parent command executes its child commands. Additionally, the ability to execute the parent command can depend on the availability of its child commands. Let's build a simple example:
  • Create a new class called ShowMessageCommand using the code below.
public class ShowMessageCommand
{
    public bool CanExecute(string message)
    {
        return !string.IsNullOrEmpty(message);
    }
 
    [Preview("CanExecute")]
    [AsyncAction(Callback = "Callback", BlockInteraction = true)]
    public string Execute(string message)
    {
        Thread.Sleep(2000);
        return "Your message: " + message;
    }
 
    public void Callback(string message)
    {
        MessageBox.Show(message);
    }
}
  • Create another class named ShowTitledMessageCommand matching the following code:
public class ShowTitledMessageCommand
{
    public bool CanExecute(string title, string message)
    {
        return !string.IsNullOrEmpty(title) && !string.IsNullOrEmpty(message);
    }
 
    [Preview("CanExecute")]
    [AsyncAction(Callback = "Callback", BlockInteraction = true)]
    public MessageInfo Execute(string title, string message)
    {
        Thread.Sleep(4000);
        return new MessageInfo { Title = title, Message = message };
    }
 
    public void Callback(MessageInfo message)
    {
        MessageBox.Show(message.Message, message.Title, MessageBoxButton.OK);
    }
 
    public class MessageInfo
    {
        public string Title { get; set; }
        public string Message { get; set; }
    }
}
  • Use the following markup to implement your main Page (for Silverlight) or your Window (for WPF):

Silverlight
<UserControl x:Class="CompositeCommands.Page"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:cc="clr-namespace:Caliburn.Commands;assembly=Caliburn.Commands"
             xmlns:cm="clr-namespace:Caliburn.RoutedUIMessaging;assembly=Caliburn.RoutedUIMessaging"
             Width="600"
             Height="300">
    <UserControl.Resources>
        <cc:AllCommand x:Key="Grandfather" />
        <cc:AllCommand x:Key="All" />
        <cc:AnyCommand x:Key="Any" />
    </UserControl.Resources>
 
    <StackPanel>
        <Button Content="Grandfather Composite"
                cm:Message.Attach="ResourceCommand Grandfather" />
 
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
 
            <StackPanel>
                <TextBlock Text="All Composite" />
                <TextBox x:Name="allMessage" />
                <TextBox x:Name="allTitle" />
 
                <Button Content="All Composite"
                        cc:Command.Parent="{StaticResource Grandfather}"
                        cm:Message.Attach="ResourceCommand All" />
                <Button Content="Attached Container Command w/ 1 Parameter"
                        cc:Command.Parent="{StaticResource All}"
                        cm:Message.Attach="ContainerCommand ShowMessage(allMessage.Text)" />
                <Button Content="Attached Container Command w/ 2 Parameters"
                        cc:Command.Parent="{StaticResource All}"
                        cm:Message.Attach="ContainerCommand ShowTitledMessage(allMessage.Text, allTitle.Text)" />
            </StackPanel>

            <StackPanel Grid.Column="1">
                <TextBlock Text="Any Composite" />
                <TextBox x:Name="anyMessage" />
                <TextBox x:Name="anyTitle" />
 
                <Button Content="Any Composite"
                        cc:Command.Parent="{StaticResource Grandfather}"
                        cm:Message.Attach="ResourceCommand Any" />
                <Button Content="Attached Container Command w/ 1 Parameter"
                        cc:Command.Parent="{StaticResource Any}"
                        cm:Message.Attach="ContainerCommand ShowMessage(anyMessage.Text)" />
                <Button Content="Attached Container Command w/ 2 Parameters"
                        cc:Command.Parent="{StaticResource Any}"
                        cm:Message.Attach="ContainerCommand ShowTitledMessage(anyMessage.Text, anyTitle.Text)" />
            </StackPanel>
        </Grid>
    </StackPanel>
</UserControl>

WPF
<Window x:Class="CompositeCommands.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:cal="http://www.caliburnproject.org"
        Title="Window1">
    <Window.Resources>
        <cal:AllCommand x:Key="Grandfather" />
        <cal:AllCommand x:Key="All" />
        <cal:AnyCommand x:Key="Any" />
    </Window.Resources>
 
    <StackPanel>
        <Button Content="Grandfather Composite"
                cal:Message.Attach="ResourceCommand Grandfather" />
 
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
 
            <GroupBox Header="All Composite">
                <StackPanel>
                    <TextBox x:Name="allMessage" />
                    <TextBox x:Name="allTitle" />
 
                    <Button Content="All Composite"
                            cal:Command.Parent="{StaticResource Grandfather}"
                            cal:Message.Attach="ResourceCommand All" />
                    <Button Content="Attached Container Command w/ 1 Parameter"
                            cal:Command.Parent="{StaticResource All}"
                            cal:Message.Attach="ContainerCommand ShowMessage(allMessage.Text)" />
                    <Button Content="Attached Container Command w/ 2 Parameters"
                            cal:Command.Parent="{StaticResource All}"
                            cal:Message.Attach="ContainerCommand ShowTitledMessage(allMessage.Text, allTitle.Text)" />
                </StackPanel>
            </GroupBox>
 
            <GroupBox Header="Any Composite"
                      Grid.Column="1">
                <StackPanel>
                    <TextBox x:Name="anyMessage" />
                    <TextBox x:Name="anyTitle" />
                     
                    <Button Content="Any Composite"
                            cal:Command.Parent="{StaticResource Grandfather}"
                            cal:Message.Attach="ResourceCommand Any" />
                    <Button Content="Attached Container Command w/ 1 Parameter"
                            cal:Command.Parent="{StaticResource Any}"
                            cal:Message.Attach="ContainerCommand ShowMessage(anyMessage.Text)" />
                    <Button Content="Attached Container Command w/ 2 Parameters"
                            cal:Command.Parent="{StaticResource Any}"
                            cal:Message.Attach="ContainerCommand ShowTitledMessage(anyMessage.Text, anyTitle.Text)" />
                </StackPanel>
            </GroupBox>
        </Grid>
    </StackPanel>
</Window>

Note: The only differences between the WPF and Silverlight version is in WPF's use of GroupBox and it's ability to work with a single xmlns.
  • Configure Caliburn with the following code. 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();
 
CaliburnFramework
    .ConfigureCore(container)
    .WithPresentationFramework()
    .Start();
 
container.RegisterSingleton<ShowMessageCommand>("ShowMessage");
container.RegisterSingleton<ShowTitledMessageCommand>("ShowTitledMessage");

Run the application. Enter text into the different text boxes. Notice how the parent commands are affected by their children. Try clicking the buttons for various commands. Notice that a parent command executes all it children. In this example, we have used two different type of composite commands, AnyCommand and AllCommand. AllCommand requires all it's children to be available before it can execute (it's a logical 'and'). AnyCommand requires only one child to be available (it's a logical 'or'). You can also create your own composite logic by implementing ICompositeCommand.

As you can see, to create the hierarchy, simply use the attached property Command.Parent to resolve the parent instance. If you are using the long Xaml syntax for declaring triggers/messages, you can set the Parent property on the CommandMessage itself.

Last edited Dec 27, 2009 at 9:52 PM by EisenbergEffect, version 13