介紹
我們可以通過控制HeaderStyle, RowStyle, AlternatingRowStyle和其他一些屬性來改變GridView, DetailsView, 和 FormView的樣式,比如cssClass, Font, BorderWidth, BorderStyle, Bar, Width, Height等
一般,自訂格式化與我們所要顯示的資料的值有關係。例如, 為了吸引使用者注意那些庫存為空白的產品,我們可以將庫存對應的欄位UnitsInStock 和UnitsOnOrder為0的資料背景色設為黃色. 為了高亮化那些貴的產品,則將UnitsInStock 高於$75.00的資料字型設定為粗體
GridView, DetailsView, FormView的格式自訂可以有多種方法, 在本文中我們將用DataBound 和 RowDataBound兩種事件來完成, 在下一篇裡我們將嘗試用alternative的方式 在GridView控制項中使用TemplateField
使用DetailsView 控制項的 DataBound 事件當綁定資料到DetailsView控制項, 不管是從資料控制項或編碼填充資料到DataSource屬性並調用其DataBind()方法。以下幾種事件將觸發
1.DataBinding事件觸發
2.資料繫結到資料繫結控制項
3.DataBound事件觸發
一般在1,2,3之後資料將會通過事件立即填充資料控制項,我們還可以自訂事件處理來確定資料是否已經被填充到控制項中並按照我們的需要調整顯示格式。我們可以來做個例子.我們將建立一個DetailsView來列出一個產品的一般資訊,並且當UnitPrice超過 $75.00 時用粗體,italic字型來顯示UnitPrice的值
Step 1: 在DetailsView中顯示產品資訊
在CustomFormatting檔案夾下建立一個CustomColors.aspx頁面,從工具箱中拖出一個DetailsView控制項到頁面中,設定ID為ExpensiveProductsPriceInBoldItalic綁定到一個新的資料來源中,並配置此資料來源到業務對象ProductsBLL類中的GetProducts()方法,這個的詳細實現步驟已經在前面詳細介紹過了,這裡就忽略了
當您綁定ObjectDataSource到DetailsView時,我們可以修改一下欄位列表,我選擇移除了ProductID, SupplierID, CategoryID, UnitsInStock, UnitsOnOrder, ReorderLevel和那些不被綁定的欄位,他們將不會顯示在DetailsView列表中,而那些留下來的我們可以重新命名他們,還可以修改他們的顯示格式. 我還清空了DetailsView的Height和Width屬性,這樣當顯示的只有一條資料時不會出現樣式的混亂。當然我們面對的資料絕不只有一條這麼少,顯示怎麼辦呢?我們可以檢查DetailsView的智能感知中檢查Enable Paging checkbox是否被勾選上, 這樣我們可以分頁查看所有的資料了
圖1: 在DetailsView的值能感知中檢查Enable Paging屬性是否被勾選上
在經過這些改變後, DetailsView的代碼更改為
<asp:DetailsView ID="DetailsView1" runat="server" AllowPaging="True" AutoGenerateRows="False" DataKeyNames="ProductID" DataSourceID="ObjectDataSource1" 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" DataFormatString="{0:c}" HeaderText="Price" HtmlEncode="False" SortExpression="UnitPrice" /> </Fields></asp:DetailsView>
您這時可以按F5執行看看
圖2: DetailsView控制項一次顯示一個資料
Step 2: 在DataBound事件中編碼確定資料的值
為了將那些UnitPrice高於$75.00的產品用粗體,italic字型顯示出來,我們首先需要編碼確定UnitPrice的值,對於DetailsView我們可以通過DataBound事件完成. 我們選擇DetailsView並查看屬性視圖(F4位快速鍵), 如果沒有顯示,則選擇 View(視圖)Property Window(屬性視窗), 在確保您選擇了DetailsView的情況下雙擊DataBound事件或者輸入您要建立的事件名
圖3: 為DataBound事件建立一個事件處理
代碼中將會自動產生以下代碼
protected void ExpensiveProductsPriceInBoldItalic_DataBound(object sender, EventArgs e){}
我們可以通過DataItem屬性來設定DetailsView的繫結項目(一些強型別的資料行(DataRow)組成的強型別的資料表(DataTable)), 當資料表(DataTable)綁定到DetailsView時,資料表的第一行將被自動綁定到DetailsView的DataItem屬性,而DataItem屬性中包含有DataRowView (Object類型),我們可以通過DataRowView來訪問一個ProductsRow 的DataRow執行個體,還可以檢測Object的值來確定ProductsRow執行個體是否存在
下面的代碼描述如何確定UnitPrice是否綁定到DetailsView並且高於$75.00
protected void ExpensiveProductsPriceInBoldItalic_DataBound(object sender, EventArgs e){ // Get the ProductsRow object from the DataItem property... Northwind.ProductsRow product = (Northwind.ProductsRow)((System.Data.DataRowView)ExpensiveProductsPriceInBoldItalic.DataItem).Row; if (!product.IsUnitPriceNull() && product.UnitPrice > 75m) { // TODO: Make the UnitPrice text bold and italic }}
注意: 當UnitPrice在資料庫的值為空白,我們在綁定到ProductsRow's UnitPrice屬性之前檢查確定他是否為空白,這很重要因為我們可以通過檢查這個屬性來拋出一個強型別的異常 StrongTypingException exception.
Step 3: 在DetailsView中格式化 UnitPrice
到這個時候我們已經知道即將綁定的UnitPrice是否高於$75.00,現在我們來看看怎麼通過編碼調整UnitPrice的格式,我們可以通過修改DetailsViewID.Rows[index];修改一行資料,而且我們可以通過訪問DetailsViewID.Rows[index].Cells[index]來訪問某一儲存格,這樣我們可以通過修改與格式相關的屬性來格式化這一儲存格
訪問某一行需要得到某行的索引,索引從0開始, UnitPrice 在 DetailsView中是第15行, 假設他在第四行那麼我們可以通過ExpensiveProductsPriceInBoldItalic.Rows[4]來訪問. 這時我們可以通過下面的代碼將這一行顯示為粗體,italic 字型
ExpensiveProductsPriceInBoldItalic.Rows[4].Font.Bold = true;ExpensiveProductsPriceInBoldItalic.Rows[4].Font.Italic = true;
然而,這樣將會格式化Label和值,如果我們只想將值格式話,而且我們需要將格式應用到當前行的第二格,請看下面的代碼
ExpensiveProductsPriceInBoldItalic.Rows[4].Cells[1].Font.Bold = true;ExpensiveProductsPriceInBoldItalic.Rows[4].Cells[1].Font.Italic = true;
我們還可以通過StyleSheet 來顯示標記和樣式相關資訊,而不是用確定的某一行某一列來設定格式,我們用CSS來控制格式,開啟Styles.css 檔案,添加一個新的Class命名為ExpensivePriceEmphasis按照下面的代碼CSS
.ExpensivePriceEmphasis{ font-weight: bold; font-style: italic;}
然後再DataBound事件中,設定單元的CssClass為ExpensivePriceEmphasis,在DataBound事件處理中添加
當查看Chai(費用低於$75.00),價格將會用正常格式顯示 圖4),但是當查看Mishi Kobe Niku,(價格為$97.00)則會用我們設定的格式顯示(圖5)
圖4: 價格低於$75.00將會用正常格式顯示
圖5: 價格高於$75.00將會用 粗體, Italic 字型顯示
使用FormView控制項的 DataBound 事件綁定到FormView資料的步驟和DetailsView的步驟類似都要建立一個DataBound事件處理, 聲明綁定到控制項的DataItem類型屬性, 然後執行綁定。然而,他們更新的方式不同
FormView不包括任何繫結資料行也不包含行的集合, 取而代之的是他由一系列包含若干靜態HTML, Web控制項,綁定運算式的模板組合。調整 FormView的外觀涉及到調整一個或多個FormView的模板
讓我們像前一個例子那樣用FormView列出產品項,但是這次我們僅僅用紅色字型顯示units小於等於10的產品的name和units
Step 1: 在FormView中顯示產品資訊
添加一個FormView到CustomColors.aspx中,設定其ID為LowStockedProductsInRed,像前一個步驟一樣綁定資料到ObjectDataSource中, 這將在FormView中建立ItemTemplate, EditItemTemplate, 和InsertItemTemplate .
移除EditItemTemplate和InsertItemTemplate 並在 ItemTemplate 中僅包含ProductName 和UnitsInStock 項, 在智能感知中檢查Allow Paging(分頁)標記是否被選上
在這些操作後FormView的代碼大概會成這樣
<asp:FormView ID="LowStockedProductsInRed" runat="server" DataKeyNames="ProductID" DataSourceID="ObjectDataSource1" AllowPaging="True" EnableViewState="False"> <ItemTemplate> <b>Product:</b> <asp:Label ID="ProductNameLabel" runat="server" Text='<%# Bind("ProductName") %>'> </asp:Label><br /> <b>Units In Stock:</b> <asp:Label ID="UnitsInStockLabel" runat="server" Text='<%# Bind("UnitsInStock") %>'> </asp:Label> </ItemTemplate></asp:FormView>
注意ItemTemplate 包含的代碼:
·靜態HTML – “Product:” 和 “Units In Stock:” 包含 <br /> 和 <b> 元素.
·Web 控制項– 兩個Label控制項, ProductNameLabel 和UnitsInStockLabel.
·綁定運算式 –<%# Bind("ProductName") %> 和<%# Bind("UnitsInStock") %> 運算式, 綁定值到Label的Text屬性上
Step 2: 在 DataBound 事件處理中編碼確定資料的值
當FormView的標記完成後,下一步就是確定UnitsInStock的值是否小於等於10,這裡和在DetailView中類似,先建立DataBound事件
圖6: 建立 DataBound 事件處理
在事件中聲明FormView的DataItem屬性到ProductsRow執行個體中,確定UnitsInPrice的值並將對應的值用紅色字型顯示
protected void LowStockedProductsInRed_DataBound(object sender, EventArgs e){ // Get the ProductsRow object from the DataItem property... Northwind.ProductsRow product = (Northwind.ProductsRow)((System.Data.DataRowView)LowStockedProductsInRed.DataItem).Row; if (!product.IsUnitsInStockNull() && product.UnitsInStock <= 10) { // TODO: Make the UnitsInStockLabel's text red }}
Step 3:在FormView 的ItemTemplate中格式化UnitsInStockLabel Label
最後一步就是要在ItemTemplate中設定UnitsInStockLabel的樣式為紅色字型,在ItemTempelete中尋找控制項可以使用FindControl(“controlID”)方法
WebControlType someName = (WebControlType)FormViewID.FindControl("controlID");
對於我們這個例子我們可以用如下代碼來尋找該Label控制項
Label unitsInStock = (Label)LowStockedProductsInRed.FindControl("UnitsInStockLabel");
當我們找到這個控制項時則可以修改其對應的style屬性,在style.css中已經有一個寫好的LowUnitsInStockEmphasis的cSS Class ,我們通過下面的代碼將cSS Class設定到對應的屬性
protected void LowStockedProductsInRed_DataBound(object sender, EventArgs e){ // Get the ProductsRow object from the DataItem property... Northwind.ProductsRow product = (Northwind.ProductsRow)((System.Data.DataRowView)LowStockedProductsInRed.DataItem).Row; if (!product.IsUnitsInStockNull() && product.UnitsInStock <= 10) { Label unitsInStock = (Label)LowStockedProductsInRed.FindControl("UnitsInStockLabel"); if (unitsInStock != null) { unitsInStock.CssClass = "LowUnitsInStockEmphasis"; } }}
注意: 這種方式在FormView和GridView中也可以通過設定TemplateFields來達到同樣的效果,我們將在下一篇中討論TemplateFields.圖7顯示FormView在當UnitsInStock大於10的情況,圖8則顯示小於等於10的情況
圖7 : 在高於10的情況下,沒有值被格式化
圖8:小於等於10時,值用紅色字型顯示
用GridView的 RowDataBound 事件自訂格式化
前面我們討論了在FormView和DetailsView中實現資料繫結的步驟,現在讓我們回顧下
DataBinding事件觸發
資料繫結到資料繫結控制項
DataBound事件觸發
對於FormView和DetailsView有效因為只需要顯示一個資料,而在GridView中,則要顯示所有資料,相對於前面三個步驟,步驟二有些不同
在步驟二中,GridView 列出所有的資料,對於某一個記錄將建立一個GridViewRow 執行個體並綁定,對於每個添加到GridView 中的 GridViewRow兩個事件將會觸發:
·RowCreated – 當GridViewRow被建立時觸發
·RowDataBound – 目前記錄綁定到GridViewRow時觸發.
對於GridView,請使用下面的步驟
DataBinding事件觸發
資料繫結到資料繫結控制項
對於每一行資料..
a.建立GridViewRow
b.觸發 RowCreated 事件
c.綁定資料到GridViewRow
d.觸發RowDataBound事件
e.添加GridViewRow到Rows 集合
DataBound事件觸發
為了自訂格式化GridView單獨記錄,我們需要為RowDataBound事件建立事件處理,讓我們添加一個GridView到CustomColors.aspx中,並顯示name, category, 和 price,用黃色背景高亮那些價格小於$10.00的產品
Step 1:在GridView中顯示產品資訊
添加一個GridView到FormView的下方,設定ID為HighlightCheapProducts.我們之前已經設定了一個ObjectDataSource來擷取產品資料,現在我們綁定GridView到ObjectDataSource. 之後,編輯GridView的繫結資料行包含產品的name.categorie,price屬性。完成之後GridView的代碼將會是:
<asp:GridView ID="HighlightCheapProducts" runat="server" AutoGenerateColumns="False" DataKeyNames="ProductID" DataSourceID="ObjectDataSource1" EnableViewState="False"> <Columns> <asp:BoundField DataField="ProductName" HeaderText="Product" SortExpression="ProductName"/> <asp:BoundField DataField="CategoryName" HeaderText="Category" ReadOnly="True"SortExpression="CategoryName" /> <asp:BoundField DataField="UnitPrice" DataFormatString="{0:c}" HeaderText="Price" HtmlEncode="False" SortExpression="UnitPrice" /> </Columns></asp:GridView>
圖九顯示瀏覽器查看的結果
圖9: GridView顯示產品的name, category, price
Step 2:在RowDataBound的事件處理中編碼確定資料對應的值
當ProductsDataTable綁定到GridView,GridView將會產生若干個ProductsRow。GridViewRow的DataItem屬性將會產生一個實際的ProductRow。在GridView的 RowDataBound事件發生之後,為了確定UnitsInStock的值,我們需要建立RowDataBound的事件處理,在其中我們可以確定UnitsInStock的值並做相應的格式化EventHandler的建立過程和前面兩個一樣
圖10: 建立GridView的RowDataBound事件的事件處理
在後台代碼裡將會自動產生如下代碼
protected void HighlightCheapProducts_RowDataBound(object sender, GridViewRowEventArgs e){}
當RowDataBound事件觸發,第二個參數GridViewRowEventArgs中包含了對GridViewRow的引用,我們用如下的代碼來訪問GridViewRow中的ProductsRow
protected void HighlightCheapProducts_RowDataBound(object sender, GridViewRowEventArgs e){ // Get the ProductsRow object from the DataItem property... Northwind.ProductsRow product = (Northwind.ProductsRow)((System.Data.DataRowView)e.Row.DataItem).Row; if (!product.IsUnitPriceNull() && product.UnitPrice < 10m) { // TODO: Highlight the row yellow... }}
當運用RowDataBound事件處理時,GridView由各種類型不同的行組成,而事件發生針對所有的行類型, GridViewRow的類型可以由RowType屬性決定,可以是以下類型中的一種
·DataRow – GridView的DataSource中的一條記錄
·EmptyDataRow – GridView的DataSource顯示出來的某一行為空白
·Footer – 底部行; 顯示由GridView的ShowFooter屬性決定
·Header – 頭部行; 顯示由GridView的ShowHeader屬性決定
·Pager – GridView的分頁,這一行顯示分頁的標記
·Separator – 對於GridView不可用,但是對於DataList和Reapter的RowType屬性卻很有用,我們將在將來的文章中討論他們
當上面四種(DataRow, Pager Rows Footer, Header)都不合適對應值時,將返回一個空的資料項目, 所以我們需要在代碼中檢查GridViewRow的RowType屬性來確定:
protected void HighlightCheapProducts_RowDataBound(object sender, GridViewRowEventArgs e){ // Make sure we are working with a DataRow if (e.Row.RowType == DataControlRowType.DataRow) { // Get the ProductsRow object from the DataItem property... Northwind.ProductsRow product = (Northwind.ProductsRow)((System.Data.DataRowView)e.Row.DataItem).Row; if (!product.IsUnitPriceNull() && product.UnitPrice < 10m) { // TODO: Highlight row yellow... } }}
Step 3:用黃色高亮那些UnitPrice小於$10.00的行
我們需要訪問GridViewID.Rows[index]來訪問index對應的那一行,GridViewID.Rows[index].Cells[index]來訪問某一儲存格.然而當RowDataBound事件觸發時,GridViewRow卻沒有添加到Rows集合中, 因此我們不能在RowDataBound事件處理中通過當前GridViewRow執行個體
取而代之,我們可以通過e.Row來訪問。為了高亮某一行我們用下面的代碼
e.Row.BackColor = System.Drawing.Color.Yellow;
我們還可以通過cSSClass取得同樣的效果(推薦)
protected void HighlightCheapProducts_RowDataBound(object sender, GridViewRowEventArgs e){ // Make sure we are working with a DataRow if (e.Row.RowType == DataControlRowType.DataRow) { // Get the ProductsRow object from the DataItem property... Northwind.ProductsRow product = (Northwind.ProductsRow)((System.Data.DataRowView)e.Row.DataItem).Row; if (!product.IsUnitPriceNull() && product.UnitPrice < 10m) { e.Row.CssClass = "AffordablePriceEmphasis"; } }}
圖 11: 所需要的行用高亮黃色顯示
總結
在本篇中我們示範了基於資料繫結來自訂格式化GridView, DetailsView, FormView的方法。為了完成這些,我們建立DataBound或者RowDataBound事件,為了訪問DetailsView或FormView的資料繫結,我們可以通過DataItem屬性。對於GridView,每個GridViewRow執行個體的DataItem屬性包含了繫結資料(在RowDataBound事件處理中可用)
為了調整格式,我們可能需要訪問某一特定的行,在GridView和DetailsView中我們可以通過索引訪問,而在FormView中我們則需要用FindControl("controlID"),同時FindControl("controlID")通常都可以訪問Web控制項Tempelete中的某個控制項.在下一篇中我們將討論如何在GridView和DetailsView使用Tempeletes, 還將討論另外一些自訂格式化的方法
祝編程快樂!
作者簡介
Scott Mitchell,著有六本ASP/ASP.NET方面的書,是4GuysFromRolla.com的創始人,自1998年以來一直應用 微軟Web技術。Scott是個獨立的技術諮詢顧問,培訓師,作家,最近完成了將由Sams出版社出版的新作,24小時內精通ASP.NET 2.0。他的聯絡電郵為mitchell@4guysfromrolla.com,也可以通過他的部落格http://scottonwriting.net/與他聯絡。