C# 每当属性值更改时引发事件?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2246777/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-07 00:44:56  来源:igfitidea点击:

Raise an event whenever a property's value changed?

c#.neteventsproperties

提问by Mohammad Dayyan

There is a property, it's named ImageFullPath1

有一个属性,名为ImageFullPath1

public string ImageFullPath1 {get; set; }

I'm going fire an event whenever its value changed. I am aware of changing INotifyPropertyChanged, but I want to do it with events.

每当其值发生变化时,我都会触发一个事件。我知道正在改变INotifyPropertyChanged,但我想用事件来做。

采纳答案by Aaronaught

The INotifyPropertyChangedinterface isimplemented with events. The interface has just one member, PropertyChanged, which is an event that consumers can subscribe to.

INotifyPropertyChanged接口通过事件实现的。该接口只有一个成员 ,PropertyChanged这是消费者可以订阅的事件。

The version that Richard posted is not safe. Here is how to safely implement this interface:

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;
}

Note that this does the following things:

请注意,这会执行以下操作:

  • Abstracts the property-change notification methods so you can easily apply this to other properties;

  • Makes a copy of the PropertyChangeddelegate beforeattempting to invoke it (failing to do this will create a race condition).

  • Correctly implements the INotifyPropertyChangedinterface.

  • 抽象属性更改通知方法,以便您可以轻松地将其应用于其他属性;

  • 尝试调用它之前制作PropertyChanged委托的副本(不这样做会产生竞争条件)。

  • 正确实现INotifyPropertyChanged接口。

If you want to additionallycreate a notification for a specificproperty being changed, you can add the following code:

如果您想为正在更改的特定属性另外创建通知,您可以添加以下代码:

protected void OnImageFullPathChanged(EventArgs e)
{
    EventHandler handler = ImageFullPathChanged;
    if (handler != null)
        handler(this, e);
}

public event EventHandler ImageFullPathChanged;

Then add the line OnImageFullPathChanged(EventArgs.Empty)after the line OnPropertyChanged("ImageFullPath").

然后在 lineOnImageFullPathChanged(EventArgs.Empty)之后添加line OnPropertyChanged("ImageFullPath")

Since we have .Net 4.5 there exists the CallerMemberAttribute, which allows to get rid of the hard-coded string for the property name in the source code:

由于我们有 .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();
            }
        }
    }

回答by Richard Berg

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*/);
    }
}                        

That said, I completely agree with Ryan. This scenario is precisely why INotifyPropertyChanged exists.

也就是说,我完全同意瑞安。这种情况正是 INotifyPropertyChanged 存在的原因。

回答by Oded

If you change your property to use a backing field (instead of an automatic property), you can do the following:

如果您更改您的属性以使用支持字段(而不是自动属性),您可以执行以下操作:

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);
    }
  }
}

回答by Ryan Brunner

Raising an event when a property changes is precisely what INotifyPropertyChanged does. There's one required member to implement INotifyPropertyChanged and that is the PropertyChanged event. Anything you implemented yourself would probably be identical to that implementation, so there's no advantage to not using it.

当属性更改时引发事件正是 INotifyPropertyChanged 所做的。实现 INotifyPropertyChanged 需要一个成员,那就是 PropertyChanged 事件。您自己实现的任何内容都可能与该实现相同,因此不使用它没有任何好处。

回答by Mikael Sundberg

I use largely the same patterns as Aaronaught, but if you have a lot of properties it could be nice to use a little generic method magic to make your code a little more DRY

我使用与 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
}

Usually I also make the OnPropertyChanged method virtual to allow sub-classes to override it to catch property changes.

通常,我还将 OnPropertyChanged 方法设为虚拟方法,以允许子类覆盖它以捕获属性更改。