【发布时间】:2011-01-15 20:22:51
【问题描述】:
有一个属性,名为ImageFullPath1
public string ImageFullPath1 {get; set; }
只要它的值发生变化,我就会触发一个事件。我知道要更改 INotifyPropertyChanged,但我想用事件来做。
【问题讨论】:
标签: c# .net events properties
有一个属性,名为ImageFullPath1
public string ImageFullPath1 {get; set; }
只要它的值发生变化,我就会触发一个事件。我知道要更改 INotifyPropertyChanged,但我想用事件来做。
【问题讨论】:
标签: c# .net events properties
INotifyPropertyChanged 接口是用事件实现的。该接口只有一个成员PropertyChanged,这是一个消费者可以订阅的事件。
Richard 发布的版本不安全。下面是如何安全地实现这个接口:
public class MyClass : INotifyPropertyChanged
{
private string imageFullPath;
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, e);
}
protected void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
public string ImageFullPath
{
get { return imageFullPath; }
set
{
if (value != imageFullPath)
{
imageFullPath = value;
OnPropertyChanged("ImageFullPath");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
请注意,它会执行以下操作:
抽象属性更改通知方法,以便您可以轻松地将其应用于其他属性;
在尝试调用 PropertyChanged 委托之前复制它(如果不这样做将产生竞争条件)。
正确实现INotifyPropertyChanged接口。
如果您想另外为正在更改的特定属性创建通知,您可以添加以下代码:
protected void OnImageFullPathChanged(EventArgs e)
{
EventHandler handler = ImageFullPathChanged;
if (handler != null)
handler(this, e);
}
public event EventHandler ImageFullPathChanged;
然后在OnPropertyChanged("ImageFullPath")行之后添加OnImageFullPathChanged(EventArgs.Empty)行。
因为我们有 .Net 4.5,所以存在 CallerMemberAttribute,它允许摆脱源代码中属性名称的硬编码字符串:
protected void OnPropertyChanged(
[System.Runtime.CompilerServices.CallerMemberName] string propertyName = "")
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
public string ImageFullPath
{
get { return imageFullPath; }
set
{
if (value != imageFullPath)
{
imageFullPath = value;
OnPropertyChanged();
}
}
}
【讨论】:
OnPropertyChanged("ImageFullPath"); 调用的参数替换为nameof(ImageFullPath)。这样,您将在编译时检查属性名称,以便在您更改它时,如果您忘记在方法调用中替换它,您将收到一条错误消息。
我使用的模式与 Aaronaught 基本相同,但如果您有很多属性,最好使用一点通用方法魔术来让您的代码更丰富一点DRY
public class TheClass : INotifyPropertyChanged {
private int _property1;
private string _property2;
private double _property3;
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) {
PropertyChangedEventHandler handler = PropertyChanged;
if(handler != null) {
handler(this, e);
}
}
protected void SetPropertyField<T>(string propertyName, ref T field, T newValue) {
if(!EqualityComparer<T>.Default.Equals(field, newValue)) {
field = newValue;
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
}
public int Property1 {
get { return _property1; }
set { SetPropertyField("Property1", ref _property1, value); }
}
public string Property2 {
get { return _property2; }
set { SetPropertyField("Property2", ref _property2, value); }
}
public double Property3 {
get { return _property3; }
set { SetPropertyField("Property3", ref _property3, value); }
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
通常我还将 OnPropertyChanged 方法设为虚拟,以允许子类覆盖它以捕获属性更改。
【讨论】:
PropertyChanged?.Invoke(this, e);
当属性改变时引发事件正是 INotifyPropertyChanged 所做的。实现 INotifyPropertyChanged 需要一个成员,那就是 PropertyChanged 事件。你自己实现的任何东西都可能与那个实现相同,所以不使用它没有任何好处。
【讨论】:
XChangedEvent,您也已经完成了这项工作,因此请继续实现 INotifyPropertyChanged。未来(如 WPF)会感谢你,因为这是未来希望你做的事情。
public event EventHandler ImageFullPath1Changed;
public string ImageFullPath1
{
get
{
// insert getter logic
}
set
{
// insert setter logic
// EDIT -- this example is not thread safe -- do not use in production code
if (ImageFullPath1Changed != null && value != _backingField)
ImageFullPath1Changed(this, new EventArgs(/*whatever*/);
}
}
也就是说,我完全同意瑞恩的观点。这种情况正是 INotifyPropertyChanged 存在的原因。
【讨论】:
ImageFullPath1Changed 的值可以更改为 null。不要调用这样的事件!
如果您将属性更改为使用支持字段(而不是自动属性),您可以执行以下操作:
public event EventHandler ImageFullPath1Changed;
private string _imageFullPath1 = string.Empty;
public string ImageFullPath1
{
get
{
return imageFullPath1 ;
}
set
{
if (_imageFullPath1 != value)
{
_imageFullPath1 = value;
EventHandler handler = ImageFullPathChanged;
if (handler != null)
handler(this, e);
}
}
}
【讨论】:
handler(this, e), e does not exist in current context我在发什么消息吗?
e 只是一个例子。您需要传入EventArgs 的实例。如果没有要通过的,可以使用EventArgs.Empty。
已经有了很好的答案,但有些人仍然感到困惑
class Program
{
static void Main(string[] args)
{
Location loc = new Location();
loc.LocationChanged += (obj, chngLoc) =>
{
Console.WriteLine("Your LocId Is");
Console.WriteLine(chngLoc.LocId);
Console.WriteLine(chngLoc.LocCode);
Console.WriteLine(chngLoc.LocName);
Console.ReadLine();
};
Console.WriteLine("Default Location Is");
Console.WriteLine(loc.LocId);
Console.WriteLine("Change Location");
loc.LocId = Console.ReadLine();
}
}
public class Location
{
private string _locId = "Default Location";
public string LocId
{
get
{
return _locId;
}
set
{
_locId = value;
if (LocationChanged != null && value != LocId)
{
B1Events b1 = new B1Events();
b1.LocCode = "Changed LocCode";
b1.LocId = value;
b1.LocName = "Changed LocName";
LocationChanged(this, b1);
}
}
}
public event EventHandler<B1Events> LocationChanged;
}
public class B1Events : EventArgs
{
public string LocId { get; set; }
public string LocCode{ get; set; }
public string LocName { get; set; }
}
【讨论】: