1

我一直在对现有应用程序进行重构,并且尝试在属性上使用 Attribute 来使用 Unity 拦截触发 NotifyPropertyChanged 事件。我到了事件触发的地步,但控件没有更新。

我不确定它是否正确调用了事件,所以在我的 ViewModelBase 上我创建了一个调用属性更改事件的 DispatchPropertyChanged 方法。此方法用于在直接从视图模型调用时启动属性更改,但是当从通过拦截处理程序内的反射检索的视图模型调用时,它不起作用。

我插入了指向https://www.dropbox.com/s/9qg2n0gd2n62elc/WPFUnityTest.zip的链接。如果您打开此解决方案并运行应用程序,然后单击“重置”按钮,您将看到“正常”文本框更新,但“Unity”文本框没有更新。

如果在 MainWindowViewModel 的第 65 行和 NotifyPropertyChangedHandler 的第 53 行放置断点,您将看到处理程序正在工作,正在调用调度方法,并且正在调用事件。但是,只有“正常”一个更新。

关于为什么“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);
    }
}
4

1 回答 1

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;
}

我已经测试了这段代码并且它有效。希望这可以帮助!

于 2013-11-21T22:32:19.830 回答