在ASP.NET 2.0中操作數(shù)據(jù)之三十二:數(shù)據(jù)控件的嵌套
導(dǎo)言
除了靜態(tài)HTML和數(shù)據(jù)綁定語法,template也可以包含Web控件和用戶控件.這些控件的屬性可以通過聲明語法,數(shù)據(jù)綁定語法或在服務(wù)器端通過事件處理編程來設(shè)置.
通過將控件嵌入到template里,可以自定義界面,提升用戶體驗(yàn).例如,在在GridView控件中使用TemplateField 里,我們學(xué)習(xí)了如何通過在GridView的TemplateField里加一個(gè)Calendar控件來表示員工的雇傭日期.在給編輯和新增界面增加驗(yàn)證控件 和定制數(shù)據(jù)修改界面 里,我們學(xué)習(xí)了如何通過添加驗(yàn)證控件, TextBox,DropDownList和其它Web控件來自定義編輯,插入界面.
Template也可以包含其它數(shù)據(jù)控件.即,我們可以讓DataList在Template里包含其它DataList(或者Repeater,GridView,DetailsView等).這個(gè)工作的挑戰(zhàn)在于將數(shù)據(jù)綁定到里面的數(shù)據(jù)控件上.有幾種不同的方法可以實(shí)現(xiàn),包括從使用ObjectDataSource的聲明語言到直接編程.
在本章里我們將探索如何使用嵌套的Repeater.外層的Repeater將每個(gè)category顯示為一個(gè)item,包含category的name和description.每個(gè)category的item里的Repeater顯示此category下的每個(gè)product(見圖1).我們將分別學(xué)習(xí)如何通過聲明和編程的方法創(chuàng)建內(nèi)層的Repeater.
圖1: Category和屬于它的Product一起被列出
第一步: 創(chuàng)建Category列表
當(dāng)創(chuàng)建一個(gè)使用嵌套數(shù)據(jù)控件的頁時(shí),我發(fā)現(xiàn)開始從最外層的控件的設(shè)計(jì),創(chuàng)建和測(cè)試開始非常的有幫助,這個(gè)時(shí)候不用管內(nèi)層嵌套的控件.因此,我們首先實(shí)現(xiàn)往頁面里添加一個(gè)Repeater來列出category的name和description.
打開DataListRepeaterBasics文件夾里的NestedControls.aspx頁.添加一個(gè)Repeater控件,將ID設(shè)為CategoryList..通過它的智能標(biāo)簽,選擇創(chuàng)建一個(gè)新的名為CategoriesDataSource的ObjectDataSource.
圖 2: 創(chuàng)建一個(gè)名為CategoriesDataSource的ObjectDataSource
用CategoriesBLL類的GetCategories方法配置O
圖3: 用CategoriesBLL類的GetCategories方法配置ObjectDataSource
我們需要切換到源視圖來手動(dòng)輸入聲明代碼指定Repeater的template內(nèi)容.增加一個(gè)帶<h4>的name和<p>的description的ItemTemplate.用<hr>將category分開.在作完這些后,你的頁面代碼里的Repeater和ObjectDataSource聲明語言應(yīng)該和下面差不多:
<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 表示現(xiàn)在在瀏覽器里瀏覽這個(gè)頁.
圖 4:列出每個(gè)Category的 Name 和Description , 用水平線隔開
第二步: 增加嵌套的Repeater顯示Product
下一步我們的任務(wù)是在CategoryList的ItemTemplate里添加一個(gè)Repeater用來顯示屬于各個(gè)category下的product.有很多方法可以存取內(nèi)層的Repeater數(shù)據(jù),我們將探討兩種現(xiàn)在我們?cè)贑ategoryList Repeater的ItemTemplate里創(chuàng)建product Repeater.每個(gè)product里將包含name和price我們將下面的標(biāo)記加到CategoryList的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>
第三步: 將各Category下的Product綁定到 ProductsByCategoryList Repeater
如果現(xiàn)在你瀏覽這個(gè)頁,你會(huì)看到象圖4一樣的頁面,因?yàn)槲覀冞€沒有在Repeater里綁定任何數(shù)據(jù).有幾種方法可以將合適的product記錄綁定到Repeater里,其中一些會(huì)比較有效.現(xiàn)在主要的任務(wù)是為指定category取到合適的product.可以通過在ItemTemplate里語法聲明ObjectDataSource或者直接在后臺(tái)代碼編程來將數(shù)據(jù)綁定到內(nèi)層的Repeater.
通過ObjectDataSource和ItemDataBound來獲取數(shù)據(jù)
這里我們還是用ObjectDataSource來實(shí)現(xiàn).ProductsBLL類的GetProductsByCategoryID(Category)
方法可以返回特定CategoryID的products信息.因此,我們將在CategoryList Repeater的ItemTemplate里新建一個(gè)ObjectDataSource,并用這個(gè)方法配置它.不幸的,Repeater不允許通過設(shè)計(jì)視圖來修改template,因此我們需要手動(dòng)添加將聲明語法.見下面的代碼:
<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>
當(dāng)使用ObjectDataSource方法時(shí)我們需要設(shè)置ProductsByCategoryList Repeater的DataSourceID為ObjectDataSource(ProductsByCategoryDataSource).注意ObjectDataSource有一個(gè)<asp:Parameter>來指定傳給GetProductsByCategoryID(categoryID)的categoryID.但是我們?cè)趺磥碇付ㄟ@個(gè)值呢?我們可以設(shè)置DefaultValue屬性為<asp:Parameter>,見下面的代碼:
<asp:Parameter Name="CategoryID" Type="Int32" DefaultValue='<%# Eval("CategoryID")' />
不幸的,數(shù)據(jù)綁定語法只能用在有DataBinding事件的控件里.Parameter類沒有這樣的事件,因此這樣使用會(huì)出錯(cuò).我們需要為CategoryList Repeater的ItemDataBound創(chuàng)建一個(gè)事件處理來設(shè)置這個(gè)值.每個(gè)item綁定到Repeater時(shí)激發(fā)ItemDataBound事件.因此每次外層的Repeater激發(fā)這個(gè)時(shí)間時(shí),我們可以將當(dāng)前的CaegoryID的值傳給ProductsByCategoryDataSource ObjectDataSource的CategoryID參數(shù).下面的代碼是為CategoryList Repeater的ItemDataBound創(chuàng)建一個(gè)event handler:
protected void CategoryList_ItemDataBound(object sender, RepeaterItemEventArgs e) { if (e.Item.ItemType == ListItemType.AlternatingItem || e.Item.ItemType == ListItemType.Item) { // Reference the CategoriesRow object being bound to this RepeaterItem Northwind.CategoriesRow category = (Northwind.CategoriesRow)((System.Data.DataRowView)e.Item.DataItem).Row; // Reference the ProductsByCategoryDataSource ObjectDataSource ObjectDataSource ProductsByCategoryDataSource = (ObjectDataSource)e.Item.FindControl("ProductsByCategoryDataSource"); // Set the CategoryID Parameter value ProductsByCategoryDataSource.SelectParameters["CategoryID"].DefaultValue = category.CategoryID.ToString(); } }
這個(gè)event handler首先保證我們操作的是data item而不是header,footer或separator item.然后,引用剛剛綁定到當(dāng)前RepeaterItem的CategoriesRow實(shí)例.最后,引用在ItemTemplate里的ObjectDataSource并將當(dāng)前RepeaterItem的CategoryID傳給CategoryID參數(shù).
在這個(gè)event handler里,每個(gè)RepeaterItem里的ProductsByCategoryList Repeater都綁定到RepeaterItem的category里的product.見圖5.
圖 5: 外層的Repeater 列出每個(gè)Category; 內(nèi)層的Repeater 列出屬于Category的Products
直接編程來獲取Category 下的Products
除了使用ObjectDataSource來獲取當(dāng)前category下的proudct外,我們還可以在ASP.NET頁的code-behind里(或App_Code文件夾里或一個(gè)單獨(dú)的類項(xiàng)目里)來創(chuàng)建一個(gè)根據(jù)傳入的CategoryID返回合適的product集的方法.假設(shè)在ASP.NET頁的code-behind里有一個(gè)名為GetProductsInCategory(categoryID)方法.我們可以使用這個(gè)方法來將當(dāng)前category下的product綁定到內(nèi)層的Repeater.見下面的代碼:
<asp:Repeater runat="server" ID="ProductsByCategoryList" EnableViewState="False" DataSource='<%# GetProductsInCategory((int)(Eval("CategoryID"))) %>'> ... </asp:Repeater>
Repeater的DataSource屬性通過綁定語法來指定它的數(shù)據(jù)是通過GetProductsInCategory(categoryID)得到.由于Eval("CategryID")返回的是Object類型,我們?cè)谒鼈魅隚etProductsInCategory(categoryID)前將它轉(zhuǎn)化成Integer.注意這里的CategoryID是通過外層Repeater(CategoryList)的CategoryID(已經(jīng)綁定到Categories table)獲取的.因此它不可能是一個(gè)NULL值.所以我們?cè)诮壎ㄇ皼]有檢查.
我們現(xiàn)在需要?jiǎng)?chuàng)建GetProductsInCategory(categoryID)方法.在這里簡(jiǎn)單使用ProductsBLL類的GetProductsByCategoryID(categoryID)方法返回的ProductsDataTable就可以了.我們?cè)贜estedControls.aspx頁的code-behind里創(chuàng)建GetProductsInCategory(categoryID).見下面的代碼:
protected Northwind.ProductsDataTable GetProductsInCategory(int categoryID) { // Create an instance of the ProductsBLL class ProductsBLL productAPI = new ProductsBLL(); // Return the products in the category return productAPI.GetProductsByCategoryID(categoryID); }
這個(gè)方法僅僅是創(chuàng)建一個(gè)ProductsBLL實(shí)例然后返回GetProductsByCategoryID(categoryID)方法的返回值.注意這個(gè)方法必須標(biāo)記為Public或Protected.如果標(biāo)記為Private,ASP.NET頁的聲明標(biāo)記里將不能調(diào)用它.
做完以上操作后,在瀏覽器里瀏覽頁面.頁面看起來應(yīng)該和使用ObjectDataSource 和ItemDataBound event handler方法差不多(圖5).
注意:在ASP.NET頁的code-behind里創(chuàng)建GetProductsInCategory(categoryID)方法好象只是一個(gè)形式,畢竟這個(gè)方法只是調(diào)用BLL里的方法.為什么不直接在內(nèi)層Repeater里的綁定語法里直接調(diào)用這個(gè)方法.比如:
DataSource='<%#ProductsBLL.GetProductsByCategoryID(CType(Eval("CategoryID"),Integer))%>')
雖然這個(gè)聲明是不起作用的(因?yàn)镚etProductsByCategoryID(categoryID)方法是一個(gè)實(shí)例方法),你可以修改ProductsBLL來包含一個(gè)這樣的靜態(tài)方法.這樣的修改可以滿足ASP.NET頁的GetProductsInCategory(categoryID)方法的需要,但是寫在code-behind里可以更靈活的獲取數(shù)據(jù),我們?cè)诤竺鏁?huì)看到這點(diǎn).
獲取所有的Product 信息
前面兩個(gè)方法我們通過調(diào)用ProductsBLL類的GetProductsByCategoryID(categoryID)方法來獲取當(dāng)前category的product(第一種通過ObjectDataSource,第二種通過GetProductsInCategory(categoryID)).每次方法被調(diào)用時(shí),BLL調(diào)用DAL,DAL通過SQL查詢數(shù)據(jù)庫,返回特定的記錄.
如果有N個(gè)category,這個(gè)方法會(huì)訪問數(shù)據(jù)庫N+1次— 一次返回所有的category,N次返回特定category下的product.然而我們可以通過訪問數(shù)據(jù)庫兩次來獲取所有需要的數(shù)據(jù)— 一次返回所有的category,一次返回所有的product.一旦我們得到所有的product,我們可以根據(jù)CategoryID來過濾,然后再綁定.
我們只需要稍微修改ASP.NET頁的code-behind里的GetProductsInCategory(categoryID)方法來實(shí)現(xiàn)這個(gè)功能.我們首先來返回所有的product,然后根據(jù)傳入的CategoryID里過濾.
private Northwind.ProductsDataTable allProducts = null; protected Northwind.ProductsDataTable GetProductsInCategory(int categoryID) { // First, see if we've yet to have accessed all of the product information if (allProducts == null) { ProductsBLL productAPI = new ProductsBLL(); allProducts = productAPI.GetProducts(); } // Return the filtered view allProducts.DefaultView.RowFilter = "CategoryID = " + categoryID; return allProducts; }
注意allProducts變量.它在第一次調(diào)用GetProductsInCategory(categoryID)時(shí)返回所有product信息.確定allProducts對(duì)象被創(chuàng)建后,在根據(jù)CategoryID來對(duì)DataTable過濾.這個(gè)方法將訪問數(shù)據(jù)庫的次數(shù)從N+1減少到2次.
這個(gè)改進(jìn)沒有修改頁面的聲明語言.僅僅只是減少了數(shù)據(jù)庫的訪問次數(shù).
注意:可能想當(dāng)然的覺得減少了數(shù)據(jù)庫訪問次數(shù)會(huì)提高性能.但是這個(gè)不一定.如果你有大量的categoryID為NULL的product,這樣使用GetProducts方法返回的product有一部分不會(huì)被顯示.而且如果你只需要顯示一部分category的proudct(分頁時(shí)就是這樣),而返回所有的product,這樣對(duì)資源也是一種浪費(fèi).通常對(duì)兩種技術(shù)進(jìn)行性能分析,唯一正確的方法是設(shè)置程序常見的場(chǎng)景來進(jìn)行壓力測(cè)試.
總結(jié)
本章我們學(xué)習(xí)了如何嵌套Web控件.通過如何在外層Repeater顯示各個(gè)category,內(nèi)層Repeater顯示每個(gè)category下的product來作為例子.主要的任務(wù)在于獲取正確的數(shù)據(jù)并綁定到內(nèi)層的Web控件上.有很多方法可以使用,我們這里討論了兩種.第一種是使用在外層控件的ItemTemplate里ObjectDataSource來綁定到內(nèi)層控件.第二種是使用ASP.NET頁的code-behind里的方法.它通過內(nèi)層控件的DataSource屬性來綁定.本章使用的控件是Repeater,也可以將Repeater嵌套在GridView里,或GridView嵌套在DataList里等.
祝編程快樂!
作者簡(jiǎn)介
Scott Mitchell,著有六本ASP/ASP.NET方面的書,是4GuysFromRolla.com的創(chuàng)始人,自1998年以來一直應(yīng)用 微軟Web技術(shù)。Scott是個(gè)獨(dú)立的技術(shù)咨詢顧問,培訓(xùn)師,作家,最近完成了將由Sams出版社出版的新作,24小時(shí)內(nèi)精通ASP.NET 2.0。他的聯(lián)系電郵為mitchell@4guysfromrolla.com,也可以通過他的博客http://ScottOnWriting.NET與他聯(lián)系。
- ASP.NET 跨頁面?zhèn)髦捣椒?/a>
- ASP.NET 2.0中的數(shù)據(jù)操作之九:跨頁面的主/從報(bào)表
- 在ASP.NET 2.0中操作數(shù)據(jù)之二十六:排序自定義分頁數(shù)據(jù)
- 在ASP.NET 2.0中操作數(shù)據(jù)之二十七:創(chuàng)建自定義排序用戶界面
- 在ASP.NET 2.0中操作數(shù)據(jù)之二十八:GridView里的Button
- 在ASP.NET 2.0中操作數(shù)據(jù)之二十九:用DataList和Repeater來顯示數(shù)據(jù)
- 在ASP.NET 2.0中操作數(shù)據(jù)之三十:格式化DataList和Repeater的數(shù)據(jù)
- 在ASP.NET 2.0中操作數(shù)據(jù)之三十一:使用DataList來一行顯示多條記錄
- 在ASP.NET 2.0中操作數(shù)據(jù)之三十三:基于DataList和Repeater使用DropDownList過濾的主/從報(bào)表
- 在ASP.NET 2.0中操作數(shù)據(jù)之三十四:基于DataList和Repeater跨頁面的主/從報(bào)表
相關(guān)文章
在ASP.NET 2.0中操作數(shù)據(jù)之二十八:GridView里的Button
本文主要介紹ASP.NET 2.0在GridView,DetailsView,FormView都可以包含Buttons,LinkButtons,或ImageButtons.這些button被點(diǎn)擊時(shí),并激發(fā)FormView和DetailsView的ItemCommand事件,GridView的RowCommand事件,根據(jù)CommandName的值來判斷哪個(gè)button被點(diǎn)擊了,執(zhí)行相應(yīng)的代碼。2016-05-05在ASP.NET 2.0中操作數(shù)據(jù)之二十一:實(shí)現(xiàn)開放式并發(fā)
本文主要介紹ASP.NET 2.0如何實(shí)現(xiàn)開放式并發(fā)控制,并逐步講解從數(shù)據(jù)訪問層到業(yè)務(wù)邏輯層以及到最終的頁面是如何一步一步操作的。2016-05-05在ASP.NET 2.0中操作數(shù)據(jù)之四十一:DataList和Repeater數(shù)據(jù)分頁
DataList 和Repeater 都沒有提供內(nèi)置的分頁和排序功能,本文主要介紹利用PagedDataSource實(shí)現(xiàn)DataList和Repeater數(shù)據(jù)分頁。2016-05-05Mac中體驗(yàn)ASP.NET 5 beta2的K gen代碼生成
這篇文章主要介紹了Mac中體驗(yàn)ASP.NET 5 beta2的K gen代碼生成,需要的朋友可以參考一下。2016-06-06在ASP.NET 2.0中操作數(shù)據(jù)之十七:研究插入、更新和刪除的關(guān)聯(lián)事件
本文主要講解ASP.NET 2.0中如何在點(diǎn)擊插入、更新、刪除等按鈕時(shí)關(guān)聯(lián)和觸發(fā)事件,以便我們?cè)谑录袑懘a,實(shí)現(xiàn)我們期望的效果。2016-05-05《解剖PetShop》之二:PetShop數(shù)據(jù)訪問層數(shù)之據(jù)庫訪問設(shè)計(jì)
本文主要講解PetShop4.0的數(shù)據(jù)訪問層設(shè)計(jì),包括:數(shù)據(jù)庫訪問、Messaging、MemberShip、Profile四部分,需要的朋友可以參考下。2016-05-05ASP.NET MVC4入門教程(八):給數(shù)據(jù)模型添加校驗(yàn)器
本文介紹給模型添加校驗(yàn)器,以前我們做表單驗(yàn)證都要前臺(tái)驗(yàn)證一遍,后臺(tái)再驗(yàn)證一遍,MVC4中這種方式,只需在模型上加驗(yàn)證規(guī)則,前后臺(tái)就都驗(yàn)證了。2016-04-04解讀ASP.NET 5 & MVC6系列教程(5):Configuration配置信息管理
這篇文章主要介紹了ASP.NET 5中Configuration配置信息管理,需要的朋友可以參考下。2016-06-06解讀ASP.NET 5 & MVC6系列教程(2):初識(shí)項(xiàng)目
這篇文章主要介紹ASP.NET 5中新建項(xiàng)目的結(jié)構(gòu)和之前的差異,介紹的比較細(xì)致,需要的朋友可以參考下。2016-06-06