Download sample application and source here

This blog post was written almost a year ago, but lethargy and constant revision has made me realize that good blog posts are hard to write.

  1. Background

  2. Nuts and Bolts

  3. Using this in your applications:

  4. Vote to get these features in the DataServiceCollection<TEntity> type

Background

When the DataGrid shipped in Silverlight 3 SDK, there were a lot of cool things you could do with it.
You could sort items by a column by clicking on the header, you could group items by writing some code, you could hook up a datapager to the control
and have the grid bind to pages of data instead of a really long list. In short, pretty cool stuff. Around the same time, I started investigating what would it
take to achieve the same effect with remote data sources, most importantly, an Astoria ( / WCF Data Services / OData / This week’s name ) service.
Imagine clicking a datagrid bound to an Astoria feed and clicking on a column header would cause us to ask the server to sort the collection and return the results. Having spent a weekend investigating what it would take to get the entire gamut of the Silverlight toolkit’s UI controls , such as DataGrid/DataPager/DataForm/Charts , to bind to an Astoria feed and have this kind of interaction , I figured out that not only was this easy to do,but that
there’s a lot of value in doing this for Silverlight controls. After this investigation and the initial excitement, We got to work on the Astoria V2 release and this work was lost in that noise.

Recently, since I have a bit of time to step back and think a bit, I decided to revive this work and release the source code and samples so that someone out there can use it in their applications and make some sense of it. As Steve Jobs ( as quoted by  Alex James ) says “Real artists ship”.

Nuts and Bolts

Let’s start with this MSDN document :How to: Group, Sort, and Filter Data in the DataGrid Control
It states how one can use the PagedCollectionView  to implement sorting/grouping/paging support in the DataGrid.
Piqued by similar documentation that existed back in the day, I reflected over this collection type and realized that it implemented
a couple of interfaces that were the secret sauce to getting this to work.
1. ICollectionView : this interface has definitions of methods for sorting/grouping a collection of items
2. IPagedCollectionView : this interface has methods for paging a collection, the DataPager control uses this to
                                           trigger paging calls on the underlying collection.
3. IEditableCollectionView : this interface has methods for editing a collection, the dataform control uses this interface to add/remove items

Having understood what makes the datagrid’s sorting/paging tick, I set to work on building an implementation of these interfaces which used a
datasource to query the OData Service to perform most of these operations.

The sorting calls were made by the $orderby query option and the paging calls were made by a combination of $skip and $top options.

OrderBy query option : http://www.odata.org/developers/protocols/uri-conventions#OrderBySystemQueryOption

Discussions with some people led me to believe that a datasource which was intrinsic to the collection types themselves seemed “evil” to a lot of people.
After multiple iterations, this is what the code looks like today .

We have  collection type called : ODataServiceCollection<T> which implements all the above interfaces and uses an IDataSource<TEntity> instance
to perform the majority of the heavy lifting required for doing the actual sorting/paging calls.
At a glance, this is what the IDataSource<TEntity> looks like :

/// <summary>
/// This interface defines the behavior for a type which acts as the DataSource for an ODataServiceCollection.
/// </summary>
/// <typeparam name="TEntity">The Entity Type</typeparam>
public interface IDataSource<TEntity> where TEntity : class
{
    /// <summary>
    /// This method is called to sort the SourceCollection on the ODataServiceCollection.
    /// </summary>
    /// <param name="sortDescriptions">A collection that contains the SortDescriptions</param>
    void Sort(SortDescriptionCollection sortDescriptions);

    /// <summary>
    /// This method is called to load the <paramref name="pageIndex"/> page of the SourceCollection.
    /// </summary>
    /// <param name="pageIndex">The page index for the new page of results that the collection should fetch</param>
    void GoToPage(int pageIndex);

    /// <summary>
    /// This method is called to calculate the TotalCount of entities in the SourceCollection.
    /// </summary>
    void GetTotalCount();

    /// <summary>
    /// The number of items to show in each page of results
    /// </summary>
    short PageSize { get; set; }

    /// <summary>
    /// A boolean value which specifies if the DataSource is loading a page of results.
    /// </summary>
    bool IsLoading { get; }

    /// <summary>
    /// This event is fired when the GetTotalCount function finishes execution.
    /// </summary>
    event EventHandler<CountEventArgs> GetTotalCountCompleted;

    /// <summary>
    /// This event is fired when the DataSource has completed loading a page of results.
    /// </summary>
    event EventHandler<EntityEventArgs<TEntity>> EntitiesDownloaded;
}

Depending on the implementation of the IDataSource.Sort method, you can do all of these operations in-memory or
query a data service to do the operations on the server-side.

Using this in your applications

The sample code contains an IDataSource implementation which calls an OData Server for sorting operations.

// This is the Data Source that does query maintenance and listens to sorting and 
// paging calls from the ODataServiceCollection // You can swap this out to use your own in-memory DataSource for unit testing.
ODataDataSource<Title> titlesDataSource = new ODataDataSource<NetFlix.Title>( new Uri("http://odata.netflix.com/Catalog"), /*Base Uri of the Data Service */ "Titles", /*Name of the Entity Set */ 5 /*number of entities to be loaded in each page*/); ODataServiceCollection<Title> titlesCollection = new ODataServiceCollection<Title>(titlesDataSource);

and then you hook up the DataGrid by setting the ItemsSource.

dgTitles.ItemsSource = titlesCollection;

From this point on, any actions performed on the DataGrid such as Sorting/Paging on the DataPager are taken care of by the collection
and the results are updated on the screen.

Vote to get these features in the DataServiceCollection<TEntity> type

Click here to enter a suggestion on our Connect site if you want to see this support in the DataServiceCollection<TEntity> type that we ship in the
Silverlight client library.A lot of this experimentation is to satisfy my own curiosity to learn these UI technologies and inner workings,
If you feel that  you want the team to build support for Sorting/Paging into the product, make a suggestion on the above link and get your voice heard.