web-dev-qa-db-fra.com

Relever un événement chaque fois que la valeur d'une propriété a changé?

Il y a une propriété, elle s'appelle ImageFullPath1

public string ImageFullPath1 {get; set; }

Je vais déclencher un événement chaque fois que sa valeur a changé. Je suis conscient de changer INotifyPropertyChanged, mais je veux le faire avec des événements.

61
Mohammad Dayyan

L'interface INotifyPropertyChanged est implémentée avec des événements. L'interface n'a qu'un seul membre, PropertyChanged, qui est un événement auquel les consommateurs peuvent s'abonner.

La version que Richard a publiée n'est pas sûre. Voici comment implémenter cette interface en toute sécurité:

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

Notez que cela fait les choses suivantes:

  • Résume les méthodes de notification de modification de propriété afin que vous puissiez facilement l'appliquer à d'autres propriétés;

  • Crée une copie du délégué PropertyChanged avant de tenter de l'invoquer (faute de cela, une condition de concurrence critique sera créée).

  • Implémente correctement l'interface INotifyPropertyChanged.

Si vous voulez en plus créer une notification pour une propriété spécifique en cours de modification, vous pouvez ajouter le code suivant:

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

public event EventHandler ImageFullPathChanged;

Ajoutez ensuite la ligne OnImageFullPathChanged(EventArgs.Empty) après la ligne OnPropertyChanged("ImageFullPath").

Comme nous avons .Net 4.5, il existe CallerMemberAttribute , qui permet de supprimer la chaîne codée en dur pour le nom de la propriété dans le code source:

    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();
            }
        }
    }
149
Aaronaught

J'utilise en grande partie les mêmes modèles qu'Aaronaught, mais si vous avez beaucoup de propriétés, il pourrait être agréable d'utiliser une petite méthode générique pour rendre votre code un peu plus SEC

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
}

En général, je mets également la méthode OnPropertyChanged en virtuelle pour permettre aux sous-classes de la remplacer pour intercepter les modifications de propriétés.

34
Mikael Sundberg

Le fait de déclencher un événement lorsqu'une propriété change est précisément ce que fait INotifyPropertyChanged. Un membre requis pour implémenter INotifyPropertyChanged est l'événement PropertyChanged. Tout ce que vous avez implémenté vous-même serait probablement identique à cette implémentation, il n'y a donc aucun avantage à ne pas l'utiliser.

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

Cela dit, je suis complètement d'accord avec Ryan. Ce scénario explique précisément pourquoi INotifyPropertyChanged existe.

5
Richard Berg

Si vous modifiez votre propriété pour utiliser un champ de sauvegarde (au lieu d'une propriété automatique), vous pouvez procéder comme suit:

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