如何在C#的INotifyPropertyChanged实现中捕获旧值和新值的变化

7

我需要在PropertyChanged事件处理程序中捕获旧值、新值和属性名称。INotifyPropertyChanged的默认实现仅提供属性的名称。因此,在互联网上搜索后,我找到了类似的问题,其中包含了扩展实现的属性更改。

以下是该问题的链接和接口声明:

NotifyPropertyChanged事件,其中事件参数包含旧值

public interface INotifyPropertyChangedExtended<T>
{
    event PropertyChangedExtendedEventHandler<T> PropertyChanged;
}

public delegate void PropertyChangedExtendedEventHandler(object sender, PropertyChangedExtendedEventArgs e);

上述接口可以解决我的问题,但是我不知道如何在实体类上实现通用接口,因为T将根据属性的数据类型而改变。

有人能帮我理解吗?是否可能使用T参数实现此接口。示例实现将非常有帮助。

提前感谢您的帮助。

Umesh

编辑#1: 根据Peter的回复,发布更新后的代码,这可能对想要在PropertyChanged事件中捕获旧值和新值的人有用。

    public class PropertyChangedExtendedEventArgs : PropertyChangedEventArgs
{
    public string OldValue { get; set; }

    public string NewValue { get; set; }

    public PropertyChangedExtendedEventArgs(string propertyName, string oldValue, string newValue) : base(propertyName)
    {
        OldValue = oldValue;
        NewValue = newValue;
    }
}

    //
// Summary:
//     Notifies clients that a property value has changed.
public interface INotifyPropertyChangedEnhanced
{
    //
    // Summary:
    //     Occurs when a property value changes.
    event PropertyChangedEventHandlerEnhanced PropertyChanged;
}

public delegate void PropertyChangedEventHandlerEnhanced(object sender, PropertyChangedExtendedEventArgs e);

public abstract class BindableBase : INotifyPropertyChangedEnhanced
{
    public event PropertyChangedEventHandlerEnhanced PropertyChanged;

    protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] 
    string propertyName = null)
    {
        if (Equals(storage, value))
        {
            return false;
        }

        var oldValue = storage;
        storage = value;
        this.OnPropertyChanged(oldValue, value, propertyName);
        return true;
    }
    protected void OnPropertyChanged<T>(T oldValue, T newValue, [CallerMemberName] string propertyName = null)
    {
        this.PropertyChanged?.Invoke(this, new PropertyChangedExtendedEventArgs(propertyName, oldValue?.ToString(), newValue?.ToString()));
    }
}
4个回答

8

只需实现自己的PropertyChangedEventArgs即可。

不需要实现新接口。

这个解决方案可行。这是多态性的一个好处。

public class PropertyChangedExtendedEventArgs : PropertyChangedEventArgs
{
        public virtual object OldValue { get; private set; }
        public virtual object NewValue { get; private set; }

        public PropertyChangedExtendedEventArgs(string propertyName, object oldValue, 
               object newValue)
               : base(propertyName)
        {
            OldValue = oldValue;
            NewValue = newValue;
        }
    }

public class NotifyObject : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void NotifyPropertyChanged(string propertyName, object oldvalue, object newvalue)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedExtendedEventArgs(propertyName, oldvalue, newvalue));
        }
    }



public class Person : NotifyObject
    {
        private int _name;
        public int Name
        {
            get { return _name; }
            set
            {
                var oldValue = _name;
                _name = value;
                NotifyPropertyChanged("Name", oldValue, value);

            }
        }
    }

你的视图模型

Person person = new Person();
person.PropertyChanged += person_PropertyChanged;

  void person_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            var args = e as PropertyChangedExtendedEventArgs;
            //do whatever you want
        }

2
我不明白如何在我的Entity类上实现通用接口,因为T将根据属性的数据类型而改变。这是正确的。你正在查看的答案并不是很好,因为它包含了无法编译或理解的代码。通过传递继承非通用PropertyChangedEventArgs类的通用args类来引发非通用PropertyChanged事件的基本思想本身是可以的。在你的情况下,它应该能够正常工作。但是!你将无法实现答案提出的通用接口,因为(正如你已经注意到的)当属性具有不同类型时,该接口不起作用。事实上,你正在查看的答案甚至不会编译该接口,因为他们的事件触发方法本身是通用的,并引发非通用PropertyChanged事件(该方法与通用接口不兼容),而且他们没有费心去实际实现他们的通用接口(如果他们尝试过,他们会注意到通用方法与通用接口不兼容...该方法的类型参数T与接口的类型参数T不同)。
很遗憾,那篇得到高赞的答案写得不太好。其中有一些有用的想法,但也有完全行不通的想法,会让那些正在寻找这样答案的人感到困惑(即最渴望阅读和使用答案的人)。

谢谢 Peter。我没有使用 T,而是使用了字符串类型来解决它;至于审计,我需要捕获每个属性的旧值和新值并将其存储在数据库中以供报告目的。 - Umesh Kanase

0
如@Andy所提到的,您可以实现INotifyPropetyChanging来获取旧值,但是存储是非常繁琐的,因此我制作了一个帮助类NotifyPropertyChangingCache,您可以按照以下方式使用:
class EntityBase : INotifyPropertyChanged, INotifyPropertyChanging
{ /* ... */ }

// initiating the entity
var entity = new EntityBase();

// initiating the cache:
var notifyPropertyChangingCache = new Ice1e0.ComponentModel.NotifyPropertyChangingCache();
entity.PropertyChanging += notifyPropertyChangingCache.OnPropertyChanging;

// your property changed event looks like this
entity.PropertyChanged += (s, e) =>
{
    var oldValue = notifyPropertyChangingCache.GetLastPropertyValue(e.PropertyName);

    // your code goes here ...
};

// when you are done with your entity, don't forget to remove the events (we don't want to create a memory leak ...)
entity.PropertyChanging -= notifyPropertyChangingCache.OnPropertyChanging;
//entity.PropertyChanged -= ... (I assume you use not a in-line method here as I did in this example)

0

不行。还要实现INotifyPropetyChanging接口,这样才能访问旧值。你可以在文档中找到两个接口的示例实现。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接