So you need to page your data. Simple enough. Most of the ASP.Net data controls have embedded paging and turing it on is simple.
What isn't simple, is making it look half way decent! So I thought "...I wonder if I could build a generic paging control to mimic the Google-esk style!" Well...here goes...
I'm going to give you the code first, then explain what's happening. Remember to rate and comment if your just here for the code!
There are three files. A UserControl and it's code behind and a display page.
User Control HTML
*****START*****
<%@ Control Language="VB" AutoEventWireup="false" CodeFile="PagerControl.ascx.vb" Inherits="PagerControl" %>
<style type="text/css">
.RemoveAll {padding:0;margin:0;border:none;}
</style>
<table id="TBPC1" class="TBPCstyle">
<tr>
<td align="right" valign="bottom" class="RemoveAll">
<asp:HyperLink ID="hypPrevPage" runat="server" CssClass="RemoveAll">
<asp:Image ID="imgLeftImage" runat="server" CssClass="RemoveAll" />
</asp:HyperLink>
</td>
<asp:PlaceHolder ID="plcRepeatedImages" runat="server" />
<td align="left" valign="bottom" class="RemoveAll">
<asp:HyperLink ID="hypNextPage" runat="server" CssClass="RemoveAll" >
<asp:Image ID="imgRightImage" runat="server" CssClass="RemoveAll" />
</asp:HyperLink>
</td>
</tr>
<tr>
<td align="center" valign="top" class="RemoveAll">
<asp:HyperLink ID="hypPrevPage2" runat="server" Text="Prev" CssClass="RemoveAll" />
</td>
<asp:PlaceHolder ID="plcPageNumbers" runat="server" />
<td align="center" valign="top" class="RemoveAll">
<asp:HyperLink ID="hypNextPage2" runat="server" Text="Next" CssClass="RemoveAll" />
</td>
</tr>
</table>
*****STOP*****
User Control Code Behind:
*****START*****
Imports System.Web.UI.WebControls
Imports System.Data
Imports System.Data.Sql
Imports System.Data.SqlClient
Imports MyCustomDAlayer.DataAccessLayer
Partial Class PagerControl
Inherits System.Web.UI.UserControl
Private _PagesToDisplay As Integer = 0
Private _ConnectionString As String = String.Empty
Private _SelectCommand As String = String.Empty
Private _SelectCommandType As System.Data.CommandType = CommandType.StoredProcedure
Private _ControlToPage As String = String.Empty
Private _ItemsPerPage As Integer = 10
Private _LeftImage As String = String.Empty
Private _RepeatImage As String = String.Empty
Private _RightImage As String = String.Empty
#Region "Control Properties"
'the number of images to display in the control as page links
Public Property PagesToDisplay() As Integer
Get
Return _PagesToDisplay
End Get
Set(ByVal value As Integer)
_PagesToDisplay = value
End Set
End Property
'the connection string to use to connect to the DB
Public Property ConnectionString() As String
Get
Return _ConnectionString
End Get
Set(ByVal value As String)
_ConnectionString = value
End Set
End Property
'The SQL SELECT command to use, either SQL string, View, Stored Procedure name etc
Public Property SelectCommand() As String
Get
Return _SelectCommand
End Get
Set(ByVal value As String)
_SelectCommand = value
End Set
End Property
'The SQL Command type
Public Property SelectCommandType() As System.Data.CommandType
Get
Return _SelectCommandType
End Get
Set(ByVal value As System.Data.CommandType)
_SelectCommandType = value
End Set
End Property
'the name of the control to paginate
Public Property ControlToPage() As String
Get
Return _ControlToPage
End Get
Set(ByVal value As String)
_ControlToPage = value
End Set
End Property
'Gets and sets the 1-based index of the page
Public Property CurrentPageIndex() As Integer
Get
Dim o As Object = Me.ViewState("_CurrentPage")
If IsDBNull(o) Or o Is Nothing Then
Return 1
Else
Return CInt(o)
End If
End Get
Set(ByVal value As Integer)
Me.ViewState("_CurrentPage") = value
End Set
End Property
'Gets and sets the number of records to display per page. Default is 1
Public Property ItemsPerPage() As Integer
Get
Return _ItemsPerPage
End Get
Set(ByVal value As Integer)
If value < 1 then value = 1
_ItemsPerPage = value
End Set
End Property
'the full path of the image to display as the left side image
Public Property LeftImage() As String
Get
Return _LeftImage
End Get
Set(ByVal value As String)
_LeftImage = value
End Set
End Property
'the full path of the image to display, repeated, depending on number pages
Public Property RepeatImage() As String
Get
Return _RepeatImage
End Get
Set(ByVal value As String)
_RepeatImage = value
End Set
End Property
'the full path of the image to display as the right side image
Public Property RightImage() As String
Get
Return _RightImage
End Get
Set(ByVal value As String)
_RightImage = value
End Set
End Property
#End Region
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
'ensure there is a control to paginate
If IsDBNull(_ControlToPage) Then Exit Sub
'ensure there is a connection string and command
If _ConnectionString = "" Or _SelectCommand = "" Then Exit Sub
'handle link click
If Not Request.QueryString("Pg") Is Nothing Then
CurrentPageIndex = Integer.Parse(Request.QueryString("Pg"))
End If
'Get the data and populate the counters
Dim MyPagedData As New PagedDataSource
Dim MyDS As New DataSet
MyDS = SqlHelper.ExecuteDataset(_ConnectionString, _SelectCommandType, _SelectCommand)
MyPagedData.DataSource = MyDS.Tables(0).DefaultView
MyPagedData.AllowPaging = True
MyPagedData.PageSize = _ItemsPerPage
MyPagedData.CurrentPageIndex = CurrentPageIndex - 1
Dim PageCount As Integer = 1
Dim StartPageNumber As Integer = 1
Dim StopPageNumber As Integer = _PagesToDisplay
If _PagesToDisplay > MyPagedData.PageCount Then
_PagesToDisplay = MyPagedData.PageCount
StopPageNumber = MyPagedData.PageCount
Else
Dim tmp As Integer = CurrentPageIndex - 1
If tmp >= _PagesToDisplay Then
Dim intH As Integer = CurrentPageIndex \ _PagesToDisplay
StartPageNumber = (intH * _PagesToDisplay) + 1
StopPageNumber = (intH * _PagesToDisplay) + _PagesToDisplay
If StopPageNumber > MyPagedData.PageCount Then
StopPageNumber = MyPagedData.PageCount
End If
End If
imgLeftImage.ImageUrl = _LeftImage
imgRightImage.ImageUrl = _RightImage
If CurrentPageIndex = MyPagedData.PageCount Then
hypNextPage2.Visible = False
Else
hypNextPage.NavigateUrl = Request.CurrentExecutionFilePath & "?Pg=" & (CurrentPageIndex + 1)
hypNextPage2.NavigateUrl = Request.CurrentExecutionFilePath & "?Pg=" & (CurrentPageIndex + 1)
End If
If CurrentPageIndex = 1 Then
hypPrevPage2.Visible = False
Else
hypPrevPage.NavigateUrl = Request.CurrentExecutionFilePath & "?Pg=" & (CurrentPageIndex - 1)
hypPrevPage2.NavigateUrl = Request.CurrentExecutionFilePath & "?Pg=" & (CurrentPageIndex - 1)
End If
For PageCount = StartPageNumber To StopPageNumber
Dim New_Table_Cell1 As New HtmlTableCell
Dim New_Table_Cell2 As New HtmlTableCell
Dim PageHyperlink As New HtmlAnchor
Dim NumericHyperlink As New HtmlAnchor
Dim NumericLabel As New Label
Dim RepeaterImage As New HtmlImage
New_Table_Cell1.Attributes.Add("valign", "top")
New_Table_Cell1.Attributes.Add("class", "RemoveAll")
RepeaterImage.Src = Page.ResolveUrl(_RepeatImage)
RepeaterImage.Alt = "Page " & PageCount
RepeaterImage.Attributes.Add("class", "RemoveAll")
If PageCount = CurrentPageIndex Then
New_Table_Cell1.Controls.Add(RepeaterImage)
NumericLabel.Text = PageCount
New_Table_Cell2.Attributes.Add("align", "center")
New_Table_Cell2.Attributes.Add("valign", "bottom")
New_Table_Cell2.Attributes.Add("class", "RemoveAll")
New_Table_Cell2.Controls.Add(NumericLabel)
Else
PageHyperlink.Controls.Add(RepeaterImage)
PageHyperlink.Target = "_self"
PageHyperlink.HRef = Request.CurrentExecutionFilePath & "?Pg=" & PageCount
PageHyperlink.Attributes.Add("class", "RemoveAll")
New_Table_Cell1.Controls.Add(PageHyperlink)
NumericHyperlink.Target = "_self"
NumericHyperlink.HRef = Request.CurrentExecutionFilePath & "?Pg=" & PageCount
NumericHyperlink.InnerText = PageCount
NumericHyperlink.Style.Add("text-decoration", "none")
New_Table_Cell2.Attributes.Add("align", "center")
New_Table_Cell2.Attributes.Add("valign", "bottom")
New_Table_Cell2.Attributes.Add("class", "RemoveAll")
New_Table_Cell2.Controls.Add(NumericHyperlink)
End If
plcPageNumbers.Controls.Add(New_Table_Cell2)
plcRepeatedImages.Controls.Add(New_Table_Cell1)
Dim MyGrid As New GridView
MyGrid = Me.Parent.FindControl(_ControlToPage)
MyGrid.DataSource = MyPagedData
MyGrid.DataBind()
Next
End Sub
End Class
*****STOP*****
Display Pages (Default.aspx there is NO code behind!)
*****START*****
<%@ Page Language="VB" %>
<%@ Register src="~/PagerControl.ascx" tagname="PagerControl" tagprefix="uc1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="True" />
<center>
<uc1:PagerControl ID="Paging_Control1" runat="server"
ConnectionString="<%$ ConnectionStrings:MyConnectionString %>"
ControlToPage="GridView1"
ItemsPerPage="10"
PagesToDisplay="10"
LeftImage="~/Images/LeftImage.gif"
RepeatImage="~/Images/RepeatImage.gif"
RightImage="~/Images/RightImage.gif"
SelectCommand="My_Stored_Procedure_To_Get_Data"
SelectCommandType="StoredProcedure"
/>
</center>
</div>
</form>
</body>
</html>
*****STOP*****
Final Output:
How It Works!
So this control uses ViewState to send persistent data back to the server, make sure it's turned on.
On the display page, you need a GridView control. If you use any other out-of-the-box ASP control or another custom control, you need to specify it where the FindControl assigns a reference to the "MyGrid" object in the Page_Load code above.
Once you have this control, dump the "PagerControl" UserControl onto the page and configure it.
- ConnectionString - The database connection string you are using. In the example above, I am using the web.config entry called "MyConnectionString". This is a required option.
- ControlToPage - This is the data display control you added. In the example above, i used a GridView called "GridView1". Beause the PagerControl passes the "page of data being display" to the control, this entry is required.
- ItemsPerPage - This controls how many entries appear in the paged control. Not required and the default is 1 if ommitted
- PagesToDisplay - This control the center page number display. Think of the Google search engine. When you have hundreds of returns from a search, the "O" is repeated a set number of times. This controls that count.
- LeftImage - Again, using Google, this would be the image of the "G". It is the first clickable item and has the same link as the first "O". If no image is set, nothing is displayed.
- RepeatImage - This is the name of the "O" file. If no image is set, nothing is displayed.
- RightImage - This is the name of the "gle" image file. If no image is set, nothing is displayed.
- SelectCommand - This is the SQL statement or stored procedure name to get data. This is required.
- SelectCommandType - This is the System.Data.CommandType enumeration value of the above SelectCommand. This is required.
So what happens is the PagerControl makes a call to your database and retreives the first page of data. Counts the records, looks to see how many "pages" of data there will be based on the "ItemsPerPage" entry. It then populates all the counters, then renders the control.
When you click one of the links, that "image button" sends postback data to the control, along with the settings, and gets the page requested. And before you ask, I did try using ImageButtons, but they didn't display well and I didn't want to go down the route of dynamically creating controls for something so small.
I also did not use the TBPCstyle CSS style in this example. You would use that for fine-tuning to get the look you want.
I'm sure there's a million different ways to do this, but this is my way.
Hope it helps!