在DetailsView控件中使用TemplateField13
简介
通过TemplateField ,能够比使用 BoundField 、CheckBoxField 、HyperLinkField 及其他数据字段控件时,更灵活地呈现数据。前一篇教程 中,我们了解了在 GridView 使用TemplateField 实现以下功能 :
- 在一列中显示多个数据字段值。具体地说,将 FirstName 和 LastName 字段合并到 GridView 列中。
- 使用可选的 Web 控件显示数据字段值。我们了解了如何使用Calendar 控件显示 HiredDate 值。
- 基于底层数据显示状态信息。由于 Employees 表未包含员工在公司工作天数的列,我们可以使用 TemplateField 和格式设置方法,显示上一篇教程中 GridView 示例中的信息。
GridView 可实现的TemplateFields 功能也可以用 DetailsView 控件实现。本教程中我们将使用一个包含两个TemplateField 的 DetailsView ,一次显示一件产品。第一个 TemplateField 将把 UnitPrice 、UnitsInStock 和 UnitsOnOrder 数据字段合并到一个 DetailsView 列中。第二个 TemplateField 将显示 Discontinued 字段的值。如果 Discontinued 的值为 True ,TemplateField 将显示 YES ,如果不为 True 则显示 NO 。
图1 :使用两个 TemplateField 定制显示
让我们开始吧!
步骤1 :将数据绑定到DetailsView
如前一篇教程所述,操作TemplateField 最简单的方法,通常是首先创建只包含 BoundField 的 DetailsView 控件,然后添加新的TemplateField 或根据需要将 BoundField 转换为TemplateField 。因此,本教程也首先通过设计器向页面添加一个 DetailsView ,并将其绑定到一个返回产品列表的 ObjectDataSource 。这将创建一个 DetailsView ,针对每个产品的非布尔值字段有一个 BoundField ,针对有一个布尔值的字段有一个 CheckBoxField ( Discontinued) 。
打开DetailsViewTemplateField.aspx 页面并从Toolbox 中拖动一个DetailsView 到设计器上。从DetailsView 的智能标记选择添加一个新的ObjectDataSource 控件 ,使其调用ProductsBLL 类的GetProducts() 方法。
图2 :添加一个新的 ObjectDataSource 控件以调用 GetProducts() 方法
移除此报表的ProductID 、SupplierID 、CategoryID 和 ReorderLevel BoundField 。然后,将 BoundField 重新排序,使CategoryName 和 SupplierName 这两个BoundField 正好显示在 ProductName BoundField 之后。根据您自己的喜好调整 HeaderText 属性,设置 BoundField 的属性。与 GridView 一样,这些 BoundField 层的编辑可以通过 Fields 对话框执行(单击 DetailsView 的智能标记上的 Edit Fields 链接以显示此对话框),也可通过声明式语法完成。最后,清除 DetailsView 的 Height 和 Width 属性值,使 DetailsView 控件能基于所显示的数据来扩展,然后选中智能标记中的 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) ,各行中分别显示产品的名称、类别、供应商、价格、存货量、已订货量及其断货状态。
图3 :通过 BoundField 显示产品详细信息
步骤2 :将价格、存货量及已订货量合并到一行
DetailsView 用一行来分别显示 UnitPrice 、UnitsInStock 和UnitsOnOrder 字段。我们可以使用 TemplateField 将这几个数据字段合并到一行中。方法是添加一个新的 TemplateField 或将现有 UnitPrice 、 UnitsInStock 及 UnitsOnOrder 这几个 BoundField 中的某一个转换为 TemplateField 。尽管笔者比较喜欢后一种方法,但是在这里,我们使用添加一个新的 TemplateField 。
首先单击DetailsView 的智能标记中的 Edit Fields 链接以显示Fields 对话框。然后,添加一个新的 TemplateField ,将其 HeaderText 属性设为 Price and Inventory ,然后移动新的 TemplateField 使其位于 UnitPrice BoundField 上方。
图4 :添加一个新的 TemplateField 到 DetailsView 控件中
由于新的TemplateField 将包含 UnitPrice 、UnitsInStock 和 UnitsOnOrder 这几个 BoundField 中显示的值,我们将它们移除。
本步骤的最后是定义 Price and Inventory TemplateField 的ItemTemplate 标记,可以在 设计器 中通过DetailsView 的模板编辑界面完成,也可以手动通过控件的声明式语法完成。与GridView 一样,单击智能标记中的 Edit Templates 链接,就可以访问DetailsView 的模板编辑界面。在此界面上,可以从下拉列表中选择要编辑的模板,然后从Toolbox 中添加任何 Web 控件。
此处我们首先添加一个 Label 控件到 Price and Inventory 的 TemplateField 的 ItemTemplate 中。然后,在 Web Label 控件的智能标记上单击 Edit DataBindings 链接,将 Text 属性绑定到 UnitPrice 字段。
图5 :将 Label 的 Text 属性绑定到 UnitPrice 数据字段
设置价格为货币格式
此设置将使得Web Label 控件 Price and Inventory 的 TemplateField 只显示选中产品的价格。图 6 显示了我们目前通过浏览器查看进度的截屏。
图6 :Price and Inventory 的 TemplateField 显示价格
注意,产品的价格未设为货币格式。通过将 BoundField 的 HtmlEncode 属性设为 false 、DataFormatString 属性设为{0:formatSpecifier} ,可以实现格式设置。而对于 TemplateField 而言,任何格式设置命令都必须通过数据绑定语法指定,或通过在应用程序代码(如: ASP.NET 页面的code-behind 类)中定义的格式设置方法指定。
要指定Web Label 控件中使用的数据绑定语法格式设置,单击 Label 的智能标记上的 Edit DataBindings 链接,返回 DataBindings 对话框。您可以在 Format 下拉列表中直接输入格式设置命令,也可从已定义的格式字符串中选择。与 BoundField 的 DataFormatString 属性一样,通过 {0:formatSpecifier} 指定格式设置。
对于UnitPrice 字段,从下拉列表中选择适当的值或手动输入{0:C} 应用货币格式。
图7 :将 Price 设为货币格式
格式设置是通过 Bind 或 Eval 方法的第二个参数声明式指定的。上面通过设计器进行的设置在声明式标记中呈现为如下数据绑定语法:
<asp:Label ID="Label1" runat="server" Text='<%# Eval("UnitPrice", "{0:C}") %>'/>
将其余数据字段添加到TemplateField
现在我们已经显示了 Price and Inventory 的 TemplateField 中的UnitPrice 数据字段,并对其进行了格式设置。接下来需要显示 UnitsInStock 和 UnitsOnOrder 字段。我们将这两个字段显示在价格下方的括号内。这类标记可以在设计器的模板编辑界面上添加,将鼠标定位在模板内,直接输入要显示的文本即可。也可直接在声明式语法中输入标记。
添加静态标记、Web 标签控件及数据绑定语法,使 Price and Inventory 的 TemplateField 显示下列价格及存货信息:
UnitPrice
(In Stock / On Order: UnitsInStock / UnitsOnOrder)
完成此任务后,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 中。
图8 :价格及存货信息显示在同一行中
步骤3 :定制Discontinued 字段信息
Products 表的 Discontinued 列是一个比特值,表示相应产品是否已停售。将 DetailsView (或 GridView )绑定到数据源控件时,像 Discontinued 这样的布尔值实现为 CheckBoxField ; ProductID 、 ProductName 等非布尔值则实现为 BoundField 。 CheckBoxField 呈现为一个禁用的复选框。如果数据字段值为True ,复选框为选中转态,反之则为不选中。
我们可能不想显示 CheckBoxField ,而是显示文本说明产品是否断货。操作方法为,从 DetailsView 中移除 CheckBoxField ,然后添加一个 DataField 属性设为 Discontinued 的 BoundField 。花点时间完成此操作。完成后, DetailsView 将为断货的产品显示文本 True ,为没有断货的产品显示文本 False 。
图9 :使用 True 和 False 字符串显示断货状态
假设我们不想显示 True 或 False 字符串,而是 YES 和 NO 。则可以借助于 TemplateField 和一个格式设置方法完成此类定制。一个格式设置方法可以使用任意数量的输入参数,但必须返回 HTML (以字符串的形式)以注入模板。
向DetailsViewTemplateField.aspx 页面的code-behind 类添加一个名为DisplayDiscontinuedAsYESorNO 的格式设置方法 , 该方法接受布尔值作为输入参数,返回字符串。如前一篇教程所述,此方法必须标记为受保护或公共方法,以便于从模板中访问。
protected string DisplayDiscontinuedAsYESorNO(bool discontinued)
{
if (discontinued)
return "YES";
else
return "NO";
}
该方法检查输入参数 (discontinued) ,如果参数为true ,返回 YES ;反之则返回 NO 。
注意:在上一篇教程介绍的格式设置方法中,传递的是可能包含 NULL 值的数据字段,因此在访问 EmployeesRow 的 HiredDate 属性前,需要对数据字段进行检查,确认员工的HiredDate 属性值在数据库中的值是否为 NULL 。此处不需再进行此类检查了,因为 Discontinued 列的数据库值不可能指定为 NULL 。所以,也是为什么本方法可以接受布尔值输入参数而非必需是一个 ProductsRow 实例或对象类型的参数。
完成此格式设置方法后,接下来只需从 TemplateField 的 ItemTemplate 调用此方法即可。可以通过移除Discontinued BoundField 创建TemplateField ,也可通过添加一个新的TemplateField 或将 Discontinued BoundField 转换为 TemplateField 。然后在声明式标记视图中,编辑 TemplateField 使其只包含一个调用 DisplayDiscontinuedAsYESorNO 方法的 ItemTemplate ,并传递当前 ProductRow 实例的 Discontinued 属性。这可通过 Eval 方法实现。具体地说,TemplateField 的标记应如下所示:
<asp:TemplateField HeaderText="Discontinued" SortExpression="Discontinued">
<ItemTemplate>
<%# DisplayDiscontinuedAsYESorNO((bool)
Eval("Discontinued")) %>
</ItemTemplate>
</asp:TemplateField>
这将使得呈现DetailsView 时调用 DisplayDiscontinuedAsYESorNO 方法,传递ProductRow 实例的 Discontinued 值。Eval 方法返回一个对象类型的值,而DisplayDiscontinuedAsYESorNO 方法需要接受的是一个布尔类型的输入参数。因此我们将 Eval 方法返回的值转换为布尔值。 DisplayDiscontinuedAsYESorNO 方法随后将根据它接收的值返回 YES 或 NO 。返回的值即 DetailsView 行中显示的内容(见图 10 )。
图10 :Discontinued 行现在显示 YES 或 NO 值
小结
使用DetailsView 控件中的 TemplateField ,可以比运用其他字段控件更灵活地显示数据。 TemplateField 尤其适用于如下场景:
- 需要在一个 GridView 列中显示多个数据字段
- 使用 Web 控件表达数据比用纯文本效果更好。
- 该输出取决于底层数据 , 如显示元数据或以重新格式化的数据显示
但尽管通过TemplateField 可以更灵活地呈现DetailsView 的底层数据,但由于每个字段呈现为 HTML<table> 的一行,DetailsView 输出显得过于中规中矩。
在配置呈现输出方面,FormView 控件则提供了更大的灵活性。FormView 不包含字段,而是一系列模板(ItemTemplate 、EditItemTemplate 、HeaderTemplate 等 )。下一篇教程将介绍如何使用 FormView 来更好地控制页面布局。
快乐编程 !