So you need to page your data. Simple enough. Most of the 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 behine and a display page.
User Control
*****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>
<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 or Stored Procedure name
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 10
Public Property ItemsPerPage() As Integer
Get
Return _ItemsPerPage
End Get
Set(ByVal value As Integer)
_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 depaending on page number
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
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
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*****
Right....here goes....
In a nutshell, the paging control is actually a "data collector". It gets your data, pages it, sets various properties then passes the paged data to a "display control". In this case Gridview1.
The usercontrol (Paging_Control1) exposes several properties that must be set. I think most of them are self-explanitory, but I'll go through them anyway.
- Connectionstring - Your connection string to the database
- ControlToPage - The control that will receive the paged data. In the code above, this is GridView1
- ItemsPerPage - How many records (items) you want the control to display
- PagesToDisplay - This sets the number of repeated "o's" for the control. However, in the code behind we will check that that number to be displayed does no exceed the number available
- LeftImage - the full path to the image you want to use on the left of the control. On Google pages, this is the "Go" letters
- RepeatImage - the repeated image for each page of data. On Google pages, this is the letter "o"
- RightImage - the full path to the image you want to use on the right of the control. On Google pages, this is the "gle >" letters
- SelectCommand - either the full SQL text (IE: SELECT * FROM Table1) or the stored procedure name
- SelectCommandType - sets what type of command the above is,
The only "dynamic" variable we need to get is the current page index/number. This is stored in viewstate and passed to the control through the QueryString object as well.
When the control "Page_Load" event fires, the select command, connection string and control to page variable are checked. Then the "Page number" variable is checked. A "PagedDataSource" object is filled with a "DataSet" and the paging properties are applied.
The it cycles through and builds the "o's" and number's (below the o's) as HTML controls, loading them into a "PlaceHolder" control when finished.
I could have built this to display nothing if no data was found, but since I'm using it where I know data is present, I didn't bother! ;)
At any rate, it works rather well. This is a base control, loads of room for improvement. I based the idea off of Dino Esposito's article on MSDN (dated October 2003) where he walks you through building a very boring SQL pager control in C#. The article is very good, I recommend you check it out (http://msdn.microsoft.com/en-us/library/ms972960(printer).aspx). Mine is better! And in VB!!!
Remember to comment and rate!
Have fun!!