Jan
31
2007
Accessible Ajax calls
Say a paging scenario. You have a repeater and shows the pages one by one and also you have some sort of "pager". Personally I never use the built in paging mechanism for say the gridviews - simple because it needs a postback. Which means the pages after the first one will not be seen by search engines.
I try to build my pagers to use simple hyperlinks instead - pointing to say showcat.aspx?page=1 or showcat.aspx?page=2 etc.
Not very hard at all, it's just a matter of reading the Request["page"] and show that page.

and when clicking on next:

However now with Ajax we would probably want to do Ajax paging instead. Or rather be able to use both:if a javascript enabled browser accesses the page it should use Ajax paging - and if not (say a search engine bot) then we want to offer regular href links instead.
The trick is to somehow decorate the hyperlink so the "onClick" event makes a postback instead. So lets start with ASPX page:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="PagingByHyperlink._Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePartialRendering="true" />
<div>
Use Ajax for update or URL (i.e to simulate no javascript is available select Url)
<asp:DropDownList ID="ddlTurnOffJava" runat="server" AutoPostBack="true" OnSelectedIndexChanged="ddlTurnOffJava_SelectedIndexChanged">
<asp:ListItem Text="Url"></asp:ListItem>
<asp:ListItem Text="Ajax"></asp:ListItem>
</asp:DropDownList>
<br />
<asp:Label ID="lblTid" runat="server"></asp:Label>
<br />
Here the repater starts...
<hr />
<asp:UpdateProgress ID="UpdateProgress1" runat="server">
<ProgressTemplate>
Updating...
</ProgressTemplate>
</asp:UpdateProgress>
<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<asp:Repeater ID="rpt1" runat="server" OnItemDataBound="rpt1_ItemDataBound">
<HeaderTemplate>
Current page: <asp:Label ID="lblPage" runat="server"></asp:Label> of total: <asp:Label ID="lblTotal" runat="server"></asp:Label>
<br />
<asp:HyperLink ID="hlPrev" runat="server" NavigateUrl="cat.aspx?p=2">Prev</asp:HyperLink> ---
<asp:HyperLink ID="hlNext" runat="server" NavigateUrl="cat.aspx?p=2">Next</asp:HyperLink>
<asp:HiddenField ID="CurrentPage" runat="server" OnValueChanged="CurrentPage_ValueChanged" />
<asp:Label ID="lblTid" runat="server"></asp:Label><ul>
</HeaderTemplate>
<FooterTemplate></ul></FooterTemplate>
<ItemTemplate><li><asp:Label ID="lbl1" runat="server"></asp:Label></li></ItemTemplate>
</asp:Repeater>
</ContentTemplate>
<Triggers>
</Triggers>
</asp:UpdatePanel>
</div>
</form>
</body>
</html>
and the code behind:
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;
namespace PagingByHyperlink
{
public partial class _Default : System.Web.UI.Page
{
protected void RefreshRepeater(int nCurrentPage)
{
DataTable dt = Microsoft.ApplicationBlocks.Data.SqlHelper.ExecuteDataset(
"Data Source=(local);Initial Catalog=northwind;User ID=sa;PWD=stefan;", CommandType.Text, "select * from [customers]").Tables[0];
PagedDataSource oSource = new PagedDataSource();
oSource.AllowPaging = true;
oSource.PageSize = 10;
oSource.DataSource = dt.Rows;
if (nCurrentPage > oSource.PageCount)
oSource.CurrentPageIndex = 0;
else
oSource.CurrentPageIndex = nCurrentPage - 1;
rpt1.DataSource = oSource;
rpt1.DataBind();
}
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
lblTid.Text = DateTime.Now.ToString();
int nCurrentPage = 1;
if (Request["page"] != null && Request["page"].Length > 0)
{
nCurrentPage = Convert.ToInt32(Request["page"]);
}
if ( Session["UseAjax"] != null && Session["UseAjax"] == "true" )
ddlTurnOffJava.SelectedIndex = 1;
else
ddlTurnOffJava.SelectedIndex = 0;
//Get page
RefreshRepeater(nCurrentPage);
}
}
protected void CurrentPage_ValueChanged(object sender, EventArgs e)
{
int nCurrentPage = Convert.ToInt32(((sender) as HiddenField).Value);
RefreshRepeater(nCurrentPage);
}
protected void rpt1_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
DataRow row = e.Item.DataItem as DataRow;
Label lbl1 = e.Item.FindControl("lbl1") as Label;
lbl1.Text = row["companyname"].ToString();
/*
HyperLink hlPlayer = e.Item.FindControl("hlPlayer") as HyperLink;
hlPlayer.Text = oObject.Namn;
*/
}
if (e.Item.ItemType == ListItemType.Header)
{
PagedDataSource oSource = rpt1.DataSource as PagedDataSource;
Label lblPage = e.Item.FindControl("lblPage") as Label;
lblPage.Text = (oSource.CurrentPageIndex + 1).ToString();
Label lblTotal = e.Item.FindControl("lblTotal") as Label;
lblTotal.Text = (oSource.PageCount).ToString();
HyperLink hlPrev = e.Item.FindControl("hlPrev") as HyperLink;
HyperLink hlNext = e.Item.FindControl("hlNext") as HyperLink;
///Should we show next?
if (oSource.CurrentPageIndex >= oSource.PageCount-1)
hlNext.Visible = false;
if (oSource.CurrentPageIndex == 0)
hlPrev.Visible = false;
hlNext.NavigateUrl = "default.aspx?page=" + (oSource.CurrentPageIndex + 2).ToString();
hlPrev.NavigateUrl = "default.aspx?page=" + (oSource.CurrentPageIndex).ToString();
if (Session["UseAjax"] == "true")
{
//Add the Ajax click event
//We send it through the CurrentPage hiddenfield
HiddenField CurrentPageField = e.Item.FindControl("CurrentPage") as HiddenField;
hlNext.Attributes["onClick"] = "javascript:$get('" + CurrentPageField.ClientID + "').value = " + (oSource.CurrentPageIndex + 2).ToString() + ";__doPostBack('" + CurrentPageField.ClientID + "','');return false;";
hlPrev.Attributes["onClick"] = "javascript:$get('" + CurrentPageField.ClientID + "').value = " + (oSource.CurrentPageIndex).ToString() + ";__doPostBack('" + CurrentPageField.ClientID + "','');return false;";
}
}
if (e.Item.ItemType == ListItemType.Footer)
{
}
}
protected void ddlTurnOffJava_SelectedIndexChanged(object sender, EventArgs e)
{
if ( ddlTurnOffJava.SelectedIndex == 1 )
Session["UseAjax"] = "true";
else
Session["UseAjax"] = "false";
}
}
}
Thanks to Raj Kaimal I learned that a hidden field could be used for this kind of scenario. The interesting stuff happens inside if (e.Item.ItemType == ListItemType.Header).
We simply set the "onClick" attribute for the next and prev hyperlinks to a) set the CurrentPage value to the next resp previous page index - and then issues a postback. The "return false;" statement is just to stop the browser from going to the href location - we flag that everything is already handled.
HiddenField CurrentPageField = e.Item.FindControl("CurrentPage") as HiddenField;
hlNext.Attributes["onClick"] = "javascript:$get('" + CurrentPageField.ClientID + "').value = " + (oSource.CurrentPageIndex + 2).ToString() + ";__doPostBack('" + CurrentPageField.ClientID + "','');return false;";
Please note that the if (Session["UseAjax"] == "true") stuff is only used in this particular test application - a way for me to by using a dropdownlist simply test both behaviours.


The whole application is availabe for download (see attachments).