Click here to Skip to main content
15,886,518 members
Articles / Desktop Programming / WPF
Article

Working with ObservableCollection<T>

Rate me:
Please Sign up or sign in to vote.
4.56/5 (25 votes)
17 Dec 2009CPOL3 min read 139K   3.3K   51   4
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:

C#
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:

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

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

C#
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:

C#
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:

C#
/// <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:

C#
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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer N/A
Canada Canada
Sanjay was weaned on VB6, then was dragged kicking and screaming into the world of .NET development, with VB.NET. He was pleasantly surprised, and quickly migrated existing projects to reap the benefits of the massive .NET libraries.

Quite a while ago, he started a pet project that involved fading in and out controls (as well as other animations) in a Windows Forms application. After weeks of hard work, he finally realized that there had to be a better way.

He found WPF.

He changed his main programming language to C#, and has never looked back.

Comments and Discussions

 
Questionmy vote of 5 Pin
ferkelrudolf24-May-17 10:43
ferkelrudolf24-May-17 10:43 
GeneralMy vote of 1 Pin
shijeesh2-Nov-10 2:04
shijeesh2-Nov-10 2:04 
GeneralNice and Simple Pin
Member 45654336-Aug-10 0:15
Member 45654336-Aug-10 0:15 
GeneralNice Post, Helps clarify a few things ... Pin
George Gomez13-Jul-10 21:24
George Gomez13-Jul-10 21:24 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.