導(dǎo)言
在前面一章里我們學(xué)習(xí)了如何用兩個頁分別顯示主/從信息。在“主”頁里我們用Repeater來顯示category。每個category的name都是一個鏈到“從”頁的hyperlink。在從頁里用一個兩列的DataList顯示選中的category下的product。本章我們將還是使用單頁,在左邊顯示category列表,category的名字用LinkButton顯示。點擊其中一個時頁面postback,在右邊以兩列的DataList顯示出相關(guān)的product。除了名字外,左邊的Repeater還會顯示與該category相關(guān)聯(lián)的product總數(shù)。(見圖1)
![](/d/20211017/ef7e53d5c41daba9b62a98ed7e0d4b6b.gif)
圖 1: Category的 Name 和 Product總數(shù)顯示在左邊
第一步: 在頁面左部顯示一個Repeater
本章我們將在左邊顯示category,右表顯示它關(guān)聯(lián)的product。web頁的內(nèi)容可以使用標(biāo)準(zhǔn)HTML元素或者CSS來定位。到目前為止我們都是使用CSS來定位。在母板頁和站點導(dǎo)航 一章里我們使用絕對定位來創(chuàng)建導(dǎo)航時,為導(dǎo)航列表和內(nèi)容之間指定了明確的距離。當(dāng)然CSS也可以用來對兩個元素的位置進(jìn)行調(diào)整。
打開DataListRepeaterFiltering文件夾下的CategoriesAndProducts.aspx頁,添加一個Repeater和DataList.ID分別設(shè)置為Categories和CategoryProducts。然后到源視圖里將它們分別放到div>元素里。也就是說在Repeater后面加一個閉合的/div>,在DataList前加一個開始的div>。現(xiàn)在你的代碼看起來應(yīng)該和下面差不多:
div>
asp:Repeater ID="Categories" runat="server">
/asp:Repeater>
/div>
div>
asp:DataList ID="CategoryProducts" runat="server">
/asp:DataList>
/div>
我們需要使用float屬性來將Repeater放到DataList左邊,見下面代碼:
div>
Repeater
/div>
div>
DataList
/div>
float:left 將第一個div>放到第二個的左邊。width和padding-right指定了第一個div>的寬和div>內(nèi)容和右邊框的距離。更多的floating元素信息請參考Floatutorial.我們在Styles.css里創(chuàng)建一個新的CSS類,名為Floatleft(而不是直接在p>的樣式里設(shè)置):
.FloatLeft
{
float: left;
width: 33%;
padding-right: 10px;
}
然后我們用div class="FloatLeft">將div style="float:left">替換掉。
完成以上所講的內(nèi)容后,切換到設(shè)計視圖。你應(yīng)該看到Repeater已經(jīng)在DataList左邊了(由于還沒有配置數(shù)據(jù)源或模板,這兩個控件都是灰的)。
![](/d/20211017/1aab5e55029066b68a4d30e231f66f6b.gif)
圖 2: 調(diào)整完位置后的頁面
第二步: 獲取每個Category關(guān)聯(lián)的Products總數(shù)
完成了樣式設(shè)置后,我們現(xiàn)在來將category數(shù)據(jù)綁定到Repeater。如圖1所示,除了category名字外,我們需要顯示和它關(guān)聯(lián)的product總數(shù),為了獲取這個信息我們可以:
在ASP.NET page的code-behind 里獲取這個信息. 根據(jù)給定的categoryID我們可以通過ProductsBLL類的GetProductsByCategoryID(categoryID)方法來獲取關(guān)聯(lián)的product總數(shù)。這個方法返回一個ProductsDataTable對象,它的Count屬性表示了我們需要知道的信息。我們可以為Repeater創(chuàng)建一個ItemDataBound event handler,在每個category綁定到Repeater時調(diào)用這個方法然后將總數(shù)輸出。
在DataSet里更新CategoriesDataTable 添加一個NumberOfProducts列. 我們可以更新CategoriesDataTable的GetCategories()方法來包含這個信息或者保留這個方法,再創(chuàng)建一個新的名為GetCategoriesAndNumberOfProducts()方法。
我們來看看這兩種方法。第一種寫起來更簡單,因為我們不需要更新DAL。但是它需要和數(shù)據(jù)庫更多的連接。在ItemDataBound event handler里調(diào)用GetProductsByCategoryID(categoryID)方法又增加了一次數(shù)據(jù)庫連接(這在每個category綁定時會發(fā)生一次)。這時一共會有N+1次對數(shù)據(jù)庫的請求(N為Repeater里顯示的category的總數(shù))。而第二種方法product總數(shù)從GetCategories()(或GetCategoriesAndNumberOfProducts())方法返回,這樣只請求一次數(shù)據(jù)庫就可以了。
在ItemDataBound Event Handler里獲取Products總數(shù)
在ItemDataBound event handler里獲取product總數(shù)不需要修改DAL。只需要直接修改CategoriesAndProducts.aspx頁。通過Repeater的智能標(biāo)簽添加一個新的名為CategoriesDataSource的ObjectDataSource。使用CategoriesBLL類的GetCategories()方法配置它。
![](/d/20211017/e691b00067cf594d6f5086a995b453ac.gif)
圖 3: 配置 ObjectDataSource
Repeater里的每個Category都是可點的,而且在點了之后,CategoryProducts DataList會顯示那些相關(guān)的product。我們可以將每個category設(shè)為hyperlink,鏈到本頁(CategoriesAndProducts.aspx),通過querystring為CategoryID賦值。這種方法的好處是,特定category的product可以通為搜索建立索引和書簽。
我們也可以將每個category設(shè)為LinkButton,在本章我們使用這個方法。LinkButton看起來象一個hyperlink,但是點擊后會產(chǎn)生一個postback。DataList的ObjectDataSource會刷新以顯示選中category相關(guān)聯(lián)的product。在本章使用hyperlink更合理。然而在別的情況下可以使用LinkButton會好一點。雖然是這樣,我們在這里也使用LinkButton。我們將會看到,使用LinkButton會有一些使用hyperlink時碰不到的挑戰(zhàn)。因此我們可以學(xué)習(xí)更好學(xué)習(xí)它,以便以后使用。
注意:如果你使用HyperLink或a>來代替LinkButton來重復(fù)練習(xí)一次本章的內(nèi)容,是最好不過了。
下面的標(biāo)記語言是Repeater和ObjectDataSource的,注意Repeater的template將每個item表示為LinkButton。
asp:Repeater ID="Categories" runat="server" DataSourceID="CategoriesDataSource">
HeaderTemplate>
ul>
/HeaderTemplate>
ItemTemplate>
li>asp:LinkButton runat="server" ID="ViewCategory" />/li>
/ItemTemplate>
FooterTemplate>
/ul>
/FooterTemplate>
/asp:Repeater>
asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetCategories" TypeName="CategoriesBLL">
/asp:ObjectDataSource>
注意:在本章Repeater的view state必須開啟(Repeater的聲明語法里的EnableViewState="False")。在第三步我們將為ItemCommand事件創(chuàng)建一個event handler,在它里面我們要更新DataList的ObjectDataSource的SeleceParameters集合。如果view state 被禁用的話Repeater的ItemCommand不會被激發(fā)。想了解具體的原因和更多的信息請參考 A Stumper of an ASP.NET Question 和its solution 。
ID為ViewCategory的LinkButton還沒有設(shè)置Text屬性。如果我們只需要顯示category名字,我們可以通過綁定語法象下面這樣來直接設(shè)置:
asp:LinkButton runat="server" ID="ViewCategory"
Text='%# Eval("CategoryName") %>' />
然而在這里我們需要顯示的是category的name和proudct的總數(shù)。見下面的代碼:
protected void Categories_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
// Make sure we're working with a data item...
if (e.Item.ItemType == ListItemType.Item ||
e.Item.ItemType == ListItemType.AlternatingItem)
{
// Reference the CategoriesRow instance bound to this RepeaterItem
Northwind.CategoriesRow category =
(Northwind.CategoriesRow) ((System.Data.DataRowView) e.Item.DataItem).Row;
// Determine how many products are in this category
NorthwindTableAdapters.ProductsTableAdapter productsAPI =
new NorthwindTableAdapters.ProductsTableAdapter();
int productCount =
productsAPI.GetProductsByCategoryID(category.CategoryID).Count;
// Reference the ViewCategory LinkButton and set its Text property
LinkButton ViewCategory = (LinkButton)e.Item.FindControl("ViewCategory");
ViewCategory.Text =
string.Format("{0} ({1:N0})", category.CategoryName, productCount);
}
}
我們首先要確保我們處理的是data item(ItemType為Item或AlternatingItem)然后引用剛剛綁定到當(dāng)前RepeaterItem的CategoriesRow。然后調(diào)用GetCategoriesByProductID(categoryID)方法,通過Count屬性獲取返回的記錄條數(shù)。最后將ItemTemplate里的ViewCategory LinkButton的Text屬性設(shè)為"CategoryName(NumberOfProductsInCategory)"。
注意:我們也可以在ASP.NET頁的code-behind里寫一個格式化功能,接收CategoryName和CategoryID的值,返回CategoryName和product總數(shù)的連接字符串。然后將結(jié)果直接賦給LinkButton的Text屬性,而不需要處理itemDataBound事件。更多的格式化功能信息參考在GridView控件中使用TemplateField 和格式化DataList和Repeater的數(shù)據(jù)。添加完event handler后,在瀏覽器里看看頁面。見圖4。
![](/d/20211017/6888b21d54d4285e9e4e2c5b233314af.gif)
圖 4: 顯示每個 Category的 Name 和 Products總數(shù)
更新CategoriesDataTable和CategoriesTableAdpter來包含每個Category的Product總數(shù)除了在每個category綁定到Repeater時獲取product總數(shù)外,我們還可以修改DAL里CategoriesDataTable和CategoriesTableAdapter來包含這個信息.我們在CategoriesDataTable里加一列.打開App_Code/DAL/Northwind.xsd,右鍵點DataTable,選擇Add/Column.見圖5.
![](/d/20211017/f680851be7f4a4ffbf5393608029773b.gif)
圖 5: 為CategoriesaDataSource增加一個新列
這樣會添加一個名為Column1的列,你可以很方便的修改它的名字.將它重命名為NumberOfProducts.然后我們需要配置這列的屬性.點這個列,來到屬性窗口.將DataType從System.String修改為System.Int32.將ReadOnly屬性設(shè)為True.見圖6.
![](/d/20211017/149904f119d02d7100a9bbd6ee9185aa.gif)
圖 6: 設(shè)置新列的屬性
現(xiàn)在CategoriesDataTable里已經(jīng)包含了NumberOfProducts列,但它的值還沒有設(shè)置.我們可以修改GetCategories()方法,當(dāng)每次獲取category信息的時候返回它的信息.在這里由于只是本章用到了這個數(shù)據(jù),我們來創(chuàng)建一個新的名為GetCategoriesAndNumberOfProducts().右鍵點CategoriesTableAdapter,選擇New Query.會出現(xiàn)TableAdapter Query配置向?qū)?選擇SQL statement.
![](/d/20211017/e967b0f2edcd76f4c81f97163f352627.gif)
圖 7: 選擇SQL Statement
![](/d/20211017/cb29c2a18928c26dfffe292d3d8b8179.gif)
圖 8: SQL Statement 返回行數(shù)
下一步需要我們寫sql語句.下面的語句返回每個category的CategoryID,CategoryName,Description和相關(guān)product的總數(shù):
SELECT CategoryID, CategoryName, Description,
(SELECT COUNT(*) FROM Products p WHERE p.CategoryID = c.CategoryID)
as NumberOfProducts
FROM Categories c
![](/d/20211017/6c1583a0920bbd5de990651f0a3115ab.gif)
圖 9: 使用的sql語句
注意計算product總數(shù)的子查詢的別名為NumberOfProducts.它和CategoriesDataTable的NumberOfProducts列關(guān)聯(lián).最后一步是寫方法的名字.分別為Fill a DataTable和Return a DataTable命名為FillWithNumberOfProducts和GetCategoriesAndNumberOfProducts.
![](/d/20211017/3669e0e5bab2de8774e0d66681235465.gif)
圖 10: 為新的TableAdapter的方法命名
現(xiàn)在DAL已經(jīng)修改完了.由于我們所有展現(xiàn)層,BLL,DAL是逐層調(diào)用,所以我們需要在CategoriesBLL類的添加相應(yīng)的GetCategoriesAndNumberOfProducts方法.
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Select, false)]
public Northwind.CategoriesDataTable GetCategoriesAndNumberOfProducts()
{
return Adapter.GetCategoriesAndNumberOfProducts();
}
完成DAL和BLL后,我們來將數(shù)據(jù)綁定到Categories Repeater.如果在"在ItemDataBound Event Handler里獲取Products總數(shù)"那部分里你已經(jīng)為Repeater創(chuàng)建了ObjectDataSource,刪掉它,然后去掉Repeater的DataSourceID屬性,同樣去掉ItemDataBound事件.Repeater現(xiàn)在回到了初始狀態(tài),添加一個名為CategoriesDataSource的ObjectDataSource.使用CategoriesBLL類的GetCategoriesAndNumberOfProducts()方法來配置它.見圖11.
![](/d/20211017/2947e5b21f5b72f00360a88ce4bf5e97.gif)
圖 11: 配置ObjectDataSource
然后修改ItemTemplate,使用數(shù)據(jù)綁定語法來將CategoryName和NumberOfProducts字段綁定到LinkButton的Text屬性.完整的標(biāo)記語言如下:
asp:Repeater ID="Categories" runat="server" DataSourceID="CategoriesDataSource">
HeaderTemplate>
ul>
/HeaderTemplate>
ItemTemplate>
li>asp:LinkButton runat="server" ID="ViewCategory"
Text='%# String.Format("{0} ({1:N0})", _
Eval("CategoryName"), Eval("NumberOfProducts")) %>' />
/li>
/ItemTemplate>
FooterTemplate>
/ul>
/FooterTemplate>
/asp:Repeater>
asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetCategoriesAndNumberOfProducts" TypeName="CategoriesBLL">
/asp:ObjectDataSource>
使用這種方法的頁面看起來和前面一種方法一樣(見圖4).
第三步: 顯示選中的Category關(guān)聯(lián)的Products
現(xiàn)在category和product總數(shù)的部分已經(jīng)完成.Repeater將每個category顯示為LinkButton,當(dāng)點擊時產(chǎn)生postback,這時我們需要將那些關(guān)聯(lián)的product在CategoryProducts DataList里顯示出來.
現(xiàn)在我們面臨的一個挑戰(zhàn)是如何將特定category下的product在DataList里顯示出拉一.在使用GridView 和DetailView實現(xiàn)的主/從報表一章里我們學(xué)習(xí)了創(chuàng)建一個GirdView,當(dāng)選擇它的一行時將"從"信息在本頁的DetailsView里顯示出來.GridView的ObjectDataSource用ProductsBLL的GetProducts()返回product信息.而DetailsView的ObjectDataSource用GetProductsByProductID(productID)返回選中的product信息.productID參數(shù)通過GirdView的SelectedValue屬性來提供.不幸的是,Repeater沒有SelectedValue屬性.
注意:這是我們在Repeater里使用LinkButton的其中一個挑戰(zhàn).如果我們使用hperlink,可以通過querystring來傳遞CategoryID.在我們解決這個問題前,首先將ObjectDataSource綁定到DataList,然后指定ItemTemplate.從DataList的智能標(biāo)簽添加一個名為CategoryProductsDataSource的ObjectDataSource,并使用ProductsBLL類的GetProductsByCategoryID(cateogryID)配置它.由于此DataList只提供只讀功能,因此在INSERT,UPDATE,DELETE標(biāo)簽里選擇None.
![](/d/20211017/5eecc729ae522cf2cd26442defd9b27a.gif)
圖 12: 配置 ObjectDataSource
由于GetProductsByCategoryID(categoryID)方法需要一個輸入?yún)?shù),向?qū)笪覀冎付▍?shù)源.我們使用GridView或DataList列出categories時,可以將參數(shù)源設(shè)為Control,ControlID設(shè)為數(shù)據(jù)控件的ID.然而由于Repeater沒有SelectedValue屬性,所以不能用作參數(shù)源.你可以查看ControlID下拉列表,它里面只包含一個控件ID—CategoryProducts(DataList).
![](/d/20211017/4dce5105b07ded51de6d305861f24da1.gif)
圖 13: 配置參數(shù)
配置完數(shù)據(jù)源后,Visual Studio為DataList自動產(chǎn)生ItemTemplate.用我們前面使用的template替換默認(rèn)的ItemTemplate.將DataList的RepeatColumns屬性設(shè)為2.完成這些后,你的代碼應(yīng)該和下面的差不多:
asp:DataList ID="CategoryProducts" runat="server" DataKeyField="ProductID"
DataSourceID="CategoryProductsDataSource" RepeatColumns="2"
EnableViewState="False">
ItemTemplate>
h5>%# Eval("ProductName") %>/h5>
p>
Supplied by %# Eval("SupplierName") %>br />
%# Eval("UnitPrice", "{0:C}") %>
/p>
/ItemTemplate>
/asp:DataList>
asp:ObjectDataSource ID="CategoryProductsDataSource"
OldValuesParameterFormatString="original_{0}" runat="server"
SelectMethod="GetProductsByCategoryID" TypeName="ProductsBLL">
SelectParameters>
asp:Parameter Name="categoryID" Type="Int32" />
/SelectParameters>
/asp:ObjectDataSource>
目前為止CategoryProductsDataSource ObjectDataSource的categoryID參數(shù)還沒有設(shè)置.所以瀏覽頁面時沒有任何的product顯示出來.我們現(xiàn)在需要將它設(shè)置為Repeater中的被點擊的category的CategoryID.這里有兩個問題,第一是我們?nèi)绾闻袛嗍裁磿r候Repeater的ItemTemplate被點了.二是哪個被點了.
和Button,ImageButton一樣,LinkButton有一個Click event和一個Command event.Click事件僅僅用來說明LinkButton被點擊了.有時候我們需要傳遞更多的信息到event handler里.這樣的話,就需要使用LinkButton的CommandName 和CommandArgument .當(dāng)LinkButton被點時,Command事件激發(fā),event handler會接受CommandName和CommandArgument的值.
當(dāng)Repeater里的template里激發(fā)了一個Command事件時,Rpeater的ItemCommand事件被激發(fā).并將被點擊的LinkButton(或者Button和ImageButton)的CommandName和CommandArgument的值傳進(jìn)來.因此,判斷category LinkButton什么時候被點擊了,我們需要:
設(shè)置Rpeater里的ItemTemplate的LinkButton的CommandName屬性(我使用的"ListProducts").設(shè)置了值后LinkButton被點后Command事件會激發(fā).
設(shè)置LinkButton的CommandArgument屬性為當(dāng)前item的CategoryID.
為Repeater的ItemCommand事件創(chuàng)建一個event handler.在它里面將傳入的CommandArgument值賦給CategoryProductsDataSource ObjectDataSource的CategoryID參數(shù).
下面是完成了1,2步后的標(biāo)記.注意CategoryID是如何通過綁定語法來賦給CommandArgument的.
ItemTemplate>
li>
asp:LinkButton CommandName="ListProducts" runat="server"
CommandArgument='%# Eval("CategoryID") %>' ID="ViewCategory"
Text='%# string.Format("{0} ({1:N0})", _
Eval("CategoryName"), Eval("NumberOfProducts")) %>'>
/asp:LinkButton>
/li>
/ItemTemplate>
由于任何一個Button,LinkButton或ImageButton的Command事件都會激發(fā)ItemCommand事件,所以無論在任何時候創(chuàng)建ItemCommand event handler首先都要小心謹(jǐn)慎的檢查CommandName的值.而由于我們現(xiàn)在只有一個LinkButton,以后我們可能會向Repeater添加新的button控件,當(dāng)點被點擊時,激發(fā)同樣的ItemCommand event handler.因此最好確保檢查了CommandName,然后根據(jù)它的值來進(jìn)行邏輯處理.
在確保了傳入的CommandName的值等于"ListProducts"后,event handler將CategoryProductsDataSource ObjectDataSource的CategoryID的參數(shù)設(shè)為傳入的CommandArgument.對ObjectDataSource的SelectParameters的修改自動引起DataList重新綁定到數(shù)據(jù)源,顯示新的選中的category關(guān)聯(lián)的product.
protected void Categories_ItemCommand(object source, RepeaterCommandEventArgs e)
{
// If it's the "ListProducts" command that has been issued...
if (string.Compare(e.CommandName, "ListProducts", true) == 0)
{
// Set the CategoryProductsDataSource ObjectDataSource's CategoryID parameter
// to the CategoryID of the category that was just clicked (e.CommandArgument)...
CategoryProductsDataSource.SelectParameters["CategoryID"].DefaultValue =
e.CommandArgument.ToString();
}
}
做完這些后,本章就結(jié)束了!現(xiàn)在在瀏覽器里看看你的頁面.圖14是第一次瀏覽時的樣子.因為還沒有category被選中,所以沒有product顯示出來.點擊一個category,比如Produce,和它關(guān)聯(lián)的product以兩列的方式顯示出來.見圖15.
![](/d/20211017/0153ff0fc800741bc0f185394003dbbb.gif)
圖 14:第一次瀏覽頁面時沒有Product顯示
![](/d/20211017/ddc59cb6c357c7734e28a1cdf69b6008.gif)
圖 15: 點擊Produce Category 后,相關(guān)的 Products 在右邊顯示出來
總結(jié)
我們在本章和前面一章里學(xué)習(xí)了主/從表可以分別顯示在兩個頁或者一起顯示在一個頁.如果顯示在一個頁上,我們需要考慮如何來控制它們的外觀.在使用GridView 和DetailView實現(xiàn)的主/從報表一章我們將從記錄顯示在主記錄之上,而在本章我們使用CSS將主記錄顯示在從記錄的左邊.我們還探討了如何獲取每個category關(guān)聯(lián)的product數(shù)量,以及在點擊Repeater里的LinkButton(或ButtonImageButton)時服務(wù)器端的處理邏輯.
到這里為止使用DataList和Repeater來顯示主/從表已經(jīng)完成了.后面我們將演示如何在DataList里添加編輯和刪除的功能.
祝編程愉快!
您可能感興趣的文章:- Repeater中添加按鈕實現(xiàn)點擊按鈕獲取某一行數(shù)據(jù)的方法
- 在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和Repeater使用DropDownList過濾的主/從報表
- 在ASP.NET 2.0中操作數(shù)據(jù)之三十四:基于DataList和Repeater跨頁面的主/從報表
- 在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和Repeater數(shù)據(jù)排序(二)
- 在ASP.NET 2.0中操作數(shù)據(jù)之四十四:DataList和Repeater數(shù)據(jù)排序(三)
- 在ASP.NET 2.0中操作數(shù)據(jù)之四十五:DataList和Repeater里的自定義Button