Вызывать событие при изменении значения свойства?

Есть свойство, оно называется 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; }
    }



 
Другие вопросы по тегам