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
事件,则应按照以下 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屋!