I saw the light Silverlight Commands Hacks: Passing EventArgs as CommandParameter to DelegateCommand triggered by EventTrigger - Alexey Zakharov's Blog

Silverlight Commands Hacks: Passing EventArgs as CommandParameter to DelegateCommand triggered by EventTrigger

Today I've tried to find a way how to pass EventArgs as CommandParameter to DelegateCommand triggered by EventTrigger. By reverse engineering of default InvokeCommandAction I find that blend team just ignores event args.

To resolve this issue I have created my own action for triggering delegate commands.

public sealed class InvokeDelegateCommandAction : TriggerAction<DependencyObject>
{
    /// <summary>
    ///
    /// </summary>
    public static readonly DependencyProperty CommandParameterProperty =
        DependencyProperty.Register("CommandParameter", typeof(object), typeof(InvokeDelegateCommandAction), null);

    /// <summary>
    ///
    /// </summary>
    public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(
        "Command", typeof(ICommand), typeof(InvokeDelegateCommandAction), null);

    /// <summary>
    ///
    /// </summary>
    public static readonly DependencyProperty InvokeParameterProperty = DependencyProperty.Register(
        "InvokeParameter", typeof(object), typeof(InvokeDelegateCommandAction), null);

    private string commandName;

    /// <summary>
    ///
    /// </summary>
    public object InvokeParameter
    {
        get
        {
            return this.GetValue(InvokeParameterProperty);
        }
        set
        {
            this.SetValue(InvokeParameterProperty, value);
        }
    }

    /// <summary>
    ///
    /// </summary>
    public ICommand Command
    {
        get
        {
            return (ICommand)this.GetValue(CommandProperty);
        }
        set
        {
            this.SetValue(CommandProperty, value);
        }
    }

    /// <summary>
    ///
    /// </summary>
    public string CommandName
    {
        get
        {
            return this.commandName;
        }
        set
        {
            if (this.CommandName != value)
            {
                this.commandName = value;
            }
        }
    }

    /// <summary>
    ///
    /// </summary>
    public object CommandParameter
    {
        get
        {
            return this.GetValue(CommandParameterProperty);
        }
        set
        {
            this.SetValue(CommandParameterProperty, value);
        }
    }

    /// <summary>
    ///
    /// </summary>
    /// <param name="parameter"></param>
    protected override void Invoke(object parameter)
    {
        this.InvokeParameter = parameter;
       
        if (this.AssociatedObject != null)
        {
            ICommand command = this.ResolveCommand();
            if ((command != null) && command.CanExecute(this.CommandParameter))
            {
                command.Execute(this.CommandParameter);
            }
        }
    }

    private ICommand ResolveCommand()
    {
        ICommand command = null;
        if (this.Command != null)
        {
            return this.Command;
        }
        var frameworkElement = this.AssociatedObject as FrameworkElement;
        if (frameworkElement != null)
        {
            object dataContext = frameworkElement.DataContext;
            if (dataContext != null)
            {
                PropertyInfo commandPropertyInfo = dataContext
                    .GetType()
                    .GetProperties(BindingFlags.Public | BindingFlags.Instance)
                    .FirstOrDefault(
                        p =>
                        typeof(ICommand).IsAssignableFrom(p.PropertyType) &&
                        string.Equals(p.Name, this.CommandName, StringComparison.Ordinal)
                    );

                if (commandPropertyInfo != null)
                {
                    command = (ICommand)commandPropertyInfo.GetValue(dataContext, null);
                }
            }
        }
        return command;
    }
}

Example:

<ComboBox>
    <ComboBoxItem Content="Foo option 1" />
    <ComboBoxItem Content="Foo option 2" />
    <ComboBoxItem Content="Foo option 3" />
    <Interactivity:Interaction.Triggers>
        <Interactivity:EventTrigger EventName="SelectionChanged" >
            <Presentation:InvokeDelegateCommandAction
                Command="{Binding SubmitFormCommand}"
                CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=InvokeParameter}" />
        </Interactivity:EventTrigger>
    </Interactivity:Interaction.Triggers>               
</ComboBox>

BTW: InvokeCommanAction CommandName property are trying to find command in properties of view. It very strange, because in MVVM pattern command should be in viewmodel supplied to datacontext.

Published Wednesday, March 24, 2010 12:19 PM by brainbox
Filed under: , , , ,

Comments

# re: Silverlight Commands Hacks: Passing EventArgs as CommandParameter to DelegateCommand triggered by EventTrigger

Thursday, June 17, 2010 12:29 AM by VB

Thanks a lot, I was looking for something like this, because I was having some trouble using  InvokeCommandAction with slider:valuechanged event and command-parameter binded to slider.value property. Whenever slider is moved, ICommand function was called in ViewModel properly but problem was command-parameter value was "previous" value not current slider value. This similar thing happens with TextBox control as well.

# re: Silverlight Commands Hacks: Passing EventArgs as CommandParameter to DelegateCommand triggered by EventTrigger

Friday, July 16, 2010 4:19 PM by Nat

Did you ever figure out why the Value is previous when used with Slider? I am seeing the same problem...

Kudos on the article as it was easy to follow and get it working.

# re: Silverlight Commands Hacks: Passing EventArgs as CommandParameter to DelegateCommand triggered by EventTrigger

Tuesday, September 21, 2010 2:23 PM by alphapapalima

I am erroring on :

PropertyInfo commandPropertyInfo = dataContext

                       .GetType()

                       .GetProperties(BindingFlags.Public | BindingFlags.Instance)

                       .FirstOrDefault(

.FirstorDefault seems to be erroring.  I have included System.Reflection.PropertyInfo.  Is this correct?

# re: Silverlight Commands Hacks: Passing EventArgs as CommandParameter to DelegateCommand triggered by EventTrigger

Tuesday, September 21, 2010 2:35 PM by alphapapalima

disregard, I need to include system.linq also.

# re: Silverlight Commands Hacks: Passing EventArgs as CommandParameter to DelegateCommand triggered by EventTrigger

Tuesday, September 21, 2010 3:17 PM by тарас ковтун

Алекс, статья хорошая но ты не мог бы форматировать код попонятней ? а то невозможно читать, в глазах рябит.

# re: Silverlight Commands Hacks: Passing EventArgs as CommandParameter to DelegateCommand triggered by EventTrigger

Wednesday, June 1, 2011 9:10 AM by phuppe

Hi,

Can you post sample code for your command "SubmitFormCommand" that you have in your example?

Thanks,

Paul

# re: Silverlight Commands Hacks: Passing EventArgs as CommandParameter to DelegateCommand triggered by EventTrigger

Wednesday, June 1, 2011 9:19 AM by phuppe

To be more specific, could you show sample code in the view model where you would declare the command "SubmitFormCommand", set it, and show the method it would call for example.

# re: Silverlight Commands Hacks: Passing EventArgs as CommandParameter to DelegateCommand triggered by EventTrigger

Monday, June 20, 2011 9:47 AM by tigerswithguitars

To be more specific, could you show sample code in the view model where you would declare the command "SubmitFormCommand", set it, and show the method it would call for example. +1

I have wired up this command framework, but my command is always triggering with a NULL object sent back? ViewModel, DelegateCommand problems methinks. But a full example/project/implementation would make a good article amazing!

# re: Silverlight Commands Hacks: Passing EventArgs as CommandParameter to DelegateCommand triggered by EventTrigger

Monday, October 31, 2011 10:28 AM by frosty

For anybody looking to see what the code is for the Command in the ViewModel.  If you are using MVVM Light's RelayCommand.

You want to pass in the EventArg of the event you are using to trigger the command.  If you aren't sure what this is, then use object, set a break point to figure out what it is.

private RelayCommand<object> _regionTreeViewIsCheckedCommand;

       public RelayCommand<object> RegionTreeViewIsCheckedCommand

       {

           get

           {

               return _regionTreeViewIsCheckedCommand ?? (_regionTreeViewIsCheckedCommand = new RelayCommand<object>((val) =>

               {

                   Debug.WriteLine(val.GetType().ToString();

               }));

           }

       }