This article is intended for people who have used the GridView control and who
have discovered a specific limitation of the built-in paging links; bots and spiders cannot find content on any page but the first page because the links are javascript, not url's.
If you want search engines to index your content and you use the GridView control to display
pages of content that you want indexed, you may or may not be
aware that the spiders and robots cannot navigate the built-in paging links that the GridView
control generates. If you mouse over a link generated by a built-in paging
link you see that its a
javascript call, not a url, so they are blocked from proceeding.
If you do searches on custom paging you will
find that this topic is an important topic
with three different definitions of scope.
- Custom paging with a very large dataset due to performance issues.
- Modification of the look and feel of the existing GridView paging
- Discussions on the solutions to this specific issue.
In #1, the issue is performance with large datasets. In normal GridView paging the entire dataset is read on each
page and paging processing selects a subset of records and calls it a page. This leads to poor performance
when you think you may be dealing with thousands of records to page from. Those articles
deal partially with the display issue but also with how the records are selected. In my
case the dataset is pretty small, maybe 50 records, so I'm not concerned about performance.
In #2, discussions of custom GridView paging lead to custom
looks for the section of the GridView control which contains the paging contols through
modification of the PagerTemplate tag. However, the generated paging links are still javascript,
not url's. This still doesn't help me.
In #3 you find this specific issue and discussions of some workarounds. Some of the
workarounds that have been suggested are:
- Have a separate page with all of the links. I'm not crazy about a
separate page just with links, its something
else to have to maintain, and it could look wierd to a
user who found it. It is also a technique that is
frowned upon
by Google.
- Modify the sitemap for the site. I'd have to regenerate the site map every time
I added an entry to the database, plus I'm not 100% sure that all bots use the site map.
- Check the User Agent and possibly adjust the paging size. Attractive, but I doubt
that there is an exhaustive list of all User Agents that are bots.
Given the constraints of my design I decided that could take advantage of the existing
paging in the GridView control by generating my own paging links in the control that have
url's in them. I can create a common processing function to work with all of my GridView
controls and just pass in parameters to it.
There are limitations to this approach. For one, it requires that the url have a querystring
command on it that indicates the page to go to, and that the recieving code process that. While thats
normally not a huge deal, you have to make sure you don't step on any existing querystring.
If a page was called with the url "SomePlace.aspx?Parm1=34&Parm2=a%20b%20c" then those
existing values have to be maintained.
The other limitation is that by using a URL its completely bypassing .NET State management
and going directly back into the page. I have to make sure that my design for this
specific page doesn't rely on any state management.
I've created a sample application that demonstrates the method I used and the results. The
program is located here
on this web site. You can download the entire solution in a zip file as well as run it and see
the individual source files.
The basic technique relies on setting the GridView control to do standard
paging and then taking over the row that paging links appear on and rewriting them.
In this example
the ID of the GridView control is "gvSamples".
First I set the datasource and set the current page number from my querystring value. The
first time through the page will be 0, from then on the 'cpndx' value determine the page
number. Thats how the page number gets set.
gvSamples.DataSource = // data from a datatable
/*
* See if we need to set the page number
*/
if (!String.IsNullOrEmpty(Request["cpndx"]))
{
string s = Request["cpndx"];
gvSamples.PageIndex = Convert.ToInt32(s);
}
gvSamples.DataBind();
Now I modify the paging area in the GridView control by getting
the table where the links are stored. Step 1 is get the current
table and clear it out to make way for my links.
/*
* Get the row where paging data is stored
*/
GridViewRow pagerRow = gvSamples.BottomPagerRow;
Table tbl = (Table)pagerRow.Cells[0].Controls[0];
/*
* Clear the existing cells and add our own table cell
*/
tbl.Rows[0].Cells.Clear();
TableCell td = new TableCell();
tbl.Rows[0].Cells.Add(td);
Then I build the querystring, taking into account that there may be values there
already, and ignoring the entry that was my own paging entry.
string querystring = "";
for (int i = 0; i < Request.QueryString.Count; i++)
{
/*
* Get name and value of this querystring item
*/
string name = Request.QueryString.Keys[i];
string value = Request.QueryString.GetValues(name)[0];
/*
* Ignore my paging command, we're adding it later
*/
if (name == "cpndx") continue;
/*
* Append this query item. Remember to prepend '&' if this isn't
* the first item
*/
querystring += String.Format("{0}{1}={2}",
i == 1 ? "&" : "",
name,
Server.UrlPathEncode(value));
}
Now I can add a HyperLink control for each page in the data source. If this is the
current page I just put up a plain page number. Note that this code doesn't handle
very large datasets very well, if there were 40 pages of data the display would look
pretty cluttered. However, I can return to that issue when I'm dealing with a larger dataset.
/*
* Add an entry for each page
*/
for (int i = 0; i < gvSamples.PageCount; i++)
{
if (gvSamples.PageIndex != i)
{
HyperLink hl = new HyperLink();
/*
* Create the url that will let us re-enter and set the page
*/
hl.NavigateUrl = String.Format("{0}?{1}{2}cpndx={3}",
Request.Path,
querystring,
querystring == "" ? "" : "&",
i);
/*
* Add in the link and a space after it
*/
hl.Text = String.Format("{0}", i + 1);
td.Controls.Add(hl);
td.Controls.Add(new LiteralControl(" "));
}
else
{
/*
* Put the current page number up
*/
td.Controls.Add(new LiteralControl(String.Format("{0} ",i+1)));
}
}
I hope that this article has helped you with this issue. As mentioned, there are several
variations on the theme of custom paging, each one trying to solve a different issue.
There is not one solution any more than there is one problem. Feel free to run and download the
application
located here to
further explore this issue.