I have been learning WinRT, new Windows 8 run time for developing Windows 8 application for a little while now. I am using my CodePlex project as study project. One of my goals is have an ability to persist collections of object into file system, loosely called database. I really want my collections to be bindable, so I attempted to use ObservableCollection<T> as the base class for my collections. This worked just find in Windows Phone 7 and Silverlight based projects. The same however does not hold true in WinRT. It contains a number of bases classes that WinRT controls such as ListBox listen to instead of INotifyCollectionChanged. Specifically, it is IObservableVector<T>. There is a sample implementation in one of the WinRT sample apps, but I really did not like how it was done – via extension method on INotifyCollectionChanged. What I really wanted to do is extend Observable Collection, because I think Microsoft will eventually have an implementation for IObservableVector<T>, maybe they would event extend the observable collection itself. First thing I ran into is that if you use IObservableVector with T anything other than object, your program will throw “bad image” exception. So, here is the outline of my solution.
- Use Observable Collection as base class
- Implement IObservableVector<object>
That should do it, right?
So, I am starting with this declaration:
public class ExtendedObservableCollection<T> : ObservableCollection<T>, IObservableVector<object>
Then I use smart tag for IObservableVector and let Visual Studio fill in the necessary interfaces I have to implement. Then I simply call base methods of Observable Collection for the majority of implementation, casting object to T as necessary. Simple and efficient, and most importantly, something I could simply remove down the road as WinRT API becomes more mature. Here is the final class.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Windows.Foundation.Collections;
namespace WinRTDatabase.Core
{
/// <summary>
/// Class that works for data bindings for WinRT list based controls
/// </summary>
/// <typeparam name="T"></typeparam>
public class ExtendedObservableCollection<T> : ObservableCollection<T>, IObservableVector<object>
{
/// <summary>
/// Raises collection changed event
/// </summary>
/// <param name="e">Event arguments for collection changed event</param>
protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
base.OnCollectionChanged(e);
switch (e.Action)
{
case System.Collections.Specialized.NotifyCollectionChangedAction.Add:
OnVectorChanged(CollectionChange.ItemInserted, (uint)e.NewStartingIndex);
break;
case System.Collections.Specialized.NotifyCollectionChangedAction.Move:
OnVectorChanged(CollectionChange.Reset, (uint)e.NewStartingIndex);
break;
case System.Collections.Specialized.NotifyCollectionChangedAction.Remove:
OnVectorChanged(CollectionChange.ItemRemoved, (uint)e.OldStartingIndex);
break;
case System.Collections.Specialized.NotifyCollectionChangedAction.Replace:
OnVectorChanged(CollectionChange.ItemChanged, (uint)e.NewStartingIndex);
break;
case System.Collections.Specialized.NotifyCollectionChangedAction.Reset:
OnVectorChanged(CollectionChange.Reset, (uint)e.NewStartingIndex);
break;
default:
break;
}
}
protected void OnVectorChanged(CollectionChange collectionChange, uint index)
{
if (VectorChanged != null)
{
VectorChanged(this, new VectorChangedEventArgs(collectionChange, index));
}
}
public event VectorChangedEventHandler<object> VectorChanged;
public int IndexOf(object item)
{
return base.IndexOf((T)item);
}
public void Insert(int index, object item)
{
base.InsertItem(index, (T)item);
}
public new object this[int index]
{
get
{
return base[index];
}
set
{
base[index] = (T)value;
}
}
public void Add(object item)
{
base.Add((T)item);
}
public bool Contains(object item)
{
return base.Contains((T)item);
}
public void CopyTo(object[] array, int arrayIndex)
{
T[] newArray = new T[array.Length];
for (int i = 0; i < array.Length; i++)
{
newArray[i] = (T)array[i];
}
CopyTo(newArray, arrayIndex);
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(object item)
{
if (Contains(item))
{
base.Remove((T)item);
return true;
}
return false;
}
public new IEnumerator<object> GetEnumerator()
{
return base.GetEnumerator() as IEnumerator<object>;
}
}
}
Thanks.