巢狀資料 Web 控制項 (VB)
在本教學課程中,我們將探索如何使用巢狀於另一個 Repeater 內的 Repeater。 這些範例將說明如何以宣告方式和以程序設計方式填入內部 Repeater。
簡介
除了靜態 HTML 和數據系結語法之外,範本也可以包含 Web 控件和使用者控制件。 這些 Web 控制項可以透過宣告式、資料系結語法指派其屬性,或在適當的伺服器端事件處理程式中以程式設計方式存取。
藉由在範本中內嵌控件,即可自定義和改善外觀和用戶體驗。 例如,在 GridView 控件教學課程中使用 TemplateFields 中,我們已瞭解如何在 TemplateField 中新增行事歷控件來自定義 GridView 顯示,以顯示員工的雇用日期;在將驗證控件新增至編輯和插入介面和自定義數據修改介面教學課程中,我們已瞭解如何藉由新增驗證控件、TextBoxes、DropDownLists 和其他 Web 控件來自定義編輯和插入介面。
範本也可以包含其他數據 Web 控制件。 也就是說,我們可以有一個 DataList,其中包含另一個 DataList (或 Repeater 或 GridView 或 DetailsView,依此類) 在其範本內。 這類介面的挑戰是將適當的數據系結至內部數據 Web 控件。 有幾種不同的方法可用,範圍從使用 ObjectDataSource 的宣告式選項到程序設計方式。
在本教學課程中,我們將探索如何使用巢狀於另一個 Repeater 內的 Repeater。 外部 Repeater 將會包含資料庫中每個類別的專案,其中顯示類別的名稱和描述。 每個類別項目的內部重複項都會顯示屬於該類別之每個產品的資訊, (請參閱點符清單中的圖 1) 。 我們的範例將說明如何以宣告方式和以程序設計方式填入內部 Repeater。
圖 1:每個類別及其產品都會列示 (按兩下即可檢視完整大小的影像)
步驟 1:建立類別清單
建置使用巢狀數據 Web 控件的頁面時,我發現先設計、建立及測試最外層數據 Web 控件會很有説明,甚至不必擔心內部巢狀控件。 因此,讓我們從逐步解說將重複程式新增至頁面所需的步驟,以列出每個類別的名稱和描述。
從開啟 NestedControls.aspx
資料夾中的頁面 DataListRepeaterBasics
開始,並將 Repeater 控制項新增至頁面,並將其 ID
屬性設定為 CategoryList
。 從 Repeater 的智慧標記中,選擇建立名為 CategoriesDataSource
的新 ObjectDataSource。
圖 2:將 New ObjectDataSource CategoriesDataSource
命名 (按兩下即可檢視完整大小的影像)
設定 ObjectDataSource,使其從 CategoriesBLL
類別 s GetCategories
方法提取其數據。
圖 3:將 ObjectDataSource 設定為使用 CategoriesBLL
類別 s GetCategories
方法, (按兩下即可檢視完整大小的影像)
若要指定 Repeater 的範本內容,我們必須移至 [來源] 檢視,並手動輸入宣告式語法。 新增 , ItemTemplate
其中顯示元素中的 <h4>
類別名稱,以及段落元素中的類別描述 (<p>
) 。 此外,讓我們以水平規則分隔每個類別 (<hr>
) 。 進行這些變更之後,您的頁面應該包含 Repeater 和 ObjectDataSource 的宣告式語法,如下所示:
<asp:Repeater ID="CategoryList" DataSourceID="CategoriesDataSource"
EnableViewState="False" runat="server">
<ItemTemplate>
<h4><%# Eval("CategoryName") %></h4>
<p><%# Eval("Description") %></p>
</ItemTemplate>
<SeparatorTemplate>
<hr />
</SeparatorTemplate>
</asp:Repeater>
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetCategories" TypeName="CategoriesBLL">
</asp:ObjectDataSource>
圖 4 顯示透過瀏覽器檢視時的進度。
圖 4:列出每個類別的名稱和描述,並以水平規則分隔 (按兩下即可檢視完整大小的影像)
步驟 2:新增巢狀產品重複程式
類別清單完成後,下一項工作是將重複項新增至 ,CategoryList
ItemTemplate
以顯示屬於適當類別之這些產品的相關信息。 有數種方式可以擷取這個內部 Repeater 的數據,其中兩種我們很快就會探索。 現在,讓我們在 Repeater s ItemTemplate
內CategoryList
建立產品 Repeater。 具體來說,讓我們讓產品重複項在點符清單中顯示每個產品,其中包含產品名稱和價格。
若要建立這個 Repeater,我們需要手動將內部 Repeater 的宣告式語法和範本輸入 CategoryList
s ItemTemplate
。 在 CategoryList
Repeater s ItemTemplate
中新增下列標記:
<asp:Repeater ID="ProductsByCategoryList" EnableViewState="False"
runat="server">
<HeaderTemplate>
<ul>
</HeaderTemplate>
<ItemTemplate>
<li><strong><%# Eval("ProductName") %></strong>
(<%# Eval("UnitPrice", "{0:C}") %>)</li>
</ItemTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>
步驟 3:將 Category-Specific 產品系結至 ProductsByCategoryList Repeater
如果您此時透過瀏覽器瀏覽頁面,您的畫面看起來會與圖 4 中的相同,因為我們尚未將任何數據系結至重複程式。 有幾種方式可以抓取適當的產品記錄,並將其系結至 Repeater,有些比其他記錄更有效率。 此處的主要挑戰是針對指定的類別取回適當的產品。
若要系結至內部 Repeater 控制件的數據,可以透過 Repeater s ItemTemplate
中的 CategoryList
ObjectDataSource 宣告方式存取,或以程式設計方式從 ASP.NET 頁的程式代碼後置頁面存取。 同樣地,此數據可以宣告方式系結至內部 Repeater - 透過內部 Repeater s DataSourceID
屬性或宣告式數據系結語法,或透過程式設計方式參考 Repeater 事件處理程式ItemDataBound
中的CategoryList
內部 Repeater、以程式設計方式設定其 DataSource
屬性,以及呼叫其 DataBind()
方法。 讓我們來探索這其中每一種方法。
使用 ObjectDataSource 控制項和ItemDataBound
事件處理程式以宣告方式存取數據
由於我們已在此教學課程系列中廣泛使用 ObjectDataSource,因此存取此範例數據的最自然選擇是與 ObjectDataSource 一起運作。 類別 ProductsBLL
有方法 GetProductsByCategoryID(categoryID)
可傳回屬於指定 categoryID
之產品的相關信息。 因此,我們可以將 ObjectDataSource 新增至 CategoryList
Repeater s ItemTemplate
,並將其設定為從這個類別 s 方法存取其數據。
可惜的是,Repeater 不允許透過設計視圖編輯其範本,因此我們需要手動新增此 ObjectDataSource 控件的宣告式語法。 下列語法會顯示CategoryList
在新增這個新的 ObjectDataSource () ProductsByCategoryDataSource
之後的 RepeaterItemTemplate
:
<h4><%# Eval("CategoryName") %></h4>
<p><%# Eval("Description") %></p>
<asp:Repeater ID="ProductsByCategoryList" EnableViewState="False"
DataSourceID="ProductsByCategoryDataSource" runat="server">
<HeaderTemplate>
<ul>
</HeaderTemplate>
<ItemTemplate>
<li><strong><%# Eval("ProductName") %></strong> -
sold as <%# Eval("QuantityPerUnit") %> at
<%# Eval("UnitPrice", "{0:C}") %></li>
</ItemTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>
<asp:ObjectDataSource ID="ProductsByCategoryDataSource" runat="server"
SelectMethod="GetProductsByCategoryID" TypeName="ProductsBLL">
<SelectParameters>
<asp:Parameter Name="CategoryID" Type="Int32" />
</SelectParameters>
</asp:ObjectDataSource>
使用 ObjectDataSource 方法時,我們需要將 Repeater 的 DataSourceID
屬性設定ProductsByCategoryList
為 ID
ObjectDataSource 的 (ProductsByCategoryDataSource
) 。 此外,請注意,我們的 ObjectDataSource 有一個 <asp:Parameter>
元素,指定 categoryID
將傳遞至 方法的值 GetProductsByCategoryID(categoryID)
。 但是,我們要如何指定此值? 在理想情況下,我們只要使用數據系結語法來設定 DefaultValue
元素的 <asp:Parameter>
屬性,如下所示:
<asp:Parameter Name="CategoryID" Type="Int32"
DefaultValue='<%# Eval("CategoryID")' />
不幸的是,數據系結語法只有在具有 DataBinding
事件的控件中才有效。 類別 Parameter
缺少這類事件,因此上述語法不合法,而且會導致運行時錯誤。
若要設定此值,我們需要為 CategoryList
Repeater s ItemDataBound
事件建立事件處理程式。 回想一下,針對 ItemDataBound
系結至 Repeater 的每個項目引發一次事件。 因此,每次針對外部 Repeater 引發此事件時,我們都可以將目前的 CategoryID
值指派給 ProductsByCategoryDataSource
ObjectDataSource s CategoryID
參數。
使用下列程式代碼建立 CategoryList
Repeater 事件的 ItemDataBound
事件處理程式:
Protected Sub CategoryList_ItemDataBound(sender As Object, e As RepeaterItemEventArgs) _
Handles CategoryList.ItemDataBound
If e.Item.ItemType = ListItemType.AlternatingItem _
OrElse e.Item.ItemType = ListItemType.Item Then
' Reference the CategoriesRow object being bound to this RepeaterItem
Dim category As Northwind.CategoriesRow = _
CType(CType(e.Item.DataItem, System.Data.DataRowView).Row, _
Northwind.CategoriesRow)
' Reference the ProductsByCategoryDataSource ObjectDataSource
Dim ProductsByCategoryDataSource As ObjectDataSource = _
CType(e.Item.FindControl("ProductsByCategoryDataSource"), _
ObjectDataSource)
' Set the CategoryID Parameter value
ProductsByCategoryDataSource.SelectParameters("CategoryID").DefaultValue = _
category.CategoryID.ToString()
End If
End Sub
此事件處理程式一開始會確保我們會處理數據項,而不是頁首、頁尾或分隔符專案。 接下來,我們會參考剛系結至目前 RepeaterItem
的實際CategoriesRow
實例。 最後,我們會參考 中的 ItemTemplate
ObjectDataSource,並將其 CategoryID
參數值指派給 CategoryID
目前 RepeaterItem
的 。
透過這個事件處理程式, ProductsByCategoryList
每個 RepeaterItem
中的 Repeater 都會系結至類別中的 RepeaterItem
這些產品。 圖 5 顯示結果輸出的螢幕快照。
圖 5:外部重複項 清單 每個類別;內部一個 清單 該類別的產品 (按兩下即可檢視完整大小的影像)
以程式設計方式依類別數據存取產品
除了使用 ObjectDataSource 來擷取目前類別的產品,我們可以在 ASP.NET 頁面的程式代碼後置類別中建立方法, (或 App_Code
位於資料夾中或個別的類別庫專案中,) 在傳入 CategoryID
時傳回適當產品集。 假設我們在 ASP.NET 頁面的程式代碼後置類別中有這類方法,而且其名稱為 GetProductsInCategory(categoryID)
。 有了這個方法,我們可以使用下列宣告式語法,將目前類別的產品系結至內部 Repeater:
<asp:Repeater runat="server" ID="ProductsByCategoryList" EnableViewState="False"
DataSource='<%# GetProductsInCategory(CType(Eval("CategoryID"), Integer)) %>'>
...
</asp:Repeater>
Repeater s DataSource
屬性使用數據系結語法來表示其數據來自 GetProductsInCategory(categoryID)
方法。 由於 Eval("CategoryID")
會傳回 類型的 Object
值,因此我們會先將 物件轉換成 , Integer
再將它傳遞至 GetProductsInCategory(categoryID)
方法。 請注意,CategoryID
透過資料系結語法存取此處的 是CategoryID
外部 Repeater (CategoryList
) ,這是系結至資料表中記錄的 Categories
。 因此,我們知道CategoryID
不能是資料庫NULL
值,這就是為什麼我們可以不檢查是否正在處理 DBNull
,而無法盲目轉換Eval
方法。
使用此方法時,我們需要建立 方法, GetProductsInCategory(categoryID)
並讓它擷取提供 categoryID
的適當產品集。 我們只要傳回 ProductsDataTable
類別 s GetProductsByCategoryID(categoryID)
方法所傳回的 ProductsBLL
即可這麼做。 讓我們在頁面的程式代碼後置類別NestedControls.aspx
中建立 GetProductsInCategory(categoryID)
方法。 請使用下列程式代碼來執行此動作:
Protected Function GetProductsInCategory(ByVal categoryID As Integer) _
As Northwind.ProductsDataTable
' Create an instance of the ProductsBLL class
Dim productAPI As ProductsBLL = New ProductsBLL()
' Return the products in the category
Return productAPI.GetProductsByCategoryID(categoryID)
End Function
這個方法只會建立 方法的 ProductsBLL
實例,並傳回 方法的結果 GetProductsByCategoryID(categoryID)
。 請注意,方法必須標示 Public
為 或 Protected
;如果方法標示 Private
為 ,將無法從 ASP.NET 頁面的宣告式標記存取。
進行這些變更以使用此新技術之後,請花點時間透過瀏覽器檢視頁面。 使用 ObjectDataSource 和 ItemDataBound
事件處理程式方法時,輸出應該與輸出相同, (請參閱圖 5 以查看螢幕快照) 。
注意
在 ASP.NET 頁面的程式代碼後置類別中建立 GetProductsInCategory(categoryID)
方法似乎很忙碌。 之後,這個方法只會建立 類別的 ProductsBLL
實例,並傳回其 GetProductsByCategoryID(categoryID)
方法的結果。 為何不只直接從內部 Repeater 中的數據系結語法呼叫這個方法,例如: DataSource='<%# ProductsBLL.GetProductsByCategoryID(CType(Eval("CategoryID"), Integer)) %>'
? 雖然這個語法無法搭配目前的 類別實作 ProductsBLL
(,因為GetProductsByCategoryID(categoryID)
方法是實例方法) ,但您可以修改ProductsBLL
以包含靜態方法,或讓 類別包含靜態GetProductsByCategoryID(categoryID)
Instance()
方法以傳回 類別的新實例ProductsBLL
。
雖然這類修改會消除 ASP.NET 頁面程式代碼後置類別中方法的需求 GetProductsInCategory(categoryID)
,但程式代碼後置類別方法可讓我們更有彈性地處理所擷取的數據,因為我們很快就會看到。
一次擷取所有產品資訊
我們已藉由呼叫類別 s GetProductsByCategoryID(categoryID)
方法來擷取目前類別的兩種技術, (第一種方法透過 ObjectDataSource 進行呼叫ProductsBLL
,第二種是透過GetProductsInCategory(categoryID)
程式代碼後置類別中的方法) 。 每次叫用這個方法時,商業規則層都會向下呼叫數據存取層,此層會使用 SQL 語句來查詢資料庫,該語句會傳Products
CategoryID
回數據表的數據列,其欄位符合提供的輸入參數。
在系統中指定 N 個類別時,此方法會 nets N + 1 呼叫資料庫一個資料庫查詢,以取得所有類別,然後 N 個呼叫來取得每個類別特定的產品。 不過,我們可以只擷取兩個資料庫呼叫中的所有必要數據,以取得所有類別,另一個則是取得所有產品。 擁有所有產品之後,我們可以篩選這些產品,讓只有符合目前 CategoryID
的產品會系結至該類別的內部 Repeater。
為了提供這項功能,我們只需要稍微修改 GetProductsInCategory(categoryID)
ASP.NET 頁面程序代碼後置類別中的方法。 如果尚未存取產品) ,則我們可以改為以盲目方式傳回類別 s GetProductsByCategoryID(categoryID)
方法的結果ProductsBLL
,而是先存取所有產品 (,然後只傳回根據傳入CategoryID
的產品篩選檢視。
Private allProducts As Northwind.ProductsDataTable = Nothing
Protected Function GetProductsInCategory(ByVal categoryID As Integer) _
As Northwind.ProductsDataTable
' First, see if we've yet to have accessed all of the product information
If allProducts Is Nothing Then
Dim productAPI As ProductsBLL = New ProductsBLL()
allProducts = productAPI.GetProducts()
End If
' Return the filtered view
allProducts.DefaultView.RowFilter = "CategoryID = " & categoryID
Return allProducts
End Function
請注意新增頁面層級變數 allProducts
這會保存所有產品的相關信息,並在第一次叫用 方法時 GetProductsInCategory(categoryID)
填入。 確保allProducts
物件已建立並填入之後,此方法會篩選 DataTable 的結果,讓只有符合指定CategoryID
的數據列CategoryID
可供存取。 此方法可減少從 N + 1 存取資料庫到兩次的次數。
這項增強功能不會對頁面的轉譯標記造成任何變更,也不會讓其他方法傳回較少的記錄。 它只會減少對資料庫的呼叫數目。
注意
其中一個可能直覺的理由是減少數據庫存取數目可保證提升效能。 不過,這可能不是這樣。 例如,如果您有大量的產品CategoryID
NULL
,則呼叫 GetProducts
方法會傳回一些永遠不會顯示的產品。 此外,如果您只顯示類別的子集,則傳回所有產品可能會浪費,如果您已實作分頁,則可能是這種情況。
一如往常,當分析兩種技術的效能時,唯一的確定fire量值是針對應用程式的常見案例執行量身打造的受控制測試。
摘要
在本教學課程中,我們已瞭解如何將某個數據 Web 控件巢狀在另一個數據 Web 控件內,具體檢查如何讓外部 Repeater 顯示每個類別的專案,並列出專案符號清單中每個類別的產品。 建置巢狀使用者介面的主要挑戰在於存取和系結正確的數據至內部數據 Web 控件。 有各種可用的技術,我們在本教學課程中會檢查其中兩種技術。 第一個檢查方法使用外部數據 Web 控制項中的 ObjectDataSource,透過其 DataSourceID
屬性系結至內部數據 Web 控件ItemTemplate
。 第二種技術會透過 ASP.NET 頁程式代碼後置類別中的 方法存取數據。 這個方法接著可以透過數據系結語法系結至內部數據 Web 控件的 DataSource
屬性。
雖然本教學課程中檢查的巢狀使用者介面使用了重複項巢狀在重複程式內,但這些技術可以延伸至其他數據 Web 控制件。 您可以將 Repeater 巢狀放在 GridView 內,或 DataList 內的 GridView 等。
快樂的程序設計!
關於作者
Scott Mitchell 是七份 ASP/ASP.NET 書籍的作者,以及 自 1998 年以來與 Microsoft Web 技術合作的 4GuysFromRolla.com 作者。 Scott 是獨立顧問、訓練員和作者。 他的最新書籍是 Sams 在 24 小時內自行 ASP.NET 2.0。 您可以透過mitchell@4GuysFromRolla.com部落格來連線到 ,您可以在 找到http://ScottOnWriting.NET。
特別感謝
本教學課程系列是由許多實用的檢閱者檢閱。 本教學課程的首席檢閱者是 Zack Jones 和 Liz Shulok。 有興趣檢閱即將推出的 MSDN 文章嗎? 如果是,請將一行 mitchell@4GuysFromRolla.com放在 。