There are many times when you need to display a large amount of data to a user. When this need arises, you could of course use a DataGrid and make use of the in-built paging mechanism which makes paging a breeze. However, what if you can't use a DataGrid? Maybe the data you are displaying does not fit the structure of a DataGrid. Fortunately, we can borrow the DataGrid's paging mechanism and use it to create our own paging control which will allow us to add paging capabilities to a wider range of controls.
This post aims to give you an alternative that will allow you to create your own custom paging control which can be bound to any control that supports the IEnumerable Interface. Another goal of this control is to allow for the paging of any data that again supports the IEnumerable interface. So rather than only paging data from a Database (through the use of a DataTable or DataView), you can also bind an ArrayList for example to the paging control and page through the contents of the ArrayList or even create your own specialised class' that implements the IEnumerable interface and page through that.
Create the User Control
First, we need to create the user control, so add a new User Control item to your project and name it "Paging". Then add the following controls (you may want to add them to a table or div, the choice is yours):
| Control Type | ID | Text |
| LinkButton | lkbMoveFirst | Move First |
| LinkButton | lkbMovePrevious | Move Previous |
| Label | lblPageInformation | |
| LinkButton | lkbMoveNext | Move Next |
| LinkButton | lkbMoveLast | Move Last |
Add the Paging Code
Copy the following code into the code-behind file of the Paging control:
Option Strict On
Partial Class paging
Inherits System.Web.UI.UserControl
'Member variables
Private _dataSource As IEnumerable
Private _pageSize As Int32 = 10
Private _pageNumber As Int32 = 0
Private _allowPaging As Boolean = True
Private _pageCount As Int32 = 0
Private pageSource As PagedDataSource 'Provides the paging functionality.
'An event that is raised each time the page is changed.
Public Event PageIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs)
Public Property DataSource() As IEnumerable
Get
Return _dataSource
End Get
Set(ByVal value As IEnumerable)
_dataSource = value
End Set
End Property
Public Property PageSize() As Int32
Get
Return _pageSize
End Get
Set(ByVal value As Int32)
_pageSize = value
'Store the _pageSize value in ViewState
ViewState("PageSize") = _pageSize
End Set
End Property
Public Property PageNumber() As Int32
Get
Return _pageNumber
End Get
Set(ByVal value As Int32)
_pageNumber = value
'Store the _pageNumber value in ViewState
ViewState("PageNumber") = _pageNumber
End Set
End Property
Public ReadOnly Property PageCount() As Int32
Get
Return _pageCount
End Get
End Property
Public Property AllowPaging() As Boolean
Get
Return _allowPaging
End Get
Set(ByVal value As Boolean)
_allowPaging = value
'Store the _allowPaging value in ViewState
ViewState("AllowPaging") = _allowPaging
End Set
End Property
Private Sub Paging()
'Create a new instance of the pageSource if required
If pageSource Is Nothing Then pageSource = New PagedDataSource
'Setup the pageSource
With pageSource
.DataSource = _dataSource
.PageSize = _pageSize
.AllowPaging = _allowPaging
If _pageNumber > .PageCount Then
Throw New ArgumentOutOfRangeException
Else
.CurrentPageIndex = _pageNumber
End If
'Display page information
lblPageInformation.Text = "Page " & (.CurrentPageIndex + 1).ToString & " of " & .PageCount.ToString
'Enable/Disable the navigation buttons based on where we are in the paging source.
lkbMoveFirst.Enabled = Not .IsFirstPage
lkbMovePrevious.Enabled = Not .IsFirstPage
lkbMoveNext.Enabled = Not .IsLastPage
lkbMoveLast.Enabled = Not .IsLastPage
'Store the current page count in ViewState
ViewState("PageCount") = .PageCount - 1
End With
End Sub
Private Sub Navigation_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles _
lkbMoveFirst.Click, lkbMovePrevious.Click, lkbMoveNext.Click, lkbMoveLast.Click
'Retrieve instance data from ViewState if available
If ViewState("PageSize") IsNot Nothing Then _pageSize = Int32.Parse(ViewState("PageSize").ToString)
If ViewState("PageNumber") IsNot Nothing Then _pageNumber = Int32.Parse(ViewState("PageNumber").ToString)
If ViewState("PageCount") IsNot Nothing Then _pageCount = Int32.Parse(ViewState("PageCount").ToString)
If ViewState("AllowPaging") IsNot Nothing Then _allowPaging = Boolean.Parse(ViewState("AllowPaging").ToString)
'Determine which control initiated the Navigation_Click
Select Case CType(sender, LinkButton).ID.ToLower
Case "lkbmovefirst"
_pageNumber = 0
Case "lkbmoveprevious"
_pageNumber -= 1
Case "lkbmovenext"
_pageNumber += 1
Case "lkbmovelast"
_pageNumber = pageCount
End Select
'Update the ViewState values
ViewState("PageNumber") = _pageNumber
'Raise the PageIndexedChanged event
RaiseEvent PageIndexChanged(Me, Nothing)
End Sub
Public Function GetPage() As IEnumerable
Paging()
Return pageSource
End Function
End Class
Now to test the control.
Add a new web form and drop the paging control onto the page. You may need to wire up the paging control to your code-behind file using:
Protected WithEvents Paging1 As paging
This first test will show how you can page through data from a database using the Paging control and display that data in a ListBox. I have used the Northwind database in SQL Server for this example. Add the following code to your code-behind file:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim connection As New System.Data.SqlClient.SqlConnection("Data Source=(local);Initial Catalog=Northwind;UID=xxx;Password=xxx")
Dim adapter As New System.Data.SqlClient.SqlDataAdapter("SELECT * FROM Customers", connection)
Dim dt As New System.Data.DataTable
adapter.Fill(dt)
'You can set your desired page size here or accept the default of 10 records.
Me.Paging1.DataSource = dt.DefaultView
ListBox1.Items.Clear()
'Populate with first n items (n = page size)
If Not Page.IsPostBack Then
ListBox1.DataSource = Paging1.GetPage
ListBox1.DataTextField = "ContactName"
ListBox1.DataValueField = "CustomerID"
ListBox1.DataBind()
End If
End Sub
Protected Sub Paging1_PageIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Paging1.PageIndexChanged
ListBox1.DataSource = Paging1.GetPage
ListBox1.DataTextField = "ContactName"
ListBox1.DataValueField = "CustomerID"
ListBox1.DataBind()
End Sub
In the above example, you will see that the DataSource of the paging control is set on Page Load and if the page is not a post back then the DataSource of the ListBox is set to the GetPage method of the paging control which returns the IEnumerable interface. Each time the page is changed, the PageIndexChanged event is raised and again, the DataSource of the ListBox is set to the GetPage method of the paging control.
The next example shows how you can perform the same paging but using an ArrayList instead of a DataTable. The same concept applies as in the first example:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim myData As New ArrayList
For i As Int32 = 1 To 100
myData.Add(i)
Next
Paging1.DataSource = myData
If Not Page.IsPostBack Then
ListBox1.DataSource = Paging1.GetPage
ListBox1.DataBind()
End If
End Sub
Protected Sub Paging1_PageIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Paging1.PageIndexChanged
ListBox1.DataSource = Paging1.GetPage
ListBox1.DataBind()
End Sub
In the above examples, the data has been bound to a ListBox, but as mentioned earlier, you can use the Paging control to bind to any control that supports the IEnumerable interface. Another thing to bear in mind is that you could also request an enumerator from the GetPage method and enumerate the page's data programatically without binding the Paging control to a web control. For example, if the Paging controls DataSource was set to a DataView then the following example, would enumerate through the current page data:
Dim myEnumerator As IEnumerator = Paging1.GetPage.GetEnumerator
Dim dv As Data.DataRowView = Nothing
Do While myEnumerator.MoveNext = True
dv = CType(myEnumerator.Current, Data.DataRowView)
Loop
I hope this proves useful to somebody.