我需要在 PropertyChanged 事件处理程序中捕获旧值、新值和属性名称。INotifyPropertyChanged 的默认实现仅提供属性的名称。因此,在互联网上搜索后,我在这里发现了类似的问题,属性的扩展实现已更改。
以下是该 Q 和接口声明的链接:
NotifyPropertyChanged 事件,其中事件参数包含旧值
public interface INotifyPropertyChangedExtended<T>
{
event PropertyChangedExtendedEventHandler<T> PropertyChanged;
}
Run Code Online (Sandbox Code Playgroud)
公共委托 void PropertyChangedExtendedEventHandler(对象发送者, PropertyChangedExtendedEventArgs e);
上面的接口将解决我的问题,但是我不明白如何在我的实体类上实现通用接口,因为 T 将根据属性的数据类型而改变。
谁能帮助我理解,是否可以用 T 参数实现这个接口。这个示例的实现将会非常有帮助。
预先感谢您的帮助。
乌梅什
编辑 #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()));
}
}
Run Code Online (Sandbox Code Playgroud)
只需实现您自己的 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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
和你的视图模型
Person person = new Person();
person.PropertyChanged += person_PropertyChanged;
void person_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
var args = e as PropertyChangedExtendedEventArgs;
//do whatever you want
}
Run Code Online (Sandbox Code Playgroud)
我不明白如何在实体类上实现通用接口,因为 T 会根据属性的数据类型而改变
这是正确的。您正在查看的答案实际上并不是那么好的答案,因为它包含无法编译或没有意义的代码。
通过传递继承非泛型类 PropertyChanged的泛型参数类来引发非泛型事件的基本思想PropertyChangedEventArgs本身是可以的。它应该在你的情况下工作得很好。
但!您将无法实现答案提出的通用接口,因为(正如您已经注意到的)当属性具有不同类型时,该接口不起作用。事实上,您正在查看的答案甚至无法使用该接口进行编译,因为它们的事件引发方法本身是通用的,并且引发非通用PropertyChanged事件(该方法将与通用接口不兼容),并且它们不必费心去实际实现他们的泛型接口(如果他们尝试过,他们会注意到泛型方法不适用于泛型接口......方法的类型参数T与接口的类型参数不同T)。
不幸的是,高票答案是这样写的。它有一些有用的想法,但它也有完全不可行的想法,并且只会让正在寻找类似答案的人(即最想阅读和使用答案的人)感到困惑。