InvokeCommandAction传递多个参数
命名空间:
xmlns:i=“http://schemas.microsoft.com/xaml/behaviors”
一般写法,一般只能传递一个参数
<i:Interaction.Triggers>
<i:EventTrigger EventName="LostFocus">
<i:InvokeCommandAction Command="{Binding DataContext.LostFocusCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" PassEventArgsToCommand="True" />
</i:EventTrigger>
<i:EventTrigger EventName="GotFocus">
<i:InvokeCommandAction Command="{Binding DataContext.GotFocusCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" PassEventArgsToCommand="True" />
</i:EventTrigger>
<!--CommandParameter="itemGrid",这种后台参数为itemGrid-->
<i:EventTrigger EventName="MouseEnter">
<i:InvokeCommandAction Command="{Binding DataContext.MouseEnterCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
CommandParameter="itemGrid"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
解析Command
Command="{Binding DataContext.LostFocusCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
Binding DataContext.LostFocusCommand——》DataContext,View对应的ViewModel (这里用的是Prism框架)
传递的是 ViewModel 的 DelegateCommand
Command="{Binding DataContext.LostFocusCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
Binding RelativeSource
RelativeSource={RelativeSource AncestorType={x:Type UserControl}} 指向 源对象
RelativeSource首先要3个关键参数
AncestorType=需要查找的类型。比如Grid
AncestorLevel= 需要向上查找几级。
Path=我们找到的元素需要绑定的属性。
<!--嵌套Grid-->
<Grid x:Name="G0" Margin="12" Background="Red">
<TextBlock Text="In this Grid0 container"/>
<Grid x:Name="G1" Margin="12" Background="Blue">
<TextBlock Text="In this Grid1 container"/>
<Grid x:Name="G2" Margin="12" Background="Yellow">
<TextBlock Text="In this Grid2 container"/>
<Grid x:Name="G3" Margin="12" Background="Beige">
<StackPanel>
<TextBlock Text="In this Grid3 container"/>
<!--AncestorType=我们需要查找的类型。比如Grid-->
<!--AncestorLevel= 我们需要向上查找几级-->
<!--Path=我们找到的元素需要绑定的属性。-->
<TextBlock Name="ces" Text="{Binding RelativeSource={RelativeSource AncestorType=Grid,AncestorLevel=1},Path=Name}"/>
</StackPanel>
</Grid>
</Grid>
</Grid>
</Grid>
我们嵌套几个Grid,并在每个嵌套的Grid中都放入了一行文本用来显示自己所在的位置。
设置了Margin使他有部分的重叠,可以更好的看到相互之间的层级关系。
最内层使用一个TextBlock.在TextBlock的Text属性上使用RelativeSource。
通过修改AncestorLevel 来设置向上查找Grid的等级。
我们设置为1.向外层查找第一个找到的Grid对象。并绑定对应的Name。
传递两个或多个参数
方式一
定义一个转换器
namespace 命名空间
{
/// <summary>
/// CommandParameter 多参数传递
/// </summary>
public class ObjectConvert : IMultiValueConverter
{
#region IMultiValueConverter Members
public static object ConverterObject;
public object Convert(object[] values, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
ConverterObject = values;
string str = values.GetType().ToString();
return values.ToArray();
}
public object[] ConvertBack(object value, Type[] targetTypes,
object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion IMultiValueConverter Members
}
}
缺点,无法传递 EventArgs
<i:EventTrigger EventName="MouseEnter">
<i:InvokeCommandAction Command="{Binding DataContext.MouseEnterCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" PassEventArgsToCommand="True">
<i:InvokeCommandAction.CommandParameter>
<MultiBinding Converter="{StaticResource ResourceKey=ObjectConvert}" Mode="TwoWay">
<MultiBinding.Bindings>
<Binding ElementName="Grid" />
<Binding ElementName="TxtTitleInput" />
</MultiBinding.Bindings>
</MultiBinding>
</i:InvokeCommandAction.CommandParameter>
</i:InvokeCommandAction>
</i:EventTrigger>
方式二
传递两个
<i:EventTrigger EventName="MouseDown">
<helper:AdvancedInvokeCommandAction
Command="{Binding DataContext.MouseClickCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
CommandParameter="{Binding ElementName=itemGrid}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
重写Invoke
namespace 命名空间
{
public class CompositeCommandParameter
{
public CompositeCommandParameter(EventArgs eventArgs, object parameter)
{
EventArgs = eventArgs;
Parameter = parameter;
}
public EventArgs EventArgs { get; }
public object Parameter { get; }
}
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;
}
}
}
传递多个参数
定义一个转换器
namespace 命名空间
{
/// <summary>
/// CommandParameter 多参数传递
/// </summary>
public class ObjectConvert : IMultiValueConverter
{
#region IMultiValueConverter Members
public static object ConverterObject;
public object Convert(object[] values, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
ConverterObject = values;
string str = values.GetType().ToString();
return values.ToArray();
}
public object[] ConvertBack(object value, Type[] targetTypes,
object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion IMultiValueConverter Members
}
}
结合传递两个参数的方法
<i:EventTrigger EventName="MouseEnter">
<helper:AdvancedInvokeCommandAction Command="{Binding DataContext.MouseEnterCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" PassEventArgsToCommand="True">
<helper:AdvancedInvokeCommandAction.CommandParameter>
<MultiBinding Converter="{StaticResource ResourceKey=ObjectConvert}" Mode="TwoWay">
<MultiBinding.Bindings>
<Binding ElementName="itemGrid" />
<Binding ElementName="TextBlockTitle" />
</MultiBinding.Bindings>
</MultiBinding>
</helper:AdvancedInvokeCommandAction.CommandParameter>
</helper:AdvancedInvokeCommandAction>
<!--<i:InvokeCommandAction Command="{Binding DataContext.MouseEnterCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" PassEventArgsToCommand="True" />-->
</i:EventTrigger>