Create a Google Style paging component in C#

Several years ago I wrote a an article about Creating a Google Style paging component in XSLT. Some users pointed out to me that the paging component wasn’t exactly Google style. Especially using the arrows didn’t comply with how Google does it, but apart from that it mimicked the Google paging very well.

This is another implementation, this time in C#. Again, I’m not using the Google style exactly. Instead I’m using a “rolling numbers” principle. The component specifies how many numbers should (at most) be present at a time, as demonstated here:

3 types of paging

3 states in the paging component

(Google uses a simple method where it defines how many number should be present before and after the current page (like my first XSLT), but this paging is better at sticking to the middle of a web page).

In this example (and in the code) I specify that I want 10 pages to be present.

Here is the code. First I define a Page class containing the information about a page. This is very simple, but you can build it as advanced as you need it.

/// <summary>
/// Page class containing the information used to create a paging control
/// </summary>
public class Page
{
  /// <summary>
  /// Gets or sets the title.
  /// </summary>
  /// <value>The title.</value>
  public string Title { get; set; }
   
  /// <summary>
  /// Gets or sets the page number.
  /// </summary>
  /// <value>The page num.</value>
  public string PageNum { get; set; }

  /// <summary>
  /// Gets or sets a value indicating whether this page is the current page.
  /// </summary>
  /// <value><c>true</c> if [current page]; otherwise, <c>false</c>.</value>
  public bool CurrentPage { get; set; }
}

Then I have a simple class returning a IEnumerable list of Page classes:

using System.Collections.Generic;

namespace MyProject
{
  /// <summary>
  /// Class generating a enumerable list of pages
  /// </summary>
  /// <remarks>
  /// Primarily used by the thumbnail list to create a paging control
  /// </remarks>
  public class PagingProvider
  {
    /// <summary>
    /// Creates a list of page numbers to be enumerated in a paging control.
    /// </summary>
    /// <remarks>
    /// Paging is 1-based, meaning that the first page is called page 1.
    /// </remarks>
    /// <param name="pageSize">Size of the page.</param>
    /// <param name="totalItems">The total items.</param>
    /// <param name="currentPage">The current page.</param>
    /// <returns></returns>
    public IEnumerable<Page> CreatePages(int pageSize, int totalItems, int currentPage)
    {
      List<Page> pages = new List<Page>();
      int totalPages = (totalItems / pageSize) + 1;
      int startIndex = 0;
      int endIndex = totalPages;
     
      if (totalPages > 10)
      {
        startIndex = currentPage - 5;
        endIndex = currentPage + 5;
        if (startIndex < 0)
        {
          startIndex = 0;
          endIndex = startIndex + 10;
        }
        if (endIndex > totalPages)
        {
          endIndex = totalPages;
          startIndex = totalPages - 10;
        }
      }
      pages.Add(new Page { Title = "««", PageNum = "1", CurrentPage = false });
      if (currentPage == 1)
        pages.Add(new Page { Title = "«", PageNum = (currentPage).ToString(), CurrentPage = false });
      else
        pages.Add(new Page { Title = "«", PageNum = (currentPage - 1).ToString(), CurrentPage = false });
      for (int i=startIndex;i<endIndex;i++)
      {
        Page page = new Page {Title = (i + 1).ToString(), PageNum = (i + 1).ToString(), CurrentPage = i == (currentPage-1)};
        pages.Add(page);
      }
      if (currentPage == totalPages)
        pages.Add(new Page { Title = "»", PageNum = (currentPage).ToString(), CurrentPage = false });
      else
        pages.Add(new Page { Title = "»", PageNum = (currentPage + 1).ToString(), CurrentPage = false });
      pages.Add(new Page { Title = "»»", PageNum = totalPages.ToString(), CurrentPage = false });
      return pages;
    }
  }
}

The function, CreatePages, is called using the information about the data to page, the page size (how many items you have per page), total number of items and the current page. The paging can be hooked to an asp:Repeater for example:

<asp:Repeater ID="repPagesBottom" runat="server" onitemcommand="repPages_ItemCommand">
  <HeaderTemplate>
    <div>
  </HeaderTemplate>
  <ItemTemplate>
    <span>
      <asp:Button CommandName="changePage" CommandArgument='<%# Eval("PageNum") %>' Text='<%# Eval("Title") %>' runat="server" CssClass='<%# Eval("CurrentPage") %>' />
    </span>
   </ItemTemplate>
   <FooterTemplate>
     </div>
   </FooterTemplate>
</asp:Repeater>

Simply apply the output from the CreatePages to the Datasource of the repeater.

Create a Google style paging component in XSLT

I’ve noticed how most of the Sitecore blogs takes up relatively advanced topics on Sitecore, so I’ve decided to share with you some of my more simpler tricks.

This article is meant as a tutorial.  This is a small XSLT I’ve done for several projects that will display a Google style paging selector for any list. Imagine you have a list with 200 items. Displaying all items on one page can slow down your page and make it cluttered. Instead we add a paging component (like Google does it) to the top of the list, and display only 10 items from the list at a time. When clicking the pager, the same page will be called with a parameter ?page=n. The list uses the querystring to determine which elements from the list to display.

Lets begin with the component. First I create a new xsl:template called tplPaging:

<xsl:template name="tplPaging">
  <!-- Identifies the number of items in the list -->
  <xsl:param name="numberOfItems" />
  <!-- Optional parameter identifying the default selected page. Default is 1 -->
  <xsl:param name="currentPage" select="sc:qs('page','1')" />
  ... <!-- more code here -->
</xsl:template>

The template needs 2 parameters, the length of the list (total number of items), and the current page. The current page value can be taken from the querystring, and the default value is page one. So if nothing is defined, we will start with page 1.

Then I need 3 variables: Total number of pages, the start page and the end page. In this case I will only draw 5 pages before and after the current page. Google draws 10, but my lists are shorter, so I’ll stick to 5.

<!-- Calculate the maximum number of pages to show in the paging component -->
<xsl:variable name="numberOfPages" select="floor((number($numberOfItems)-1) div 10)+1"/>

<!-- Calaulate the starting position of the numbers -->
<xsl:variable name="startPage">
  <xsl:choose>
    <xsl:when test="$currentPage > 6">
      <xsl:value-of select="$currentPage - 5"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="1"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:variable>

<!-- Calculate the ending position of the numbers -->
<xsl:variable name="endPage">
  <xsl:choose>
    <xsl:when test="$numberOfPages - $currentPage > 5">
      <xsl:value-of select="$currentPage + 5"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$numberOfPages"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:variable>

Now I can recursively draw the numbers in the paging component. I’ll show you the recursive template later. First the code to call the recursive loop:

<!-- Recursively draw the paging component -->
<ul>
  <xsl:if test="$startPage > 1">
	<li>
      <a href="?page={$currentPage - 6}">Previous</a></li>
  </xsl:if>
  <xsl:call-template name="tplNumber">
    <xsl:with-param name="current" select="$currentPage"/>
    <xsl:with-param name="number" select="$startPage"/>
    <xsl:with-param name="max" select="$endPage"/>
  </xsl:call-template>
  <xsl:if test="$currentPage + 5 < $numberOfPages">
	<li>
      <a href="?page={$currentPage + 6}">Next</a></li>
</xsl:if></ul>

Notice how I do not draw the Previous and the Next unless they are needed?
The template tplNumber is called to draw the actual numbers:

<xsl:template name="tplNumber">
  <xsl:param name="current"/>
  <xsl:param name="number"/>
  <xsl:param name="max"/>

  <xsl:choose>
    <xsl:when test="$number = $current">
      <!-- Show current page without a link -->
	<li class="current">
        <xsl:value-of select="$number"/>
     </li>
    </xsl:when>
    <xsl:otherwise>
	<li>
        <a href="?page={$number}"><xsl:value-of select="$number"/></a>
     </li>
    </xsl:otherwise>
  </xsl:choose>

  <!-- Recursively call the template untill we reach the max number of pages -->
  <xsl:if test="$number < $max">
    <xsl:call-template name="tplNumber">
      <xsl:with-param name="current" select="$current"/>
      <xsl:with-param name="number" select="$number+1"/>
      <xsl:with-param name="max" select="$max"/>
    </xsl:call-template>
  </xsl:if>
</xsl:template>

You can expand this function. What should the pager do if there is no items in the list? Anyway, the examle is actually very simple and shows how easy it is to do paging. And by the way – Sitecore will actually render this blindingly fast.

Follow

Get every new post delivered to your Inbox.

Join 92 other followers