Home > English > INotifyPropertyChanged woes? Too much ceremony?

INotifyPropertyChanged woes? Too much ceremony?

One of the things that Sivlerlight, Windows Phone 7, Windows Presentation Foundation, and WinRT developers face is the issue of data binding. This is a powerful feature that XAML technologies give us out of the box but there is a painful pattern that you have to implement in any property in order to get two-way binding to work.

First, you must implement the INotifyPropertyChanged event. Next you need to go through all of your properties and make sure you call the OnPropertyChanged method that you have in your implementation. This is a pain and adds no value to the underlying structure of your object. The only reason we need this extra fluff is to support data binding.

Well, there is a better way. There are actually several solutions or products out on the market right now but I prefer one that is the least intrusive and expensive. Let me introduce you to the NotifyPropertyWeaver project.

Here is a sample of what your code has to look like in order to have your properties raise the PropertyChanged event properly:

public class Person : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public string GivenNames { get; set; }
    public string FamilyName { get; set; }

    public string FullName
    {
        get
        {
            return string.Format("{0} {1}", GivenNames, FamilyName);
        }
    }
}

This is really nice in that you don’t have to worry about any scaffolding or ceremony to get your properties to behave the way you would expect them. It also keeps your domain very clean and tight in that the only real fluff that you are exposing is the PropertyChanged event.

Here is what your code looks like when you compile it:

public class Person : INotifyPropertyChanged
{

    public event PropertyChangedEventHandler PropertyChanged;

    string givenNames;
    public string GivenNames
    {
        get { return givenNames; }
        set
        {
            if (value != givenNames)
            {
                givenNames = value;
                OnPropertyChanged("GivenNames");
                OnPropertyChanged("FullName");
            }
        }
    }

    string familyName;
    public string FamilyName
    {
        get { return familyName; }
        set
        {
            if (value != familyName)
            {
                familyName = value;
                OnPropertyChanged("FamilyName");
                OnPropertyChanged("FullName");
            }
        }
    }

    public string FullName
    {
        get
        {
            return string.Format("{0} {1}", GivenNames, FamilyName);
        }
    }

    public virtual void OnPropertyChanged(string propertyName)
    {
        var propertyChanged = PropertyChanged;
        if (propertyChanged != null)
        {
            propertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

As you can see, this pattern is typically what you would have had to have done in the first place but this now happens during compilation. The cool thing about this is that you have less code to maintain.

You can also have your objects derive from a base class that has the PropertyChanged event defined and anything else that you may want to have exposed in your base. Your base class would look like the following:

public class ModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    // Common logic here ....
}

Your derived class would look like the following:

public class Person : ModelBase
{
    public string GivenNames { get; set; }
    public string FamilyName { get; set; }

    public string FullName
    {
        get
        {
            return string.Format("{0} {1}", GivenNames, FamilyName);
        }
    }
}

Finally, your source code would look like the following after it has been compiled:

public class Person : ModelBase
{
    string givenNames;
    public string GivenNames
    {
        get { return givenNames; }
        set
        {
            if (value != givenNames)
            {
                givenNames = value;
                base.OnPropertyChanged("GivenNames");
                base.OnPropertyChanged("FullName");
            }
        }
    }

    string familyName;
    public string FamilyName
    {
        get { return familyName; }
        set
        {
            if (value != familyName)
            {
                familyName = value;
                base.OnPropertyChanged("FamilyName");
                base.OnPropertyChanged("FullName");
            }
        }
    }

    public string FullName
    {
        get
        {
            return string.Format("{0} {1}", GivenNames, FamilyName);
        }
    }
}

This is really nice in that we did not have to do any extra work to get this functionality. It also finds any dependencies and makes sure that the OnPropertyChange method is called for them as well. We can see an example of this with the FullName property. It is readonly but it should still be updated whenever the GivenNames or FamilyName properties are changed.

One of the reasons why I like this solution so much is that it uses a convention to provide the IL weaving. You are not required to have any attributes over your class or properties to get this to work. I find the solution simple and elegant in its assumptions and it typically represents the ways we would end up coding the INotifyPropertyChanged interface any ways.

Another nice aspect to this project is that it already supports the following toolkits:

  • Caliburn.Micro
  • Caliburn
  • MvvmLight
  • Prism
  • Cinch
  • Windows Phone Essentials

You also have the flexibility to use custom Attirbutes to let the compiler know what to weave as well.  I personally, don’t like this since I want to keep my domain as clean as possible but it does give you a lot of flexibility.

In order to get this working for your project, you have several options.  You can either download and install a VSIX Package and have this capability ready for any of your Visual Studio projects.  Or, you can use NuGet and bring in the package on demand in the project that you need.

There are other solutions out there on the market.  The biggest one that comes to mind is PostSharp.  With PostSharp, you can do even more than weaving for the property changed event.  There is a free version with it as well but I didn’t see a single implementation that didn’t require an attribute.

I will be going over the power of PostSharp in a later post so that we can compare the goodness of both implementations.

Hope this helps…

Advertisements
  1. September 27, 2011 at 2:14 pm

    Thanks for the information on the NotifyPropertyWeaver project. Personally, I would like a new keyword to represent this: observable.

    • September 27, 2011 at 2:28 pm

      Christopher,

      If you get a chance to watch the C# futures presentation during the Build conference. I believe they are introducing a concept that will get rid of all of this mess for us.

  2. September 27, 2011 at 2:15 pm

    What a pointless blog post!

    You can derive from your own base class that implements INotifyProperty changed, and you give an example of the most basic there is. Or you could use something like Prism that gives you the same functionality with the advanced feature of using Lambda Expressions to refer to properties, saving you from errors as a result of having your property values as strings in your source code.

    • September 27, 2011 at 2:31 pm

      Martin,

      I don’t think you are seeing the big picture. It doesn’t matter whether you have a lambda expression or a string name when you are using IL weaving. In fact, the string will be faster than the lambda expression. With this approach, you are not coding the implementation, the compiler does this for us. Therefore, we are not introducing bugs. I also mentioned that this weaving already supports tools like Prism.

      Matt

    • September 27, 2011 at 9:47 pm

      I guess you don’t have any idea of how slow the lambda expressions are. It’s a horrible idea, specially for Silverlight/Windows Phone.

      Also, even with lambdas you still have to write the actual RaiseProperty code. Why bother writing unnecessary code, when you can IL weave it? Only a stupid programmer writes more code than necessary! (while still keeping it clean & readable)

  3. September 27, 2011 at 4:27 pm

    That’s pretty much how I do. You really only have a problem when you have a class that needs to inherit from 2 classes. Then you have to get creative.

  4. September 27, 2011 at 11:41 pm

    @MartinRandall

    A few points
    -your approach does not support automatic properties
    -the lambda expression is strong typed but it does not prevent problems where u notify the wrong property or forget to do the notify

  5. September 28, 2011 at 7:56 am

    Yes I did miss the point of this post. I didn’t get it until I followed the link to IL weaver.

    OK – the one objection I have to this is code obscuring. I’m a freelance developer and I could quite easily write code like the first example above, have my NotifyPropertyWeaver plug-in installed in Visual Studio and everything works just fine. I check into the team repository and move on to the next job. Joe Developer checks out the code, does not have the plug in and yet the code still compiles OK but fails to work. He’s left scratching his head and cursing my name! I don’t really like the idea of code that you need a plug-in installed in order to make it work correctly – but your circumstances may differ.

    Nice idea, but on balance I think I will stick to Prism.

    As for Lambda’s version strings – yes strings are quicker, but how often is your code notifying of property changes? Is it really thousands of times a second that would make those extra clock cycles count?

  6. September 28, 2011 at 9:17 am

    @MartinRandall

    As per http://code.google.com/p/notifypropertyweaver/
    * No install required
    * No attributes required
    * No references required
    * No base class required

    Yes you can choose to install the VS Addin but it only does 2 things
    -makes it easier to inject the msbuild task into a project
    -handles updating the msbuild task to the latest version.

    The msbuild task assembly is the thing that does the work and it is 100% source control friendly. It requires nothing installed to work. So in the your scenario “Joe Developer” will have no problems. it will just work. It also works on a build server with nothing installed.

    Re “I will stick to Prism”. this is not about “not using prism”. It is about writing less code when using any tool kit that requires INPC.

    Re “Lambda is fast enough” agree 100% that approach is “fast enough”. but again this about writing less code not making things run faster. Although using NotifyPropertyWeaver results in code that is as fast as that coded by hand.

    • September 28, 2011 at 9:36 am

      I see. I saw VS Addin and assumed that it was required to make it work. I didn’t go into the details of how it introduced build tasks, etc. I can see how that can make things work for my scenario.

      I’m still not a fan as I think that you are obscuring the code somewhat, but that’s my argument against any IL weaving, not just this particular application of it.

  7. September 28, 2011 at 4:18 pm

    Guys, thanks for such a great discussion.

    Martin, I can see your concern about this possibly being black box and making it harder for someone to follow in your footsteps but I believe that by removing the “ceremony” of your code, you are actually helping anyone who follows you. Sure, you are going to need to have some guidance or a dev handbook that your developers need to be aware of. Once they understand it and learn of the advantages they can gain by maintaining less code, I believe that they will thank you for it.

  8. September 29, 2011 at 5:31 am
  9. September 30, 2011 at 3:56 pm

    Cool implementation of this age old idea :). I’ve tried this out a bit and see only two issues (other than the “obscuring the code somewhat” magic that Martin dislikes, and I won’t dismiss that lightly). One is a bug (I opened it on the bug DB). The generated OnPropertyChanged (configurable) is declared wrong. It’s always declared “public virtual”, while for non-sealed classes it should be “protected virtual” and for sealed classes it should be “private”. I think this is fairly serious… it shouldn’t be possible to raise the even outside of the class implementation, and this bug defeats that. The other issue has a workaround. The “magic” production of the OnPropertyChanged means that at compile time it doesn’t exist. You can’t manually call it, and FxCop will produce warnings about PropertyChanged never being used. Luckily, if you implement OnPropertyChanged yourself the weaver will gladly use your implementation, solving this issue.

  10. October 2, 2011 at 6:58 am

    @wekempf

    -You dont have to have NPW inject for all properties. You may choose the configuration over convention and put an attribute on each property that you want weaved. This way it is clear in the source code where OnPropertyChanged will be injected. to do this set TryToWeaveAllTypes to false http://code.google.com/p/notifypropertyweaver/wiki/WeavingTaskOptions and then you can use the NotifyPropertyAttribute http://code.google.com/p/notifypropertyweaver/wiki/Attributes
    -I fixed the public V Sealed issue http://code.google.com/p/notifypropertyweaver/issues/detail?id=89
    -I will not be changing the injected OnPropertyChanged to protected. This is public by design as it makes it easier for testing and mocking. If you wish to have define your own OnPropertyChanged that is “protected virtual” you may do so and NPW will detect that one and use it.

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: