Поднять событие при изменении значения свойства?
Есть свойство, оно называется ImageFullPath1
public string ImageFullPath1 {get; set; }
Я собираюсь запустить событие, когда его значение изменится. Я знаю об изменении INotifyPropertyChanged
, но я хочу делать это с событиями.
Ответы
Ответ 1
Интерфейс 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();
}
}
}
Ответ 2
Я использую в основном те же шаблоны, что и 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 виртуальным, чтобы подклассы могли переопределять его для отслеживания изменений свойств.
Ответ 3
Поднятие события, когда изменяется свойство, является именно тем, что делает INotifyPropertyChanged. Там требуется один член для реализации INotifyPropertyChanged, и это событие PropertyChanged. Все, что вы внедрили, вероятно, будет идентичным этой реализации, поэтому нет преимущества, чтобы не использовать его.
Ответ 4
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.
Ответ 5
Если вы измените свойство для использования поля поддержки (вместо автоматического свойства), вы можете сделать следующее:
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);
}
}
}
Ответ 6
Ткач Fody делает большую часть работы за вас. Он внедряет INotifyPropertyChanging в ваш код во время компиляции:
https://github.com/Fody