Click here to Skip to main content
15,899,679 members
Articles / Programming Languages / C#
Tip/Trick

C# paging helper class

Rate me:
Please Sign up or sign in to vote.
2.00/5 (1 vote)
7 Jan 2012CPOL 33.7K   7   5
It's common to extract a single page from a set of records. This utility simplifies the maths involved.

A common way to extract a page from a set of records by checking whether the index of that record is between the index of the first and last items on that page.


While not difficult to do it's easy to accidentally get wrong, if you don't crunch the numbers properly.


This PagingLocation class allows a paging location to be set (like a set of co-ordinates) and then passed to the data access tier to tell it which page to extract.


It includes an IsInPage(int) function which will tell you if the index you provide is within the specified page.


It also has an AbsoluteTotal property which holds the total number of items found in all pages. This is used by the UI to calculate how many pages to let the user choose from.


Here's a crude example of how it's used:


C#
void Page_Load(object sender, EventArgs e)
{
  int pageIndex = 2;
  int pageSize = 10;
  PagingLocation location = new PagingLocation(pageIndex, pageSize);

  ArrayList page = GetPage(location);

  grid.DataSource = page;
  grid.VirtualItemCount = location.AbsoluteTotal;
  grid.DataBind();
}

ArrayList GetPage(PagingLocation location)
{
  ArrayList list = GetAllData();

  location.AbsoluteTotal = list.Count;

  for (int i = 0; i < list.Count; i++)
  {
    if (location.IsInPage(i))
        page.Add(list[i]);
  }
}

Here's a real world example of it being used by a data access component:


C#
protected virtual IEntity[] GetPage(IObjectSet objectSet, PagingLocation location)
{
    int i = 0;
    
    List<IEntity> list = new List<IEntity>();
    
    // Loop through each index in the object set
    for (i = 0; i < objectSet.Count; i++)
    {
        // If it's not in the current page then skip it
        if (location.IsInPage(i))
        {
            // Add the entity to the collection
            list.Add((IEntity)objectSet[i]);
        }
    }
    
    location.AbsoluteTotal = i;
    
    return list.ToArray();
}

http://code.google.com/p/sitestarter/source/browse/trunk/Src/App/SoftwareMonkeys.SiteStarter.Data.Db4o/Db4oDataIndexer.cs#791[^]


Here's the PagingLocation class (with the logging code removed):


C#
using System;
using SoftwareMonkeys.SiteStarter.Diagnostics;

namespace SoftwareMonkeys.SiteStarter.Entities
{
    /// <summary>
    /// Holds the coordinates to a specific page on an index.
    /// </summary>
    public class PagingLocation : IPagingLocation
    {
        private int pageIndex;
        /// <summary>
        /// Gets/sets the current page index.
        /// Note: This is 0 based so PageIndex=(PageNumber-1)
        /// </summary>
        public int PageIndex    
        {
            get { return pageIndex; }
            set { pageIndex = value; }
        }
       
        private int pageSize;
        /// <summary>
        /// Gets/sets the size of each page.
        /// </summary>
        public int PageSize
        {
            get { return pageSize; }
            set { pageSize = value; }
        }
       
        private int absoluteTotal;
        /// <summary>
        /// Gets/sets the absolute total count of all matching items,
        /// including those on ALL pages, not just the specified one.
        /// </summary>
        public int AbsoluteTotal
        {
            get { return absoluteTotal; }
            set { absoluteTotal = value; }
        }
       
        /// <summary>
        /// Empty constructor.
        /// </summary>
        public PagingLocation()
        {
        }
               
        /// <summary>
        /// Sets the page index and page size of the current location.
        /// </summary>
        /// <param name="pageIndex">The index of the current page.</param>
        /// <param name="pageSize">The size of each page.</param>
        public PagingLocation(int pageIndex, int pageSize)
        {
                PageIndex = pageIndex;
                PageSize = pageSize;
        }
       
       
        /// <summary>
        /// Checks whether the specified position is within the specified page.
        /// </summary>
        /// <param name="i">The 0 based index of item to check.</param>
        /// <returns>A bool value indicating whether the specified
        ///   index position is within the specified page.</returns>
        public bool IsInPage(int i)
        {
                // Create the return flag
                bool isInPage = false;
                   
                // Calculate the position of the first item on the page
                int first = (pageIndex * pageSize); // 0 based
               
                // Calculate the position of the last item on the page
                int last = ((pageIndex * pageSize) + pageSize) -1;
                // -1 to make it the last of the page, instead of first item on next page
               
               
                // The position is in the current page if it is between or equal
                // to the first and last items on the page
                isInPage = i >= first
                        && i <= last;
     
                return isInPage;
        }
    }
}

http://code.google.com/p/sitestarter/source/browse/trunk/Src/App/SoftwareMonkeys.SiteStarter.Entities/PagingLocation.cs[^]


To ensure it works properly here are a bunch of unit tests: http://code.google.com/p/sitestarter/source/browse/trunk/Src/App/SoftwareMonkeys.SiteStarter.Entities.Tests/PagingLocationTests.cs[^]

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
SoftwareMonkeys
Australia Australia
Founder of www.softwaremonkeys.net

Comments and Discussions

 
GeneralRe: Good tip. Now I'm creating a RavenDB provider for the data m... Pin
SoftwareMonkeys19-Jan-12 22:18
SoftwareMonkeys19-Jan-12 22:18 
GeneralRe: Use the System.Linq library. Then you can turn this: ... Pin
James Hurburgh11-Jan-12 18:27
James Hurburgh11-Jan-12 18:27 
GeneralReason for my vote of 2 There are much simpler and more obvi... Pin
James Hurburgh9-Jan-12 19:50
James Hurburgh9-Jan-12 19:50 
GeneralRe: My mistake I figured it was all fairly self explanatory so I... Pin
SoftwareMonkeys9-Jan-12 23:52
SoftwareMonkeys9-Jan-12 23:52 
My mistake I figured it was all fairly self explanatory so I skimped on the details.
The goal is to be able to hold all the paging related info on a single object and, while it's at it, provide a helper function for you to use if you want to.
Before this I was passing multiple paging properties into my data layer (index, size) and then had to figure out how to return the absolute total as required by the ASP.NET grid controls.
Putting the paging info into an object prevents the order of the parameters from being mixed up and provides a useful way to return the absolute total back to the UI layer from the data layer.
I use db4o and I can loop through the IObjectSet and load only items those within the current page, rather than loading all of them. This saves the performance hit of loading all items, even the ones not on the desired page.
As with most for loops what I have to work with is the position in the list/loop, which is the integer passed to the IsInPage function. The return value tells you when to load the record and include it in the output.
The PagingLocation class and this approach can be used with any kind of record set, or from any collection of objects. Just look at the GetPage function to see how that's done. Swap the IObjectSet for an array, or a collection, or something.
If you can suggest ways to simplify the paging in the SiteStarter project I'm all ears.
I totally agree there are plenty of other ways to store data, access data, etc. and depending on how you do it you may do paging in a way that's completely incompatible with this. There's always different ways to do things. But there are plenty of cases when the PagingLocation can make it simple to extract just a page of items out of a larger list of items.
Generalthis is all I can say: http://i0.kym-cdn.com/photos/images/o... Pin
Seishin#8-Jan-12 1:44
Seishin#8-Jan-12 1:44 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.