2016-02-12 226 views
0

考慮以下幾點:是否可以使用CallMethodAction與參數進行交互?

<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只能用於調用任一不帶參數或有兩個參數的方法,其中第一個參數是對象類型和的方法第二個可以被分配給一個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猜測)。如果這不合適,那麼您需要根據需要調整綁定。

+0

我會試試這個。我將不得不做另一個可以調用Remove的方法......但是我可能找到了另一種使用我尚未測試過的數據綁定的更好方法......但這似乎與我想要的最接近。 – Will

+0

我的第一個選擇將不起作用。但是,我敢肯定,這將是。我想創建另一個帳戶只是爲了再次提出這個答案。 – Will

1

您可以嘗試使用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> 
+0

是的,我已經看到過,我不打算使用它。 – Will

相關問題