Introduction
When displaying large sets of data, it is sometimes necessary to split data into pages. In this article, I will show how to create a number-based paging by using the XSL Transformation style sheet. For this example, I will take data from the Northwind
database in XML format. Below is an excerpt from the data file.
<Northwind>
<Products ProductName="Chai">
<ProductID>1</ProductID>
<SupplierID>1</SupplierID>
<CategoryID>1</CategoryID>
<QuantityPerUnit>10 boxes x 20 bags</QuantityPerUnit>
<UnitPrice>18.0000</UnitPrice>
<UnitsInStock>39</UnitsInStock>
<UnitsOnOrder>0</UnitsOnOrder>
<ReorderLevel>10</ReorderLevel>
<Discontinued>false</Discontinued>
</Products>
</Northwind>
The root element is <Northwind>
and the details are stored within the number of <Products>
.
Creating Number-Based Paging
The page buttons are created as links with parameters where page numbers are passed as a part of the URL, i.e., default.aspx?page=5. One way to create such links in code is described here. But the goal of the current article is to create those links using pure XSL transformation. The transformation is applied within the System.Web.UI.WebControls.Xml
control, which is created in the default.aspx as:
<asp:Xml id="Xml1" runat="server"
DocumentSource="northwind_products.xml"
TransformSource="default.xslt"></asp:Xml>
The XML source is loaded from the northwind_products.xml, and the XSL Transformation style sheet is loaded from the default.xslt. Before we proceed to the transformation style sheet, I need to point out the following code in the Page_Load
handler. This code takes the ?page=
parameter, creates XsltArgumentList
and passes CurrentPage
parameter to the transformation.
double CurrentPage = 1D;
if(Request.Params["page"] != null)
CurrentPage = Double.Parse(Request.Params["page"]);
XsltArgumentList xal = new XsltArgumentList();
xal.AddParam("CurrentPage", String.Empty, CurrentPage);
Xml1.TransformArgumentList = xal;
The CurrentPage
parameter is required to display the correct page of data and to create the appropriate links to other pages. Other optional parameters of the transformation style sheet are PageSize
and MaxPages
. PageSize
is the number of items displayed per page. MaxPages
defines the maximum number of page links.
The button for a current page is not displayed as a link. For example, if the CurrentPage=50
and MaxPages=5
, then we will get the following result: "First ... [48] [49] [50] [51] [52] ... Last
", where "[50]
" is a button without a link. The XSL template, which renders the buttons is provided below:
<xsl:template name="Pages">
<xsl:variable name="TotalItems" select="count(Products)" />
<xsl:variable name="Pages" select="ceiling($TotalItems div $PageSize)" />
<xsl:for-each select="Products[((position()-1) mod $PageSize = 0)]">
<xsl:choose>
<xsl:when test="(position() > ($CurrentPage - ceiling($MaxPages div 2)) or
position() > (last() - $MaxPages)) and
((position() < $CurrentPage + $MaxPages div 2) or
(position() < 1 + $MaxPages))">
<xsl:if test="position()=$CurrentPage">
[ <xsl:value-of select="position()"/> ]
</xsl:if>
</xsl:choose>
</xsl:for-each>
</xsl:template>
The interesting part of this code is where the appropriate nodes are selected. The first step is to select one node of each page - the related XPath expression is Products[((position()-1) mod $PageSize = 0)]
. The resulting set will contain as many elements as there are pages. The second step is to pick only $MaxPages
of them, which surround the $CurrentPage
. Finally, the result is rendered using the position()
statement. To reduce the size of this example, I have excluded part of the code which creates links, but you can get the idea about how it is done from the <xsl:value-of select="position()"/>
statement.
Filtering the Data According to Page Number
Template
, which displays the table is rather simple:
<xsl:template match="Products">
<xsl:choose>
<xsl:when test="(position() >= 1 + ($CurrentPage - 1) * $PageSize) and
(position() < (1 + $CurrentPage * $PageSize))">
<tr>
<td><xsl:value-of select="position()"/></td>
<td><xsl:value-of select="@ProductName"/></td>
<td><xsl:value-of select="format-number(UnitPrice,'$#.00')"/></td>
<td align="center"><xsl:value-of select="UnitsInStock"/></td>
</tr>
</xsl:when>
</xsl:choose>
</xsl:template>
The XPath
expression which selects data of the appropriate page is highlighted in bold. Note that product name is stored as an attribute, which means that we will use @ProductName
statement to select it.
Points of Interest
No doubt that there exists a simpler way of creating page buttons, consider DataGrid
for example. But think of portability. In this article, I have used C# only to pass an argument to XSLT parser, but XSLT parsers exist in various programming languages and platforms. Hopefully, my code will be useful to those, who are concerned about compatibility and portability. Also it is possible to create a letter-based paging with XSLT. If anyone has a practical need for it, please drop me a line. Thank you for your interest and remember that your opinions are highly appreciated.
License
This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.
A list of licenses authors might use can be found here.