接著上篇介紹,上篇已經通過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 neededFirstPage.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有所協助。