Introduction
While working on a project, I realized the need to use the DataList control to present my content data in a list of records format. However the problem encountered while dealing with large number of data records, the user has to wait until all the data is retrieved from the database and bound to the DataList. Of course I could have used the DataGrid control because it already implements inbuilt paging, however I did not require all the other features that it supports other then that, I also wanted to have some control over the displayed HTML and therefore it was time to build my own paging mechanism with the DataList control.
I am sure that you are familiar with the DataList control, if not I recommend you to check the below link for more information.
http://www.w3schools.com/aspnet/aspnet_datalist.asp
Here are the steps that we will perform throughout this demonstration.
- Create a user control and the specified UI
- Write the server side code to implement the needed functionality
HTML Code
In the user control, we will create two panels which will serve as containers for the DataList and the navigation toolbar. The below code shows the HTML for our containers.
<asp:panel id="DataListContainer"
style="Z-INDEX: 101; LEFT: 6px; POSITION: absolute; TOP: 60px"
Runat="server"
width="100%">
<asp:DataList id="DataList1"
BorderWidth="1px"
GridLines="Both"
BorderStyle="None"
BorderColor="#3366CC"
Width="100%" runat="server">
<%-- Style tags ommited to display HTML--%>
<ItemTemplate>
<b><u>FirstName</u></b>
<asp:Label ID="lblName" Runat="server">
<%#DataBinder.Eval(Container.DataItem,"FirstName")%>
</asp:Label><br />
<b><u>LastName</u></b>
<asp:Label ID="lblLastName" Runat="server">
<%#DataBinder.Eval(Container.DataItem,"LastName")%>
</asp:Label><br />
<b><u>Company</u></b>
<asp:Label ID="lblCompany" Runat="server">
<%#DataBinder.Eval(Container.DataItem,"Company")%>
</asp:Label>
<div id="Line" runat="server"
style="azimuth:center; text-align:center;">
<hr style="width:50%;azimuth:center" />
</div>
</ItemTemplate>
<HeaderStyle Font-Bold="True"
ForeColor="#CCCCFF"
BackColor="#003399" />
</asp:DataList>
<asp:panel id="Navigation" style="TEXT-ALIGN: center" Width="100%"
runat="server" Height="17">
<table>
<tr>
<td style="WIDTH: 400px; TEXT-ALIGN: left">
<asp:Label id="RecordNumber" runat="server"
CssClass="RecordNumber"></asp:Label></td>
<td style="WIDTH: 200px; TEXT-ALIGN: left">
<asp:Label id="PageNumber" runat="server"
CssClass="RecordNumber"></asp:Label></td>
<td style="WIDTH: 50px; TEXT-ALIGN: left">
<asp:Label id="lblNext" runat="server"></asp:Label></td>
<td style="WIDTH: 50px; TEXT-ALIGN: left">
<asp:Label id="lblPrevious" runat="server"></asp:Label></td>
<td style="FLOAT: right; WIDTH: 25px; CURSOR: hand">
<asp:ImageButton id="Previous" Runat="server"
AlternateText="Previous page"
ImageUrl="Resources/NewsImages/Navigation/Back.jpg" /></td>
<td style="FLOAT: left; WIDTH: 25px; CURSOR: hand">
<asp:ImageButton id="Next" style="POSITION: relative" Width="16"
runat="server" AlternateText="Next Page"
ImageUrl="Resources/NewsImages/Navigation/Next.jpg" /></td>
</tr>
</table>
</asp:panel>
</asp:panel>
Two images have been used (Next and Previous) which can be found in the attached download zip file.
Code Behind
In this section, we will write the necessary code to implement our logic. First we will setup the database connection, retrieve our data, filter them based on specific criteria, and then bind it to the control.
Below are the main events and methods we will use:
- The
page_load event used to open a database connection, fill the DataSet which will be saved into a session variable to increase the performance and minimize the need for an opened connection while users are navigating through the control.
- We will handle two
onclick events for the next and previous ImageButtons. The events are called GoNext and GoPrevious.
- The following helper functions are created:
BindDatalist, FillDataSet, BindAndSetPage, and SetNext/.
- A public string property
RowsNumber in which we will allow the developer to specify how many records to display per page.
The Page Load Event:
private void Page_Load(object sender, System.EventArgs e)
{
DataSet ds = Session["ds"] as DataSet;
if (ds == null)
{
// Save the dataset into a session variable
Session["ds"] = this.FillDataSet(ds);
// call the BindDataList function
BindDataList();
}
// Show the total number of records
RecordNumber.Text = "Total Number of News: " +
ds.Tables[0].Rows.Count.ToString();
}
In the code above, we will check if the session variable containing the DataSet is null, we will open the database connection, fetch our data and store them in a DataSet object through the FillDataSet function which will be saved into a session variable, and then call the BindDataList function.
FillDataSet Function
protected DataSet FillDataSet(DataSet ds)
{
// Get the connection string form the Web.Config file
string conString =
ConfigurationSettings.AppSettings["LocalSqlServer"].ToString();
// Set the select statement
string Select = "SELECT * FROM Customers";
// Create an instance of the SqlDataAdapter object and fill the dataset
SqlDataAdapter da = new SqlDataAdapter(Select, conString);
// Fill the dataset
da.Fill(ds, "Customers");
return ds;
}
FillDataSet Method
protected void BindDataList()
{
this.SetObjects();
// Check if the number of rows per page is specified
if(rowsNumber != null || rowsNumber != string.Empty)
{
// store the number of rows per page in an integer variable
int rowsToDisplay = Convert.ToInt32(rowsNumber);
if(rowsToDisplay >0 && rowsToDisplay < dt.Rows.Count)
{
// if page is first time loaded
if (!Page.IsPostBack)
{
// Filter the dataview according to the first two records
dv.RowFilter = "ID >= 1 AND ID<= " +
dt.Rows[Convert.ToInt32(rowsNumber)-1]["ID"];
// get the number of Pages
double pageNumber = Convert.ToDouble(dt.Rows.Count) / rowsToDisplay;
int pageNumb = Convert.ToInt32(Math.Ceiling(pageNumber));
Session["PageNumber"] = pageNumb;
// Display The current page and the max number of pages.
PageNumber.Text = "Page 1 of " + pageNumb.ToString();
// if more than 1 page and the specified rows
are less than the records number
if (pageNumb > 1 && rowsToDisplay < dt.Rows.Count)
{
// Set the ID of the next two records to be displayed inside a label
this.SetNext(rowsToDisplay,0);
}
else
{
// Hide the Previous and Next images
Page.RegisterStartupScript("hideimage","<script language=javascript>
document.getElementById('Back').style.display='none';
document.getElementById('Next').style.display='none';
</script>");
}
}
else
{
// Set the next records ids
this.SetNext(rowsToDisplay,0);
}
}
else
{
// Hide the Previous and Next images
Page.RegisterStartupScript("hideimage","<script language=javascript>
document.getElementById('"+Next.ClientID+"').style.display='none';
document.getElementById('"+Previous.ClientID+"').style.display='none';
</script>");
}
this.BindAndSetPage(true,true);
}
}
The code above is responsible for filtering data to show specified records in the DataList control. We used the DataView object to filter records based on the ID column in the database, Calculated the number of pages, then calling the SetNext method which is used to retrieve the next two ids to be saved inside a hidden Label control. Of course, the JavaScript code is injected to the form to show or hide the images; finally we bind the DataList to the DataView object.
protected void SetObjects()
{
// retrieve the dataset from the session and create a Dataview object
DataSet ds = (DataSet)Session["ds"];
dt = ds.Tables[0];
dv = dt.DefaultView;
}
BindAndSetPage Function
protected void BindAndSetPage(bool IsNext,bool firstLoad)
{
int pagesNumb = Convert.ToInt32(Session["PageNumber"]);
string[] pageNumb = PageNumber.Text.Split(' ');
if(!firstLoad)
{
if(!IsNext)
{
// set the page number
int pages = Convert.ToInt32(pageNumb[1])-1;
PageNumber.Text = "Page " + pages.ToString() + " of " +
pagesNumb.ToString();
}
else
{
int pages = Convert.ToInt32(pageNumb[1])+ 1;
PageNumber.Text = "Page " + pages.ToString() + " of " +
pagesNumb.ToString();
}
}
// Bind the datasource
DataList1.DataSource = dv;
DataList1.DataBind();
Page.DataBind();
}
Figure 1

You should see a similar screenshot as above, when the datalist control is first loaded.
SetNext Function
protected void SetNext(int rowsToDisplay,int finalRecord)
{
if(!Page.IsPostBack)
{
Page.RegisterStartupScript("hideimage", "<script language=javascript>
document.getElementById('"+Previous.ClientID+"').style.display='none';
</script>");
}
if(finalRecord>-1)
{
// Cleat the next label
lblNext.Text = "";
// Get the number of records
int rowsCount = dt.Rows.Count;
int nextRows = finalRecord + rowsToDisplay;
if(rowsCount -1 > nextRows)
{
// set the next label to the next records
lblNext.Text = nextRows.ToString() + " " + (nextRows +1).ToString();
}
else
{
// save the next first record in a variable
string nextText = Convert.ToString(finalRecord + rowsToDisplay-1);
if(Convert.ToInt32(nextText) < rowsCount -1)
{
// get the id of the next first record
lblNext.Text = dt.Rows[Convert.ToInt32(nextText)]["ID"].ToString();
// loop through all the next records
for(int i= finalRecord + rowsToDisplay; i<rowsCount -1;i++)
{
// save the records ID in the next label
lblNext.Text = lblNext.Text + " " + dt.Rows[i]["ID"];
if(i == rowsCount)
{
// Hide the next image
Page.RegisterStartupScript("hideimage", "<script language=javascript>
document.getElementById('"+Next.ClientID+"').style.display='none';
</script>");
}
}
}
else
{
// hide the next image
Page.RegisterStartupScript("hideimage", "<script language=javascript>
document.getElementById('"+Next.ClientID+"').style.display='none';
</script>");
}
}
}
}
The above method is used to save the next records ID into a label control.
The values are then used each time the user presses the Next or Previous link to keep track
on which records to display on each event.
GoNext Event
protected void GoNext(object sender, System.Web.UI.ImageClickEventArgs e)
{
// Get the number of records
int rowsToDisplay = Convert.ToInt32(rowsNumber);
// Get the next id from the hidden label
string next = lblNext.Text;
// Get the dataset from the session and create a DataView object
DataSet ds = (DataSet)Session["ds"];
DataView dt = new DataView(ds.Tables[0]);
// if more than 2 records id exists
if(next.Length >1)
{
// Split the hidden label text by the space character
string[] records = next.Split(Convert.ToChar(" "));
// filter the DataView
dt.RowFilter = "ID >= " + records[0] + " AND ID <= " + records[1];
// Get the previous ID
int previousID = Convert.ToInt32(records[0]) - rowsToDisplay;
// Save the previous Two records ID in the hidden previous label
lblPrevious.Text = previousID.ToString() + " " +
(Convert.ToInt32(records[0]) -1).ToString();
// set the next records ID
int finalRecord = Convert.ToInt32(records[0]);
this.SetNext(ds,rowsToDisplay,finalRecord);
}
else
{
if(next != string.Empty)
{
dt.RowFilter = "ID = " + next;
Page.RegisterStartupScript("hideimage", "<script language=javascript>
document.getElementById('"+Next.ClientID+"').style.display='none';
</script>");
// Set the previous id
int previousID = Convert.ToInt32(next) - rowsToDisplay ;
lblPrevious.Text = previousID.ToString() + " " +
(Convert.ToInt32(next) -1).ToString();
}
}
// Set the Page Number
int pagesNumb = Convert.ToInt32(Session["PageNumber"]);
string[] pageNumb = PageNumber.Text.Split(' ');
int pages = Convert.ToInt32(pageNumb[1])+ 1;
PageNumber.Text = "Page " + pages.ToString() + " of " + pagesNumb.ToString();
// Bind the dataView to the datalist
DataList1.DataSource = dt;
DataList1.DataBind();
Page.DataBind();
}
The GoNext event is used to handle the Next image click event. In the above code, we will retrieve the values from the label control, filter the DataView object based on these values, set the previous and next records id and then bind it to the DataList control.
GoPrevious Event
protected void GoPrevious(object sender, System.Web.UI.ImageClickEventArgs e)
{
// Save the next records into a variable and get the total number of rows.
string next = lblNext.Text;
int rowsToDisplay = Convert.ToInt32(rowsNumber);
this.SetObjects();
// get the previous id from the hidden label
string previousID = lblPrevious.Text;
string[] filterPreID = previousID.Split(Convert.ToChar(" "));
//set the next two recrods
this.SetNext(rowsToDisplay,Convert.ToInt32(filterPreID[0]));
// set the previous id in the hidden label
int lastprevID = Convert.ToInt32(filterPreID[0]);
if((lastprevID - rowsToDisplay) > -1)
{
lblPrevious.Text = Convert.ToString((lastprevID - rowsToDisplay)) + " " +
Convert.ToString((lastprevID -1));
}
else
{
Page.RegisterStartupScript("hideimage", "<script language=javascript>
document.getElementById('"+Previous.ClientID+"').style.display='none';
</script>");
}
// filter the dataview
dv.RowFilter= "ID >= " + dt.Rows[lastprevID]["ID"] + " AND ID<= " +
dt.Rows[Convert.ToInt32(filterPreID[1])]["ID"] ;
this.BindAndSetPage(false,false);
}
The above code is being executed when the previous image is clicked. It is similar to the GoNext functionality however to retrieve previous records.
We are retrieving the values of the previous records from a hidden label.
Figure 2

The above figure will show how the DataList control will look when the user clicked on the next image.
You may have realized that the next image now is hidden because there are no records to navigate.
The RowsNumber Property
public string RowsNumber
{
get
{
return rowsNumber;
}
set
{
rowsNumber = value;
}
}
The above property is used to enable the developer to specify how many records he/she wants to display per page.
The main advantage of this user control is to give the developer the option to specify how many
records he/she wants to display per page. This can be achieved by setting the RowsNumber property.
<uc1:PagingDataList
id="PagingDataList1"
runat="server"
rowsNumber="25"/>
Conclusion
After completing the demonstration, you will have a better understanding on how to implement custom paging with a DataList control. It has been developed as a user control to include it into future projects. The page and injected JavaScript files has been tested on IE7 and Mozilla Firefox 2.0.
Download
Paging Project
About Haissam Abdul Malak
 |
Haissam Abdul Malak works as senior software developer in CCC. He has been developing web application using ASP.NET technology over the last 3 years. He achieved the Microsoft Certified Application Developer [MCAD] and been certified since 2006.
He is a regular contributor on the ASP.NET official f...
This author has published 3 articles on DotNetSlackers. View other articles or the complete profile here.
|
You might also be interested in the following related blog posts
DataBinding with user control as a template
read more
Creating CSS Friendly Websites with ASP.NET 2.0
read more
Taming DataList With a Custom Adapter
read more
Update: The LINQ to XML extensibility story
read more
Silverlight.FX Effects in Depth
read more
InPlaceEditing - Implementing Extender Server Controls
read more
Windows Workflow Tracking and the TrackingExtract functionality
read more
WPF / Silverlight: RadChart and BindableLinq
read more
|
|
Please login to rate or to leave a comment.