Introduction
Data binding is a fantastic feature of many components in the development world. The ability to connect the value of a property on a data object to some other property on another component can save a lot of time and code, if it works. .NET 2.0 brings many new features to the table and has cleaned up some old features. The INotifyPropertyChanged
is a powerful new interface in the System.Component
namespace.
The INotifyPropertyChanged
interface provides a standard way to notify binding clients of a property value change. In the past, it was sometimes hit-or-miss as to when or whether a binding client would recognize a property change on a data object. If developers did not know how to bend the data bound client to their will, then a lot of nasty code often followed, along with some very foul words about hating data binding. Implementing INotifyPropertyChanged
will help you keep your code clean and get the most out of the hard work that other developers have done to implement binding clients.
Special thanks to Jason Wergin for the base class code, and for pointing out this new and very useful interface.
A Quick Look at INotifyPropertyChanged
The interface itself, like most elegant solutions, is disgustingly simple. It has no properties and no methods. It has just one event, with a simple and perfectly clear name, PropertyChanged
. The delegate for this event has two parameters. The first, of course, is the sender,
and the second, the PropertyChangedEventArgs
, which has one property, PropertyName
. As I said, “Simple”.
A Simple Base Class
Indeed the interface is so simple that you could write a very useful base class to speed the implementation of this interface across any data objects you would like to bind. Below is a simple base class that we use in some of the products that I am working on.
public abstract class NotifyProperyChangedBase : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region methods
protected bool CheckPropertyChanged<T>
(string propertyName, ref T oldValue, ref T newValue)
{
if (oldValue == null && newValue == null)
{
return false;
}
if ((oldValue == null && newValue != null) || !oldValue.Equals((T)newValue))
{
oldValue = newValue;
FirePropertyChanged(propertyName);
return true;
}
return false;
}
protected void FirePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
The class wraps the logic to see if a value is changing in a generic method, and also provides a standard method call for raising the PopertyChanged
event, all very simple and very useful. Now implementing the INotifyPropertyChanged
interface is as simple as inheriting this base class and calling these two methods when needed.
Now Let’s See an Example
In this example, we are going to create two data objects, one that implements INotifyPropertyChanged
and one that does not. Then we will bind instances of them to list boxes and see how differently they behave (see download for demo code).
- Open up Visual Studio 2005 and create a new C# Windows Forms project and name it
INotifyDemo
. - In the same solution, create a new C# class library project and name it
MyComponentModel
. This project just represents the framework library that we should put our useful NotifyProperyChangedBase
class in. - Now add a new class to the
MyComponentModel
project and name it NotifyProperyChangedBase
. The base class code laid out in the previous section of this article should be copied into the class file and saved. - Right click on the
MyComponentModel
project in the solution explorer and click the Build
menu item to compile the project.
Now we are going to create the two data objects. These are going to be simple person objects, one will use our new base class thereby implementing INotifyPropertyChanged
, and the other will be a plain old regular object.
Add a new class to the INotifyDemo
project and name it RegularPerson
. Put the following code in the class:
class RegularPerson
{
private string _firstName;
public string FirstName
{
get { return _firstName; }
set { _firstName = value; }
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set { _lastName = value; }
}
public string DisplayName
{
get { return _firstName + " " + _lastName; }
}
}
As you can see, it is a very vanilla data class that holds the name of a person.
Now we need to create a similar class that inherits our useful base class which implements the INotifyPropertyChanged
interface. So, add another new class to the INotifyDemo
project and name it NotifiablePerson
. Put the following code in the class:
class NotifiablePerson : MyComponentModel.NotifyProperyChangedBase
{
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
if (this.CheckPropertyChanged<string>
("FirstName", ref _firstName, ref value))
{
this.DisplayNameChanged();
}
}
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set
{
if (this.CheckPropertyChanged<string>
("LastName", ref _lastName, ref value))
{
this.DisplayNameChanged();
}
}
}
public string DisplayName
{
get { return _firstName + " " + _lastName; }
}
private void DisplayNameChanged()
{
this.FirePropertyChanged("DisplayName");
}
}
In this class, we inherit the NotifyPropertyChangedBase
class. In the setters for the FirstName
and LastName
properties, we use the generic CheckPropertyChanged
method to set the class attribute that holds the property value and we raise the PropertyChanged
event if needed. Also, if the property value indeed changes, then the read only property DisplayName
also changes. So, we use the FirePropertyChanged
method to inform the binding client of the change. Simple enough.
Now drag two groupboxes, two listboxes, and four textboxes onto Form1
and arrange them so the form looks like this:
With the form laid out, you need to wire up event handlers for the form Load
event and for the TextChanged
event on each of the text boxes. For each of the listboxes, set the DisplayMemeber
property to DisplayName
and the ValueMemeber
property to FirstName
. Now add code to Form1
until it looks like this:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
LoadRegularList();
LoadNotiList();
}
private void LoadRegularList()
{
System.ComponentModel.BindingList<RegularPerson> rl =
new System.ComponentModel.BindingList<RegularPerson>();
RegularPerson rp = rl.AddNew();
rp.FirstName = "John";
rp.LastName = "Smith";
RegularPerson rp2 = rl.AddNew();
rp2.FirstName = "Joe";
rp2.LastName = "Shmoe";
listBox1.DataSource = rl;
}
private void LoadNotiList()
{
System.ComponentModel.BindingList<NotifiablePerson> nl =
new System.ComponentModel.BindingList<NotifiablePerson>();
NotifiablePerson np = nl.AddNew();
np.FirstName = "Jane";
np.LastName = "Doe";
NotifiablePerson np2 = nl.AddNew();
np2.FirstName = "Mary";
np2.LastName = "Smith";
listBox2.DataSource = nl;
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
if (listBox1.SelectedItem != null)
{
((RegularPerson)listBox1.SelectedItem).FirstName = textBox1.Text;
}
}
private void textBox2_TextChanged(object sender, EventArgs e)
{
if (listBox1.SelectedItem != null)
{
((RegularPerson)listBox1.SelectedItem).LastName = textBox2.Text;
}
}
private void textBox3_TextChanged(object sender, EventArgs e)
{
if (listBox2.SelectedItem != null)
{
((NotifiablePerson)listBox2.SelectedItem).FirstName = textBox3.Text;
}
}
private void textBox4_TextChanged(object sender, EventArgs e)
{
if (listBox2.SelectedItem != null)
{
((NotifiablePerson)listBox2.SelectedItem).LastName = textBox4.Text;
}
}
}
On this form, we now have two lists whose item values we can manipulate. The top section edits the FirstName
and LastName
of the selected RegularPerson
item in the list and the bottom section edits the same properties of the selected NotifiablePerson
item in its list.
Build and run INotifyDemo
.
Try selecting an item in the “Regular Person” list and typing a new name into either the “First Name” or “Last Name” text boxes. You will notice that the item text in the list does not change even though the underlying value has changed. You can set a break point on one of the TextChanged
events and see that the value has indeed changed.
Now select an item in the “Notifiable Person” list and type a new name into either the “First Name” or “Last Name” text boxes. TA-DA! The item text in the list changes immediately. The binding client, in this case the list box, was informed via the PropertyChanged
event and did its job perfectly; no need to write ugly code to get the listbox item text to refresh.
Conclusion
You can expect this kind of good behavior from all binding clients when you implement INotifyPropertyChanged
. Create a good solid base class (like the one in this article) that implements the interface and use it on any object you wish to bind. It will allow you to take full advantage of binding clients and save you from having to write unnecessary and ugly code.
NOTE: We also used the generic BindingList
; this is also recommended when binding collections to clients.
History
- 5th October, 2006: Initial post
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.