有一个属性,它被命名为ImageFullPath1
public string ImageFullPath1 {get; set; }
每当值改变时,我希望触发一个事件。我知道可以使用INotifyPropertyChanged
进行更改,但我想使用事件来实现。
有一个属性,它被命名为ImageFullPath1
public string ImageFullPath1 {get; set; }
每当值改变时,我希望触发一个事件。我知道可以使用INotifyPropertyChanged
进行更改,但我想使用事件来实现。
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();
}
}
}
我使用和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
}
PropertyChanged?.Invoke(this, e);
- Tzwenni当属性发生变化时触发事件,这正是 INotifyPropertyChanged 的作用。实现 INotifyPropertyChanged 接口需要一个必要的成员,即 PropertyChanged 事件。你自己实现的任何内容都可能与该实现完全相同,因此不使用它没有任何优势。
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*/);
}
}
话虽如此,我完全同意Ryan的观点。这正是为什么INotifyPropertyChanged存在的原因。
ImageFullPath1Changed
的值可能会更改为null
。不要像这样调用事件! - Aaronaughtpublic 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 在当前上下文中不存在
。我有什么遗漏吗? - Abhijeete
只是一个例子。你需要传入一个 EventArgs
的实例。如果你没有要传递的实例,可以使用 EventArgs.Empty
。 - Oded已经有很好的答案了,但仍有一些人感到困惑
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; }
}
OnPropertyChanged("ImageFullPath");
的参数替换为nameof(ImageFullPath)
。这样,您就可以在编译时检查属性名称,以便在更改它时,如果忘记在方法调用中替换它,就会收到错误消息。 - meJustAndrew