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.
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.
There are 8 templates available. All templates have access to the container page number property.
and the page count property.
Note, in the separator template, these properties will not return anything as this template is not data-bound.
HeaderTemplate will always return the current page via
Page <%#Container.PageNumber%> of <%#Container.PageCount%>:
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
ShowLast properties are set to true, this information will be rendered via the standard
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
ShowPrevious properties should be set to true for these templates to be rendered.
CurrentPageTemplate allows the currently selected page to be styled differently than other pages, which will use the
SeparatorTemplate allows you to specify HTML that should be rendered between each page number. Note, it is applied only between
CurrentPageTemplate and the
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 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.
ShowNext Booleans indicating whether to bind the associated templates.
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) _
If e.CommandName = "Page" Then
TemplatedPager1.PageNumber = e.CommandArgument
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
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
- 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.
I'd love to hear any feedback and suggested improvements, particularly if you have any advice/comments/suggestions on templated control development.
- Version 1: August 28, 2007.