MVVM 在视图中调用方法(通过 ViewModel 从 ControlTemplate)

发布于 2024-10-02 10:36:56 字数 392 浏览 0 评论 0原文

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

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

我在我的视图中创建了一个属性,它可以调用该方法,但我无法使用触发器为该属性设置新值,因为我无法告诉设置器将控件模板外部的属性作为目标。

所以本质上我想更新视图模型中的属性并触发视图类中的设置属性。或者,如果您有任何想法如何解决这个问题:我愿意接受新想法! :D

问候 戈佩

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 which 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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

茶色山野 2024-10-09 10:36:56

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

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

糖果控 2024-10-09 10:36:56

我找到了一个可以接受的解决方案:我将 CallMethodAction 移植到 3.5 并编写了自己的 PropertyChangedTrigger。通过视图模型中的 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 等旧版本的人


戈佩
戈佩

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文