Change Notification For Objects and Collections

Abhi2434
Posted by in C# category on for Beginner level | Views : 34207 red flag
Rating: 4.6 out of 5  
 5 vote(s)

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.


 Download source code for Change Notification For Objects and Collections

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

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.

Event System

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.

NotifyProperty System

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. AnnualPay

AnnualPay 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.

Collection Notifier

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.

Page copy protected against web site content infringement by Copyscape

About the Author

Abhi2434
Full Name: Abhishek Sur
Member Level: Silver
Member Status: Member,Microsoft_MVP,MVP
Member Since: 12/2/2009 4:19:08 AM
Country: India
www.abhisheksur.com
http://www.abhisheksur.com
Working for last 2 and 1/2 years in .NET environment with profound knowledge on basics of most of the topics on it.

Login to vote for this post.

Comments or Responses

Posted by: Kunal2383 on: 5/16/2010
Nice Article Abhishek. Also, glad to see my name in the snapshot... ha hah.. :)

Regards,
Kunal

Posted by: Abhi2434 on: 5/16/2010
Yes bro... :) :)

Thanks for your appreciation.

Cheers

Login to post response

Comment using Facebook(Author doesn't get notification)