简体   繁体   English

使用 InvokeCommandAction 将额外参数传递给命令

[英]Pass extra argument to command with InvokeCommandAction

Is there a way to pass an extra argument ( alongside the default argument) to a command with InvokeCommandAction from Microsoft.Xaml.Behaviors.Wpf ?有没有办法通过Microsoft.Xaml.Behaviors.WpfInvokeCommandAction将额外参数(默认参数一起)传递给命令?

Like the following:如下所示:

<behaviors:Interaction.Triggers>
    <behaviors:EventTrigger EventName="MouseDown">
        <behaviors:InvokeCommandAction Command="{Binding Command, Mode=OneWay}" PassEventArgsToCommand="True" />
    </behaviors:EventTrigger>
</behaviors:Interaction.Triggers>

Here the passed argument is MouseButtonEventArgs :这里传递的参数是MouseButtonEventArgs

<behaviors:Interaction.Triggers>
    <behaviors:EventTrigger EventName="MouseDown">
        <behaviors:InvokeCommandAction Command="{Binding Command, Mode=OneWay}" PassEventArgsToCommand="True">
            <behaviors:InvokeCommandAction.CommandParameter>
                <MultiBinding Converter="{StaticResource ResourceKey=CommandConverter}">
                    <Binding ElementName="OtherElement" Mode="OneWay" />
                </MultiBinding>
            </behaviors:InvokeCommandAction.CommandParameter>
        </behaviors:InvokeCommandAction>
    </behaviors:EventTrigger>
</behaviors:Interaction.Triggers>

And here I want to pass the OtherElement and MouseButtonEventArgs together.在这里,我想将OtherElementMouseButtonEventArgs一起传递。 Is there a way to specify the MouseButtonEventArgs argument?有没有办法指定MouseButtonEventArgs参数?

The InvokeCommandAction supports exactly one CommandParameter , which is either the event arguments or the bound command parameter. InvokeCommandAction支持一个CommandParameter ,即事件 arguments 或绑定命令参数。 If you try to do both, the command parameter will take precedence .如果您尝试同时执行这两种操作,则命令参数将优先 Since the XAML behaviors are open source, you can see it yourself in the Invoke method of the class on Github.由于 XAML 行为是开源的,您可以在 Github 上的 class 的Invoke方法中自己查看。

In order to achieve passing both, you will have to write your own action.为了实现两者都通过,您必须编写自己的操作。 This would be an easy task, if you could simply create a derived type of InvokeCommandAction and override Invoke , but unfortunately it is sealed .如果您可以简单地创建InvokeCommandAction的派生类型并覆盖Invoke ,这将是一件容易的事,但不幸的是它是sealed That means, you have to copy the code for InvokeCommandAction and adapt it.这意味着,您必须复制InvokeCommandAction的代码并对其进行调整。

To start off, create a small class that encapsulates both the event arguments and the command parameter.首先,创建一个封装事件 arguments 和命令参数的小型 class。

public class CompositeCommandParameter
{
   public CompositeCommandParameter(EventArgs eventArgs, object parameter)
   {
      EventArgs = eventArgs;
      Parameter = parameter;
   }

   public EventArgs EventArgs { get; }

   public object Parameter { get; }
}

Next, copy the code from GitHub .接下来, 从 GitHub 复制代码 In essence, you have to replace explicit references to the InvokeCommandAction type with your custom type, here AdvancedInvokeCommandAction and of course adapt the Invoke method so that it creates a CompositeCommandParameter instance and calls the command with it.本质上,您必须用您的自定义类型替换对InvokeCommandAction类型的显式引用,这里是AdvancedInvokeCommandAction ,当然还要调整Invoke方法,以便它创建一个CompositeCommandParameter实例并使用它调用命令。

public sealed class AdvancedInvokeCommandAction : TriggerAction<DependencyObject>
{
   private string commandName;

   public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(AdvancedInvokeCommandAction), null);
   public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register("CommandParameter", typeof(object), typeof(AdvancedInvokeCommandAction), null);
   public static readonly DependencyProperty EventArgsConverterProperty = DependencyProperty.Register("EventArgsConverter", typeof(IValueConverter), typeof(AdvancedInvokeCommandAction), new PropertyMetadata(null));
   public static readonly DependencyProperty EventArgsConverterParameterProperty = DependencyProperty.Register("EventArgsConverterParameter", typeof(object), typeof(AdvancedInvokeCommandAction), new PropertyMetadata(null));
   public static readonly DependencyProperty EventArgsParameterPathProperty = DependencyProperty.Register("EventArgsParameterPath", typeof(string), typeof(AdvancedInvokeCommandAction), new PropertyMetadata(null));

   // ...other code.

   public object CommandParameter
   {
      get { return this.GetValue(AdvancedInvokeCommandAction.CommandParameterProperty); }
      set { this.SetValue(AdvancedInvokeCommandAction.CommandParameterProperty, value); }
   }

   // ...other code.

   protected override void Invoke(object parameter)
   {
      if (this.AssociatedObject != null)
      {
         ICommand command = this.ResolveCommand();

         if (command != null)
         {
            object eventArgs = null;
            object commandParameter = this.CommandParameter;

            //if no CommandParameter has been provided, let's check the EventArgsParameterPath
            if (!string.IsNullOrWhiteSpace(this.EventArgsParameterPath))
            {
               eventArgs = GetEventArgsPropertyPathValue(parameter);
            }

            //next let's see if an event args converter has been supplied
            if (eventArgs == null && this.EventArgsConverter != null)
            {
               eventArgs = this.EventArgsConverter.Convert(parameter, typeof(object), EventArgsConverterParameter, CultureInfo.CurrentCulture);
            }

            //last resort, let see if they want to force the event args to be passed as a parameter
            if (eventArgs == null && this.PassEventArgsToCommand)
            {
               eventArgs = parameter;
            }

            if (command.CanExecute(commandParameter))
            {
               var compositeCommandParameter = new CompositeCommandParameter((EventArgs) eventArgs, commandParameter);
               command.Execute(compositeCommandParameter);
            }
         }
      }
   }
   
   // ...other code.
}

In your XAML code, you can now use both.在您的 XAML 代码中,您现在可以同时使用两者。 Since you will most probably use this action only with both parameters, you could further customize the action to remove the PassEventArgsToCommand parameter.由于您很可能只将此操作与这两个参数一起使用,因此您可以进一步自定义操作以删除PassEventArgsToCommand参数。

<b:Interaction.Triggers>
   <b:EventTrigger EventName="MouseDown">
      <local:AdvancedInvokeCommandAction Command="{Binding Command, Mode=OneWay}"
                                         PassEventArgsToCommand="True"
                                         CommandParameter="{Binding ElementName=OtherElement}" />
   </b:EventTrigger>
</b:Interaction.Triggers>

In your view model, the command will now get an object of type CompositeCommandParameter .在您的视图 model 中,该命令现在将获得一个CompositeCommandParameter类型的 object 。


Here is the complete code for the AdvancedInvokeCommandAction .这是AdvancedInvokeCommandAction的完整代码。

public sealed class AdvancedInvokeCommandAction : TriggerAction<DependencyObject>
{
   private string commandName;

   public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(AdvancedInvokeCommandAction), null);
   public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register("CommandParameter", typeof(object), typeof(AdvancedInvokeCommandAction), null);
   public static readonly DependencyProperty EventArgsConverterProperty = DependencyProperty.Register("EventArgsConverter", typeof(IValueConverter), typeof(AdvancedInvokeCommandAction), new PropertyMetadata(null));
   public static readonly DependencyProperty EventArgsConverterParameterProperty = DependencyProperty.Register("EventArgsConverterParameter", typeof(object), typeof(AdvancedInvokeCommandAction), new PropertyMetadata(null));
   public static readonly DependencyProperty EventArgsParameterPathProperty = DependencyProperty.Register("EventArgsParameterPath", typeof(string), typeof(AdvancedInvokeCommandAction), new PropertyMetadata(null));

   /// <summary>
   /// Gets or sets the name of the command this action should invoke.
   /// </summary>
   /// <value>The name of the command this action should invoke.</value>
   /// <remarks>This property will be superseded by the Command property if both are set.</remarks>
   public string CommandName
   {
      get
      {
         this.ReadPreamble();
         return this.commandName;
      }
      set
      {
         if (this.CommandName != value)
         {
            this.WritePreamble();
            this.commandName = value;
            this.WritePostscript();
         }
      }
   }

   /// <summary>
   /// Gets or sets the command this action should invoke. This is a dependency property.
   /// </summary>
   /// <value>The command to execute.</value>
   /// <remarks>This property will take precedence over the CommandName property if both are set.</remarks>
   public ICommand Command
   {
      get { return (ICommand)this.GetValue(CommandProperty); }
      set { this.SetValue(CommandProperty, value); }
   }

   /// <summary>
   /// Gets or sets the command parameter. This is a dependency property.
   /// </summary>
   /// <value>The command parameter.</value>
   /// <remarks>This is the value passed to ICommand.CanExecute and ICommand.Execute.</remarks>
   public object CommandParameter
   {
      get { return this.GetValue(AdvancedInvokeCommandAction.CommandParameterProperty); }
      set { this.SetValue(AdvancedInvokeCommandAction.CommandParameterProperty, value); }
   }

   /// <summary>
   /// Gets or sets the IValueConverter that is used to convert the EventArgs passed to the Command as a parameter.
   /// </summary>
   /// <remarks>If the <see cref="Command"/> or <see cref="EventArgsParameterPath"/> properties are set, this property is ignored.</remarks>
   public IValueConverter EventArgsConverter
   {
      get { return (IValueConverter)GetValue(EventArgsConverterProperty); }
      set { SetValue(EventArgsConverterProperty, value); }
   }

   /// <summary>
   /// Gets or sets the parameter that is passed to the EventArgsConverter.
   /// </summary>
   public object EventArgsConverterParameter
   {
      get { return (object)GetValue(EventArgsConverterParameterProperty); }
      set { SetValue(EventArgsConverterParameterProperty, value); }
   }

   /// <summary>
   /// Gets or sets the parameter path used to extract a value from an <see cref= "EventArgs" /> property to pass to the Command as a parameter.
   /// </summary>
   /// <remarks>If the <see cref="Command"/> propert is set, this property is ignored.</remarks>
   public string EventArgsParameterPath
   {
      get { return (string)GetValue(EventArgsParameterPathProperty); }
      set { SetValue(EventArgsParameterPathProperty, value); }
   }

   /// <summary>
   /// Specifies whether the EventArgs of the event that triggered this action should be passed to the Command as a parameter.
   /// </summary>
   /// <remarks>If the <see cref="Command"/>, <see cref="EventArgsParameterPath"/>, or <see cref="EventArgsConverter"/> properties are set, this property is ignored.</remarks>
   public bool PassEventArgsToCommand { get; set; }

   /// <summary>
   /// Invokes the action.
   /// </summary>
   /// <param name="parameter">The parameter to the action. If the action does not require a parameter, the parameter may be set to a null reference.</param>
   protected override void Invoke(object parameter)
   {
      if (this.AssociatedObject != null)
      {
         ICommand command = this.ResolveCommand();

         if (command != null)
         {
            object eventArgs = null;
            object commandParameter = this.CommandParameter;

            //if no CommandParameter has been provided, let's check the EventArgsParameterPath
            if (!string.IsNullOrWhiteSpace(this.EventArgsParameterPath))
            {
               eventArgs = GetEventArgsPropertyPathValue(parameter);
            }

            //next let's see if an event args converter has been supplied
            if (eventArgs == null && this.EventArgsConverter != null)
            {
               eventArgs = this.EventArgsConverter.Convert(parameter, typeof(object), EventArgsConverterParameter, CultureInfo.CurrentCulture);
            }

            //last resort, let see if they want to force the event args to be passed as a parameter
            if (eventArgs == null && this.PassEventArgsToCommand)
            {
               eventArgs = parameter;
            }

            if (command.CanExecute(commandParameter))
            {
               var compositeCommandParameter = new CompositeCommandParameter((EventArgs) eventArgs, commandParameter);
               command.Execute(compositeCommandParameter);
            }
         }
      }
   }

   private object GetEventArgsPropertyPathValue(object parameter)
   {
      object commandParameter;
      object propertyValue = parameter;
      string[] propertyPathParts = EventArgsParameterPath.Split('.');
      foreach (string propertyPathPart in propertyPathParts)
      {
         PropertyInfo propInfo = propertyValue.GetType().GetProperty(propertyPathPart);
         propertyValue = propInfo.GetValue(propertyValue, null);
      }

      commandParameter = propertyValue;
      return commandParameter;
   }

   private ICommand ResolveCommand()
   {
      ICommand command = null;

      if (this.Command != null)
      {
         command = this.Command;
      }
      else if (this.AssociatedObject != null)
      {
         // todo jekelly 06/09/08: we could potentially cache some or all of this information if needed, updating when AssociatedObject changes
         Type associatedObjectType = this.AssociatedObject.GetType();
         PropertyInfo[] typeProperties = associatedObjectType.GetProperties(BindingFlags.Public | BindingFlags.Instance);

         foreach (PropertyInfo propertyInfo in typeProperties)
         {
            if (typeof(ICommand).IsAssignableFrom(propertyInfo.PropertyType))
            {
               if (string.Equals(propertyInfo.Name, this.CommandName, StringComparison.Ordinal))
               {
                  command = (ICommand)propertyInfo.GetValue(this.AssociatedObject, null);
               }
            }
         }
      }

      return command;
   }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使用MVVM在WPF应用程序中的InvokeCommandAction中将多个参数作为CommandParameter传递 - How to pass Multiple parameters as CommandParameter in InvokeCommandAction In WPF App Using MVVM 如何在Windows Phone的InvokeCommandAction中将多个参数作为CommandParameter传递 - How to pass Multiple parameters as CommandParameter in InvokeCommandAction in Windows phone InvokeCommandAction CommmandParameter - InvokeCommandAction CommmandParameter 将命令行参数传递给Beyond Compare - Pass Command Line argument to Beyond Compare 在命令行中传递参数并获取完整路径 - Pass Argument in Command line and get full path 将 JSON 字符串作为命令行参数传递 - Pass a JSON string as a command line argument 如何使用InvokeCommandAction调用我的方法并传入参数? - How do I go about using InvokeCommandAction to call a method of mine and pass in parameters? (如何)你能否在C#中将空格作为命令行参数传递? - (How) can you pass whitespace as a command line argument in C#? 如何使用C#在Python中将字符串作为命令行参数传递 - How to pass string as command line argument in python using C# 如何使用触发器将事件参数传递给命令? - How can I pass the event argument to a command using triggers?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM