Spring AOP + MVVM基础+ PropertyChanged [英] Spring AOP + MVVM Foundation + 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
事件,则应按照以下技巧中的建议手动执行此操作.
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.
要在代理和目标上触发PropertyChanged
的黑客或替代方法
The hack or workaround to fire PropertyChanged
on proxy and the target
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.
步骤1:将属性声明为虚拟
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屋!