65.9K
CodeProject is changing. Read more.
Home

Working with ObservableCollection<T>

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.56/5 (22 votes)

Dec 17, 2009

CPOL

3 min read

viewsIcon

140314

downloadIcon

3260

One of the most useful classes when working with WPF can be found in the System.ComponentModel namespace, namely the ObservableCollection

Introduction

One of the most useful classes when working with WPF can be found in the System.ComponentModel namespace, namely the ObservableCollection<T>. This class notifies interested parties of changes to its internal item collection.

Specifically concerning WPF, the ObservableCollection class allows you to bind ItemsControl derived classes, like so: <ListBox ItemsSource="{Binding Contacts}"/>. Assuming the DataContext of the Listbox has a property named Contacts (which has to be a collection type), the Listbox will populate the Listbox with ListboxItems for you. If the property is of type ObservableCollection, then the Listbox will automatically propagate changes into the UI.

The property would look something like this:

private ObservableCollection<ContactViewModel> _Contacts;
public ObservableCollection<ContactViewModel> Contacts
{
	get
            {
                if (_Contacts == null)
                {
                    _Contacts = new ObservableCollection<ContactViewModel>();
                }
                return _Contacts;
            }
}

Views of a Collection

However, behind the scenes, the Listbox is not binding directly to the collection, it is, in fact, binding to a CollectionView object. Depending on what interfaces the collection implements, the framework chooses the type of CollectionView to create. Since we are talking about an ObservableCollection, the framework will create a ListCollectionView. The ListCollectionView provides utilities for filtering, grouping, and sorting*.

Options To Work with Views

In order to access these utilities, you have a number of options. One, you can call a static method called CollectionViewSource.GetDefaultView, which takes as its only parameter, a source object, and returns an object implementing the ICollectionView interface. This requires you to cast to get a concrete class that you can work with. In order to cast correctly, you have to know the interfaces that your collection implements. This can get kind of annoying, even though for ObservableCollection, you pretty much know that you are casting to ListCollectionView. The other thing is that all bindings will share the same view, so you cannot specify two different sort orders if you have multiple controls binding to the collection. From the example above:

ListCollectionView colView = (ListCollectionView)(
    CollectionViewSource.GetDefaultView(Contacts));

Another option is to only expose the collection as a ListCollectionView. For example:

    private ObservableCollection<ContactViewModel> _Contacts;
    private ListCollectionView _ContactsView;
        public ListCollectionView ContactsView
        {
            get
            {
                if (_ContactsView == null)
                {
                    _ContactsView = new ListCollectionView(_Contacts);
                }
                return _ContactsView;
            }
        }

The problem with this approach will quickly become obvious. Users of your class no longer have access to the strongly typed collection that backs the CollectionView. Actually, the ListCollectionView does offer a property called SourceCollection, but this is of type IEnumerable, so you lose the type of your collection, as well as the type of the items within the collection.

The second-last attempt I made to correct this issue was to simply expose two properties. One an ObservableCollection, and the other of type ListCollectionView. This gives you the benefit of the CollectionView, and allows users of your ViewModels (like Unit Tests, or other ViewModels) to access the strongly-typed source collection.

Here is the code:

private ObservableCollection<ContactViewModel> _Contacts;
        public ObservableCollection<ContactViewModel> Contacts
        {
            get
            {
                if (_Contacts == null)
                {
                    _Contacts = new ObservableCollection<ContactViewModel>();
                }
                return _Contacts;
            }
        }
 
        private ListCollectionView _ContactsView;
        public ListCollectionView ContactsView
        {
            get
            {
                if (_ContactsView == null)
                {
                    _ContactsView = new ListCollectionView(this.Contacts);
                }
                return _ContactsView;
            }
        }

This code works and offers a lot of flexibility to consumers of your ViewModels. However, this pattern of exposing two properties to express one concept has always irked me, and I finally took the time to create a better solution. This new solution was actually really simple, and just involves subclassing ObservableCollection<T>, and adding a property of type ListCollectionView. Note that I couldn’t think of a proper name for it, so I called it ViewableCollection. If anyone can think of a better name, please post it in the comments, and I will rename it and re-upload the sources.

Without further ado, here is the ViewableCollection class:

    /// <summary>
    /// Represents a dynamic data collection that raises notifications
    /// when items are added, removed, moved, or replaced. In addition,
    /// this collection also has a View property that offers a bindable
    /// version of this collection. Note that this collection can only be edited
    /// on the UI thread.
    /// </summary>
    /// <typeparam name="T">The type of items in this collection.</typeparam>
    public class ViewableCollection<T> : ObservableCollection<T>
    {
        private ListCollectionView _View;
        /// <summary>
        /// A bindable view of this Observable Collection (of T) that supports
        /// filtering, sorting, and grouping.
        /// </summary>
        public ListCollectionView View
        {
            get
            {
                if (_View == null)
                {
                    _View = new ListCollectionView(this);
                }
                return _View;
            }
        }
    }

With this class in place, we can change our MainWindowViewModel class as follows:

private ViewableCollection&lt;ContactViewModel&gt; _Contacts;
        public ViewableCollection&lt;ContactViewModel&gt; Contacts
        {
            get
            {
                if (_Contacts == null)
                {
                    _Contacts = new ViewableCollection&lt;ContactViewModel&gt;();
                }
                return _Contacts;
            }
        }

And our binding becomes: <ListBox ItemsSource="{Binding Contacts.View}" />.

This class lazily instantiates the view, so if you never use the property it won't take up room in memory. To add SortDescriptions to this collection:this.Contacts.View.SortDescriptions.Add(new SortDescription("FirstName", ListSortDirection.Descending));.

Take at the look at the demo project to see how this concept is implemented. Note that my method of overriding the ToString method of the ContactViewModel class was for brevity, and should never, never, never be done in an actual WPF application (use DataTemplates instead).

This is actually my first article on Code Project, so I'd appreciate any feedback for future articles. Stay tuned for another article about creating a subclass of ViewableCollection that adds support for Commands common to collections.