分頁和排序報告資料 (C#)
演講者:Scott Mitchell
分頁和排序是在線上應用程式中顯示資料時兩個非常常見的功能。 在這個教學課程中,我們將首次探討如何為報告新增排序和分頁,並在未來的教學課程中進一步擴展這些內容。
簡介
分頁和排序是在線上應用程式中顯示資料時兩個非常常見的功能。 例如,當在在線書店中搜尋 ASP.NET 書籍時,可能會有數百本這樣的書籍,但顯示搜尋結果的報告每頁僅列出十個匹配項。 此外,結果還可以按標題、價格、頁數、作者姓名等進行排序。 在過去的 23 個教學課程中,我們探討了如何建構各種報告,包括允許新增、編輯和刪除資料的介面,但我們尚未討論如何對資料進行排序,而唯一的分頁範例是使用 DetailsView 和 FormView 控制項。
在本教學課程中,我們將了解如何為報告新增排序和分頁,這可以通過簡單地勾選幾個復選框來完成。 不幸的是,這種簡單的實現存在一些缺點:排序介面還有改進的空間,而分頁例程並不適合有效率地處理大型結果集。 未來的教學課程將探討如何克服開箱即用的分頁和排序解決方案的限制。
步驟1:新增分頁和排序教學課程網頁
在開始本教學課程之前,我們先花點時間加入本教學課程和接下來的三個教學課程所需的 ASP.NET 頁面。 先在專案中建立一個名為 PagingAndSorting
的新資料夾。 接下來,將以下五個 ASP.NET 頁面新增至此資料夾,並將所有頁面設定為使用主版頁面 Site.master
:
Default.aspx
SimplePagingSorting.aspx
EfficientPaging.aspx
SortParameter.aspx
CustomSortingUI.aspx
圖 1:建立 PagingAndSorting 資料夾並新增教學課程 ASP.NET 頁面
接下來,開啟 Default.aspx
頁面並將 SectionLevelTutorialListing.ascx
使用者控制項從 UserControls
資料夾拖曳到設計介面上。 這個使用者控制項是我們在「主版頁和網站導覽/瀏覽」教學課程中建立的,它會列舉網站地圖並,以項目符號清單的形式顯示目前區段中的這些教學課程。
圖 2:將 SectionLevelTutorialListing.ascx 使用者控制項加入 Default.aspx
為了讓項目符號清單顯示我們將要建立的分頁和排序教學課程,我們需要將它們新增到網站地圖中。 開啟 Web.sitemap
檔案並在編輯、插入和刪除網站地圖節點標記後新增以下標記:
<siteMapNode title="Paging and Sorting" url="~/PagingAndSorting/Default.aspx"
description="Samples of Reports that Provide Paging and Sorting Capabilities">
<siteMapNode url="~/PagingAndSorting/SimplePagingSorting.aspx"
title="Simple Paging & Sorting Examples"
description="Examines how to add simple paging and sorting support." />
<siteMapNode url="~/PagingAndSorting/EfficientPaging.aspx"
title="Efficiently Paging Through Large Result Sets"
description="Learn how to efficiently page through large result sets." />
<siteMapNode url="~/PagingAndSorting/SortParameter.aspx"
title="Sorting Data at the BLL or DAL"
description="Illustrates how to perform sorting logic in the Business Logic
Layer or Data Access Layer." />
<siteMapNode url="~/PagingAndSorting/CustomSortingUI.aspx"
title="Customizing the Sorting User Interface"
description="Learn how to customize and improve the sorting user interface." />
</siteMapNode>
圖 3:更新網站地圖以包含新的 ASP.NET 頁面
步驟 2:在 GridView 中顯示產品訊息
在我們實際實作分頁和排序功能之前,我們先建立一個標準的不可排序、不可分頁的 GridView 來列出產品資訊。 這是我們之前在本教學課程系列中多次完成的任務,因此這些步驟應該很熟悉。 首先打開 SimplePagingSorting.aspx
頁面,並將 GridView 控制項從工具箱拖曳到設計器上,將其 ID
屬性設為 Products
。 接下來,建立一個新的 ObjectDataSource,它使用 ProductsBLL 類別的 GetProducts()
方法傳回所有產品資訊。
圖 4:使用 GetProducts() 方法檢索有關所有產品的資訊
由於此報告是唯讀報告,因此無需將 ObjectDataSource 的 Insert()
、Update()
或 Delete()
方法對應到相應的 ProductsBLL
方法;因此,從 UPDATE、INSERT 和 DELETE 標籤的下拉式清單中選擇 (無)。
圖 5:在 UPDATE、INSERT 和 DELETE 標籤的下拉式清單中選擇 (無) 選項
接下來,我們自訂 GridView 的欄位,以便僅顯示產品名稱、供應商、類別、價格和停產狀態。 此外,您可以隨意進行任何欄位層級的格式更改,例如調整 HeaderText
屬性或將價格格式化為貨幣。 進行這些變更後,您的 GridView 宣告性標記應類似於以下內容:
<asp:GridView ID="Products" runat="server" AutoGenerateColumns="False"
DataKeyNames="ProductID" DataSourceID="ObjectDataSource1"
EnableViewState="False">
<Columns>
<asp:BoundField DataField="ProductName" HeaderText="Product"
SortExpression="ProductName" />
<asp:BoundField DataField="CategoryName" HeaderText="Category"
ReadOnly="True" SortExpression="CategoryName" />
<asp:BoundField DataField="SupplierName" HeaderText="Supplier"
ReadOnly="True" SortExpression="SupplierName" />
<asp:BoundField DataField="UnitPrice" HeaderText="Price"
SortExpression="UnitPrice" DataFormatString="{0:C}"
HtmlEncode="False" />
<asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued"
SortExpression="Discontinued" />
</Columns>
</asp:GridView>
圖 6 顯示了我們迄今為止透過瀏覽器查看的進度。 請注意,該頁面在一個畫面中列出了所有產品,顯示每個產品的名稱、類別、供應商、價格和停產狀態。
圖 6:列出每個產品 (點擊查看完整圖片)
步驟 3:新增分頁支援
在一個螢幕上列出所有產品可能會導致使用者仔細閱讀資料時出現資訊過載。 為了使結果更易於管理,我們可以將資料分解為更小的資料頁,並允許使用者一次一頁地瀏覽資料。 要完成此操作,只需選取 GridView s 智慧標籤中的啟用分頁核取方塊 (這會將 GridView s 的 AllowPaging
屬性設為 true
)。
圖 7:選取啟用分頁核取方塊以新增分頁支援 (點擊查看完整圖片)
啟用分頁會限制每頁顯示的記錄數,並在 GridView 中新增分頁介面。 預設分頁介面 (如圖 7 所示) 是一系列頁碼,可讓使用者快速從一頁資料導覽/瀏覽到另一頁。 這個分頁介面應該看起來很熟悉,因為在過去的教學課程中,我們在為 DetailsView 和 FormView 控制項新增分頁支援時也見過它。
DetailsView 和 FormView 控制項每頁僅顯示一筆記錄。 然而,GridView 會查閱其 PageSize
屬性來決定每頁顯示多少筆記錄 (此屬性預設值為 10)。
可以使用下列屬性自訂此 GridView、DetailsView 和 FormView 的分頁介面:
PagerStyle
指定分頁介面的樣式資訊;可以設定如BackColor
、ForeColor
、CssClass
、HorizontalAlign
等設定。PagerSettings
包含許多屬性,可以自訂分頁介面的功能;PageButtonCount
指示在分頁介面中顯示的最大數字頁碼數量 (預設為 10) ;Mode
屬性指示分頁介面的運作方式,並可以設定為:NextPrevious
顯示「下一頁」和「上一頁」按鈕,可讓使用者每次向前或向後翻頁一次NextPreviousFirstLast
除了「下一頁」和「上一頁」按鈕外,還包括「第一頁」和「最後一頁」按鈕,讓使用者快速移動到資料的第一頁或最後一頁Numeric
顯示一系列頁碼,可讓使用者立即跳到任何頁面NumericFirstLast
除了顯示頁碼外,還包含「首頁」和「末頁」按鈕,讓使用者能快速跳移至資料的第一頁或最後一頁;當所有頁碼無法顯示時,只有在這種情況下才會顯示首頁和末頁按鈕
此外,GridView、DetailsView 和 FormView 都提供 PageIndex
和 PageCount
屬性,分別指示目前正在查看的頁面和資料的總頁數。 PageIndex
屬性的索引從 0 開始,這表示查看第一頁資料時,PageIndex
將等於 0。 另一方面,PageCount
從 1 開始計數,這代表 PageIndex
僅限於 0 和 PageCount - 1
之間的值。
讓我們花點時間改進 GridView 分頁介面的預設外觀。 具體來說,讓分頁介面右對齊,背景為淺灰色。 讓我們在 Styles.css
中建立一個名為 PagerRowStyle
的 CSS 類別,然後透過我們的主題分配 PagerStyle
的 CssClass
屬性,而不是直接透過 GridView 的 PagerStyle
屬性來設定這些屬性。 先開啟 Styles.css
,並加入以下 CSS 類別定義:
.PagerRowStyle
{
background-color: #ddd;
text-align: right;
}
接下來,打開 App_Themes
資料夾內 DataWebControls
資料夾中的 GridView.skin
檔案。 正如我們在「主版頁面和網站導覽」教學課程中所討論的,外觀檔案可用於指定 Web 控制項的預設屬性值。 因此,請擴充現有設定以包含將 PagerStyle
的 CssClass
屬性設為 PagerRowStyle
。 另外,讓我們將分頁介面設定為使用 NumericFirstLast
分頁介面最多顯示五個數字分頁按鈕。
<asp:GridView runat="server" CssClass="DataWebControlStyle">
<AlternatingRowStyle CssClass="AlternatingRowStyle" />
<RowStyle CssClass="RowStyle" />
<HeaderStyle CssClass="HeaderStyle" />
<FooterStyle CssClass="FooterStyle" />
<SelectedRowStyle CssClass="SelectedRowStyle" />
<PagerStyle CssClass="PagerRowStyle" />
<PagerSettings Mode="NumericFirstLast" PageButtonCount="5" />
</asp:GridView>
分頁使用者體驗
圖 8 顯示了在選取 GridView 的啟用分頁核取方塊,並透過 GridView.skin
檔案進行 PagerStyle
和 PagerSettings
設定後透過瀏覽器存取的網頁。 請注意,目前僅顯示十筆記錄,且分頁介面顯示我們正在查看資料的第一頁。
圖 8:啟用分頁後,一次只會顯示記錄的子集 (點擊查看完整圖片)
當使用者點擊分頁介面中的頁碼之一時,會發生回傳,並且頁面會重新載入,顯示所請求頁面的記錄。 圖 9 顯示了選擇查看最後一頁資料後的結果。 請注意,最後一頁只有一筆記錄;這是因為總共有 81 筆記錄,因此有 8 頁,每頁 10 筆記錄,再加上一頁只有一筆記錄。
圖 9:點擊頁碼會導致回傳並顯示適當的記錄子集 (點擊查看完整圖片)
分頁的伺服器端工作流程
當終端使用者點擊分頁介面中的按鈕時,會發生回傳並開始以下伺服器端工作流程:
- 觸發 GridView (或 DetailsView 或 FormView)
PageIndexChanging
事件 - ObjectDataSource 重新請求 BLL 中的所有資料; GridView 的
PageIndex
和PageSize
屬性值用於決定從 BLL 傳回的哪些記錄需要顯示在 GridView 中 - 觸發 GridView 的
PageIndexChanged
事件
在步驟 2 中,ObjectDataSource 重新請求其資料來源中的所有資料。 這種類型的分頁通常稱為預設分頁,因為它是將 AllowPaging
屬性設為 true
時,預設使用的分頁行為。 使用預設分頁,資料 Web 控制項會直接檢索每個資料頁的所有記錄,即使實際上只有記錄的子集呈現到傳送到瀏覽器的 HTML 中。 除非資料庫資料由商務邏輯層 (BLL) 或 ObjectDataSource 快取,否則對於大量結果集或具有許多並行使用者的網頁應用程式,預設分頁是不可行的。
在下一個教學課程中,我們將研究如何實作自訂分頁。 透過自訂分頁,您可以特別指示 ObjectDataSource 僅擷取所要求的資料頁所需的精確記錄集。 可以想像,自訂分頁大大提高了在大型結果集中進行分頁的效率。
注意
雖然預設分頁不適合有大量分頁的結果集或具有許多同時使用者的網站,但要意識到自訂分頁需要更多的更改和工作來實現,並且不像選中復選框那麼簡單 (預設分頁也是如此)。 因此,對於小型、流量較低的網站,或在處理相對較小的結果集時,預設分頁可能是理想的選擇,因為其實現起來更簡單且更快速。
例如,如果我們知道資料庫中永遠不會有超過100個產品,那麼自訂分頁所帶來的微小效能提升可能會因實現所需的努力而被抵消。 然而,如果我們將來可能會有數千或數萬個產品,那麼不實作自訂分頁將會大大妨礙我們應用程式的可擴展性。
步驟 4:自訂分頁體驗
資料網頁控制項提供了多個屬性,可以用來增強使用者的分頁體驗。 例如,PageCount
屬性指示總共有多少頁,而 PageIndex
屬性指示目前正在造訪的頁面,並且可以設定為將使用者快速移動到特定頁面。 為了說明如何使用這些屬性來改善使用者的分頁體驗,讓我們在頁面上新增一個標籤 (Label) 控制項,以告知使用者他們目前正在造訪的頁面,並新增一個下拉式清單 (DropDownList) 控制項,讓他們可以快速跳移至任何特定頁面。
首先,在頁面上新增一個 Label Web 控制項,將其 ID
屬性設為 PagingInformation
,然後清除其 Text
屬性。 接下來,為 GridView 的 DataBound
事件建立一個事件處理常式並新增以下程式碼:
protected void Products_DataBound(object sender, EventArgs e)
{
PagingInformation.Text = string.Format("You are viewing page {0} of {1}...",
Products.PageIndex + 1, Products.PageCount);
}
這個事件處理常式將標籤PagingInformation
標籤的 Text
屬性指派給一則訊息,通知使用者目前正在造訪的頁面是 Products.PageIndex + 1
,總共有多少頁 Products.PageCount
(我們在 Products.PageIndex
屬性上加 1,因為 PageIndex
是從 0 開始編制索引的)。 我選擇在 DataBound
事件處理常式中分配此標籤的 Text
屬性,而不是在 PageIndexChanged
事件處理常式中分配,因為每次將資料繫結到 GridView 時都會觸發 DataBound
事件,而 PageIndexChanged
事件處理常式僅在頁面索引更改時觸發。 當 GridView 最初在第一個頁面存取時進行資料繫結時,不會觸發PageIndexChanging
事件 (而是觸發 DataBound
事件)。
新增此內容後,現在會向使用者顯示一則訊息,指示他們正在造訪哪個頁面以及總共有多少頁資料。
圖 10:顯示目前頁碼和總頁數 (點擊查看完整圖片)
除了 Label 控制項之外,我們還新增一個 DropDownList 控制項,該控制項會列出 GridView 中的頁碼,並選擇目前檢視的頁面。 這裡的想法是,使用者只需從 DropDownList 中選擇新頁面索引即可快速從目前頁面跳到另一個頁面。 首先將 DropDownList 新增至設計器,將其 ID
屬性設為 PageList
並從其智慧標籤中選取啟用 AutoPostBack 選項。
接下來,傳回 DataBound
事件處理常式並新增以下程式碼:
// Clear out all of the items in the DropDownList
PageList.Items.Clear();
// Add a ListItem for each page
for (int i = 0; i < Products.PageCount; i++)
{
// Add the new ListItem
ListItem pageListItem = new ListItem(string.Concat("Page ", i + 1), i.ToString());
PageList.Items.Add(pageListItem);
// select the current item, if needed
if (i == Products.PageIndex)
pageListItem.Selected = true;
}
此程式碼會先清除 PageList
DropDownList 中的項目。 這似乎是多餘的,因為人們不會期望頁數發生變化,但其他使用者可能會同時使用該系統,在 Products
表中新增或刪除記錄。 此類插入或刪除可能會改變資料頁數。
接下來,我們需要再次建立頁碼,並預設選擇對應到目前 GridView PageIndex
的頁碼。 我們透過從 0 到 PageCount - 1
的循環來完成此操作,在每次迭代中新增一個新的 ListItem
,並在目前迭代索引等於 GridView 的 PageIndex
屬性時將其 Selected
屬性設為 True。
最後,我們需要為 DropDownList 的 SelectedIndexChanged
事件建立一個事件處理常式,每次使用者從清單中選擇不同的項目時都會觸發該事件處理常式。 要建立此事件處理常式,只需在設計器中按兩下 DropDownList,然後新增以下程式碼:
protected void PageList_SelectedIndexChanged(object sender, EventArgs e)
{
// Jump to the specified page
Products.PageIndex = Convert.ToInt32(PageList.SelectedValue);
}
如圖 11 所示,僅更改 GridView 的 PageIndex
屬性就會導致資料重新繫結到 GridView。 在 GridView 的 DataBound
事件處理常式中,選擇適當的 DropDownList ListItem
。
圖 11:選擇第 6 頁下拉式清單項目時,使用者會自動前往第六頁 (點擊查看完整圖片)
步驟 5:新增雙向排序支援
新增雙向排序支援就像新增分頁支援一樣簡單,只需從 GridView 的智慧標籤 (將 GridView 的 AllowSorting
屬性設為 true
) 中選取「啟用排序」選項即可。 這會將 GridView 欄位的每個標題呈現為 LinkButton,當點擊該按鈕時,會導致回傳並傳回按按一下的列按升序排序的資料。 再次按一下相同標題 LinkButton 會按降序對資料重新排序。
注意
如果您使用自訂資料存取層而不是類型化資料集,則 GridView 的智慧標籤中可能沒有「啟用排序」選項。 只有繫結到本身支援排序的資料來源的 GridView 才可以使用此核取方塊。 類型化資料集提供開箱即用的排序支援,因為 ADO.NET DataTable 提供了 Sort
方法,在呼叫該方法時,使用指定的條件對 DataTable 的 DataRow 進行排序。
如果您的 DAL 不傳回本身支援排序的物件,您將需要設定 ObjectDataSource 以將排序資訊傳遞到商務邏輯層,商務邏輯層可以對資料進行排序或讓 DAL 對資料進行排序。 我們將在以後的教學課程中探討如何在商務邏輯和資料存取層對資料進行排序。
排序 LinkButton 呈現為 HTML 超連結,其目前顏色 (未造訪的連結為藍色,已造訪的連結為深紅色) 與標題行的背景顏色衝突。 相反,讓所有標題行連結顯示為白色,無論它們是否被造訪過。 這可以透過將以下內容新增到 Styles.css
類別中來完成:
.HeaderStyle a, .HeaderStyle a:visited
{
color: White;
}
此語法指示在使用 HeaderStyle 類別的元素內顯示這些超連結時使用白色文字。
在新增 CSS 後,透過瀏覽器造訪該頁面時,您的畫面應類似於圖 12。 特別是,圖 12 顯示了點擊 Price 欄位的標題連結後的結果。
圖 12:結果已按單價升序排列 (點擊查看完整圖片)
檢查排序工作流程
所有 GridView 欄位 (BoundField、CheckBoxField、TemplateField 等) 都有一個 SortExpression
屬性,該屬性指示點擊該欄位的排序標題連結時會套用於對資料進行排序的運算式。 GridView 還有 SortExpression
屬性。 當您按一下排序標題 LinkButton 時,GridView 會將該欄位的 SortExpression
值指派給其 SortExpression
屬性。 接下來,從 ObjectDataSource 重新檢索資料並根據 GridView 的 SortExpression
屬性進行排序。 以下清單詳細介紹了終端使用者在 GridView 中對資料進行排序時所發生的步驟順序:
- 觸發 GridView 的 排序事件
- GridView 的
SortExpression
屬性設定為點選排序標頭 LinkButton 欄位的SortExpression
- ObjectDataSource 重新從 BLL 檢索所有資料,然後使用 GridView 的
SortExpression
對資料進行排序 - GridView 的
PageIndex
屬性重設為 0,表示排序時使用者返回到資料的第一頁 (假設已實作分頁支援) - 觸發 GridView 的
Sorted
事件
與預設分頁一樣,預設排序選項會重新檢索 BLL 中的所有記錄。 當使用不含分頁的排序或使用預設分頁的排序時,無法避免這種效能損失 (除非快取資料庫資料)。 但是,正如我們將在以後的教學課程中看到的,使用自訂分頁時可以有效地對資料進行排序。
透過 GridView 智慧標籤中的下拉式清單將 ObjectDataSource 繫結到 GridView 時,每個 GridView 欄位都會自動將其 SortExpression
屬性指派給 ProductsRow
類別中資料欄位的名稱。 例如,ProductName
BoundField 的 SortExpression
設定為 ProductName
,如以下宣告性標記所示:
<asp:BoundField DataField="ProductName" HeaderText="Product"
SortExpression="ProductName" />
可以透過清除欄位的 SortExpression
屬性 (將其指派給空字串) 來設定欄位,使其不可排序。 為了說明這一點,假設我們不想讓客戶以價格對我們的產品進行排序。 可以從宣告性標記或透過「欄位」對話方塊 (可透過點選 GridView 智慧標籤中的「編輯列」連結來存取) 刪除 UnitPrice
BoundField 的 SortExpression
屬性。
圖 13:結果已以單價升序排列
刪除 UnitPrice
BoundField 的 SortExpression
屬性後,標題將呈現為文字而不是連結,從而阻止使用者按價格對資料進行排序。
圖 14:透過刪除 SortExpression 屬性,使用者無法再依價格對產品進行排序 (點擊查看完整圖片)
以程式設計方式對 GridView 進行排序
您也可以使用 GridView 的 Sort
方法以程式設計方式對 GridView 的內容進行排序。 只需將要排序的 SortExpression
值與 SortDirection
(Ascending
或 Descending
) 一起傳入,GridView 的資料就會重新排序。
想像一下,我們關閉 UnitPrice
排序的原因是因為我們擔心顧客只會購買價格最低的產品。 但是,我們希望鼓勵他們購買最昂貴的產品,因此我們希望他們能夠按價格對產品進行排序,但只能從最昂貴的價格到最便宜的價格排序。
要完成此操作,請在頁面上新增一個 Button Web 控制項,將其 ID
屬性設為 SortPriceDescending
,並將其 Text
屬性設為 Sort by Price。 接下來,透過按兩下設計器中的 Button 控制項為 Button 的 Click
事件建立事件處理常式。 將以下程式碼新增至該事件處理常式:
protected void SortPriceDescending_Click(object sender, EventArgs e)
{
// Sort by UnitPrice in descending order
Products.Sort("UnitPrice", SortDirection.Descending);
}
點擊此按鈕,使用者將返回第一頁,其中的產品按價格從最貴到最便宜排序 (請參閱圖 15)。
圖 15:點擊按鈕按從最貴到最便宜的順序對產品進行排序 (點擊查看完整圖片)
摘要
在本教學課程中,我們了解如何實現預設的分頁和排序功能,這兩個功能都像選取核取方塊一樣簡單! 當使用者對資料進行排序或分頁時,會出現類似的工作流程:
- 隨後發生回傳
- 資料 Web 控制項的預級事件觸發 (
PageIndexChanging
或Sorting
) - 所有資料均由 ObjectDataSource 重新檢索
- 資料 Web 控制項的後級事件觸發 (
PageIndexChanged
或Sorted
)
雖然實現基本的分頁和排序很容易,但必須付出更多的努力來利用更有效率的自訂分頁或進一步增強分頁或排序介面。 未來的教學課程將探討這些主題。
快樂程式!
關於作者
Scott Mitchell 是七本 ASP/ASP.NET 書籍的作者,也是 4GuysFromRolla.com 的創辦人,自 1998 年以來一直在使用 Microsoft 網路技術。 Scott 是一位獨立顧問、培訓師兼作家。 他的最新著作是 Sams Teach Yourself ASP.NET 2.0 in 24 Hours。 您可以透過電子郵件聯繫他:mitchell@4GuysFromRolla.com。 或者透過他的部落格與他聯繫,網址為 http://ScottOnWriting.NET。