在 DetailsView 控件中使用 TemplateField (C#)
作者 :斯科特·米切尔
GridView 中提供的同一 TemplateFields 功能也可用于 DetailsView 控件。 在本教程中,我们将使用包含 TemplateFields 的 DetailsView 一次显示一个产品。
介绍
TemplateField 提供比 BoundField、CheckBoxField、HyperLinkField 和其他数据字段控件更灵活地呈现数据。 在 前面的教程中 ,我们介绍了如何在 GridView 中使用 TemplateField 来:
- 在一列中显示多个数据字段值。 具体而言,字段
FirstName
LastName
和字段合并为一个 GridView 列。 - 使用备用 Web 控件来表示数据字段值。 我们了解了如何使用日历控件显示
HiredDate
值。 - 基于基础数据显示状态信息。
Employees
虽然该表不包含返回员工已上岗天数的列,但我们可以使用 TemplateField 和格式设置方法在上一教程的 GridView 示例中显示此类信息。
GridView 中提供的同一 TemplateFields 功能也可用于 DetailsView 控件。 在本教程中,我们将使用包含两个 TemplateFields 的 DetailsView 一次显示一个产品。 第一个 TemplateField 会将和UnitsInStock
UnitsOnOrder
数据字段合并UnitPrice
为一个 DetailsView 行。 第二个 TemplateField 将显示字段的值 Discontinued
,但将使用格式设置方法显示“YES”(如果 Discontinued
为 true
)和“NO”,否则为“NO”。
图 1:两个 TemplateField 用于自定义显示(单击以查看全尺寸图像)
现在就开始吧!
步骤 1:将数据绑定到 DetailsView
如上一教程中所述,使用 TemplateFields 时,创建仅包含 BoundFields 的 DetailsView 控件,然后根据需要添加新的 TemplateFields 或将现有 BoundFields 转换为 TemplateFields,这通常最容易。 因此,通过设计器将 DetailsView 添加到页面并将其绑定到返回产品列表的 ObjectDataSource,以启动本教程。 这些步骤将为产品的每个非布尔值字段创建一个 DetailsView,并为一个布尔值字段(已停用)创建 CheckBoxField。
DetailsViewTemplateField.aspx
打开页面,将“详细信息视图”从工具箱拖到设计器上。 从 DetailsView 的智能标记中选择添加新的 ObjectDataSource 控件,该控件调用 ProductsBLL
类 GetProducts()
的方法。
图 2:添加调用方法的新 ObjectDataSource 控件 GetProducts()
(单击以查看全尺寸图像)
对于此报表,CategoryID
请删除 ProductID
BoundFields 和 SupplierID
ReorderLevel
BoundFields。 接下来,对 BoundFields 重新排序,以便 CategoryName
BoundFields 和 SupplierName
BoundFields 立即显示在 BoundField 之后 ProductName
。 可以随意调整 HeaderText
BoundFields 的属性和格式属性,如你认为合适。 与 GridView 一样,可以通过“字段”对话框(通过单击 DetailsView 智能标记中的“编辑字段”链接或通过声明性语法进行访问)执行这些 BoundField 级编辑。 最后,清除 DetailsView 的 Height
属性值, Width
以允许 DetailsView 控件根据显示的数据展开,并在智能标记中选中“启用分页”复选框。
进行这些更改后,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:产品的详细信息使用一系列 BoundFields 显示(单击以查看全尺寸图像)
步骤 2:将价格、库存单位和按订单单位合并为一行
DetailsView 有一行用于UnitPrice
和UnitsInStock
UnitsOnOrder
字段。 可以通过添加新 TemplateField 或通过将其中一个现有UnitPrice
UnitsInStock
字段和 BoundField 转换为 TemplateField,将这些数据字段合并为单个行和 UnitsOnOrder
TemplateField。 虽然我个人更喜欢转换现有的 BoundFields,但让我们通过添加新的 TemplateField 来练习。
首先,单击 DetailsView 智能标记中的“编辑字段”链接以显示“字段”对话框。 接下来,添加新的 TemplateField 并将其属性设置为 HeaderText
“Price and Inventory”,并移动新的 TemplateField,使其定位在 UnitPrice
BoundField 上方。
图 4:向 DetailsView 控件添加新 TemplateField(单击以查看全尺寸图像)
由于这个新的 TemplateField 将包含当前显示在和 BoundFields 中的UnitPrice
UnitsInStock
UnitsOnOrder
值,因此让我们将其删除。
此步骤的最后一项任务是定义 ItemTemplate
Price 和 Inventory TemplateField 的标记,该标记可以通过设计器中的 DetailsView 模板编辑界面或手动完成控件的声明性语法。 与 GridView 一样,可以通过单击智能标记中的“编辑模板”链接来访问 DetailsView 的模板编辑界面。 从此处,你可以从下拉列表中选择要编辑的模板,然后从工具箱中添加任何 Web 控件。
在本教程中,首先向 Price 和 Inventory TemplateField 添加 ItemTemplate
标签控件。 接下来,单击标签 Web 控件智能标记中的“编辑 DataBindings”链接,并将 Text
属性绑定到 UnitPrice
该字段。
图 5:将标签 Text
的属性绑定到 UnitPrice
数据字段(单击以查看全尺寸图像)
将价格格式设置为货币
添加后,标签 Web 控件“价格”和“库存模板字段”现在仅显示所选产品的价格。 图 6 显示到目前为止通过浏览器查看进度的屏幕截图。
图 6:价格和库存模板字段显示价格(单击以查看全尺寸图像)
请注意,产品的价格的格式不是货币。 使用 BoundField 时,可以通过将HtmlEncode
属性false
设置为 属性DataFormatString
{0:formatSpecifier}
来设置格式。 但是,对于 TemplateField,必须在数据绑定语法中指定任何格式说明,或者通过使用应用程序代码中某个位置定义的格式设置方法(例如,在 ASP.NET 页的代码隐藏类中)。
若要指定标签 Web 控件中使用的数据绑定语法的格式,请通过单击标签智能标记中的“编辑 DataBindings”链接返回到 DataBindings 对话框。 可以直接在“格式”下拉列表中键入格式说明,或选择其中一个定义的格式字符串。 与 BoundField DataFormatString
的属性一样,使用 {0:formatSpecifier}
指定格式设置。
UnitPrice
对于字段,请使用通过选择适当的下拉列表值或手动键入{0:C}
来指定的货币格式。
图 7:将价格设置为货币格式(单击以查看全尺寸图像)
以声明方式将格式规范指示为或Eval
方法中的Bind
第二个参数。 刚刚通过设计器进行的设置会导致声明性标记中的以下数据绑定表达式:
<asp:Label ID="Label1" runat="server" Text='<%# Eval("UnitPrice", "{0:C}") %>'/>
将剩余数据字段添加到 TemplateField
此时,我们已在 Price 和 Inventory TemplateField 中显示和格式化 UnitPrice
数据字段,但仍需要显示 UnitsInStock
和 UnitsOnOrder
字段。 让我们在价格和括号下方的一行中显示这些值。 在设计器中的模板编辑界面中,可以通过将光标定位到模板中并直接键入要显示的文本来添加此类标记。 或者,可以直接在声明性语法中输入此标记。
添加静态标记、标签 Web 控件和数据绑定语法,以便 Price 和 Inventory TemplateField 显示价格和库存信息,如下所示:
UnitPrice
(库存/订单: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:自定义已停用的字段信息
表 Products
的 Discontinued
列是一个位值,该值指示产品是否已停用。 将 DetailsView(或 GridView)绑定到数据源控件时,布尔值字段(如 Discontinued
)作为 CheckBoxFields 实现,而非布尔值字段(如 ProductID
, ProductName
等)则实现为 BoundFields。 CheckBoxField 呈现为已禁用的复选框,如果数据字段的值为 True 且未选中,则选中该复选框。
我们不想显示 CheckBoxField,而是显示指示产品是否停止使用的文本。 为此,我们可以从 DetailsView 中删除 CheckBoxField,然后添加一个属性 DataField
设置为 Discontinued
的 BoundField。 请花点时间执行此操作。 更改后,DetailsView 将显示已停用产品的文本“True”,对于仍然处于活动状态的产品,则显示“False”。
图 9:字符串 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
参数为discontinued
“是”,则返回“YES”;否则返回“NO”。
注意
在上一教程中检查的格式设置方法中,我们回顾一下,我们传入了可能包含 NULL
s 的数据字段,因此需要检查员工HiredDate
属性值在访问EmployeesRow
该属性HiredDate
之前是否具有数据库NULL
值。 此处不需要进行此类检查, Discontinued
因为列永远无法分配数据库 NULL
值。 此外,这就是为什么该方法可以接受布尔输入参数,而不必接受 ProductsRow
类型的 object
实例或参数。
完成此格式设置方法后,所有保留项都是从 TemplateField 的 ItemTemplate
调用。 若要创建 TemplateField,请删除 Discontinued
BoundField 并添加新的 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>
这将导致 DisplayDiscontinuedAsYESorNO
在呈现 DetailsView 时调用该方法,并传入 ProductRow
实例 Discontinued
的值。 Eval
由于该方法返回类型的object
值,但DisplayDiscontinuedAsYESorNO
该方法需要类型的bool
输入参数,因此我们将方法返回值强制转换为Eval
bool
。 然后,该方法 DisplayDiscontinuedAsYESorNO
将返回“YES”或“NO”,具体取决于它接收的值。 返回的值是此 DetailsView 行中显示的值(请参阅图 10)。
图 10:“是”或“否”值现在显示在“已停止使用行”中(单击以查看全尺寸图像)
总结
DetailsView 控件中的 TemplateField 允许比其他字段控件提供的数据更灵活地显示数据,并且非常适合以下情况:
- 需要在一个 GridView 列中显示多个数据字段
- 最好使用 Web 控件而不是纯文本来表示数据
- 输出取决于基础数据,例如显示元数据或重新格式化数据
虽然 TemplateFields 在呈现 DetailsView 的基础数据方面具有更大的灵活性,但 DetailsView 输出仍然感觉有点框,因为每个字段在 HTML <table>
中呈现为一行。
FormView 控件在配置呈现的输出方面提供了更大的灵活性。 FormView 不包含字段,而只包含一系列模板(ItemTemplate
、 EditItemTemplate
、 HeaderTemplate
等等)。 我们将在下一教程中了解如何使用 FormView 实现对呈现布局的更多控制。
快乐编程!
关于作者
斯科特·米切尔,七本 ASP/ASP.NET 书籍的作者和 4GuysFromRolla.com 的创始人,自1998年以来一直在与Microsoft Web 技术合作。 斯科特担任独立顾问、教练和作家。 他的最新书是 山姆斯在24小时内 ASP.NET 2.0。 他可以通过他的博客联系到mitchell@4GuysFromRolla.com他,可以在该博客中找到http://ScottOnWriting.NET。
特别感谢
本教程系列由许多有用的审阅者审阅。 本教程的主要审阅者是 Dan Jagers。 有兴趣查看即将发布的 MSDN 文章? 如果是这样,请把我扔一条线。mitchell@4GuysFromRolla.com