有一个属性,名为 ImageFullPath1
public string ImageFullPath1 {get; set; }
每当它的值发生变化时,我都会触发一个事件。我知道要改变INotifyPropertyChanged
,但我想用事件来做。
答案 0 :(得分:147)
使用事件实现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;
然后在第OnImageFullPathChanged(EventArgs.Empty)
行之后添加第OnPropertyChanged("ImageFullPath")
行。
由于我们有.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();
}
}
}
答案 1 :(得分:33)
我使用与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方法设为虚拟,以允许子类覆盖它以捕获属性更改。
答案 2 :(得分:7)
在属性更改时引发事件正是INotifyPropertyChanged所做的事情。有一个必需的成员来实现INotifyPropertyChanged,那就是PropertyChanged事件。您自己实现的任何内容都可能与该实现相同,因此不使用它是没有优势的。
答案 3 :(得分:5)
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存在的原因。
答案 4 :(得分:4)
如果您将属性更改为使用支持字段(而不是自动属性),则可以执行以下操作:
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);
}
}
}
答案 5 :(得分:0)
已经有很好的答案了,但还是有一些人一头雾水
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; }
}