Apr
28
2009

An Introduction to ObservableCollection in WPF

The ObservableCollection<T> is one of the most important features of WPF data binding. From the documentation:

Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed.

Most people seem confused about this, so it’s worth a clear note. An ObservableCollection is just like a regular collection. If you check the documentation, you can see that ObservableCollection is defined as:

public class ObservableCollection<T> : Collection<T>, INotifyCollectionChanged, INotifyPropertyChanged

So you have access to all the members you would have in Collection<T>. Again, refer to the documentation for the list of all those members.

Notice that, like any collection that derives from Collection<T>, its methods (namelly Add and Remove) accept null parameters and do not throw an exception.

But the main feature of the ObservableCollection<T> are the events it raises when the items it contains change. By implementing the INotifyCollectionChanged and INotifyPropertyChanged interfaces, the collection has events for CollectionChanged and PropertyChanged. All these events are related. The first one is raised whenever something changed in the collection, be it Add, Remove, Move, etc. This also trigger the PropertyChanged event for the Items[] property. When you’re adding or removing items, PropertyChanged is also raised for the Count property.

But enough theory, here’s a small example of how to use it. First of all, we start with our domain model, the Person.

namespace PSampaio.ObservableCollectionSample
{
	public class Person
	{
		public string FirstName { get; set; }
		public string LastName { get; set; }
		public string FullName { get { return string.Format("{0} {1}", FirstName, LastName); } }
	}
}

Next, we have a ViewModel that uses this domain model.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows.Input;

namespace PSampaio.ObservableCollectionSample
{
	public class PickOneViewModel
	{
		readonly Random random = new Random();

		public List<Person> AvailablePeople { get; private set; }
		public ICommand PickOnecommand { get; private set; }
		public ObservableCollection<Person> SelectedPeople { get; private set; }

		public PickOneViewModel()
		{
			AvailablePeople = new List<Person>
			{
				new Person {FirstName = "John", LastName = "Doe"},
				new Person {FirstName = "Michael", LastName = "Jones"},
				new Person {FirstName = "Jane", LastName = "Smith"},
			};

			PickOnecommand = new DelegateCommand<object>(obj =>
			{
				int availablePersonIndex = random.Next(0, AvailablePeople.Count);
				SelectedPeople.Add(AvailablePeople[availablePersonIndex]);
			});

			SelectedPeople = new ObservableCollection<Person>();
		}
	}
}

This PickOneViewModel has three properties, that we will bind to in the Window XAML. AvailablePeople is just a list of the people available to be selected. It’s just a standard List because we do not want to update it, just use it as a source for SelectedPeople. PickOneCommand is a DelegateCommand. This delegate command was based on the CompositeWPF class with the same name, and lets the you create an ICommand that’s defined with delegates, instead of CommandBindings. Finally, SelectedPeople is an ObservableCollection that starts empty and where we will be adding the (random) person selected each time we execute PickOneCommand.

The UI for this sample is fairly simple. Just add the ViewModel as an ObjectDataProvider to the window resources and use it as a DataSource for the window contents.

<Window
	x:Class="PSampaio.ObservableCollectionSample.Window1"
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:Sample="clr-namespace:PSampaio.ObservableCollectionSample"
	Title="Window1"
	Width="300"
	Height="300">

	<Window.Resources>
		<ObjectDataProvider
			x:Key="Viewmodel"
			ObjectType="{x:Type Sample:PickOneViewModel}"/>
	</Window.Resources>

	<DockPanel DataContext="{StaticResource Viewmodel}">
		<ListView DockPanel.Dock="Left" Width="130"
			ItemsSource="{Binding AvailablePeople}"
			DisplayMemberPath="FullName" />
		<ListView DockPanel.Dock="Right" Width="130"
			ItemsSource="{Binding SelectedPeople}"
			DisplayMemberPath="FullName" />
		<Button Width="20"
			Command="{Binding PickOnecommand}"
			Content=">>"/>
	</DockPanel>
</Window>

It really is that simple. What is happening is that, when you click on the button, PickOneCommand is executed, adding one random person to the SelectedPerson collection. When that occurs, the list raises a CollectionChanged event that WPF listens. By now, the data binding knows that, because the list has changed, that it needs to update its contents, which it does.

So now you know how to use ObservableCollection. Please keep in mind that you don’t have use a view model, although I would consider it a best practice, that allows you to effectively separate the content from the presentation. Also, you can replace DelegateCommand with your CommandBindings if you prefer. Again, this is a matter of preference, as I believe using it makes for cleaner code.

Related Posts

About the Author: Pedro Sampaio

I'm an UX Software Engineer at FARO Technologies, in Portugal. I work mainly with .NET technologies, such as WPF, during the day. Off work, I develop web application in Rails. I also have experience with ASP.NET MVC, Test and Behavior Driven Development, and agile methodologies, namely Scrum.

8 Comments + Add Comment

  • Hi Pedro. Very clear explanation. I have a more complex set of classes I’m trying to work with. Maybe you’ve tried something like this before and could offer advice? For example, let’s say I want a screen with a few textboxes and a few combo boxes. I’ve created a MainViewModel with the ObservableCollection of the main item of interest–call it People, containing PersonViewModels. Also in the MainViewModel is a SelectedPerson item (a PersonViewModel instance). This works great to track a selected Person in a listbox.

    But now I need a combo box to contain Gender types. So I created a GenderViewModel. It’s based on a GenderDataModel, and contains a GenderID and GenderType. But I need a list of these, so I make GenderViewModelList, which, like MainViewModel contains an ObservableCollection of GenderViewModels, as well as SelectedGender (a GenderViewModel instance).

    Now, I add an instance of GenderViewModelList inside PersonViewModel. I bind my textboxes to the SelectedPerson, and my combo boxes to GederViewModelList. All is well, except–how on earth can I get my PropertyChanged event to bubble up from GenderViewModel? Here is some partial sample code:

    public class PeopleVM : INotifyPropertyChanged
    {
    private ObservableCollection persons = new ObservableCollection();
    private PersonVM selectedPerson;
    private GenderListVM genderList;
    //more stuff here
    public GenderListVM GenderList
    {
    get { return genderList; }
    set {
    genderList = value;
    RaisePropertyChanged(“GenderList”);
    }
    }

    public class GenderVM
    {
    private Gender gender;
    public GenderVM(Gender gender)
    {
    this.gender = gender;
    }
    public int GenderID
    {
    get { return gender.GenderID; }
    set{gender.GenderID = value;}
    }

    public string GenderType
    {
    get { return gender.GenderType; }
    set {gender.GenderType = value;}
    }
    }

    public class GenderListVM : INotifyPropertyChanged
    {
    private ObservableCollection genders = new ObservableCollection();
    private GenderVM selectedGender;
    //more stuff, including raising property change event on selectedGender which does fire

    …and a XAML snippet:

    The data context of the whole UserControl is PeopleVM.

    So the list is supplied properly. But the SelectedItem binding doesn’t update back to my SelectedGender object. I want to capture the GenderID of the SelectedItem. I felt I should structure the VMs the same way as the Main VM, but it’s definitely causing problems, since although the ChangedProperty is raised on SelectedGender, it’s down at the GenderVM level and doesn’t seem to make it back to my bound object which is contained in PeopleVM. How can I make this work and still have my ViewModels neat and tidy?

  • Just discovered that this was actually working perfectly well! It was a case of not seeing the forest for the trees. I forgot that I had to update the object that was supplying data to all the textboxes (call it NewPerson) on the screen with the data members supplied from the selected item in the list (GenderListVM.SelectedGender).

  • monica :

    Just discovered that this was actually working perfectly well! It was a case of not seeing the forest for the trees. I forgot that I had to update the object that was supplying data to all the textboxes (call it NewPerson) on the screen with the data members supplied from the selected item in the list (GenderListVM.SelectedGender).

    Glad you got that working for you!

    Just a small note. I realize that your domain is probably more complex than what you’re showing, but if not, are you sure you need all viewmodels?

  • Very good explanation.I was exploring the difference between generic list and Observable collection and this link helped me out.Thanks.

  • Thanks for the explanation. However when using ObservableCollection as the type of a dependency property, we can have a lot of surprises and the solution is not obvious.

    The problem is when you want to react to any change related to the dependency property. The first guess is that you can just implement a callback via the metadata:

    public static readonly DependencyProperty SourcesProperty =
    DependencyProperty.Register(“Sources”,
    typeof(ObservableCollection),
    typeof(Joint),
    new FrameworkPropertyMetadata(
    new ObservableCollection(),
    new PropertyChangedCallback(OnSourcesChanged)));

    However, as unexperimented WPF users like me bitterly find, OnSourcesChanged will only be called when a new ObservableCollection will be set in the property. This event may occur only once in the life of the object which owns this property. So this means we need another way for being notified when the content (items) or the collection changes.

    This is done by subscribing to an event of the collection. As you realize, we are moving away from the dependency property now and we need to monitor a second event raised by the collection when the collection’s content has been changed.

    This event is ObservableCollection.CollectionChanged.It allows to monitor items added, removed, moved, or replaced, or the collection is cleared, and to my understanding, there is no way of having the dependency property handling this event automatically. The ObservableCollection implements INotifyCollectionChanged that allows for such CollectionChanged notification.

    You may think this is it. Nop (or I’m missing something, which is possible!)

    The most painful part is that “item replaced” means an item has been replaced by another one (you have replaced an object by another with a different reference), an not at all that the item has been updated (the item is the same, but a value in the item has changed).

    If you want to be informed of this event, you need now to monitor the item itself by hooking on Source.OnItemPropertyChanged (in my exemple above, the item type is Source). Of course, your item type must implements INotifyPropertyChanged, so that you can actually hook and be notified automatically.

    Note that the two additional events are related respectivelly to the current collection and the current items in the collection. You need to update your hooks each time those objects change. So you need to hook/unhook to the current colllection each time the property callback informs you the property has changed. Similarly, you can hook to the items when items are added, i.e. you can subscribe to notification when you are processing the notification for CollectionChanged, and the type of change is an addition.

    You need to unhook on previously hooked objects, if you don’t you’ll continue to be notified by objects now out of your scope of interest.

    We have moved really away from the dependency property we saw initially as our best friend. This is a lot of code to maintain, and to my view a serious problem when using dependency properties associated to collections. That said, this is consistent with the collection spirit. Just we miss some ready to use variant of the dependency property which would handle that with a single callback.

    To sum up: to monitor your property has changed in some way, you need to hook on the property (permanent callback, easy to do), dynamically on the collection as soon as it is set, and dynamically on each item as soon as it is added.

  • Thanks for this wonderful explanation. This was realy helpful in understanding the concept.

  • Thanks for the link….I am a newbie in this MVVM structure and trying to learn. One question: How did you customized your VS Editor Screen? Black background and good mixture of keywords in there…

    Can you help me on this? Thanks!

Leave a comment