When you want to page a collection in Silverlight all the online documentation points you to the 'PagedCollectionView' class, which offers a paging wrapper around an IEnumerable.
This needs the full list of IEnumerable data to function in the first place, which is fine for small datasets, but for the most part you want to page a database resultset from many thousands of rows, down to maybe 10 at a time.
Your Silverlight application probably gets its data from a WCF service, either directly or by using RIA Services link, so you want to pass your paging request from Silverlight to the service layer, where it can be transformed through your architecture to more than likely a SQL query, so that one page of data is transported at a time.
There are hints on Google to implement an IPagedCollectionView in order to achieve this. Below shows my version of an IPagedCollectionView which can then be used for 'virtual' paging of data in Silverlight:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Collections.Specialized;
using System.Collections;
public class PagedVirtualCollectionView : IPagedCollectionView, IEnumerable, INotifyCollectionChanged, INotifyPropertyChanged
{
public PagedVirtualCollectionView(IEnumerable source)
{
_sourceCollection = source;
}
private IEnumerable _sourceCollection;
public IEnumerable SourceCollection
{
get { return _sourceCollection; }
set {
_sourceCollection = value;
OnPropertyChanged("SourceCollection");
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
private int _VirtualItemCount;
public int VirtualItemCount
{
get { return _VirtualItemCount; }
set {
_VirtualItemCount = value;
OnPropertyChanged("VirtualItemCount");
OnPropertyChanged("ItemCount");
OnPropertyChanged("TotalItemCount");
}
}
public int VirtualPageCount
{
get { return (int)Math.Ceiling((double)VirtualItemCount / (double)PageSize); }
}
private int _pageIndex = 0;
private int _pageSize;
#region "IEnumerable"
public IEnumerator GetEnumerator()
{
return _sourceCollection.GetEnumerator();
}
#endregion
#region "IPagedCollectionView"
public bool CanChangePage
{
get { return !_isPageChanging; }
}
private bool _isPageChanging = false;
public bool IsPageChanging
{
get { return _isPageChanging; }
set { _isPageChanging = value; OnPropertyChanged("IsPageChanging"); }
}
public int ItemCount
{
get { return VirtualItemCount; }
}
public bool MoveToFirstPage()
{
return MoveToPage(0);
}
public bool MoveToLastPage()
{
return MoveToPage(VirtualPageCount - 1);
}
public bool MoveToNextPage()
{
return MoveToPage(PageIndex + 1);
}
public bool MoveToPage(int pageIndex)
{
if (pageIndex >= 0 && pageIndex <= (VirtualPageCount - 1))
{
PageChangingEventArgs pcea = new PageChangingEventArgs(pageIndex);
OnPageChanging(pcea);
if (!pcea.Cancel)
{
IsPageChanging = true;
_pageIndex = pageIndex;
IsPageChanging = false;
OnPropertyChanged("PageIndex");
OnPageChanged();
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
public bool MoveToPreviousPage()
{
return MoveToPage(PageIndex - 1);
}
public event EventHandler<EventArgs> PageChanged;
protected void OnPageChanged()
{
if (PageChanged != null)
PageChanged(this, EventArgs.Empty);
}
public event EventHandler<PageChangingEventArgs> PageChanging;
protected void OnPageChanging(PageChangingEventArgs e)
{
if (PageChanging != null)
PageChanging(this, e);
}
public int PageIndex
{
get { return _pageIndex; }
}
public int PageSize
{
get
{
return _pageSize;
}
set
{
_pageSize = value;
}
}
public int TotalItemCount
{
get { return VirtualItemCount; }
}
#endregion
#region "INotifyPropertyChanged"
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
#region"INotifyCollectionChanged"
public event NotifyCollectionChangedEventHandler CollectionChanged;
protected void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (CollectionChanged != null)
CollectionChanged(this, e);
}
#endregion
}
Basically it works by allowing you to specify a 'VirtualItemCount' aswel as your 'SourceCollection' so that you can dictate how many items there are in total (before paging has been applied).
Whether you are using MVVM or traditional DataContext, you should bind your grid and pager to the instance of the PagedVirtualCollectionView and handle the 'PageChanging' event. You can then replace the 'SourceCollection' with the current page of data and set the 'VirtualItemCount' to the total count of records.
E.g.
private PagedVirtualCollectionView _SearchResults = new PagedVirtualCollectionView(new List<SearchResult>());
public PagedVirtualCollectionView SearchResults
{
get { return _SearchResults; }
}
public MyViewModel()
{
_SearchResults.PageChanging += new EventHandler<PageChangingEventArgs>(SearchResultsPageChangingHandler);
}
protected void SearchResultsPageChangingHandler(object sender, PageChangingEventArgs e)
{
UpdateSearchResults(e.NewPageIndex);
}
public void UpdateSearchResults(int pageIndex)
{
}
void UpdateSearchResultsCompleted(object sender, SearchCompletedEventArgs e)
{
if (e.Error == null)
{
SearchResults.SourceCollection = e.Result;
SearchResults.VirtualItemCount = e.totalRecords;
}
}