This article shows how to achieve Custom SEO Friendly paging for controls like Repeater or DataList those doesn't support paging out of box.
Introduction
This article gives A to Z solution to do SEO Friendly custom paging for ASP.NET Repeater and DataList controls that doesn't support paging out of box. To my experience this is one of the most efficient way of doing custom paging as this doesn't require you to keep ViewState of any of the controls enabled and one of the best thing is that it is SEO friendly so search engine like Google and Bing can crawl all records of the pages easily.
My sample page looks like below.

How to do SEO friendly custom paging
In order to explain custom paging for Repeater or DataList control, I have taken a small example of a database table that has AutoID and Title column. I am using SQL Server 2005 database in this sample.
ASPX Page - Repeater Control
Lets see the code for the repeater control.
<asp:Label ID="lblMessage" runat="Server" ForeColor="Red" EnableViewState="False"></asp:Label>
<h3>Repeater: Custom SEO Friendly Paging</h3>
<asp:Repeater ID="Repeater1" runat="server" EnableViewState="false">
<ItemTemplate>
<b><%# Eval("AutoID") %>.</b> <%# Eval("Title") %><br />
</ItemTemplate>
</asp:Repeater>
<asp:Literal ID="litPaging" runat="server" EnableViewState="False"></asp:Literal>
The first line Label (lblMessage) is to show if any error occurs. The ItemTemplate for the repeater control contains the AutoID and Title columns of the database. The last line is the Literal (litPaging) control that will list the links of different page numbers. Notice that all my controls has EnableViewState=false. This will ensure that no viewstate is being stored in the page to maintain its state and makes our page download faster.
Code behind page
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Data.SqlClient;
using System.Text;
public partial class paging : System.Web.UI.Page
{
string connStr = ConfigurationManager.AppSettings["ArtSQLConnStr"].ToString();
int _startIndex = 0;
int _thisPage = 1;
int _pageSize = 10;
int _totalNumberOfRows = 0;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
// check for current index
if (Request["start"] != null && Request["page"] != null)
{
int.TryParse(Request["start"].ToString(), out _startIndex);
int.TryParse(Request["page"].ToString(), out _thisPage);
}
BindGridViewArticels(); // bind articles
}
}
private void BindGridViewArticels()
{
DataTable dTable = new DataTable();
using (SqlConnection conn = new SqlConnection(connStr))
{
using (SqlCommand dCmd = new SqlCommand())
{
SqlParameter[] prms = new SqlParameter[4];
prms[0] = new SqlParameter("@startRowIndex", SqlDbType.Int);
prms[0].Value = _startIndex;
prms[1] = new SqlParameter("@pageSize", SqlDbType.Int);
prms[1].Value = _pageSize;
prms[3] = new SqlParameter("@totalCount", SqlDbType.Int);
prms[3].Direction = ParameterDirection.Output;
dCmd.CommandText = "LoadData";
dCmd.CommandType = CommandType.StoredProcedure;
dCmd.Parameters.AddRange(prms);
dCmd.Connection = conn;
using (SqlDataAdapter dAd = new SqlDataAdapter())
{
// assign the select command to the Adapter object
dAd.SelectCommand = dCmd;
// now open the connection
conn.Open();
dAd.Fill(dTable);
conn.Close(); // close the connection
}
_totalNumberOfRows = int.Parse(prms[3].Value.ToString());
}
}
Repeater1.DataSource = dTable;
Repeater1.DataBind();
litPaging.Text = GetPagingDone(_thisPage, _totalNumberOfRows, _pageSize,
"/csharp/paging.aspx", "");
}
private string GetPagingDone(int thisPageNo, int totalCount, int pageSize, string pageName, string extraQstringToAdd)
{
int pageno = 0;
int start = 0;
int loop = totalCount / pageSize;
int remainder = totalCount % pageSize;
StringBuilder strB = new StringBuilder("<br /><b><font color=\"green\">Page:</font> ", 500);
for (int i = 0; i < loop; i++)
{
pageno = i + 1;
if (pageno.Equals(thisPageNo))
strB.Append(pageno + " | ");
else
strB.Append("<a href=\"" + pageName + "?start=" + start + "&page=" + pageno + extraQstringToAdd + "\" title=\"Go to Page " + pageno + "\">" + pageno + "</a> | ");
start += pageSize;
}
if (remainder > 0)
{
pageno++;
if (pageno.Equals(thisPageNo))
strB.Append("<b>" + pageno + " </b>| ");
else
strB.Append("<a href=\"" + pageName + "?start=" + start + "&page=" + pageno + extraQstringToAdd + "\" title=\"Go to Page " + pageno + "\">" + pageno + "</a> | ");
}
return strB.ToString() + "</b></span>";
}
}
Get solutions of the .NET problems with video explanations, .pdf and source code in .NET How to's.
In the above code snippet, I have a 4 page level variables related with custom paging, they are:
- _startIndex - This is to store the start index of the row in the result set, this will help us to get the next page data
- _thisPage - This is to store the current page number
- _pageSize - This is to store the page size (no. of records per page)
- _totalNumberOfRows - This is to store the total number of rows in the database. This will help us in calculating the number of pages.
First, let's look at the BindGridViewArticles() method in above code snippets. This method contains simple SQL Server data access code that is using LoadData stored procedure with two input parameter (startRowIndex and pageSize) and one output parameter (totalCount). The code for LoadData stored procedure is below.
CREATE PROCEDURE [dbo].[LoadDataTest]
@startRowIndex int,
@pageSize int,
@totalCount int output
AS
BEGIN
SET NOCOUNT ON;
SET @totalCount = 0
SET @startRowIndex = @startRowIndex + 1
BEGIN
SELECT * FROM (
Select SampleTable.*, ROW_NUMBER() OVER (ORDER BY SampleTable.AutoID DESC) as RowNum
FROM SampleTable WHERE
SampleTable.Active = 1
) as ArticleList
WHERE
RowNum BETWEEN @startRowIndex AND (@startRowIndex + @pageSize) - 1
ORDER BY AutoID DESC
SELECT @totalCount = Count(AutoID) FROM SampleTable WHERE Active = 1
END
END
The first SELECT statement is getting paginated data from the database and last SELECT statement is getting the total number of active records from database. Note that 1st SELECT statement is only supported from SQL Server 2005 onward version of the SQL Server.
After executing the stored procedure in the BindGridViewArticles() method, I am binding the Repeate control with the resulst set (DataTable). The last line of this method is to write the number of pages and proper links for those pages.
The paging links are being formed in the GetPagingDone method that is taking five parameters, they are:
- thisPageNo - This is the value of current page being shown
- totalCount - This is the total number of records returned from the last SELECT statement of the stored procedure.
- pageSize - This is the size of the page (records per page)
- pageName - This is the name of the page where paging is being done (current page). Its good practice to keep this function in common utility class so that if you need to access this function from different pages in your project you can use this, in that case this parameter will be useful. (You can avoid passing this parameter by getting the current requested page with the help of Request object, so if you want to do that you can modify this method to use Request object).
- extraQueryStringToAdd - This is extra string you want to add with your paging links (In real scenario, you may want to pass your search filter parameters or category ids), I do not want to use this so I have kept empty.
The code inside GetPagingDone method is not very complicated, it's math to divide the total number of records by pageSize , loop through the dividend result (loop vairable) and write the paging links. If there is reminder write the last paging links as well.
If you want to achieve the same funtionality with the DatList control, simply replace the Repeater control code with the DataList control code like below.
<asp:Label ID="lblMessage" runat="Server" ForeColor="Red" EnableViewState="False"></asp:Label>
<h3>Repeater: Custom SEO Friendly Paging</h3>
<asp:DataList ID="DataList1" runat="server" EnableViewState="False">
<ItemTemplate>
<b><%# Eval("AutoID") %>.</b> <%# Eval("Title") %><br />
</ItemTemplate>
</asp:DataList>
<asp:Literal ID="litPaging" runat="server" EnableViewState="False"></asp:Literal>
Once you are done, run your code and you should see Custom SEO friendly paging of your records as displayed in the above picture. As EnableViewState property of all related controls are false so no need to worry about the ViewState size of the page. The links of different pages are hyperlink with querystring values so search engines will quickly grab them and crawl all those page.
This article described how to achieve Fast Custom SEO Friendly paging with Repeater and DataList controls. This technique can not only be applied in Repeater and DataList controls but also it can be used in other Data controls like GridView, DataGrid etc.
Hope you liked this article, please subscribe for RSS feed to get subsequent article alert in your email directly.