This blog post provides an implementation of IPagedCollectionView which allows paging of data from the server. An IPagedDataSource is introduced that allows any paged data source to be plugged in, with the standard controls such as DataPager making it easy to create paging applications.
With web-based applications, bandwidth constraints often mean that when querying large datasets, the results must be paged, i.e. split into discrete pages each containing a small number of results, with an API available for moving forwards / backwards or to a specific page. There are numerous APIs available on the web that expose this type of interface, for example:
- Twitter Search API
- Nestoria API for searching houses for sale
- Any of the OData producers
With paging being a common application requirement, Silverlight has the functionality to deal with this scenario built-in. The System.Windows.Data assembly contains the IPagedCollectionView interface, which defines the following methods, properties and events, allowing navigation of paged data:
System.Windows.Controls assembly contains a DataPager control, that collaborates with this interface to provide a UI for paging the dataset. Simply set the Source property of this control to an instance of
IPagedCollectionView and your user can page through the data:
Source property of
DataPager is of type
IEnumerable, which is a little odd, because it doesn't really need to enumerate the collection of items being displayed. Internally it probes the object you supply as the Source to see if it implements
IPagedCollectionView, and if it does, the control becomes 'active'.
This control gives you quite a bit of functionality for free, the buttons become enabled / disabled based on whether navigation forwards / backwards is possible. It respects the CanChangePage property, computes the total number of pages etc ...
So, how do you make use of this interface and control?
System.Windows.Data assembly also contains a concrete implementation of the paging interface, PagedCollectionView. This class gives much more than just paging, it also implements grouping, sorting and filtering of a source collection (
IEnumerable). To make use of this class simply construct an instance based on your collection of data and use that as your
The problem is
PagedCollectionView allows you to sort, group and page a collection of data that is already held in memory. What I want to do, and what I think is a more common use-case, is page data that is supplied by the server. I don't want to have to fetch all the data up-front then page it on the client, this would defeat the object of paging in the first place ... to improve performance!
I discovered that there is an
IPagedCollectionView implementation as part of RIA Services that provides this functionality, the
DomainCollectionView. However, if I want to page data form a simple JSON formatted web service, adding a dependency to RIA Services seems like overkill. So I decided to create my own simple implementation of this interface.
In order to make a generic/ re-useable implementation of this paging interface, I first created an interface that would act as the source of the data:
This interface is very simple, having a single method that fetches data for the given page. The result is returned asynchronously, providing both the items within the given page and the total item count.
My paging collection view extends
ObservableCollection and takes an instance of
IPagedDataSource in its constructor:
As an aside, aren't generics great?
The various properties defined in
TotalPages, etc ...) are implemented as standard field-backed properties that notify changes via
INotifyPropertyChanged which is inherited via
ObservableCollection, I am not going to show them here.
The various methods that permit navigation are all implemented using the same pattern:
RefreshData method doing all the work, it is the only method that uses the supplied
IPagedDataSource interface and is shown below:
With the simple implementation of
IPagedCollectionView given above I can now page data from a range of web services by simply implementing
IPagedDataSource. For example, to page data from NetFlix you can use the following implementation:
As you can see, this simple implementation of our paging data source constructs the required URL based on the search string and request page number. It also uses the 'syndication' APIs which are able to parse the response from the NetFlix OData APIs.
To test out this implementation of the paged search interface I have created a very simple movie search application. The view model for this application has a
SearchString property, exposes the results as
SearchResults property and has a single command which initiates a new search. The important parts of this class are shown below:
ExecuteSearchCommand is invoked, we create a new
ServerSidePagedCollectionView, providing it with the NetFlix data source. From here on, the
ServerSidePagedCollectionView is responsible for the paging of data.
The view is as follows:
The DataPager simply binds to the
SearchResults as does the ItemsControl. I have also added a
Rectangle element which grays-out the current search results when a new page is being fetched.
You can see the application in action below:
So in summary, by implementing
IPagedCollectionView with a pluggable datasource,
IPagedDataSource, it is possible to very quickly and easily create an application that pages data from the server.
You can download the full sourcecode here: PagedCollectionViewExample.zip
Regards, Colin E.