Paging and sorting are the most commonly used features of a ListView Control. But this features becomes a time killer when we have large data in a select query (the rows count is greater than thousands/Lacs). The data binding time can be reduced if we fetch a portion of data that is required to display on the current page instead of fetching the complete data set.

Overview

First, we will optimize the select query used for the binding. Instead of writing a conventional select query we will write a Stored Procedure that will return a single page of records. The Stored Procedure will have StartIndex, SortBy Expression, Filter Expression and TotalRows will be the Output Parameter.

Finally, In the presentation layer we will have a ListView as the Presentation Control and Custom Paging. Also a few hidden fields to maintain the current Sort Expression, StartIndex and TotalPages.

Database details

I have dummy data as an employee table.




Stored Procedure

  1. -- =============================================
  2. -- USP_GetGVData 0, 0 ,'gender' ,'-1'
  3. -- USP_GetGVData 0, 0 ,'MaritalStatus','-1'
  4. -- =============================================
  5. CREATE PROCEDURE [dbo].[USP_GetGVData]
  6. @startIndex INT ,
  7. @totalRows INT OUTPUT ,
  8. @sortBy VARCHAR(50) ,
  9. @jobTitle VARCHAR(50)
  10. AS
  11. BEGIN
  12. DECLARE @sqlStatement NVARCHAR(MAX),
  13. @upperBound INT,
  14. @pageSize AS INT = 9;
  15. -- page size is declared as 10 records/ page
  16. -- calculate row number to be fetched = startindex + pagesize
  17. IF @startIndex < 1
  18. SET @startIndex = 1
  19. IF @pageSize < 1 SET @pageSize = 1
  20. SET @upperBound = @startIndex + @pageSize
  21. -- calculate total rows
  22. SELECT @totalRows = Count(*)
  23. FROM Employee
  24. WHERE JobTitle = CASE @jobTitle WHEN '-1' THEN JobTitle ELSE @jobTitle END
  25. ---- select data
  26. ;WITH T AS (
  27. SELECT ROW_NUMBER () OVER ( ORDER BY
  28. CASE @sortBy WHEN 'EmployeeNumber' THEN [EmployeeNumber]
  29. WHEN 'JobTitle' THEN [JobTitle]
  30. WHEN 'MaritalStatus' THEN [MaritalStatus]
  31. WHEN 'Gender' THEN [Gender]
  32. ELSE [EmployeeNumber] END
  33. ) AS ROWNUM
  34. , *
  35. FROM Employee
  36. WHERE JobTitle = CASE @jobTitle WHEN '-1' THEN JobTitle ELSE @jobTitle END )
  37. SELECT * FROM T
  38. WHERE ROWNUM BETWEEN @startIndex AND @upperBound
  39. END

The preceding Stored Procedure will always return <= 10 records with RowNumber manipulated depending on sortBy and Filter expression. Also the OutPut parameter @totalRows returns TotalRows for calculating the pages requred to display the data for the selected sortBy and Filter criteria.



Presentaion Layer

When to use Gridview, ListView and Repetear ??
For data presentation a GridView, ListView or a Repeater Control can be used. But among them Repeater is the fastest and most optimized since it is made up of HTML tags as well as it has lesser viewstate, due to which page has less payload for a postback. But it cannot have the functionality to handle events such as edit, delete and so on and also requires separate coding for paging.

A GridView is the slowest but it has built-in support for sorting, paging, deleting, editing and so on that can be added using less code. Many times a GridView has a huge ViewState that increases the payload for a postback. Hence the page becomes a slow performer.

A ListView is fast and has a few features that a Repeater and GridView has making it an average performer. It has less ViewState, less than a GridView and is faster than a GridView but slower than a Repeater. It doesn't however have built-in support for paging, inserting, deleting and updating the data.

aspx Page Implementation


.aspx script
  1. <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="TestApplication.Default" %>
  2. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  3. <html xmlns="http://www.w3.org/1999/xhtml">
  4. <head runat="server">
  5. <title></title>
  6. </head>
  7. <body>
  8. <form id="form1" runat="server">
  9. <div>
  10. <table width="100%">
  11. <tr>
  12. <td colspan="5" align="center">
  13. <asp:Label ID="lblError" runat="server"></asp:Label>
  14. <asp:HiddenField ID="TotalRows" runat="server" Value="0" />
  15. <asp:HiddenField ID="startIndex" runat="server" Value="0" />
  16. </td>
  17. </tr>
  18. <tr>
  19. <td align="right">
  20. Job Title :
  21. </td>
  22. <td align="left">
  23. <asp:DropDownList ID="ddlJobTitle" runat="server">
  24. <asp:ListItem Text="Select" Value="-1" Selected="True"></asp:ListItem>
  25. <asp:ListItem Text="Chief Executive Officer" Value="Chief Executive Officer"></asp:ListItem>
  26. <asp:ListItem Text="Design Engineer" Value="Design Engineer"></asp:ListItem>
  27. <asp:ListItem Text="Senior Tool Designer" Value="Senior Tool Designer"></asp:ListItem>
  28. <asp:ListItem Text="Marketing Manager" Value="Marketing Manager"></asp:ListItem>
  29. <asp:ListItem Text="Marketing Specialist" Value="Marketing Specialist"></asp:ListItem>
  30. </asp:DropDownList>
  31. </td>
  32. <td>
  33. <asp:Button ID="btnSearch" runat="server" Text="Search" OnClick="btnSearch_Click" />
  34. </td>
  35. </tr>
  36. <tr>
  37. <td colspan="5" align="center">
  38. <br />
  39. <br />
  40. <asp:ListView ID="lvData" runat="server" OnSorting="lvData_Sorting">
  41. <LayoutTemplate>
  42. <table border="0" cellpadding="1" width="100%">
  43. <tr style="background-color: #E5E5FE">
  44. <th>
  45. SrNo
  46. </th>
  47. <th>
  48. LoginID
  49. </th>
  50. <th>
  51. <asp:LinkButton ID="EmpNumber" runat="server" CommandName="Sort" CommandArgument="EmpNumber">EmpNumber</asp:LinkButton>
  52. </th>
  53. <th>
  54. <asp:LinkButton ID="JobTitle" runat="server" CommandName="Sort" CommandArgument="JobTitle">Job Title</asp:LinkButton>
  55. </th>
  56. <th>
  57. Birth Date
  58. </th>
  59. <th>
  60. <asp:LinkButton ID="MaritalStatus" runat="server" CommandName="Sort" CommandArgument="MaritalStatus">Marital Status</asp:LinkButton>
  61. </th>
  62. <th>
  63. <asp:LinkButton ID="Gender" runat="server" CommandName="Sort" CommandArgument="Gender">Gender</asp:LinkButton>
  64. </th>
  65. <th>
  66. Edit
  67. </th>
  68. </tr>
  69. <tr id="itemPlaceholder" runat="server">
  70. </tr>
  71. </table>
  72. </LayoutTemplate>
  73. <ItemTemplate>
  74. <tr>
  75. <td>
  76. <%# Eval("ROWNUM")%>
  77. </td>
  78. <td>
  79. <%# Eval("LoginID")%>
  80. </td>
  81. <td>
  82. <%# Eval("EmployeeNumber")%>
  83. </td>
  84. <td>
  85. <%# Eval("JobTitle")%>
  86. </td>
  87. <td>
  88. <%# Eval("BirthDate","{0:d}")%>
  89. </td>
  90. <td>
  91. <%# Eval("MaritalStatus")%>
  92. </td>
  93. <td>
  94. <%# Eval("Gender")%>
  95. </td>
  96. <th>
  97. Edit
  98. </th>
  99. </tr>
  100. </ItemTemplate>
  101. <AlternatingItemTemplate>
  102. <tr style="background-color: #cecece">
  103. <td>
  104. <%# Eval("ROWNUM")%>
  105. </td>
  106. <td>
  107. <%# Eval("LoginID")%>
  108. </td>
  109. <td>
  110. <%# Eval("EmployeeNumber")%>
  111. </td>
  112. <td>
  113. <%# Eval("JobTitle")%>
  114. </td>
  115. <td>
  116. <%# Eval("BirthDate","{0:d}")%>
  117. </td>
  118. <td>
  119. <%# Eval("MaritalStatus")%>
  120. </td>
  121. <td>
  122. <%# Eval("Gender")%>
  123. </td>
  124. <th>
  125. Edit
  126. </th>
  127. </tr>
  128. </AlternatingItemTemplate>
  129. </asp:ListView>
  130. </td>
  131. </tr>
  132. <tr>
  133. <td colspan="5">
  134. <table>
  135. <tr>
  136. <td>
  137. <asp:PlaceHolder ID="plcPaging" runat="server" />
  138. <br />
  139. <asp:Label runat="server" ID="lblPageName" />
  140. </td>
  141. </tr>
  142. <tr>
  143. <td>
  144. <asp:Label runat="server" ID="lblPage" ForeColor="Green" />
  145. </td>
  146. </tr>
  147. </table>
  148. </td>
  149. </tr>
  150. </table>
  151. </div>
  152. </form>
  153. </body>
  154. </html>

The code behind for the .aspx.cs is as below:

  1. using System;
  2. using System.Web.UI.WebControls;
  3. using System.Data;
  4. using System.Data.SqlClient;
  5. using System.Configuration;
  6. namespace TestApplication
  7. {
  8. public partial class Default : System.Web.UI.Page
  9. {
  10. int totalCnt = 0;
  11. protected void Page_Load(object sender, EventArgs e)
  12. {
  13. if (!IsPostBack)
  14. { }
  15. else
  16. {
  17. plcPaging.Controls.Clear();
  18. CreatePagingControl();
  19. }
  20. }
  21. protected void btnSearch_Click(object sender, EventArgs e)
  22. {
  23. ViewState["SortExpression"] = string.Empty;
  24. TotalRows.Value = "0";
  25. startIndex.Value = "0";
  26. getLvData(Convert.ToInt32(startIndex.Value), ref totalCnt, Convert.ToString(ViewState["SortExpression"]), ddlJobTitle.SelectedValue.ToString());
  27. TotalRows.Value = totalCnt.ToString();
  28. startIndex.Value = "11";
  29. plcPaging.Controls.Clear();
  30. CreatePagingControl();
  31. }
  32. #region " [ListView Events ] "
  33. protected void lvData_Sorting(object sender, ListViewSortEventArgs e)
  34. {
  35. ViewState["SortExpression"] = e.SortExpression;
  36. TotalRows.Value = "0";
  37. startIndex.Value = "0";
  38. getLvData(Convert.ToInt32(startIndex.Value), ref totalCnt, Convert.ToString(ViewState["SortExpression"]), ddlJobTitle.SelectedValue.ToString());
  39. TotalRows.Value = totalCnt.ToString();
  40. startIndex.Value = "11";
  41. plcPaging.Controls.Clear();
  42. CreatePagingControl();
  43. }
  44. #endregion
  45. #region " [ Paging ] "
  46. private void CreatePagingControl()
  47. {
  48. for (int i = 0; i < (Convert.ToInt32(TotalRows.Value) / 10) + 1; i++)
  49. {
  50. LinkButton lnk = new LinkButton();
  51. lnk.Click += new EventHandler(lbl_Click);
  52. lnk.ID = "lnkPage" + (i + 1).ToString();
  53. lnk.Text = (i + 1).ToString();
  54. plcPaging.Controls.Add(lnk);
  55. Label spacer = new Label();
  56. spacer.Text = " ";
  57. plcPaging.Controls.Add(spacer);
  58. lblPage.Text = "Total Pages : " + ((Convert.ToInt32(TotalRows.Value) / 10) + 1).ToString() + ", Selected Page : 1";
  59. }
  60. }
  61. void lbl_Click(object sender, EventArgs e)
  62. {
  63. LinkButton lnk = sender as LinkButton;
  64. int currentPage = int.Parse(lnk.Text);
  65. int take = currentPage * 10;
  66. int skip = currentPage == 1 ? 0 : take - 10;
  67. startIndex.Value = (((currentPage * 10) - 10) + 1).ToString();
  68. getLvData(Convert.ToInt32(startIndex.Value), ref totalCnt, Convert.ToString(ViewState["SortExpression"]), ddlJobTitle.SelectedValue.ToString());
  69. TotalRows.Value = totalCnt.ToString();
  70. lblPage.Text = "Total Pages : " + ((Convert.ToInt32(TotalRows.Value) / 10) + 1).ToString() + ", Selected Page : " + lnk.Text;
  71. }
  72. #endregion
  73. #region " [ Private Function ] "
  74. private void getLvData(int startIndex, ref int totalRows, string sortBy, string jobTitle)
  75. {
  76. DataSet dsData = new DataSet();
  77. SqlConnection sqlCon = null;
  78. SqlCommand sqlCmd = null;
  79. SqlDataAdapter sqlSelectCmd = null;
  80. try
  81. {
  82. using (sqlCon = new SqlConnection(ConfigurationManager.ConnectionStrings["connectionString"].ConnectionString))
  83. {
  84. sqlCmd = new SqlCommand("USP_GetGVData", sqlCon);
  85. sqlCmd.CommandType = CommandType.StoredProcedure;
  86. sqlCmd.Parameters.AddWithValue("@startIndex", startIndex);
  87. sqlCmd.Parameters.AddWithValue("@sortBy", sortBy);
  88. sqlCmd.Parameters.AddWithValue("@jobTitle", jobTitle);
  89. sqlCmd.Parameters.AddWithValue("@totalRows", totalRows);
  90. ((SqlParameter)sqlCmd.Parameters["@totalRows"]).Direction = ParameterDirection.Output;
  91. sqlCon.Open();
  92. sqlSelectCmd = new SqlDataAdapter();
  93. sqlSelectCmd.SelectCommand = sqlCmd;
  94. sqlSelectCmd.Fill(dsData);
  95. totalRows = Convert.ToInt32(((SqlParameter)sqlCmd.Parameters["@totalRows"]).Value);
  96. sqlCon.Close();
  97. }
  98. }
  99. catch
  100. {
  101. throw;
  102. }
  103. lvData.DataSource = dsData;
  104. lvData.DataBind();
  105. if (!IsPostBack)
  106. {
  107. CreatePagingControl();
  108. }
  109. }
  110. #endregion
  111. }
  112. }

Compile and run the page and it will look as in the following:



Paging Event



Search with Filter Expression



Search with Sorting Expression (job title selected)



Download the source code for the database script and other explanations.