Spring AOP + MVVM 基础 + PropertyChanged [英] Spring AOP + MVVM Foundation + PropertyChanged

查看:27
本文介绍了Spring AOP + MVVM 基础 + PropertyChanged的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用 Spring.Net 1.3.1 和 MVVM Foundation 将横切应用到我的视图模型.我注意到,如果在对象转换为横切代理之前分配属性更改处理程序,则代理引擎不会将属性更改处理程序应用于代理.有谁知道这是否是预期行为,如果是,是否有解决方法?

I'm using Spring.Net 1.3.1 alongside MVVM Foundation to apply cross-cutting to my viewmodels. I've noticed that if I assign a property changed handler before the object is converted to a proxy for cross-cutting that the proxy engine does not apply the property changed handler to the proxy. Does anyone know if this is expected behavior and if so, if there is a workaround?

我的工厂是这样的

public static class AopProxyFactory {
    public static object GetProxy(object target) {
        var factory = new ProxyFactory(target);

        factory.AddAdvisor(new Spring.Aop.Support.DefaultPointcutAdvisor(
                                new AttributeMatchMethodPointcut(typeof(AttributeStoringMethod)),
                                new UnitValidationBeforeAdvice())
                           );

        factory.AddAdvice(new NotifyPropertyChangedAdvice());
        factory.ProxyTargetType = true;

        return factory.GetProxy();
    }
}

建议看起来像这样

    public class UnitValidationBeforeAdvice : IMethodBeforeAdvice {
    public UnitValidationBeforeAdvice() {            
    }

    public void Before(MethodInfo method, object[] args, object target) {
        if (args.Length != 1) {
            throw new ArgumentException("The args collection is not valid!");
        }

        var canConvertTo = true;
        if (!canConvertTo) {
            throw new ArgumentException("The '{0}' cannot be converted.");
        }
    }
}

public class NotifyPropertyChangedAdvice : IAfterReturningAdvice, INotifyPropertyChanged {
    public event PropertyChangedEventHandler PropertyChanged;

    public void AfterReturning(object ReturnValue, MethodInfo Method, object[] Args, object Target) {
        if (Method.Name.StartsWith("set_")) {
            RaisePropertyChanged(Target, Method.Name.Substring("set_".Length));
        }
    }

    private void RaisePropertyChanged(Object Target, String PropertyName) {
        if (PropertyChanged != null)
            PropertyChanged(Target, new PropertyChangedEventArgs(PropertyName));
    }
}

我代理的对象看起来像这样

The object I'm proxying look like this

    public class ProxyTypeObject : ObservableObject {
    private string whoCaresItsBroke;
    public string WhoCaresItsBroke {
        get { return whoCaresItsBroke; }
        set {
            whoCaresItsBroke = value;
            RaisePropertyChanged("WhoCaresItsBroke");
        }
    }
}

和调用代码

var pto = new ProxyTypeObject();
                pto.WhoCaresItsBroke = "BooHoo";
                pto.PropertyChanged += (object sender, System.ComponentModel.PropertyChangedEventArgs e) => {
                    return;
                };

                var proxy = AopProxyFactory.GetProxy(pto);
                (proxy as ProxyTypeObject).WhoCaresItsBroke = "BooHoo2";

您会注意到,当我设置WhoCaresItsBroke"属性时,我之前连接的属性更改处理程序从未被命中.(我尝试使用 spring.net 论坛中提供的 NotifyPropertyChangedAdvice,但这似乎不起作用.)

You will notice that when I set the "WhoCaresItsBroke" property the property changed handler I previously hooked up is never hit. (I tried using the NotifyPropertyChangedAdvice as provided in the spring.net forums but that does not appear to work.)

推荐答案

你应该将 WhoCaresItsBroke 属性声明为虚拟的,否则它不会被你的代理对象覆盖.将其设为虚拟会导致 pto 上的处理程序再次被调用,因为代理会将属性调用委托给其目标.

You should declare the WhoCaresItsBroke property as virtual, otherwise it will not be overridden by your proxy object. Making it virtual will cause your handler on pto to be called again, because the proxy will delegate the property call to its target.

您不需要NotifyPropertyChangedAdvice,您可以将其删除.您正在使用的 ObservableObject 类已经实现了所需的行为.

You do not need the NotifyPropertyChangedAdvice, you can remove it. The desired behavior is already implemented by the ObservableObject class you're using.

如果您希望在触发目标 PropertyChanged 事件时在代理上触发 PropertyChanged 事件,则应按照以下 hack 中的建议手动实现.

If you want the PropertyChanged event to be fired on the proxy when the target PropertyChanged event is fired, you should implement this manually, as suggested in the following hack.

在代理目标

proxyfactory 不会将目标事件连接到代理上的类似事件,但您可以手动执行此操作.我不确定我是否会建议您这样做,但您可以使用以下技巧.

A proxyfactory does not wire target events to similar event on a proxy, but you could do this manually. I'm not sure if I would advice you to do so, but you could use the following hack.

重写你的代理工厂和ProxyTypeObject:

public class ProxyTypeObject : ObservableObject
{
    private string whoCaresItsBroke;
    // step 1:
    // make the property virtual, otherwise it will not be overridden by the proxy
    public virtual string WhoCaresItsBroke
    {
      // original implementation
    }

    public void PublicRaisePropertyChanged(string name)
    {
        RaisePropertyChanged(name);
    }
}

public static class AopProxyFactory
{
    public static object GetProxy(object target)
    {
        ProxyFactory factory = GetFactory(target);

        object proxy = factory.GetProxy();

        if(target is ProxyTypeObject)
        {
            // step 2:
            // hack: hook handlers ...
            var targetAsProxyTypeObject = (ProxyTypeObject)target;
            var proxyAsProxyTypeObject = (ProxyTypeObject)proxy;
            HookHandlers(targetAsProxyTypeObject, proxyAsProxyTypeObject);
        }

        return proxy;

    }

    private static void HookHandlers(ProxyTypeObject target, ProxyTypeObject proxy)
    {
        target.PropertyChanged += (sender, e) =>
        {
            proxy.PublicRaisePropertyChanged(e.PropertyName);
        };
    }

    private static ProxyFactory GetFactory(object target)
    {
        var factory = new ProxyFactory(target);
        // I simply add the advice here, but you could useyour original
        //  factory.AddAdvisor( ... )
        factory.AddAdvice(new UnitValidationBeforeAdvice());
        // You don't need this:
        // factory.AddAdvice(new NotifyPropertyChangedAdvice()); 
        factory.ProxyTargetType = true;
        return factory;
    }
}

这要求 ProxyTypeObject 有一个公开可见的方法来引发 PropertyChangedEvent;你可能应该用不同的方式来做这件事,但这不是重点.

This requires ProxyTypeObject to have a publicly visible method to raise a PropertyChangedEvent; you probably should do this differently, but that's besides the point.

工作原理

工厂返回类型为 ProxyTypeObject 的代理,因为您已设置 factory.ProxyTargetType = true;.尽管如此,它仍然是一个基于组合的代理:代理后,您将拥有原始对象(目标)新的代理对象.代理和目标都是 ProxyTypeObject 类型,可以引发 PropertyChanged 事件.

The factory returns a proxy of type ProxyTypeObject, because you have set factory.ProxyTargetType = true;. It is still a composition based proxy though: after proxying you will have the original object (the target) and the new proxy object. Both proxy and target are of type ProxyTypeObject and can raise a PropertyChanged event.

在此阶段,当在代理上设置 WhoCaresItsBroke 时,PropertyChanged 事件将在您的代理上触发,但不会在目标上触发.目标属性未更改.

At this stage, when setting WhoCaresItsBroke on the proxy, the PropertyChanged event will fire on your proxy, but not on the target. The target property is not changed.

第一步:将属性声明为virtual

step 1: declare property as virtual

因为我们已经将属性 WhoCaresItsBroke 设为虚拟的,它可以在代理中被覆盖.在被覆盖的属性中,代理对象将所有对 WhoCaresItsBroke 属性的 WhoCaresItsBroke 调用委托给目标.

Because we've made the property WhoCaresItsBroke virtual, it can be overridden in the proxy. In the overridden property, the proxy object delegates all WhoCaresItsBroke calls to the WhoCaresItsBroke property to the target.

在这一步之后,您将看到您附加到您的 pto 实例的原始处理程序被调用.但是,不会引发代理上的 PropertyChanged 事件.

After this step, you'll see that the original handler you attached to your pto instance is called. However, the PropertyChanged event on the proxy is not raised.

第 2 步:将目标事件挂接到代理上的处理程序

step 2: hook target event to a handler on proxy

只需将目标 PropertyChanged 事件挂接到代理上的处理程序,该处理程序引发其自己的 PropertyChanged 事件.我们可以使用相同的名称,因为在代理中我们可以假设我们是相同的类型.

Simply hook the target PropertyChanged event to a handler on the proxy that raises its own PropertyChanged event. We can use the same name because in the proxy we can assume we're of the same type.

这篇关于Spring AOP + MVVM 基础 + PropertyChanged的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆