视图中的MVVM调用方法(来自ControlTemplate通过ViewModel)

时间:2010-11-13 14:28:32

标签: wpf mvvm controltemplate

我想知道如何在不使用事件聚合的情况下解决以下问题。这适用于WPF 3.5 SP1,因此CallMethodBehavior不可用。

简单场景:需要在VM上触发ControlTemplate内部按钮的单击。我使用了CaliburnMicro的ActionMessage,效果很好。在ViewModel中我想在View中触发一个方法,它只启动一个自定义转换(没有真正的逻辑)。我尝试了很多东西,但我没有成功。

我在视图中创建了一个属性,它可以调用该方法但我无法使用触发器为属性设置新值,因为我无法告诉setter定位controltemplate之外的属性。 / p>

所以本质上我想更新viewmodel中的Property并在view类中触发set-property。或者,如果你有任何想法如何解决这个问题:我对新想法持开放态度! :d

此致 Gope

2 个答案:

答案 0 :(得分:1)

我认为最简单的方法是从您的虚拟机中公开一个事件并在您的视图中订阅它? 我使用this对话框从vm发送DialogResult

答案 1 :(得分:0)

我找到了一个可以忍受的解决方案:我将CallMethodAction移植到3.5并编写了我自己的PropertyChangedTrigger。通过viewmodel中的PropertyChange调用视图中的方法非常简单 - 孩子:不要在家里尝试这个。这只适用于特殊场景! :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;
            }
        }
    }
}

希望这有助于任何人。欢迎所有问题!记住:所有这些都可以在Expression Blend SDK 4中找到。此代码仅适用于被迫使用旧版本(如3.5)的人

此致 Gope Gope