Implementing Continuous Scrolling UI Pattern in ASP.NET

When you have numerous records to show, we have to resort to Paging. There is a better alternative to display voluminous data (especially read only data) while providing a better user experience & also making efficient use of server bandwidth - the Continuous Scrolling or Infinite Scrolling UI Pattern. Read on to know more about how to implement Continuous Scrolling UI Pattern in ASP.NET with a GridView.

If you have used Google Reader, Live Image Search or Wikia Search (the open source search engine), you may have noticed how the Infinite Scrolling or the Continuous Scrolling UI Pattern works.



UIPatterns explains Continuous Scrolling thus:
In contrast to the Pagination patterns, the Continuous Scrolling pattern has no natural break. When using pagination patterns, a decision to only show a subset of data at a time and then let the user request more data if wanted is chosen. With the Continuous Scrolling, new data is automatically retrieved as the user has scrolled to the bottom of the page. It thus appears as if the page has no end, as more data will be loaded and inserted into the page each time the user scrolls to the bottom of page.

The minimalistic code sample explained here will show how to fetch records asynchronously on scrolling from a hypothetical Orders table after the first batch is initially fetched. A Generic Handler (which is similar to an ASPX page but lacks HTML tags) is used to retrieve records asynchronously and hand it over to the parent page. So the sample basically consists of  the following 2 files which you will have to copy to a ASP.NET 2.0 Website in VS.NET 2005 or VS.NET 2008 -
FetchOnDemand.aspx
AsyncHandler.ashx

All the JavaScript magic to track when the user reaches the end of the scroll bar (in this example we simulate DIV scrolling  by restricting it's height & setting overflow:auto style for the DIV tag) is done effortlessly by jQuery, a "JavaScript Library that simplifies HTML document traversing, event handling, animating, and Ajax interactions for rapid web development".

You can grab the external JavaScript file representing the jQuery library need for this sample from either of these URLs  -
http://code.jquery.com/jquery-latest.js
http://ajax.googleapis.com/ajax/libs/jquery/1.2.3/jquery.min.js

You can copy and place the external JavaScript file in a JS folder under the ASP.NET 2.0 website you create in VS.NET for running this code sample or you may even choose to reference it directly in the code. The jQuery library will be used in the parent page (FetchOnDemand.aspx) to trigger fetching of new records dynamically on scrolling to the bottom. The parent page communicates the unique Id related to the last row that was previously fetched to a Generic Handler (AsyncHandler.ashx). The Handler in turn returns a batch of new records asynchronously as a HTML table so that it can be injected at the end of table rendered by the GridView in the parent page.

Onto the code ...

FetchOnDemand.aspx displays the first 20 records through a GridView. jQuery helps us get the last Order Id from the last row of the table rendered from the GridView. We pass this as parameter to the Generic Handler's querystring -
"AsyncHandler.ashx?lastOrderId="

<%@ Page Language="C#" %>

<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
         if (!IsPostBack)
        {
            DataSet ds = new DataSet();
            SqlConnection conn = new SqlConnection("_place_your_connection_string_here");
            string _cmdText = "Select TOP 20 OrderID, Convert(Varchar,OrderDate,101) OrderDate, Quantity From Orders";
            SqlDataAdapter da = new SqlDataAdapter(_cmdText, conn);
            da.Fill(ds, "Orders");
            tblOrders.DataSource = ds;
            tblOrders.DataBind();
        }
    }  
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Dynamic Content Load On Scroll</title>
    <style type="text/css">
        body
        {
            font-family: Verdana, Arial, Helvetica, sans-serif;
            font-size: 11px;
            color: #666666;
        }
        .divLeft
        {
            height: 200px;
            border: solid 1px black;
            width: 300px;
            text-align: left;
            overflow: auto;
        }
        .divProgress
        {
            width: 15%;
            background-color: red;
            color: white;
        }
        .tblHeader
        {
            font-weight: bold;
            text-align: left;
            background-color: gray;
            color: black;
        }
        td
        {
            text-align:center;
        }
    </style>

    <script src="js/jquery-1.2.6.js" type="text/javascript"></script>

    <script type="text/javascript">
        //following code utilizes jQuery 1.2.6
        var prev = 0;
         $(document).ready(
         
         //DIV showing the message "Loading..." is hidden initially
        //The message will be shown when records are fetched with AJAX
        //when user has scrolled to the bottom of the DIV scrollbar
        function() {
             $(".divProgress").hide();

             $(".divLeft").scroll(
            
        function() {
        //triggering point is when the difference of the heights of the TABLE
        //and DIV match the DIV's scrollTop value
        if ($("#tblOrders").height() - this.scrollTop == $(this).height()) {
        //progress bar        
        $(".divProgress").ajaxStart(function() {
             $(this).show();
        });
         $(".divProgress").ajaxStop(function() {
             $(this).hide();
        });

        //get last Order Id to track next fetch
        var OrderIdLast = $("#tblOrders tr:last").children("td:first").html();

        //get last table row in order to append the new result set increment
        var trLast = $("#tblOrders tr:last");
        if (parseInt(OrderIdLast, 10) > parseInt(prev, 10)) {
            prev = OrderIdLast;
             //make a async call to fetch the incremental results    
            $.post("AsyncHandler.ashx?lastOrderId=" + OrderIdLast, function(data) {
                 if (data != null) {
                     //append new result set to last row
                    trLast.after(data);
                }
            });
        }
    }
});
});
    </script>
</head>
<body>
    <h3>This is a demo to show Continous Scrolling UI Pattern</h3>
    <form id="form1" runat="server">
    <div class="divLeft">
        <asp:GridView ID="tblOrders" runat="server" AutoGenerateColumns="false" CellPadding="2" Width="100%">
            <HeaderStyle CssClass="tblHeader" />
            <Columns>
                <asp:TemplateField HeaderText="Order Id">
                     <ItemTemplate>
                          <%#Eval("OrderID")%>
                    </ItemTemplate>
                </asp:TemplateField>
                <asp:TemplateField HeaderText="Order Date">
                    <ItemTemplate>
                        <%# Eval("OrderDate")%>
                        <input type="hidden" id="htxtOrderId" runat="server" value='<%#Eval("OrderID")%>' />
                    </ItemTemplate>
                </asp:TemplateField>
                <asp:TemplateField HeaderText="Quantity">
                     <ItemTemplate>
                          <%#Eval("Quantity")%>
                    </ItemTemplate>
                </asp:TemplateField>
            </Columns>
        </asp:GridView>
    </div>
    <div class="divProgress">
        Loading....
     </div>
    </form>
</body>
</html>


AsyncHandler.ashx keeps the weight of the response lighter than an equivalent ASPX page. It takes a OrderId value & returns the next five records formatted in a HTML table so that it can be readily appended to the table in the parent view containing the bigger set of records.


<%@ WebHandler Language="C#" Class="AsyncHandler" %>

using System;
using System.Web;
using System.Text;
using System.Data;
using System.Data.SqlClient;

public class AsyncHandler : IHttpHandler {
    
     public void ProcessRequest (HttpContext context) {
         
        //REMOVE BELOW LINE if you are using it in a real application
        //It is here to simulate the delay while fetching results
        System.Threading.Thread.Sleep(2000);
        
         //The last OrderId is used to get the next increment
        string lastOrderId = Convert.ToString(context.Request.QueryString["lastOrderId"]);

        //The PrepareDataSet method stuffs the DataSet into a HTML table
        context.Response.Write(PrepareDataSet(lastOrderId));
    }

    private string PrepareDataSet(string _orderId)
    {
        System.Data.SqlClient.SqlConnection conn = new SqlConnection("_place_your_connection_string_here");
        string _cmdText =
    "Select TOP 10 OrderID, Convert(Varchar,OrderDate,101) OrderDate, Quantity From Orders Where OrderID >" + _orderId;

        SqlDataAdapter da = new System.Data.SqlClient.SqlDataAdapter(_cmdText, conn);
        DataSet ds = new System.Data.DataSet();
        da.Fill(ds, "Orders");

        //The BuildRows method prepares a HTML table & stuffs the resultset into it
        return BuildRows(ds.Tables[0]);
    }

     private string BuildRows(System.Data.DataTable dt)
    {
        StringBuilder sb = new StringBuilder();
        System.Data.DataRow dr;
        if (dt.Rows.Count > 0)
        {
            for (int i = 0; i < dt.Rows.Count; i++)
             {
                 sb.Append("<tr class='tblRow'>");
                dr = dt.Rows[i];
                 for (int j = 0; j < dt.Columns.Count; j++)
                 {
                      sb.Append("<td>" + dr[j] + "</td>");
                 }
                 sb.Append("</tr>");
            }
        }
         return sb.ToString();
    }
    
      public bool IsReusable {
        get {
            return false;
        }
     }

}


The Continuous Scrolling UI Pattern is an user-friendly way to display a large number of records. jQuery makes it easy to manipulate the DOM and dynamically add incremental results to the initial result set.

References:
Load Content While Scrolling With jQuery
Implementing infinite scrolling with jQuery

By mv ark   Popularity  (15443 Views)
Picture
Biography - mv ark
M.V. 'Anil' Radhakrishna is a seasoned developer who enjoys working with Microsoft tools & technologies. He blogs his little discoveries and about Web development tips, tricks and trivia quite regularly. You can find some of his unusual code samples & snippets at his Code Gallery.