Reputation: 5899
I was learning about ObservableCollections in WPF and something is not clear to me. I understand that if I bind a control to an ObservableCollection and the collection changes, the control will reflect the changes. My questions are:
ObservableCollection implements INotifyCollectionChanged, which is just an event, CollectionChanged. That event should be fired whenever the collection changes but who subscribes to that event? Is it done automatically when you create a binding to the collection?
I was looking at ObservableCollection using Reflector and was trying to see when the CollectionChanged event gets fired. However I couldn't find where it is done. For example, I wanted to see when it was fired when I added a new item to the collection. Add(...) is implemented in ObservableCollection's base class, Collection, but Collection does not implement INotifyCollectionChanged so I don't understand how bound controls are notified of the change.
I am guessing that a lot of this is taken care of under the covers but any info is greatly appreciated.
Upvotes: 1
Views: 2972
Reputation: 10373
To answer your first question: it all starts inside the "Items" property of the ItemContainerGenerator class (which, all ItemsControl objects have an instance of). If you look at the setter for the said "Items" property, you'll see that it has special logic that checks if the given IList is of type INotifyCollectionChanged, it will attach an event listener.
ItemContainerGenerator.Items property:
internal IList Items
{
get
{
return this._items;
}
set
{
if (this._items != value)
{
INotifyCollectionChanged source = this._items as INotifyCollectionChanged;
if ((this._items != this.Host.View) && (source != null))
{
CollectionChangedEventManager.RemoveListener(source, this);
}
this._items = value;
source = this._items as INotifyCollectionChanged;
if ((this._items != this.Host.View) && (source != null))
{
CollectionChangedEventManager.AddListener(source, this);
}
}
}
}
It really has nothing to do with Bindings. See, if you have the following code that doesn't use Bindings at all, the collection changed notification will still work:
<Window x:Class="DynamicObjectTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel>
<Button Click="Button_Click">click</Button>
<ListBox x:Name="listBox"/>
</StackPanel>
</Grid>
</Window>
public partial class MainWindow : Window
{
ObservableCollection items = new ObservableCollection();
public MainWindow()
{
InitializeComponent();
this.listBox.ItemsSource = items;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
items.Add("A");
}
}
Upvotes: 3
Reputation: 96722
That event should be fired whenever the collection changes but who subscribes to that event? Is it done automatically when you create a binding to the collection?
The Binding
object subscribes to the source's CollectionChanged
and/or PropertyChanged
events.
Upvotes: 1
Reputation: 64068
@Grant Crofton's answer provides where the "meat and potatoes" of INotifyCollectionChanged
is inside of ObservableCollection<T>
. However, to answer your first question you should read up on WPF and Binding Sources.
Basically, when you bind an object in WPF, it is checked against certain "contracts", one of which is INotifyCollectionChanged
. WPF handles attaching to the event and receiving the notifications. The Control
then determines how to respond when it is notified of an update to one of its DependencyProperty
objects.
Interestingly enough, WPF uses a View of a Collection rather than the collection itself in a binding:
WPF never binds directly to a collection. If you specify a collection as a binding source, WPF actually binds to the collection's default view. For information about default views, see Data Binding Overview.
Upvotes: 0
Reputation: 9101
From reflecting .Net 3.5, Collection<T>'s Add() method calls InsertItem:
public void Add(T item)
{
//...
int count = this.items.Count;
this.InsertItem(count, item);
}
InsertItem() is overridden in ObservableColletion<T>, which does the notifying:
protected override void InsertItem(int index, T item)
{
this.CheckReentrancy();
base.InsertItem(index, item);
this.OnPropertyChanged("Count");
this.OnPropertyChanged("Item[]");
this.OnCollectionChanged(NotifyCollectionChangedAction.Add, item, index);
}
Upvotes: 0
Reputation: 2874
Simple plain english answer:
ObservableCollections update controls they are databound to when objects are added or removed from the collections.
They do NOT update databinding when objects in the collection are modified.
Upvotes: 1