The article describes how you can implement your class using INotifyPropertyChanged, INotifyPropertyChanging to work on your objects and INotifyCollectionChanged on your collection.
The article also describes the common mistakes and workarounds how you can handle notification scenarios in real world scenarios.
Introduction
Classes are the building blocks for any programming language. Even though we use class to define our custom business logic and apply them, but the most important utility of a class is to store data blocks within the object itself. C# classes are capable of storing data using fields, but they produced one step more abstraction level by introducing the Property System. Properties are elements that are defined to wrap data members. Thus anything that we need to expose through the object should be wrapped around using these properties. As a rule we don’t expose any fields as public for a class, rather we create a property and expose that to the outside world, so that the developers can easily impose one level of abstraction by not exposing the actual data.
Introduction of Property System
Properties has two methods.
Get and
Set.
Get is called whenever data is fetched using the property, whereas Set is called whenever the property value is
set. There is an implicit variable
value which allows you to grab the data sent from the external world to the property setter. Thus typically an example of property is :
private int _data;
public int Data
{
get { if (this._data == 0) this._data = 50; return this._data; }
set
{
if (value >= 0)
this._data = value;
}
}
Here in the above example you can see, I have wrapped around a property element from the external world using a
property. Thus I have also implemented our own custom logic around the
data when it is get or set from the
property.
It is always recommended to expose a data element using property even though you don't have any more to write than get or set the value to private variables. For those which doesn't need to write any custom logic you can use the implicit
Property descriptor feature introduced with C# lately which doesn't need you to declare private variable for your property.
public int Data { get; set; }
The above line will work just like private fields, so CLR will automatically generate a private anonymous field for you during run time to store the value of the property.
Little about Events
Another important object which you often need are events. Events are custom calls to the outside world which might call automatically a method which is registered to that particular event. Therefore, if you define an event, and want the person who creates an object of it need to call a method when a particular task is performed, you might go for events. Like properties, events are also exposed to outside using Event Accessors. Event accessors help the outside world to register for the events. Thus rather than exposing the whole event directly you can define your custom
event accessor which will be called whenever the event is registered. The return type of an event is a delegate. You may say a delegate as a Function pointers where you can pass a function.
Let us take an example of Event :
private EventHandler _myEvent;
public event EventHandler MyEvent
{
add { this._myEvent += value; }
remove { this._myEvent -= value; }
}
In the above example you can see that the
_myEvent is kept as private while I have exposed the Event Accessor
MyEvent to the external world. The
EventHandler is the delegate which the outside world should pass to register the event. The type of the Value to add and remove method of the event accessor is the function of type EventHandler.
To read more about delegates and event handling you might read one practical example from my blog:
http://www.abhisheksur.com/2010/01/how-to-expose-events-from-usercontrol.html
Property Notifiers
Property Notifiers are actually an event which are exposed through an interface called
INotifyPropertyChanged and
INotifyPropertyChanging. If you implement these interfaces you will automatically need to invoke an event whenever you require the property to notify the external world when the object is modified.
With this cool feature, Microsoft actually mixed up the two concepts that I have discussed just before this and introduced an unified model using these two interfaces. Now I am going to show you how you can implement these interfaces.
Now let us define a student class which declares 5 properties :
1.
Name, 2.
Company, 3.
Designation, 4.
MonthlyPay and 5.
AnnualPayAnnualPay defines only the Getter, so it is
readonly and which actually calculates the value from
MonthlyPay and returns back the value. Now let us see how I have implemented the class :
public class Student : INotifyPropertyChanged, INotifyPropertyChanging
{
#region INotifyPropertyChanged Members
private event PropertyChangedEventHandler _propertyChanged;
public event PropertyChangedEventHandler PropertyChanged
{
add { this._propertyChanged += value; }
remove { this._propertyChanged -= value; }
}
protected virtual void OnPropertyChanged(string propertyName)
{
if (this._propertyChanged != null)
this._propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
#region INotifyPropertyChanging Members
private event PropertyChangingEventHandler _propertyChanging;
public event PropertyChangingEventHandler PropertyChanging
{
add { this._propertyChanging += value; }
remove { this._propertyChanging -= value; }
}
protected virtual void OnPropertyChanging(string propertyName)
{
if (this._propertyChanging != null)
this._propertyChanging(this, new PropertyChangingEventArgs(propertyName));
}
#endregion
private string _name;
public string Name
{
get { return this._name; }
set
{
this.OnPropertyChanging("Name");
this._name = value;
this.OnPropertyChanged("Name");
}
}
private string _company;
public string Company
{
get { return this._company; }
set
{
this.OnPropertyChanging("Company");
this._company = value;
this.OnPropertyChanged("Company");
}
}
private string _designation;
public string Designation
{
get { return this._name; }
set
{
this.OnPropertyChanging("Designation");
this._designation = value;
this.OnPropertyChanged("Designation");
}
}
private Single _monthlypay;
public Single MonthlyPay
{
get { return this._monthlypay; }
set
{
this.OnPropertyChanging("MonthlyPay");
this.OnPropertyChanging("AnnualPay");
this._monthlypay = value;
this.OnPropertyChanged("MonthlyPay");
this.OnPropertyChanged("AnnualPay");
}
}
public Single AnnualPay
{
get { return 12 * this.MonthlyPay; }
}
}
Here in the first section, I have implemented the interfaces
INotifyPropertyChanged and
INotifyPropertyChanging. To implement the interfaces, I actually have to define a variable and expose the event using an Event Accessor already defined within the Interface.
I have also declared one virtual method which is actually needed to call the event, so that we first check if the external world has actually registered the event or not.
protected virtual void OnPropertyChanging(string propertyName)
{
if (this._propertyChanging != null)
this._propertyChanging(this, new PropertyChangingEventArgs(propertyName));
}
Thus we check if the event has method registered. And if so, then we call that event. PropertyChanging event actually requires the name of the property which is changed. So we pass the name of the string whenever we need to update the property.
In case of properties which generates output based on the value of another variable like AnnualPay here, we need to invoke the events when the dependent property is changed.
You can see I have invoked the event when MonthlyPay is changed. So if anyone registers for AnnualPay, he will get notified when the MontlyPay gets changed.
It is very important to use Property Notifiers as many of the entities and controls (such as Binding in WPF) are totally dependent on these systems. We will see one practical example later on with the sample application.
Collection Notification
Nowadays a large number of collection being used very widely. Each of them are specific to its usage. Collection notification is also sometimes very necessary when we need to invoke an event when the collection element or when the collection itself is modified from the external world. Microsoft introduces another cool Interface called
INotifyCollectionChanged which you might use to implement your own collection so that whenever the collection is changed, you will get notified automatically.
Microsoft also created a collection for us just following the Notification system called
ObservableCollection<T> which is actually very popular nowadays, as most of the UIElements and WCF objects needed these notification to update itself.
In this article, I will focus how you can define your custom Collection notifier rather than using the already existing
ObservableCollection<T> as you might need it sometimes. To do that, let us create a collection of our Student class called Students, and later on we will try to use them in our sample application.
public class Students : IEnumerable<Student>, INotifyCollectionChanged, INotifyPropertyChanged, ICollection<Student>
{
#region INotifyCollectionChanged Members
private event NotifyCollectionChangedEventHandler _collectionChanged;
public event NotifyCollectionChangedEventHandler CollectionChanged
{
add { this._collectionChanged += value; }
remove { this._collectionChanged -= value; }
}
protected virtual void OnCollectionChanged(NotifyCollectionChangedAction action)
{
if (this._collectionChanged != null)
this._collectionChanged(this, new NotifyCollectionChangedEventArgs(action));
}
protected virtual void OnCollectionChanged(NotifyCollectionChangedAction action, Student item, int index)
{
if (this._collectionChanged != null)
this._collectionChanged(this, new NotifyCollectionChangedEventArgs(action, item, index));
}
protected virtual void OnCollectionChanged(NotifyCollectionChangedAction action, List<Student> itemlist)
{
if (this._collectionChanged != null)
this._collectionChanged(this, new NotifyCollectionChangedEventArgs(action, itemlist));
}
#endregion
private List<Student> _list = new List<Student>();
public void Add(Student stud)
{
this._list.Add(stud);
this.OnPropertyChanged("Count");
this.OnCollectionChanged(NotifyCollectionChangedAction.Add, stud, 0);
}
public void RemoveAt(int index)
{
Student stud = this._list[index];
this._list.RemoveAt(index);
this.OnPropertyChanged("Count");
this.OnCollectionChanged(NotifyCollectionChangedAction.Remove, stud, index);
}
public void Remove(Student stud)
{
int index = this._list.IndexOf(stud);
this._list.RemoveAt(index);
this.OnPropertyChanged("Count");
this.OnCollectionChanged(NotifyCollectionChangedAction.Remove, stud, index);
}
public void Clear()
{
List<Student> lst = new List<Student>();
lst.AddRange(this._list);
this._list.Clear();
this.OnPropertyChanged("Count");
this.OnCollectionChanged(NotifyCollectionChangedAction.Reset, lst);
}
public void Sort(bool IsDescending)
{
IOrderedEnumerable<Student> sortedList = IsDescending ? this._list.OrderByDescending<Student, string>(item => item.Name) : this._list.OrderBy<Student, string>(item => item.Name);
this._list = sortedList.ToList<Student>();
this.OnCollectionChanged(NotifyCollectionChangedAction.Move);
}
public void Replace(Student stud, int index)
{
this._list.Insert(index, stud);
this._list.RemoveAt(index + 1);
this.OnCollectionChanged(NotifyCollectionChangedAction.Replace);
}
public bool Contains(Student item)
{
return this._list.Contains(item);
}
public void CopyTo(Student[] array, int arrayIndex)
{
this._list.CopyTo(array, arrayIndex);
List<Student> studarr = new List<Student>();
studarr.AddRange(array);
this.OnCollectionChanged(NotifyCollectionChangedAction.Add, studarr);
}
public bool IsReadOnly
{
get { return false; }
}
public void Replace(Student stud, int index)
{
this._list.Insert(index, stud);
this._list.RemoveAt(index + 1);
this.OnCollectionChanged(NotifyCollectionChangedAction.Replace);
}
public int Count
{
get
{
return this._list.Count;
}
}
public Student this[int index]
{
get
{
return this._list[index];
}
}
#region IEnumerable<Student> Members
public IEnumerator<Student> GetEnumerator()
{
foreach (Student stud in this._list)
yield return stud;
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this._list.GetEnumerator();
}
#endregion
#region INotifyPropertyChanged Members
private event PropertyChangedEventHandler _propertyChanged;
public event PropertyChangedEventHandler PropertyChanged
{
add { this._propertyChanged += value; }
remove { this._propertyChanged -= value; }
}
protected virtual void OnPropertyChanged(string propertyName)
{
if (this._propertyChanged != null)
this._propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
In the above code, I have implemented the class Student from
INotifyCollectionChanged interface. The collection actually is a true implementation of a collection class which uses List to store the objects. You can easily use Array instead of generic list to build a very low level collection implementation.
Each collection has few operation which the collection must possess to become a true collection. The method which one must declare to become a true collection are Add, Remove, Contains, Clear etc. You might find them in the
ICollection interface. The notifier event for the collection takes
NotifyCollectionChangedAction which should be defined appropriately to create your class. You can see, I have used few overloads on the event invoker method to ensure that we could call the most appropriate method while calling the event.
NotifyCollectionChangedAction.Add : It will be called when new object is added. Remember, you need to pass the object as well as the index where it is inserted when creating object of
NotifyCollectionChangedEventArgs.
NotifyCollectionChangedAction.Remove : Takes same number of argument as Add and should be called when an object is removed from the collection.
NotifyCollectionChangedAction.Reset : Takes the whole list which reset operation is performed. You should call it when the entire collection is recreated. I have used this in case of Clear.
NotifyCollectionChangedAction.Replace : Takes one single object with an index, where the object is replaced.
NotifyCollectionChangedAction.Moved: Same as replace.
Thus I think you have clear idea on how you can implement the object. Please note, I have also implemented the collection class with
INotifyPropertyChanged as well, so that the Count and other properties also notify itself whenever the collection is modified.
Sample Application
In this sample application I have just created the list of items, register the events for each item and tried to see what events are generated in what scenario. Lets take a look at the sample application:
Here you can see a
ListBox is bound with the Students Collection that we have created. The
ListBox shows all its data using
DataTemplate.
As each data element is an object of Student which implements
INotifyPropertyChanged, the original data will update itself whenever the data in those textbox is updated. This is the best example of Property Binding.
In WPF, Binding needs the object to be implemented from
INotifyPropertyChanged, which is automatically done in case of
DependancyProperties. So here if I update any element the Data which corresponds to the element is actually updated.
The lower panel shows how the individual and Collection event occurs.
void stud_PropertyChanging(object sender, System.ComponentModel.PropertyChangingEventArgs e)
{
lstLog.Items.Add(string.Format("{0} is changing for object {1}", e.PropertyName, sender.ToString()));
}
void stud_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
lstLog.Items.Add(string.Format("{0} is changed for object {1}", e.PropertyName, sender.ToString()));
}
void _students_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
lstLog.Items.Add(string.Format("{0} action taken on Collection {1}", e.Action.ToString(), sender.ToString()));
}
You can see the three events actually produces a separate entry into the ListBox at the bottom of the screen.
Now Let us Add a new Item from the UI.
Here two events get generated when I add a new Student in the Collection. You can see, as I have implemented the collection using
INotifyCollectionChanged, the addition of new element on the Collection actually updates the
ListBox. Hence the code on the Add button looks like
private void btnAdd_Click(object sender, RoutedEventArgs e)
{
this._students.Add(new Student { Name = txtName.Text, Company = txtComp.Text, Designation = txtDesig.Text });
}
In this way when I remove an item from the ListBox, the events will invoke as in the figure.
If you see the code for removing an item, it looks like :
private void btnRemove_Click(object sender, RoutedEventArgs e)
{
Student stud = this.lstCustomListBox.SelectedItem as Student;
if (stud != null)
{
this._students.Remove(stud);
}
}
Thus removing an item is also very easily handled. I have just removed the item from the List, and the rest will be automatically done by the ListBox.
You can try out the sample application to see how actually the code works.
Conclusion
Thus you can see any change notifications can easily be handled using these interfaces. The
INotifyPropertyChanged and
INotifyCollectionChanged are very handy in many real world situations. I hope this article gives you a good knowledge how you can implement this in real world applications.
Thank you for reading.