IWeakEventListener.ReceiveWeakEvent() 在源对象上调用 PropertyChanged(null) 时调用多次

IWeakEventListener.ReceiveWeakEvent() called multiple times when PropertyChanged(null) is called on source object

我正在使用 PropertyObserver class in my code to avoid doing string comparisons in PropertyChanged event handling and factor out the handling of null or string.Empty as its argument (Which indicates 对象的所有属性都已更改)。

此 class 使用 PropertyChangedEventManager 在目标对象中注册回调并实现 IWeakEventListener 以响应每次在源对象上调用 PropertyChanged 事件。

但是在创建单元测试的过程中我发现IWeakEventListener.ReceiveWeakEvent()被调用了N次,N是注册回调的次数。这仅在指定 null 或 string.Empty 时发生,而不是在 PropertyChanged 事件中给出有效的 属性 名称时发生。

有谁知道为什么会这样以及如何解决?我的目标是在给定 null 时对已注册的处理程序执行一次 foreach,这样我就可以通过获取源对象的所有属性来更新我的目标对象。但是当 ReceiveWeakEvent() 被调用 N 次时,foreach 将被重复 N 次!

为了说明这一点,以下是 PropertyObserver class 和来源 class 的简化版本(我使用 MVVM Light 的 ObservableObject 实现 INotifyPropertyChanged ):

public class PropertyObserver : IWeakEventListener {
    public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e) {
        if (managerType == typeof(PropertyChangedEventManager)) {
            string propertyName = ((PropertyChangedEventArgs)e).PropertyName;

            if (string.IsNullOrEmpty(propertyName)) {
                Console.WriteLine ("Foreach registered handlers and invoke one by one");
            } else {
                Console.WriteLine ("Invoke handler for property {0}", propertyName);
            }
            return true;
        }
        return false;
    }
}

public class ViewModel : ObservableObject {
    private int mProp1;
    private int mProp2;

    public int Prop1 {
        get { return mProp1; }
        set {
            mProp1 = value;
            RaisePropertyChanged("Prop1");
        }
    }

    public int Prop2 {
        get { return mProp2; }
        set {
            mProp2 = value;
            RaisePropertyChanged("Prop2");
        }
    }

    public void RaiseAllPropertyChanged() {
        RaisePropertyChanged(null);
    }
}

在控制台应用程序的 Main 中,我们可以这样称呼它们:

var vm = new ViewModel();
var obs = new PropertyObserver();

// Normally this is done inside the PropertyObserver class.
PropertyChangedEventManager.AddListener(vm, obs, "Prop1");
PropertyChangedEventManager.AddListener(vm, obs, "Prop2");

vm.Prop1 = 1; // Results in a console line "Invoke handler for property Prop1"
vm.Prop2 = 2; // Results in a console line "Invoke handler for property Prop2"

// Results in two console lines: "Foreach registered handlers and invoke one by one", expected is only 1!
vm.RaiseAllPropertyChanged();

好吧,之前没看懂AddListener()这个方法。我只需要注册一次监听器:

PropertyChangedEventManager.AddListener(vm, obs, string.Empty);

侦听源对象的所有 PropertyChanged 事件。这样做将产生 PropertyObserver class:

的正确工作
vm.Prop1 = 1;    // "Invoke handler for property Prop1"
vm.Prop2 = 2;    // "Invoke handler for property Prop2"

// Now results in one console line "Foreach registered handlers and invoke one by one"
vm.RaisePropertyChanged();

每个具有非空第三个参数(属性 名称)的注册侦听器将仅响应指定的 属性 名称 nullstring.Empty。所以这就是为什么在原始代码上调用了两次 foreach 的原因。