ASP.Net VB.Net WebForms usercontrol custom paging controller

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!