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:
The 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:
NOTE: The 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?
The 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 DataContext
:
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 IPagedCollectionView
(CanChangePage
, 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:
With the 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:
When 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.