Paging ASP.NET ListView Control with a DataPagerControl

Today I would like to discuss paging a listview control. First I will show you paging a list view that uses a SqlDataSource control then I will show you paging a list view that does not use a data source control.

The DataPager control makes paging almost effortless when used in conjunction with a DataSource control as you will see.

You will need a database to query. Fortunately in the downloadable project there is a database included along with all the code examples we will be discussing.

On your .aspxpage add the following markup:

  1. <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString='<%$ ConnectionStrings:DefaultConnection %>' SelectCommand="SELECT [TestCol1], [TestCol2], [TestCol3] FROM [TestTable]"></asp:SqlDataSource>  
  2. <asp:ListView ID="ListView1" runat="server" DataSourceID="SqlDataSource1" DataKeyNames="TestCol1" GroupItemCount="3">  
  3.   
  4.     <AlternatingItemTemplate>  
  5.         <td runat="server" style="background:#808080;color:#ffffff;">TestCol1:  
  6.             <asp:Label Text='<%# Eval("TestCol1") %>' runat="server" ID="TestCol1Label" /><br /> TestCol2:  
  7.             <asp:Label Text='<%# Eval("TestCol2") %>' runat="server" ID="TestCol2Label" /><br /> TestCol3:  
  8.             <asp:Label Text='<%# Eval("TestCol3") %>' runat="server" ID="TestCol3Label" /><br />  
  9.         </td>  
  10.     </AlternatingItemTemplate>  
  11.   
  12.   
  13.     <EmptyDataTemplate>  
  14.         <table runat="server">  
  15.             <tr>  
  16.                 <td>No data was returned.</td>  
  17.             </tr>  
  18.         </table>  
  19.     </EmptyDataTemplate>  
  20.     <EmptyItemTemplate>  
  21.         <td runat="server" />  
  22.     </EmptyItemTemplate>  
  23.     <GroupTemplate>  
  24.         <tr runat="server" id="itemPlaceholderContainer">  
  25.             <td runat="server" id="itemPlaceholder"></td>  
  26.         </tr>  
  27.     </GroupTemplate>  
  28.   
  29.   
  30.     <ItemTemplate>  
  31.         <td runat="server">TestCol1:  
  32.             <asp:Label Text='<%# Eval("TestCol1") %>' runat="server" ID="TestCol1Label" /><br /> TestCol2:  
  33.             <asp:Label Text='<%# Eval("TestCol2") %>' runat="server" ID="TestCol2Label" /><br /> TestCol3:  
  34.             <asp:Label Text='<%# Eval("TestCol3") %>' runat="server" ID="TestCol3Label" /><br />  
  35.         </td>  
  36.     </ItemTemplate>  
  37.     <LayoutTemplate>  
  38.         <table runat="server">  
  39.             <tr runat="server">  
  40.                 <td runat="server">  
  41.                     <table runat="server" id="groupPlaceholderContainer" border="0">  
  42.                         <tr runat="server" id="groupPlaceholder"></tr>  
  43.                     </table>  
  44.                 </td>  
  45.             </tr>  
  46.             <tr runat="server">  
  47.                 <td runat="server">  
  48.                     <asp:DataPager runat="server" PageSize="5" ID="DataPager1">  
  49.                         <Fields>  
  50.                             <asp:NextPreviousPagerField ButtonType="Button" ShowFirstPageButton="True" ShowNextPageButton="False" ShowPreviousPageButton="False"></asp:NextPreviousPagerField>  
  51.                             <asp:NumericPagerField></asp:NumericPagerField>  
  52.                             <asp:NextPreviousPagerField ButtonType="Button" ShowLastPageButton="True" ShowNextPageButton="False" ShowPreviousPageButton="False"></asp:NextPreviousPagerField>  
  53.                         </Fields>  
  54.                     </asp:DataPager>  
  55.                 </td>  
  56.             </tr>  
  57.         </table>  
  58.     </LayoutTemplate>  
  59.   
  60. </asp:ListView>  
We have added a SqlDataSource control, a ListView control and a DataPager control. In the ListView control we have defined an EmptyDataTemplate which, as the name suggests, is displayed if no datahas been returned.

We have also defined an ItemTemplate which defines the layout of each individual item,A GroupTemplate which contains a placeholder for the item and a LayoutTemplate which defines the layout of the items and also contains our DataPager control.

If you run the application you’ll see that it displays the first 5 items and when you click the little number 2 you’ll see the final 2 items. And that’s it you now have a ListView that pages through data without writing a single line of C# code. Wonderful isn’t it? It is so easy that anyone could do it. A group of ASP.NET controls that work together in complete harmony straight out of the box.

However if you have ever tied this without a data source control you will know that some strange behavior occurs. What was so easy only moments ago, in the previous example, suddenly becomes a bit of a nightmare. To demonstrate thisI will show you what happens when you try to page a list view without a Sql Data Source. In the downloadable project included with this post there is a webform called Default2.aspx. On that webform is two list views, the next example is taken from the list view on the top half of the page.

On your .aspx page add the following markup:
  1. <asp:Label ID="lblError" runat="server"></asp:Label>  
  2. <asp:ListView ID="ListView1" runat="server" GroupItemCount="3">  
  3.     <EmptyDataTemplate>  
  4.         <table runat="server">  
  5.             <tr>  
  6.                 <td>No data was returned.</td>  
  7.             </tr>  
  8.         </table>  
  9.     </EmptyDataTemplate>  
  10.     <EmptyItemTemplate>  
  11.         <td runat="server" />  
  12.     </EmptyItemTemplate>  
  13.     <GroupTemplate>  
  14.         <tr runat="server" id="itemPlaceholderContainer">  
  15.             <td runat="server" id="itemPlaceholder"></td>  
  16.         </tr>  
  17.     </GroupTemplate>  
  18.   
  19.   
  20.     <ItemTemplate>  
  21.         <td runat="server">TestCol1:  
  22.             <asp:Label Text='<%# Eval("TestCol1") %>' runat="server" ID="TestCol1Label" /><br /> TestCol2:  
  23.             <asp:Label Text='<%# Eval("TestCol2") %>' runat="server" ID="TestCol2Label" /><br /> TestCol3:  
  24.             <asp:Label Text='<%# Eval("TestCol3") %>' runat="server" ID="TestCol3Label" /><br />  
  25.         </td>  
  26.     </ItemTemplate>  
  27.     <LayoutTemplate>  
  28.         <table runat="server">  
  29.             <tr runat="server">  
  30.                 <td runat="server">  
  31.                     <table runat="server" id="groupPlaceholderContainer" border="0">  
  32.                         <tr runat="server" id="groupPlaceholder"></tr>  
  33.                     </table>  
  34.                 </td>  
  35.             </tr>  
  36.             <tr runat="server">  
  37.                 <td runat="server">  
  38.                     <asp:DataPager runat="server" PageSize="5" ID="DataPager1">  
  39.                         <Fields>  
  40.                             <asp:NumericPagerField></asp:NumericPagerField>  
  41.   
  42.                         </Fields>  
  43.                     </asp:DataPager>  
  44.                 </td>  
  45.             </tr>  
  46.         </table>  
  47.     </LayoutTemplate>  
  48.   
  49. </asp:ListView>  
As you can see the only difference between this example and the previous example is there is nodata source control present. Obviously it still requires a data source soin your code behind you will query the database to retrieve the data and populate the list view with the returned data.

Before we do that we need to add a reference to the connection string that is stored in the web.config file. I have added mine in above the Page_Load event.
  1. private string connectionString =  
  2.     WebConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;  
Then we need the code that actually queries the database. To do this we will create a sql query, which we will place into a variable for ease of use.
  1. string queryString = "select * from dbo.TestTable";  
Then we need to create a Sql connection object which, as the name suggests connects to the database server.
  1. SqlConnection con = new SqlConnection(connectionString);  
After which we need to do something with this connection so we create a Sql command object which takes two parameters. It needs the command we need to perform, which is currently stored in a handy variable (called queryString) and it also requires the connection object.
  1. SqlCommand cmd = new SqlCommand(queryString, con);  
We will query the database and put the data returned into a dataset which is then used to populate the list view. So we create a new Sql data adapter and initialize it with the command object.
  1. SqlDataAdapter adp = new SqlDataAdapter(cmd);  
Now we need to attempt to open the connection to the database.
  1. Con.Open();  
The next step is to extract the required data and put it data into a dataset. To do that we create a new data set(the first of the following two lines) then we use the Sql Data Adapter to fill the newly created dataset with the data returned by the query (the second line). We do this by calling the sql data adapters fill method which takes two arguments
  1. DataSet ds = new DataSet();  
  2. adp.Fill(ds, "TestTable");  
Then we bind the data in the dataset to the list view. To do this we add the following two lines of code, which adds the data set we created as the data source of the listview then it binds the data to the list view.
  1. ListView1.DataSource = ds;  
  2. ListView1.DataBind();  
Now we need to close the connection to make sure your application runs efficiently and the server doesn’t get jammed up with lots of lingering connections left lying around. We do this by adding the following line of code.
  1. con.Close();  
Below is the complete code that queries the database and binds the list view. I have also added in some error handling code in the form of a Try…Catch…Finally code block.
  1. protected void getData()   
  2. {  
  3.     string queryString = "select * from dbo.TestTable";  
  4.     SqlConnection con = new SqlConnection(connectionString);  
  5.     SqlCommand cmd = new SqlCommand(queryString, con);  
  6.     SqlDataAdapter adp = new SqlDataAdapter(cmd);  
  7.     try  
  8.     {  
  9.         con.Open();  
  10.         DataSet ds = new DataSet();  
  11.         adp.Fill(ds, "TestTable");  
  12.         ListView1.DataSource = ds;  
  13.         ListView1.DataBind();  
  14.   
  15.     } catch (Exception er)   
  16.     {  
  17.         lblError.Text = "Error retrieving data: " + er.Message;  
  18.     } finally  
  19.     {  
  20.         con.Close();  
  21.     }  
  22. }  
Now we have the code to get the data we need to call it. We will do that in the Page_Load event and we will wrap it in an if statement which checks to make sure that it is not a post back as the data only needs to be called once, after which it is stored in memory in a data set. Below is the complete Page_Load event.
  1. protected void Page_Load(object sender, EventArgs e)   
  2. {  
  3.   
  4.     if (!this.IsPostBack)   
  5.     {  
  6.         getData();  
  7.     }  
  8.   
  9. }  
Go ahead and run the application and see what happens. It displays the first page just fine but when you click on the little number 2 you have to click it twice to see the next page but then you are unable to view the previous page again.Very annoying.

In order to correct this you will need to implement the following steps:

First add the ‘OnPagePropertiesChanging’ event to the listview. It is highlighted in yellow for convenience.
  1. <asp:ListView ID="ListView1" runat="server" OnPagePropertiesChanging="ListView1_PagePropertiesChanging" GroupItemCount="3">  
In your code behind you will need to add the event.
  1. protected void ListView1_PagePropertiesChanging(object sender, PagePropertiesChangingEventArgs e) {}  
Then inside that event you will need to find the Data Pager control
  1. DataPager dp = (DataPager)ListView1.FindControl("DataPager1");  
On the line immediately after finding the data pager control you will need to call the data pagers SetPageProperties method. In that method you will set the index of the first record on the page of data, the maximum number of items on a single page of data and rebind the control to false.
  1. dp.SetPageProperties(e.StartRowIndex, e.MaximumRows, false);   
Because we have set the rebind property to false in the previous line we need to call our code that queried the database and data bound the control in the first place.
  1. getData();   
Your complete ListView1_PagePropertiesChanging event should look like this.
  1. protected void ListView1_PagePropertiesChanging(object sender, PagePropertiesChangingEventArgs e)  
  2. {  
  3.     DataPager dp = (DataPager) ListView1.FindControl("DataPager1");  
  4.     dp.SetPageProperties(e.StartRowIndex, e.MaximumRows, false);  
  5.     getData();  
  6. }  
Now run your application and you will see that it now performs the pagination correctly. And as a bonus, despite the initial frustration of discovering the nuances of paging without a Sql Data Source control, it was remarkably simple and easy to implement. Pagination is now demystified for all, I hope.

I have included a downloadable project for you to examine and play with at your leisure.I hope you found this tutorial helpful and easy to follow if you have any questions or comments please add them below in the comments section.

If you know of anyone who may find this article interesting or helpful then please share it around.