Combining Command and TriggerAction In Silverlight

Since Blend 3, we can create a TriggerAction to implement various redundant things. Even redundant business logic can be implemented as TriggerAction.

Commands on the other side is great way to notify controls that an action can be executed. A command can also be used on more than one control.

In this post, I will discuss a way to create command and bind it to an action so the action is invoked when a command is executed. After that, you will be able to use Blend 3 to create a command in resources and bind it to an action.

A reusable command

We will start by implementing a command that have an event Executing. This event is going to occur when the method Execute is called.

public class EventCommand : DependencyObject, ICommand
{
    #region Methods
    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        if (Executing != null)
            Executing(parameter);
    }
    #endregion

    #region Events
    public event EventHandler CanExecuteChanged;
    public event ExecuteEventHandler Executing;
    #endregion
}

public delegate void ExecuteEventHandler(object parameter);

A Trigger to invoke the action

We need a class that will handle the executing event of the EventCommand and fire the action. We call this class a CommandTrigger. Here is the implementation of this trigger:

public class CommandTrigger : TriggerBase<DependencyObject>
{
    #region Methods
    private void HandleCommandExecuting(object parameter)
    {
        InvokeActions(parameter);
    }
    #endregion

    #region Properties
    #region Command DependencyProperty
    public EventCommand Command
    {
        get { return (EventCommand)GetValue(CommandProperty); }
        set { SetValue(CommandProperty, value); }
    }

    public static readonly DependencyProperty CommandProperty = DependencyProperty.Register
    (
        "Command",
        typeof(EventCommand),
        typeof(CommandTrigger),
        new PropertyMetadata(OnCommandChanged)
    );

    private static void OnCommandChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        var trigger = (CommandTrigger)o;
        var oldValue = (EventCommand)e.OldValue;
        var newValue = (EventCommand)e.NewValue;

        if (oldValue != null)
            oldValue.Executing -= trigger.HandleCommandExecuting;

        if (newValue != null)
            newValue.Executing += trigger.HandleCommandExecuting;
    }
    #endregion
    #endregion
}

Advantages of this technique:

  • Having a trigger separate the action from the invoker. The action can now be invoked when the control is loaded, on a timer.
  • Having a command helps reuse the same instance of the action and invoke it from anywhere.

Disavantages of this techinique:

  • I did not find a good way of setting the CanExecute property of the command.

Usage

Image you want to remove an element when the user click a button, you can now do it like that:

<Grid>
   <Grid.Resources>
      <local:EventCommand x:Key="RemoveElementCommand"/>
   </Grid.Resources>

   <Button Command="{StaticResource RemoveElementCommand}"
               Content="Remove Element" />

   <ContentControl>
      <i:Interaction.Triggers>
         <local:CommandTrigger Command="{StaticResource RemoveElementCommand}">
            <mi:RemoveElementAction/>
         </local:CommandTrigger>
      </i:Interaction.Triggers>
   </ContentControl>

</Grid>

Happy programming!

0 comments: