63 votes

Automatiquement INotifyPropertyChanged

Est-il possible d'obtenir automatiquement notifié des changements de propriété dans une classe sans avoir à écrire OnPropertyChanged dans chaque setter? (J'ai des centaines de propriétés que je veux savoir si elles ont changé).


Anton suggère des proxies dynamiques. En fait, j'ai utilisé le "Château" de la bibliothèque pour quelque chose de semblable dans le passé, et même si elle fait de réduire la quantité de code que j'ai eu à écrire, elle a ajouté environ 30 secondes pour mon programme de démarrage (ymmv) - parce que c'est une solution d'exécution.

Je me demandais si il y a un moment de la compilation de la solution, peut-être à l'aide de la compilation des attributs...


Slashene et TcKs donner des suggestions qui génère le code répétitif - malheureusement, pas toutes mes propriétés sont un cas simple de m_Value = valeur - beaucoup d'entre eux ont un code personnalisé dans le setters, de sorte que l'emporte-pièce de code à partir des extraits et le xml n'est pas possible pour mon projet.

48voto

Isak Savo Points 15357

EDIT: L'auteur de NotifyPropertyWeaver a retiré de l'outil en faveur de la plus générale Fody. (Un guide de migration pour les personnes se déplaçant à partir de tisserand pour fody est disponible.)


Un outil très pratique que j'ai utilisé pour mes projets est d' Informer la Propriété Weaver Fody.

Il s'installe comme une étape de génération dans vos projets et lors de la compilation injecte du code que soulève l' PropertyChanged événement.

Prise de propriétés élever PropertyChanged est fait en mettant les attributs spéciaux :

[ImplementPropertyChanged]
public string MyProperty { get; set; }

Comme un bonus, vous pouvez également spécifier des relations pour les propriétés qui dépendent d'autres propriétés

[ImplementPropertyChanged]
public double Radius { get; set; }

[DependsOn("Radius")]
public double Area 
{
    get { return Radius * Radius * Math.PI; }
}

39voto

Svish Points 32303

Nous utilisons le code ci-dessous (à Partir de http://www.ingebrigtsen.info/post/2008/12/11/INotifyPropertyChanged-revisited.aspx). Fonctionne très bien :)

public static class NotificationExtensions
{
    #region Delegates

    /// <summary>
    /// A property changed handler without the property name.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="sender">The object that raised the event.</param>
    public delegate void PropertyChangedHandler<TSender>(TSender sender);

    #endregion

    /// <summary>
    /// Notifies listeners about a change.
    /// </summary>
    /// <param name="EventHandler">The event to raise.</param>
    /// <param name="Property">The property that changed.</param>
    public static void Notify(this PropertyChangedEventHandler EventHandler, Expression<Func<object>> Property)
    {
        // Check for null
        if (EventHandler == null)
            return;

        // Get property name
        var lambda = Property as LambdaExpression;
        MemberExpression memberExpression;
        if (lambda.Body is UnaryExpression)
        {
            var unaryExpression = lambda.Body as UnaryExpression;
            memberExpression = unaryExpression.Operand as MemberExpression;
        }
        else
        {
            memberExpression = lambda.Body as MemberExpression;
        }

        ConstantExpression constantExpression;
        if (memberExpression.Expression is UnaryExpression)
        {
            var unaryExpression = memberExpression.Expression as UnaryExpression;
            constantExpression = unaryExpression.Operand as ConstantExpression;
        }
        else
        {
            constantExpression = memberExpression.Expression as ConstantExpression;
        }

        var propertyInfo = memberExpression.Member as PropertyInfo;

        // Invoke event
        foreach (Delegate del in EventHandler.GetInvocationList())
        {
            del.DynamicInvoke(new[]
            {
                constantExpression.Value, new PropertyChangedEventArgs(propertyInfo.Name)
            });
        }
    }


    /// <summary>
    /// Subscribe to changes in an object implementing INotifiyPropertyChanged.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="ObjectThatNotifies">The object you are interested in.</param>
    /// <param name="Property">The property you are interested in.</param>
    /// <param name="Handler">The delegate that will handle the event.</param>
    public static void SubscribeToChange<T>(this T ObjectThatNotifies, Expression<Func<object>> Property, PropertyChangedHandler<T> Handler) where T : INotifyPropertyChanged
    {
        // Add a new PropertyChangedEventHandler
        ObjectThatNotifies.PropertyChanged += (s, e) =>
            {
                // Get name of Property
                var lambda = Property as LambdaExpression;
                MemberExpression memberExpression;
                if (lambda.Body is UnaryExpression)
                {
                    var unaryExpression = lambda.Body as UnaryExpression;
                    memberExpression = unaryExpression.Operand as MemberExpression;
                }
                else
                {
                    memberExpression = lambda.Body as MemberExpression;
                }
                var propertyInfo = memberExpression.Member as PropertyInfo;

                // Notify handler if PropertyName is the one we were interested in
                if (e.PropertyName.Equals(propertyInfo.Name))
                {
                    Handler(ObjectThatNotifies);
                }
            };
    }
}

Utilisé par exemple de cette façon:

public class Employee : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private string _firstName;
    public string FirstName
    {
        get { return this._firstName; }
        set
        {
            this._firstName = value;
            this.PropertyChanged.Notify(()=>this.FirstName);
        }
    }
}

private void firstName_PropertyChanged(Employee sender)
{
    Console.WriteLine(sender.FirstName);
}

employee = new Employee();
employee.SubscribeToChange(() => employee.FirstName, firstName_PropertyChanged);

Quelques erreurs de syntaxe dans l'exemple peuvent exister. Ne pas le tester. Mais vous devriez avoir le concept il y a au moins :)

EDIT: je vois maintenant que vous avez voulu encore moins de travail, mais ouais... les trucs ci-dessus au moins il est beaucoup plus facile. Et de vous prévenir de tout ce qui fait peur, des problèmes avec la référence à des propriétés à l'aide de cordes.

37voto

takrl Points 3285

Le framework 4.5 nous fournit les CallerMemberNameAttribute , ce qui rend inutile de passer le nom de la propriété sous forme de chaîne:

 private string m_myProperty;
public string MyProperty
{
    get { return m_myProperty; }
    set
    {
        m_myProperty = value;
        OnPropertyChanged();
    }
}

private void OnPropertyChanged([CallerMemberName] string propertyName = "none passed")
{
    // ... do stuff here ...
}
 

Semblable à la solution de Svish, remplacer simplement lambda awesomeness par une fonctionnalité de framework ennuyeuse ;-)

11voto

Nicolas Dorier Points 4038

implémenter un coffre-fort de type INotifyPropertyChanged: voir ici

Créez ensuite votre propre extrait de code:

  private $Type$ _$PropertyName$;
 public $Type$ $PropertyName$
    {
        get
        {
        	return _$PropertyName$;
        }
        set
        {
        	if(value != _$PropertyName$)
        	{
        	             _$PropertyName$ = value;
                         OnPropertyChanged(o => o.$PropertyName$);

        	}
        }
    }
 

Avec le concepteur d'extraits de code et vous avez fait! Un moyen simple et sécurisé d’acquérir votre INotifyPropertyChanged.

11voto

Konstantin Spirin Points 5347

Vous pouvez avoir une méthode d'extension sur votre délégué PropertyChanged et l'utiliser comme ceci:

 public string Name
{
    get { return name; }
    set
    {
        name = value;
        PropertyChanged.Raise(() => Name);
    }
}
 

La méthode d'extension permet de déterminer le nom de l'expéditeur et de la propriété simplement en inspectant l'arbre d'expression lambda sans impact majeur sur les performances :

 public static class PropertyChangedExtensions
{
    public static void Raise(this PropertyChangedEventHandler handler, Expression<Func<object>> property)
    {
        if (handler == null)
            return;

        var boxingExpr = property.Body as UnaryExpression;

        var memberExpr = (MemberExpression)(boxingExpr == null ? 
            property.Body : boxingExpr.Operand);

        var propertyName = memberExpr.Member.Name;
        var sender = ((ConstantExpression)memberExpr.Expression).Value;
        handler.Invoke(sender, new PropertyChangedEventArgs(propertyName));
    }
}
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X