我们可以 通过控制 HeaderStyle , RowStyle , AlternatingRowStyle 和其他一些属性来 改变 GridView, DetailsView, 和 FormView 的样式 , 比如 cssClass, Font, BorderWidth , BorderStyle , BorderColor , Width , Height 等
一般 , 自定义格式化与我们所要显示的数据的值有关系。例如 , 为了吸引用户注意那些库存为空的产品,我们可以将库存对应的字段 UnitsInStock 和 UnitsOnOrder 为 0 的数据背景色设为黄色 . 为了高亮化那些贵的产品,则将 UnitsInStock 高于 $75.00 的数据字体设置为粗体
GridView, DetailsView, FormView 的格式自定义可以有多种方法 , 在本文中我们将用 DataBound 和 RowDataBound 两种事件来完成, 在下一篇里我们将尝试用 alternative 的方式 在 GridView 控件中使用 TemplateField
使用 DetailsView 控件的 DataBound 事件
当绑定数据到 DetailsView 控件 , 不管是从数据控件或编码填充数据到 DataSource 属性并调用其 DataBind() 方法。以下几种事件将触发
- DataBinding 事件触发
- 数据绑定到数据绑定控件
- 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 是否被勾选上 , 这样我们可以分页查看所有的数据了
图一 : 在 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 执行看看
图二 : DetailsView 控件一次显示一个数据
Step 2: 在 DataBound 事件中 编码确定数据的值
为了将那些 UnitPrice 高于 $75.00 的产品用粗体, italic 字体显示出来 , 我们首先需要编码确定 UnitPrice 的值 , 对于 DetailsView 我们可以通过 DataBound 事件完成 . 我们选择 DetailsView 并查看属性视图 (F4 位快捷键 ), 如果没有显示,则选择 View (视图)
Property Window( 属性窗口 ), 在确保您选择了 DetailsView 的情况下双击 DataBound 事件或者输入您要创建的事件名
图三 : 为 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, 还将讨论另外一些自定义格式化的方法