Click here to Skip to main content
Click here to Skip to main content

AutoPropertyChanged - Type-safe INotifyPropertyChanged Implementation

, 24 May 2010
Rate this:
Please Sign up or sign in to vote.
Typesafe INotifyPropertyChanged implementation without run-time Reflection and without lambda expressions.

Introduction

I'd guess anyone who delved a little bit deeper into WPF has learned how the class ObservableCollection integrates so smoothly into WPF's databinding.

The problem is, once you start doing simple things like linking a ViewModel's properties to, say, a CheckBox control, you inevitably start to wonder if you really need to implement the INotificationPropertyChanged interface all the time by foot.

To me, one of the biggest drawbacks of implementing this interface directly is that you need to duplicate a property's name as a string, since the PropertyChangedEventArgs parameter needs the property name. Once you start refactoring, better not forget to update all places referencing the property - if you forget one place, you'll pay that back at runtime Smile | :)

Another drawback is the amount of code you have to write each time you add a new class to your ViewModel. Also, usually you construct a "new PropertyChangedEventArgs" object every time any property changes. This impacts the managed heap and reduces performance.

A good summary of existing solutions to this is here (great site as well, by the way).

This article tries - to my knowledge - a new way of getting around the hassle. Using AutoPropertyChanged, you can get around most of the mentioned problems.

The demo project as well as the Utility.AutoPropertyChangedNotification project are both created using Visual Studio 2010 and .NET 4.0. This blog entry describes a way how 2010 solution and project files can be opened in the 2008 version - never tried it myself though.

Using the Code

First of all, the class which wants to expose its properties needs to be derived from the class ImplementsPropertyChanged. This could be a big drawback, but on the other hand, you're free to bind your properties not to your ViewModel directly but to a property of it, so you could as well leave your ViewModel as it is and contain the ImplementsPropertyChanged object instead of deriving directly.

The first step looks like this:

public class ListItem : ImplementsPropertyChanged
{
    public ListItem()
    {
    }
}

Okay, well this does next to nothing. ImplementsPropertyChanged adds a PropertyChangedEventHandler event to the class and provides a default implementation of OnPropertyChanged.

Let's add a property to the class:

public class ListItem : ImplementsPropertyChanged
{
    public ListItem()
    {
    }

    [AutoPropertyChangedNotification]
    public AutoPropertyChanged<ListItem, bool, Property0> IsChecked
    {
        get;
        set;
    }
}

This code wouldn't run yet. You need to initialize IsChecked to get it to work:

public class ListItem : ImplementsPropertyChanged
{
    public ListItem()
    {
        IsChecked = Prop.New(IsChecked, this);
    }

    [AutoPropertyChangedNotification]
    public AutoPropertyChanged<ListItem, bool, Property0> IsChecked
    {
        get;
        set;
    }
}

Done - that's all you need (save calling AutoPropertyInitializer.InitializeProperties() on startup).

The AutoPropertyChanged has a property of its own, Value. Accessing the property from code or XAML would thus look like this:

ListItem myItem = new ListItem();
myItem.IsChecked.Value = true;

You need to keep this in mind when using it - I didn't find a way to workaround this, since indexers need at least one parameter.

The code-behind in the XAML could look like this:

<CheckBox Content="Check this box" Name="checkBox1" 
          IsChecked="{Binding Path=IsChecked.Value}" />

But what's this generic parameter Property0?

That's the not-so-nice aspect of this solution. As I mentioned above, the problem was to tell the .NET runtime to keep the static PropertyChangedEventArgs fields different, and I didn't find a nicer approach.

But still, it works, and if you mess something up (say, using the same PropertyN field for more than just one property), you'll learn this during the one-time initialization phase by catching a DuplicatePropertyFoundException Smile | :) .

How does it work?

AutoPropertyChangedNotification

First of all, there is obviously this attribute AutoPropertyChangedNotification. It's implemented without any fancy stuff, just a plain and simple attribute which can be set to properties only:

[AttributeUsage(AttributeTargets.Property)]
public sealed class AutoPropertyChangedNotificationAttribute : Attribute
{
}

Its purpose is to keep the AutoPropertyChanged-properties (easily) identifiable by Reflection - and also a little bit to indicate to the reader that there is a bit more going on in the background.

Prop.New

The static method Prop.New is just a simplification - usually, you'd need to create the properties like this:

IsChecked = new AutoPropertyChanged<ListItem, bool, Property0>(this);

This looks errorprone. Prop.New alleviates this:

public static class Prop
{
    public static AutoPropertyChanged<T0, T1, T2> New<T0, T1, T2>(
           AutoPropertyChanged<T0, T1, T2> field, T0 hostClass) 
           where T0 : ImplementsPropertyChanged
    {
        return new AutoPropertyChanged<T0, T1, T2>(hostClass);
    }
}

Possibly, this doesn't look nice. It quite surely doesn't, but it compiles to quite a few IL instructions and incurs no runtime overhead - and makes the resulting code much more readable.

AutoPropertyChanged

The generic class AutoPropertyChanged is a little bit more involved:

public class AutoPropertyChanged<TBase, TProperty, 
       TPropertyName> where TBase : ImplementsPropertyChanged
{
    public static PropertyChangedEventArgs propertyChanged;
    public AutoPropertyChanged(TBase parent)
    {
        _parent = parent;
    }
    private TBase _parent;
    private TProperty _value;
    public TProperty Value
    {
        get
        {
            return _value;
        }
        set
        {
            _value = value;
            _parent.OnPropertyChanged(propertyChanged);
        }
    }
}

There's the aforementioned static PropertyChangedEventArgs field. As you can see, still nothing special going on here: two dynamic fields (_parent and _value), and the rest is plain-and-simple invoking the event if needed.

Obviously, this introduces a little bit of overhead - the _parent reference is needed for invoking the PropertyChanged event; we wouldn't need this though if we were implementing the IPropertyChangedNotification interface by foot.

This is everything the runtime needs to execute. No Reflection and no lambda expressions until here - just once Reflection is needed, and this happens in the one-time initialisation phase.

AutoPropertyInitializer.InitializeProperties

This static method triggers the one-time initialisation of the properties. Most of the ugliness is contained there. Let's have a look:

// ... Iterating all loaded assemblies,
// enumerating their types omitted for brevity. Rest:
// Set AutoPropertyAttribute static PropertyChangedEventArgs fields
PropertyInfo[] props = type.GetProperties();
foreach (PropertyInfo prop in props)
{
    if (prop.GetCustomAttributes(typeof(
        AutoPropertyChangedNotificationAttribute), false).Length > 0)
    {
        if (!prop.PropertyType.Name.StartsWith("AutoPropertyChanged"))
        {
            throw new AutoPropertyChangedNotificationAttributeIncorrectlyUsedException(
              BuildExceptionMessage(type, prop, "The attribute " + 
              "[AutoPropertyChangedNotification] may only be used " + 
              "if the following property is an AutoPropertyChanged property.") );
        }
        if( prop.PropertyType.GetField("propertyChanged").GetValue(null)!=null )
        {
            throw new DuplicatePropertyFoundException(
              BuildExceptionMessage(type, prop, "AutoPropertyChanged properties " + 
              "must be distinguished within one class by the last template " + 
              "parameter (Property0...Property19)."));
        }
        prop.PropertyType.GetField("propertyChanged").SetValue(null, 
             new System.ComponentModel.PropertyChangedEventArgs(prop.Name));
    }
}

The most interesting line - aside from the exception section - is the line with the SetValue. That's where the static field of AutoPropertyChanged is initialized. And that's it.

The two checks directly above this line pinpoint typical error scenarios.

If you set more than one property of one class to the same PropertyN generic parameter, you'll receive a DuplicatePropertyFoundException. The exception message will describe which class and which property was the cause, so fixing this shouldn't be problematic.

The other situation is if the attribute [AutoPropertyChangedNotification] was set on a property which isn't of the type AutoPropertyChanged. In that case, a AutoPropertyChangedNotificationAttributeIncorrectlyUsedException is thrown (thanks to God, there is auto-completion). You'll also receive a message which is detailed enough to directly point you to the offending property.

The Demo Application

The demo application is greatly inspired by this article (thanks, Philip) - which, by the way, has one of the best TreeView implementations for WPF I ever encountered. I learned quite a lot about WPF by looking closer into this project Smile | :)

It looks like this:

Screenshot of Demo application

On the left hand side, there are some controls which interact with the controls on the right hand side (and vice versa).

The application itself uses the MVVM view model, having one ViewModel (DemoViewModel) and two Views (LeftView and RightView).

Since there's not that much code contained in it, I think it is probably best you explore the source yourself.

Summary

Pro's

  • Low run-time overhead (still more than coding by foot of course):
    • One additional field per property and
    • One reference per property to its parent
  • No dynamic creation of PropertyChangedEventArgs
  • Type safety and by this, refactoring safety
  • Quite easy to use

Con's

  • Property0...PropertyN required to tell different properties apart (but no runtime penalty for this)
  • Needs property exposing classes to be derived from one base class (I'm quite sure you could get rid of the base class, I'm looking into this)

Change log

  • 1.0: Initial public release (2010-05-24)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

WPFanatic

Germany Germany
I am working with computers for quite a long time now, starting with BASIC on an old "Acorn Electron".
 
Then i continued with C and moved quickly on to C++ with some Java in the mean time.
 
Now, my personal favorite is C# and i love especially WPF and its versatileness, as well as the TPL. I'm still on a quite steep learning curve, but thanks to this community (among others of course Smile | :) ) i'm doing okay.

Comments and Discussions

 
GeneralThere are other ways... [modified] PinmemberOleg Shilo26-May-10 0:29 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140721.1 | Last Updated 24 May 2010
Article Copyright 2010 by WPFanatic
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid