Prism 4 – Silverlight and Custom Commands

Silverlight 4 includes commanding support for ButtonBase classes, but for anything else we would need to write a little bit of code.

There is a nice section here on how to extend this to other situations. Here is a quick sample on how to create command support for MouseLeftButtonDown on an UIElement. You can download the code from here.

Here is how we can use the Command behavior we create for invoking a command on the mouseleftbutton up event

<UserControl x:Class=”SLPrismCustomCommands.MainPage”
    xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation
    xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml
    xmlns:d=”http://schemas.microsoft.com/expression/blend/2008
    xmlns:mc=”http://schemas.openxmlformats.org/markup-compatibility/2006
    mc:Ignorable=”d”
             xmlns:local=”clr-namespace:SLPrismCustomCommands”
    d:DesignHeight=”300″ d:DesignWidth=”400″>

    <Grid x:Name=”LayoutRoot” Background=”White”>
        <Ellipse Fill=”Green” Width=”20″ Height=”20″
                 local:Click.CommandParameter=”{Binding RelativeSource={RelativeSource Self}}”
                 local:Click.Command=”{Binding MyCommand}”/>
    </Grid>
</UserControl>

The following would create a behavior

  public class MouseLeftButtonDownBehavior : CommandBehaviorBase<UIElement>
    {
        public MouseLeftButtonDownBehavior(UIElement obj)
            : base(obj)
        {
            if (obj == null) throw new System.ArgumentNullException(“object cannot be null”);
            obj.MouseLeftButtonDown += new MouseButtonEventHandler(OnMouseButton);
        }

        private void OnMouseButton(object sender, System.Windows.RoutedEventArgs e)
        {
            ExecuteCommand();
        }
    }

Here is the class for the Attached properties

public static class Click
    {
        private static readonly DependencyProperty ClickCommandBehaviorProperty = DependencyProperty.RegisterAttached(
            “ClickCommandBehavior”,
            typeof(MouseLeftButtonDownBehavior),
            typeof(Click),
            null);
        /// <summary>
        /// Command to execute on click event.
        /// </summary>
        public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached(
            “Command”,
            typeof(ICommand),
            typeof(Click),
            new PropertyMetadata(OnSetCommandCallback));

        /// <summary>
        /// Command parameter to supply on command execution.
        /// </summary>
        public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.RegisterAttached(
            “CommandParameter”,
            typeof(object),
            typeof(Click),
            new PropertyMetadata(OnSetCommandParameterCallback));
        /// <summary>
        /// Sets the <see cref=”ICommand”/> to execute on the click event.
        /// </summary>
        /// <param name=”buttonBase”>UIElement dependency object to attach command</param>
        /// <param name=”command”>Command to attach</param>       
        public static void SetCommand(UIElement element, ICommand command)
        {
            if (element == null) throw new System.ArgumentNullException(“element”);
            element.SetValue(CommandProperty, command);
        }

        /// <summary>
        /// Retrieves the <see cref=”ICommand”/> attached to the <see cref=”ButtonBase”/>.
        /// </summary>
        /// <param name=”buttonBase”>ButtonBase containing the Command dependency property</param>
        /// <returns>The value of the command attached</returns>       
        public static ICommand GetCommand(UIElement element)
        {
            if (element == null) throw new System.ArgumentNullException(“element”);
            return element.GetValue(CommandProperty) as ICommand;
        }

        /// <summary>
        /// Sets the value for the CommandParameter attached property on the provided <see cref=”ButtonBase”/>.
        /// </summary>
        /// <param name=”buttonBase”>ButtonBase to attach CommandParameter</param>
        /// <param name=”parameter”>Parameter value to attach</param>       
        public static void SetCommandParameter(UIElement element, object parameter)
        {
            if (element == null) throw new System.ArgumentNullException(“element”);
            element.SetValue(CommandParameterProperty, parameter);
        }

        /// <summary>
        /// Gets the value in CommandParameter attached property on the provided <see cref=”ButtonBase”/>
        /// </summary>
        /// <param name=”buttonBase”>ButtonBase that has the CommandParameter</param>
        /// <returns>The value of the property</returns>       
        public static object GetCommandParameter(UIElement element)
        {
            if (element == null) throw new System.ArgumentNullException(“element”);
            return element.GetValue(CommandParameterProperty);
        }

        private static void OnSetCommandCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            UIElement element = dependencyObject as UIElement;
            if (element != null)
            {
                MouseLeftButtonDownBehavior behavior = GetOrCreateBehavior(element);
                behavior.Command = e.NewValue as ICommand;
            }
        }

        private static void OnSetCommandParameterCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            UIElement element = dependencyObject as UIElement;
            if (element != null)
            {
                MouseLeftButtonDownBehavior behavior = GetOrCreateBehavior(element);
                behavior.CommandParameter = e.NewValue;
            }
        }

        private static MouseLeftButtonDownBehavior GetOrCreateBehavior(UIElement element)
        {
            MouseLeftButtonDownBehavior behavior = element.GetValue(ClickCommandBehaviorProperty) as MouseLeftButtonDownBehavior;
            if (behavior == null)
            {
                behavior = new MouseLeftButtonDownBehavior(element);
                element.SetValue(ClickCommandBehaviorProperty, behavior);
            }

            return behavior;
        }
    }
and here is the code for the CommandBehaviorBase

public class CommandBehaviorBase<T> where T : UIElement
    {
        private ICommand command;
        private object commandParameter;
        private readonly WeakReference targetObject;
        private readonly EventHandler commandCanExecuteChangedHandler;
        /// <summary>
        /// Constructor specifying the target object.
        /// </summary>
        /// <param name=”targetObject”>The target object the behavior is attached to.</param>
        public CommandBehaviorBase(T targetObject)
        {
            this.targetObject = new WeakReference(targetObject);

            // In Silverlight, unlike WPF, this is strictly not necessary since the Command properties
            // in Silverlight do not expect their CanExecuteChanged handlers to be weakly held,
            // but holding on to them in this manner should do no harm.
            this.commandCanExecuteChangedHandler = new EventHandler(this.CommandCanExecuteChanged);
        }

        /// <summary>
        /// Corresponding command to be execute and monitored for <see cref=”ICommand.CanExecuteChanged”/>
        /// </summary>
        public ICommand Command
        {
            get { return command; }
            set
            {
                if (this.command != null)
                {
                    this.command.CanExecuteChanged -= this.commandCanExecuteChangedHandler;
                }

                this.command = value;
                if (this.command != null)
                {
                    this.command.CanExecuteChanged += this.commandCanExecuteChangedHandler;
                    UpdateEnabledState();
                }
            }
        }

        /// <summary>
        /// The parameter to supply the command during execution
        /// </summary>
        public object CommandParameter
        {
            get { return this.commandParameter; }
            set
            {
                if (this.commandParameter != value)
                {
                    this.commandParameter = value;
                    this.UpdateEnabledState();
                }
            }
        }

        /// <summary>
        /// Object to which this behavior is attached.
        /// </summary>
        protected T TargetObject
        {
            get
            {
                return targetObject.Target as T;
            }
        }
        /// <summary>
        /// Updates the target object’s IsEnabled property based on the commands ability to execute.
        /// </summary>
        protected virtual void UpdateEnabledState()
        {
            if (TargetObject == null)
            {
                this.Command = null;
                this.CommandParameter = null;
            }
            else if (this.Command != null)
            {
                TargetObject.IsHitTestVisible = this.Command.CanExecute(this.CommandParameter);
            }
        }

        private void CommandCanExecuteChanged(object sender, EventArgs e)
        {
            this.UpdateEnabledState();
        }

        /// <summary>
        /// Executes the command, if it’s set, providing the <see cref=”CommandParameter”/>
        /// </summary>
        protected virtual void ExecuteCommand()
        {
            if (this.Command != null)
            {
                this.Command.Execute(this.CommandParameter);
            }
        }
    }

 

 

Advertisements

6 thoughts on “Prism 4 – Silverlight and Custom Commands

  1. Fantastic! I’ve been looking for a jack-of-all-trades behaviour / command class for non-button ui elements … this works great.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s