接著上篇介紹,上篇已經通過DropDownList簡單實現了排序的功能,下面讓我們看看帶有分頁的排序該怎麼做。
第五步: 為使用默認分頁的DataList添加排序的支持
打開PagingSortingDataListRepeater文件夾裡的SortingWithDefaultPaging.aspx和Paging.aspx 頁。在Paging.aspx 頁裡查看源文件。將圖8裡選擇的文本復制下來,然後粘貼到SortingWithDefaultPaging.aspx 頁裡的<asp:Content> 標簽內。
圖 8: 復制粘貼代碼
然後將Paging.aspx頁後台代碼裡的屬性和方法也粘貼到SortingWithDefaultPaging.aspx頁後台代碼裡。現在浏覽SortingWithDefaultPaging.aspx頁,它現在應該有和Paging.aspx頁一樣的外觀和功能。
在ProductsBLL 裡添加默認的分頁和排序方法
前面一章裡我們在ProductsBLL類裡創建了一個GetProductsAsPagedDataSource(pageIndex, pageSize)方法,它返回一個PagedDataSource對象。這個對象通過BLL的GetProducts()方法獲取所有的product,然而綁定到DataList的只是那些和輸入參數pageIndex 和 pageSize 相關的記錄。
本章前面我們已經通過在ObjectDataSource的 Selecting event handler裡指定sort expression來添加了排序功能。當ObjectDataSource返回可排序對象時這個方法運轉的很好,比如GetProducts()方法返回的ProductsDataTable。然而GetProductsAsPagedDataSource方法返回的PagedDataSource對象並不支持對它內部數據的排序,因此我們需要在將數據放入PagedDataSource前對GetProducts()方法返回的記錄進行排序。
在ProductsBLL類裡建一個GetProductsSortedAsPagedDataSource(sortExpression, pageIndex, pageSize)方法。指定GetProducts() 方法返回的ProductsDataTable的default 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參數,將它的值賦給ProductDataTable的 DefaultView的Sort屬性。並將ProductDataTable的DefaultView賦給PagedDataSource對象的DataSource。
調用 GetProductsSortedAsPagedDataSource 方法並指定輸入參數SortExpression的值
完成這些後,下一步需要提供參數值。SortingWithDefaultPaging.aspx頁的ObjectDataSource現在被配置用來調用GetProductsAsPagedDataSource方法並通過兩個QueryStringParameters來傳遞參數,這些參數在SelectParameters集合裡已經指定了。這兩個QueryStringParameters參數表示GetProductsAsPagedDataSource方法的pageIndex 和 pageSize 參數從querystring裡獲取。
修改ObjectDataSource的SelectMethod屬性,讓它調用GetProductsSortedAsPagedDataSource方法。然後添加一個新的QueryStringParameter來讓sortExpression 參數通過querystring的sortExpression字段獲取。將QueryStringParameter的默認值設為“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頁會按照product name的字母順序排序。見圖9。這是因為GetProductsSortedAsPagedDataSource方法的sortExpression 參數的默認值為“ProductName”。
圖 9: 默認的按照 ProductName 排序
如果你手動添加一個sortExpression querystring字段–比如SortingWithDefaultPaging.aspx?sortExpression=CategoryName –那麼結果會以指定的sortExpression來排序。然而在轉到另外一個頁時這個sortExpression參數並沒有包含在querystring裡。實際上當點上或者下一頁時我們會返回Paging.aspx。而且當前並沒有排序界面。用戶可以改變數據排序的唯一方法是直接操作querystring。
創建排序界面
我們首先要修改RedirectUser方法來將用戶重定向到SortingWithDefaultPaging.aspx頁(而不是Paging.aspx),並將sortExpression的值包含到querystring裡。我們還應該添加一個只讀的SortExpression屬性。這個屬性和前面一章裡創建的PageIndex 和 PageSize屬性相似,在sortExpression querystring字段存在時返回它的值,否則的話使用默認值“ProductName”。
現在的RedirectUser方法只接收一個參數–顯示的頁的index。然而可能有些時候我們需要使用排序表達式將用戶重定向到特定數據的頁。我們將馬上來為這個頁創建排序界面,它將包含一些button來為指定的列排序。當其中一個button被點擊時,我們需要傳入合適的排序表達式的值來重定向用戶。為了提供這個功能,創建兩個RedirectUser方法。第一個接收page的index,第二個接收page index和sort expression(排序表達式)。
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來創建了一個排序界面。我們將在這個例子裡使用3個button(它們位於DataList上方)–一個表示為ProductName排序,一個為CategoryName,一個為SupplierName。添加三個button並設置它們的ID和Text。
<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>
然後為每個button創建一個Click event handler。這個事件處理將調用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"); }
第一次浏覽該頁時,數據將按照product name的字母順序排序(見圖9)。點Next button來浏覽第二頁,然後點“Sort by Category” button。這樣將讓頁返回到第一頁,並按照category name來排序,見圖10。同樣的,點“Sort by Supplier” button會將數據按照supplier排序,並返回到第一頁。當數據分頁時排序的選擇會被記下來。圖11是按照category排序並浏覽第十三頁的樣子。
圖 10: Products 按照Category排序
圖 11: 分頁時會記下Sort Expression
第六步: Repeater的自定義分頁
第五步裡的DataList示例使用默認的分頁技術。當大數據量時,我們需要使用自定義分頁。回到Efficiently Paging Through Large Amounts of Data 和 Sorting Custom Paged Data 裡,我們學習了默認和自定義這兩種分頁方式的不同,並在BLL裡為自定義分頁和對自定義分頁數據的排序創建了方法。在這兩章裡我們在ProductsBLL裡添加了下面三個方法:
GetProductsPaged(startRowIndex, maximumRows) – 返回從startRowIndex開始並不超過maximumRows 的特定記錄集。
GetProductsPagedAndSorted(sortExpression, startRowIndex, maximumRows) – 根據指定的sortExpression 返回特定記錄集。
TotalNumberOfProducts() – 提供Products 表的總記錄數。
這些方法可以用來在DataList或Repeater進行高效的分頁並排序。我們首先創建一個支持自定義分頁的Repeater。然後再添加排序支持。打開PagingSortingDataListRepeater文件夾下的SortingWithCustomPaging.aspx頁,添加一個Repeater,將ID設為Products。從智能標簽裡創建一個名為ProductsDataSource的ObjectDataSource。使用ProductsBLL類的GetProductsPaged方法來配置它的select標簽。
圖 12: 配置 ObjectDataSource
在UPDATE, INSERT, DELETE標簽裡選擇“(None)”,點下一步。現在我們需要為GetProductsPaged方法的startRowIndex 和 maximumRows 參數選擇源。實際上這裡不需要配置。這兩個參數的值會在ObjectDataSource的Selecting event handler裡通過Arguments屬性來指定,就好象我們在本章的第一個例子裡指定sortExpression 一樣。因此,在參數源的下拉列表裡選擇“None”。
圖 13:將參數源設為 “None”
注意:不要將ObjectDataSource的EnablePaging屬性設為true。這樣會讓ObjectDataSource自動的將它的startRowIndex 和 maximumRows 參數包含在SelectMethod的已經存在的參數列表裡。EnablePaging屬性在將自定義分頁數據綁定到GridView, DetailsView, FormView時才有用。由於我們是為DataList 和Repeater手動添加分頁支持,因此將它們設為false(默認的),我們將在ASP.NET頁裡直接實現這些功能。
最後,定義Repeater的ItemTemplate,讓它只顯示product'的name, category, supplier。完成這些後,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>
現在浏覽該頁,注意沒有返回任何記錄。這是因為我們還沒有指定startRowIndex 和 maximumRows 參數的值。為了指定這些值,為ObjectDataSource的Selecting event創建一個event handler,並將參數值硬編碼的設置為0和5。
protected void ProductsDataSource_Selecting (object sender, ObjectDataSourceSelectingEventArgs e) { e.InputParameters["startRowIndex"] = 0; e.InputParameters["maximumRows"] = 5; }
現在浏覽頁面時會顯示前5條product記錄。
圖 14: 顯示前5條product
注意:圖14列出的products以product name排序是因為自定義分頁使用的GetProductsPaged存儲過程返回的結果是以ProductName排序。
為了讓用戶可以翻頁,我們需要在postback過程中記下start row index 和 maximum rows。在默認分頁的例子裡我們用querystring來保存這些值。這個例子裡我們將使用view state。創建下面兩個屬性:
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; } }
然後更新Selecting event handler的代碼,使用StartRowIndex 和 MaximumRows屬性代替硬編碼的0和5。
e.InputParameters["startRowIndex"] = StartRowIndex; e.InputParameters["maximumRows"] = MaximumRows;
現在我們的頁仍然只顯示5條記錄。然而完成這些屬性後,我們已經可以創建分頁界面了。
添加分頁界面
我們還是使用和默認分頁例子裡一樣的First, Previous, Next, Last分頁界面,並包含顯示當前是哪頁和總頁數的label。在Repeater下面添加4個button和1一個label。
<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>
然後為4個button創建Click event handlers。當其中一個button被點時,我們需要修改StartRowIndex並將數據重新綁定到Repeater。First, Previous, 和 Next button的代碼都非常簡單,但是對Last button來說,我們如何判斷最後一頁數據的start row index?為了計算出這個index–和判斷Next 和 Last button是否應該enabled一樣–我們需要知道分頁數據的總數。我們可以調用ProductsBLL類的TotalNumberOfProducts()方法來獲取這個總數。我們來創建一個只讀的屬性,名為TotalRowCount,它返回TotalNumberOfProducts()方法的結果。
private int TotalRowCount { get { // Return the value from the TotalNumberOfProducts() method ProductsBLL productsAPI = new ProductsBLL(); return productsAPI.TotalNumberOfProducts(); } }
有了這個屬性後我們現在可以獲取最後一頁的start row index。它可以通過TotalRowCount除以MaximumRows的結果的整數部分然後乘以MaximumRows來得到。我們現在可以為4個分頁界面的button來寫Click event handlers。
最後,在浏覽第一頁時需要禁用First 和 Previous buttons,在浏覽最後一頁時要禁用Next 和 Last buttons。在ObjectDataSource的Selecting event handler裡添加以下代碼:
// 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;
完成這些後,浏覽該頁。見圖15。當第一次浏覽該頁時,First 和 Previous buttons被禁用。點Next會顯示第二頁的數據。點Last會顯示最後一頁的數據(見圖16和17)。當浏覽最後一頁時,Next 和 Last buttons被禁用。
圖 15: 浏覽第一頁時 Previous 和 Last Buttons 被禁用
圖 16: 第二頁數據
圖 17: 最後一頁
祝編程快樂!
作者簡介
本系列教程作者 Scott Mitchell,著有六本ASP/ASP.NET方面的書,是4GuysFromRolla.com的創始人,自1998年以來一直應用 微軟Web技術。大家可以點擊查看全部教程《[翻譯]Scott Mitchell 的ASP.NET 2.0數據教程》,希望對大家的學習ASP.NET有所幫助。