Monday 14 May 2012

Rendering GridView Rows when scrolling down using AJAX concept - Part 1


Introduction

We had seen several stuffs on GridView in this blog. In this post, I am planning to blog another cool stuff on GridView – Rendering GridView rows when scrolling the page down. This concept helps us to bind any number of records to the GridView with no performance impact.

There can be different ways of implementation when binding records to the GridView -
  1. Simple Way - When binding records to the GridView, we fetch all the rows from the database and assign as DataSource and call a Bind method. When changing the page index of the GridView, we will assign the index of the page to the PageIndex property and call Bind method. This method will work fine when records are not more and will not grow huge in future.
  2. Database Pagination when records are more - When records are in 5000+, a good solution would be to fetch only required records for the page and bind to the GridView. This method is useful when the GridView allowed to show with Pagination. I had blogged doing pagination at database level before binding GridView in the below posts.
    Database Pagination in GridView using SQL Store Procedure
    Database Pagination in GridView using nHibernate
  3. Most of our requirements falls under any of the above two ways of implementation, but when customer wants to show all the records in a single page and the records are more then 5000+ which can grow more and more in future, we will be ending up considering a suitable solution using AJAX concept only.

The use case for this implementation would be as below –
  1. Show list of records in a GridView with all sort of customizations possible (such as colors, data formatting etc.,).
  2. The GridView should show all the records in a single page without having pagination.
  3. The GridView should have a scroll bar to scroll and see the records up and down.
  4. Initially the GridView should show only first two pages on the screen (or decided by the programmer on the code).
  5. When user scrolls down to see last records, the page should fetch the next 10 records (or decided by programmer on the code) and add with the records at the last shown in the GridView.
  6. When user scrolls further down to see the newly added records, the page should fetch the next 10 records and add at the last to the GridView. This repeats until last record reached.

I am implementing this requirement in three different examples. So you can take the relevant example near to your requirement.
  1. First example implements the exact requirement specified in the use case below.
  2. Addition to the first implementation, the second example shows a processing message on the top corner of the screen when server processing to let the user know something is happening behind.
    To know more about this implementation, navigate to the below link.
    Rendering GridView Rows when scrolling down using AJAX concept - Part 2
  3. In previous two examples, initially two pages are loaded by default and when user scrolls down to last row, the next page loads. In this third example, once the page loaded it won’t wait for the user to scroll to the end; instead it will start fetching the next page from the server and add to the GridView. This repeats in background without any user interaction.
    To know more about this implementation, navigate to the below link.
    Rendering GridView Rows when scrolling down using AJAX concept - Part 3

Apart from these three examples, I am also given code by implementing sorting option for each example.

Implementation View

For implementing this example, I used XML HTTP AJAX concept in the page. For more information about how to implement XML HTTP AJAX, please review the following posts.
How to implement XML HTTP AJAX in ASP.NET Web Pages
Implementing XML HTTP AJAX with JSON concept in ASP.NET Web Pages
Implementing XML HTTP AJAX with JSON in ASP.NET (Cascading Dropdown, retain the list and selection)

For implementation of this example, I have two aspx files and one class file.
  • One page will act as a consumer, which has the actual GridView to show to the end user. In our example AJAXGrid.aspx is consumer page (for first implementation).
  • Another page will act as the respondent, which will create the required rows using GridView and return back to the consumer page to show on the screen. In our project RowsBuilder.aspx will create the required rows and return back to the consumer when calling using AJAX.
  • I have a class ProductDAO which connects the database and fetchs the required rows for binding as data source to the GridView (It fetches only the records for a particular page).

Note:
  1. For this implementation, I am taking Northwind database for binding records to the GridView. So make sure to have the same database setup for running to source code provided.
  2. This example can be used only for viewing the records on the GridView. For any CRUD operations on records such as Edit, Update, Delete will not be able as we do with GridView in normal way. This is because; the contents of the records are fetched from the server and added to the GridView using client script (Javascript). So server will not be trigged on the Save, Update, Delete events. (To do this, please consider having AJAX implementation for that too)
  3. Addition to the main page (consumer - where the actual GridView exist), I also have once more aspx page for fetching remaining records page by page and bind to the GridView (respondent). Both the pages have same type of GridView, so the developer needs to define both the GridView stylesheets are same. So when adding remaining rows to the main page GridView, it will look like same.

First implementation (AJAXGrid.aspx)

This shows the first method of implementation. In this method, initally the GridView gets loaded with 2 pages of records (ie., 10*2 = 20 records). When the user scrolls down to the end of the records, the page triggers a call the server to fetch the next 1 page records (i.e., 10 records) and adds to the GridView at the end. This same process repeats till the end of the record reached.

The GridView script (AJAXGrid.aspx)
<div id="GridViewContainer" style="width:100%;height:400px;" onscroll="RefreshRows(this)" >
    <asp:GridView ID="grdViewProducts" CssClass="ViewProducts" runat="server" AutoGenerateColumns="False" TabIndex="1" 
        AllowPaging="false" DataKeyNames="ProductID" Width="100%" GridLines="Both" CellPadding="3" 
        AllowSorting="false" BackColor="White">
        <Columns>
            <asp:BoundField DataField="ProductName" HeaderText="Product Name" 
                SortExpression="ProductName">
            <ItemStyle Width="26%" />
            </asp:BoundField>
            <asp:BoundField DataField="CategoryName" HeaderText="Category" 
                SortExpression="CategoryName" >
            <ItemStyle Width="17%" />
            </asp:BoundField>
            <asp:BoundField DataField="QuantityPerUnit" HeaderText="Quantity/Unit" 
                SortExpression="QuantityPerUnit" >
            <ItemStyle Width="17%" />
            </asp:BoundField>
            <asp:BoundField DataField="UnitPrice" HeaderText="Unit Price" 
                SortExpression="UnitPrice" DataFormatString="{0:#0.00}" >
            <ItemStyle Width="10%" />
            </asp:BoundField>
            <asp:BoundField DataField="UnitsInStock" HeaderText="Units In Stock" 
                SortExpression="UnitsInStock" >
            <ItemStyle Width="10%" />
            </asp:BoundField>
            <asp:BoundField DataField="UnitsOnOrder" HeaderText="Units On Order" 
                SortExpression="UnitsOnOrder" >
            <ItemStyle Width="10%" />
            </asp:BoundField>
            <asp:BoundField DataField="ReorderLevel" HeaderText="Reorder Level" 
                SortExpression="ReorderLevel" >
            <ItemStyle Width="10%" />
            </asp:BoundField>
        </Columns>
        <EmptyDataTemplate>
            <div>
                <p style="color:Red">No search results found.</p>
            </div>
        </EmptyDataTemplate>
        <PagerSettings Position="Bottom" />
        <RowStyle BackColor="#E7E7FF" ForeColor="#4A3C8C" />
        <FooterStyle BackColor="#B5C7DE" ForeColor="#4A3C8C" />
        <PagerStyle BackColor="#E7E7FF" ForeColor="#4A3C8C" HorizontalAlign="Right" />
        <SelectedRowStyle BackColor="#738A9C" Font-Bold="True" ForeColor="#F7F7F7" />
        <HeaderStyle BackColor="#4A3C8C" Font-Bold="True" ForeColor="#F7F7F7" />
        <AlternatingRowStyle BackColor="#F7F7F7" />
        <SortedAscendingCellStyle BackColor="#F4F4FD" />
        <SortedAscendingHeaderStyle BackColor="#5A4C9D" />
        <SortedDescendingCellStyle BackColor="#D8D8F0" />
        <SortedDescendingHeaderStyle BackColor="#3E3277" />
    </asp:GridView>
    <div id="divChildGrid">
    </div>
</div>
<input id="hiddenInitialNoOfPageLoad" type="hidden" value="0" runat="server" />
<input id="hiddenTotalRecords" type="hidden" value="0" runat="server" />
<input id="hiddenPageRecordCount" type="hidden" value="0" runat="server" />
The C# code behind (AJAXGrid.aspx.cs)
protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        hiddenInitialNoOfPageLoad.Value = "2";
        hiddenPageRecordCount.Value = ConfigurationManager.AppSettings["PageRecordCount"].ToString();
        BindGrid();
    }
}
private void BindGrid()
{
    // Defining int variable to get the Total Record Count from Data layer
    int totalRecordCount = 0;

    // Getting how many records required to show in the Grid per page
    int intPageRecordCount = Convert.ToInt32(hiddenPageRecordCount.Value) * 2; // I am fetching 3 pages at first time

    ProductDAO objProductDAO = new ProductDAO();

    IList<ProductView> ProductViewList = objProductDAO.GetProducts(1, intPageRecordCount, string.Empty, SortType.Ascending, out totalRecordCount); // Getting First three pages
    //Adding one empty row for just to show the grid
    if (ProductViewList.Count == 0)
        ProductViewList.Add(new ProductView());

    grdViewProducts.DataSource = ProductViewList;
    grdViewProducts.DataBind();

    // Assign the Total Record count to 
    hiddenTotalRecords.Value = totalRecordCount.ToString();
}
The Javascript (AJAXGrid.aspx)
var is_ie = (navigator.userAgent.indexOf('MSIE') >= 0) ? 1 : 0;
var is_ie5 = (navigator.appVersion.indexOf("MSIE 5.5") != -1) ? 1 : 0;
var xmlHttp;
var varStartAJAXPage = 3;
var varPageOnBehind = 0;

function pageLoad() {
 GetChildGrid(varStartAJAXPage);
}
window.onload = pageLoad;

/* This function requests the HTTPRequest, will be used to render the Dynamic content html markup 
* and it will call HandleResponse to handle the response
*/
function GetChildGrid(pageId) {

 // Get the Div
 var childGridDiv = document.getElementById("divChildGrid");

 if (childGridDiv) {
  // If the Child Grid is not fetched, then go and fetch it.
  var varTotalRecords = document.getElementById('<%= hiddenTotalRecords.ClientID %>').value;
  var varPageRecordCount = document.getElementById('<%= hiddenPageRecordCount.ClientID %>').value;

  if ((pageId > 0) && (parseInt(varTotalRecords) > (parseInt(varPageRecordCount) * (parseInt(pageId) - 1)))) {

   var url = 'RowsBuilder.aspx?PageId=' + pageId +
      '&PageRecordsCount=' + varPageRecordCount;
   xmlHttp = createAjaxObject();
   if (xmlHttp) {
    xmlHttp.open('get', url, true);
    xmlHttp.onreadystatechange = function () {
     HandleResponse(pageId);
    }
    varPageOnBehind = varStartAJAXPage;
    xmlHttp.send(null);
   }
  }
 }
}
function RefreshRows(div) {
 if (varStartAJAXPage - varPageOnBehind > 0) {
  if (div.scrollHeight - (div.scrollTop + div.offsetHeight) < 200) {
   varPageOnBehind = varStartAJAXPage;
   GetChildGrid(varStartAJAXPage);
  }
 }
}
/* This function is used to handler the http response */
function HandleResponse() { 

 var childGridDiv = document.getElementById("divChildGrid");

 // If Response completed
 if (xmlHttp.readyState == 4) {

  if (xmlHttp.status == 200) {

   // Here is the response
   var str = xmlHttp.responseText;

   var divRows = document.createElement('div');
   divRows.innerHTML = str;

   childGridDiv.appendChild(divRows);
   varStartAJAXPage++;

   xmlHttp.abort();
  }
 }
}
/* function to create Ajax object */
function createAjaxObject() {
 var ro;
 var browser = navigator.appName;
 if (browser == "Microsoft Internet Explorer") {
  if (xmlHttp != null) {
   xmlHttp.abort();
  }
  ro = new ActiveXObject("Microsoft.XMLHTTP");
 }
 else {
  if (xmlHttp != null) {
   xmlHttp.abort();
  }
  ro = new XMLHttpRequest();
 }
 return ro;
}

/* Get the XML Http Object */
function GetXmlHttpObject(handler) {
 var objXmlHttp = null;
 if (is_ie) {
  var strObjName = (is_ie5) ? 'Microsoft.XMLHTTP' : 'Msxml2.XMLHTTP';

  try {
   objXmlHttp = new ActiveXObject(strObjName);
   objXmlHttp.onreadystatechange = handler;
  }
  catch (e) {
   alert('Object could not be created');
   return;
  }
 }
 return objXmlHttp;
}

function xmlHttp_Get(xmlhttp, url) {
 xmlhttp.open('GET', url, true);
 xmlhttp.send(null);
}
The CSS script (AJAXGrid.aspx)
#GridViewContainer
{           
    overflow: auto;
}
.ViewProducts
{
    border-top-style:solid;
    border-left-style:solid;
    border-right-style:solid;
    border-bottom-style:none;
}
.ViewChildProducts
{
    border-top-style:solid;
    border-left-style:solid;
    border-right-style:solid;
    border-bottom-style:none;
}
Page script that create rows on request (RowsBuilder.aspx)
<asp:GridView ID="grdAJAXViewProducts" runat="server" ShowHeader="false" ShowFooter="false"
    AutoGenerateColumns="False" TabIndex="1" AllowPaging="false" DataKeyNames="ProductID"
    Width="100%" GridLines="Both" CellPadding="3" AllowSorting="True"
    BackColor="White" CssClass="ViewProducts">
    <Columns>
        <asp:BoundField DataField="ProductName" HeaderText="Product Name" 
            SortExpression="ProductName" >
        <ItemStyle Width="26%" />
        </asp:BoundField>
        <asp:BoundField DataField="CategoryName" HeaderText="Category" 
            SortExpression="CategoryName" >
        <ItemStyle Width="17%" />
        </asp:BoundField>
        <asp:BoundField DataField="QuantityPerUnit" HeaderText="Quantity/Unit" 
            SortExpression="QuantityPerUnit" >
        <ItemStyle Width="17%" />
        </asp:BoundField>
        <asp:BoundField DataField="UnitPrice" HeaderText="Unit Price" 
            SortExpression="UnitPrice" DataFormatString="{0:#0.00}" >
        <ItemStyle Width="10%" />
        </asp:BoundField>
        <asp:BoundField DataField="UnitsInStock" HeaderText="Units In Stock" 
            SortExpression="UnitsInStock" >
        <ItemStyle Width="10%" />
        </asp:BoundField>
        <asp:BoundField DataField="UnitsOnOrder" HeaderText="Units On Order" 
            SortExpression="UnitsOnOrder" >
        <ItemStyle Width="10%" />
        </asp:BoundField>
        <asp:BoundField DataField="ReorderLevel" HeaderText="Reorder Level" 
            SortExpression="ReorderLevel" >
        <ItemStyle Width="10%" />
        </asp:BoundField>
    </Columns>
    <PagerSettings Position="Bottom" />
    <RowStyle BackColor="#E7E7FF" ForeColor="#4A3C8C" />
    <FooterStyle BackColor="#B5C7DE" ForeColor="#4A3C8C" />
    <PagerStyle BackColor="#E7E7FF" ForeColor="#4A3C8C" HorizontalAlign="Right" />
    <SelectedRowStyle BackColor="#738A9C" Font-Bold="True" ForeColor="#F7F7F7" />
    <HeaderStyle BackColor="#4A3C8C" Font-Bold="True" ForeColor="#F7F7F7" />
    <AlternatingRowStyle BackColor="#F7F7F7" />
    <SortedAscendingCellStyle BackColor="#F4F4FD" />
    <SortedAscendingHeaderStyle BackColor="#5A4C9D" />
    <SortedDescendingCellStyle BackColor="#D8D8F0" />
    <SortedDescendingHeaderStyle BackColor="#3E3277" />
</asp:GridView>
Code behind for page that create rows on request (RowsBuilder.aspx)
protected void Page_Load(object sender, EventArgs e)
{
    if ((!Page.IsPostBack) && (Request.QueryString["PageId"] != null))
    {
        Response.Clear();
        Response.ContentType = "text/xml";

        BindGrid();

        System.IO.StringWriter stringWrite = new System.IO.StringWriter();
        System.Web.UI.HtmlTextWriter htmlWrite = new HtmlTextWriter(stringWrite);
        grdAJAXViewProducts.RenderControl(htmlWrite);
        Response.Write(stringWrite.ToString());

        Response.End();
    }
}
public override void VerifyRenderingInServerForm(Control control)
{
}
private void BindGrid()
{
    // Defining int variable to get the Total Record Count from Data layer
    int totalRecordCount = 0;
            
    string strSortExpression = string.Empty;
    SortType sortType = SortType.Ascending;

    if (Request.QueryString["SortExpression"] != null)
    {
        // Get the SortByExpression and SortType from Query String (by default, that will be updated in script)
        strSortExpression = Request.QueryString["SortExpression"].ToString().Split(",".ToCharArray())[0];
        sortType = (SortType)Enum.Parse(typeof(SortType), Request.QueryString["SortExpression"].ToString().Split(",".ToCharArray())[1], true);
    }

    // Getting how many records required to show in the Grid per page
    int intPageRecordCount = Convert.ToInt32(Request.QueryString["PageRecordsCount"].ToString());

    ProductDAO objProductDAO = new ProductDAO();

    IList<ProductView> ProductViewList = objProductDAO.GetProducts(Convert.ToInt32(Request.QueryString["PageId"].ToString()), intPageRecordCount, strSortExpression, sortType, out totalRecordCount);
    //Adding one empty row for just to show the grid
    if (ProductViewList.Count == 0)
        ProductViewList.Add(new ProductView());

    grdAJAXViewProducts.DataSource = ProductViewList;
    grdAJAXViewProducts.DataBind();
}
The code for fetching records (ProductDAO.cs)
public class ProductDAO
{
    public IList<ProductView> GetProducts(int currentPageNo, int pageRecordsCount, string sortBy, SortType sortType, out int totalRecordCount)
    {
        SqlConnection connection = null;
        SqlDataReader dataReader = null;

        try
        {
            IList<ProductView> productViewList = new List<ProductView>();
            connection = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ToString());

            SqlDataAdapter dataAdaptor = new SqlDataAdapter("SP_GET_PRODUCTS", connection);
            dataAdaptor.SelectCommand.CommandType = System.Data.CommandType.StoredProcedure;

            dataAdaptor.SelectCommand.Parameters.Add(new SqlParameter("@CurrentPageNo", SqlDbType.Int));
            dataAdaptor.SelectCommand.Parameters["@CurrentPageNo"].Value = currentPageNo;

            dataAdaptor.SelectCommand.Parameters.Add(new SqlParameter("@PageRecordsCount", SqlDbType.Int));
            dataAdaptor.SelectCommand.Parameters["@PageRecordsCount"].Value = pageRecordsCount;

            dataAdaptor.SelectCommand.Parameters.Add(new SqlParameter("@SortBy", SqlDbType.VarChar));
            dataAdaptor.SelectCommand.Parameters["@SortBy"].Value = sortBy;

            dataAdaptor.SelectCommand.Parameters.Add(new SqlParameter("@SortType", SqlDbType.VarChar));
            dataAdaptor.SelectCommand.Parameters["@SortType"].Value = ((sortType == null) ? string.Empty : (sortType == SortType.Ascending ? "Asc" : "Desc"));

            dataAdaptor.SelectCommand.Parameters.Add(new SqlParameter("@TotalRecordCount", SqlDbType.Int));
            dataAdaptor.SelectCommand.Parameters["@TotalRecordCount"].Direction = ParameterDirection.Output;
            connection.Open();

            DataSet dataSet = new DataSet();
            dataAdaptor.Fill(dataSet);

            foreach (DataRow dataRow in dataSet.Tables[0].Rows)
            {
                ProductView productView = new ProductView();
                productView.ProductID = Convert.ToInt32(dataRow["ProductID"].ToString());
                productView.ProductName = dataRow["ProductName"].ToString();
                productView.CompanyName = dataRow["CompanyName"].ToString();
                productView.CategoryName = dataRow["CategoryName"].ToString();
                productView.QuantityPerUnit = dataRow["QuantityPerUnit"].ToString();
                productView.UnitPrice = Convert.ToDouble(dataRow["UnitPrice"].ToString());
                productView.UnitsInStock = Convert.ToInt32(dataRow["UnitsInStock"].ToString());
                productView.UnitsOnOrder = Convert.ToInt32(dataRow["UnitsOnOrder"].ToString());
                productView.ReorderLevel = Convert.ToInt32(dataRow["ReorderLevel"].ToString());

                productViewList.Add(productView);
            }
            // To make more processing time 
            //for (int i = 0; i < (1000000000/2); i++) { int b = 5 + 4 + 3 + 2 - 3; }
            totalRecordCount = Convert.ToInt32(dataAdaptor.SelectCommand.Parameters["@TotalRecordCount"].Value);
            connection.Close();

            //return
            return productViewList;
        }
        catch (Exception ex)
        {
            if (connection != null) connection.Close();
            if (dataReader != null) dataReader.Close();

            // Log
            throw ex;
        }
    }
}
The ProductView entity class
public class ProductView
{
    public int ProductID { get; set; }
    public string ProductName { get; set; }
    public string CompanyName { get; set; }
    public string CategoryName { get; set; }
    public string QuantityPerUnit { get; set; }
    public double UnitPrice { get; set; }
    public int UnitsInStock { get; set; }
    public int UnitsOnOrder { get; set; }
    public int ReorderLevel { get; set; }
}
public enum SortType
{
    Ascending,
    Descending
}
The Database script
Create View [dbo].[ProductView]
As
Select Products.ProductID,
  Products.ProductName,
  Suppliers.CompanyName,
  Categories.CategoryName,
  Products.QuantityPerUnit,
  Products.UnitPrice,
  Products.UnitsInStock,
  Products.UnitsOnOrder,
  Products.ReorderLevel
from Products
Join Suppliers on Suppliers.SupplierID = Products.SupplierID
Join Categories on Categories.CategoryID = Products.CategoryID
Go


CREATE PROCEDURE [dbo].[SP_GET_PRODUCTS]
@CurrentPageNo AS INT,
@PageRecordsCount AS INT,
@SortBy AS NVARCHAR(250),
@SortType AS NVARCHAR(250),
@TotalRecordCount AS INT OUTPUT
AS
 
SET NOCOUNT ON
 
DECLARE @SQL AS NVARCHAR(1000)
DECLARE @RecordsFrom AS INT
DECLARE @RecordsTo AS INT
 
-- RecordsFrom => For Ex: CurrentPage 2, PageRecordCount = 10, then (((2-1) * 10) + 1) => ((1 * 10) + 1) => 11
SET @RecordsFrom = (((@CurrentPageNo - 1) * @PageRecordsCount) + 1) 
 
-- @RecordsTo => For Ex: CurrentPage 2, PageRecordCount = 10, then (2 * 10) => 20
SET @RecordsTo = (@CurrentPageNo * @PageRecordsCount)
 
IF @SortBy = '' OR @SortBy = NULL
 SET @SortBy = 'ProductName'
 
IF @SortType = '' OR @SortType = NULL
 SET @SortType = 'Asc'
 
-- Getting the Total Record Count
SELECT @TotalRecordCount = COUNT(*) FROM ProductView
 
-- Getting the records for a particular page
SET @SQL = 
  'SELECT * FROM ' +
  '( ' +
  ' SELECT ' +
  '  ROW_NUMBER() OVER (ORDER BY ' + @SortBy + ' ' +  @SortType + ') AS RowNumber, *' +
  ' FROM ProductView ' +
  ') AS RECORDS ' +
  'Where RowNumber between ' + CAST(@RecordsFrom AS VARCHAR) + ' and ' + CAST(@RecordsTo AS VARCHAR)
 
EXECUTE(@SQL)

Implementation with Sorting Option
In this implementation, there will be a sorting icon (arrow) on each column. By clicking Up and Down arrows, the records will be sorted for that column. When showing records, that will be as per our implementation.
Add below Style Sheet
th a.sortdown
{
 background: url(../../images/up.png) left center no-repeat;
 padding-left: 22px;
}
th a.sortup
{
 background: url(../../images/down.png) left center no-repeat;
 padding-left: 22px;
}
The ASPX Script (AJAXGridSorting.aspx)
Add one hidden control hiddenSortExpression in the Page and add sorting event for the GridView.
<div id="GridViewContainer" style="width:100%;height:400px;" onscroll="RefreshRows(this)" >
    <asp:GridView ID="grdViewProducts" CssClass="ViewProducts" runat="server" 
        AutoGenerateColumns="False" TabIndex="1" 
        AllowPaging="false" DataKeyNames="ProductID" Width="100%" GridLines="Both" CellPadding="3" 
        AllowSorting="True" BackColor="White" onsorting="grdViewProducts_Sorting">
        <Columns>
            
        </Columns>
    </asp:GridView>
    <div id="divChildGrid">
    </div>
</div>
<input id="hiddenInitialNoOfPageLoad" type="hidden" value="0" runat="server" />
<input id="hiddenTotalRecords" type="hidden" value="0" runat="server" />
<input id="hiddenPageRecordCount" type="hidden" value="0" runat="server" />
<input id="hiddenSortExpression" type="hidden" value="0" runat="server" />
The C# Codebehind (AJAXGridSorting.aspx.cs)
protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        hiddenInitialNoOfPageLoad.Value = "2";
        hiddenPageRecordCount.Value = ConfigurationManager.AppSettings["PageRecordCount"].ToString();
        hiddenSortExpression.Value = "ProductName,Ascending";
        BindGrid();
    }
}
private void BindGrid()
{
    // Defining int variable to get the Total Record Count from Data layer
    int totalRecordCount = 0;

    // Get the SortByExpression and SortType from View State (by default, that will be updated in script)
    string strSortExpression = hiddenSortExpression.Value.Split(",".ToCharArray())[0];
    SortType sortType = (SortType)Enum.Parse(typeof(SortType), hiddenSortExpression.Value.Split(",".ToCharArray())[1], true);

    // Getting how many records required to show in the Grid per page
    int intPageRecordCount = Convert.ToInt32(hiddenPageRecordCount.Value) * 2; // I am fetching 3 pages at first time

    ProductDAO objProductDAO = new ProductDAO();

    IList<ProductView> ProductViewList = objProductDAO.GetProducts(1, intPageRecordCount, strSortExpression, sortType, out totalRecordCount); // Getting First three pages
    //Adding one empty row for just to show the grid
    if (ProductViewList.Count == 0)
        ProductViewList.Add(new ProductView());

    grdViewProducts.DataSource = ProductViewList;
    grdViewProducts.DataBind();

    // Assign the Total Record count to 
    hiddenTotalRecords.Value = totalRecordCount.ToString();

    // Try to find the sorted column
    for (int intRowIndex = 0; intRowIndex < grdViewProducts.Columns.Count; intRowIndex++)
    {
        if (strSortExpression == grdViewProducts.Columns[intRowIndex].SortExpression)
            ((LinkButton)grdViewProducts.HeaderRow.Cells[intRowIndex].Controls[0])
            .CssClass = (sortType == SortType.Ascending ? "sortup" : "sortdown");
    }
}
protected void grdViewProducts_Sorting(object sender, GridViewSortEventArgs e)
{
    string strSortExpression = hiddenSortExpression.Value.Split(",".ToCharArray())[0];
    SortType sortType = (SortType)Enum.Parse(typeof(SortType), hiddenSortExpression.Value.Split(",".ToCharArray())[1], true);

    if (strSortExpression == e.SortExpression)
        sortType = sortType == SortType.Ascending ? SortType.Descending : SortType.Ascending;
    else
    {
        strSortExpression = e.SortExpression;
        sortType = SortType.Ascending;
    }
    hiddenSortExpression.Value = e.SortExpression + "," + sortType.ToString();

    BindGrid();
}
The Javascript
function GetChildGrid(pageId) {

    // Get the Div
    var childGridDiv = document.getElementById("divChildGrid");

    if (childGridDiv) {
        // If the Child Grid is not fetched, then go and fetch it.
        var varTotalRecords = document.getElementById('<%= hiddenTotalRecords.ClientID %>').value;
        var varPageRecordCount = document.getElementById('<%= hiddenPageRecordCount.ClientID %>').value;
        var varSortExpression = document.getElementById('<%= hiddenSortExpression.ClientID %>').value;

        if ((pageId > 0) && (parseInt(varTotalRecords) > (parseInt(varPageRecordCount) * (parseInt(pageId) - 1)))) {

            var url = 'RowsBuilder.aspx?PageId=' + pageId + 
                            '&PageRecordsCount=' + varPageRecordCount +
                            '&SortExpression=' + varSortExpression;
            xmlHttp = createAjaxObject();
            if (xmlHttp) {
                xmlHttp.open('get', url, true);
                xmlHttp.onreadystatechange = function () {
                    HandleResponse(pageId);
                }
                varPageOnBehind = varStartAJAXPage;
                xmlHttp.send(null);
            }
        }
    }
}
The output of this code would be:

The vedio output


download the working copy of the source code in C# here and in VB here.

The next post talks about the third implementation and the sorting option on each.


8 Responses to “Rendering GridView Rows when scrolling down using AJAX concept - Part 1”

  • a says:
    17 October 2012 at 09:03

    good example but the rowsbuilder.aspx dont registers gridview row databound and command event args event. as a result any command option doesnot work for the lower pages.Any alternate for those?????

  • Thirumalai M says:
    17 October 2012 at 09:57

    Hi,

    Yes, As we are rendering the lower pages using AJAX call. Soo the events on the rows such as Edit, Delete etc., wont work in lower pages.

    To overcome this issue, we need to go with calling server from Javascript/JQuery using AJAX only. Below is a simple example on how to make this work...

    http://www.dotnettwitter.com/2012/06/hierarchical-gridview-in-aspnet-with_27.html

    In this example, I can edit a particular child row event that is rendered using AJAX call. The same way, we need to do in this example also.

  • a says:
    17 October 2012 at 10:05

    can you please update this same example and integrate this in this example then it would be great...So that this would be bugs free....

  • Thirumalai M says:
    17 October 2012 at 10:43

    Sure.. I will update. But not today, will do in this week end :)

  • a says:
    17 October 2012 at 12:23

    please fix it in such a way that it uses onCommand event of gridview...It uses gridviewdatabound event but not gridview oncommand events.... Thanks

  • a says:
    21 October 2012 at 07:49

    hi any update on this. Did you got a chance to work on this. Please let me know....

  • Thirumalai M says:
    21 October 2012 at 21:46

    Hi, Look at the following url which has Edit, Delete functionalities.
    http://www.dotnettwitter.com/2012/10/rendering-gridview-rows-when-scrolling.html

    To remember, as the implementation uses AJAX it will be not possible to use the same as what we do in GridView.

  • Sharmila says:
    7 April 2014 at 11:21

    Please give me the db/ tables and columns.

Post a Comment