Dependent Actions

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 Dependent Actions (Based on Samples - DependentActions)

Dependent actions offer an additional set of features on top of the standard action mechanism. They enable a way to tell Caliburn about what properties of the class the action is dependent on. Using this information, the developer can have more fine grained control over the invalidation of trigger availability and Caliburn can more deeply understand the system.
  • In your project, create a new class named Calculator. Use the code below:

public class Calculator : INotifyPropertyChanged
{
    private double _left;
    private double _right;
    private double _result;
 
    public double Left
    {
        get { return _left; }
        set { _left = value; RaisePropertyChanged("Left"); }
    }
 
    public double Right
    {
        get { return _right; }
        set { _right = value; RaisePropertyChanged("Right"); }
    }
 
    public double Result
    {
        get { return _result; }
        set { _result = value; RaisePropertyChanged("Result"); }
    }
 
    [Preview("CanDivide")]
    [Dependencies("Left", "Right")]
    public void Divide()
    {
        Result = Left / Right;
    }
 
    public bool CanDivide()
    {
        return Right != 0 && Left != 0;
    }
 
    public event PropertyChangedEventHandler PropertyChanged = delegate { };
 
    protected virtual void RaisePropertyChanged(string propertyName)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}
  • Next, use the following markup to implement your main Page (for Silverlight) or your Window (for WPF). The markup is the same with the exception that WPF could use a single xmlns:

WPF and Silverlight
<UserControl x:Class="DependentActions.Page"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:cm="clr-namespace:Caliburn.RoutedUIMessaging;assembly=Caliburn.RoutedUIMessaging"
             xmlns:ca="clr-namespace:Caliburn.Actions;assembly=Caliburn.Actions"
             xmlns:local="clr-namespace:DependentActions"
             Width="400"
             Height="300">
    <ca:Action.Target>
        <local:Calculator />
    </ca:Action.Target>
     
    <StackPanel>
        <Grid Margin="10">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
 
            <TextBox Grid.Column="0"
                     Text="{Binding Left, Mode=TwoWay}" />
            <TextBlock Text="/"
                       Margin="10 0"
                       Grid.Column="1" />
            <TextBox Grid.Column="2"
                     Text="{Binding Right, Mode=TwoWay}" />
            <Border BorderBrush="Black"
                    BorderThickness="0 0 0 1"
                    Margin="10 0 0 0"
                    Grid.Column="3">
                <TextBlock Text="{Binding Result}" />
            </Border>
        </Grid>
 
        <Button Content="Divide"
                cm:Message.Attach="Divide" />
    </StackPanel>
</UserControl>

Note: This sample instantiates the Calculator instance inline. I generally prefer not to have the view control instantiation. This is for demo purposes only.

Run the application, enter values into the fields and click the button. You'll notice that the application acts similar to previous examples. In this version, we have no need to pass parameters to the action, because the View Model is maintaining the state of the needed values through its properties. In order for this to work, you must implement INotifyPropertyChanged. This allows Caliburn to track changes on specified properties in order to refresh trigger availability. Use the DependenciesAttribute to specify the properties that a particular action is dependent on. Using this technique, the controller/presenter can manually invalidate trigger affecting filters by changing the dependent property values.

Note: For a more responsive UI, WPF developers should consider using UpdateSourceTrigger=PropertyChanged on databindings to dependent properties.

Last edited May 5, 2010 at 8:34 PM by EisenbergEffect, version 13