是否可以使用带有参数的 CallMethodAction 交互?
Posted
技术标签:
【中文标题】是否可以使用带有参数的 CallMethodAction 交互?【英文标题】:Is it possible to use the CallMethodAction interaction with an argument? 【发布时间】:2016-02-12 15:06:58 【问题描述】:鉴于以下情况:
<Storyboard x:Key="Foo" AutoReverse="True" RepeatBehavior="3x">
<Storyboard.Children/>
</Storyboard>
<DoubleAnimationUsingKeyFrames x:Key = "Bar"/>
<ei:DataTrigger
Binding="
Binding SomeVar,
ElementName=SomeElement,
FallbackValue=False,
Mode=OneWay"
Value="True">
<ei:CallMethodAction
TargetObject="
Binding Mode=OneWay,
Path=Children,
Source=StaticResource Foo"
MethodName="Add"/>
</ei:DataTrigger>
有什么方法可以将Bar
作为参数传递给方法调用Children.Add
?
【问题讨论】:
【参考方案1】:CallMethodAction
只能用于调用不带参数的方法或具有两个参数的方法,其中第一个参数是 object 类型,第二个参数可以分配给 EventArgs 类型的变量。
鉴于此,您将无法使用CallMethodAction
做您想做的事。但是,您可以创建自己的触发操作,该操作将调用您的方法并传入您指定的值。我只对此做了一些简单的测试,但它应该非常接近您的需要。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Interactivity;
namespace LocalActions
public class CallUnaryMethodAction : TargetedTriggerAction<DependencyObject>
// The name of the method to invoke.
public static readonly DependencyProperty MethodNameProperty =
DependencyProperty.Register( "MethodName",
typeof( string ),
typeof( CallUnaryMethodAction ),
new PropertyMetadata( OnNeedsMethodInfoUpdated ) );
public string MethodName
get return (string)GetValue( MethodNameProperty );
set SetValue( MethodNameProperty, value );
// Flag that lets us determine if we want to search non-public methods in our target object.
public static readonly DependencyProperty AllowNonPublicMethodsProperty =
DependencyProperty.Register( "AllowNonPublicMethods",
typeof( bool ),
typeof( CallUnaryMethodAction ),
new PropertyMetadata( OnNeedsMethodInfoUpdated ) );
public bool AllowNonPublicMethods
get return (bool)GetValue( AllowNonPublicMethodsProperty );
set SetValue( AllowNonPublicMethodsProperty, value );
// Parameter we want to pass to our method. If this has not been set, then the value passed
// to the trigger action's Invoke method will be used instead.
public static readonly DependencyProperty ParameterProperty =
DependencyProperty.Register( "Parameter",
typeof( object ),
typeof( CallUnaryMethodAction ) );
public object Parameter
get return GetValue( ParameterProperty );
set SetValue( ParameterProperty, value );
private static void OnNeedsMethodInfoUpdated( DependencyObject d, DependencyPropertyChangedEventArgs e )
var action = d as CallUnaryMethodAction;
if( action != null )
action.UpdateMethodInfo();
protected override void OnAttached()
UpdateMethodInfo();
protected override void OnTargetChanged( DependencyObject oldTarget, DependencyObject newTarget )
UpdateMethodInfo();
protected override void Invoke( object parameter )
object target = this.TargetObject ?? this.AssociatedObject;
if( target == null )
return;
// Determine what we are going to pass to our method.
object methodParam = ReadLocalValue( ParameterProperty ) == DependencyProperty.UnsetValue ?
parameter : this.Parameter;
// Pick the best method to call given the parameter we want to pass.
Method methodToCall = m_methods.FirstOrDefault( method =>
(methodParam != null) && method.ParameterInfo.ParameterType.IsAssignableFrom( methodParam.GetType() ) );
if( methodToCall == null )
throw new InvalidOperationException( "No suitable method found." );
methodToCall.MethodInfo.Invoke( target, new object[] methodParam );
private void UpdateMethodInfo()
m_methods.Clear();
object target = this.TargetObject ?? this.AssociatedObject;
if( target == null || string.IsNullOrEmpty( this.MethodName ) )
return;
// Find all unary methods with the given name.
BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
if( this.AllowNonPublicMethods )
flags |= BindingFlags.NonPublic;
foreach( MethodInfo methodInfo in target.GetType().GetMethods( flags ) )
if( methodInfo.Name == this.MethodName )
ParameterInfo[] parameters = methodInfo.GetParameters();
if( parameters.Length == 1 )
m_methods.Add( new Method( methodInfo, parameters[0] ) );
// Order the methods so that methods with most derived parameters are ordered first.
// This will help us pick the most appropriate method in the call to Invoke.
m_methods = m_methods.OrderByDescending<Method, int>( method =>
int rank = 0;
for( Type type = method.ParameterInfo.ParameterType; type != typeof( object ); type = type.BaseType )
++rank;
return rank;
).ToList<Method>();
private List<Method> m_methods = new List<Method>();
// Holds info on the list of possible methods we can call.
private class Method
public Method( MethodInfo methodInfo, ParameterInfo paramInfo )
this.MethodInfo = methodInfo;
this.ParameterInfo = paramInfo;
public MethodInfo MethodInfo get; private set;
public ParameterInfo ParameterInfo get; private set;
然后,您可以在 XAML 中使用它,就像通常用于 CallMethodAction
一样。您只需要引入适当的 XAML 命名空间。
...
xmlns:local="clr-namespace:LocalActions"
...
<ei:DataTrigger
Binding="
Binding SomeVar,
ElementName=SomeElement,
FallbackValue=False,
Mode=OneWay"
Value="True">
<local:CallUnaryMethodAction
TargetObject="
Binding Mode=OneWay,
Path=Children,
Source=StaticResource Foo"
MethodName="Add"
Parameter="StaticResource Bar"/>
</ei:DataTrigger>
假设您的 DoubleAnimationUsingKeyFrames
确实是一种资源(我根据您对 x:Key
的使用猜测)。如果这不合适,那么您需要根据需要调整绑定。
【讨论】:
我会试试这个。我将不得不制作另一个它也可以调用 Remove 的方法......但我可能已经找到了另一种使用我尚未测试过的数据绑定的更好方法......但这似乎最接近我想要的。跨度> 我的第一选择行不通。但这会的,我敢肯定。我会创建另一个帐户只是为了再次支持这个答案。【参考方案2】:您可以尝试使用 Interactivity 中的 InvokeCommandAction
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
<ei:DataTrigger
Binding="
Binding SomeVar,
ElementName=SomeElement,
FallbackValue=False,
Mode=OneWay"
Value="True">
<i:InvokeCommandAction Command="Binding SomeCommand, Source=StaticResource SomeViewModel" CommandParameter="Bar"/>
</ei:DataTrigger>
【讨论】:
是的,我以前见过,我不会使用它。以上是关于是否可以使用带有参数的 CallMethodAction 交互?的主要内容,如果未能解决你的问题,请参考以下文章