简介
通过多种与样式相关的属性,可以自定义GridView 、DetailsView 和FormView 控件的外观。 CssClass 、Font 、BorderWidth 、BorderStyle 、BorderColor 、Width 和 Height 是决定所显示控件整体外观的属性。 HeaderStyle 、RowStyle 、AlternatingRowStyle 及其他属性则用于为特定区域应用相同的样式设置。这些样式设置同样可应用于字段级别。
但在很多场景中,格式化要求根据所显示数据的值有所不同。例如,若要突出显示脱销产品,可以将产品信息列表中 UnitsInStock 和 UnitsOnOrder 字段都为 0 的产品背景设为黄色。要突出显示价格较高的产品,可将价格在 $75.00 以上的产品以粗体字显示。
可以用多种方式实现基于数据绑定对 GridView 、DetailsView 或FormView 的格式的调整。本教程中,我们将说明如何通过使用 DataBound 和 RowDataBound Event Handler 来完成数据绑定格式化。下一本教程中将探讨另一种方法。
使用 DetailsView 控件的DataBoundEvent Handler
数据被绑定到DetailsView 时,从数据源控件或通过编程将数据指定到控件的DataSource 属性并调用其 DataBind() 方法。步骤如下 :
- Web 数据控件的 DataBinding 事件触发。
- 该数据被绑定到 Web 数据控件。
- Web 数据控件的 DataBound 事件触发。
上述步骤 1 -3 完成后,可以通过 Event Handler 立即注入自定义逻辑。通过为 DataBound 事件创建一个 Event Handler ,我们可通过编程指定要绑定到 Web 数据控件的数据并根据需要调整格式。作为演示,下面我们将创建一个 DetailsView 列出关于某件产品的常用信息,但将其 UnitPrice 值显示为加粗字体,如果其价格超过 $75.00 则显示为加粗斜体。
步骤1 :在 DetailsView 中显示产品信息
打开CustomFormatting 文件夹中的CustomColors.aspx 页面,从 Toolbox 上拖动一个 DetailsView 控件到 Designer 中,将其 ID 属性值赋值为 ExpensiveProductsPriceInBoldItalic 并将其绑定到一个新的 ObjectDataSource 控件,该控件调用 ProductsBLL 类的GetProducts() 方法。由于前面的教程中已经讲解过上述操作,因此这里省略其详细步骤不提。
将ObjectDataSource 绑定到 DetailsView 后,花点时间来更改字段列表。这里我选择了移除ProductID 、SupplierID 、CategoryID 、UnitsInStock 、UnitsOnOrder 、ReorderLevel 和 Discontinued BoundFields ,并对其余的 BoundField 进行重命名和重新格式化操作。此外,我还清除了 Width 和 Height 设置。由于 DetailsView 只显示一条记录,我们需要启用分页功能以便终端用户能查看所有产品。操作方法为勾选上 DetailsView 的智能标记中的 Enable Paging 复选框。
图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>
花点时间在浏览器中测试一下该页面。
图2 :DetailsView 控件一次显示一种产品
步骤2 :在 DataBound Event Handler 中通过编程确定数据的值
要用加粗斜体字显示其 UnitPrice 值超过 $75.00 的产品价格,我们需要首先能通过编程确定该UnitPrice 值。在 DetailsView 中,这一操作通过 DataBound Event Handler 来完成。要创建 Event Handler,单击 Designer 中的 DetailsView ,然后进入 Properties 窗口。若 Properties 窗口未打开,需要按下 F4 使其显示,或选择 View 菜单上的 Properties Window 菜单选项。单击 Properties 窗口中的闪电图标列出 DetailsView 的事件。然后双击 DataBound 事件或键入要创建的 Event Handler 的名称。
图3 :为 DataBound 事件创建 Event Handler
这将自动创建一个Event Handler 并进入其被添加的代码部分。此时,可看到如下代码:
protected void ExpensiveProductsPriceInBoldItalic_DataBound(object sender, EventArgs e)
{
}
通过DataItem 属性可访问绑定到 DetailsView 的数据。记住,我们正在将控件绑定到强类型的 DataTable ,DataTable 包含一个强类型的 DataRow 实例集。当 DataTable 被绑定到 DetailsView 时, DataTable 中的第一个 DataRow 被指定为 DetailsView 的 DataItem 属性。具体地说, DataItem 属性被指定到一个 DataRowView 对象。我们可以使用 DataRowView 的 Row 属性来访问底层的 DataRow 对象,该对象实际上是一个 ProductsRow 实例。一旦有了这个 ProductsRow 实例,我们就可以通过简单查看该对象的属性值来作出决定。
下列代码演示了如何判断绑定到DetailsView 控件的UnitPrice 值是否大于$75.00 :
protected void ExpensiveProductsPriceInBoldItalic_DataBound(object sender, EventArgs e)
{
// Get the ProductsRow object from the DataItem property...
Northwind.ProductsRow product = (Northwind.ProductsRow)
((DataRowView)ExpensiveProductsPriceInBoldItalic.DataItem).Row;
if (!product.IsUnitPriceNull() && product.UnitPrice > 75m)
{
// TODO: Make the UnitPrice text bold and italic
}
}
注意:由于 UnitPrice 在数据库中的值可以为 NULL ,所以 我们要在访问 ProductsRow 的 UnitPrice 属性 之前首先查看,以确认操作的不是 NULL 值。此检查非常重要,因为如果试图访问值为 NULL 的 UnitPrice 属性,ProductsRow 对象将抛出一个StrongTypingException 异常 。
步骤3 :设置 DetailsView 中的 UnitPrice 值
现在我们能够确定绑定到 DetailsView 的 UnitPrice 值是否超过 $75.00 ,但还要学习如何据此通过编程调整 DetailsView 的格式。要更改 DetailsView 中某行的格式,使用 DetailsViewID.Rows[index] 通过编程访问该行;要更改某单元格,使用DetailsViewID.Rows[index].Cells[index] 。一旦有了对行或单元格的引用后,接下来便可通过设置其与样式相关的属性来调整其外观了。
要通过编程访问某行,需要知道该行的索引。索引从 0 开始。UnitPrice 行是 DetailsView 的第 5 行,因此应使用其索引 4 ,并使用 ExpensiveProductsPriceInBoldItalic.Rows[4] 使其通过编程访问。现在我们可以使用以下代码,使此行所有内容显示为加粗斜体字:
ExpensiveProductsPriceInBoldItalic.Rows[4].Font.Bold = true;
ExpensiveProductsPriceInBoldItalic.Rows[4].Font.Italic = true;
但这将使标签 (Price) 和值都显示为加粗斜体。如果只希望值显示为加粗斜体,需要将此格式应用到行中的第二个单元格,相应代码如下:
ExpensiveProductsPriceInBoldItalic.Rows[4].Cells[1].Font.Bold = true;
ExpensiveProductsPriceInBoldItalic.Rows[4].Cells[1].Font.Italic = true;
由于到目前为止的教程都使用样式表来严格区分显示的标记和样式相关的信息,而不是象前面所示那样设置具体的样式属性。我们接下来将使用一个 CSS 类。打开 Styles.css 样式表,新增一个名为 ExpensivePriceEmphasis 的 CSS 类,带有如下定义:
.ExpensivePriceEmphasis
{
font-weight: bold;
font-style: italic;
}
然后在 DataBound Event Handler 中将单元格的CssClass 属性赋值为 ExpensivePriceEmphasis。下列代码完整显示了DataBound Event Handler:
protected void ExpensiveProductsPriceInBoldItalic_DataBound(object sender, EventArgs e)
{
// Get the ProductsRow object from the DataItem property...
Northwind.ProductsRow product = (Northwind.ProductsRow)
((DataRowView)ExpensiveProductsPriceInBoldItalic.DataItem).Row;
if (!product.IsUnitPriceNull() && product.UnitPrice > 75m)
{
ExpensiveProductsPriceInBoldItalic.Rows[4].Cells[1].CssClass =
"ExpensivePriceEmphasis";
}
}
查看Chai 时,由于其价格低于 $75.00 ,因此以常规字体显示( 见图4 )。但是在查看 Mishi Kobe Niku 时,由于其价格为 $97.00 ,因此显示为加粗斜体(见图 5 )。
图4 :价格低于 $75.00 的以常规字体显示
图5 :较贵的产品价格显示为加粗斜体
使用 FormView 控件的DataBoundEvent Handler
确定绑定到FormView 的底层数据的步骤与确定绑定到 DetailsView 的底层数据的步骤相同:创建一个DataBound Event Handler ,将 DataItem 属性转换为绑定到控件的相应对象,并确定处理方式。但 FormView 和 DetailsView 在如何更新其用户界面的外观上有所不同。
FormView 不包含任何 BoundFields ,因此没有 Rows 集合。而 FormView 由模板组成,模板中可包含静态 HTML 、Web 控件和数据绑定语法。调整 FormView 样式通常涉及到调整 FormView 模板中一个或多个 Web 控件。
作为演示,我们将使用一个 FormView 列出产品,与上个示例一样,但这次是只显示产品名称及其存货数量,小于或等于10 的存货数量将显示为红色字体。
步骤4:在 FormView 中显示产品信息
向CustomColors.aspx 页面的 DetailsView 下方添加一个 FormView ,并将其 ID 属性赋值为 LowStockedProductsInRed 。将 FormView 绑定到上个步骤中创建的 ObjectDataSource 控件。这将为 FormView 创建一个 ItemTemplate 、 EditItemTemplate 和 InsertItemTemplate 。移除 EditItemTemplate 和 InsertItemTemplate ,简化 ItemTemplate 使其仅包含 ProductName 和 UnitsInStock 值,这两个值都位于各自被命名的 Label 控件中。与上例中的 DetailsView 相同,也勾选上 FormView 的智能标记中的 Enable Paging 复选框。
完成上述编辑后,FormView 的标记应如下所示:
<asp:FormView ID="LowStockedProductsInRed" DataKeyNames="ProductID"
DataSourceID="ObjectDataSource1" AllowPaging="True"
EnableViewState="False" runat="server">
<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 属性。
步骤 5:在DataBound Event Handler中,通过编程确定数据的值
完成FormView 的标记后,下一步是通过编程确定 UnitsInStock 的值是否小于或等于10 。 FormView 的操作方法与 DetailsView 的操作方法完全相同。首先为 FormView 的 DataBound 事件创建一个 Event Handler。
图6 :创建 DataBound Event Handler
在Event Handler 中,将 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)
((DataRowView)LowStockedProductsInRed.DataItem).Row;
if (!product.IsUnitsInStockNull() && product.UnitsInStock <= 10)
{
// TODO: Make the UnitsInStockLabel text red
}
}
步骤6:在 FormView 的 ItemTemplate 中设置 UnitsInStockLabel Label 控件的格式
最后一步是将显示的 UnitsInStock 值( 小于等于 10 )设置为红色字体。为此,需要通过编程访问 ItemTemplate 中的 UnitsInStockLabel 控件,并设置其样式属性,使其文本显示为红色。要访问模板中的 Web 控件,需要以如下方式使用 FindControl("controlID") 方法:
WebControlType someName = (WebControlType)FormViewID.FindControl("controlID");
本例中,我们想访问一个 ID 值为 UnitsInStockLabel 的Label 控件,因此使用 :
Label unitsInStock = (Label)LowStockedProductsInRed.FindControl("UnitsInStockLabel");
一旦有了对 Web 控件的编程引用后,我们就可根据需要更改其样式相关的属性。与上个示例相同,本例在 Styles.css 中创建了一个名为 LowUnitsInStockEmphasis 的 CSS 类。要将此样式应用于 Label Web 控件,应对 CssClass 属性作相应设置。
protected void LowStockedProductsInRed_DataBound(object sender, EventArgs e)
{
// Get the ProductsRow object from the DataItem property...
Northwind.ProductsRow product = (Northwind.ProductsRow)
((DataRowView)LowStockedProductsInRed.DataItem).Row;
if (!product.IsUnitsInStockNull() && product.UnitsInStock <= 10)
{
Label unitsInStock =
(Label)LowStockedProductsInRed.FindControl("UnitsInStockLabel");
if (unitsInStock != null)
{
unitsInStock.CssClass = "LowUnitsInStockEmphasis";
}
}
}
注意:在使用 DetailsView 或 GridView 控件中的 TemplateFields 时,也可使用设置模板格式的语法:使用FindControl("controlID") 通过编程访问Web 控件,然后设置其样式相关的属性。我们将在下一篇教程中详细介绍 TemplateFields 。
图7 显示的是查看一个其 UnitsInStock 值大于 10 的产品 时的 FormView 。图 8 则为查看一个其 UnitsInStock 值小于 10 的产品时的 FormView
图7 :自定义格式不应用于存货量相当大的产品
图8 :存货量小于或等于 10 的产品,其存货量显示为红色
使用GridView 的RowDataBound 事件设置格式
前面我们详细介绍了对 DetailsView 和 FormView 控件进行数据绑定的步骤。现在再次对这些步骤作一回顾。
- Web 数据控件的 DataBinding 事件触发。
- 该数据被绑定到 Web 数据控件。
- Web 数据控件的 DataBound 事件触发。
由于DetailsView 和 FormView 只显示一个记录,所以使用这三个简单步骤就足够了。对于 GridView 而言,由于它显示的是与它绑定的所有记录(而不只是第一个),步骤 2 就稍微复杂一些。
在步骤2 中,GridView 列出了数据源,并为每个记录都创建一个GridViewRow 实例并将当前记录绑定到该实例。对于每个添加到 GridView 的 GridViewRow ,都会触发两个事件:
- RowCreated:在创建 GridViewRow 后触发;
- RowDataBound:在当前记录被绑定到 GridViewRow 后触发。
那么,就 GridView 而言,以下步骤对数据绑定描述得更准确 :
- GridView 的 DataBinding 事件触发。
- 数据被绑定到 GridView 。
对数据源中的每个记录:- 创建一个 GridViewRow 对象;
- 触发 RowCreated 事件;
- 将记录绑定到 GridViewRow ;
- 触发 RowDataBound 事件;
- 将 GridViewRow 添加到 Rows 集合。
- 触发 GridView 的 DataBound 事件。
然后,要自定义GridView 中单个记录的格式,我们需要为 RowDataBound 事件创建一个 Event Handler 。作为演示,我们将向 CustomColors.aspx 页面添加一个 GridView ,列出每件产品的名称、类别和价格。价格低于 $10.00 的产品将以黄色背景突出显示。
步骤7 :在 GridView 中显示产品信息
在上个示例中的 FormView 下方添加一个 GridView , 并将其ID 属性赋值为HighlightCheapProducts 。由于我们已经有一个返回页面上所有产品的 ObjectDataSource ,将 GridView 绑定到该 ObjectDataSource 。最后,编辑 GridView 的 BoundFields ,使其仅包含产品的名称、类别和价格。这些编辑完成后, GridView 的标记应如下所示:
<asp:GridView ID="HighlightCheapProducts" AutoGenerateColumns="False"
DataKeyNames="ProductID" DataSourceID="ObjectDataSource1"
EnableViewState="False" runat="server">
<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 显示的是通过浏览器看到的目前的操作进度。
图9 :GridView 列出每件产品的名称、类别和价格
步骤8 :在RowDataBound Event Handler 中通过编程 确定数据的值
ProductsDataTable 被绑定到 GridView 时,其 ProductsRow 实例将被列举,并为每个 ProductsRow 创建一个 GridViewRow 。 GridViewRow 的 DataItem 属性被指定到特定的 ProductRow ,然后 GridView 的 RowDataBound Event Handler 被触发。然后,我们需要为 GridView 的 RowDataBound 事件创建一个 Event Handler,以确定绑定到 GridView 的每件产品的 UnitPrice 值。在此 Event Handler 中,可以查看当前 GridViewRow 的 UnitPrice 值,并确定该行的格式。
此 Event Handler 可以用与 FormView 和 DetailsView 一样的步骤来创建。
图10 :为 GridView 的 RowDataBound 事件创建一个 Event Handler
以这种方式创建Event Handler 将会自动向 ASP.NET 页面的代码部分添加如下代码 :
protected void HighlightCheapProducts_RowDataBound(object sender, GridViewRowEventArgs e)
{
}
触发 RowDataBound 事件时,这个EventHandler 的第二个参数被传入,这个参数是一个GridViewRowEventArgs类型的对象,该对象有个属性为Row 。此属性返回一个刚刚绑定的 GridViewRow 的引用。访问绑定到 GridViewRow 的 ProductsRow 实例,我们需要使用 DataItem 属性,如下所示:
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 Event Handler 时,重要的是 要记住 GridView 包含多个类型的行,而此 Event Handler 对于所有行类型都是可触发的。 GridViewRow 的类型可以通过其 RowType 属性确定,其值可为如下某种:
- DataRow :绑定到 GridView 的 DataSource 的某条记录的行;
- EmptyDataRow :当 GridView 的 DataSource 为空时显示的行;
- Footer :脚注行,在 GridView 的 ShowFooter 属性被赋值为 true 时显示;
- Header :标题行,在 GridView 的 ShowHeader 属性被赋值为 true ( 默认值 )时显示;
- Pager :针对实现分页的 GridView ,为显示分页界面的行;
- Separator :不用于 GridView ,但被 RowType 属性用于 DataList 和 Repeater 控件,我们将在后面教程中介绍这两个 Web 数据 控件。
由于 EmptyDataRow 、Header 、Footer 和 Pager 行不与 DataSource 记录相关联,他们始终将 null 赋值给其 DataItem 属性。因此,在准备操作当前 GridViewRow 的 DataItem 属性之前,必须首先确保操作的是 DataRow 。这可以通过查看 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...
}
}
}
步骤 9:用黄色突出显示小于 $10.00 的 UnitPrice 值
最后一步是通过编程突出显示 UnitPrice 值小于 $10.00 的整个 GridViewRow 。访问 GridView 中的行或单元格的语法与 DetailsView 相同,即用 GridViewID.Rows[index] 访问整行,用 GridViewID.Rows[index].Cells[index] 访问特定单元格。但是,当 RowDataBound Event Handler 触发数据绑定时,还需要为 GridView 的 Rows 集合添加 GridViewRow 。因此,不能使用 Rows 集合在 RowDataBound Event Handler 中访问当前 GridViewRow 实例。
我们可以使用 e.Row ,而不是 GridViewID.Rows[index] ,在 RowDataBound Event Handler 中引用当前 GridViewRow 实例。也就是说,要从 RowDataBound Event Handler 中突出显示当前 GridViewRow 实例,应使用:
e.Row.BackColor = System.Drawing.Color.Yellow;
我们将不直接设置 GridViewRow 的 BackColor 属性,而是仍然使用 CSS 类。我已经创建了一个名为AffordablePriceEmphasis 的CSS 类,并将背景设为黄色。完成的 RowDataBound Event Handler 如下所示 :
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 事件创建一个 Event Handler,用来查看底层数据并在需要时进行格式更改。要访问绑定到 DetailsView 或 FormView 的数据,在 DataBound Event Handler 中要使用 DataItem 属性。对于 GridView ,每个 GridViewRow 实例的 DataItem 属性包含绑定到该行的数据,这些数据在 RowDataBound Event Handler 中是可用的。
通过编程调整Web 数据 控件格式的语法取决于 Web 控件和要设置格式的数据的显示方式。 DetailsView 和 GridView 控件的行和单元格可以用一个顺序索引访问。由于FormView 使用模板,因此常使用 FindControl("controlID") 方法在模板中定位 Web 控件。
我们在下一篇教程中将介绍如何在 GridView 和 DetailsView 中使用模板。此外,还将介绍根据底层数据自定义格式的另一种方法。
快乐编程 !