DataList 或重複項控制項中的排序資料 (C#)

作者 :Scott Mitchell

下載 PDF

在本教學課程中,我們將探討如何在 DataList 和 Repeater 中包含排序支援,以及如何建構可分頁和排序數據的 DataList 或 Repeater。

簡介

上一個教學課程中, 我們已探討如何將分頁支援新增至 DataList。 我們在 類別中 ProductsBLL 建立了新方法, (GetProductsAsPagedDataSource 傳回 PagedDataSource 物件的) 。 系結至 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() 方法擷取數據。 從 [INSERT]、[更新] 和 [刪除] 索引卷標的下拉式清單中選取 ([無) ] 選項。

建立 ObjectDataSource 並將其設定為使用 GetProductsAsPagedDataSource () 方法

圖 1:建立 ObjectDataSource 並將其設定為使用 GetProductsAsPagedDataSource() 方法 (按兩下即可檢視完整大小的影像)

將 UPDATE、INSERT 和 DELETE 索引標籤中的 Drop-Down 清單 設定為 ([無])

圖 2:將 UPDATE、INSERT 和 DELETE 索引標籤中的 Drop-Down 清單 設定為 ([無]) (按兩下即可檢視完整大小的映像)

不同於 DataList,Visual Studio 不會在將重複項控件系結至數據源之後自動建立 ItemTemplate 。 此外,我們必須以宣告方式新增這個 ItemTemplate ,因為重複項控件的智慧標記缺少 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 排序數據

若要排序重複程序中顯示的數據,我們需要通知 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,其數據會整齊地配置在數據行中。 不過,對於 DataList 和 Repeater 控件,需要不同的排序介面。 與數據方格) 相反之數據清單的常見排序介面 (,是一個下拉式清單,提供可排序數據的欄位。 讓我們針對本教學課程實作這類介面。

在 Repeater 上方 SortableProducts 新增 DropDownList Web 控件,並將其 ID 屬性設定為 SortBy。 從 屬性視窗 按兩下 屬性中的Items省略號,以顯示 ListItem 集合 編輯器。 新增 ListItem ,依、 CategoryNameSupplierName 欄位排序數據ProductName。 此外,請新增 , ListItem 以反向字母順序依其名稱排序產品。

屬性 ListItemText 可以設定為任何值 (,例如 Name ) ,但 Value 屬性必須設定為數據欄位的名稱 (,例如 ProductName ) 。 若要以遞減順序排序結果,請將字串 DESC 附加至資料功能變數名稱,例如 ProductName DESC 。

為每個可排序的數據欄位新增 ListItem

圖 5:為每個可排序的數據欄位新增ListItem

最後,將 Button Web 控制項新增至 DropDownList 右側。 將其 設定為 RefreshRepeaterID並將其 Text 屬性設定為 [重新整理]。

建立 ListItem s 並新增 [重新整理] 按鈕之後,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 的檢視狀態已停用,因而使 Repeater 在每次回傳時重新系結至其數據源。 如果您已讓重複程序檢視狀態保持啟用狀態,變更排序下拉式清單不會影響排序順序。 若要解決此問題,請建立 Refresh Button s Click 事件的事件處理程式,並呼叫 Repeater s DataBind() 方法) ,將 Repeater 重新系結至其數據源 (。

記住排序表達式和方向

在可能發生非排序相關回傳的頁面上建立可排序的 DataList 或 Repeater 時,排序運算式和方向必須在回傳之間記住。 例如,假設我們在本教學課程中更新了重複程式,以包含每個產品的 [刪除] 按鈕。 當使用者按兩下 [刪除] 按鈕時,我們會執行一些程式代碼來刪除選取的產品,然後將數據重新系結至 Repeater。 如果未跨回傳保存排序詳細數據,畫面上顯示的數據將會還原為原始排序順序。

在本教學課程中,DropDownList 會隱含地將排序表達式和方向儲存在其檢視狀態。 如果我們使用不同的排序介面,例如 LinkButtons,提供我們必須小心記住跨回傳的排序順序的各種排序選項。 這可藉由將排序參數儲存在頁面檢視狀態、在querystring中包含排序參數,或透過一些其他狀態持續性技術來完成。

本教學課程的未來範例會探索如何在頁面檢視狀態中保存排序詳細數據。

步驟 5:將排序支援新增至使用預設分頁的 DataList

上述教學課程中, 我們檢查了如何使用DataList實作預設分頁。 讓我們擴充上述範例,以包含排序分頁數據的能力。 首先,開啟資料夾中的 SortingWithDefaultPaging.aspxPagingSortingDataListRepeaterPaging.aspx 頁面。 Paging.aspx從頁面上,按兩下 [來源] 按鈕以檢視頁面的宣告式標記。 複製選取的文字 (請參閱圖 8) ,並將它貼到標記之間的<asp:Content>宣告式標記SortingWithDefaultPaging.aspx中。

將 asp:Content> 標記中的<宣告式標記從 Paging.aspx 複寫至 SortingWithDefaultPaging.aspx

圖 8:將標籤的 <asp:Content> 宣告式標記複 Paging.aspx 寫至 SortingWithDefaultPaging.aspx (按一下即可檢視完整大小的影像)

複製宣告式標記之後,將頁面程式代碼後置類別中的Paging.aspx方法和屬性複製到的程式代碼後置類別。SortingWithDefaultPaging.aspx 接下來,花點時間在瀏覽器中檢視 SortingWithDefaultPaging.aspx 頁面。 它應該會呈現與 相同的功能和外觀 Paging.aspx

增強 ProductsBLL 以包含預設分頁和排序方法

在上一個GetProductsAsPagedDataSource(pageIndex, pageSize)教學課程中,我們在傳回 PagedDataSource 對象的 類別中ProductsBLL建立了 方法。 此 PagedDataSource 物件已填入 所有 透過 BLL 方法 GetProducts()) (的產品,但系結至 DataList 時,只會顯示對應至指定 pageIndexpageSize 輸入參數的記錄。

稍早在本教學課程中,我們新增了排序支援,方法是從 ObjectDataSource s Selecting 事件處理程式指定排序表達式。 當 ObjectDataSource 傳回可以排序的物件時,就像方法所傳回的 GetProducts() 一樣ProductsDataTable,這可正常運作。 不過, PagedDataSource 方法所 GetProductsAsPagedDataSource 傳回的物件不支援其內部數據源的排序。 相反地,我們需要先排序方法 GetProducts() 傳回的結果, 將結果 PagedDataSource放入 中。

若要達成此目的,請在 類別GetProductsSortedAsPagedDataSource(sortExpression, pageIndex, pageSize)ProductsBLL建立新的 方法。 若要排序 ProductsDataTable 方法所傳回的 GetProducts() ,請指定 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,並將此值指派給 SortDefaultView屬性ProductDataTable。 稍後幾行程式代碼會PagedDataSource指派ProductDataTableDefaultView物件 s DataSource。

呼叫 GetProductsSortedAsPagedDataSource 方法並指定 SortExpression 輸入參數的值

GetProductsSortedAsPagedDataSource完成 方法之後,下一個步驟是提供此參數的值。 中的 SortingWithDefaultPaging.aspx ObjectDataSource 目前已設定為呼叫 方法,GetProductsAsPagedDataSource並透過其在集合中指定的SelectParametersQueryStringParameters個輸入參數傳入兩個輸入參數。 這兩QueryStringParameters個表示方法 pageIndexpageSize 參數的來源GetProductsAsPagedDataSource來自 querystring 字段pageIndexpageSize

更新 ObjectDataSource s SelectMethod 屬性,使其叫用新的 GetProductsSortedAsPagedDataSource 方法。 然後,新增 QueryStringParameter ,以便從 querystring 欄位sortExpression存取 sortExpression 輸入參數。 設定為 QueryStringParameterDefaultValue ProductName 。

這些變更之後,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 的值會當做 GetProductsSortedAsPagedDataSourcesortExpression 參數的方法傳入。

根據預設,結果會依 ProductName 排序

圖 9:根據預設,結果會依 ProductName (單擊以檢視完整大小的影像)

如果您手動新增 sortExpression 查詢字串字位,例如 SortingWithDefaultPaging.aspx?sortExpression=CategoryName 結果會依指定的 sortExpression排序。 不過,移至不同數據頁面時,查詢字串中不會包含此參數 sortExpression 。 事實上,按兩下一頁/ 或 [最後一頁] 按鈕可讓我們回到 Paging.aspx! 此外,目前沒有排序介面。 用戶可以變更分頁數據排序順序的唯一方式,就是直接操作 querystring。

建立排序介面

我們必須先更新 RedirectUser 方法,以將用戶傳送至 SortingWithDefaultPaging.aspx (,而不是 Paging.aspx) ,並在 sortExpression querystring中包含值。 我們也應該新增只讀的頁面層級具名 SortExpression 屬性。 此屬性與PageIndex上一個教學課程中建立的 sortExpressionPageSize 屬性類似,如果存在,則會傳回 querystring 欄位的值,否則會傳回預設值 ( 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、一個用於 CategoryName,另一個用於 SupplierName。 新增三個 Button 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,並將 Repeater 新增至頁面,並將其 ID 屬性設定為 Products。 從 Repeater 的智慧標記中,建立名為 ProductsDataSource的新 ObjectDataSource。 設定它以從 ProductsBLL 類別 s GetProductsPaged 方法中選取其數據。

將 ObjectDataSource 設定為使用 ProductsBLL 類別 s GetProductsPaged 方法

圖 12:將 ObjectDataSource 設定為使用 ProductsBLL 類別 s GetProductsPaged 方法, (按兩下即可檢視完整大小的影像)

將 UPDATE、INSERT 和 DELETE 索引標籤中的下拉式清單設定為 ([無) ],然後按兩下 [下一步] 按鈕。 [設定數據源精靈] 現在會提示方法 startRowIndexmaximumRows 輸入參數的來源GetProductsPaged。 實際上,會忽略這些輸入參數。 相反地, startRowIndexmaximumRows 值會透過 Arguments ObjectDataSource 事件處理程式 Selecting 中的 屬性傳入,就像我們在本教學課程的第一個示範中指定 sortExpression 的方式一樣。 因此,將精靈中的參數來源下拉式清單保留為 [無]。

將 [參數來源] 保留為 [無]

圖 13:將 [參數來源] 保留為 [無], (按兩下即可檢視完整大小的影像)

注意

請勿將 ObjectDataSource s EnablePaging 屬性設定為 true。 這會導致 ObjectDataSource 自動將自己的 startRowIndexmaximumRows 參數包含在 SelectMethod 現有的參數清單中。 當EnablePaging將自定義分頁數據系結至 GridView、DetailsView 或 FormView 控件時,屬性很有用,因為這些控件預期只有屬性為 trueEnablePaging,才能從 ObjectDataSource 取得特定行為。 由於我們必須手動新增 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排序結果。

為了允許使用者逐步執行頁面,我們必須追蹤開始數據列索引和最大數據列,並記住這些跨回傳的值。 在預設分頁範例中,我們使用 querystring 字段來保存這些值;針對此示範,讓我們將此資訊保存在頁面的檢視狀態中。 建立下列兩個屬性:

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

接下來,更新 Select 事件處理程式中的程式代碼,使其使用 StartRowIndexMaximumRows 屬性,而不是硬式編碼值為 0 和 5:

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

此時,我們的頁面仍然只會顯示前五筆記錄。 不過,有了這些屬性,我們就可以開始建立分頁介面。

新增分頁介面

讓我們使用預設分頁範例中使用的相同 First、Previous、Next、Last 分頁介面,包括顯示所檢視數據頁面的卷標 Web 控件,以及有多少總頁面存在。 在重複項下方新增四個 Button 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>

接下來,建立 Click 四個 Button 的事件處理程式。 按兩下其中一個按鈕時,我們需要更新 StartRowIndex 並將數據重新系結至重複項。 First、Previous 和 Next 按鈕的程式代碼夠簡單,但對於 [最後一個] 按鈕,我們該如何判斷最後一頁數據的起始數據列索引? 若要計算此索引,以及能夠判斷是否應該啟用 [下一頁] 和 [最後一頁] 按鈕,我們必須知道總記錄的分頁次數。 我們可以藉由呼叫 ProductsBLL 類別 s 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 s GetProductsPagedAndSorted 方法的 startRowIndexmaximumRows 輸入參數 GetProductsPaged與 相同,但允許額外的 sortExpression 輸入參數。 若要從 SortingWithCustomPaging.aspx使用 GetProductsPagedAndSorted 方法,我們必須執行下列步驟:

  1. 將 ObjectDataSource 的 SelectMethod 屬性從 GetProductsPaged 變更為 GetProductsPagedAndSorted
  2. sortExpressionParameter 物件新增至 ObjectDataSource s SelectParameters 集合。
  3. 建立私人頁面層級 SortExpression 屬性,以透過頁面檢視狀態跨回傳保存其值。
  4. 更新 ObjectDataSource s Selecting 事件處理程式,以指派 ObjectDataSource s sortExpression 參數頁面層級 SortExpression 屬性的值。
  5. 建立排序介面。

從更新 ObjectDataSource s SelectMethod 屬性並新增 sortExpressionParameter 開始。 請確定 sortExpressionParameterType 屬性已設定為 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;

剩下的一切都是實作排序介面。 如同我們在上一個範例中所做的一樣,讓我們使用三個 Button 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" />

建立 Click 這三個 Button 控件的事件處理程式。 在事件處理程式中,將 重 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:依類別排序的最後一頁數據會顯示 (按兩下即可檢視完整大小的影像)

注意

在先前的範例中,依供應商 SupplierName 排序時,會當做排序表示式使用。 不過,針對自定義分頁實作,我們需要使用 CompanyName。 這是因為負責實作自定義分頁 GetProductsPagedAndSorted 的預存程式會將排序表達式傳遞至 ROW_NUMBER() 關鍵詞,關鍵詞 ROW_NUMBER() 需要實際的數據行名稱,而不是別名。 因此,我們必須使用 CompanyName (數據表中的數據Suppliers行名稱) ,而不是用於排序表達式之查詢 (SupplierName) 中使用的SELECT別名。

摘要

DataList 和 Repeater 都無法提供內建排序支援,但使用一些程式代碼和自定義排序介面,即可新增這類功能。 實作排序,但無法分頁時,可以透過 DataSourceSelectArguments 傳遞至 ObjectDataSource s Select 方法的物件來指定排序運算式。 這個 DataSourceSelectArguments 物件 s SortExpression 屬性可以在 ObjectDataSource 的 Selecting 事件處理程式中指派。

若要將排序功能新增至已經提供分頁支援的 DataList 或 Repeater,最簡單的方法是自定義商業規則層,以包含接受排序表達式的方法。 這項資訊接著可以透過 ObjectDataSource s SelectParameters中的參數傳入。

本教學課程會使用 DataList 和 Repeater 控件完成分頁和排序的檢查。 我們的下一個和最後一個教學課程將探討如何將 Button Web 控件新增至 DataList 和 Repeater 的範本,以便根據每個專案提供一些自定義的使用者起始功能。

快樂的程序設計!

關於作者

Scott Mitchell 是 1998 年以來,1998 年與 Microsoft Web 技術合作的 篇 ASP/ASP.NET 書籍和 4GuysFromRolla.com 作者。 Scott 是獨立的顧問、訓練者和作者。 他的最新書籍是 Sams 在 24 小時內自行 ASP.NET 2.0。 您可以透過mitchell@4GuysFromRolla.com部落格連到,也可以透過其部落格來存取,網址為 http://ScottOnWriting.NET

特別感謝

本教學課程系列是由許多實用的檢閱者所檢閱。 本教學課程的首席檢閱者是 David Suru。 想要檢閱即將推出的 MSDN 文章嗎? 如果是,請將一行放在 mitchell@4GuysFromRolla.com。