Introduction
Server-side paging of large resultsets is a common (and important) task for web developers. Combining it with the use of data controls, one needs to provide some form of paging UI. Simple forward and back buttons sometimes suffice, but where large numbers of pages are expected, they don't really provide a compelling interface. Setting up other forms of paging in this scenario can be time-consuming, whether you use the PagedDataSource
class or create some custom method. And, I have frequently found that the design constraints of a particular site will require a laborious rewrite of my paging class.
There are a couple of other paging controls here on CodeProject to look at here and here. The paging control I've written differs (primarily) in that it's templated to allow for easy implementation of any flavor of paging UI. It responds to an ItemCommand
event if you wish to use buttons of any sort, or works equally well (for the more SEO conscious) with querystring links.
The attached zip contains the compiled DLL, the full control source, and a couple of example implementations on a WebForm.
Background
This is the first templated control I've developed. I found there was a lot to be "discovered" in trying to do so. I won't go into all the details here, but the following resources proved to be useful:
There are lots of resources on server-side paging available, depending on what sort of database you use. It's pretty straightforward in Oracle and SQL Server 2005. This article provides a good look at methods available for SQL Server 2000.
Using the TemplatedPager
The easiest way to try out the control is to right click your VS2005 Toolbox, select Choose Items, browse to your unzip location for the attached file, and select TemplatedPager.dll. Then, drag an instance of the control from your toolbox onto your WebForm.
Templates
There are 8 templates available. All templates have access to the container page number property.
<%#Container.PageNumber%>
and the page count property.
<%#Container.PageCount%>
Note, in the separator template, these properties will not return anything as this template is not data-bound.
The HeaderTemplate
will always return the current page via Container.PageNumber
.
<HeaderTemplate>
Page <%#Container.PageNumber%> of <%#Container.PageCount%>:
</HeaderTemplate>
FirstPageTemplate
and the LastPageTemplate
allow you to specify what any "go to first/last page" element should look and behave like. Container.PageNumber
in this template is always 1 in the case of FirstPage
, and the last page number in the case of LastPage
. If this template is not included and the control's ShowFirst
/ShowLast
properties are set to true, this information will be rendered via the standard PageTemplate
.
The PreviousPageTemplate
and NextPageTemplate
are pretty obvious. Unlike the first and last page templates, however, they are not rendered via the PageTemplate
if their corresponding template does not exist in the control. Note, the ShowNext
and ShowPrevious
properties should be set to true for these templates to be rendered.
The CurrentPageTemplate
allows the currently selected page to be styled differently than other pages, which will use the PageTemplate
.
<CurrentPageTemplate>
<strong><%#Container.PageNumber%></strong>
</CurrentPageTemplate>
<PageTemplate>
<asp:LinkButton ID="lP" CommandName="Page"
CommandArgument="<%#Container.PageNumber%>"
runat="server"><%#Container.PageNumber%></asp:LinkButton>
</PageTemplate>
Finally, the SeparatorTemplate
allows you to specify HTML that should be rendered between each page number. Note, it is applied only between CurrentPageTemplate
and the PageTemplate
instances.
Properties
These can be set at design-time and/or run-time:
MaxPageNumbers
How many page numbers to show.PageCount
You can either set this, or the PagedItemCount
property. If you set this property, it will be used instead of PagedItemCount
, regardless of what the value of PagedItemCount
is.PagedItemCount
To have the page count calculated for you, set this property with the total number of items to be paged. Also, be sure to set the PageSize
property to get an accurate page count. Calculation is CType(Math.Ceiling(PagedItemCount / PageSize), Integer)
.PageNumber
The current page number. Cannot be less than 1.PageSize
Number of items per page.ShowFirst
, ShowLast
, ShowPrevious
, ShowNext
Booleans indicating whether to bind the associated templates.
Events
The ItemCommand
event allows you to put button controls in your templates and respond to the events raised. For example:
Protected Sub TemplatedPager1_ItemCommand(ByVal sender As Object, _
ByVal e As Bxi.Web.UI.WebControls.PagerItemCommandEventArgs) _
Handles TemplatedPager1.ItemCommand
If e.CommandName = "Page" Then
TemplatedPager1.PageNumber = e.CommandArgument
End If
End Sub
Note: There is no need to call "DataBind
" on this control. This is handled within the control by a requiresBind
flag that gets set whenever control properties are altered.
Points of Interest
There are a number of "bumps in the road" when you're developing a (templated) control. Here are a few and the solutions I've discovered:
- I initially inherited from
WebControl
. It has a lot of unnecessary inherited properties for this control, so I altered to inherit from Control
. Now, when I tried to add templates in the control, I was seeing the full list of server controls, rather than just my templates. This was resolved by adding the attributes ParseChildren(True)
and PersistChildren(False)
. There's an informative blog about why this is necessary, heree. - Inheriting from
Control
also resulted in no longer being able to access the container properties (e.g., Container.PageNumber
). A lot of trial and error resulted in discovering that one needs to call the DataBind
method on the template class (in my case, PagerItem
) after adding it to the Controls
collection, which interestingly is not necessary when inheriting from WebControl
. - It's silly, but I like to have a nice icon associated with a control, using the
ToolboxBitmap
attribute (to save you a few minutes, it's in System.Drawing
). This actually took longer to accomplish than writing most of the control logic. Bob Powell has a good article here, which absolutely didn't work, but got me pointed in the right direction. If you follow all those instructions and can't get your icon to show, open your .dll in Reflector, look in the resources, and find your .bmp image (you should have set the build action on it to Embedded Resource). If it doesn't have a name that includes the root namespace (if there is one), the namespace, and any subdirectories it may reside in, in VS, rename the .bmp file to include all of these. In my case, I changed the file name to Bxi.Web.UI.WebControls.Resources.TemplatedPager.bmp and, bingo. - Be aware of your root namespace. I prefer not to use one, and explicitly type the full namespaces out in the classes. It's easy to forget about it and then have issues with associating your control designer or your assembly
TagPrefix
, for example.
Improvements
I'd love to hear any feedback and suggested improvements, particularly if you have any advice/comments/suggestions on templated control development.
History
- Version 1: August 28, 2007.