Jan 31 2007

Accessible Ajax calls

Posted by admin under ASP.NET AJAX formally Atlas

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).