Implementing INotifyPropertyChanged
One of the things you do regularly when working with Silverlight and/or WPF is implementing INotifyPropertyChanged.
In short: this interface exposes an event (PropertyChanged) that is raised when a property of the object changes its value. This allows other parts of your application to respond to changes in the class that implements the interface. For instance: a TextBlock shows the value of the Name property of a Person object and you want the TextBlock to update the text as soon as the Name property changes its value. So the Text property of the TextBlock need to be notified of changes to the Name property of the Person.
Here is a very basic implementation of the Person class:
1: public class Person : INotifyPropertyChanged
2: {
3: private string _name;
4:
5: public string Name
6: {
7: get { return _name; }
8: set
9: {
10: _name = value;
11: if (PropertyChanged != null)
12: {
13: PropertyChanged(this, new PropertyChangedEventArgs("Name"));
14: }
15: }
16: }
17: public event PropertyChangedEventHandler PropertyChanged;
18: }
As you can see in lines 11-14: if at least a single object subscribed to the PropertyChanged event the class raises the event passing itself as the source and the name of the property that changed. BTW: notice that in order to have the event raised you should use the property setter when writing to the property and not the field (_name) unless you want to suppress the event.
There a couple of things wrong with this implementation. I’ll fix them one by one.
First: when there are more properties we need to repeat the same code over and over in the setters so let’s factor out the raising of the event:
1: public class Person : INotifyPropertyChanged
2: {
3: private string _name;
4:
5: public string Name
6: {
7: get { return _name; }
8: set
9: {
10: _name = value;
11: OnPropertyChanged("Name");
12: }
13: }
14:
15: protected void OnPropertyChanged(string propertyName)
16: {
17: if (PropertyChanged != null)
18: {
19: PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
20: }
21: }
22: public event PropertyChangedEventHandler PropertyChanged;
23: }
The protected OnPropertyChanged method raises the event and can easily be called from within any setter in the class and its descendants.
Now a tricky error in this implementation. Suppose that between lines 17 and 19 the last subscriber to the event unregisters. This would clear the event’s invocation list and would cause a null reference exception in line 19. To make the method thread-safe we use the following construction:
1: protected void OnPropertyChanged(string propertyName)
2: {
3: PropertyChangedEventHandler p = PropertyChanged;
4: if (p != null)
5: {
6: p(this, new PropertyChangedEventArgs(propertyName));
7: }
8: }
It isn’t that obvious that this solves the concurrency problem. But it does. Here is how: the invocation list is immutable so as soon as there is a change to the list (someone registers or unregisters) a NEW list is created. So when we first copy (p in line 3) the reference, both references point to the same invocation list but when an object (un)registers the event will point to a different list but the copy still contains the original list.
On to the next problem. As you can see we specify the name of the changed property in the call to the OnPropertyChanged method as a literal string. This can cause problems when making spelling errors or when renaming the property without updating the literal string. Even worse: it fails silently.
1: public string FirstName
2: {
3: get { return _firstName; }
4: set
5: {
6: _firstName = value;
7: OnPropertyChanged("Name");
8: }
9: }
Although the event is raised it will communicate the wrong property name to the event subscriber.
So let’s solve this by using reflection:
1: public string Name
2: {
3: get { return _name; }
4: set
5: {
6: _name = value;
7: OnPropertyChanged(MethodInfo.GetCurrentMethod().Name.Substring(4));
8: }
9: }
The setter is a method that is prefixed with “set_” so that is why we cut off those first four characters.
This seems fine and it does work however we now pay a penalty due to the fact that reflection is slow. So let’s add an optimization:
1: public string Name
2: {
3: get { return _name; }
4: set
5: {
6: if (_name != value)
7: {
8: _name = value;
9: OnPropertyChanged(MethodInfo.GetCurrentMethod().Name.Substring(4));
10: }
11: }
12: }
This way (line 6) we only raise the event when there really is a change of the value. This addition is an optimization that is a good idea even when we do not use reflection. Now let’s try to get rid of the reflection:
1: public string Name
2: {
3: get { return _name; }
4: set
5: {
6: if (_name != value)
7: {
8: string propertyName = "Name";
9: Debug.Assert(
10: propertyName == MethodInfo.GetCurrentMethod().Name.Substring(4),
11: String.Format("Error in property setter: {0}",
12: MethodInfo.GetCurrentMethod().Name.Substring(4));
13: _name = value;
14: OnPropertyChanged(propertyName);
15: }
16: }
17: }
The Debug comes to the rescue: Assert and its parameters will only be evaluated and called when running the Debug version of the assembly is being executed. So this solution requires unit tests to make sure that the assertions are evaluated before building the release version.
When renaming the property and forgetting to change line 8 with the correct value an exception will be thrown when running the debug build version.
Our simple property has grown quite a bit and now requires a lot of typing. I see two valid solutions for that:
- An item template for a class that implements INotifyPropertyChanged and a snippet for a property that raises the PropertyChanged event.
- Code generation using a tool or T4 templates (I might add a post on that later)
I picked the first option for the time being. My template:
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;
namespace $rootnamespace$
{
public class $safeitemname$ : INotifyPropertyChanged
{
#region INotifyPropertyChanged Implementation
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler p = PropertyChanged;
if (p != null)
{
p(this, new PropertyChangedEventArgs(propertyName));
}
}
private static readonly string ERROR_PROP_NOT_FOUND = "Property name {0} not found.n" +
"Possible cause: renaming the property {1}.{2} from {0} to {2} without " +
"updating the value of the propertyName variable in setter.";
#endregion
}
}
And the snippet:
<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets >="http://schemas.microsoft.com/VisualStudio/2008/CodeSnippet">
<CodeSnippet Format="1.0.0">
<Header>
<Title>Property Changed</Title>
<Description>Insert Property that supports INotifyPropertyChanged</Description>
<Shortcut>proppc</Shortcut>
</Header>
<Snippet>
<Declarations>
<Literal>
<ID>type</ID>
<ToolTip>Property type</ToolTip>
<Default>int</Default>
</Literal>
<Literal>
<ID>field</ID>
<ToolTip>Field</ToolTip>
<Default>myField</Default>
</Literal>
<Literal>
<ID>property</ID>
<ToolTip>Property name</ToolTip>
<Default>MyProperty</Default>
</Literal>
</Declarations>
<Code Language="CSharp">
<![CDATA[#region $property$ Property
private $type$ _$field$;
public $type$ $property$
{
get
{
return _$field$;
}
set
{
if(_$field$ != value)
{
string propertyName = "$property$";
Debug.Assert(
propertyName == MethodInfo.GetCurrentMethod().Name.Substring(4),
String.Format(ERROR_PROP_NOT_FOUND, propertyName,
this.GetType().Name,
MethodInfo.GetCurrentMethod().Name.Substring(4)));
_$field$ = value;
OnPropertyChanged(propertyName);
}
}
}
#endregion
]]>
</Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>
When adding a new class to a WPF or Silverlight application (e.g. when adding a class to the ViewModel in MVVM) I now get to pick a new template:
And when coding a property I use proppc:
I am quite happy with this. There are other concerns such as:
- When updating the property a lot (animation) this code generates a lot of EventArgs objects littering the heap.
- The check for the correct name is done only in the Debug build.
Thoughts and solutions can be found here:
- http://justinangel.net/AutomagicallyImplementingINotifyPropertyChanged
- http://jeffhandley.com/archive/2008/10/07/inotifypropertychanged—-searching-for-a-better-way.aspx
- http://thetreeknowseverything.wordpress.com/2009/01/21/auto-implement-inotifypropertychanged-with-aspects/
- http://joshsmithonwpf.wordpress.com/2007/08/29/a-base-class-which-implements-inotifypropertychanged/
You can download my snippet and template here.
Copy the snippet to: <drive>:Users<user>DocumentsVisual Studio 2010Code SnippetsVisual C#My Code Snippets
Copy the zipped template to: <drive>:Users<user>DocumentsVisual Studio 2010TemplatesItemTemplatesVisual C#
NB: Please also read my next post