About Command in Silverlight

Aug 16, 2009 at 3:59 AM

Hi,

I have a question in Command. As you know, in SL3, Prism only supply the button click event such as:

cal:Click.Command="{Binding Path=XXCommand}"

,but when I want to bind any control's event to a Command such as TextBlock's MouseLeftButtonUp event:

private void FlightPlanning_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)

, how to do the binding to it?

Aug 16, 2009 at 5:44 PM
Edited Aug 16, 2009 at 5:46 PM

I have created a snippet out of the PRISM command for use with any control and have the entire snippet embedded here for use. You would place this in your C# snippet area (My Documents\Visual Studio 2008\Code Snippets\Visual C#\My Code Snippets) and use it by slcmd. The snippet is a work in progress but gets you what you are looking for and all you need to do is name the class and the control type. You would create class file where you want it and then delete the actual class definition in that file to begin:

using System;
...

namespace MyNamespace.Commands
{
    slcmd
}

This will create you what you need for commanding any event on any control. For example you would create the Click class file and then enter Click for the class name and ButtonBase for the control to create the one PRISM supports by default.

UPDATE: BTW, the $commandname$CommandBehavior class that is created was templated from the Loaded event and so you will probably need to rewrite the methods when you create the command but it is a start.

HTH

<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <CodeSnippet Format="1.0.0">
    <Header>
      <SnippetTypes>
        <SnippetType>Expansion</SnippetType>
      </SnippetTypes>
      <Title>Silverlight Commanding Class Definition</Title>
      <Shortcut>slcmd</Shortcut>
      <Description>Create a Silverlight Command Class</Description>
      <Author>Fred Hirschfeld</Author>
    </Header>
    <Snippet>
      <Declarations>
        <Literal Editable="true">
          <ID>commandname</ID>
          <ToolTip>Name of the command to create.</ToolTip>
          <Default>MyCommand</Default>
          <Function>
          </Function>
        </Literal>
        <Literal Editable="true">
          <ID>controlType</ID>
          <ToolTip>The type of control this command applies to.</ToolTip>
          <Default>Control</Default>
          <Function>
          </Function>
        </Literal>
      </Declarations>
      <Code Language="csharp">
        <![CDATA[
public static class $commandname$
    {
        private static readonly DependencyProperty $commandname$CommandBehaviorProperty = DependencyProperty.RegisterAttached(
            "$commandname$CommandBehavior",
            typeof($commandname$CommandBehavior),
            typeof($commandname$),
            null);


        /// <summary>
        /// Command to execute on ?? event.
        /// </summary>
        public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached(
            "Command",
            typeof(ICommand),
            typeof($commandname$),
            new PropertyMetadata(OnSetCommandCallback));

        /// <summary>
        /// Command parameter to supply on command execution.
        /// </summary>
        public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.RegisterAttached(
            "CommandParameter",
            typeof(object),
            typeof($commandname$),
            new PropertyMetadata(OnSetCommandParameterCallback));


        /// <summary>
        /// Sets the <see cref="ICommand"/> to execute on the $$ event.
        /// </summary>
        /// <param name="element">$controlType$ dependency object to attach command</param>
        /// <param name="command">Command to attach</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Only works for buttonbase")]
        public static void SetCommand($controlType$ element, ICommand command)
        {
            element.SetValue(CommandProperty, command);
        }

        /// <summary>
        /// Retrieves the <see cref="ICommand"/> attached to the <see cref="ButtonBase"/>.
        /// </summary>
        /// <param name="element">$controlType$ containing the Command dependency property</param>
        /// <returns>The value of the command attached</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Only works for buttonbase")]
        public static ICommand GetCommand($controlType$ element)
        {
            return element.GetValue(CommandProperty) as ICommand;
        }

        /// <summary>
        /// Sets the value for the CommandParameter attached property on the provided <see cref="$controlType$"/>.
        /// </summary>
        /// <param name="element">$controlType$ to attach CommandParameter</param>
        /// <param name="parameter">Parameter value to attach</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Only works for buttonbase")]
        public static void SetCommandParameter($controlType$ element, object parameter)
        {
            element.SetValue(CommandParameterProperty, parameter);
        }

        /// <summary>
        /// Gets the value in CommandParameter attached property on the provided <see cref="$controlType$"/>
        /// </summary>
        /// <param name="element">$controlType$ that has the CommandParameter</param>
        /// <returns>The value of the property</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Only works for buttonbase")]
        public static object GetCommandParameter($controlType$ element)
        {
            return element.GetValue(CommandParameterProperty);
        }

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

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

        private static $commandname$CommandBehavior GetOrCreateBehavior($controlType$ element)
        {
            $commandname$CommandBehavior behavior = element.GetValue($commandname$CommandBehaviorProperty) as $commandname$CommandBehavior;
            if (behavior == null)
            {
                behavior = new $commandname$CommandBehavior(element);
                element.SetValue($commandname$CommandBehaviorProperty, behavior);
            }

            return behavior;
        }
    }

    public class $commandname$CommandBehavior : CommandBehaviorBase<$controlType$>
    {
        public $commandname$CommandBehavior($controlType$ element)
            : base(element)
        {
            // TODO: Hook into the event of the control
            $end$
        }

        private void Element_Loaded(object sender, RoutedEventArgs e)
        {
            // TODO: Setup the command parameter if appropriate.
            //this.CommandParameter = null;

            ExecuteCommand();
        }
    }]]>
      </Code>
    </Snippet>
  </CodeSnippet>
</CodeSnippets>
Aug 16, 2009 at 5:56 PM

Hi, fredhirschfeld,

Thank you for your reply. But maybe you misunderstand my problem.

I want to click on the TextBlock and fire its MouseLeftButtonUp event. Today I studied into how Button's click event in Prism and TextBox's KeyDown event in RI, I found they both inherit from base class Control, which apply for the Prism definition:

public class CommandBehaviorBase<T> where T: Control

, but TextBlock doesn't inherit from Control, so I find no way to implement Command mechinism on it.

Your code is fine, which is the same as I wrote before, so it's no use. I think the probelm can be summarize to: how to implement the Command on the controls that not implement from Control?

Aug 16, 2009 at 7:12 PM

You will need to create your own CommandBehaviorBase<T> class based on the original and remove that restriction.

Aug 17, 2009 at 1:21 AM

Does TextBlock must inherit from CommandBehaviorBase<T>?

I know someone write code like this:

    public class StoryboardCommandBehavior : ICommand, IProcessingCommand
    {
        public bool CanExecute(object parameter) { return true; }
        public event EventHandler CanExecuteChanged;
        public ICommand Command { get { return this; } }
        public ProcessingOrderEnum ProcessingOrder { get; set; }

        public StoryboardCommandBehavior()
        {
            ProcessingOrder = ProcessingOrderEnum.PreProcessing;
        }

        private void UpdateTargetName()
        {
            if (!String.IsNullOrEmpty(TargetName))
            {
                Storyboard.Stop();
                Storyboard.Children.ToList().ForEach(time => time.SetValue(Storyboard.TargetNameProperty, TargetName));
            }
        }

        public Storyboard Storyboard {get; set;}
        public string TargetName {get; set;}

        public void Execute(object parameter)
        {
            UpdateTargetName();
            Storyboard.Begin();
        }
    }

What's the difference? 

Aug 17, 2009 at 2:34 PM

Anyone else can help me for this issue?

Today I try to write a MouseLeftButtonUp behavior as follow, but failed, I don't know the reason:

    public static class MouseLeftButtonUp
    {
        private static readonly DependencyProperty MouseLeftButtonUpCommandBehaviorProperty = DependencyProperty.RegisterAttached(
            "MouseLeftButtonUpCommandBehavior",
            typeof(MouseLeftButtonUpCommandBehavior),
            typeof(MouseLeftButtonUp),
            null);

        /// <summary>
        /// Command to execute on MouseLeftButtonUp event.
        /// </summary>
        public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached(
            "Command",
            typeof(ICommand),
            typeof(MouseLeftButtonUp),
            new PropertyMetadata(OnSetCommandCallback));

        /// <summary>
        /// Command parameter to supply on command execution.
        /// </summary>
        public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.RegisterAttached(
            "CommandParameter",
            typeof(object),
            typeof(MouseLeftButtonUp),
            new PropertyMetadata(OnSetCommandParameterCallback));

        /// <summary>
        /// Sets the <see cref="ICommand"/> to execute on the MouseLeftButtonUp event.
        /// </summary>
        /// <param name="textBlock">TextBlock dependency object to attach command</param>
        /// <param name="command">Command to attach</param>
        public static void SetCommand(TextBlock textBlock, ICommand command)
        {
            textBlock.SetValue(CommandProperty, command);
        }

        /// <summary>
        /// Retrieves the <see cref="ICommand"/> attached to the <see cref="TextBlock"/>.
        /// </summary>
        /// <param name="textBlock">TextBlock containing the Command dependency property</param>
        /// <returns>The value of the command attached</returns>
        public static ICommand GetCommand(TextBlock textBlock)
        {
            return textBlock.GetValue(CommandProperty) as ICommand;
        }

        /// <summary>
        /// Sets the value for the CommandParameter attached property on the provided <see cref="TextBlock"/>.
        /// </summary>
        /// <param name="buttonBase">TextBlock to attach CommandParameter</param>
        /// <param name="parameter">Parameter value to attach</param>
        public static void SetCommandParameter(TextBlock textBlock, object parameter)
        {
            textBlock.SetValue(CommandParameterProperty, parameter);
        }

        /// <summary>
        /// Gets the value in CommandParameter attached property on the provided <see cref="ButtonBase"/>
        /// </summary>
        /// <param name="buttonBase">TextBlock that has the CommandParameter</param>
        /// <returns>The value of the property</returns>
        public static object GetCommandParameter(TextBlock textBlock)
        {
            return textBlock.GetValue(CommandParameterProperty);
        }

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

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

        private static MouseLeftButtonUpCommandBehavior GetOrCreateBehavior(TextBlock textBlock)
        {
            MouseLeftButtonUpCommandBehavior behavior = textBlock.GetValue(MouseLeftButtonUpCommandBehaviorProperty) as MouseLeftButtonUpCommandBehavior;
            if (behavior == null)
            {
                behavior = new MouseLeftButtonUpCommandBehavior(textBlock);
                textBlock.SetValue(MouseLeftButtonUpCommandBehaviorProperty, behavior);
            }

            return behavior;
        }
    }
    public class MouseLeftButtonUpCommandBehavior : CommandBehaviorBase<TextBlock>
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="ButtonBaseClickCommandBehavior"/> class and hooks up the Click event of 
        /// <paramref name="clickableObject"/> to the ExecuteCommand() method. 
        /// </summary>
        /// <param name="clickableObject">The clickable object.</param>
        public MouseLeftButtonUpCommandBehavior(TextBlock clickableObject)
            : base(clickableObject)
        {
            clickableObject.MouseLeftButtonUp += OnMouseLeftButtonUp;
        }

        private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            ExecuteCommand();
        }
    public class CommandBehaviorBase<T>
        where T : FrameworkElement
    {
        private ICommand command;
        private object commandParameter;
        private readonly WeakReference targetObject;


        /// <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);
        }

        /// <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.CommandCanExecuteChanged;
                }

                this.command = value;
                if (this.command != null)
                {
                    this.command.CanExecuteChanged += this.CommandCanExecuteChanged;
                    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.IsEnabled = 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);
            }
        }
    }