Introduction
With the out-of-the-box ASP.NET DataGrid
or GridView
, it's very easy to control data with paging. However, the weakness is you have to bind all data in order to make the paging to work. It's not practical. Let's say you have 1,000,000 number of data rows and you want to display only 25 per page, you will not want to bind the whole 1,000,000 data rows just for making the paging to work.
This control that I wrote inherits from Repeater
, and I added a Literal
control for the paging. This control also implements IPostBackEventHandler
, which allows you to perform postback to the server (used by the Literal
for the paging). This control allows you to bind only the data that you want to display to the user, but also makes the paging to work.
Background
This article is only for intermediate-advanced ASP.NET developers who have had experience in creating custom controls. The files attached are .vb files which you need to add to your own project. I will not explain how to create a Class Library project in Visual Studio, etc.
This control will use a custom EventArgs
and delegates and events. If you're not familiar with these, you may get confused.
Using the Code
From the file attachment, RepeaterWithPaging.vb.
- Add a reference to
System.Web
. - Add the
Imports
declarations.
Imports System.Collections.Generic
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
- Inherit from
Repeater
and implement IPostBackEventHandler
to allow postback.
Public Class RepeaterWithPaging _
Inherits Repeater _
Implements IPostBackEventHandler
- Create the required class variables and properties.
#Region "Class Variables"
Private litPaging As Literal
Private intNumberOfData As Integer
Private intNumberPerPage As Integer
Private intCurrentPageNumber As Integer
Private strPagingCssClass As String
Public Event PagingClicked As PagingLiteralEventHandler
#End Region
#Region "Public Properties"
Public Property NumberOfData() As Integer
Get
Return intNumberOfData
End Get
Set(ByVal Value As Integer)
intNumberOfData = Value
End Set
End Property
Public Property NumberPerPage() As Integer
Get
Return intNumberPerPage
End Get
Set(ByVal Value As Integer)
intNumberPerPage = Value
End Set
End Property
Public Property CurrentPageNumber() As Integer
Get
Return Me.intCurrentPageNumber
End Get
Set(ByVal value As Integer)
Me.intCurrentPageNumber = value
End Set
End Property
Public Property PagingCssClass() As String
Get
Return Me.strPagingCssClass
End Get
Set(ByVal value As String)
Me.strPagingCssClass = value
End Set
End Property
#End Region
- Implement the required method from
IPostBackEventHandler
. This method is called when the postback is triggered by the paging. I'll explain this later.
Public Sub RaisePostBackEvent(ByVal eventArgument As String) _
Implements System.Web.UI.IPostBackEventHandler.RaisePostBackEvent
End Sub
- Create a delegate for handling the postback event by the paging literal.
Public Delegate Sub PagingLiteralEventHandler(ByVal sender As Object, _
ByVal ev As PagingLiteralEventArgs)
- Create an event based on that delegate.
Public Event PagingClicked As PagingLiteralEventHandler
- The
PagingLiteralEventArgs
class is defined in the file PagingLiteralEventArgs.vb. This file inherits from the EventArgs
class, and it has a public property CurrentPage
. Using EventArgs
, you can pass arguments. You can add as many arguments as you want, but for the paging literal control to work, we only need the CurrentPage
property which we use to get the current page number viewed by the user.
Public Class PagingLiteralEventArgs
Inherits EventArgs
#Region "Class Variables"
Private intCurrentPage As Integer
#End Region
#Region "Public Properties"
Public Property CurrentPage() As Integer
Get
Return intCurrentPage
End Get
Set(ByVal Value As Integer)
intCurrentPage = Value
End Set
End Property
#End Region
End Class
- Back to RepeaterWithPaging.vb. We now need to initialize the
Literal
for paging. Let's override the OnInit
method from the base class, and also, when the page is not posting back, we set the current page number to 1.
Protected Overrides Sub OnInit(ByVal e As System.EventArgs)
MyBase.OnInit(e)
litPaging = New Literal
Me.Controls.Add(litPaging)
If Not Me.Page.IsPostBack Then
Me.intCurrentPageNumber = 1
End If
End Sub
- Populate the paging literal. I want to stress out this method:
Me.Page.ClientScript.GetPostBackEventReference(Me, i)
This method is the one that triggers the postback! You can add this method as a JavaScript call, and if you View Source in Internet Explorer or Firefox, you can see that it renders as _doPostBack()
. This method passes two input parameters: the calling control and the argument. I pass Me
as the control and the page number (denoted by "i
" - see the method below) to pass the clicked page number as the argument.
This will then be captured by this method implemented from the interface explained earlier:
RaisePostBackEvent(ByVal eventArgument As String)
The eventArgument
variable will contain the argument passed.
Private Sub PopulatePaging()
Dim intNoOfPages As Integer = Me.NumberOfData / Me.NumberPerPage
If Me.NumberOfData Mod Me.NumberPerPage > 0 Then
intNoOfPages += 1
End If
For i As Integer = 1 To intNoOfPages
If i = Me.intCurrentPageNumber Then
Me.litPaging.Text &= "<a class=""" & _
Me.PagingCssClass & """ href=""#"" onclick=""" & _
Me.Page.ClientScript.GetPostBackEventReference(Me, i) & _
"""><b>" & i & "</b></a> | "
Else
Me.litPaging.Text &= "<a class=""" & Me.PagingCssClass & _
""" href=""#"" onclick=""" & _
Me.Page.ClientScript.GetPostBackEventReference(Me, i) & """>" & _
i & "</a> | "
End If
Next
End Sub
- Within
RaisePostBackEvent()
, trigger the PagingClicked
event we declares previously. It means that when the text is clicked on the paging literal, the PagingClicked event is fired. We then pass in the page number argument that is passed through the eventArgument
variable.
Public Sub RaisePostBackEvent(ByVal eventArgument As String) _
Implements System.Web.UI.IPostBackEventHandler.RaisePostBackEvent
Dim objEventArgs As New PagingLiteralEventArgs
objEventArgs.CurrentPage = CInt(eventArgument)
Me.intCurrentPageNumber = eventArgument
RaiseEvent PagingClicked(Me, objEventArgs)
End Sub
- Render both the
Repeater
and the Literal
.
Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
MyBase.Render(writer)
writer.Write("<p/>")
Me.PopulatePaging()
Me.litPaging.RenderControl(writer)
End Sub
That's it! Now, let's go to the front end ASPX page.
Front-End
- Register your custom control.
<%@ Register TagPrefix="CRE" Namespace="ContentReadyEnterprise.Web.Controls"
Assembly="ContentReadyEnterprise.Web.Controls" %>
- Declare your custom repeater as a normal repeater, but note the custom properties and the event we created earlier. When you create a new event based on a custom delegate (in this case,
PagingClicked
), Visual Studio automatically adds the "On" word in front of it! Pretty good! When you view the properties of the Repeater
from the ASPX, you can see the OnPagingClicked
event is there!
<CRE:RepeaterWithPaging ID="rptTest" runat="server"
OnPagingClicked="Paging_Clicked"
NumberOfData="100" NumberPerPage="10">
<ItemTemplate>
<%#Container.DataItem%>
<br />
</ItemTemplate>
</CRE:RepeaterWithPaging>
- On the code-behind, let's capture the
Paging_Clicked
event.
Protected Sub Paging_Clicked(ByVal sender As Object, ByVal ev As PagingLiteralEventArgs)
Response.Write("I click page number: " & ev.CurrentPage.ToString())
End Sub
- Bind the test data and test the paging.
Protected Sub Page_Load(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Me.Load
If Not Page.IsPostBack Then
Me.GetTestData()
End If
End Sub
Protected Sub Paging_Clicked(ByVal sender As Object, _
ByVal ev As PagingLiteralEventArgs)
Me.GetTestData()
End Sub
Private Sub GetTestData()
Dim alistTest As New List(Of String)
Dim intStartNumber As Integer = _
rptTest.CurrentPageNumber * rptTest.NumberPerPage
If rptTest.CurrentPageNumber = 1 Then
intStartNumber = 1
End If
Dim intEndNumber As Integer = intStartNumber + rptTest.NumberPerPage
For i As Integer = intStartNumber To intEndNumber
alistTest.Add(i)
Next
rptTest.DataSource = alistTest
rptTest.DataBind()
End Sub
That's it guys!
Points of Interest
It's very interesting to see how easy it is to make a control that can do postback.