Manish Dalal's blog

Exploring .net!

Silverlight 3 Custom Sorting with Paging Support

This post updates the custom sorting technique I had originally posted for Silverlight 2 and adds paging support. image

Introduction

Silverlight 3 includes PagedCollectionView class, which provides grouping, sorting, and paging functionality for any data source that implements the IEnumerable interface. This works as long as all the data that you need to sort (and/or page) is already fetched to the client. However when you are retrieving only a limited set of data from the server at a time, you need an alternative solution that does sorting on the server. 

One way to achieve this is to use .net RIA services. In particular, DomainDataSource control provides automatic server sorting and paging support. However, if you are not using .net RIA services, than you will need to implement ICollectionView to handle sorting and IPagedCollectionView to handle paging. This post updates the SortableCollectionView class that I had introduced in the previous custom sorting post and adds paging support by implementing IPagedCollectionView as PagedSortableCollectionView. I will assume that you have already read the previous article!

Custom Sorting

SortableCollectionView class implements ICollectionView and provides custom sorting support. Sorting functionality encompasses CanSort and SortDescriptions properties. For custom sorting, we need to listen for changes to SortDescriptions collection, and in turn refresh the data for new sort options. (A SortDescription defines the direction and the property name to be used as the criteria for sorting a collection.)image

For Silverlight 3, SortableCollectionView was updated to provide implementation for several methods that were previously not used. In addition, we no longer need the CustomSortDescription class as the framework will automatically call Refresh method any time there is a change in the SortDescriptions collection. This functionality is enabled by implementing DeferRefresh method and utilizing the DeferRefreshHelper class. As the name implies, DeferRefresh method just delays the automatic refresh of data till all the needed changes are completed and ready to process. When the DeferRefreshHelper object is disposed, we fire the OnRefresh event, which is our cue that we need new data from server.

Following code snippet shows the DeferRefresh method returning DeferRefreshHelper object with Refresh method passed as callback:
 
public IDisposable DeferRefresh() {
    return new DeferRefreshHelper(() => Refresh());
}

private class DeferRefreshHelper : IDisposable {
    private Action _callback;

    public DeferRefreshHelper(Action callback) {
        _callback = callback;
    }

    public void Dispose() {
        _callback();
    }
}
 
Refresh method fires OnRefresh event and passes in RefreshEventArgs, providing access to SortDescriptions collection. A call is made to the server to get new data, passing in proper sort descriptors information. In the callback, we clear existing data and load new sorted data.
 
public void Refresh() {
    if (null != OnRefresh) {
        OnRefresh(this, new RefreshEventArgs() { SortDescriptions = SortDescriptions });
    }
}
imagePaging

Silverlight 3 introduced a new interface IPagedCollectionView to support paging and the DataPager control that can utilize the IPagedCollectionView and provides UI to page the data. IPagedCollectionView includes method to navigate to different pages. PagedSortableCollectionView implements the IPagedCollectionView interface on the base SortableCollectionView. The main method that does paging is MoveToPage:

public bool MoveToPage(int pageIndex) {
    if (pageIndex < -1) {
        return false;
    }
    if ((pageIndex == -1) && (this.PageSize > 0)) {
        return false;
    }
    if ((pageIndex >= this.PageCount) || (this._pageIndex == pageIndex)) {
        return false;
    }
    //
    try {
        IsPageChanging = true;
        if (null != PageChanging) {
            PageChangingEventArgs args = new PageChangingEventArgs(pageIndex);
            OnPageChanging(args);
            if (args.Cancel) return false;
        }
        //
        _pageIndex = pageIndex;
        Refresh();
        IsPageChanging = false;
        OnPropertyChanged("PageIndex");
        OnPageChanged(EventArgs.Empty);
        return true;
    } finally {
        IsPageChanging = false;
    }
}

Whenever there is a change in paging, Refresh method is called, which in turn fires OnRefresh event and we get new data from server.

Sample Applicationimage

Sample application is similar to the one from previous post. On the server side there is no change, I have used that same Person model that I had used earlier. Similarly I have used the same PeopleService to get data. PeopleService exposes a GetData method that build list of persons and then optionally sorts them and returns requested paged data back to the client.

On the client side, PeopleViewModel exposes PagedSortableCollectionView as DataList for data biding. It also subscribes to the OnRefresh event and calls the server to get data. Following code snippet shows the client GetData method which is used to build paging and sorting parameters and subsequently fetch data from server using the PeopleService.GetData method.

private void GetData() {
    int take = DataList.PageSize;
    int skip = DataList.PageIndex * DataList.PageSize;
    //
    Dictionary<string, string> sortBy = new Dictionary<string, string>();
    foreach (SortDescription sortDesc in DataList.SortDescriptions) {
        sortBy.Add(sortDesc.PropertyName, (sortDesc.Direction == ListSortDirection.Ascending ? "ASC" : "DESC"));
    }
    //
    PeopleServiceClient proxy = new PeopleServiceClient();
    proxy.GetDataCompleted +=new EventHandler<GetDataCompletedEventArgs>(proxy_GetDataCompleted);
    proxy.GetDataAsync(skip, take, sortBy);
}

image

Steps to get started
  1. Include PagedSortableCollectionView,  SortableCollectionView and RefreshEventArgs classes in your Silverlight client code.
  2. Handle the OnRefresh event and call server with sorting and paging information.
  3. Clear existing data and reload the results.

Source code: CustomSortingSL3.zip

Technorati Tags:

Comments

Serchuz said:

Thanks for the example...

I'm waiting for the filter functionality too

# October 3, 2009 6:11 PM

Emilian said:

Thanks a lot.

I used your sample to use custom pagination with .Net RIA (converting a old project to Silverlight).

# October 9, 2009 4:13 AM

Neil said:

Great example, I have encountered one problem though when I change the page size in xaml <vm:PeopleViewModel PageSize="50"/> the first page displays only 10 items still, the default set in the PagedSortableCollectionView class am I missing something else I have to set in order to display the same on each page. I have a collection of items from a database and also changed TotalItemCount to the count of the items in the database

# December 4, 2009 4:15 AM

Kevin said:

Hi Manish -

I noticed that the SortableCollectionView class does not handle the GroupDescriptions and Group properties.  Is there any way to use your classes with grouping functionality?

Thanks

Kevin

# January 11, 2010 4:04 PM