Introduction
ASP.NET 3.5 contains a very useful component - the DataPager
control. This control allows you to page contents of any control implementing the IPageableItemContainer
interface - which is so far only the universal ListView
control, also part of ASP.NET 3.5.
ASP.NET 4.0 adds another cool feature to web forms: URL routing, first seen in ASP.NET MVC. The routing engine allows you to create canonical and user-friendly URLs easier than hacking URL rewriting. You can read more about URL routing in this blog post by Scott Guthrie.
The problem is that these two features do not work well together, and the DataPager
control does have several other limitations.
- In the current ASP.NET 4.0 beta 2, the
DataPager
does not work at all when the QueryStringField
property is specified. This is a known bug and will be fixed in the final release (bug #515348). But for early adopters, this is currently a problem.
- The
DataPager
control supports only query string or JavaScript postback for paging. You can't have the current page index specified in the route.
DataPager
renders its own UI, which can be customized, but not easily, especially with regard to CSS styling.
Solution
My solution is to write my own data paging control. This control uses the same IPageableItemContainer
infrastructure like DataPager
does, but with a different UI generation philosophy.
Instead of rendering its own interface, the control uses the standard HyperLink
controls, sets their NavigateUrl
property, and based on the configuration, can hide or disable them if they are not needed.
This control works as a control extender, extending any IPageableItemContainer
(in short: ListView
) control.
Configuration properties of the DataPagerExtender control
TargetControlID
- ID of the control that should be paged.
PageSize
- number of items on a single page; defaults to 10.
PageIndexSource
- where to get the current page index from. Can be either RouteParameter
(default) or QueryString
.
CollectionKey
- sets the name of the query string field or route parameter (see above) to get the current page index from.
InactiveLinkMode
- specifies how to behave when a link is to be inactive (i.e., "previous" link on first page). The default value is Ignore
, in which case the link is simply ignored and left unchanged. When set to Hide
, the particular link control is hidden (Visible=false
), and when set to Disable
, the control is disabled (Enabled=false
).
FirstLinkID
, PreviousLinkID
, NextLinkID
, LastLinkID
- these properties specify the IDs of HyperLink
controls used for navigation. If you leave a property blank, it is ignored. Thus, if you want only the "previous" and "next" links, only specify PreviousLinkID
and NextLinkID
.
Using the DataPagerExtender control
First of all, display your data in a ListView
control the usual way. Then, create four HyperLink
controls for navigating to the first, previous, next, and last page (marked in bold in the code below):
<asp:ListView ID="ListView1" runat="server" DataSourceID="LinqDataSource1">
<LayoutTemplate>
<table>
<thead>
<tr>
<th>ProductID</th>
<th>ProductName</th>
<th>UnitPrice</th>
</tr>
</thead>
<tbody>
<asp:PlaceHolder ID="itemPlaceholder" runat="server" />
</tbody>
<tfoot>
<tr>
<td colspan="3">
<div style="float: left">
<asp:HyperLink ID="LinkFirst" runat="server" Text="<< first" />
<asp:HyperLink ID="LinkPrevious" runat="server" Text="< previous" />
</div>
<div style="float: right">
<asp:HyperLink ID="LinkNext" runat="server" Text="next >" />
<asp:HyperLink ID="LinkLast" runat="server" Text="last >>" />
</div>
</td>
</tr>
</tfoot>
</LayoutTemplate>
<ItemTemplate>
<tr>
<td>
<asp:Label runat="server" Text='<%# Eval("ProductID") %>' />
</td>
<td>
<asp:Label runat="server" Text='<%# Eval("ProductName") %>' />
</td>
<td>
<asp:Label runat="server" Text='<%# Eval("UnitPrice") %>' />
</td>
</tr>
</ItemTemplate>
</asp:ListView>
<asp:LinqDataSource ID="LinqDataSource1" runat="server" TableName="Products"
ContextTypeName="NorthwindDataContext" OrderBy="ProductName"
Select="new (ProductID, ProductName, UnitPrice)" />
Then, add the DataPagerExtender
control (in my case, I registered it on the my
prefix, and I'm using the query string):
<my:DataPagerExtender ID="DPE1" runat="server" TargetControlID="ListView1"
PageIndexSource="QueryString" CollectionKey="Page"
FirstLinkID="LinkFirst" LastLinkID="LinkLast"
NextLinkID="LinkNext" PreviousLinkID="LinkPrevious" />
Using the DataPagerExtender with routing
First of all, you must slightly modify the control reference above:
<my:DataPagerExtender ID="DPE1" runat="server" TargetControlID="ListView1"
PageIndexSource="RouteParameter" CollectionKey="Page"
FirstLinkID="LinkFirst" LastLinkID="LinkLast"
NextLinkID="LinkNext" PreviousLinkID="LinkPrevious" />
Then, you must create some route, which contains the {Page}
route parameter (and possibly some others). It's recommended that this parameter would default to "1"
, so the first page is shown when none is explicitly specified:
void Application_Start(object sender, EventArgs e) {
RouteTable.Routes.MapPageRoute("PagedProducts", "pagedproducts/{Page}", "~/Products2.aspx", false, new RouteValueDictionary { { "Page", "1" } }); }
Limitations
Compared to the original DataPager
control, my DataPagerExtender
has several limitations:
- It does not support postback for paging.
- Only the Next/Previous etc., pager style is supported, not a list of page numbers with direct jump capability.
- There is no declarative way to display paging information (e.g., "showing items 1-10 from 100, page 1 of 10").