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.

9 Comments

  • 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.

  • 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.

  • 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?

  • disregard, I need to include system.linq also.

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

  • Hi,
    Can you post sample code for your command "SubmitFormCommand" that you have in your example?
    Thanks,
    Paul

  • 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.

  • 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!

  • 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 _regionTreeViewIsCheckedCommand;

    public RelayCommand RegionTreeViewIsCheckedCommand
    {
    get
    {
    return _regionTreeViewIsCheckedCommand ?? (_regionTreeViewIsCheckedCommand = new RelayCommand((val) =>
    {
    Debug.WriteLine(val.GetType().ToString();
    }));
    }


    }

Comments have been disabled for this content.