導言
比起BoundField、CheckBoxField、HyperLinkField以及其他的那些數據字段控件(data field controls)來說,TemplateField提供了一種高度復雜的數據呈現的方法。在上一節中,我們主要著重於在GridVIew中使用TemplateField,以實現:
·在一列中顯示多個數據字段。比如說,將FirstName和LastName字段合並起來顯示在一個GridView列中。
·使用交互Web控件來展示數據。我們看到了如何使用一個Calendar控件來顯示HiredDate的值。
·顯示基於潛在數據的狀態信息。盡管Employees表中並沒有包含一個關於雇員在公司干了多久的數據列,但我們仍然可以使用TemplateField 和格式化方法在GridView中實現這樣的功能,就像我們在上一節中做的那樣。
就像在GridView中那樣,DetailsView控件也可以同樣的使用TemplateField。在本節教程中,我們將使用一個包含兩個TemplateField的DetailsView來一次一個的顯示產品信息。第一個TemplateField將整合UnitPrice、UnitsInStock和UnitsOnOrder等數據並顯示在一個DetailsView行上。第一個TemplateField則將顯示Discontinued的數據,不過將使用格式化方法,在有折扣的時候就顯示“YES”,否則就顯示“NO”。
圖一:使用兩個模板列來自定義顯示
好了,讓我們開始吧!
第一步:將數據綁定到DetailsView
像前一節中所討論的那樣,要使用TemplateField最簡單的辦法就是先創建一個僅包含BoundField的DetailsView控件,然後添加新的TemplateField或是將某些BoundField轉換成TemplateField。因此,我們先通過設計器向頁面上添加一個DetailsView控件,並綁定一個返回產品列表的ObjectDataSource給它。這些操作將創建一個帶有BoundField和CheckBoxField 的DetailsView,BoundField用於非布爾值,CheckBoxField當然就是用於布爾值了(比如說“是否打折”)。
打開DetailsViewTemplateField.aspx頁面,從工具箱中拖一個DetailsView到設計器上。從DetailsView的智能標簽(smart tag)上選擇並添加一個新的調用ProductsBLL類的GetProducts ()方法的ObjectDataSource控件。
圖二:添加一個新的調用GetProducts ()方法的ObjectDataSource控件
在這個報表中,刪除ProductID、SupplierID、CategoryID以及ReorderLevel等BoundField。然後,調整剩下的BoundField的順序以使CategoryName和SupplierName跟在ProductName後面。然後設置各BoundField的HeaderText和formatting屬性,不用緊張,隨便填,只要你覺得爽就行。就像在GridView中那樣,可以通過字段對話框或是直接修改聲明代碼(declarative syntax。譯者注:直譯應該是什麼?聲明語法?不管怎麼樣,反正就是HTML視圖裡面的那些東西)來編輯這些綁定列。最後,清空DetailsView的Height和Width屬性,這樣可以使其根據需要顯示的數據來自動擴展,另外,再在智能標簽中選上“啟用分頁(Enable Paging)”復選框。
在做了這些更改之後,你的DetailsView控件的聲明標記應該像下面這樣:
<asp:DetailsView ID="DetailsView1" runat="server" AutoGenerateRows="False" DataKeyNames="ProductID" DataSourceID="ObjectDataSource1" AllowPaging="True" EnableViewState="False"> <Fields> <asp:BoundField DataField="ProductName" HeaderText="Product" SortExpression="ProductName" /> <asp:BoundField DataField="CategoryName" HeaderText="Category" ReadOnly="True" SortExpression="CategoryName" /> <asp:BoundField DataField="SupplierName" HeaderText="Supplier" ReadOnly="True" SortExpression="SupplierName" /> <asp:BoundField DataField="QuantityPerUnit" HeaderText="Qty/Unit" SortExpression="QuantityPerUnit" /> <asp:BoundField DataField="UnitPrice" HeaderText="Price" SortExpression="UnitPrice" /> <asp:BoundField DataField="UnitsInStock" HeaderText="Units In Stock" SortExpression="UnitsInStock" /> <asp:BoundField DataField="UnitsOnOrder" HeaderText="Units On Order" SortExpression="UnitsOnOrder" /> <asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued" SortExpression="Discontinued" /> </Fields> </asp:DetailsView>
同樣,我們還是花點時間到浏覽器中看看效果吧!現在,你可以看到一個單獨的產品(Chai),它包括一些顯示其屬性的行:名稱、分類、供應商、庫存量、訂貨量還有它的打折狀態。
圖三:使用一組綁定列來顯示產品明細
第二步:將單價、庫存量和訂貨量合並在一列中
DetailsView有一列用於UnitPrice、UnitsInStock和UnitsOnOrder。通過TemplateField可以將這3個數據合並到一行中,你可以添加一個新的TemplateField,也可以將UnitPrice、UnitsInStock或UnitsOnOrder任何一個BoundField直接轉換成TemplateField。雖然我個人還是喜歡將已有的BoundField轉換成TemplateField這種方式,不過這裡我們還是來聯系一下添加新的TemplateField吧。
在DetailsView的智能標簽的彈出菜單中點擊“編輯字段(Edit Fields)”。在彈出的字段對話框中,添加一個新的TemplateField並將其HeaderText屬性設置為“Price and Inventory”,然後將這個新的TemplateField移動到UnitPrice的上面。
圖四:給DetailsView控件添加一個模板列
由於新添加的TemplateField將要顯示UnitPrice、UnitsInStock以及UnitsOnOrder等BoundField中的數據,所以讓我們先把這幾個BoundField刪了。
這一個步驟的最後一個任務是定義“Price and Inventory”這個TemplateField 的ItemTemplate,你可以通過設計器中DetailsView的模板編輯界面也可以手工編寫聲明代碼來完成這個任務。就像GridView那樣,在智能標簽的彈出菜單中點擊“編輯模板”( Edit Templates),就可以使用模板編輯界面了。這裡你可以在下拉框中選擇你想要編輯的模板並從工具箱中添加任何你喜歡的Web控件。
在本教程中,首先給“Price and Inventory”模板列的ItemTemplate添加一個Label。然後,在Label控件的智能標簽上點擊“編輯數據綁定(Edit DataBindings)”並將其Text屬性綁定到UnitPrice字段上。
圖五:將Label的Text屬性綁定到UnitPrice字段上
將單價格式化為貨幣形式,做了這個操作之後,“Price and Inventory”模板列的Label上僅顯示了所選產品的單價。圖六向我們展示了到現在為止我們所做的成果。
圖六:“單價和總量”模板列顯示了單價
注意產品的單價現在還沒有格式化為貨幣格式。如果是一個BoundField,格式化可以通過將HtmlEncode屬性設置為false並將DataFormatString屬性設置為“{0:formatSpecifier}”來實現。然而,在TemplateField中,任何格式化說明都必須在數據綁定語法中指定或是通過使用一個在應用程序的某個地方(比如說在ASP.NET頁面的後置代碼類中)編寫的格式化方法。
要指定Label的數據綁定代碼中的格式化,可以在Label的智能標簽中點擊“編輯數據綁定(Edit DataBindings)”,然後在彈出的數據綁定對話框中的格式(Format)下拉框直接輸入格式化說明或選擇一個預定義的格式化字符串。就像BoundField的DataFormatString屬性那樣,格式化使用{0:formatSpecifier}來進行指定。為了使UnitPrice字段使用貨幣格式,我們可以在那個下拉框中選擇一個合適值,也可以直接輸入“{0:C}”。
圖七:將單價格式化為貨幣形式
說明一下,格式化說明是Bind或Eval方法的第二個參數。剛才通過設計器添加的這個設置表現為以下的標記語言:<asp:Label ID="Label1" runat="server" Text='<%# Eval("UnitPrice", "{0:C}") %>'></asp:Label>將剩下的數據字段添加到TemplateField中
現在,我們已經在“Price and Inventory”模板列中顯示並格式化了UnitPrice字段,不過我還需要將UnitsInStock和UnitsOnOrder顯示出來。讓我們把它們顯示在單價的下面一行的圓括號中吧!在設計器的模板編輯器中,這些用於顯示的標記語言可以簡單的用鍵盤輸入,當然你需要先將光標定位到模板中的某個位置。另外,你也可以直接在聲明代碼中直接輸入。
添加了靜態的標記語言、Label控件以及數據綁定代碼,所以“Price and Inventory”模板列可以像下面這樣顯示單價和總量信息:
單價(In Stock / On Order: 庫存量 / 訂貨量)
做了這些之後,你的DetailsView的聲明標記代碼應該像這個樣子:
<asp:DetailsView ID="DetailsView1" runat="server" AutoGenerateRows="False" DataKeyNames="ProductID" DataSourceID="ObjectDataSource1" AllowPaging="True" EnableViewState="False"> <Fields> <asp:BoundField DataField="ProductName" HeaderText="Product" SortExpression="ProductName" /> <asp:BoundField DataField="CategoryName" HeaderText="Category" ReadOnly="True" SortExpression="CategoryName" /> <asp:BoundField DataField="SupplierName" HeaderText="Supplier" ReadOnly="True" SortExpression="SupplierName" /> <asp:BoundField DataField="QuantityPerUnit" HeaderText="Qty/Unit" SortExpression="QuantityPerUnit" /> <asp:TemplateField HeaderText="Price and Inventory"> <ItemTemplate> <asp:Label ID="Label1" runat="server" Text='<%# Eval("UnitPrice", "{0:C}") %>'></asp:Label> <br /> <strong> (In Stock / On Order: </strong> <asp:Label ID="Label2" runat="server" Text='<%# Eval("UnitsInStock") %>'></asp:Label> <strong>/</strong> <asp:Label ID="Label3" runat="server" Text='<%# Eval("UnitsOnOrder") %>'> </asp:Label><strong>)</strong> </ItemTemplate> </asp:TemplateField> <asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued" SortExpression="Discontinued" /> </Fields> </asp:DetailsView>
做了這些修改之後,我們已經把單價和總量信息統一的顯示在一個單獨的DetailsView行中了。
圖八:單價和總量信息顯示在一個單獨的行上了
第三步:自定義折扣字段的信息
Products表的Discontinued字段是一個BIT值,它指明一個產品是否打折。當把一個DetailsView(或者GridView)綁定到一個數據源控件時,布爾型的字段(比如說Discontinued)會被實現為CheckBoxField,而非布爾型的字段(比如說ProductID、ProductName等等)將被實現為BoundField。CheckBoxField被呈現為一個禁用的CheckBox,如果數據的值為true則CheckBox為選中狀態,否則就是未選中狀態。
比起顯示一個CheckBoxField,可能我們更希望將其顯示為一個文本以說明這個產品是不是有折打。要做到這一點,我們可以從DetailsView中刪掉這個CheckBoxField,再添加一個BoundField,並將其DataField屬性設置到Discontinued上。嗯,花點時間完成它!做完這個改變後,DetailsView對那些打折的產品就顯示一個“True”,而對其他的就顯示一個“False”。
圖九:字符串“True”和“False”用來顯示打折狀態
想想一下,我們不想用“True”或者“False”,而是想要“YES”和“NO”。這樣的自定義可以由一個TemplateField和一個格式化方法來實現。格式化方法可以接受若干個輸入參數,但是智能返回一個用於插入到模板中的HTML(字符串類型的)
在DetailsViewTemplateField.aspx頁面的後置代碼類中添加一個名為DisplayDiscontinuedAsYESorNO的格式化方法,它接受一個布爾型的值作為參數並返回一個字符串。就像前一節中討論的那樣,這個方法必須標記為protected或是public,不然就不能從模板中訪問到它了。
protected string DisplayDiscontinuedAsYESorNO(bool discontinued) { if (discontinued) return "YES"; else return "NO"; }
這個方法檢查輸入的參數(是否折扣),如果為true則返回“YES”,否則就返回“NO”。
注意:回憶一下我們在上一節中的相關內容,傳遞給格式化方法的參數可能是空值,所以我們需要在訪問雇員的HiredDate之前對它進行空值檢查。這樣的檢查在這裡卻是不需要的,因為Discontinued字段永遠不會是空值。此外,這也是為什麼這個方法接受的是一個布爾值而不是ProductsRow實例或object類型的參數的原因。
完成了這個格式化方法之後,剩下的就只是在TemplateField的ItemTemplate中調用它了。要創建這個TemplateField,我們可以刪除Discontinued綁定列再添加一個新的TemplateField,也可以直接將Discontinued BoundField轉換成TemplateField。然後,在源視圖(譯者注:就是HTML視圖)編輯TemplateField以使其包含一個調用DisplayDiscontinuedAsYESorNO方法的ItemTemplate,傳過去的參數就是當前ProductRow實例的Discontinued屬性。這個可以通過Eval方法來訪問。現在,這個TemplateField的標記代碼就像這樣了:
<asp:TemplateField HeaderText="Discontinued" SortExpression="Discontinued"> <ItemTemplate> <%# DisplayDiscontinuedAsYESorNO((bool) Eval("Discontinued")) %> </ItemTemplate> </asp:TemplateField>
這樣,DisplayDiscontinuedAsYESorNO方法就會在呈現DetailsView時被調用,傳給它的是ProductRow實例的Discontinued值。由於Eval方法返回的是一個obejct類型的值,而DisplayDiscontinuedAsYESorNO方法僅接受布爾型的參數,所以我們將Eval方法的返回值轉換成布爾型的。根據接收到的值,DisplayDiscontinuedAsYESorNO方法將會返回“YES”或“NO”,這個返回值就是要在這個DetailsView行中顯示的東西。
圖十:現在在折扣行中顯示的就是“YES”或“NO”了
總結
在DetailsView控件中,相對於其他的列控件來說,模板列可以處理更加復雜的數據呈現。模板列主要用於這樣一些情況:
·一個DetailsView列中需要顯示多個數據列
·使用一個Web控件來展示數據比一段簡單的文本更好
·頁面的輸出取決於綁定到DetailsView的數據,比如說元數據或者說是數據的重新格式化
雖然模板列可以高度復雜的呈現DetailsView的數據,但DetailsView的輸入仍然讓人感到有些別扭,因為它把每個字段都顯示成HTML標記<table>的一行。
FormView控件提供了一種更加復雜的輸出呈現。FormView並不含有什麼列,它僅僅包括一系列的模板(ItemTemplate、EditItemTemplate、HeaderTemplate等等)。在下一節中,我們將看到如何使用FormView來達到呈現更多控件的效果。
編程愉快!
關於作者
Scott Mitchell,著有六本ASP/ASP.NET方面的書,是4GuysFromRolla.com的創始人,自1998年以來一直應用微軟Web技術。Scott是個獨立的技術咨詢顧問,培訓師,作家,最近完成了將由Sams出版社出版的新作,24小時內精通ASP.NET 2.0。他的聯系電郵為[email protected],也可以通過他的博客http://ScottOnWriting.NET與他聯系。