web-dev-qa-db-fra.com

Comment implémenter INotifyPropertyChanged en C # 6.0?

La réponse à cette question a été modifiée pour indiquer qu'en C # 6.0, INotifyPropertyChanged peut être implémenté avec la procédure OnPropertyChanged suivante:

protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

Cependant, il n'est pas clair à partir de cette réponse quelle devrait être la définition de propriété correspondante. À quoi ressemble une implémentation complète d'INotifyPropertyChanged en C # 6.0 lorsque cette construction est utilisée?

22
T.C.

Après avoir incorporé les différentes modifications, le code ressemblera à ceci. J'ai mis en évidence avec des commentaires les parties qui ont changé et comment chacune aide

public class Data : INotifyPropertyChanged
{ 
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        //C# 6 null-safe operator. No need to check for event listeners
        //If there are no listeners, this will be a noop
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    // C# 5 - CallMemberName means we don't need to pass the property's name
    protected bool SetField<T>(ref T field, T value,
    [CallerMemberName] string propertyName = null)
    {
        if (EqualityComparer<T>.Default.Equals(field, value)) 
            return false;
        field = value;
        OnPropertyChanged(propertyName);
        return true;
    }

    private string name;
    public string Name
    {
        get { return name; }
        //C# 5 no need to pass the property name anymore
        set { SetField(ref name, value); }
    }
}
32
Panagiotis Kanavos

J'utilise la même logique dans mon projet. J'ai une classe de base pour tous les modèles de vue dans mon application:

using System.ComponentModel;
using System.Runtime.CompilerServices;

public class PropertyChangedBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Chaque modèle de vue hérite de cette classe. Maintenant, dans le setter de chaque propriété, j'ai juste besoin d'appeler OnPropertyChanged().

public class EveryViewModel : PropertyChangedBase
{
    private bool initialized;
    public bool Initialized
    {
        get
        {
            return initialized;
        }
        set
        {
            if (initialized != value)
            {
                initialized = value;
                OnPropertyChanged();
            }
        }
    }

Pourquoi ça marche?

[CallerMemberName] Est automatiquement rempli par le compilateur avec le nom du membre qui appelle cette fonction. Lorsque nous appelons OnPropertyChanged à partir de Initialized, le compilateur place nameof(Initialized) comme paramètre dans OnPropertyChanged

n autre détail important à garder à l'esprit

Le cadre requiert que PropertyChanged et toutes les propriétés auxquelles vous vous associez soient public.

15
Amadeusz Wieczorek

Je sais que cette question est ancienne, mais voici ma mise en œuvre

Bindable utilise un dictionnaire comme magasin de propriétés. Il est assez facile d'ajouter les surcharges nécessaires à une sous-classe pour gérer son propre champ de sauvegarde à l'aide des paramètres ref.

  • Pas de chaîne magique
  • Pas de réflexion
  • Peut être amélioré pour supprimer la recherche de dictionnaire par défaut

Le code:

    public class Bindable : INotifyPropertyChanged
    {
        private Dictionary<string, object> _properties = new Dictionary<string, object>();

        /// <summary>
        /// Gets the value of a property
        /// <typeparam name="T"></typeparam>
        /// <param name="name"></param>
        /// <returns></returns>
        protected T Get<T>([CallerMemberName] string name = null)
        {
            object value = null;
            if (_properties.TryGetValue(name, out value))
                return value == null ? default(T) : (T)value;
            return default(T);
        }

        /// <summary>
        /// Sets the value of a property
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <param name="name"></param>
        protected void Set<T>(T value, [CallerMemberName] string name = null)
        {
            if (Equals(value, Get<T>(name)))
                return;
            _properties[name] = value;
            OnPropertyChanged(name);
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

utilisé comme ça

public class Item : Bindable
{
     public Guid Id { get { return Get<Guid>(); } set { Set<Guid>(value); } }
}
5
Aaron. S