Nice Clean Example

Modifying the paging in the GridView control so that spiders and robots can navigate it

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.

  1. Custom paging with a very large dataset due to performance issues.
  2. Modification of the look and feel of the existing GridView paging
  3. 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.

1 responses