I know this topic is a little run of the mill and you can probably find built in solutions for pagination within one of the latest Silverlight toolkits but I thought I’d take a minute to review a custom pagination solution for Silverlight 3.
PaginationHelper<object> phelper = new PaginationHelper<object>(new ObservableCollection<object>(), 10);
dg.ItemsSource = phelper;
dg.SelectedIndex = 0;
stackpanel_pagination.Children.Add(phelper.GetPaginationControl());
Without getting too fancy I’ve developed a PaginationHelper class that extends an ObservableCollection<T>. In this way we can use the class itself as the DataContext or ItemsSource for the FrameworkElement in question.
public class PaginationHelper<T> : ObservableCollection<T>
{
private ObservableCollection<T> ItemsRef { get; set; }
private int ItemCount { get; set; }
private int PageItemCount { get; set; }
private int TotalPages { get; set; }
...
So our basic steps for setting up pagination for a control:
a) Instantiate the PaginationHelper()
b) Set the DataContext/ItemsSource for our control
c) Generate/Add our Pagination Control
a) Instantiate the PaginationHelper()
PaginationHelper<object> phelper = new PaginationHelper<object>(new ObservableCollection<object>(), 10);
private ObservableCollection<T> ItemsRef { get; set; }
private int ItemCount { get; set; }
private int PageItemCount { get; set; }
private int TotalPages { get; set; }
private int CurrentPage { get; set; }
private int IndexDisplay = 5;
private int ActiveIndexDisplay { get; set;}
private StackPanel sp;
private double opacity = 0.5;
public PaginationHelper(ObservableCollection<T> items, int pageItemCount)
{
ItemsRef = items;
ItemCount = ItemsRef.Count();
PageItemCount = pageItemCount;
TotalPages = Convert.ToInt32(Math.Ceiling((double)ItemCount / (double)PageItemCount));
CurrentPage = -1;
ActiveIndexDisplay = 1;
}
Our Properties:
ItemsRef – stores the original DataContext/ItemsSource for reference.
ItemCount – Total Items in the ItemsRef (could just use ItemsRef.Count()).
PageItemCount – Items per “Page” of data.
TotalPages – TotalPages required to paginate contents of ItemsRef as per PageItemCount.
CurrentPage – Current active page.
IndexDisplay – Number Page shortcuts to display (pagination control element)
ActiveIndexDisplay – Group index of active IndexDisplay.
Sp – Our pagination control element parent
Opacity - non-focused control element buttons (faded)
b) Set the DataContext/ItemSrouce for our Control
dg.ItemsSource = phelper;
dg.SelectedIndex = 0;
This Step is pretty simple, as the PaginationHelper is an ObservableCollection<T> in itself it can serve as the DataContext/ItemSource for your control.
c) Set the DataContext/ItemSrouce for our Control
stackpanel_pagination.Children.Add(phelper.GetPaginationControl());
This step calls the GetPaginationControl() function which generates a number of buttons that each call and load a page of items, and is returned as a FrameworkElement (StackPanel). The page the btn represents is stored as an int in its Tag property. The returned FrameworkElement can be placed really wherever you want. I placed mine underneath the DataGrid I set the PaginationHelper up for.

public FrameworkElement GetPaginationControl()
{
sp = new StackPanel();
sp.Height = 21;
sp.Orientation = Orientation.Horizontal;
for (int i = 1; i <= TotalPages; i++)
{
Button btn_page = new Button();
btn_page.Name = "page" + i.ToString();
btn_page.Opacity = opacity;
btn_page.Content = i.ToString();
btn_page.Style = (Style)App.Current.Resources["ButtonTabStyle"];
btn_page.Width = 17;
btn_page.Height = 17;
btn_page.Tag = i;
btn_page.MouseEnter += (s, e) =>
{
btn_page.Cursor = Cursors.Hand;
btn_page.Opacity = 1.0;
};
btn_page.MouseLeave += (s, e) =>
{
btn_page.Cursor = Cursors.Arrow;
if ((CurrentPage) != (int)btn_page.Tag)
{
btn_page.Opacity = opacity;
}
};
btn_page.Click += new RoutedEventHandler(btn_page_Click);
sp.Children.Add(btn_page);
if (i > IndexDisplay)
{
btn_page.Visibility = Visibility.Collapsed;
}
}
GetPage(1);
return sp;
}
When a page button is clicked the GetPage function is called which takes the clicked button’s tag (int) property, retrieves a list of items representing the requested “page” of data, and reassigns those items to the entire class itself. As the PaginationHelper class extends ObservableCollection<T> (see INotifyPropertyChanged), the displayed data updates itself.
It’s worth noting as well that using a back and forward button users can manually iterate page loads in an ascending/descending pattern, and that multiple pages of pages can be setup such that iterating beyond the scope of the current displayed collection of pages will load the next set of pages:
private void SetPaginationVisibility()
{
if(sp != null)
{
for (int i = 1; i <= TotalPages; i++)
{
if (sp.Children[i].GetType() == typeof(Button))
{
Button btn = (Button)sp.Children[i];
if (btn.Tag != null)
{
int high = IndexDisplay * ActiveIndexDisplay;
int low = high - IndexDisplay;
if ((int)btn.Tag > low && (int)btn.Tag <= high)
{
btn.Visibility = Visibility.Visible;
}
else
{
btn.Visibility = Visibility.Collapsed;
}
}
}
}
}
}
Conclusion
There are a few things that could be cleaned up, there may be better ways of implemented a pagination solution, but all in all this turned out to be a pretty simple working pagination helper.