ObservableCollection vs BindingList
14 March 2015

To bind or not to bind: that is the question…

If you are using Silverlight (my deepest condolences) you have no choice of using BindingList. WPF has it, but you should be aware that BindingList is maybe doing more than you expect and want.

To show the difference let’s start with ObservableCollection first. Assume we have class TestData with property Text implemented to fire notification when changed:

public class TestData : NotifyObject
{
public int Number { get; set; }
private string _text;
public string Text
{
get { return _text; }
set {
_text = value;
NotifyPropertyChanged("Text");
}
}
public TestData(int i)
{
Number = i;
Text = "Row" + i;
}
public override string ToString()
{
return " <" + Text + ">";
}
}
public abstract class NotifyObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}

In order to bind list of TestData to a grid we have following code to create _items as ObservableCollection. Binding to a gird could be done from xaml or code… your choice.

private ObservableCollection<TestData> _items;
private TestData _secondElement;
public NewWindow()
{
InitializeComponent();
_items = new ObservableCollection<TestData>();
_items.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(ItemsCollectionChanged);
for (int i = 0; i < 10; i++)
{
_items.Add(new TestData(i));
}
_secondElement = _items[1];
}

We are also monitoring all notifications on ObservableCollection by hooking on CollectionChanged event. Handler will dump event details as per code below:

void ItemsCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
Console.WriteLine("***** ItemsCollectionChanged *****");
Console.WriteLine("Action = " + e.Action);
Console.WriteLine("OldStartingIndex = " + e.OldStartingIndex);
Console.WriteLine("Old Items:");
if (e.OldItems != null)
{
foreach (var item in e.OldItems)
{
Console.WriteLine(item.ToString());
}
}
Console.WriteLine("NewStartingIndex = " + e.NewStartingIndex);
Console.WriteLine("New Items:");
if (e.NewItems != null)
{
foreach (var item in e.NewItems)
{
Console.WriteLine(item.ToString());
}
}
}

On a button click we will execute code changing _items collection. Replace, Add and Remove are changing collection, but second change is replacing Text property only:

private void ModifyItem_OnClick(object sender, RoutedEventArgs e)
{
_items[2] = new TestData(33); // Replace
_secondElement.Text = "Second Element"; // Change One Property
_items.Add(new TestData(55)); // Add
_items.RemoveAt(3); // Remove
}

Let’s observe the output:

***** ItemsCollectionChanged ***** Action = Replace OldStartingIndex = 2 Old Items: <Row2> NewStartingIndex = 2 New Items: <Row33> ***** ItemsCollectionChanged ***** Action = Add OldStartingIndex = -1 Old Items: NewStartingIndex = 10 New Items: <Row55> ***** ItemsCollectionChanged ***** Action = Remove OldStartingIndex = 3 Old Items: <Row3> NewStartingIndex = -1 New Items:

Although 4 changes are initiated on _items collection, only 3 are logged. ObservableCollection didn’t react on changing Text property. Still cell on the grid changed because there is a binding between grid cell and property Text. Property Text sent notification and grid cell picked up change. All four changes are properly displayed on the grid.

Now replace ObservableCollection with BindingList and monitor ListChanged event:

_items = new BindingList<TestData>();
_items.ListChanged += new ListChangedEventHandler(ItemsListChanged);
void ItemsListChanged(object sender, ListChangedEventArgs e)
{
Console.WriteLine("***** ItemsListChanged *****");
Console.WriteLine("Change Type = " + e.ListChangedType);
Console.WriteLine("OldIndex Index = " + e.OldIndex);
Console.WriteLine("New Index = " + e.NewIndex);
Console.WriteLine("Property = " + ((e.PropertyDescriptor != null) ? e.PropertyDescriptor.Name : ""));
}

Executing same code to change collection will now record 4 changes:

***** ItemsListChanged ***** Change Type = ItemChanged OldIndex Index = -1 New Index = 2 Property = ***** ItemsListChanged ***** Change Type = ItemChanged OldIndex Index = 1 New Index = 1 Property = Text ***** ItemsListChanged ***** Change Type = ItemAdded OldIndex Index = -1 New Index = 10 Property = ***** ItemsListChanged ***** Change Type = ItemDeleted OldIndex Index = -1 New Index = 3 Property =

That is the big difference! Changing property Text will be also propagated to a collection level and this is something ObservableCollection didn’t do. This is probably overhead you don’t need, not to mention that it looks like this implementation doesn’t scale well.

The moral of the story: Use ObservableCollection when working with WPF.