Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Concise PropertyChanged Implementation

0.00/5 (No votes)
29 Jun 2016 1  
Save wear and tear on your keyboard: INotifyPropertyChanged with the fewest key-strokes.

I H8 PropertyChanged

...because I got used to auto-properties really quickly...

The most painful part of building entities to interact with the UI, (for me at least) is having to create properties that participate in the INotifyPropertyChanged interface.

You know the ones I mean:

string _backingField;

public string MyProperty  
{
    get { return _backingField; }
    set 
    { 
          if (!string.Equals(value, _backingField)
          {
              _backingField = value;
              OnPropertyChanged("MyProperty");
          }
    }
}

You can improve the rate at which you can add these using a code-snippet, but there are annoying limitations, like having to rename the backing field to match the naming convention, the problem of the name being passed to OnPropertyChanged becoming out-of-date with refactoring *(unless you are using C#6.0/VS2015 and "nameof()") and that it's too verbose, you end up with a bunch of backing fields you don't want, and it's just fugly.

Wouldn't it be nicer if it looked like this?

public string MyProperty
{
    get { return GetValue(() => MyProperty); }
    set { SetValue(() => MyProperty, value); }
}

The above code can be generated easily enough with a snippet, it neatly aligns, requires no backing field, type-inference and generics work together to make it strongly-typed, and refactoring the property-name will not break anything.

What's the Trick?

Expressions! The GetValue and SetValue method's first parameter is an Expression<Func<T>> - this allows you to pass a lambda expression into the method (with the property name) and supplies the generic-type context (T)

The rest of the trick is that the backing field is in fact a Dictionary<string, object> that stores the values for all the properties.

/// <summary>
/// stores the values for the properties
/// </summary>
protected Dictionary<string, object> m_values = new Dictionary<string, object>();

/// <summary>
/// gets the value of the member specified in the member expression.
/// Generic type parameters should be inferred.
/// </summary>
/// <typeparam name="T">the property type</typeparam>
/// <param name="memberExpression">lambda expression
/// referring to the property invoking this method</param>
/// <returns>the value, or default(T)</returns>
public T GetValue<T>(Expression<Func<T>> memberExpression)
{
    var body = memberExpression.Body as MemberExpression;
    if (body != null)
    {
        object value;
        if (m_values.TryGetValue(body.Member.Name, out value))
        {
            // return the value;
            return (T)value;
        }
    }

    // return a default:
    return default(T);
}

/// <summary>
/// sets the value
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="memberExpression"></param>
/// <param name="value"></param>
/// <returns></returns>
public bool SetValue<T>(Expression<Func<T>> memberExpression, T value)
{
    // is the value different from the existing?
    if (EqualityComparer<T>.Default.Equals(value, GetValue(memberExpression)))
    {
        return false;
    }

    // fetch the name of the property:
    var body = memberExpression.Body as MemberExpression;
    if (body != null)
    {
        // set the value:
        m_values[body.Member.Name] = value;

        // raise the property-changed event
        OnPropertyChanged(body.Member.Name);
    }

    // return true for changed:
    return true;
}

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here