开发者

MVVM Invoke Method in View (from ControlTemplate via ViewModel)

开发者 https://www.devze.com 2023-01-25 10:59 出处:网络
I would like to know how the following problem can be solved WITHOUT using Event Aggregation. This is for WPF 3.5 SP1, so the CallMethodBehavior is not available.

I would like to know how the following problem can be solved WITHOUT using Event Aggregation. This is for WPF 3.5 SP1, so the CallMethodBehavior is not available.

Simple Scenario: A click on a button inside a ControlTemplate needs to be triggered to the VM. I used CaliburnMicro's ActionMessage whi开发者_Python百科ch worked fine. Inside the ViewModel I want to trigger a method inside the View, which only starts a custom transition (no real logic). I tried many things, but I did not work out.

I created a Property in my view, which could call the method but I am not able to use Triggers to set a new value for the property, because I can't tell the setter to target a property outside the controltemplate.

So in essence I want to update a Property in the viewmodel and trigger a set-property in the view class. Or if you have any idea how to get around this at all: I am open to new ideas! :D

Regards Gope


i think the most simple way is to expose an event from your vm and subscribe to it in your view? i used this for dialogs to send DialogResult from vm


I found a solution I can live with: I ported the CallMethodAction to 3.5 and wrote my own PropertyChangedTrigger. It's pretty simple to call a method inside the view via a PropertyChange in the viewmodel - Kids: don't try this at home. It's only for special scenarios! :D

Find my code below:

usage:

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;
            }
        }
    }
}

Hope this helps anybode. All questions are welcome! Remeber: all this can be found in the Expression Blend SDK 4. This code is only for people who are forced to work with older versions like 3.5

Regards Gope Gope

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号