Sorting Data in a DataList or Repeater Control (VB)
DOWNLOAD ASSETS: Sample App or PDF
In this tutorial we'll examine how to include sorting support in the DataList and Repeater, as well as how to construct a DataList or Repeater whose data can be paged and sorted.
Introduction
In the previous tutorial we examined how to add paging support
to a DataList. We created a new method in the
ProductsBLL class
(GetProductsAsPagedDataSource) that returned a
PagedDataSource object. When bound to a DataList or
Repeater, the DataList or Repeater would display just the
requested page of data. This technique is similar to what is used
internally by the GridView, DetailsView, and FormView controls to
provide their built-in default paging functionality.
In addition to offering paging support, the GridView also includes out of the box sorting support. Neither the DataList nor Repeater provides built-in sorting functionality; however, sorting features can be added with a bit of code. In this tutorial we ll examine how to include sorting support in the DataList and Repeater, as well as how to construct a DataList or Repeater whose data can be paged and sorted.
A Review of Sorting
As we saw in the Paging and Sorting Report Data
tutorial, the GridView control provides out of the box sorting
support. Each GridView field can have an associated
SortExpression, which indicates the data field by
which to sort the data. When the GridView s
AllowSorting property is set to true,
each GridView field that has a SortExpression
property value has its header rendered as a LinkButton. When a
user clicks a particular GridView field s header, a
postback occurs and the data is sorted according to the clicked
field s SortExpression.
The GridView control has a SortExpression
property as well, which stores the SortExpression of
the GridView field the data is sorted by. Additionally, a
SortDirection property indicates whether the data is
to be sorted in ascending or descending order (if a user clicks a
particular GridView field s header link twice in
succession, the sort order is toggled).
When the GridView is bound to its data source control, it
hands off its SortExpression and
SortDirection properties to the data source control.
The data source control retrieves the data and then sorts it
according to the supplied SortExpression and
SortDirection properties. After sorting the data,
the data source control returns it to the GridView.
To replicate this functionality with the DataList or Repeater controls, we must:
- Create a sorting interface
- Remember the data field to sort by and whether to sort in ascending or descending order
- Instruct the ObjectDataSource to sort the data by a particular data field
We ll tackle these three tasks in steps 3 and 4. Following that, we ll examine how to include both paging and sorting support in a DataList or Repeater.
Step 2: Displaying the Products in a Repeater
Before we worry about implementing any of the sorting-related
functionality, let s start by listing the products in a
Repeater control. Start by opening the Sorting.aspx
page in the PagingSortingDataListRepeater folder.
Add a Repeater control to the web page, setting its
ID property to SortableProducts. From
the Repeater s smart tag, create a new ObjectDataSource
named ProductsDataSource and configure it to
retrieve data from the ProductsBLL class s
GetProducts() method. Select the
(None) option from the drop-down lists in the
INSERT, UPDATE, and DELETE tabs.
Figure 1: Create an ObjectDataSource and Configure it to Use the GetProductsAsPagedDataSource() Method (Click to view full-size image)
Figure 2: Set the Drop-Down Lists in the UPDATE, INSERT, and DELETE tabs to (None) (Click to view full-size image)
Unlike with the DataList, Visual Studio does not automatically
create an ItemTemplate for the Repeater control
after binding it to a data source. Furthermore, we must add this
ItemTemplate declaratively, as the Repeater
control s smart tag lacks the Edit Templates
option found in the DataList s. Let s use the same
ItemTemplate from the previous tutorial, which
displayed the product s name, supplier, and category.
After adding the ItemTemplate, the Repeater and
ObjectDataSource s declarative markup should look similar
to the following:
<asp:Repeater ID="SortableProducts" DataSourceID="ProductsDataSource"
EnableViewState="False" runat="server">
<ItemTemplate>
<h4><asp:Label ID="ProductNameLabel" runat="server"
Text='<%# Eval("ProductName") %>'></asp:Label></h4>
Category:
<asp:Label ID="CategoryNameLabel" runat="server"
Text='<%# Eval("CategoryName") %>'></asp:Label><br />
Supplier:
<asp:Label ID="SupplierNameLabel" runat="server"
Text='<%# Eval("SupplierName") %>'></asp:Label><br />
<br />
<br />
</ItemTemplate>
</asp:Repeater>
<asp:ObjectDataSource ID="ProductsDataSource" runat="server"
OldValuesParameterFormatString="original_{0}" TypeName="ProductsBLL"
SelectMethod="GetProducts">
</asp:ObjectDataSource>
Figure 3 shows this page when viewed through a browser.
Figure 3: Each Product s Name, Supplier, and Category is Displayed (Click to view full-size image)
Step 3: Instructing the ObjectDataSource to Sort the Data
To sort the data displayed in the Repeater, we need to inform
the ObjectDataSource of the sort expression by which the data
should be sorted. Before the ObjectDataSource retrieves its data,
it first fires its Selecting event, which
provides an opportunity for us to specify a sort expression. The
Selecting event handler is passed an object of type
ObjectDataSourceSelectingEventArgs, which has
a property named Arguments of type
DataSourceSelectArguments. The
DataSourceSelectArguments class is designed to pass
data-related requests from a consumer of data to the data source
control, and includes a SortExpression
property.
To pass sorting information from the ASP.NET page to the
ObjectDataSource, create an event handler for the
Selecting event and use the following code:
Protected Sub ProductsDataSource_Selecting(ByVal sender As Object, _
ByVal e As ObjectDataSourceSelectingEventArgs) _
Handles ProductsDataSource.Selecting
e.Arguments.SortExpression = sortExpression
End Sub
The sortExpression value should be assigned the name of the data field to sort the data by (such as ProductName ). There is no sort direction-related property, so if you want to sort the data in descending order, append the string DESC to the sortExpression value (such as ProductName DESC ).
Go ahead and try some different hard-coded values for sortExpression and test the results in a browser. As Figure 4 shows, when using ProductName DESC as the sortExpression, the products are sorted by their name in reverse alphabetical order.
Figure 4: The Products are Sorted by their Name in Reverse Alphabetical Order (Click to view full-size image)
Step 4: Creating the Sorting Interface and Remembering the Sort Expression and Direction
Turning on sorting support in the GridView converts each sortable field s header text into a LinkButton that, when clicked, sorts the data accordingly. Such a sorting interface makes sense for the GridView, where its data is neatly laid out in columns. For the DataList and Repeater controls, however, a different sorting interface is needed. A common sorting interface for a list of data (as opposed to a grid of data), is a drop-down list that provides the fields by which the data can be sorted. Let s implement such an interface for this tutorial.
Add a DropDownList Web control above the
SortableProducts Repeater and set its
ID property to SortBy. From the
Properties window, click the ellipses in the Items
property to bring up the ListItem Collection Editor. Add
ListItems to sort the data by the
ProductName, CategoryName, and
SupplierName fields. Also add a
ListItem to sort the products by their name in
reverse alphabetical order.
The ListItem Text properties can be
set to any value (such as Name ), but the
Value properties must be set to the name of the data
field (such as ProductName ). To sort the results in
descending order, append the string DESC to the
data field name, like ProductName DESC .

Figure 5: Add a ListItem for Each of the Sortable Data Fields
Finally, add a Button Web control to the right of the
DropDownList. Set its ID to
RefreshRepeater and its Text property
to Refresh .
After creating the ListItems and adding the
Refresh button, the DropDownList and Button s declarative
syntax should look similar to the following:
<asp:DropDownList ID="SortBy" runat="server">
<asp:ListItem Value="ProductName">Name</asp:ListItem>
<asp:ListItem Value="ProductName DESC">Name (Reverse Order)
</asp:ListItem>
<asp:ListItem Value="CategoryName">Category</asp:ListItem>
<asp:ListItem Value="SupplierName">Supplier</asp:ListItem>
</asp:DropDownList>
<asp:Button runat="server" ID="RefreshRepeater" Text="Refresh" />
With the sorting DropDownList complete, we next need to update
the ObjectDataSource s Selecting event handler
so that it uses the selected SortBy
ListItem s Value property as
opposed to a hard-coded sort expression.
Protected Sub ProductsDataSource_Selecting _
(ByVal sender As Object, ByVal e As ObjectDataSourceSelectingEventArgs) _
Handles ProductsDataSource.Selecting
' Have the ObjectDataSource sort the results by the
' selected sort expression
e.Arguments.SortExpression = SortBy.SelectedValue
End Sub
At this point when first visiting the page the products will
initially be sorted by the ProductName data field,
as it s the SortBy ListItem
selected by default (see Figure 6). Selecting a different sorting
option such as Category and
clicking Refresh will cause a postback and re-sort the data by
the category name, as Figure 7 shows.
Figure 6: The Products are Initially Sorted by their Name (Click to view full-size image)
Figure 7: The Products are Now Sorted by Category (Click to view full-size image)
Note: Clicking the Refresh button causes the data to
automatically be re-sorted because the Repeater s view
state has been disabled, thereby causing the Repeater to rebind
to its data source on every postback. If you ve left the
Repeater s view state enabled, changing the sorting
drop-down list won t have any affect on the sort order. To
remedy this, create an event handler for the Refresh
Button s Click event and rebind the Repeater
to its data source (by calling the Repeater s
DataBind() method).
Remembering the Sort Expression and Direction
When creating a sortable DataList or Repeater on a page where non-sort related postbacks may occur, it s imperative that the sort expression and direction be remembered across postbacks. For example, imagine that we updated the Repeater in this tutorial to include a Delete button with each product. When the user clicks the Delete button we d run some code to delete the selected product and then rebind the data to the Repeater. If the sort details are not persisted across postback, the data displayed on screen will revert to the original sort order.
For this tutorial, the DropDownList implicitly saves the sort expression and direction in its view state for us. If we were using a different sorting interface one with, say, LinkButtons that provided the various sorting options we d need to take care to remember the sort order across postbacks. This could be accomplished by storing the sorting parameters in the page s view state, by including the sort parameter in the querystring, or through some other state persistence technique.
Future examples in this tutorial explore how to persist the sorting details in the page s view state.
Step 5: Adding Sorting Support to a DataList that Uses Default Paging
In the preceding tutorial we examined how to implement default
paging with a DataList. Let s extend this previous example
to include the ability to sort the paged data. Start by opening
the SortingWithDefaultPaging.aspx and
Paging.aspx pages in the
PagingSortingDataListRepeater folder. From the
Paging.aspx page, click on the Source button to view
the page s declarative markup. Copy the selected text (see
Figure 8) and paste it into the declarative markup of
SortingWithDefaultPaging.aspx between the
<asp:Content> tags.
Figure 8: Replicate the Declarative Markup in the <asp:Content> Tags from Paging.aspx to SortingWithDefaultPaging.aspx (Click to view full-size image)
After copying the declarative markup, copy the methods and
properties in the Paging.aspx page s
code-behind class to the code-behind class for
SortingWithDefaultPaging.aspx. Next, take a moment
to view the SortingWithDefaultPaging.aspx page in a
browser. It should exhibit the same functionality and appearance
as Paging.aspx.
Enhancing ProductsBLL to Include a Default Paging and Sorting Method
In the previous tutorial we created a
GetProductsAsPagedDataSource(pageIndex,
pageSize) method in the ProductsBLL
class that returned a PagedDataSource object. This
PagedDataSource object was populated with all
of the products (via the BLL s GetProducts()
method), but when bound to the DataList only those records
corresponding to the specified pageIndex and
pageSize input parameters were displayed.
Earlier in this tutorial we added sorting support by
specifying the sort expression from the ObjectDataSource s
Selecting event handler. This works well when the
ObjectDataSource is returned an object that can be sorted, like
the ProductsDataTable returned by the
GetProducts() method. However, the
PagedDataSource object returned by the
GetProductsAsPagedDataSource method does not support
sorting of its inner data source. Instead, we need to sort the
results returned from the GetProducts() method
before we put it in the PagedDataSource.
To accomplish this, create a new method in the
ProductsBLL class,
GetProductsSortedAsPagedDataSource(sortExpression,
pageIndex, pageSize). To sort the
ProductsDataTable returned by the
GetProducts() method, specify the Sort
property of its default DataTableView:
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Select, False)> _
Public Function GetProductsSortedAsPagedDataSource _
(sortExpression As String, pageIndex As Integer, pageSize As Integer) _
As PagedDataSource
' Get ALL of the products
Dim products As Northwind.ProductsDataTable = GetProducts()
'Sort the products
products.DefaultView.Sort = sortExpression
' Limit the results through a PagedDataSource
Dim pagedData As New PagedDataSource()
pagedData.DataSource = products.DefaultView
pagedData.AllowPaging = True
pagedData.CurrentPageIndex = pageIndex
pagedData.PageSize = pageSize
Return pagedData
End Function
The GetProductsSortedAsPagedDataSource method
differs only slightly from the
GetProductsAsPagedDataSource method created in the
previous tutorial. In particular,
GetProductsSortedAsPagedDataSource accepts an
additional input parameter
sortExpression and assigns this value to
the Sort property of the
ProductDataTable s DefaultView. A
few lines of code later, the PagedDataSource
object s DataSource is assigned the
ProductDataTable s
DefaultView.
Calling the GetProductsSortedAsPagedDataSource Method and Specifying the Value for the SortExpression Input Parameter
With the GetProductsSortedAsPagedDataSource
method complete, the next step is to provide the value for this
parameter. The ObjectDataSource in
SortingWithDefaultPaging.aspx is currently
configured to call the GetProductsAsPagedDataSource
method and passes in the two input parameters through its two
QueryStringParameters, which are specified in the
SelectParameters collection. These two
QueryStringParameters indicate that the source for
the GetProductsAsPagedDataSource method s
pageIndex and pageSize parameters come from the
querystring fields pageIndex and
pageSize.
Update the ObjectDataSource s SelectMethod
property so that it invokes the new
GetProductsSortedAsPagedDataSource method. Then, add
a new QueryStringParameter so that the
sortExpression input parameter is accessed from the
querystring field sortExpression. Set the
QueryStringParameter s
DefaultValue to ProductName .
After these changes, the ObjectDataSource s declarative markup should look like:
<asp:ObjectDataSource ID="ProductsDefaultPagingDataSource"
OldValuesParameterFormatString="original_{0}" TypeName="ProductsBLL"
SelectMethod="GetProductsSortedAsPagedDataSource"
OnSelected="ProductsDefaultPagingDataSource_Selected" runat="server">
<SelectParameters>
<asp:QueryStringParameter DefaultValue="ProductName"
Name="sortExpression" QueryStringField="sortExpression"
Type="String" />
<asp:QueryStringParameter DefaultValue="0" Name="pageIndex"
QueryStringField="pageIndex" Type="Int32" />
<asp:QueryStringParameter DefaultValue="4" Name="pageSize"
QueryStringField="pageSize" Type="Int32" />
</SelectParameters>
</asp:ObjectDataSource>
At this point, the SortingWithDefaultPaging.aspx
page will sort its results alphabetically by the product name
(see Figure 9). This is because, by default, a value of
ProductName is passed in as the
GetProductsSortedAsPagedDataSource method s
sortExpression parameter.
Figure 9: By Default, the Results are Sorted by ProductName (Click to view full-size image)
If you manually add a sortExpression querystring
field such as
SortingWithDefaultPaging.aspx?sortExpression=CategoryName
the results will be sorted by the specified
sortExpression. However, this
sortExpression parameter is not included in the
querystring when moving to a different page of data. In fact,
clicking on the Next or Last page buttons takes us back to
Paging.aspx! Furthermore, there s currently no
sorting interface. The only way a user can change the sort order
of the paged data is by manipulating the querystring
directly.
Creating the Sorting Interface
We first need to update the RedirectUser method
to send the user to SortingWithDefaultPaging.aspx
(instead of Paging.aspx) and to include the
sortExpression value in the querystring. We should
also add a read-only, page-level named
SortExpression property. This property, similar to
the PageIndex and PageSize properties
created in the previous tutorial, returns the value of the
sortExpression querystring field if it exists, and
the default value ( ProductName ) otherwise.
Currently the RedirectUser method accepts only a
single input parameter the index of the page to
display. However, there may be times when we want to redirect the
user to a particular page of data using a sort expression other
than what s specified in the querystring. In a moment
we ll create the sorting interface for this page, which
will include a series of Button Web controls for sorting the data
by a specified column. When one of those Buttons is clicked, we
want to redirect the user passing in the appropriate sort
expression value. To provide this functionality, create two
versions of the RedirectUser method. The first one
should accept just the page index to display, while the second
one accepts the page index and sort expression.
Private ReadOnly Property SortExpression() As String
Get
If Not String.IsNullOrEmpty(Request.QueryString("sortExpression")) Then
Return Request.QueryString("sortExpression")
Else
Return "ProductName"
End If
End Get
End Property
Private Sub RedirectUser(ByVal sendUserToPageIndex As Integer)
' Use the SortExpression property to get the sort expression
' from the querystring
RedirectUser(sendUserToPageIndex, SortExpression)
End Sub
Private Sub RedirectUser(ByVal sendUserToPageIndex As Integer,
ByVal sendUserSortingBy As String)
' Send the user to the requested page with the
' requested sort expression
Response.Redirect(String.Format("SortingWithDefaultPaging.aspx?" & _
"pageIndex={0}&pageSize={1}&sortExpression={2}", _
sendUserToPageIndex, PageSize, sendUserSortingBy))
End Sub
In the first example in this tutorial, we created a sorting
interface using a DropDownList. For this example, let s use
three Button Web controls positioned above the DataList
one for sorting by ProductName, one for
CategoryName, and one for SupplierName.
Add the three Button Web controls, setting their ID
and Text properties appropriately:
<p>
<asp:Button runat="server" id="SortByProductName"
Text="Sort by Product Name" />
<asp:Button runat="server" id="SortByCategoryName"
Text="Sort by Category" />
<asp:Button runat="server" id="SortBySupplierName"
Text="Sort by Supplier" />
</p>
Next, create a Click event handler for each. The
event handlers should call the RedirectUser method,
returning the user to the first page using the appropriate sort
expression.
Protected Sub SortByProductName_Click(sender As Object, e As EventArgs) _
Handles SortByProductName.Click
'Sort by ProductName
RedirectUser(0, "ProductName")
End Sub
Protected Sub SortByCategoryName_Click(sender As Object, e As EventArgs) _
Handles SortByCategoryName.Click
'Sort by CategoryName
RedirectUser(0, "CategoryName")
End Sub
Protected Sub SortBySupplierName_Click(sender As Object, e As EventArgs) _
Handles SortBySupplierName.Click
'Sort by SupplierName
RedirectUser(0, "SupplierName")
End Sub
When first visiting the page, the data is sorted by the product name alphabetically (refer back to Figure 9). Click the Next button to advance to the second page of data and then click the Sort by Category button. This returns us to the first page of data, sorted by category name (see Figure 10). Likewise, clicking the Sort by Supplier button sorts the data by supplier starting from the first page of data. The sort choice is remembered as the data is paged through. Figure 11 shows the page after sorting by category and then advancing to the thirteenth page of data.
Figure 10: The Products are Sorted by Category (Click to view full-size image)
Figure 11: The Sort Expression is Remembered When Paging Through the Data (Click to view full-size image)
Step 6: Custom Paging Through Records in a Repeater
The DataList example examined in step 5 pages through its data
using the inefficient default paging technique. When paging
through sufficiently large amounts of data, it is imperative that
custom paging be used. Back in the Efficiently Paging Through
Large Amounts of Data and Sorting Custom Paged Data
tutorials, we examined the differences between default and custom
paging and created methods in the BLL for utilizing custom paging
and sorting custom paged data. In particular, in these two
previous tutorials we added the following three methods to the
ProductsBLL class:
GetProductsPaged(startRowIndex, maximumRows)returns a particular subset of records starting at startRowIndex and not exceeding maximumRows.GetProductsPagedAndSorted(sortExpression, startRowIndex, maximumRows)returns a particular subset of records sorted by the specified sortExpression input parameter.TotalNumberOfProducts()provides the total number of records in theProductsdatabase table.
These methods can be used to efficiently page and sort through data using a DataList or Repeater control. To illustrate this, let s start by creating a Repeater control with custom paging support; we ll then add sorting capabilities.
Open the SortingWithCustomPaging.aspx page in the
PagingSortingDataListRepeater folder and add a
Repeater to the page, setting its ID property to
Products. From the Repeater s smart tag,
create a new ObjectDataSource named
ProductsDataSource. Configure it to select its data
from the ProductsBLL class s
GetProductsPaged method.
Figure 12: Configure the ObjectDataSource to Use the ProductsBLL Class s GetProductsPaged Method (Click to view full-size image)
Set the drop-down lists in the UPDATE, INSERT, and DELETE tabs
to (None) and then click the Next button. The
Configure Data Source wizard now prompts for the sources of the
GetProductsPaged method s startRowIndex
and maximumRows input parameters. In actuality, these
input parameters are ignored. Instead, the startRowIndex
and maximumRows values will be passed in through the
Arguments property in the ObjectDataSource s
Selecting event handler, just like how we specified
the sortExpression in this tutorial s first demo.
Therefore, leave the parameter source drop-down lists in the
wizard set at None .
Figure 13: Leave the Parameter Sources Set to None (Click to view full-size image)
Note: Do not set the ObjectDataSource s
EnablePaging property to true. This
will cause the ObjectDataSource to automatically include its own
startRowIndex and maximumRows parameters to the
SelectMethod s existing parameter list. The
EnablePaging property is useful when binding custom
paged data to a GridView, DetailsView, or FormView control
because these controls expect certain behavior from the
ObjectDataSource that s only available when
EnablePaging property is true. Since we
have to manually add the paging support for the DataList and
Repeater, leave this property set to false (the
default), as we ll bake in the needed functionality
directly within our ASP.NET page.
Finally, define the Repeater s ItemTemplate
so that the product s name, category, and supplier are
shown. After these changes, the Repeater and
ObjectDataSource s declarative syntax should look similar
to the following:
<asp:Repeater ID="Products" runat="server" DataSourceID="ProductsDataSource"
EnableViewState="False">
<ItemTemplate>
<h4><asp:Label ID="ProductNameLabel" runat="server"
Text='<%# Eval("ProductName") %>'></asp:Label></h4>
Category:
<asp:Label ID="CategoryNameLabel" runat="server"
Text='<%# Eval("CategoryName") %>'></asp:Label><br />
Supplier:
<asp:Label ID="SupplierNameLabel" runat="server"
Text='<%# Eval("SupplierName") %>'></asp:Label><br />
<br />
<br />
</ItemTemplate>
</asp:Repeater>
<asp:ObjectDataSource ID="ProductsDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetProductsPaged" TypeName="ProductsBLL">
<SelectParameters>
<asp:Parameter Name="startRowIndex" Type="Int32" />
<asp:Parameter Name="maximumRows" Type="Int32" />
</SelectParameters>
</asp:ObjectDataSource>
Take a moment to visit the page through a browser and note
that no records are returned. This is because we ve yet to
specify the startRowIndex and maximumRows parameter
values; therefore, values of 0 are being passed in for both.
To specify these values, create an event handler for the
ObjectDataSource s Selecting event and
set these parameters values programmatically to hard-coded
values of 0 and 5, respectively:
Protected Sub ProductsDataSource_Selecting(sender As Object, _
e As ObjectDataSourceSelectingEventArgs) _
Handles ProductsDataSource.Selecting
e.InputParameters("startRowIndex") = 0
e.InputParameters("maximumRows") = 5
End Sub
With this change, the page, when viewed through a browser, shows the first five products.
Figure 14: The First Five Records are Displayed (Click to view full-size image)
Note: The products listed in Figure 14 happen to be
sorted by product name because the GetProductsPaged
stored procedure that performs the efficient custom paging query
orders the results by ProductName.
In order to allow the user to step through the pages, we need to keep track of the start row index and maximum rows and remember these values across postbacks. In the default paging example we used querystring fields to persist these values; for this demo, let s persist this information in the page s view state. Create the following two properties:
Private Property StartRowIndex() As Integer
Get
Dim o As Object = ViewState("StartRowIndex")
If o Is Nothing Then
Return 0
Else
Return CType(o, Integer)
End If
End Get
Set(ByVal value As Integer)
ViewState("StartRowIndex") = value
End Set
End Property
Private Property MaximumRows() As Integer
Get
Dim o As Object = ViewState("MaximumRows")
If o Is Nothing Then
Return 5
Else
Return CType(o, Integer)
End If
End Get
Set(ByVal value As Integer)
ViewState("MaximumRows") = value
End Set
End Property
Next, update the code in the Selecting event handler so
that it uses the StartRowIndex and
MaximumRows properties instead of the hard-coded
values of 0 and 5:
e.InputParameters("startRowIndex") = 0
e.InputParameters("maximumRows") = 5
At this point our page still shows just the first five records. However, with these properties in place, we re ready to create our paging interface.
Adding the Paging Interface
Let s use the same First, Previous, Next, Last paging interface used in the default paging example, including the Label Web control that displays what page of data is being viewed and how many total pages exist. Add the four Button Web controls and Label below the Repeater.
<p>
<asp:Button runat="server" ID="FirstPage" Text="<< First" />
<asp:Button runat="server" ID="PrevPage" Text="< Prev" />
<asp:Button runat="server" ID="NextPage" Text="Next >" />
<asp:Button runat="server" ID="LastPage" Text="Last >>" />
</p>
<p>
<asp:Label runat="server" ID="CurrentPageNumber"></asp:Label>
</p>
Next, create Click event handlers for the four
Buttons. When one of these Buttons is clicked, we need to update
the StartRowIndex and rebind the data to the
Repeater. The code for the First, Previous, and Next buttons is
simple enough, but for the Last button how do we determine the
start row index for the last page of data? To compute this index
as well as being able to determine whether the Next
and Last buttons should be enabled we need to know
how many records in total are being paged through. We can
determine this by calling the ProductsBLL
class s TotalNumberOfProducts() method.
Let s create a read-only, page-level property named
TotalRowCount that returns the results of the
TotalNumberOfProducts() method:
Private ReadOnly Property TotalRowCount() As Integer
Get
'Return the value from the TotalNumberOfProducts() method
Dim productsAPI As New ProductsBLL()
Return productsAPI.TotalNumberOfProducts()
End Get
End Property
With this property we can now determine the last page s
start row index. Specifically, it s the integer result of
the TotalRowCount minus 1 divided by
MaximumRows, multiplied by MaximumRows.
We can now write the Click event handlers for the
four paging interface buttons:
Protected Sub FirstPage_Click(sender As Object, e As EventArgs) _
Handles FirstPage.Click
'Return to StartRowIndex of 0 and rebind data
StartRowIndex = 0
Products.DataBind()
End Sub
Protected Sub PrevPage_Click(sender As Object, e As EventArgs) _
Handles PrevPage.Click
'Subtract MaximumRows from StartRowIndex and rebind data
StartRowIndex -= MaximumRows
Products.DataBind()
End Sub
Protected Sub NextPage_Click(sender As Object, e As EventArgs) _
Handles NextPage.Click
'Add MaximumRows to StartRowIndex and rebind data
StartRowIndex += MaximumRows
Products.DataBind()
End Sub
Protected Sub LastPage_Click(sender As Object, e As EventArgs) _
Handles LastPage.Click
'Set StartRowIndex = to last page's starting row index and rebind data
StartRowIndex = ((TotalRowCount - 1) \ MaximumRows) * MaximumRows
Products.DataBind()
End Sub
Finally, we need to disable the First and Previous buttons in
the paging interface when viewing the first page of data and the
Next and Last buttons when viewing the last page. To accomplish
this, add the following code to the ObjectDataSource s
Selecting event handler:
' Disable the paging interface buttons, if needed
FirstPage.Enabled = StartRowIndex <> 0
PrevPage.Enabled = StartRowIndex <> 0
Dim LastPageStartRowIndex As Integer = _
((TotalRowCount - 1) \ MaximumRows) * MaximumRows
NextPage.Enabled = (StartRowIndex < LastPageStartRowIndex)
LastPage.Enabled = (StartRowIndex < LastPageStartRowIndex)
After adding these Click event handlers and the
code to enable or disable the paging interface elements based on
the current start row index, test the page in a browser. As
Figure 15 illustrates, when first visiting the page the First and
Previous buttons will are disabled. Clicking Next shows the
second page of data, while clicking Last displays the final page
(see Figures 16 and 17). When viewing the last page of data both
the Next and Last buttons are disabled.
Figure 15: The Previous and Last Buttons are Disabled When Viewing the First Page of Products (Click to view full-size image)
Figure 16: The Second Page of Products are Dispalyed (Click to view full-size image)
Figure 17: Clicking Last Displays the Final Page of Data (Click to view full-size image)
Step 7: Including Sorting Support with the Custom Paged Repeater
Now that custom paging has been implemented, we re ready
to include sorting support. The ProductsBLL
class s GetProductsPagedAndSorted method has
the same startRowIndex and maximumRows input
parameters as GetProductsPaged, but permits an
additional sortExpression input parameter. To use the
GetProductsPagedAndSorted method from
SortingWithCustomPaging.aspx, we need to perform the
following steps:
- Change the ObjectDataSource s
SelectMethodproperty fromGetProductsPagedtoGetProductsPagedAndSorted. - Add a sortExpression
Parameterobject to the ObjectDataSource sSelectParameterscollection. - Create a private, page-level
SortExpressionproperty that persists its value across postbacks through the page s view state. - Update the ObjectDataSource s
Selectingevent handler to assign the ObjectDataSource s sortExpression parameter the value of the page-levelSortExpressionproperty. - Create the sorting interface.
Start by updating the ObjectDataSource s
SelectMethod property and adding a
sortExpression Parameter. Make sure that the
sortExpression Parameter s
Type property is set to String. After
completing these first two tasks, the ObjectDataSource s
declarative markup should look like the following:
<asp:ObjectDataSource ID="ProductsDataSource" runat="server"
OldValuesParameterFormatString="original_{0}" TypeName="ProductsBLL"
SelectMethod="GetProductsPagedAndSorted"
OnSelecting="ProductsDataSource_Selecting">
<SelectParameters>
<asp:Parameter Name="sortExpression" Type="String" />
<asp:Parameter Name="startRowIndex" Type="Int32" />
<asp:Parameter Name="maximumRows" Type="Int32" />
</SelectParameters>
</asp:ObjectDataSource>
Next, we need a page-level SortExpression
property whose value is serialized to view state. If no sort
expression value has been set, use ProductName as
the default:
Private Property SortExpression() As String
Get
Dim o As Object = ViewState("SortExpression")
If o Is Nothing Then
Return "ProductName"
Else
Return o.ToString()
End If
End Get
Set(ByVal value As String)
ViewState("SortExpression") = value
End Set
End Property
Before the ObjectDataSource invokes the
GetProductsPagedAndSorted method we need to set the
sortExpression Parameter to the value of the
SortExpression property. In the
Selecting event handler, add the following line of
code:
e.InputParameters("sortExpression") = SortExpression
All that remains is to implement the sorting interface. As we did in the last example, let s have the sorting interface implemented using three Button Web controls that allow the user to sort the results by product name, category, or supplier.
<asp:Button runat="server" id="SortByProductName"
Text="Sort by Product Name" />
<asp:Button runat="server" id="SortByCategoryName"
Text="Sort by Category" />
<asp:Button runat="server" id="SortBySupplierName"
Text="Sort by Supplier" />
Create Click event handlers for these three
Button controls. In the event handler, reset the
StartRowIndex to 0, set the
SortExpression to the appropriate value, and rebind
the data to the Repeater:
Protected Sub SortByProductName_Click(sender As Object, e As EventArgs) _
Handles SortByProductName.Click
StartRowIndex = 0
SortExpression = "ProductName"
Products.DataBind()
End Sub
Protected Sub SortByCategoryName_Click(sender As Object, e As EventArgs) _
Handles SortByCategoryName.Click
StartRowIndex = 0
SortExpression = "CategoryName"
Products.DataBind()
End Sub
Protected Sub SortBySupplierName_Click(sender As Object, e As EventArgs) _
Handles SortBySupplierName.Click
StartRowIndex = 0
SortExpression = "CompanyName"
Products.DataBind()
End Sub
That s all there is to it! While there were a number of steps to get custom paging and sorting implemented, the steps were very similar to those needed for default paging. Figure 18 shows the products when viewing the last page of data when sorted by category.
Figure 18: The Last Page of Data, Sorted by Category, is Displayed (Click to view full-size image)
Note: In previous examples, when sorting by the supplier
SupplierName was used as the sort expression.
However, for the custom paging implementation, we need to use
CompanyName . This is because the stored procedure
responsible for implementing custom paging
GetProductsPagedAndSorted passes the
sort expression into the ROW_NUMBER()
keyword, The ROW_NUMBER() keyword requires
the actual column name rather than an alias. Therefore, we must
use CompanyName (the name of the column in
the Suppliers table) rather than the alias
used in the SELECT query
(SupplierName) for the sort
expression.
Summary
Neither the DataList nor Repeater offer built-in sorting
support, but with a bit of code and a custom sorting interface,
such functionality can be added. When implementing sorting, but
not paging, the sort expression can be specified through the
DataSourceSelectArguments object passed into the
ObjectDataSource s Select method. This
DataSourceSelectArguments object s
SortExpression property can be assigned in the
ObjectDataSource s Selecting event
handler.
To add sorting capabilities to a DataList or Repeater that
already provides paging support, the easiest approach is to
customize the Business Logic Layer to include a method that
accepts a sort expression. This information can then be passed in
through a parameter in the ObjectDataSource s
SelectParameters.
This tutorial completes our examination of paging and sorting with the DataList and Repeater controls. Our next and final tutorial will examine how to add Button Web controls to the DataList and Repeater s templates in order to provide some custom, user-initiated functionality on a per-item basis.
Happy Programming!
About the Author
Scott Mitchell, author of seven ASP/ASP.NET books and founder of 4GuysFromRolla.com, has been working with Microsoft Web technologies since 1998. Scott works as an independent consultant, trainer, and writer. His latest book is Sams Teach Yourself ASP.NET 2.0 in 24 Hours. He can be reached at mitchell@4GuysFromRolla.com. or via his blog, which can be found at http://ScottOnWriting.NET.
Special Thanks To
This tutorial series was reviewed by many helpful reviewers. Lead reviewer for this tutorial was David Suru. Interested in reviewing my upcoming MSDN articles? If so, drop me a line at mitchell@4GuysFromRolla.com.


















Comments (0) RSS Feed