使用WPF和INotifyPropertyChanged进行Unity拦截

时间:2013-11-21 19:49:16

标签: c# wpf unity-container inotifypropertychanged interception

我一直在做现有应用程序的重构,我试图使用属性上的属性来使用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);
    }
}

1 个答案:

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

我已经测试了这段代码,但它确实有用。希望这有帮助!