排序 DataList 或 Repeater 控件中的数据 (C#)

作者 :Scott Mitchell

下载 PDF

本教程介绍如何在 DataList 和 Repeater 中包含排序支持,以及如何构造数据可以分页和排序的 DataList 或 Repeater。

简介

上一教程中 ,我们介绍了如何将分页支持添加到 DataList。 我们在类 (GetProductsAsPagedDataSource) 中创建了一个PagedDataSource返回 对象的新方法ProductsBLL。 绑定到 DataList 或 Repeater 时,DataList 或 Repeater 将仅显示请求的数据页。 此方法类似于 GridView、DetailsView 和 FormView 控件在内部用于提供其内置默认分页功能的方法。

除了提供分页支持外,GridView 还包括现成的排序支持。 DataList 和 Repeater 均未提供内置排序功能;但是,可以使用一些代码添加排序功能。 本教程介绍如何在 DataList 和 Repeater 中包含排序支持,以及如何构造数据可以分页和排序的 DataList 或 Repeater。

排序回顾

正如我们在 分页和排序报表数据 教程中看到的那样,GridView 控件提供现成的排序支持。 每个 GridView 字段都可以有一个关联的 SortExpression,该字段指示数据排序依据的数据字段。 当 GridView 的 AllowSorting 属性设置为 true时,每个具有 SortExpression 属性值的 GridView 字段的标头都呈现为 LinkButton。 当用户单击特定的 GridView 字段标头时,将发生回发,并且数据将根据单击的字段 SortExpression进行排序。

GridView 控件也有一个 SortExpression 属性,该属性存储 SortExpression 对数据进行排序的 GridView 字段的 。 此外, SortDirection 属性指示数据是按升序还是降序排序, (如果用户连续单击特定 GridView 字段的标头链接两次,则排序顺序) 切换。

当 GridView 绑定到其数据源控件时,它会将其 SortExpressionSortDirection 属性移交给数据源控件。 数据源控件检索数据,然后根据提供的 SortExpressionSortDirection 属性对其进行排序。 对数据进行排序后,数据源控件将其返回到 GridView。

若要使用 DataList 或 Repeater 控件复制此功能,我们必须:

  • 创建排序接口
  • 记住要按排序的数据字段,以及按升序还是降序排序
  • 指示 ObjectDataSource 按特定数据字段对数据进行排序

我们将在步骤 3 和 4 中处理这三个任务。 之后,我们将研究如何在 DataList 或 Repeater 中包含分页和排序支持。

步骤 2:在中继器中显示产品

在担心实现任何与排序相关的功能之前,让我们先在 Repeater 控件中列出产品。 首先打开 Sorting.aspx 文件夹中的页面 PagingSortingDataListRepeater 。 将 Repeater 控件添加到网页,并将其 ID 属性设置为 SortableProducts。 在 Repeater 的智能标记中,创建一个名为 ProductsDataSource 的新 ObjectDataSource,并将其配置为从 ProductsBLL 类 s GetProducts() 方法检索数据。 从“插入”、“更新”和“删除”选项卡的下拉列表中选择“ (无) ”选项。

创建 ObjectDataSource 并将其配置为使用 GetProductsAsPagedDataSource () 方法

图 1:创建 ObjectDataSource 并将其配置为使用 GetProductsAsPagedDataSource() 方法 (单击以查看全尺寸图像)

将“更新”、“插入”和“删除”选项卡中的 Drop-Down Lists 设置为“无” ()

图 2:将“更新”、“插入”和“删除”选项卡中的 Drop-Down Lists 设置为 (“无”) (单击以查看全尺寸图像)

与 DataList 不同,Visual Studio 在将 Repeater 控件绑定到数据源后不会自动为其创建 ItemTemplate 。 此外,我们必须以声明方式添加此 ItemTemplate 内容,因为 Repeater 控件的智能标记缺少 DataList s 中的“编辑模板”选项。 让我们使用上一教程中的相同 ItemTemplate 方法,其中显示了产品名称、供应商和类别。

添加 ItemTemplate后,Repeater 和 ObjectDataSource 的声明性标记应如下所示:

<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>

图 3 显示了通过浏览器查看时的此页面。

显示每个产品名称、供应商和类别

图 3:显示每个产品名称、供应商和类别 (单击以查看全尺寸图像)

步骤 3:指示 ObjectDataSource 对数据进行排序

若要对 Repeater 中显示的数据进行排序,我们需要通知 ObjectDataSource 排序表达式,以便对数据进行排序。 在 ObjectDataSource 检索其数据之前,它首先会触发其 Selecting 事件,这为我们提供了指定排序表达式的机会。 事件处理程序 Selecting 将传递类型 ObjectDataSourceSelectingEventArgs为 的对象,该对象具有类型为 的名为 Arguments 的属性 DataSourceSelectArguments。 类DataSourceSelectArguments旨在将数据相关请求从数据使用者传递到数据源控件,并包含 属性SortExpression

若要将排序信息从 ASP.NET 页传递到 ObjectDataSource,请为 Selecting 事件创建事件处理程序,并使用以下代码:

protected void ProductsDataSource_Selecting
    (object sender, ObjectDataSourceSelectingEventArgs e)
{
    e.Arguments.SortExpression = sortExpression;
}

应为 sortExpression 值分配数据字段的名称,以便按 productName ) 等 (对数据进行排序。 没有与排序方向相关的属性,因此,如果要按降序对数据进行排序,请将字符串 DESC 追加到 sortExpression 值 (,例如 ProductName DESC ) 。

继续为 sortExpression 尝试一些不同的硬编码值,并在浏览器中测试结果。 如图 4 所示,使用 ProductName DESC 作为 sortExpression 时,产品按名称按反向字母顺序排序。

产品按名称按反向字母顺序排序

图 4:产品按名称按反向字母顺序排序 (单击以查看全尺寸图像)

步骤 4:创建排序接口并记住排序表达式和方向

在 GridView 中启用排序支持会将每个可排序字段的标题文本转换为 LinkButton,单击该链接时,该链接会相应地对数据进行排序。 这种排序接口对于 GridView 有意义,因为 GridView 的数据按列整齐排列。 但是,对于 DataList 和 Repeater 控件,需要不同的排序接口。 数据 (列表(而不是数据) 网格)的常见排序接口是一个下拉列表,它提供数据排序依据的字段。 让我们在本教程中实现这样的接口。

在 Repeater 上方 SortableProducts 添加 DropDownList Web 控件,并将其 ID 属性设置为 SortBy。 在属性窗口,单击 属性中的Items省略号以显示 ListItem 集合编辑器。 添加 ListItem 以按 ProductNameCategoryNameSupplierName 字段对数据进行排序。 此外,添加 以 ListItem 按产品名称按反向字母顺序对产品进行排序。

属性 ListItemText 可以设置为任何值 (如 Name ) ,但 Value 属性必须设置为数据字段的名称 (如 ProductName ) 。 若要按降序对结果进行排序,请将字符串 DESC 追加到数据字段名称,如 ProductName DESC 。

为每个可排序数据字段添加 ListItem

图 5:为每个可排序数据字段添加ListItem

最后,在 DropDownList 的右侧添加一个 Button Web 控件。 将其 ID 设置为 RefreshRepeater ,其 Text 属性设置为 Refresh 。

创建 ListItem 并添加“刷新”按钮后,DropDownList 和 Button 的声明性语法应如下所示:

<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" />

完成 DropDownList 排序后,接下来需要更新 ObjectDataSource 的 Selecting 事件处理程序,使其使用所选 SortBy``ListItem 的 s Value 属性,而不是硬编码的排序表达式。

protected void ProductsDataSource_Selecting
    (object sender, ObjectDataSourceSelectingEventArgs e)
{
    // Have the ObjectDataSource sort the results by the selected
    // sort expression
    e.Arguments.SortExpression = SortBy.SelectedValue;
}

此时,首次访问页面时,产品最初将按 ProductName 数据字段排序,因为它是 SortByListItem 默认选择 (见图 6) 。 选择其他排序选项(如“类别”)并单击“刷新”将导致回发并按类别名称对数据重新排序,如图 7 所示。

产品最初按名称排序

图 6:产品最初按其名称排序 (单击以查看全尺寸图像)

产品现在按类别排序

图 7:产品现在按类别排序 (单击以查看全尺寸图像)

注意

单击“刷新”按钮会使数据自动重新排序,因为中继器视图状态已被禁用,从而导致中继器在每次回发时重新绑定到其数据源。 如果已启用 Repeater 的视图状态,则更改排序下拉列表不会对排序顺序产生任何影响。 若要解决此问题,请为“刷新按钮” Click 事件创建事件处理程序,并通过) 调用 Repeater 方法 DataBind() 将中继器重新绑定到其数据源 (。

记住排序表达式和方向

在可能发生非排序相关回发的页面上创建可排序的 DataList 或 Repeater 时,必须跨回发记住排序表达式和方向。 例如,假设我们更新了本教程中的 Repeater,以包含每个产品的“删除”按钮。 当用户单击“删除”按钮时,我们将运行一些代码来删除所选产品,然后将数据重新绑定到 Repeater。 如果排序详细信息未在回发中保留,则屏幕上显示的数据将还原原始排序顺序。

在本教程中,DropDownList 将排序表达式和方向隐式保存在其视图状态中。 如果我们使用不同的排序接口,例如 LinkButtons,它提供了各种排序选项,我们需要注意记住跨回发的排序顺序。 这可以通过将排序参数存储在页面视图状态中、通过在查询字符串中包含排序参数或通过一些其他状态持久性技术来实现。

本教程中的后续示例将探讨如何在页面视图状态中保留排序详细信息。

步骤 5:将排序支持添加到使用默认分页的 DataList

前面的教程中 ,我们介绍了如何使用 DataList 实现默认分页。 让我们扩展前面的示例,以包括对分页数据进行排序的功能。 首先打开 文件夹中的 SortingWithDefaultPaging.aspxPagingSortingDataListRepeaterPaging.aspx 页。 在 Paging.aspx 页面中,单击“源”按钮以查看页面的声明性标记。 复制所选文本 (请参阅图 8) ,并将其粘贴到标记之间的声明性标记 SortingWithDefaultPaging.aspx<asp:Content>

将 asp:Content> 标记中的<声明性标记从 Paging.aspx 复制到 SortingWithDefaultPaging.aspx

图 8:将标记中的 <asp:Content> 声明性标记从 Paging.aspx 复制到 SortingWithDefaultPaging.aspx (单击以查看全尺寸图像)

复制声明性标记后,将页代码隐藏类中的 Paging.aspx 方法和属性复制到 的代码 SortingWithDefaultPaging.aspx隐藏类。 接下来,花点时间在浏览器中查看 SortingWithDefaultPaging.aspx 页面。 它应显示与 相同的功能和外观 Paging.aspx

增强 ProductsBLL 以包括默认分页和排序方法

在上一教程中,我们在 类中ProductsBLL创建了一个GetProductsAsPagedDataSource(pageIndex, pageSize)PagedDataSource返回 对象的 方法。 此PagedDataSource对象填充了通过 BLL 方法 GetProducts()) (的所有产品,但在绑定到 DataList 时,仅显示与指定 pageIndexpageSize 输入参数对应的记录。

在本教程的前面部分中,我们通过指定 ObjectDataSource Selecting 事件处理程序中的排序表达式来添加排序支持。 当 ObjectDataSource 返回可排序的对象(如 ProductsDataTable 方法返回 GetProducts() 的 )时,这很有效。 但是, PagedDataSource 方法返回 GetProductsAsPagedDataSource 的 对象不支持对其内部数据源进行排序。 相反,我们需要先对方法 GetProducts() 返回的结果进行排序, 然后再 将其 PagedDataSource放入 中。

为此,请在 类 GetProductsSortedAsPagedDataSource(sortExpression, pageIndex, pageSize)中创建ProductsBLL一个新方法。 若要对 方法返回的 GetProducts() 排序ProductsDataTable,请指定Sort其默认 DataTableView的 属性:

[System.ComponentModel.DataObjectMethodAttribute
    (System.ComponentModel.DataObjectMethodType.Select, false)]
public PagedDataSource GetProductsSortedAsPagedDataSource
    (string sortExpression, int pageIndex, int pageSize)
{
    // Get ALL of the products
    Northwind.ProductsDataTable products = GetProducts();
    // Sort the products
    products.DefaultView.Sort = sortExpression;
    // Limit the results through a PagedDataSource
    PagedDataSource pagedData = new PagedDataSource();
    pagedData.DataSource = products.DefaultView;
    pagedData.AllowPaging = true;
    pagedData.CurrentPageIndex = pageIndex;
    pagedData.PageSize = pageSize;
    return pagedData;
}

方法 GetProductsSortedAsPagedDataSource 仅与上一教程中创建的方法略有 GetProductsAsPagedDataSource 不同。 具体而言,GetProductsSortedAsPagedDataSource接受其他输入参数sortExpression,并将此值Sort分配给 的 DefaultView属性ProductDataTable。 稍后几行代码,为 PagedDataSource 对象 DataSource 分配了 ProductDataTable s DefaultView

调用 GetProductsSortedAsPagedDataSource 方法并指定 SortExpression 输入参数的值

GetProductsSortedAsPagedDataSource方法完成后,下一步是提供此参数的值。 中的 ObjectDataSource SortingWithDefaultPaging.aspx 当前配置为调用 GetProductsAsPagedDataSource 方法,并通过集合中指定的SelectParameters两个 传递两个QueryStringParameters输入参数。 这两QueryStringParameters个指示方法 pageIndexpageSize 参数的GetProductsAsPagedDataSource源来自查询字符串字段pageIndexpageSize

更新 ObjectDataSource 的 SelectMethod 属性,使其调用新 GetProductsSortedAsPagedDataSource 方法。 然后,添加新的 QueryStringParameter ,以便从查询字符串字段 sortExpression访问 sortExpression 输入参数。 将 QueryStringParameter 设置为 ProductName 。DefaultValue

进行这些更改后,ObjectDataSource 的声明性标记应如下所示:

<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>

此时, SortingWithDefaultPaging.aspx 页面将按产品名称按字母顺序对结果进行排序 (请参阅图 9) 。 这是因为,默认情况下,ProductName 的值作为GetProductsSortedAsPagedDataSource方法 sortExpression 参数传入。

默认情况下,结果按 ProductName 排序

图 9:默认情况下,结果按 ProductName 排序 (单击以查看全尺寸图像)

如果手动添加 sortExpression 查询字符串字段(例如 SortingWithDefaultPaging.aspx?sortExpression=CategoryName ),则结果将按指定的 sortExpression进行排序。 但是,在移动到其他数据页时,此参数 sortExpression 不包括在 querystring 中。 事实上,单击“下一页”或“最后一页”按钮会将我们带回 Paging.aspx! 此外,目前没有排序接口。 用户更改分页数据的排序顺序的唯一方法是直接操作查询字符串。

创建排序接口

我们首先需要更新 RedirectUser 方法,以便将用户发送到 SortingWithDefaultPaging.aspx (而不是 Paging.aspx) ,并将 值包含在 sortExpression querystring 中。 我们还应添加一个只读的页级命名 SortExpression 属性。 此属性类似于 PageIndex 上一教程中创建的 和 PageSize 属性,如果存在,则返回 querystring 字段的值 sortExpression ,否则返回默认值 ( ProductName ) 。

目前, RedirectUser 方法只接受一个输入参数,用于显示页面的索引。 但是,有时我们可能需要使用查询字符串中指定的 排序表达式将用户重定向到特定数据页。 稍后,我们将为此页面创建排序界面,其中包含一系列用于按指定列对数据进行排序的 Button Web 控件。 单击其中一个按钮时,我们希望重定向传入相应排序表达式值的用户。 若要提供此功能,请创建 方法的两个 RedirectUser 版本。 第一个应仅接受要显示的页面索引,而第二个应接受页面索引和排序表达式。

private string SortExpression
{
    get
    {
        if (!string.IsNullOrEmpty(Request.QueryString["sortExpression"]))
            return Request.QueryString["sortExpression"];
        else
            return "ProductName";
    }
}
private void RedirectUser(int sendUserToPageIndex)
{
    // Use the SortExpression property to get the sort expression
    // from the querystring
    RedirectUser(sendUserToPageIndex, SortExpression);
}
private void RedirectUser(int sendUserToPageIndex, string sendUserSortingBy)
{
   // 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));
}

在本教程的第一个示例中,我们使用 DropDownList 创建了排序接口。 对于此示例,让我们使用位于 DataList 上方的三个 Button Web 控件,一个用于排序依据ProductName,一个用于 排序,一个用于 CategoryNameSupplierName排序。 添加三个按钮 Web 控件,并相应地设置它们的 IDText 属性:

<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>

接下来,为每个事件创建事件处理程序 Click 。 事件处理程序应调用 RedirectUser 方法,使用适当的排序表达式将用户返回到第一页。

protected void SortByProductName_Click(object sender, EventArgs e)
{
    // Sort by ProductName
    RedirectUser(0, "ProductName");
}
protected void SortByCategoryName_Click(object sender, EventArgs e)
{
    // Sort by CategoryName
    RedirectUser(0, "CategoryName");
}
protected void SortBySupplierName_Click(object sender, EventArgs e)
{
    // Sort by SupplierName
    RedirectUser(0, "SupplierName");
}

首次访问页面时,数据按产品名称按字母顺序排序, (请参阅图 9) 。 单击“下一步”按钮转到第二页数据,然后单击“按类别排序”按钮。 这会返回数据的第一页,按类别名称排序 (请参阅图 10) 。 同样,单击“按供应商排序”按钮会从数据的第一页开始按供应商对数据进行排序。 在对数据进行分页时,将记住排序选项。 图 11 显示按类别排序后的页面,然后前进到数据的第十三页。

产品按类别排序

图 10:产品按类别排序 (单击以查看全尺寸图像)

在对数据进行分页时,将记住排序表达式

图 11:分页浏览数据时会记住排序表达式 (单击以查看全尺寸图像)

步骤 6:在中继器中自定义分页记录

使用低效的默认分页技术在步骤 5 页中检查的 DataList 示例通过其数据。 分页浏览足够大量的数据时,必须使用自定义分页。 回到 高效分页大量数据排序自定义分页数据 教程中,我们检查了默认分页和自定义分页之间的差异,以及 BLL 中用于利用自定义分页和排序自定义分页数据的方法。 具体而言,在前面的两个教程中,我们向 ProductsBLL 类添加了以下三种方法:

  • GetProductsPaged(startRowIndex, maximumRows) 返回从 startRowIndex 开始且不超过 maximumRows 的特定记录子集。
  • GetProductsPagedAndSorted(sortExpression, startRowIndex, maximumRows) 返回按指定的 sortExpression 输入参数排序的特定记录子集。
  • TotalNumberOfProducts() 提供数据库表中的记录 Products 总数。

这些方法可用于使用 DataList 或 Repeater 控件有效地对数据进行分页和排序。 为了说明这一点,让我们首先创建具有自定义分页支持的 Repeater 控件;然后,我们将添加排序功能。

SortingWithCustomPaging.aspx打开 文件夹中的页面PagingSortingDataListRepeater,并将中继器添加到页面,并将其ID属性设置为 Products。 在 Repeater 的智能标记中,创建名为 ProductsDataSource的新 ObjectDataSource。 将其配置为从 ProductsBLL 类 s GetProductsPaged 方法中选择其数据。

将 ObjectDataSource 配置为使用 ProductsBLL 类 GetProductsPaged 方法

图 12:将 ObjectDataSource 配置为使用 ProductsBLL 类 s GetProductsPaged 方法 (单击以查看全尺寸图像)

将“更新”、“插入”和“删除”选项卡中的下拉列表设置为 (“无”) 然后单击“下一步”按钮。 “配置数据源”向导现在提示输入方法 startRowIndexmaximumRows 输入参数的源GetProductsPaged。 实际上,这些输入参数将被忽略。 相反,startRowIndexmaximumRows 值将通过 ObjectDataSource 事件处理程序中的 Selecting 属性传入Arguments,就像我们在本教程第一个演示中指定 sortExpression 的方式一样。 因此,将向导中的参数源下拉列表保留设置为“无”。

将参数源设置为“无”

图 13:将参数源设置为“无” (单击以查看全尺寸图像)

注意

不要将 ObjectDataSource 的 EnablePaging 属性设置为 true。 这将导致 ObjectDataSource 自动将自己的 startRowIndexmaximumRows 参数包含在 SelectMethod 现有参数列表中。 将自定义分页数据绑定到 GridView、DetailsView 或 FormView 控件时,属性EnablePaging很有用,因为这些控件需要 ObjectDataSource 中的某些行为,仅在 属性为 trueEnablePaging可用。 由于我们必须手动添加 DataList 和 Repeater 的分页支持,因此请将此属性设置为 false (默认) ,因为我们将直接在 ASP.NET 页中烘焙所需的功能。

最后,定义中继器, ItemTemplate 以便显示产品的名称、类别和供应商。 进行这些更改后,Repeater 和 ObjectDataSource 的声明性语法应如下所示:

<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>

请花点时间通过浏览器访问页面,注意不会返回任何记录。 这是因为我们尚未指定 startRowIndexmaximumRows 参数值;因此,为两者传入值 0。 若要指定这些值,请为 ObjectDataSource 事件 Selecting 创建事件处理程序,并通过编程方式将这些参数值分别设置为 0 和 5 的硬编码值:

protected void ProductsDataSource_Selecting
    (object sender, ObjectDataSourceSelectingEventArgs e)
{
    e.InputParameters["startRowIndex"] = 0;
    e.InputParameters["maximumRows"] = 5;
}

通过此更改,页面在通过浏览器查看时会显示前五个产品。

显示前五条记录

图 14:前五条记录 (单击查看全尺寸图像)

注意

图 14 中列出的产品恰好按产品名称排序, GetProductsPaged 因为执行高效自定义分页查询的存储过程按 对结果进行 ProductName排序。

为了允许用户单步执行页面,我们需要跟踪开始行索引和最大行数,并在回发中记住这些值。 在默认分页示例中,我们使用查询字符串字段来保留这些值;对于此演示,让我们将此信息保存在页面的视图状态中。 创建以下两个属性:

private int StartRowIndex
{
    get
    {
        object o = ViewState["StartRowIndex"];
        if (o == null)
            return 0;
        else
            return (int)o;
    }
    set
    {
        ViewState["StartRowIndex"] = value;
    }
}
private int MaximumRows
{
    get
    {
        object o = ViewState["MaximumRows"];
        if (o == null)
            return 5;
        else
            return (int)o;
    }
    set
    {
        ViewState["MaximumRows"] = value;
    }
}

接下来,更新选择事件处理程序中的代码,使其使用 StartRowIndexMaximumRows 属性,而不是 0 和 5 的硬编码值:

e.InputParameters["startRowIndex"] = StartRowIndex;
e.InputParameters["maximumRows"] = MaximumRows;

此时,我们的页面仍然只显示前五条记录。 但是,有了这些属性,我们就可以创建分页接口了。

添加分页接口

让我们使用默认分页示例中使用的相同 First、Previous、Next、Last 分页接口,包括显示正在查看的数据页和总页数的 Label Web 控件。 在中继器下方添加四个按钮 Web 控件和标签。

<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>

接下来,为四个 Button 创建 Click 事件处理程序。 单击其中一个按钮时,我们需要更新 StartRowIndex 并将数据重新绑定到中继器。 “第一个”、“上一个”和“下一步”按钮的代码很简单,但对于“最后一个”按钮,如何确定最后一页数据的起始行索引? 若要计算此索引,并能够确定是否应启用“下一步”和“最后一个”按钮,我们需要知道正在分页的记录总数。 可以通过调用 ProductsBLL 类的 TotalNumberOfProducts() 方法来确定这一点。 让我们创建一个名为 TotalRowCount 的只读页面级属性,该属性返回 方法的结果 TotalNumberOfProducts()

private int TotalRowCount
{
    get
    {
        // Return the value from the TotalNumberOfProducts() method
        ProductsBLL productsAPI = new ProductsBLL();
        return productsAPI.TotalNumberOfProducts();
    }
}

通过此属性,我们现在可以确定最后一页的起始行索引。 具体而言,它是减 1 除以 MaximumRows的整数结果TotalRowCount,乘以 MaximumRows。 现在可以为四个 Click 分页界面按钮编写事件处理程序:

protected void FirstPage_Click(object sender, EventArgs e)
{
    // Return to StartRowIndex of 0 and rebind data
    StartRowIndex = 0;
    Products.DataBind();
}
protected void PrevPage_Click(object sender, EventArgs e)
{
    // Subtract MaximumRows from StartRowIndex and rebind data
    StartRowIndex -= MaximumRows;
    Products.DataBind();
}
protected void NextPage_Click(object sender, EventArgs e)
{
    // Add MaximumRows to StartRowIndex and rebind data
    StartRowIndex += MaximumRows;
    Products.DataBind();
}
protected void LastPage_Click(object sender, EventArgs e)
{
    // Set StartRowIndex = to last page's starting row index and rebind data
    StartRowIndex = ((TotalRowCount - 1) / MaximumRows) * MaximumRows;
    Products.DataBind();
}

最后,我们需要在查看数据的第一页时禁用分页界面中的“第一个”和“上一个”按钮,在查看最后一页时禁用“下一步”和“最后一个”按钮。 为此,请将以下代码添加到 ObjectDataSource 的 Selecting 事件处理程序:

// Disable the paging interface buttons, if needed
FirstPage.Enabled = StartRowIndex != 0;
PrevPage.Enabled = StartRowIndex != 0;
int LastPageStartRowIndex = ((TotalRowCount - 1) / MaximumRows) * MaximumRows;
NextPage.Enabled = StartRowIndex < LastPageStartRowIndex;
LastPage.Enabled = StartRowIndex < LastPageStartRowIndex;

添加这些 Click 事件处理程序和代码以根据当前起始行索引启用或禁用分页接口元素后,请在浏览器中测试页面。 如图 15 所示,首次访问页面时,将禁用“第一个”和“上一个”按钮。 单击“下一步”显示第二页数据,而单击“最后一页”显示最后一页 (请参阅图 16 和 17) 。 查看最后一页数据时,禁用“下一步”和“最后一页”按钮。

查看产品首页时禁用“上一个”和“最后一个”按钮

图 15:查看产品首页时禁用“上一个”和“最后一个”按钮 (单击以查看全尺寸图像)

显示产品的第二页

图 16:显示第二页产品 (单击以查看全尺寸图像)

单击“最后一页”显示数据的最后一页

图 17:单击“最后一页”显示数据的最终页 (单击以查看全尺寸图像)

步骤 7:使用自定义分页中继器包括排序支持

现在,自定义分页已实现,我们准备包括排序支持。 类 ProductsBLL 方法 GetProductsPagedAndSorted 具有与 相同的 startRowIndexmaximumRows 输入参数 GetProductsPaged,但允许额外的 sortExpression 输入参数。 若要使用 GetProductsPagedAndSorted 中的 SortingWithCustomPaging.aspx方法,需要执行以下步骤:

  1. 将 ObjectDataSource 的 SelectMethod 属性从 GetProductsPaged 更改为 GetProductsPagedAndSorted
  2. sortExpressionParameter 对象添加到 ObjectDataSource 的 SelectParameters 集合。
  3. 创建一个专用的页级 SortExpression 属性,该属性通过页面视图状态跨回发保留其值。
  4. 更新 ObjectDataSource 的 Selecting 事件处理程序,为 ObjectDataSource 的 sortExpression 参数分配页面级 SortExpression 属性的值。
  5. 创建排序接口。

首先更新 ObjectDataSource 的 SelectMethod 属性并添加 sortExpressionParameter。 请确保 sortExpressionParameter s Type 属性设置为 String。 完成前两个任务后,ObjectDataSource 的声明性标记应如下所示:

<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>

接下来,我们需要一个页面级 SortExpression 属性,其值已序列化以查看状态。 如果未设置排序表达式值,请使用 ProductName 作为默认值:

private string SortExpression
{
    get
    {
        object o = ViewState["SortExpression"];
        if (o == null)
            return "ProductName";
        else
            return o.ToString();
    }
    set
    {
        ViewState["SortExpression"] = value;
    }
}

在 ObjectDataSource 调用 方法之前, GetProductsPagedAndSorted 我们需要将 sortExpressionParameter 设置为 属性的值 SortExpression 。 在事件处理程序中 Selecting ,添加以下代码行:

e.InputParameters["sortExpression"] = SortExpression;

剩下的就是实现排序接口。 正如我们在上一个示例中所做的那样,让我们使用三个按钮 Web 控件实现排序界面,这些控件允许用户按产品名称、类别或供应商对结果进行排序。

<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" />

为这三个 Button 控件创建 Click 事件处理程序。 在事件处理程序中,将 StartRowIndex 重置为 0,将 设置为 SortExpression 适当的值,然后将数据重新绑定到 Repeater:

protected void SortByProductName_Click(object sender, EventArgs e)
{
    StartRowIndex = 0;
    SortExpression = "ProductName";
    Products.DataBind();
}
protected void SortByCategoryName_Click(object sender, EventArgs e)
{
    StartRowIndex = 0;
    SortExpression = "CategoryName";
    Products.DataBind();
}
protected void SortBySupplierName_Click(object sender, EventArgs e)
{
    StartRowIndex = 0;
    SortExpression = "CompanyName";
    Products.DataBind();
}

这就是它的全部! 虽然有许多步骤可以实现自定义分页和排序,但这些步骤与默认分页所需的步骤非常相似。 图 18 显示了按类别排序时查看最后一页数据时的产品。

显示数据的最后一页(按类别排序)

图 18:按类别排序 (显示“最后一页数据” 以查看全尺寸图像)

注意

在前面的示例中,按供应商供应商名称排序时,用作排序表达式。 但是,对于自定义分页实现,需要使用 CompanyName。 这是因为负责实现自定义分页GetProductsPagedAndSorted的存储过程将排序表达式传递到ROW_NUMBER()关键字 (keyword) ,关键字 (keyword) ROW_NUMBER() 需要实际的列名称,而不是别名。 因此,我们必须使用 CompanyName (表中列的名称 Suppliers) ,而不是在查询 (SupplierName) 排序表达式中使用的SELECT别名。

总结

DataList 和 Repeater 都不提供内置排序支持,但通过一些代码和自定义排序接口,可以添加此类功能。 实现排序而不是分页时,可以通过传递到 ObjectDataSource 方法的对象Select指定DataSourceSelectArguments排序表达式。 可以在 ObjectDataSource 的事件处理程序中分配此 DataSourceSelectArguments 对象 SortExpression 属性 Selecting

若要向已提供分页支持的 DataList 或 Repeater 添加排序功能,最简单的方法是自定义业务逻辑层,以包含接受排序表达式的方法。 然后,可以通过 ObjectDataSource 中的 SelectParameters参数传入此信息。

本教程将完成使用 DataList 和 Repeater 控件进行分页和排序的检查。 我们的下一个和最后一个教程将介绍如何将 Button Web 控件添加到 DataList 和 Repeater 的模板中,以便按项提供一些自定义的、用户启动的功能。

编程快乐!

关于作者

斯科特·米切尔是七本 ASP/ASP.NET 书籍的作者和 4GuysFromRolla.com 的创始人,自 1998 年以来一直在使用 Microsoft Web 技术。 Scott 担任独立顾问、培训师和作家。 他的最新一本书是 山姆斯在 24 小时内 ASP.NET 2.0。 可以在 上mitchell@4GuysFromRolla.com联系他,也可以通过他的博客(可在 中找到http://ScottOnWriting.NET)。

特别感谢

本教程系列由许多有用的审阅者审阅。 本教程的首席审阅者是 David Suru。 有兴趣查看我即将发布的 MSDN 文章? 如果是,请在 处mitchell@4GuysFromRolla.com放置一行。