Creating a Custom EPiServer Paging Control

Posted on September 20, 2009 by Frederik Vig in Code Snippet, EPiServer

Earlier I wrote about the PageList control from EPiServer, I mentioned in that post that the markup that the PagingControl renders is not the best. For instance it will not work without JavaScript enabled on the client (for search engines the links wont work). I therefor decided to create my own.

To start with I went to the EPiServer SDK to have a look at the PagingControl class. Here I was able to see the namespace and what .dll file it lives in (EPiServer.dll). I then opened up Reflector, to inspect EPiServer.dll and have a closer look at the code.

Description of Reflector by Red Gate.

.NET Reflector enables you to easily view, navigate, and search through, the class hierarchies of .NET assemblies, even if you don’t have the code for them. With it, you can decompile and analyze .NET assemblies in C#, Visual Basic, and IL.

To find the PagingControl class you can either use the search feature or navigate the namespaces.

PagingControl class with Reflector

The great thing is that all the methods are virtual which means that we can override them :).

Start by creating a new class and inherit from PagingControl (remember to add using EPiServer.Web.WebControls;).

using System;
using System.Text;
using System.Web;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using EPiServer;
using EPiServer.Core;
using EPiServer.Web.WebControls;
 
namespace FV.Templates.FV.Classes
{
    public class CustomPager : PagingControl
    {
    }
}

Then create a new page template with a PageList control.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Page.aspx.cs" Inherits="FV.Templates.FV.Pages.Page" MasterPageFile="~/Templates/Public/MasterPages/MasterPage.master" %>
<asp:Content runat="server" ContentPlaceHolderID="MainBodyRegion">
    <EPiServer:PageList runat="server" ID="pglList">
        <HeaderTemplate>
            <ul>
        </HeaderTemplate>
        <ItemTemplate>
            <li><EPiServer:Property runat="server" PropertyName="PageLink" /></li>
        </ItemTemplate>
        <FooterTemplate>
            </ul>
        </FooterTemplate>
    </EPiServer:PageList>
</asp:Content>
using System;
using EPiServer;
using EPiServer.Core;
using FV.Templates.FV.Classes;
 
namespace FV.Templates.FV.Pages
{
    public partial class Page : TemplatePage
    {
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
 
            pglList.Paging = true;
            pglList.PageLink = PageReference.StartPage;
            pglList.PagingControl = new CustomPager();
            pglList.PagesPerPagingItem = 2;
        }
    }
}

Notice that we set the PageList’s PagingControl property to a new instance of our CustomPager web control.

If you open up a browser and run the code, you’ll just get a standard PagingControl with the JavaScript and bad markup (since we haven’t overridden anything yet).

Lets start by adding an ordered list for the paging items.

To do this we have to override the CreatePagingItems, AddSelectedPagingLink and AddUnselectedPagingLink methods.

using System;
using System.Text;
using System.Web;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using EPiServer;
using EPiServer.Core;
using EPiServer.Web.WebControls;
 
namespace FV.Templates.FV.Classes
{
    public class CustomPager : PagingControl
    {
        private HtmlGenericControl Container
        {
            get; set;
        }
 
        private static string Translate(string text)
        {
            return LanguageManager.Instance.Translate("/webcontrols/paging/" + text);
        }
 
        protected override LinkButton AddSelectedPagingLink(int pagingIndex, string text, string altText)
        {
            var li = new HtmlGenericControl("li");
            li.Attributes.Add("class", this.CssClassSelected);
            li.Attributes.Add("title", altText);
            li.InnerText = text;
            this.Container.Controls.Add(li);
            return null;
        }
 
        protected override LinkButton AddUnselectedPagingLink(int pagingIndex, string text, string altText, bool visible)
        {
            var li = new HtmlGenericControl("li");
            var child = this.CreatePagingLink(pagingIndex, text, altText);
            child.CssClass = this.CssClassUnselected;
            li.Visible = visible;
 
            li.Controls.Add(child);
            this.Container.Controls.Add(li);
            return null;
        }
 
        public override void CreatePagingItems(PageDataCollection pages)
        {
            int pagingIndex = this.CurrentPagingItemCount - 1;
            bool visible = this.CurrentPagingItemIndex > 0;
            bool flag2 = this.CurrentPagingItemIndex < pagingIndex;
            if ((pagingIndex != 0) || !this.AutoPaging)
            {
                this.Container = new HtmlGenericControl("ol");
                this.LinkCounter = 0;
                if (this.FirstPagingItemText.Length > 0)
                {
                    this.AddUnselectedPagingLink(0, this.FirstPagingItemText, Translate("firstpage"), visible);
                }
                if (this.PrevPagingItemText.Length > 0)
                {
                    this.AddUnselectedPagingLink(this.CurrentPagingItemIndex - 1, this.PrevPagingItemText, Translate("prevpage"), visible);
                }
                for (int i = 0; i <= pagingIndex; i++)
                {
                    if (i == this.CurrentPagingItemIndex)
                    {
                        this.AddSelectedPagingLink(i, Convert.ToString((int)(i + 1)), Translate("currentpage"));
                    }
                    else
                    {
                        this.AddUnselectedPagingLink(i, Convert.ToString((int)(i + 1)), string.Format(Translate("pagenumber"), i + 1), true);
                    }
                }
                if (this.NextPagingItemText.Length > 0)
                {
                    this.AddUnselectedPagingLink(this.CurrentPagingItemIndex + 1, this.NextPagingItemText, Translate("nextpage"), flag2);
                }
                if (this.LastPagingItemText.Length > 0)
                {
                    this.AddUnselectedPagingLink(pagingIndex, this.LastPagingItemText, Translate("lastpage"), flag2);
                }
 
                this.Controls.Add(this.Container);
            }
        }
    }
}

I added a private property to hold my ordered list (Container), so that I can easily add child controls to it. I Also removed the calls to the AddLinkSpacing method (since we don’t need it).

If you browse the page you’ll see that everything is inside an ordered list now.

To fix the JavaScript links I created a new method CreatePagingHyperLink that will create the HyperLink control with the correct url. I Then added code in the OnInit method to get the query string and set the PageList’s CurrentPagingItemIndex property.

using System;
using System.Text;
using System.Web;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using EPiServer;
using EPiServer.Core;
using EPiServer.Web;
using EPiServer.Web.WebControls;
 
namespace FV.Templates.FV.Classes
{
    public class CustomPager : PagingControl
    {
        private HtmlGenericControl Container
        {
            get; set;
        }
 
        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
 
            if (HttpContext.Current.Request.QueryString["p"] == null)
            {
                return;
            }
 
            var p = HttpUtility.HtmlEncode(HttpContext.Current.Request.QueryString["p"]);
            int num;
 
            int.TryParse(p, out num);
            this.CurrentPagingItemIndex = num;
        }
 
        private static string Translate(string text)
        {
            return LanguageManager.Instance.Translate("/webcontrols/paging/" + text);
        }
 
        protected override LinkButton AddSelectedPagingLink(int pagingIndex, string text, string altText)
        {
            var li = new HtmlGenericControl("li");
            li.Attributes.Add("class", this.CssClassSelected);
            li.Attributes.Add("title", altText);
            li.InnerText = text;
            this.Container.Controls.Add(li);
            return null;
        }
 
        protected override LinkButton AddUnselectedPagingLink(int pagingIndex, string text, string altText, bool visible)
        {
            var li = new HtmlGenericControl("li");
            var child = this.CreatePagingHyperLink(pagingIndex, text, altText);
            child.CssClass = this.CssClassUnselected;
            li.Visible = visible;
 
            li.Controls.Add(child);
            this.Container.Controls.Add(li);
            return null;
        }
 
        private static string CreateUrl(int count)
        {
            var url = new UrlBuilder(HttpContext.Current.Request.Url);
 
            if (UrlRewriteProvider.IsFurlEnabled)
            {
                Global.UrlRewriteProvider.ConvertToExternal(url, null, Encoding.UTF8);
            }
 
            return UriSupport.AddQueryString(url.ToString(), "p", count.ToString());
        }
 
        protected HyperLink CreatePagingHyperLink(int pagingIndex, string text, string altText)
        {
            var link = new HyperLink();
            this.LinkCounter++;
            link.ID = "PagingID" + this.LinkCounter;
            link.NavigateUrl = CreateUrl(pagingIndex);
            link.Text = text;
            link.ToolTip = altText;
 
            return link;
        }
 
        public override void CreatePagingItems(PageDataCollection pages)
        {
            int pagingIndex = this.CurrentPagingItemCount - 1;
            bool visible = this.CurrentPagingItemIndex > 0;
            bool flag2 = this.CurrentPagingItemIndex < pagingIndex;
            if ((pagingIndex != 0) || !this.AutoPaging)
            {
                this.Container = new HtmlGenericControl("ol");
                this.LinkCounter = 0;
                if (this.FirstPagingItemText.Length > 0)
                {
                    this.AddUnselectedPagingLink(0, this.FirstPagingItemText, Translate("firstpage"), visible);
                }
                if (this.PrevPagingItemText.Length > 0)
                {
                    this.AddUnselectedPagingLink(this.CurrentPagingItemIndex - 1, this.PrevPagingItemText, Translate("prevpage"), visible);
                }
                for (int i = 0; i <= pagingIndex; i++)
                {
                    if (i == this.CurrentPagingItemIndex)
                    {
                        this.AddSelectedPagingLink(i, Convert.ToString((int)(i + 1)), Translate("currentpage"));
                    }
                    else
                    {
                        this.AddUnselectedPagingLink(i, Convert.ToString((int)(i + 1)), string.Format(Translate("pagenumber"), i + 1), true);
                    }
                }
                if (this.NextPagingItemText.Length > 0)
                {
                    this.AddUnselectedPagingLink(this.CurrentPagingItemIndex + 1, this.NextPagingItemText, Translate("nextpage"), flag2);
                }
                if (this.LastPagingItemText.Length > 0)
                {
                    this.AddUnselectedPagingLink(pagingIndex, this.LastPagingItemText, Translate("lastpage"), flag2);
                }
 
                this.Controls.Add(this.Container);
            }
        }
    }
}

I also added some CSS code to give you an idea of how easy it is to style and change the look.

.PagingContainer ol {
    margin: 0;
    padding: 0;
    overflow: hidden;
}
 
.PagingContainer li {
    list-style: none;
    display: inline;
}
 
.PagingContainer a, .SelectedPagingItem {
    text-decoration: none;
    float: left;
    padding: .2em;
    border: 1px solid #303233;
    color: #303233;
    font-weight: bold;
    margin-right: .1em;
}
 
.PagingContainer .SelectedPagingItem {
    background: #303233;
    color: #fff;
}

The result

Custom paging control

Markup rendered

<div class="PagingContainer">
        <ol>
            <li class="SelectedPagingItem" title="Current page">1</li>
            <li>
                <a id="ctl00_MainRegion_MainContentRegion_MainBodyRegion_pglList_ctl05_PagingID4" title="Page 2" class="UnselectedPagingItem" href="http://fv/en/Test-Paging/?p=1">2</a>
            </li>
            <li>
                <a id="ctl00_MainRegion_MainContentRegion_MainBodyRegion_pglList_ctl05_PagingID5" title="Page 3" class="UnselectedPagingItem" href="http://fv/en/Test-Paging/?p=2">3</a>
            </li>
            <li>
                <a id="ctl00_MainRegion_MainContentRegion_MainBodyRegion_pglList_ctl05_PagingID6" title="Next page" class="UnselectedPagingItem" href="http://fv/en/Test-Paging/?p=1">></a>
            </li>
            <li>
                <a id="ctl00_MainRegion_MainContentRegion_MainBodyRegion_pglList_ctl05_PagingID7" title="Last page" class="UnselectedPagingItem" href="http://fv/en/Test-Paging/?p=2">»</a>
            </li>
        </ol>
    </div>

Download the code

Related Posts: