Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a good strongly typed way to do PropertyChanged events in C#?

It must be a somewhat common event to change the name of a property and expect the Rename functionality in Visual Studio to take care of all the necessary renaming, except for the property name of the PropertyChanged event of INotifyPropertyChanged. Is there a better way to somehow get it strongly typed so you don't need to remember to manually rename it?

like image 852
Davy8 Avatar asked Jul 14 '09 21:07

Davy8


People also ask

How do you implement INotifyPropertyChanged?

To implement INotifyPropertyChanged you need to declare the PropertyChanged event and create the OnPropertyChanged method. Then for each property you want change notifications for, you call OnPropertyChanged whenever the property is updated.

What is Propertychangedeventhandler C#?

Represents the method that will handle the PropertyChanged event raised when a property is changed on a component.

What is OnPropertyChanged?

The INotifyPropertyChanged interface is used to notify clients, typically binding clients, that a property value has changed. For example, consider a Person object with a property called FirstName .


3 Answers

Edit: nameof arrived in c# 6. Yay!


There is no nameof / infoof etc; this is much discussed, but it is what it is.

There is a way to do it using lambda expressions in .NET 3.5 (and parsing the expression tree), but in reality it isn't worth the overhead. For now, I'd just stick with strings (and unit tests if you are determined not to break it).


using System;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Reflection;
class Program : INotifyPropertyChanged {
    public event PropertyChangedEventHandler PropertyChanged;
    static void Main() {
        var p = new Program();
        p.PropertyChanged += (s, a) => Console.WriteLine(a.PropertyName);
        p.Name = "abc";
    }
    protected void OnPropertyChanged<T>(Expression<Func<Program, T>> property) {
        MemberExpression me = property.Body as MemberExpression;
        if (me == null || me.Expression != property.Parameters[0]
              || me.Member.MemberType != MemberTypes.Property) {
            throw new InvalidOperationException(
                "Now tell me about the property");
        }
        var handler = PropertyChanged;
        if (handler != null) handler(this,
          new PropertyChangedEventArgs(me.Member.Name));
    }
    string name;
    public string Name {
        get{return name;}
        set {
            name = value;
            OnPropertyChanged(p=>p.Name);
        }
    }
}
like image 112
Marc Gravell Avatar answered Sep 24 '22 02:09

Marc Gravell


C# 5 seem to have a solution. With an CallerMemberName attribute that can be used with parameters (One example on the net).

class Employee : INotifyPropertyChanged
{
    private string _Name;
    public string Name
    {
        get { return _Name; }

        set
        {
            _Name = value;
            RaisePropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void RaisePropertyChanged([CallerMemberName] string caller = "")
    {
        var temp = PropertyChanged;

        if ( temp != null )
        {
            temp( this, new PropertyChangedEventArgs( caller ) );
        }
    }
}
like image 35
uli78 Avatar answered Sep 27 '22 02:09

uli78


The simplest solution is to look at the stack trace and completly remove every explicit reference to the property.

public String Name
{
    get { return this.name; }
    set
    {
        if (value != this.name)
        {
            this.RaisePropertyChanging();
            this.name = value;
            this.RaisePropertyChanged();
        }
    }
}
private String name = null;

private void RaisePropertyChanged()
{
    String propertyName =
       new StackTrace().GetFrame(1).GetMethod().Name.SubString(4);

    PropertyChangedEventHandler handler = this.PropertyChanged;
    if (handler != null)
    {
        handler(new PropertyChangedEventArgs(propertyName));
    }
}

The code derives the property name through the stack trace from the caling method - that is the property setter method named set_<PropertyName>. If the compiler no longer follows this naming convention, the code breaks.

The other solution is to derive the property name from a lambda expression.

public static String GetPropertyNameFromLambdaExpression<TObject, TProperty>(
    Expression<Func<TObject, TProperty>> expression)
{
    return ((MemberExpression)expression.Body).Member.Name;
}

For example

GetPropertyNameFromLambdaExpression<String, Int32>(s => s.Length)

will return "Length" as exspected. A production version of the code really demands additional checks and better integration into the rest of the code. For example it is possible to use type inference for the generic arguments.

UPDATE

And there is a third solution - you can use MethodBase.GetCurrentMethod() inside a property getter or setter to obtain the name of the setter or getter method.

public String Name
{
    get { return this.name; }
    set
    {
        if (value != this.name)
        {
            String propertyName = MethodBase.GetCurentMethod().Name.SubString(4);

            this.RaisePropertyChanging(propertyName);
            this.name = value;
            this.RaisePropertyChanged(propertyName);
        }
    }
}
private String name = null;
like image 23
Daniel Brückner Avatar answered Sep 26 '22 02:09

Daniel Brückner