February20

Minimal Virtualized Data List for WP7

I recently gave a talk about data management on Windows Phone 7 for XapFest and I put in a slide about virtualizing your data for data bound objects.  I didn’t really think it was that big of a topic when I was putting together the talk, but it generated a lot of interest from attendees.

I am going to demonstrate the minimal virtualized data collection object you can easily create for data binding with a Listbox.

For this example I am using a simple class named FeedItemDataModel.  This represents a class that holds a single item from an RSS Feed.  In this simple implementation the values are simple and set in the constructor.

The sample application shows a UI with a collection of this object databound to the UI and lets you jump to a specific index in the list.  The red text you see is a memory watcher class that shows memory usage within your app.

VirtualizedData

Silverlight handles the UI part

To be clear, Silverlight handles the xaml part of this for you.  If you have a list with one million objects only the number that fit on screen are built up for the xaml display (from your DataTemplate most likely).  The runtime is smart enough to realize that the listitem is offscreen and recycle it for you.  This is a huge performance boost, but what about the data behind that databound collection?  If you bind one million objects they are all loaded into that collection by default!  If you want to only load those that are on screen you have to handle that yourself.  But it really is not that hard to do for most data objects.

Three “must have” parts

You don’t need to implement a lot of things to make data binding work against a custom object, but you must implement the following three things.  The below code snippet excludes a bunch of stuff you have to implement due to the interface, but you can just throw a not implemented exception.  You don’t need them to be completely implemented for this to work.

IndexOf – So the data binding can lookup a specific object and return it as the selected item.

Count – How many total items are in the collection. Listboxes need this to show the correct size scroll bar.

this[] – Go get the item, this could be from disk or from a cache.

public class MinVirtualizedList : IList
{
// Total items to show in the list
public const int TotalItems = 1000000;

/// <summary>
/// Count returns the total items in the list.
/// Important for the scrollbar to have a scale that matches the data
/// </summary>
public int Count
{
    get { return TotalItems; }
}

/// <summary>
/// Find the index of an object in the data list.
/// Important to allow quick seek access to the data position
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public int IndexOf(object value)
{
    if (value == null)
    {
        return -1;
    }

    FeedItemDataModel item = (FeedItemDataModel)value;
    return item.FeedItemId;
}

/// <summary>
/// Indexer for the list - this is where Silverlight will call back for each item that is
/// needed to fill the virtualized UI elements
/// </summary>
/// <param name="index">The index of the item to get</param>
/// <returns>The item</returns>
public object this[int index]
{
    get
    {
        // If using SterlingDB you would simple have to Load<FeedItemDataModel>(index)
        FeedItemDataModel loadedItem = null;
        loadedItem = new FeedItemDataModel { FeedItemId = index, FeedTitle = "Title " };
        return loadedItem;
    }
    set
    {
        throw new NotImplementedException();
    }
}  // See code on bitbucket for complete implementation with stubbed out methods

Load on Demand

You may be able to get away with always loading on demand if your objects are small.  I may update this sample later to include on demand loading using SterlingDB.  It would be really easy to implement, and I think there is value in having such a sample in the community.

Don’t be greedy

Only load the items you need.  If the user rarely scrolls down, don’t load anything beyond what a single screen can show (maybe a couple extra for the first part of the scroll).

Most apps may need a hybrid approach to this.  Either build a façade around your object and only load the parts that are needed for the UI, or load the smallest subset you expect the user to realistically consume.

Doesn’t IObservableCollection handle this?

I think this is a common misconception. ObservableCollections are for handling the change notifications to the UI. You only have to databind them one time, and when the collection is changed the UI gets updated. You could probably use the same technique here, but you would have to derive your own custom class.

Sample Code on BitBucket

I put the complete sample up on BitBucket, click the link to go direct to the source.

VirtualizedDataWP7 BitBucket Page

Cache in the collection

Another optimization would be to cache the objects in the collection.  This would be pretty easy to implement an MRU (most recently used) cache.  Sounds like another future blog post.

Pingbacks and trackbacks (1)+

Comments are closed