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.