我一直在做现有应用程序的重构,我试图使用属性上的属性来使用Unity拦截来触发NotifyPropertyChanged事件。我到了事件发生的地步,但控件没有更新。
我不确定它是否正确调用了事件,因此在我的ViewModelBase上创建了一个调用属性更改事件的DispatchPropertyChanged方法。此方法用于在从视图模型直接调用时启动已更改的属性,但是当从通过拦截处理程序内部的反射检索的ViewModel调用时,它不起作用。
我已插入https://www.dropbox.com/s/9qg2n0gd2n62elc/WPFUnityTest.zip的链接。如果您打开此解决方案并运行该应用程序,然后单击“重置”按钮,您将看到“正常”文本框更新,但“Unity”文本框不会更新。
如果在MainWindowViewModel的第65行和NotifyPropertyChangedHandler的第53行放置断点,您将看到处理程序正在运行,正在调用dispatch方法,并且正在调用该事件。但是,只有“正常”更新。
任何有关“Unity”文本框未更新的原因的帮助都很棒,谢谢!
阿曼达
编辑:
很抱歉原来不包括这个,我真的不知道问题出在哪里。这是下面正确的拦截行为的原始代码:
public class NotifyPropertyChangedHandler : IInterceptionBehavior
{
public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
{
var npcAttribute = getNotifyPropertyChangedAttributeForSetter(input) as NotifyPropertyChangedAttribute;
if (npcAttribute != null)
{
if (npcAttribute.TimingOption == PropertyChangedTiming.Always||
shouldRaiseEvent(input))
{
raiseEvent(input);
}
}
return getNext()(input, getNext);
}
public IEnumerable<Type> GetRequiredInterfaces()
{
return new[] { typeof(INotifyPropertyChanged) };
}
public bool WillExecute { get { return true; } }
private object getNotifyPropertyChangedAttributeForSetter(IMethodInvocation input)
{
if (!input.MethodBase.Name.StartsWith("set_"))
{
return null;
}
return input.MethodBase.ReflectedType.GetProperty(input.MethodBase.Name.Substring(4))
.GetCustomAttributes(true).SingleOrDefault(attr => attr.GetType() == typeof (NotifyPropertyChangedAttribute));
}
private void raiseEvent(IMethodInvocation input)
{
raiseEvent(input, new PropertyChangedEventArgs(input.MethodBase.Name.Substring(4)));
}
private void raiseEvent(IMethodInvocation input, PropertyChangedEventArgs eventArgs)
{
var viewModel = input.Target as ViewModelBase;
if (viewModel != null)
{
viewModel.DispatchPropertyChangedEvent(eventArgs.PropertyName);
}
}
private static bool shouldRaiseEvent(IMethodInvocation input)
{
var methodBase = input.MethodBase;
if (!methodBase.IsSpecialName || !methodBase.Name.StartsWith("set_"))
{
return false;
}
var propertyName = methodBase.Name.Substring(4);
var property = methodBase.ReflectedType.GetProperty(propertyName);
var getMethod = property.GetGetMethod();
if (getMethod == null)
{
return false;
}
var oldValue = getMethod.Invoke(input.Target, null);
var value = input.Arguments[0];
return (value == null) ? oldValue !=null :
!value.Equals(oldValue);
}
}
答案 0 :(得分:3)
您的NotifyPropertyChangedHandler.Invoke
方法代码存在问题:
您的代码
我添加了一些注释来描述此代码的问题。
public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
{
var npcAttribute = getNotifyPropertyChangedAttributeForSetter(input) as NotifyPropertyChangedAttribute;
if (npcAttribute != null)
{
if (npcAttribute.TimingOption == PropertyChangedTiming.Always||
shouldRaiseEvent(input))
{
// You are raising property changed event here,
// however the value of the property is not changed until getNext()(input, getNext) called
// So, WPF will re-read the same "old" value and you don't see anything updated on the screen
raiseEvent(input);
}
}
// Property value is updated here!!!
return getNext()(input, getNext);
}
将该代码更改为:
public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
{
var temp = false;
var npcAttribute = getNotifyPropertyChangedAttributeForSetter(input) as NotifyPropertyChangedAttribute;
if (npcAttribute != null)
{
if (npcAttribute.TimingOption == PropertyChangedTiming.Always||
shouldRaiseEvent(input))
{
temp = true;
}
}
var returnValue = getNext()(input, getNext); // Changing the value here
if (temp) raiseEvent(input); // Raising property changed event, if necessary
return returnValue;
}
我已经测试了这段代码,但它确实有用。希望这有帮助!