更好的PropertyChanged和PropertyChanging事件处理

use*_*225 13 .net c# c#-4.0

我正在为我们的应用程序实现观察者模式 - 目前正在使用RX Framework.

我目前有一个看起来像这样的例子:

Observable.FromEventPattern<PropertyChangedEventArgs>(Instance.Address, "PropertyChanged")
    .Where(e => e.EventArgs.PropertyName == "City")
    .ObserveOn(Scheduler.ThreadPool)
    .Subscribe(search => OnNewSearch(search.EventArgs));
Run Code Online (Sandbox Code Playgroud)

(我有一个类似的"PropertyChanging")

EventArgs不会给我太多.我想要的是EventArgs的扩展,它使我能够查看先前和新值,以及在"更改"侦听器中标记事件的能力,以便更改实际上不会持续存在.如何才能做到这一点?谢谢.

Dr.*_*ice 24

我认为这取决于您如何实现INotifyPropertyChanging和INotifyPropertyChanged接口.

遗憾的是,PropertyChangingEventArgs和PropertyChangedEventArgs类不提供属性的前后值或取消更改的能力,但您可以派生自己的事件args类来提供该功能.

首先,定义以下事件args类.请注意,这些派生自PropertyChangingEventArgs类和PropertyChangedEventArgs类.这允许我们将这些对象作为参数传递给PropertyChangingEventHandler和PropertyChangedEventHandler委托.

class PropertyChangingCancelEventArgs : PropertyChangingEventArgs
{
    public bool Cancel { get; set; }

    public PropertyChangingCancelEventArgs(string propertyName)
        : base(propertyName)
    {
    }
}

class PropertyChangingCancelEventArgs<T> : PropertyChangingCancelEventArgs
{
    public T OriginalValue { get; private set; }

    public T NewValue { get; private set; }

    public PropertyChangingCancelEventArgs(string propertyName, T originalValue, T newValue)
        : base(propertyName)
    {
        this.OriginalValue = originalValue;
        this.NewValue = newValue;
    }
}

class PropertyChangedEventArgs<T> : PropertyChangedEventArgs
{
    public T PreviousValue { get; private set; }

    public T CurrentValue { get; private set; }

    public PropertyChangedEventArgs(string propertyName, T previousValue, T currentValue)
        : base(propertyName)
    {
        this.PreviousValue = previousValue;
        this.CurrentValue = currentValue;
    }
}
Run Code Online (Sandbox Code Playgroud)

接下来,您需要在INotifyPropertyChanging和INotifyPropertyChanged接口的实现中使用这些类.实现的示例如下:

class Example : INotifyPropertyChanging, INotifyPropertyChanged
{
    public event PropertyChangingEventHandler PropertyChanging;

    public event PropertyChangedEventHandler PropertyChanged;

    protected bool OnPropertyChanging<T>(string propertyName, T originalValue, T newValue)
    {
        var handler = this.PropertyChanging;
        if (handler != null)
        {
            var args = new PropertyChangingCancelEventArgs<T>(propertyName, originalValue, newValue);
            handler(this, args);
            return !args.Cancel;
        }
        return true;
    }

    protected void OnPropertyChanged<T>(string propertyName, T previousValue, T currentValue)
    {
        var handler = this.PropertyChanged;
        if (handler != null)
            handler(this, new PropertyChangedEventArgs<T>(propertyName, previousValue, currentValue));
    }

    int _ExampleValue;

    public int ExampleValue
    {
        get { return _ExampleValue; }
        set
        {
            if (_ExampleValue != value)
            {
                if (this.OnPropertyChanging("ExampleValue", _ExampleValue, value))
                {
                    var previousValue = _ExampleValue;
                    _ExampleValue = value;
                    this.OnPropertyChanged("ExampleValue", previousValue, value);
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

注意,PropertyChanging和PropertyChanged事件的事件处理程序仍然需要将原始的PropertyChangingEventArgs类和PropertyChangedEventArgs类作为参数,而不是更具体的版本.但是,您可以将事件args对象强制转换为更具体的类型,以便访问新属性.

以下是这些事件的事件处理程序示例:

class Program
{
    static void Main(string[] args)
    {
        var exampleObject = new Example();

        exampleObject.PropertyChanging += new PropertyChangingEventHandler(exampleObject_PropertyChanging);
        exampleObject.PropertyChanged += new PropertyChangedEventHandler(exampleObject_PropertyChanged);

        exampleObject.ExampleValue = 123;
        exampleObject.ExampleValue = 100;
    }

    static void exampleObject_PropertyChanging(object sender, PropertyChangingEventArgs e)
    {
        if (e.PropertyName == "ExampleValue")
        {
            int originalValue = ((PropertyChangingCancelEventArgs<int>)e).OriginalValue;
            int newValue = ((PropertyChangingCancelEventArgs<int>)e).NewValue;

            // do not allow the property to be changed if the new value is less than the original value
            if(newValue < originalValue)
                ((PropertyChangingCancelEventArgs)e).Cancel = true;
        }

    }

    static void exampleObject_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "ExampleValue")
        {
            int previousValue = ((PropertyChangedEventArgs<int>)e).PreviousValue;
            int currentValue = ((PropertyChangedEventArgs<int>)e).CurrentValue;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)