導言
在前面的教程裡我們學習了DataList提供了一些風格樣式的屬性.而且我們還學習了如何定義HeadStyle, ItemStyle, AlternatingItemStyle, 和SelectedItemStyle等屬性的默認CSS.除了這四個屬性外,DataList還提供了其它屬性,比如Font, ForeColor, BackColor, 和BorderWidth.而Repeater沒有提供任何這樣的屬性.如果你需要用Reperter來實現這些效果,你就需要在templates裡直接寫標記語言.
通常,數據需要怎樣的格式取決於數據本身.比如,我們可能使用灰色的字體列出那些被停止使用的product,或者在UnitsInStock等於0的時候顯示高亮.前面的教程裡我們已經學習了GridView, DetailsView, 和FormView 都提供了兩種截然不同的格式化數據的方法.
DataBound 事件— 為DataBound 事件創建一個合適的event handler, 它在數據綁定到item的時候激發(對GridView來說是RowDataBound 事件; 對 DataList 和Repeater來說是 ItemDataBound 事件). 在這些事件裡, 剛剛綁定的數據可以被格式化. 參見《基於數據的自定義格式化》 這章.
Templates 的格式化功能— 在DetailsView 或GridView 裡使用TemplateFields , 或 在FormView 裡使用template , 我們可以在ASP.NET page的code-behind class裡或者BLL裡,或者任何其它web程序裡可以調用的類庫裡加格式化信息. 這種格式化功能可以接收任意的輸入參數, 但是在template裡比如返回HTML . 格式化功能最早在在GridView控件中使用TemplateField 這章裡談到過. 這兩種方法都可以在DataList和Repeater裡使用.在本章裡我們將一步步用這兩種方法在這兩個控件裡做示例.
使用 ItemDataBound Event Handler
當數據綁定到 DataList時, 無論是使用數據源控件或者 直接在代碼裡使用DataSource 和 DataBind() , DataList的DataBinding 事件都會被激發. DataList 為數據源的每條記錄創建一個 DataListItem 對象,然後綁定到當前記錄. 在這個過程中DataList 激發兩個事件:
ItemCreated — 在創建DataListItem 後激發
ItemDataBound — 當前記錄綁定到DataListItem 後激發
下面列出了DataList數據綁定過程的大概步驟
DataList的DataBinding event 被激發
DataList
對數據源的每條記錄...
For each record in the data source…
創建一個DataListItem 對象
激發ItemCreated event
綁定記錄到DataListItem
激發ItemDataBound event
將DataListItem 添加到Items collection
當數據綁定到Repeater時,和上面所說的情況一樣.唯一的區別在於,DataListItem換成了RepeaterItem.
注意:細心的讀者可能注意到了DataList和Repeater綁定到數據時的步驟順序和GridView有些許差別.在數據綁定過程的後期,GridView會激發DataBound事件,而DataList和Repeater則都沒有這個事件.
和GridView一樣,可以為ItemDataBound事件創建一個event handler 來格式化數據.這個event handler 可以處理剛剛綁定到DataListItem或RepeaterItem的數據,來按照需要進行格式化.
對DataList來說,可以使用風格樣式相關的屬性,如Font, ForeColor, BackColor, CssClass等,來格式化item.而如果你想格式化Datalist裡的template裡的web控件,你需要編程去獲取這些控件,然後來控制.我們在《Custom Formatting Based Upon Data》裡已經看過怎樣做.和Repeater控件一樣,RepeaterItem類也沒有風格樣式相關的屬性,因此,你需要在ItemDataBound event handler裡編程去實現.
由於在DataList和Repeater裡使用ItemDataBound格式化技術從本質上來說是由於的,因此我們的示例主要講DataList.
第一步: 在DataList顯示Product 信息
在學習格式化之前,我們首先創建一個使用DataList顯示product信息的頁面.在前面一章裡,我們創建了一個ItemTemplate顯示product 的name,category, supplier, quantity和price的DataList.我們在本章來重復做一次.你可以重新創建DataList和它的ObjectDataSource ,或者直接把前面一章裡的Basics.aspx裡的控件復制到本章的頁面(Formatting.aspx)裡.當你完成了Formatting.aspx後,將DataList的ID從DataList1改為ItemDataBoundFormattingExample.下面,在浏覽器裡看看DataList.如圖1所示,唯一的格式在於每個product的交替的背景色.
圖 1: 在DataList 裡列出product信息
在本章教程裡,我們來將價格小於 $20.00 的product的名字和單價用黃色 高亮來顯示.
第二步: 在 ItemDataBound Event Handler裡編程判斷數據的值
由於只有價格低於$20.00 的product會被格式化,因此我們首先要判斷每個product的價格.在綁定數據到DataList時,DataList 為每條數據源的記錄創建一個DataListItem實例,並綁定數據.當記錄綁定到DataListItem對象後,ItemDataBound事件被激發.我們可以為這個事件創建一個event handler來判斷當前DataListItem的值,再根據這個值來格式化數據.
添加以下代碼為DataList創建ItemDataBound事件
protected void ItemDataBoundFormattingExample_ItemDataBound (object sender, DataListItemEventArgs e) { if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem) { // Programmatically reference the ProductsRow instance bound // to this DataListItem Northwind.ProductsRow product = (Northwind.ProductsRow)((System.Data.DataRowView)e.Item.DataItem).Row; // See if the UnitPrice is not NULL and less than $20.00 if (!product.IsUnitPriceNull() && product.UnitPrice < 20) { // TODO: Highlight the product's name and price } } }
DataList的ItemDataBound event handler在概念和語義上來說,和GridView的RowDataBound event handler一樣(見基於數據的自定義格式化),語法上有一點差別.當ItemDataBound事件激發時,剛剛綁定數據的DataListItem通過e.Item(在GridView裡是e.Row和RowDataBound)傳遞給相關的event handler.DataList的ItemDataBound event handler影響到每一行,包括 header , footer 和separator.但是product信息只綁定到data行.因此,在處理ItemDataBound事件前,我們首先要判斷處理的是否是data行.這個可以通過檢查DataListItem的ItemType 屬性來完成,它可以有以下八個值:
AlternatingItem
EditItem
Footer
Header
Item
Pager
SelectedItem
Separator
Item和AlternatingItem都表示DataList的data item.假設我們在處理Item或AlternatingItem,我們可以獲取綁定到當前DataListItem的ProductsRow的實例.DataListItem的DataItem屬性包含了DataRowView對象的引用,通過它的Row屬性可以獲取ProductsRow對象.
下面我們來檢查ProductsRow實例的單價屬性.由於Product表的UnitPrice字段允許空值,所以在獲取UnitPrice屬性前我們應該先用IsUnitPriceNull()方法檢查這個值是否為空.如果不是,我們再檢查看它是否低於$20.00.如果是,我們就進行格式化處理.
第三步: 是Product的 Name 和Price高亮顯示
一旦我們發現Product的price低於$20.00,我們將使它的name和price顯示高亮.首先我們要編程獲得ItemTemplate裡顯示Product的name和price的Label控件.然後我們將它的背景色顯示為黃色.這個可以直接通過修改Label空間的BackColor屬性(LabelID.BackColor = Color.Yellow).當然最理想的做法是所有的顯示相關的行為都通過CSS來實現.實際上我們在基於數據的自定義格式化一章裡創建的Styles.css - AffordablePriceEmphasis已經提供了這個功能.
使用以下代碼設置兩個Label控件的CssClass 屬性為AffordablePriceEmphasis來完成格式化:
// Highlight the product name and unit price Labels // First, get a reference to the two Label Web controls Label ProductNameLabel = (Label)e.Item.FindControl("ProductNameLabel"); Label UnitPriceLabel = (Label)e.Item.FindControl("UnitPriceLabel"); // Next, set their CssClass properties if (ProductNameLabel != null) ProductNameLabel.CssClass = "AffordablePriceEmphasis"; if (UnitPriceLabel != null) UnitPriceLabel.CssClass = "AffordablePriceEmphasis";
ItemDataBound 事件完成後,在浏覽器裡浏覽Formatting.aspx頁.如圖2所示,價格低於 $20.00 的product的name和prict都高亮顯示了.
圖2: 價格低於$20.00 的product都被高亮顯示
注意:由於DataList使用 HTML <table>, DataListItem實例有可以設置整個item風格的屬性.比如,如果我們想在price低於$20.00時將所有的item都用黃色來高亮顯示,我們可以用e.Item.CssClass = "AffordablePriceEmphasis"來代替上面的代碼(見圖3).
而組成Repeater的RepeaterItem並沒有提供這樣的屬性.因此,在Repeater裡自定義格式需要設置templates裡的控件的格式,象在圖2裡所做的那樣.
圖 3: The Entire Product Item is Highlighted for Products Under $20.00
使用 Template的格式化功能
在在GridView控件中使用TemplateField 一章裡,我們學習了如何使用GridView TemplateField的格式化功能來格式化GridView的數據.格式化功能是一種可以從template裡調用並返回HTML顯示的方法.格式化功能可以寫在ASP.NET page的 code-behind class 或App_Code 文件夾裡的類文件裡或單獨的類庫項目裡.如果你想在其它ASP.NET web程序或多個ASP.NET 頁用到同樣的功能,那麼不要把它下在ASP.NET page的 code-behind class 裡.
為了演示這個功能,我們將修改product信息.如果product被停用,我們在product的name後面增加一個“[DISCONTINUED]”的text.同樣的,如果price低於 $20.00 我們將會用黃色來高亮顯示(如我們在ItemDataBound event handler例子裡做的那樣).如果price等於或高於 $20.00,我們將不顯示實際的價格,而是在text裡顯示“Please call for a price quote”. 圖4是完成以上功能的頁面截圖.
圖 4: 將比較貴的Products 的價格用文本“Please call for a price quote”來代替.
第一步: 創建格式化功能
這個例子裡我們需要兩個格式化功能,其一是在被停用的product name後面加上“[DISCONTINUED]”, 其二是對價格低於$20.00的product高亮顯示,其它則顯示“Please call for a price quote”.我們將在ASP.NET page的code-behind class 裡創建這些功能,並給它們取名為DisplayProductNameAndDiscontinuedStatus 和DisplayPrice.這兩個方法都需要返回HTML,而且為了在ASP.NET page的聲明語法裡調用,都需要標記為Protected (or Public).下面是這兩個方法的代碼:
protected string DisplayProductNameAndDiscontinuedStatus (string productName, bool discontinued) { // Return just the productName if discontinued is false if (!discontinued) return productName; else // otherwise, return the productName appended with the text "[DISCONTINUED]" return string.Concat(productName, " [DISCONTINUED]"); } protected string DisplayPrice(Northwind.ProductsRow product) { // If price is less than $20.00, return the price, highlighted if (!product.IsUnitPriceNull() && product.UnitPrice < 20) return string.Concat("<span class=\"AffordablePriceEmphasis\">", product.UnitPrice.ToString("C"), "</span>"); else // Otherwise return the text, "Please call for a price quote" return "<span>Please call for a price quote</span>"; }
注意到DisplayProductNameAndDiscontinuedStatus 方法接收productName 和discontinued 的值.而DisplayPrice 方法接收ProductsRow (而不是UnitPrice).如果格式化功能處理可能包含數據庫空值(比如UnitPrice,而ProductName和Discontinued都不允許空)的量值,要特別小心處理.
輸入的值可能是一個DBNull而不是你期望的數據類型,因此輸入參數的類型必須為Object.而且比如檢查傳進來的值是否為database NULL.也就是說,如果我們想讓DisplayPrice 方法以價格為參數,我們需要以下代碼:
protected string DisplayPrice(object unitPrice) { // If price is less than $20.00, return the price, highlighted if (!Convert.IsDBNull(unitPrice) && ((decimal) unitPrice) < 20) return string.Concat("<span class=\"AffordablePriceEmphasis\">", ((decimal) unitPrice).ToString("C"), "</span>"); else // Otherwise return the text, "Please call for a price quote" return "<span>Please call for a price quote</span>"; }
注意輸入參數UnitPrice的類型為Object,條件判斷語句被修改為判斷unitPrice 是否為DBNull.而且,由於UnitPrice是作為Object傳進來的,所以必須要類型轉換為decimal.
第二步: 在DataList 的ItemTemplate調用格式化方法
在完成以上代碼後,剩下的工作就是在DataList的ItemTemplate裡調用這些格式化功能.我們需要使用以下代碼:
<%# MethodName(inputParameter1, inputParameter2, ...) %>
在DataList的ItemTemplate裡,ProductNameLabel Label通過指定text屬性為<%# Eval("ProductName") %>顯示的product的name.為了在需要的情況下加上“[DISCONTINUED]” ,修改代碼,使用DisplayProductNameAndDiscontinuedStatus 方法來指定text屬性.我們需要使用Eval("columnName") 語法來將product的name和discontinued的值傳進去.Eval 返回的值為Object類型,而DisplayProductNameAndDiscontinuedStatus 的參數為String 和Boolean.因此,我們需要將Eval 方法返回的值轉換為需要的參數類型,代碼如下:
<h4> <asp:Label ID="ProductNameLabel" runat="server" Text='<%# DisplayProductNameAndDiscontinuedStatus((string) Eval("ProductName"), (bool) Eval("Discontinued")) %>'> </asp:Label> </h4>
和顯示product的name和“[DISCONTINUED]” 文本一樣,我們設置UnitPriceLabel label的屬性為DisplayPrice 的返回值來顯示價格.我們將ProductsRow作為參數,而不是UnitPrice:
<asp:Label ID="UnitPriceLabel" runat="server" Text='<%# DisplayPrice((Northwind.ProductsRow) ((System.Data.DataRowView) Container.DataItem).Row) %>'> </asp:Label>
完成以上代碼後,在浏覽器裡看一下頁面.你的頁面應該和圖5看起來差不多.
圖 5: 將比較貴的Products 的價格用文本“Please call for a price quote”來代替
總結
基於數據格式化DataList或Repeater有兩種方法.一種是為ItemDataBound 創建event handler .ItemDataBound 在數據源的每條記錄綁定到DataListItem 或RepeaterItem時被激發.在ItemDataBound event handler裡,可以判斷當前item的數據並格式化,而對DataListItem可以格式化整個item.
另一種是通過格式化功能來完成自定義格式化.格式化功能是一種可以從template裡調用並返回HTML顯示的方法.通常,通過判斷綁定到當前item的值來決定返回什麼樣的HTML.這些值或者綁定到item的對象可以傳遞到格式化功能裡.祝編程快樂!
作者簡介
Scott Mitchell,著有六本ASP/ASP.NET方面的書,是4GuysFromRolla.com的創始人,自1998年以來一直應用 微軟Web技術。Scott是個獨立的技術咨詢顧問,培訓師,作家,最近完成了將由Sams出版社出版的新作,24小時內精通ASP.NET 2.0。他的聯系電郵為[email protected],也可以通過他的博客http://scottonwriting.net/與他聯系。