Вызывать событие при изменении значения свойства?
Есть свойство, оно называется ImageFullPath1
public string ImageFullPath1 {get; set; }
Я собираюсь запустить событие, когда его значение изменилось. Я в курсе перемен INotifyPropertyChanged
Но я хочу сделать это с событиями.
6 ответов
INotifyPropertyChanged
интерфейс реализован с событиями. Интерфейс имеет только один член, PropertyChanged
, который является событием, на которое потребители могут подписаться.
Версия, которую опубликовал Ричард, небезопасна. Вот как можно безопасно реализовать этот интерфейс:
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();
}
}
}
Я использую в основном те же шаблоны, что и Aaronaught, но если у вас много свойств, было бы неплохо использовать немного общего волшебства метода, чтобы сделать ваш код немного более СУХИМЫМ
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 виртуальным, чтобы позволить подклассам переопределять его, чтобы перехватывать изменения свойств.
Вызывать событие при изменении свойства - это именно то, что делает 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*/);
}
}
Тем не менее, я полностью согласен с Райаном. Этот сценарий именно поэтому существует INotifyPropertyChanged.
Если вы измените свое свойство на использование вспомогательного поля (вместо автоматического свойства), вы можете сделать следующее:
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);
}
}
}
Уже есть хорошие ответы, но некоторые люди все еще не понимают
- EventArgs и кто они могут его использовать
- И некоторые из них о том, как передавать специальные параметры
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; }
}