Hello ObservableVector, goodbye ObservableCollection

WARNING: Article content applies to Windows 8 Developer Preview only. ObservableCollection<T> is alive and kicking in the current release.

WinRT, the new Windows 8 runtime for Metro applications, introduces a new interface for collection change notification. IObservableVector<T> replaces ye olde INotifyCollectionChanged. The ObservableCollection class still exists, you can continue to use it. Unfortunately its collection change events are ignored by the WinRT framework. I can assure you that this will give cross-platform developers a serious headache - or worse. Don't say I didn't warn you:

The IObservableVector<T> interface is defined, but the framework does not contain an implementation yet. There's no built-in collection that raises the new VectorChanged events. The fresh cocoon framework on CodePlex contains an implementation of ObservableVector<T>. Unsurprisingly it's a light-weight wrapper around an IList<T>. The class is described here.

Here's my own version of the class - I just adapted it to my property change notification mechanism:

namespace Mvvm
{
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using Windows.Foundation.Collections;
    using Windows.UI.Xaml.Data;

    /// <summary>
    /// IObservableVector<T> implementation.
    /// </summary>
    public class ObservableVector<T> : Collection<T>, INotifyPropertyChanged, IObservableVector<T>
    {
        // *** Events ***

        public event PropertyChangedEventHandler PropertyChanged;
        public event VectorChangedEventHandler<T> VectorChanged;

        // *** Constructors ***

        public ObservableVector()
            : base()
        {}

        public ObservableVector(IList<T> list)
            : base(list)
        {}

        // *** Protected Methods ***   

        protected override void ClearItems()
        {
            base.ClearItems();
            this.PropertyChanged.Raise(this, o => o.Count);
            this.PropertyChanged.Raise(this, o => o.Items);
            this.OnVectorChanged(CollectionChange.Reset, 0);
        }

        protected override void InsertItem(int index, T item)
        {
            base.InsertItem(index, item);
            this.PropertyChanged.Raise(this, o => o.Count);
            this.PropertyChanged.Raise(this, o => o.Items);
            this.OnVectorChanged(CollectionChange.ItemInserted, (uint)index);
        }

        protected override void RemoveItem(int index)
        {
            base.RemoveItem(index);
            this.PropertyChanged.Raise(this, o => o.Count);
            this.PropertyChanged.Raise(this, o => o.Items);
            this.OnVectorChanged(CollectionChange.ItemRemoved, (uint)index);
        }

        protected override void SetItem(int index, T item)
        {
            base.SetItem(index, item);
            this.PropertyChanged.Raise(this, o => o.Items);
            this.OnVectorChanged(CollectionChange.ItemChanged, (uint)index);
        }

        // *** Event Handlers ***

        protected void OnVectorChanged(CollectionChange collectionChange, uint index)
        {
           this.OnVectorChanged(new VectorChangedEventArgs(collectionChange, index));
        }

        protected virtual void OnVectorChanged(IVectorChangedEventArgs e)
        {
            if (this.VectorChanged != null)
                this.VectorChanged(this, e);
        }

        // *** Private Sub-classes ***

        private class VectorChangedEventArgs : IVectorChangedEventArgs
        {
            // *** Fields ***

            private readonly CollectionChange collectionChange;
            private readonly uint index;

            // *** Constructors ***

            public VectorChangedEventArgs(CollectionChange collectionChange, uint index)
            {
                this.collectionChange = collectionChange;
                this.index = index;
            }

            // *** Properties ***

            public CollectionChange CollectionChange
            {
                get
                {
                    return this.collectionChange;
                }
            }

            public uint Index
            {
                get
                {
                    return this.index;
                }
            }
        }
    }
}

I consider this temporary code - I assume that next versions of WinRT will have a native version of ObservableVector<T>.

I built a small sample application around to demonstrate the usage of the class in a MVVM application: all the work is done through data and command bindings. The app just manages two collections of Dragons: 'All Dragons' and 'Favorites'. The selected Dragon in each ListBox can be moved to the other collection by clicking the buttons in the middle. Here's how the app looks like:

By the way, in the current version of WinRT -Developer Preview- the collection changed events are only handled if T is object, so you have to define the collection as follows:

public ObservableVector<object> Dragons { get; set; }

For any other type -like ObservableVector<string> or ObservableVector<Dragon>- the change events will be simply ignored by the binding mechanism. That's another headacheUndecided.

You use the class exactly the same way as good old ObservableCollection, e.g. as ItemsSource to a an ItemControl:

<ListBox DataContext="{Binding}"
         ItemsSource="{Binding Dragons}"
         SelectedItem="{Binding SelectedDragon, Mode=TwoWay}" />

Here's the full source code, it's built with Visual Studio 11 Developer Preview: U2UConsult.WinRT.ObservableVector.Sample.zip (47,83 kb)

Enjoy!