Thursday, December 01, 2011

ObservableVector as a replacement for ObservableCollection in Metro-style apps

For those of you who have had previous experience in making WPF and Silverlight applications you are probably familiar with the ObservableCollection<T> class. This represents a list of items (implementing IList<T>) whilst also raising the INotifyCollectionChanged.CollectionChanged event whenever items are added, removed or moved within the collection. When such collections are databound to the various list controls in WPF and Silverlight the UI will automatically update to reflect any changes.

ObservableCollection<T> and Metro-Style Apps

If you have followed the same technique when writing Windows 8 Metro-style apps you will have found that this no longer works. There are a couple of reasons for this behaviour,

  1. In the Developer Preview version of Windows 8 the INotifyPropertyChanged interface is duplicated in both the System.ComponentModel and Windows.UI.Xaml.Data namespaces. Whilst ObservableCollection<T> implements the former, the new Metro XAML framework uses the latter.
  2. Rather than INotifyCollectionChanged as implemented by ObservableCollection<T>, WinRT uses the IObservableVector<T> interface.

Observable Collections in Metro-Style Apps

Whilst this issue is likely to be addressed prior to release of Windows 8, for the time being there are a few workarounds.

  1. The Windows 8 SDK Data Binding sample includes an ObservableVector class that takes an existing object that implements INotifyCollectionChanged, and wraps it in an IObservableVector<T> implementation.
  2. Colin Eberhardt has posted a similar solution on his blog.
  3. Avi Pilosof has taken a slightly different approach by deriving a new class from ObservableCollection<T>, implementing both INotifyCollectionChanged and IObservableVector<T>.

Whilst these solve the problem, they all involve wrapping the existing ObservableCollection<T> class, resulting in events being raised for both the old and new approaches. This is ideal for cases where you wish to write interoperable code for both existing and Metro applications, but is an added overhead when you are natively writing a new Windows 8 application.

A Metro-Designed Observable Collection Class

In response to this I have written a ground-up ObservableVector<T> class for use in Windows 8 Metro-style applications that does not have the overhead of an underlying ObservableCollection<T>. The code has been released as part of the freely available Cocoon framework. You can use this class by creating a new instance directly,

IList<Person> list = new ObservableVector<Person>();
list.Add(new Person("Andrew", "Wilkinson"));


or by deriving a custom class,



public class PersonCollection : ObservableVector<Person>
{
    ...
}


It is important to note however that in the current Windows Developer Preview there is a bug which means that databinding only works correctly to lists when the implement ObservableVector<object> rather than strongly typed collections. This is likely to change in the future.


In both cases the behaviour is designed to match that expected by WPF and Silverlight developers. There are a small number of subtle differences with respect to the previous ObservableCollection<T> class,



  1. Since IObservableVector<T> does not have the same concept of moving items it does not implement the Move() method. Instead you should remove the item and then add it to its new place in the collection.

  2. The constructor of the ObservableVector<T> class optionally accepts an IList<T> that is wrapped by the implementation rather than copied internally – the reason for this will become apparent in later posts.

  3. Currently no attempts are made to stop VectorChanged event handlers from modifying the original collection.

Summary


In conclusion, the Cocoon framework provides an IObservableVector<T> implementation specifically designed from the ground-up for Metro applications. The code is freely available for download from the Cocoon CodePlex site (to get the latest version go to the “Source Code” tab, select the first change set and use the “Download” link).


Next time I will discuss data virtualisation and the IVirtualizingVector interface.

3 comments:

Unknown said...

Looks like obserablevector still needs to be an object in the consumer preview... very disappointing!

Unknown said...

Ross...

Thanks for the comment. I too had hoped that this restriction would be overcome with the Consumer Preview. I've not had a deep look at this yet (am currently in the process of porting the Cocoon code to the CP) but will do.

This mismatch is something that the DataList and IDataListSource elements in Cocoon are designed to circumvent - whilst the IDataListSource can be strongly typed, this can then be exposed to WinRT as an object.

Unknown said...

Ross...

I have done some investigations regarding IObservableVector in the Consumer Preview and agree that implementing this interface only seems to work when T is object.

However, with the Consumer Preview you can now use the usual INotifyCollectionChanged interface and this is projected into WinRT correctly, so you can now make your observable collections type safe.

I've implemented this in the Cocoon framework and will push the latest changes to CodePlex when I have upgraded everything to the CP.