程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> ASP.NET >> ASP.NET基礎 >> 在ASP.NET 2.0中操作數據之三十二:數據控件的嵌套

在ASP.NET 2.0中操作數據之三十二:數據控件的嵌套

編輯:ASP.NET基礎

導言

  除了靜態HTML和數據綁定語法,template也可以包含Web控件和用戶控件.這些控件的屬性可以通過聲明語法,數據綁定語法或在服務器端通過事件處理編程來設置.

  通過將控件嵌入到template裡,可以自定義界面,提升用戶體驗.例如,在在GridView控件中使用TemplateField 裡,我們學習了如何通過在GridView的TemplateField裡加一個Calendar控件來表示員工的雇傭日期.在給編輯和新增界面增加驗證控件 和定制數據修改界面 裡,我們學習了如何通過添加驗證控件, TextBox,DropDownList和其它Web控件來自定義編輯,插入界面.

  Template也可以包含其它數據控件.即,我們可以讓DataList在Template裡包含其它DataList(或者Repeater,GridView,DetailsView等).這個工作的挑戰在於將數據綁定到裡面的數據控件上.有幾種不同的方法可以實現,包括從使用ObjectDataSource的聲明語言到直接編程.

  在本章裡我們將探索如何使用嵌套的Repeater.外層的Repeater將每個category顯示為一個item,包含category的name和description.每個category的item裡的Repeater顯示此category下的每個product(見圖1).我們將分別學習如何通過聲明和編程的方法創建內層的Repeater.

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916480205.png
圖1: Category和屬於它的Product一起被列出

第一步: 創建Category列表

  當創建一個使用嵌套數據控件的頁時,我發現開始從最外層的控件的設計,創建和測試開始非常的有幫助,這個時候不用管內層嵌套的控件.因此,我們首先實現往頁面裡添加一個Repeater來列出category的name和description.

  打開DataListRepeaterBasics文件夾裡的NestedControls.aspx頁.添加一個Repeater控件,將ID設為CategoryList..通過它的智能標簽,選擇創建一個新的名為CategoriesDataSource的ObjectDataSource.

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916480302.png
圖 2: 創建一個名為CategoriesDataSource的ObjectDataSource

用CategoriesBLL類的GetCategories方法配置O

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916480333.png
圖3: 用CategoriesBLL類的GetCategories方法配置ObjectDataSource

  我們需要切換到源視圖來手動輸入聲明代碼指定Repeater的template內容.增加一個帶<h4>的name和<p>的description的ItemTemplate.用<hr>將category分開.在作完這些後,你的頁面代碼裡的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 表示現在在浏覽器裡浏覽這個頁.

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916480327.png
圖 4:列出每個Category的 Name 和Description , 用水平線隔開

第二步: 增加嵌套的Repeater顯示Product

  下一步我們的任務是在CategoryList的ItemTemplate裡添加一個Repeater用來顯示屬於各個category下的product.有很多方法可以存取內層的Repeater數據,我們將探討兩種現在我們在CategoryList Repeater的ItemTemplate裡創建product Repeater.每個product裡將包含name和price我們將下面的標記加到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

  如果現在你浏覽這個頁,你會看到象圖4一樣的頁面,因為我們還沒有在Repeater裡綁定任何數據.有幾種方法可以將合適的product記錄綁定到Repeater裡,其中一些會比較有效.現在主要的任務是為指定category取到合適的product.可以通過在ItemTemplate裡語法聲明ObjectDataSource或者直接在後台代碼編程來將數據綁定到內層的Repeater.

  通過ObjectDataSource和ItemDataBound來獲取數據

  這裡我們還是用ObjectDataSource來實現.ProductsBLL類的GetProductsByCategoryID(Category)
方法可以返回特定CategoryID的products信息.因此,我們將在CategoryList Repeater的ItemTemplate裡新建一個ObjectDataSource,並用這個方法配置它.不幸的,Repeater不允許通過設計視圖來修改template,因此我們需要手動添加將聲明語法.見下面的代碼:

<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方法時我們需要設置ProductsByCategoryList Repeater的DataSourceID為ObjectDataSource(ProductsByCategoryDataSource).注意ObjectDataSource有一個<asp:Parameter>來指定傳給GetProductsByCategoryID(categoryID)的categoryID.但是我們怎麼來指定這個值呢?我們可以設置DefaultValue屬性為<asp:Parameter>,見下面的代碼:

<asp:Parameter Name="CategoryID" Type="Int32"
  DefaultValue='<%# Eval("CategoryID")' />

  不幸的,數據綁定語法只能用在有DataBinding事件的控件裡.Parameter類沒有這樣的事件,因此這樣使用會出錯.我們需要為CategoryList Repeater的ItemDataBound創建一個事件處理來設置這個值.每個item綁定到Repeater時激發ItemDataBound事件.因此每次外層的Repeater激發這個時間時,我們可以將當前的CaegoryID的值傳給ProductsByCategoryDataSource ObjectDataSource的CategoryID參數.下面的代碼是為CategoryList Repeater的ItemDataBound創建一個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();
 }
} 
       

  這個event handler首先保證我們操作的是data item而不是header,footer或separator item.然後,引用剛剛綁定到當前RepeaterItem的CategoriesRow實例.最後,引用在ItemTemplate裡的ObjectDataSource並將當前RepeaterItem的CategoryID傳給CategoryID參數.

  在這個event handler裡,每個RepeaterItem裡的ProductsByCategoryList Repeater都綁定到RepeaterItem的category裡的product.見圖5.

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916480456.png
圖 5: 外層的Repeater 列出每個Category; 內層的Repeater 列出屬於Category的Products

  直接編程來獲取Category 下的Products

  除了使用ObjectDataSource來獲取當前category下的proudct外,我們還可以在ASP.NET頁的code-behind裡(或App_Code文件夾裡或一個單獨的類項目裡)來創建一個根據傳入的CategoryID返回合適的product集的方法.假設在ASP.NET頁的code-behind裡有一個名為GetProductsInCategory(categoryID)方法.我們可以使用這個方法來將當前category下的product綁定到內層的Repeater.見下面的代碼:

<asp:Repeater runat="server" ID="ProductsByCategoryList" EnableViewState="False"
  DataSource='<%# GetProductsInCategory((int)(Eval("CategoryID"))) %>'>
 ...
</asp:Repeater>

  Repeater的DataSource屬性通過綁定語法來指定它的數據是通過GetProductsInCategory(categoryID)得到.由於Eval("CategryID")返回的是Object類型,我們在它傳入GetProductsInCategory(categoryID)前將它轉化成Integer.注意這裡的CategoryID是通過外層Repeater(CategoryList)的CategoryID(已經綁定到Categories table)獲取的.因此它不可能是一個NULL值.所以我們在綁定前沒有檢查.

  我們現在需要創建GetProductsInCategory(categoryID)方法.在這裡簡單使用ProductsBLL類的GetProductsByCategoryID(categoryID)方法返回的ProductsDataTable就可以了.我們在NestedControls.aspx頁的code-behind裡創建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);
}

  這個方法僅僅是創建一個ProductsBLL實例然後返回GetProductsByCategoryID(categoryID)方法的返回值.注意這個方法必須標記為Public或Protected.如果標記為Private,ASP.NET頁的聲明標記裡將不能調用它.
做完以上操作後,在浏覽器裡浏覽頁面.頁面看起來應該和使用ObjectDataSource 和ItemDataBound event handler方法差不多(圖5).

  注意:在ASP.NET頁的code-behind裡創建GetProductsInCategory(categoryID)方法好象只是一個形式,畢竟這個方法只是調用BLL裡的方法.為什麼不直接在內層Repeater裡的綁定語法裡直接調用這個方法.比如:
DataSource='<%#ProductsBLL.GetProductsByCategoryID(CType(Eval("CategoryID"),Integer))%>')
雖然這個聲明是不起作用的(因為GetProductsByCategoryID(categoryID)方法是一個實例方法),你可以修改ProductsBLL來包含一個這樣的靜態方法.這樣的修改可以滿足ASP.NET頁的GetProductsInCategory(categoryID)方法的需要,但是寫在code-behind裡可以更靈活的獲取數據,我們在後面會看到這點.

獲取所有的Product 信息

  前面兩個方法我們通過調用ProductsBLL類的GetProductsByCategoryID(categoryID)方法來獲取當前category的product(第一種通過ObjectDataSource,第二種通過GetProductsInCategory(categoryID)).每次方法被調用時,BLL調用DAL,DAL通過SQL查詢數據庫,返回特定的記錄.

  如果有N個category,這個方法會訪問數據庫N+1次— 一次返回所有的category,N次返回特定category下的product.然而我們可以通過訪問數據庫兩次來獲取所有需要的數據— 一次返回所有的category,一次返回所有的product.一旦我們得到所有的product,我們可以根據CategoryID來過濾,然後再綁定.

  我們只需要稍微修改ASP.NET頁的code-behind裡的GetProductsInCategory(categoryID)方法來實現這個功能.我們首先來返回所有的product,然後根據傳入的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變量.它在第一次調用GetProductsInCategory(categoryID)時返回所有product信息.確定allProducts對象被創建後,在根據CategoryID來對DataTable過濾.這個方法將訪問數據庫的次數從N+1減少到2次.
這個改進沒有修改頁面的聲明語言.僅僅只是減少了數據庫的訪問次數.

  注意:可能想當然的覺得減少了數據庫訪問次數會提高性能.但是這個不一定.如果你有大量的categoryID為NULL的product,這樣使用GetProducts方法返回的product有一部分不會被顯示.而且如果你只需要顯示一部分category的proudct(分頁時就是這樣),而返回所有的product,這樣對資源也是一種浪費.通常對兩種技術進行性能分析,唯一正確的方法是設置程序常見的場景來進行壓力測試.

總結

  本章我們學習了如何嵌套Web控件.通過如何在外層Repeater顯示各個category,內層Repeater顯示每個category下的product來作為例子.主要的任務在於獲取正確的數據並綁定到內層的Web控件上.有很多方法可以使用,我們這裡討論了兩種.第一種是使用在外層控件的ItemTemplate裡ObjectDataSource來綁定到內層控件.第二種是使用ASP.NET頁的code-behind裡的方法.它通過內層控件的DataSource屬性來綁定.本章使用的控件是Repeater,也可以將Repeater嵌套在GridView裡,或GridView嵌套在DataList裡等.

  祝編程快樂!

作者簡介

  Scott Mitchell,著有六本ASP/ASP.NET方面的書,是4GuysFromRolla.com的創始人,自1998年以來一直應用 微軟Web技術。Scott是個獨立的技術咨詢顧問,培訓師,作家,最近完成了將由Sams出版社出版的新作,24小時內精通ASP.NET 2.0。他的聯系電郵為[email protected],也可以通過他的博客http://ScottOnWriting.NET與他聯系。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved