2010-11-13 3 views
0

Я хотел бы знать, как можно решить следующую проблему БЕЗ использования агрегирования событий. Это для WPF 3.5 SP1, поэтому CallMethodBehavior недоступен.MVVM Invoke Method in View (из ControlTemplate через ViewModel)

Простой сценарий: щелчок по кнопке внутри элемента ControlTemplate должен запускаться на виртуальной машине. Я использовал ActionMessage CaliburnMicro, который отлично работал. Внутри ViewModel я хочу вызвать метод внутри представления, который запускает только пользовательский переход (без реальной логики). Я много пробовал, но я не работал.

Я создал свойство в своем представлении, которое может вызвать метод, но я не могу использовать триггеры, чтобы установить новое значение для свойства, потому что я не могу сказать, что установщик нацелился на свойство вне элемента управления.

Так что в сущности, я хочу обновить свойство в модели viewmodel и вызвать свойство set в классе представления. Или если у вас есть идея, как обойти это вообще: я открыт для новых идей! : D

С уважением Gope

ответ

0

Я нашел решение, которое я могу жить с: Я перенес CallMethodAction 3,5 и написал свой собственный PropertyChangedTrigger. Достаточно просто вызвать метод внутри представления через PropertyChange в viewmodel - Kids: не пытайтесь это делать дома. Это только для особых сценариев! : D

Найти мой код ниже:

использование:

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 

<i:Interaction.Triggers >   
    <Framework:PropertyChangedTrigger Binding="{Binding StartTransition}" Value="True"> 
     <Framework:CallMethodAction MethodName="ApplyTransition" /> 
    </Framework:PropertyChangedTrigger> 
</i:Interaction.Triggers> 

PropertyChangedTrigger:

public class PropertyChangedTrigger : TriggerBase<DependencyObject> 
{ 
    public static readonly DependencyProperty BindingProperty = DependencyProperty.Register("Binding", typeof(object), typeof(PropertyChangedTrigger), new PropertyMetadata(new PropertyChangedCallback(OnBindingChanged))); 
    public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(object), typeof(PropertyChangedTrigger), new PropertyMetadata(null)); 

    public object Binding 
    { 
     get 
     { 
      return base.GetValue(BindingProperty); 
     } 
     set 
     { 
      base.SetValue(BindingProperty, value); 
     } 
    } 

    public object Value 
    { 
     get 
     { 
      return base.GetValue(ValueProperty); 
     } 
     set 
     { 
      base.SetValue(ValueProperty, value); 
     } 
    } 

    protected virtual void EvaluateBindingChange(object args) 
    { 
     var propertyChangedArgs = (DependencyPropertyChangedEventArgs)args; 
     string newValue = propertyChangedArgs.NewValue.ToString(); 
     bool equal = string.Equals(newValue, Value.ToString(),StringComparison.InvariantCultureIgnoreCase); 
     if(equal) 
     { 
      InvokeActions(args); 
     } 
    } 

    private static void OnBindingChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) 
    { 
     ((PropertyChangedTrigger)sender).EvaluateBindingChange(args); 
    } 
} 

CallMethodAction:

public class CallMethodAction : TargetedTriggerAction<FrameworkElement> 
{ 
    private List<MethodDescriptor> methodDescriptors = new List<MethodDescriptor>(); 
    public static readonly DependencyProperty MethodNameProperty = DependencyProperty.Register("MethodName", typeof(string), typeof(CallMethodAction), new PropertyMetadata(new PropertyChangedCallback(OnMethodNameChanged))); 
    public static readonly DependencyProperty TargetObjectProperty = DependencyProperty.Register("TargetObject", typeof(object), typeof(CallMethodAction), new PropertyMetadata(new PropertyChangedCallback(OnTargetObjectChanged))); 

    protected override void OnAttached() 
    { 
     base.OnAttached(); 
     this.UpdateMethodInfo(); 
    } 

    protected override void OnDetaching() 
    { 
     this.methodDescriptors.Clear(); 
     base.OnDetaching(); 
    } 

    private static void OnMethodNameChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) 
    { 
     ((CallMethodAction)sender).UpdateMethodInfo(); 
    } 

    private static void OnTargetObjectChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) 
    { 
     ((CallMethodAction)sender).UpdateMethodInfo(); 
    } 

    private static bool AreMethodParamsValid(ParameterInfo[] methodParams) 
    { 
     if (methodParams.Length == 2) 
     { 
      if (methodParams[0].ParameterType != typeof(object)) 
      { 
       return false; 
      } 
      if (!typeof(EventArgs).IsAssignableFrom(methodParams[1].ParameterType)) 
      { 
       return false; 
      } 
     } 
     else if (methodParams.Length != 0) 
     { 
      return false; 
     } 
     return true; 
    } 

    protected override void Invoke(object parameter) 
    { 
     if (base.AssociatedObject != null) 
     { 
      MethodDescriptor descriptor = this.FindBestMethod(parameter); 
      if (descriptor != null) 
      { 
       ParameterInfo[] parameters = descriptor.Parameters; 
       if (parameters.Length == 0) 
       { 
        descriptor.MethodInfo.Invoke(this.Target, null); 
       } 
       else if ((((parameters.Length == 2) && (base.AssociatedObject != null)) && ((parameter != null) && parameters[0].ParameterType.IsAssignableFrom(base.AssociatedObject.GetType()))) && parameters[1].ParameterType.IsAssignableFrom(parameter.GetType())) 
       { 
        descriptor.MethodInfo.Invoke(this.Target, new object[] { base.AssociatedObject, parameter }); 
       } 
      } 
      else if (this.TargetObject != null) 
      { 
       throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "No valid method found.", new object[] { this.MethodName, this.TargetObject.GetType().Name })); 
      } 
     } 
    } 

    private MethodDescriptor FindBestMethod(object parameter) 
    { 
     if (parameter != null) 
     { 
      parameter.GetType(); 
     } 
     return this.methodDescriptors.FirstOrDefault(methodDescriptor => (!methodDescriptor.HasParameters || ((parameter != null) && methodDescriptor.SecondParameterType.IsAssignableFrom(parameter.GetType())))); 
    } 

    private void UpdateMethodInfo() 
    { 
     this.methodDescriptors.Clear(); 
     if ((this.Target != null) && !string.IsNullOrEmpty(this.MethodName)) 
     { 
      foreach (MethodInfo info in this.Target.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance)) 
      { 
       if (this.IsMethodValid(info)) 
       { 
        ParameterInfo[] parameters = info.GetParameters(); 
        if (AreMethodParamsValid(parameters)) 
        { 
         this.methodDescriptors.Add(new MethodDescriptor(info, parameters)); 
        } 
       } 
      } 
      this.methodDescriptors = this.methodDescriptors.OrderByDescending<MethodDescriptor, int>(delegate(MethodDescriptor methodDescriptor) 
      { 
       int num = 0; 
       if (methodDescriptor.HasParameters) 
       { 
        for (Type type = methodDescriptor.SecondParameterType; type != typeof(EventArgs); type = type.BaseType) 
        { 
         num++; 
        } 
       } 
       return (methodDescriptor.ParameterCount + num); 
      }).ToList<MethodDescriptor>(); 
     } 
    } 


    private bool IsMethodValid(MethodInfo method) 
    { 
     if (!string.Equals(method.Name, this.MethodName, StringComparison.Ordinal)) 
     { 
      return false; 
     } 
     if (method.ReturnType != typeof(void)) 
     { 
      return false; 
     } 
     return true; 
    } 

    public void InvokeInternal() 
    { 
     if (AssociatedObject != null) 
     { 
      foreach (
       MethodInfo info in AssociatedObject.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance)) 
      { 
       if (IsMethodValid(info)) 
       { 
        info.Invoke(AssociatedObject, new object[0]); 
       } 
      } 
     } 
    } 





    public string MethodName 
    { 
     get 
     { 
      return (string)base.GetValue(MethodNameProperty); 
     } 
     set 
     { 
      base.SetValue(MethodNameProperty, value); 
     } 
    } 

    private object Target 
    { 
     get 
     { 
      return (TargetObject ?? base.AssociatedObject); 
     } 
    } 

    public object TargetObject 
    { 
     get 
     { 
      return base.GetValue(TargetObjectProperty); 
     } 
     set 
     { 
      base.SetValue(TargetObjectProperty, value); 
     } 
    } 





    private class MethodDescriptor 
    { 
     public MethodDescriptor(MethodInfo methodInfo, ParameterInfo[] methodParams) 
     { 
      MethodInfo = methodInfo; 
      Parameters = methodParams; 
     } 

     public bool HasParameters 
     { 
      get 
      { 
       return (Parameters.Length > 0); 
      } 
     } 

     public MethodInfo MethodInfo { get; private set; } 

     public int ParameterCount 
     { 
      get 
      { 
       return Parameters.Length; 
      } 
     } 

     public ParameterInfo[] Parameters { get; private set; } 

     public Type SecondParameterType 
     { 
      get 
      { 
       if (Parameters.Length >= 2) 
       { 
        return Parameters[1].ParameterType; 
       } 
       return null; 
      } 
     } 
    } 
} 

Надеется, что это помогает anybode. Все вопросы приветствуются! Remeber: все это можно найти в Expression Blend SDK 4. Этот код предназначен только для людей, которые вынуждены работать с более старыми версиями, как 3,5

С уважением Gope Gope

+0

Здравствуйте, Gope, я хотел бы сделать тесты с помощью этого фрагмента кода. К сожалению, он не завершен. Методы FirtsOrDefault и OrderByDescending не определены в классе MethodDescriptor. Не могли бы вы добавить этот код? Редактировать: Упс, методDescriptor имеет тип List , поэтому эти методы поступают из списка . с использованием System.Linq отсутствовал. – 2011-05-26 12:16:11

1

я думаю, что самый простой способ, чтобы выставить событие из вашей виртуальной машины, и подписаться на него с вашей точки зрения? я использовал this для диалоговых окон для отправки DialogResult из виртуальной машины

+0

Действительно, это идея, хотя я постарайтесь свести код-позади к минимуму. То, что мне не нравится в этом, заключается в том, что у вас не должно быть никаких ссылок на viewmodel, то есть на листинг DataContext, который я тоже не люблю, но на самом деле ваше предложение будет работать и просто, что делает его очаровательным , : D Я нашел другое возможное решение, которое я размещаю ниже. С уважением, спасибо, что нашли время ответить на мой вопрос! – Gope

Смежные вопросы