web-dev-qa-db-fra.com

Gestion de OnPropertyChanged

Je ne connais pas bien la programmation événementielle. En gros, je trébuche toujours avec. J'essaie de mettre quelque chose en place, mais même avec les tutoriels, je ne peux pas m'en tenir à la tête. Ce que je voudrais faire (en mots) est le suivant:

  1. J'ai un objet de données où une propriété change. Je le remarque dans le setter de la propriété et je souhaite déclencher un événement que la propriété a changé.

  2. Ailleurs (dans une classe entièrement différente), je veux savoir que la propriété de cet objet a changé et prendre des mesures.

Maintenant, je suis sûr que c'est un scénario assez courant, mais mon google-fu me laisse tomber. Je ne comprends tout simplement pas http://msdn.Microsoft.com/en-us/library/ms743695.aspx .

J'ai ceci:

public class ChattyClass {
  private int someMember;

  public event PropertyChangedEventHandler PropertyChanged;

  public int SomeMember {
    get {
      return this.someMember;
    }
    set {
      if (this.someMember != value){
        someMember = value;
        // Raise event/fire handlers. But how?
      }
   }
}

public class NosyClass{
  private List<ChattyClass> myChatters;

  public void addChatter(ChattyClass chatter){
    myChatters.add(chatter);
    // Start listening to property changed events
  }

  private void listner(){
    // I want this to be called when the PropertyChangedEvent is called
    Console.WriteLine("Hey! Hey! Listen! A property of a chatter in my list has changed!");
  }
}

Que dois-je faire pour câbler cela?

Concernant le commentaire me renvoyant au lien:

Dans l'exemple que je vois:

protected void OnPropertyChanged(string name)
{
    PropertyChangedEventHandler handler = PropertyChanged;
    if (handler != null)
    {
        handler(this, new PropertyChangedEventArgs(name));
    }
}

Ce que je ne comprends pas:

  • Pourquoi n'est-ce pas simplement appeler PropertyChanged(this, new PropertyCHangedEventArgs(name))
  • Où PropertyChanged est-il affecté?
  • À quoi ressemble la mission?
33
Martijn

Vous devez déclencher l'événement. Dans l'exemple sur MSDN, ils ont créé une méthode protégée OnPropertyChanged pour gérer cela plus facilement (et pour éviter la duplication de code).

// Create the OnPropertyChanged method to raise the event 
protected void OnPropertyChanged(string name)
{
    PropertyChangedEventHandler handler = PropertyChanged;
    if (handler != null)
    {
        handler(this, new PropertyChangedEventArgs(name));
    }
}

Ce que fait cette méthode, c'est de voir si un gestionnaire d'événements est affecté ou non (s'il n'est pas affecté et que vous l'appelez, vous obtiendrez un NullReferenceException). S'il y en a un, appelez ce gestionnaire d'événements. Le gestionnaire d'événements fourni doit avoir la signature du délégué PropertyChangedEventHandler. Cette signature est:

void MyMethod(object sender, PropertyChangedEventArgs e)

Où le premier paramètre doit être du type objet et représente l'objet qui déclenche l'événement, et le deuxième paramètre contient les arguments de cet événement. Dans ce cas, votre propre classe déclenche l'événement et donne donc this comme paramètre sender. Le deuxième paramètre contient le nom de la propriété qui a changé.

Maintenant, pour pouvoir réagir au déclenchement de l'événement, vous devez affecter un gestionnaire d'événements à la classe. Dans ce cas, vous devrez l'assigner dans votre méthode addChatter. En dehors de cela, vous devrez d'abord définir votre gestionnaire. Dans votre NosyClass, vous devrez ajouter une méthode pour ce faire, par exemple:

private void chatter_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    Console.WriteLine("A property has changed: " + e.PropertyName);
}

Comme vous pouvez le voir, cette méthode correspond à la signature que j'ai expliquée précédemment. Dans le deuxième paramètre, vous pourrez trouver les informations dont le paramètre a été modifié. La dernière chose à faire est d'ajouter le gestionnaire d'événements. Maintenant, dans votre méthode addChatter, vous devrez assigner ceci:

public void AddChatter(ChattyClass chatter)
{
    myChatters.Add(chatter);
    // Assign the event handler
    chatter.PropertyChanged += new PropertyChangedEventHandler(chatter_PropertyChanged);
}

Je vous suggère de lire quelque chose sur les événements dans .NET/C #: http://msdn.Microsoft.com/en-us/library/awbftdfh . Je pense qu'après avoir lu/appris cela, les choses seront plus claires pour vous.

Vous pouvez trouver une application console ici sur Pastebin si vous souhaitez la tester rapidement (il suffit de copier/coller dans une nouvelle application console).

Avec les nouvelles versions de C #, vous pouvez incorporer l'appel au gestionnaire d'événements:

// inside your setter
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MyProperty)));

Vous pouvez également utiliser quelque chose comme Fody PropertyChanged pour générer automatiquement le code nécessaire (visitez le lien vers leur page GitHub, avec des exemples).

36
Styxxy

Le lien que vous avez recherché concerne le modèle MVVM et WPF . Ce n'est pas une implémentation C # générale. Vous avez besoin de quelque chose comme ça:

public event EventHandler PropertyChanged;

    public int SomeMember {
        get {
            return this.someMember;
        }
        set {
            if (this.someMember != value) {
                someMember = value;
                if (PropertyChanged != null) { // If someone subscribed to the event
                    PropertyChanged(this, EventArgs.Empty); // Raise the event
                }
            }
        }

...

public void addChatter(ChattyClass chatter) {
    myChatters.add(chatter);
    chatter.PropertyChanged += listner; // Subscribe to the event
}
// This will be called on property changed
private void listner(object sender, EventArgs e){
    Console.WriteLine("Hey! Hey! Listen! A property of a chatter in my list has changed!");
}

Si vous souhaitez savoir quelle propriété a changé, vous devez modifier la définition de votre événement en:

public event PropertyChangedEventHandler PropertyChanged;

Et changez l'appel en:

public int SomeMember {
    get {
        return this.someMember;
    }
    set {
        if (this.someMember != value){
            someMember = value;
            if (PropertyChanged != null) { // If someone subscribed to the event
                PropertyChanged(this, new PropertyChangedEventArgs("SomeMember")); // Raise the event
            }
        }
   }

   private void listner(object sender, PropertyChangedEventArgs e) {
       string propertyName = e.PropertyName;
       Console.WriteLine(String.Format("Hey! Hey! Listen! a {0} of a chatter in my list has changed!", propertyName));
   }
11
Vale

pourquoi n'est-ce pas simplement appeler PropertyChanged (ceci, nouveau PropertyCHangedEventArgs (nom))

Parce que si personne n'a attaché un gestionnaire à l'événement, l'objet PropertyChanged renvoie null. Vous devrez donc vous assurer qu'il n'est pas nul avant de l'appeler.

où PropertyChanged est-il affecté?

Dans les classes "auditeur".

Par exemple, vous pouvez écrire dans une autre classe:

ChattyClass tmp = new ChattyClass();
tmp.PropertyChanged += (sender, e) =>
    {
        Console.WriteLine(string.Format("Property {0} has been updated", e.PropertyName));
    };

À quoi ressemble la mission?

En C #, nous utilisons les opérateurs d'affectation += et -= pour les événements. Je recommande de lire l'article suivant pour comprendre comment écrire des gestionnaires d'événements en utilisant le formulaire de méthode anonyme (exemple ci-dessus) et le "vieux" formulaire.

6
ken2k

En reprenant le code original et en incorporant la réponse de @Styxxy, je ressort avec:

public class ChattyClass  : INotifyPropertyChanged 
{
  private int someMember, otherMember;

  public int SomeMember
  {
      get
      {
          return this.someMember;
      }
      set
      {
          if (this.someMember != value)
          {
              someMember = value;
              OnPropertyChanged("Some Member");
          }
      }
  }

  public int OtherMember
  {
      get
      {
          return this.otherMember;
      }
      set
      {
          if (this.otherMember != value)
          {
              otherMember = value;
              OnPropertyChanged("Other Member");
          }
      }
  }

  protected virtual void OnPropertyChanged(string propertyName)
  {
      PropertyChangedEventHandler handler = PropertyChanged;
      if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
  }

  public event PropertyChangedEventHandler PropertyChanged;

}

public class NosyClass
{
    private List<ChattyClass> myChatters = new List<ChattyClass>();

    public void AddChatter(ChattyClass chatter)
    {
        myChatters.Add(chatter);
        chatter.PropertyChanged+=chatter_PropertyChanged;
    }

    private void chatter_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        Console.WriteLine("A property has changed: " + e.PropertyName);
    }
}
3
Paul Richards