从 GridView 页脚插入新记录 (C#)
虽然 GridView 控件不提供插入新数据记录的内置支持,但本教程演示如何扩充 GridView 以包含插入接口。
简介
如 插入、更新和删除数据概述 教程中所述,GridView、DetailsView 和 FormView Web 控件都包含内置的数据修改功能。 与声明性数据源控件一起使用时,可以快速轻松地将这三个 Web 控件配置为修改数据,而无需编写单行代码。 遗憾的是,只有 DetailsView 和 FormView 控件提供内置的插入、编辑和删除功能。 GridView 仅提供编辑和删除支持。 但是,使用一点肘部脂,我们可以扩充 GridView 以包含插入接口。
在向 GridView 添加插入功能时,我们负责决定如何添加新记录、创建插入接口以及编写代码以插入新记录。 本教程介绍如何将插入接口添加到 GridView 的页脚行 (请参阅图 1) 。 每列的页脚单元格包括相应的数据收集用户界面元素 (产品名称的 TextBox、供应商的 DropDownList 等) 。 我们还需要“添加”按钮的列,单击时,该列将导致回发,并使用页脚行中提供的值将 Products
新记录插入表中。
图 1:页脚行提供了一个用于添加新产品的界面, (单击以查看全尺寸图像)
步骤 1:在 GridView 中显示产品信息
在关注在 GridView 的页脚中创建插入接口之前,让我们首先重点介绍如何将 GridView 添加到列出数据库中产品的页面。 首先打开 文件夹中的页面InsertThroughFooter.aspx
EnhancedGridView
,将“GridView”从“工具箱”拖到Designer,并将 GridView 的 ID
属性设置为 Products
。 接下来,使用 GridView 的智能标记将其绑定到名为 ProductsDataSource
的新 ObjectDataSource。
图 2:新建名为 ProductsDataSource
的对象数据源 (单击以查看全尺寸图像)
将 ObjectDataSource 配置为使用 ProductsBLL
类 s GetProducts()
方法来检索产品信息。 在本教程中,让我们严格关注添加插入功能,而不必担心编辑和删除。 因此,请确保“插入”选项卡中的下拉列表设置为 AddProduct()
,并且“更新”和“删除”选项卡中的下拉列表设置为“无” () 。
图 3:将 AddProduct
方法映射到 ObjectDataSource 方法 Insert()
(单击以查看全尺寸图像)
图 4:将“更新”和“删除”选项卡 Drop-Down Lists 设置为“无 (”) (单击以查看全尺寸图像)
完成 ObjectDataSource 的“配置数据源”向导后,Visual Studio 会自动将每个相应数据字段的字段添加到 GridView。 现在,请保留 Visual Studio 添加的所有字段。 在本教程的后面部分,我们将返回并删除一些在添加新记录时不需要指定其值的字段。
由于数据库中有近 80 个产品,因此用户必须一直向下滚动到网页底部才能添加新记录。 因此,让我们启用分页,使插入界面更可见且更易于访问。 若要打开分页,只需检查 GridView 智能标记中的“启用分页”复选框。
此时,GridView 和 ObjectDataSource 的声明性标记应如下所示:
<asp:GridView ID="Products" runat="server" AutoGenerateColumns="False"
DataKeyNames="ProductID" DataSourceID="ProductsDataSource"
AllowPaging="True" EnableViewState="False">
<Columns>
<asp:BoundField DataField="ProductID" HeaderText="ProductID"
InsertVisible="False" ReadOnly="True"
SortExpression="ProductID" />
<asp:BoundField DataField="ProductName" HeaderText="ProductName"
SortExpression="ProductName" />
<asp:BoundField DataField="SupplierID" HeaderText="SupplierID"
SortExpression="SupplierID" />
<asp:BoundField DataField="CategoryID" HeaderText="CategoryID"
SortExpression="CategoryID" />
<asp:BoundField DataField="QuantityPerUnit" HeaderText="QuantityPerUnit"
SortExpression="QuantityPerUnit" />
<asp:BoundField DataField="UnitPrice" HeaderText="UnitPrice"
SortExpression="UnitPrice" />
<asp:BoundField DataField="UnitsInStock" HeaderText="UnitsInStock"
SortExpression="UnitsInStock" />
<asp:BoundField DataField="UnitsOnOrder" HeaderText="UnitsOnOrder"
SortExpression="UnitsOnOrder" />
<asp:BoundField DataField="ReorderLevel" HeaderText="ReorderLevel"
SortExpression="ReorderLevel" />
<asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued"
SortExpression="Discontinued" />
<asp:BoundField DataField="CategoryName" HeaderText="CategoryName"
ReadOnly="True" SortExpression="CategoryName" />
<asp:BoundField DataField="SupplierName" HeaderText="SupplierName"
ReadOnly="True" SortExpression="SupplierName" />
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ProductsDataSource" runat="server"
InsertMethod="AddProduct" OldValuesParameterFormatString="original_{0}"
SelectMethod="GetProducts" TypeName="ProductsBLL">
<InsertParameters>
<asp:Parameter Name="productName" Type="String" />
<asp:Parameter Name="supplierID" Type="Int32" />
<asp:Parameter Name="categoryID" Type="Int32" />
<asp:Parameter Name="quantityPerUnit" Type="String" />
<asp:Parameter Name="unitPrice" Type="Decimal" />
<asp:Parameter Name="unitsInStock" Type="Int16" />
<asp:Parameter Name="unitsOnOrder" Type="Int16" />
<asp:Parameter Name="reorderLevel" Type="Int16" />
<asp:Parameter Name="discontinued" Type="Boolean" />
</InsertParameters>
</asp:ObjectDataSource>
图 5:所有产品数据字段都显示在分页的 GridView 中 (单击以查看全尺寸图像)
步骤 2:添加页脚行
除了标题行和数据行,GridView 还包括页脚行。 根据 GridView 和 ShowHeader
属性的值显示页眉和 ShowFooter
页脚行。 若要显示页脚行,只需将 ShowFooter
属性设置为 true
。 如图 6 所示,将 属性设置为 ShowFooter
向 true
网格添加页脚行。
图 6:若要显示页脚行,请将 设置为 ShowFooter
True
(单击 以查看全尺寸图像)
请注意,页脚行的背景色为深红色。 这是由于我们在使用 ObjectDataSource 显示数据 教程中创建并应用于所有页面的 DataWebControls 主题。 具体而言, GridView.skin
文件配置 FooterStyle
属性,以便使用 FooterStyle
CSS 类。 类 FooterStyle
在 中 Styles.css
定义,如下所示:
.FooterStyle
{
background-color: #a33;
color: White;
text-align: right;
}
注意
我们在以前的教程中探索了如何使用 GridView 的页脚行。 如果需要,请参阅 在 GridView 的页脚中显示摘要信息 教程进行复习。
将 ShowFooter
属性设置为 true
后,请花点时间在浏览器中查看输出。 目前页脚行不包含任何文本或 Web 控件。 在步骤 3 中,我们将修改每个 GridView 字段的页脚,使其包含相应的插入接口。
图 7:“空页脚行”显示在分页界面控件上方 (单击以查看全尺寸图像)
步骤 3:自定义页脚行
回到 GridView 控件中的使用 TemplateFields 教程中,我们了解了如何使用 TemplateFields (而不是 BoundFields 或 CheckBoxFields) 极大地自定义特定 GridView 列的显示;在 自定义数据修改接口 中,我们查看了如何使用 TemplateFields 自定义 GridView 中的编辑界面。 回想一下,TemplateField 由许多模板组成,这些模板定义用于某些类型的行的标记、Web 控件和数据绑定语法的组合。 例如,指定 ItemTemplate
用于只读行的模板,而 EditItemTemplate
定义可编辑行的模板。
除了 和 EditItemTemplate
,ItemTemplate
TemplateField 还包括一个 FooterTemplate
,用于指定页脚行的内容。 因此,我们可以将每个字段插入接口所需的 Web 控件添加到 中 FooterTemplate
。 若要开始,请将 GridView 中的所有字段转换为 TemplateFields。 为此,可以单击 GridView 智能标记中的“编辑列”链接,选择左下角的每个字段,然后单击“将此字段转换为 TemplateField”链接。
图 8:将每个字段转换为 TemplateField
单击“将此字段转换为 TemplateField”会将当前字段类型转换为等效的 TemplateField。 例如,每个 BoundField 替换为 TemplateField,其中包含 ItemTemplate
显示相应数据字段的 Label 和 EditItemTemplate
TextBox 中显示数据字段的 。 ProductName
BoundField 已转换为以下 TemplateField 标记:
<asp:TemplateField HeaderText="ProductName" SortExpression="ProductName">
<EditItemTemplate>
<asp:TextBox ID="TextBox1" runat="server"
Text='<%# Bind("ProductName") %>'></asp:TextBox>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="Label2" runat="server"
Text='<%# Bind("ProductName") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
同样, Discontinued
CheckBoxField 已转换为 TemplateField,其 ItemTemplate
和 EditItemTemplate
包含 CheckBox Web 控件 (ItemTemplate
,) 禁用了 CheckBox。 只读ProductID
的 BoundField 已转换为 TemplateField,在 和 EditItemTemplate
中都有ItemTemplate
一个 Label 控件。 简言之,将现有 GridView 字段转换为 TemplateField 是切换到更可自定义的 TemplateField 且不丢失任何现有字段功能的快速简单方法。
由于我们正在使用的 GridView 不支持编辑,因此请随时从每个 TemplateField 中删除 EditItemTemplate
,只 ItemTemplate
留下 。 执行此操作后,GridView 的声明性标记应如下所示:
<asp:GridView ID="Products" runat="server" AutoGenerateColumns="False"
DataKeyNames="ProductID" DataSourceID="ProductsDataSource"
AllowPaging="True" EnableViewState="False" ShowFooter="True">
<Columns>
<asp:TemplateField HeaderText="ProductID" InsertVisible="False"
SortExpression="ProductID">
<ItemTemplate>
<asp:Label ID="Label1" runat="server"
Text='<%# Bind("ProductID") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="ProductName" SortExpression="ProductName">
<ItemTemplate>
<asp:Label ID="Label2" runat="server"
Text='<%# Bind("ProductName") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="SupplierID" SortExpression="SupplierID">
<ItemTemplate>
<asp:Label ID="Label3" runat="server"
Text='<%# Bind("SupplierID") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="CategoryID" SortExpression="CategoryID">
<ItemTemplate>
<asp:Label ID="Label4" runat="server"
Text='<%# Bind("CategoryID") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="QuantityPerUnit"
SortExpression="QuantityPerUnit">
<ItemTemplate>
<asp:Label ID="Label5" runat="server"
Text='<%# Bind("QuantityPerUnit") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="UnitPrice" SortExpression="UnitPrice">
<ItemTemplate>
<asp:Label ID="Label6" runat="server"
Text='<%# Bind("UnitPrice") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="UnitsInStock"
SortExpression="UnitsInStock">
<ItemTemplate>
<asp:Label ID="Label7" runat="server"
Text='<%# Bind("UnitsInStock") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="UnitsOnOrder"
SortExpression="UnitsOnOrder">
<ItemTemplate>
<asp:Label ID="Label8" runat="server"
Text='<%# Bind("UnitsOnOrder") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="ReorderLevel"
SortExpression="ReorderLevel">
<ItemTemplate>
<asp:Label ID="Label9" runat="server"
Text='<%# Bind("ReorderLevel") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Discontinued"
SortExpression="Discontinued">
<ItemTemplate>
<asp:CheckBox ID="CheckBox1" runat="server"
Checked='<%# Bind("Discontinued") %>' Enabled="false" />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="CategoryName"
SortExpression="CategoryName">
<ItemTemplate>
<asp:Label ID="Label10" runat="server"
Text='<%# Bind("CategoryName") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="SupplierName"
SortExpression="SupplierName">
<ItemTemplate>
<asp:Label ID="Label11" runat="server"
Text='<%# Bind("SupplierName") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
现在,每个 GridView 字段都已转换为 TemplateField,我们可以在每个字段中 FooterTemplate
输入相应的插入接口。 某些字段 (没有插入界面 ProductID
,例如) ;其他字段在用于收集新产品信息的 Web 控件中会有所不同。
若要创建编辑界面,请从 GridView 智能标记中选择“编辑模板”链接。 然后,从下拉列表中选择相应的字段,FooterTemplate
并将相应的控件从“工具箱”拖到Designer。
图 9:将适当的插入接口添加到每个字段 FooterTemplate
(单击以查看全尺寸图像)
以下项目符号列表枚举 GridView 字段,指定要添加的插入接口:
ProductID
没有。ProductName
添加 TextBox 并将其设置为ID
NewProductName
。 还添加 RequiredFieldValidator 控件,以确保用户输入新产品名称的值。SupplierID
没有。CategoryID
没有。QuantityPerUnit
添加 TextBox,并将其ID
设置为NewQuantityPerUnit
。UnitPrice
添加名为NewUnitPrice
的 TextBox 和 CompareValidator,以确保输入的值是大于或等于零的货币值。UnitsInStock
使用设置为NewUnitsInStock
的ID
TextBox。 包括一个 CompareValidator,确保输入的值是大于或等于零的整数值。UnitsOnOrder
使用设置为NewUnitsOnOrder
的ID
TextBox。 包括一个 CompareValidator,确保输入的值是大于或等于零的整数值。ReorderLevel
使用设置为NewReorderLevel
的ID
TextBox。 包括一个 CompareValidator,确保输入的值是大于或等于零的整数值。Discontinued
添加 CheckBox,并将其ID
设置为NewDiscontinued
。CategoryName
添加 DropDownList 并将其设置为ID
NewCategoryID
。 将其绑定到名为CategoriesDataSource
的新 ObjectDataSource,并将其配置为使用CategoriesBLL
类 sGetCategories()
方法。 让 DropDownList 显示ListItem
CategoryName
数据字段,并将CategoryID
数据字段用作其值。SupplierName
添加 DropDownList 并将其设置为ID
NewSupplierID
。 将其绑定到名为SuppliersDataSource
的新 ObjectDataSource,并将其配置为使用SuppliersBLL
类 sGetSuppliers()
方法。 让 DropDownList 显示ListItem
CompanyName
数据字段,并将SupplierID
数据字段用作其值。
对于每个验证控件,请清除 ForeColor
属性, FooterStyle
以便 CSS 类的白色前景色将用于替代默认红色。 此外, ErrorMessage
请使用 属性进行详细说明,但将 Text
属性设置为星号。 若要防止验证控件的文本导致插入接口换行为两行,请将每个使用验证控件的 FooterTemplate
s 属性设置为 FooterStyle
Wrap
false。 最后,在 GridView 下添加 ValidationSummary 控件,并将其 ShowMessageBox
属性设置为 true
,将其 ShowSummary
属性设置为 false
。
添加新产品时,我们需要提供 CategoryID
和 SupplierID
。 此信息通过 和 SupplierName
字段的页脚单元格CategoryName
中的 DropDownLists 捕获。 我选择使用这些字段而不是 CategoryID
和 SupplierID
TemplateFields,因为在网格的数据行中,用户可能更感兴趣的是查看类别和供应商名称,而不是他们的 ID 值。 CategoryID
由于 和 SupplierID
值现在在 和 SupplierName
字段的插入接口中CategoryName
捕获,因此我们可以从 GridView 中删除 CategoryID
和 SupplierID
TemplateFields。
同样, ProductID
添加新产品时不使用 ,因此 ProductID
也可以删除 TemplateField。 但是,让我们将 ProductID
字段保留在网格中。 除了构成插入界面的 TextBoxes、DropDownLists、CheckBoxes 和验证控件外,我们还需要一个“添加”按钮,单击该按钮时,该按钮将执行逻辑以将新产品添加到数据库。 在步骤 4 中,我们将在 TemplateField 的FooterTemplate
插入界面中包含ProductID
一个“添加”按钮。
随意改进各种 GridView 字段的外观。 例如,你可能想要将 UnitPrice
值格式化为货币,右对齐 UnitsInStock
、 UnitsOnOrder
和 ReorderLevel
字段,并更新 HeaderText
TemplateFields 的值。
在 FooterTemplate
中创建大量插入接口、删除 SupplierID
、 和 CategoryID
TemplateFields,并通过设置和对齐 TemplateFields 来提高网格的美观性后,GridView 声明性标记应如下所示:
<asp:GridView ID="Products" runat="server" AutoGenerateColumns="False"
DataKeyNames="ProductID" DataSourceID="ProductsDataSource"
AllowPaging="True" EnableViewState="False" ShowFooter="True">
<Columns>
<asp:TemplateField HeaderText="ProductID" InsertVisible="False"
SortExpression="ProductID">
<ItemTemplate>
<asp:Label ID="Label1" runat="server"
Text='<%# Bind("ProductID") %>'></asp:Label>
</ItemTemplate>
<ItemStyle HorizontalAlign="Center" />
</asp:TemplateField>
<asp:TemplateField HeaderText="Product" SortExpression="ProductName">
<ItemTemplate>
<asp:Label ID="Label2" runat="server"
Text='<%# Bind("ProductName") %>'></asp:Label>
</ItemTemplate>
<FooterTemplate>
<asp:TextBox ID="NewProductName" runat="server"></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidator1"
runat="server" ControlToValidate="NewProductName"
Display="Dynamic" ForeColor="
ErrorMessage="You must enter a name for the new product.">
* </asp:RequiredFieldValidator>
</FooterTemplate>
<FooterStyle Wrap="False" />
</asp:TemplateField>
<asp:TemplateField HeaderText="Category" SortExpression="CategoryName">
<ItemTemplate>
<asp:Label ID="Label10" runat="server"
Text='<%# Bind("CategoryName") %>'></asp:Label>
</ItemTemplate>
<FooterTemplate>
<asp:DropDownList ID="NewCategoryID" runat="server"
DataSourceID="CategoriesDataSource"
DataTextField="CategoryName" DataValueField="CategoryID">
</asp:DropDownList>
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetCategories" TypeName="CategoriesBLL">
</asp:ObjectDataSource>
</FooterTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Supplier" SortExpression="SupplierName">
<ItemTemplate>
<asp:Label ID="Label11" runat="server"
Text='<%# Bind("SupplierName") %>'></asp:Label>
</ItemTemplate>
<FooterTemplate>
<asp:DropDownList ID="NewSupplierID" runat="server"
DataSourceID="SuppliersDataSource"
DataTextField="CompanyName" DataValueField="SupplierID">
</asp:DropDownList><asp:ObjectDataSource ID="SuppliersDataSource"
runat="server" OldValuesParameterFormatString="original_{0}"
SelectMethod="GetSuppliers" TypeName="SuppliersBLL">
</asp:ObjectDataSource>
</FooterTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Qty/Unit" SortExpression="QuantityPerUnit">
<ItemTemplate>
<asp:Label ID="Label5" runat="server"
Text='<%# Bind("QuantityPerUnit") %>'></asp:Label>
</ItemTemplate>
<FooterTemplate>
<asp:TextBox ID="NewQuantityPerUnit" runat="server"></asp:TextBox>
</FooterTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Price" SortExpression="UnitPrice">
<ItemTemplate>
<asp:Label ID="Label6" runat="server"
Text='<%# Bind("UnitPrice", "{0:c}") %>'></asp:Label>
</ItemTemplate>
<FooterTemplate>
$<asp:TextBox ID="NewUnitPrice" runat="server" Columns="8" />
<asp:CompareValidator ID="CompareValidator1" runat="server"
ControlToValidate="NewUnitPrice"
ErrorMessage="You must enter a valid currency value greater than
or equal to 0.00. Do not include the currency symbol."
ForeColor="" Operator="GreaterThanEqual" Type="Currency"
ValueToCompare="0" Display="Dynamic">
* </asp:CompareValidator>
</FooterTemplate>
<ItemStyle HorizontalAlign="Right" />
<FooterStyle Wrap="False" />
</asp:TemplateField>
<asp:TemplateField HeaderText="Units In Stock"
SortExpression="Units In Stock">
<ItemTemplate>
<asp:Label ID="Label7" runat="server"
Text='<%# Bind("UnitsInStock") %>'></asp:Label>
</ItemTemplate>
<FooterTemplate>
<asp:TextBox ID="NewUnitsInStock" runat="server" Columns="5" />
<asp:CompareValidator ID="CompareValidator2" runat="server"
ControlToValidate="NewUnitsInStock" Display="Dynamic"
ErrorMessage="You must enter a valid numeric value for units
in stock that's greater than or equal to zero."
ForeColor="" Operator="GreaterThanEqual" Type="Integer"
ValueToCompare="0">*</asp:CompareValidator>
</FooterTemplate>
<ItemStyle HorizontalAlign="Right" />
<FooterStyle Wrap="False" />
</asp:TemplateField>
<asp:TemplateField HeaderText="Units On Order" SortExpression="UnitsOnOrder">
<ItemTemplate>
<asp:Label ID="Label8" runat="server"
Text='<%# Bind("UnitsOnOrder") %>'></asp:Label>
</ItemTemplate>
<FooterTemplate>
<asp:TextBox ID="NewUnitsOnOrder" runat="server" Columns="5" />
<asp:CompareValidator ID="CompareValidator3" runat="server"
ControlToValidate="NewUnitsOnOrder" Display="Dynamic"
ErrorMessage="You must enter a valid numeric value for units on
order that's greater than or equal to zero."
ForeColor="" Operator="GreaterThanEqual" Type="Integer"
ValueToCompare="0">*</asp:CompareValidator>
</FooterTemplate>
<ItemStyle HorizontalAlign="Right" />
<FooterStyle Wrap="False" />
</asp:TemplateField>
<asp:TemplateField HeaderText="Reorder Level" SortExpression="ReorderLevel">
<ItemTemplate>
<asp:Label ID="Label9" runat="server"
Text='<%# Bind("ReorderLevel") %>'></asp:Label>
</ItemTemplate>
<FooterTemplate>
<asp:TextBox ID="NewReorderLevel" runat="server" Columns="5" />
<asp:CompareValidator ID="CompareValidator4" runat="server"
ControlToValidate="NewReorderLevel" Display="Dynamic"
ErrorMessage="You must enter a valid numeric value for reorder
level that's greater than or equal to zero."
ForeColor="" Operator="GreaterThanEqual" Type="Integer"
ValueToCompare="0">*</asp:CompareValidator>
</FooterTemplate>
<ItemStyle HorizontalAlign="Right" />
<FooterStyle Wrap="False" />
</asp:TemplateField>
<asp:TemplateField HeaderText="Discontinued" SortExpression="Discontinued">
<ItemTemplate>
<asp:CheckBox ID="CheckBox1" runat="server"
Checked='<%# Bind("Discontinued") %>' Enabled="false" />
</ItemTemplate>
<FooterTemplate>
<asp:CheckBox ID="NewDiscontinued" runat="server" />
</FooterTemplate>
<ItemStyle HorizontalAlign="Center" />
<FooterStyle HorizontalAlign="Center" />
</asp:TemplateField>
</Columns>
</asp:GridView>
通过浏览器查看时,GridView 的页脚行现在包含已完成的插入接口 (请参阅图 10) 。 此时,插入界面不包括用户指示她输入了新产品的数据并希望将新记录插入数据库的方法。 此外,我们尚未解决在页脚中输入的数据如何转换为数据库中的新记录 Products
。 在步骤 4 中,我们将了解如何将“添加”按钮包含在插入界面中,以及如何在单击代码时在回发时执行代码。 步骤 5 演示如何使用页脚中的数据插入新记录。
图 10:GridView 页脚提供用于添加新记录的接口 (单击以查看全尺寸图像)
步骤 4:在插入界面中包含“添加”按钮
我们需要在插入界面的某个位置包含一个“添加”按钮,因为页脚行插入界面当前缺乏用户指示他们已完成输入新产品信息的方法。 这可以放置在现有 FooterTemplate
中的一个 中,或者我们可以将新列添加到网格中以实现此目的。 对于本教程,让我们将“添加”按钮放在 ProductID
TemplateField s 中 FooterTemplate
。
在Designer,单击 GridView 智能标记中的“编辑模板”链接,然后从下拉列表中选择ProductID
字段FooterTemplate
。 如果希望) 模板,请将按钮 Web 控件 (、LinkButton 或 ImageButton 添加,将其 ID 设置为 AddProduct
,将其 CommandName
设置为 Insert,将其 Text
属性设置为 Add,如图 11 所示。
图 11:将“添加”按钮置于 ProductID
TemplateField (FooterTemplate
单击以查看全尺寸图像)
包含“添加”按钮后,在浏览器中测试页面。 请注意,单击插入界面中具有无效数据的“添加”按钮时,回发会短路,ValidationSummary 控件指示无效数据 (见图 12) 。 输入适当的数据后,单击“添加”按钮会导致回发。 但是,不会向数据库添加任何记录。 我们需要编写一些代码才能实际执行插入。
图 12:如果插入接口中存在无效数据,则添加按钮的回发短路 (单击以查看全尺寸图像)
注意
插入接口中的验证控件未分配给验证组。 只要插入接口是页面上唯一的一组验证控件,这就可以正常工作。 但是,如果页面上有其他验证控件 (如网格编辑界面) 中的验证控件,则应为插入界面和添加按钮属性 ValidationGroup
中的验证控件分配相同的值,以便将这些控件与特定的验证组相关联。 有关将页面上的验证控件和按钮分区到验证组的详细信息,请参阅 在 ASP.NET 2.0 中剖析 验证控件。
步骤 5:在表中插入新记录Products
使用 GridView 的内置编辑功能时,GridView 会自动处理执行更新所需的所有工作。 具体而言,单击“更新”按钮时,它会将从编辑界面输入的值复制到 ObjectDataSource 集合 UpdateParameters
中的参数,并通过调用 ObjectDataSource 方法 Update()
启动更新。 由于 GridView 不提供用于插入的此类内置功能,因此我们必须实现调用 ObjectDataSource 方法 Insert()
的代码,并将值从插入接口复制到 ObjectDataSource 集合 InsertParameters
。
应在单击“添加”按钮后执行此插入逻辑。 如 在 GridView 中添加和响应按钮 教程中所述,只要单击 GridView 中的 Button、LinkButton 或 ImageButton,GridView 事件 RowCommand
就会在回发时触发。 此事件将触发是否显式添加了 Button、LinkButton 或 ImageButton,例如页脚行中的“添加”按钮,或者是否由 GridView (自动添加,例如选择“启用排序”时每列顶部的 LinkButton,或者选择“启用分页”) 时分页界面中的 LinkButton。
因此,若要响应单击“添加”按钮的用户,我们需要为 GridView 事件 RowCommand
创建事件处理程序。 由于每当单击 GridView 中的任何 Button、LinkButton 或 ImageButton 时,就会触发此事件,因此只有当传递到事件处理程序中的属性映射到CommandName
“添加”按钮的值 (“插入”) 时,我们才继续插入逻辑CommandName
。 此外,我们还应仅在验证控件报告有效数据时继续操作。 若要适应这种情况,请使用以下代码为 RowCommand
事件创建事件处理程序:
protected void Products_RowCommand(object sender, GridViewCommandEventArgs e)
{
// Insert data if the CommandName == "Insert"
// and the validation controls indicate valid data...
if (e.CommandName == "Insert" && Page.IsValid)
{
// TODO: Insert new record...
}
}
注意
你可能想知道为什么事件处理程序会费力检查 Page.IsValid
属性。 毕竟,如果插入接口中提供了无效数据,回发是否会被禁止? 只要用户未禁用 JavaScript 或已采取措施规避客户端验证逻辑,此假设就是正确的。 简言之,绝不应严格依赖于客户端验证;在使用数据之前,应始终执行服务器端检查的有效性。
在步骤 1 中, ProductsDataSource
我们创建了 ObjectDataSource,以便其 Insert()
方法映射到 ProductsBLL
类 s AddProduct
方法。 若要将新记录插入表中 Products
,只需调用 ObjectDataSource 方法 Insert()
即可:
protected void Products_RowCommand(object sender, GridViewCommandEventArgs e)
{
// Insert data if the CommandName == "Insert"
// and the validation controls indicate valid data...
if (e.CommandName == "Insert" && Page.IsValid)
{
// Insert new record
ProductsDataSource.Insert();
}
}
Insert()
调用 方法后,剩下的就是将值从插入接口复制到传递给ProductsBLL
类 s AddProduct
方法的参数。 正如我们在 检查与插入、更新和删除关联的事件 教程中看到的那样,这可以通过 ObjectDataSource 事件 Inserting
来完成。 在 事件中 Inserting
,我们需要以编程方式引用 GridView 页脚行中的 Products
控件,并将其值分配给 e.InputParameters
集合。 如果用户省略某个值(例如将 ReorderLevel
TextBox 留空),则需要指定插入到数据库中的值应为 NULL
。 AddProducts
由于 方法接受可为空数据库字段的可为空类型,因此只需使用可为空类型,并在省略用户输入的情况下将其值设置为 null
。
protected void ProductsDataSource_Inserting
(object sender, ObjectDataSourceMethodEventArgs e)
{
// Programmatically reference Web controls in the inserting interface...
TextBox NewProductName =
(TextBox)Products.FooterRow.FindControl("NewProductName");
DropDownList NewCategoryID =
(DropDownList)Products.FooterRow.FindControl("NewCategoryID");
DropDownList NewSupplierID =
(DropDownList)Products.FooterRow.FindControl("NewSupplierID");
TextBox NewQuantityPerUnit =
(TextBox)Products.FooterRow.FindControl("NewQuantityPerUnit");
TextBox NewUnitPrice =
(TextBox)Products.FooterRow.FindControl("NewUnitPrice");
TextBox NewUnitsInStock =
(TextBox)Products.FooterRow.FindControl("NewUnitsInStock");
TextBox NewUnitsOnOrder =
(TextBox)Products.FooterRow.FindControl("NewUnitsOnOrder");
TextBox NewReorderLevel =
(TextBox)Products.FooterRow.FindControl("NewReorderLevel");
CheckBox NewDiscontinued =
(CheckBox)Products.FooterRow.FindControl("NewDiscontinued");
// Set the ObjectDataSource's InsertParameters values...
e.InputParameters["productName"] = NewProductName.Text;
e.InputParameters["supplierID"] =
Convert.ToInt32(NewSupplierID.SelectedValue);
e.InputParameters["categoryID"] =
Convert.ToInt32(NewCategoryID.SelectedValue);
string quantityPerUnit = null;
if (!string.IsNullOrEmpty(NewQuantityPerUnit.Text))
quantityPerUnit = NewQuantityPerUnit.Text;
e.InputParameters["quantityPerUnit"] = quantityPerUnit;
decimal? unitPrice = null;
if (!string.IsNullOrEmpty(NewUnitPrice.Text))
unitPrice = Convert.ToDecimal(NewUnitPrice.Text);
e.InputParameters["unitPrice"] = unitPrice;
short? unitsInStock = null;
if (!string.IsNullOrEmpty(NewUnitsInStock.Text))
unitsInStock = Convert.ToInt16(NewUnitsInStock.Text);
e.InputParameters["unitsInStock"] = unitsInStock;
short? unitsOnOrder = null;
if (!string.IsNullOrEmpty(NewUnitsOnOrder.Text))
unitsOnOrder = Convert.ToInt16(NewUnitsOnOrder.Text);
e.InputParameters["unitsOnOrder"] = unitsOnOrder;
short? reorderLevel = null;
if (!string.IsNullOrEmpty(NewReorderLevel.Text))
reorderLevel = Convert.ToInt16(NewReorderLevel.Text);
e.InputParameters["reorderLevel"] = reorderLevel;
e.InputParameters["discontinued"] = NewDiscontinued.Checked;
}
Inserting
事件处理程序完成后,可以通过 GridView 的页脚行将新记录添加到Products
数据库表。 继续尝试添加几个新产品。
增强和自定义添加操作
目前,单击“添加”按钮会将新记录添加到数据库表,但不提供记录已成功添加的任何类型的视觉反馈。 理想情况下,标签 Web 控件或客户端警报框会通知用户其插入操作已成功完成。 我把这个留作读者的练习。
本教程中使用的 GridView 不对列出的产品应用任何排序顺序,也不允许最终用户对数据进行排序。 因此,记录按主键字段按数据库中的顺序排序。 由于每个新记录 ProductID
的值都大于最后一条记录,因此每次添加新产品时,都会将其插入网格的末尾。 因此,可能需要在添加新记录后自动将用户发送到 GridView 的最后一页。 为此,可以在事件处理程序中RowCommand
对 ProductsDataSource.Insert()
的调用后添加以下代码行,以指示在将数据绑定到 GridView 后需要将用户发送到最后一页:
// Indicate that the user needs to be sent to the last page
SendUserToLastPage = true;
SendUserToLastPage
是一个页级布尔变量,最初赋值为 false
。 在 GridView 的 DataBound
事件处理程序中,如果 SendUserToLastPage
为 false,则 PageIndex
更新 属性以将用户发送到最后一页。
protected void Products_DataBound(object sender, EventArgs e)
{
// Send user to last page of data, if needed
if (SendUserToLastPage)
Products.PageIndex = Products.PageCount - 1;
}
在事件处理程序 (RowCommand
而不是事件处理程序) 中设置DataBound
属性的原因是PageIndex
,当事件处理程序触发时RowCommand
,我们尚未将新记录添加到Products
数据库表。 因此,在 RowCommand
事件处理程序中,最后一个页索引 (PageCount - 1
) 表示添加新产品 之前 的最后一个页面索引。 对于要添加的大多数产品,在添加新产品后,最后一个页面索引是相同的。 但是,当添加的产品生成新的最后一页索引时,如果我们在事件处理程序中RowCommand
错误地更新 PageIndex
,则在添加新产品) 而不是新的最后一页索引之前,我们将转到第二页到最后一页 (最后一页索引。 由于事件处理程序在 DataBound
添加新产品且数据反弹到网格后触发,因此通过设置 PageIndex
该属性,我们知道我们重新获得了正确的最后一页索引。
最后,本教程中使用的 GridView 非常宽,因为必须收集用于添加新产品的字段数。 由于此宽度,详细信息视图的垂直布局可能是首选。 通过收集更少的输入,可以减小 GridView 的总体宽度。 在添加新产品时,我们也许不需要收集 UnitsOnOrder
、 UnitsInStock
和 ReorderLevel
字段,在这种情况下,可以从 GridView 中删除这些字段。
若要调整收集的数据,可以使用以下两种方法之一:
- 继续使用
AddProduct
需要 、UnitsInStock
和ReorderLevel
字段值UnitsOnOrder
的方法。 在事件处理程序中Inserting
,提供硬编码的默认值,用于从插入接口中删除的这些输入。 - 在 类中创建
ProductsBLL
方法的新重载,该重载AddProduct
不接受 、UnitsInStock
和ReorderLevel
字段的UnitsOnOrder
输入。 然后,在“ASP.NET”页中,将 ObjectDataSource 配置为使用此新重载。
任一选项同样工作。 在以前的教程中,我们使用了后一个选项,即为 ProductsBLL
类的 方法 UpdateProduct
创建多个重载。
总结
GridView 缺少 DetailsView 和 FormView 中的内置插入功能,但需要一些努力,插入接口可以添加到页脚行。 若要在 GridView 中显示页脚行,只需将其 ShowFooter
属性设置为 true
。 可以通过将字段转换为 TemplateField 并将插入接口添加到 FooterTemplate
,为每个字段自定义页脚行内容。 正如我们在本教程中看到的, FooterTemplate
可以包含 Buttons、TextBoxes、DropDownLists、CheckBoxes、用于填充数据驱动 Web 控件 (的数据源控件(如 DropDownLists) )和验证控件。 除了用于收集用户输入的控件之外,还需要添加按钮、LinkButton 或 ImageButton。
单击“添加”按钮时,将调用 ObjectDataSource 方法 Insert()
以启动插入工作流。 然后,ObjectDataSource 将调用配置的 insert 方法 (ProductsBLL
类方法 AddProduct
,在本教程中) 。 在调用 insert 方法之前,必须将值从 GridView 插入接口复制到 ObjectDataSource 集合 InsertParameters
。 这可以通过以编程方式引用 ObjectDataSource 事件处理程序 Inserting
中的插入接口 Web 控件来实现。
本教程完成我们对用于增强 GridView 外观的技术的了解。 下一组教程将介绍如何使用二进制数据(如图像、PDF、Word文档等)和数据 Web 控件。
编程愉快!
关于作者
Scott Mitchell 是七本 ASP/ASP.NET 书籍的作者, 4GuysFromRolla.com 的创始人,自 1998 年以来一直从事 Microsoft Web 技术工作。 Scott 担任独立顾问、培训师和作家。 他的最新书是 山姆斯在24小时内 ASP.NET 2.0自学。 可以在 上联系 mitchell@4GuysFromRolla.com他, 也可以通过他的博客联系到他,该博客可在 http://ScottOnWriting.NET中找到。
特别感谢
本教程系列由许多有用的审阅者查看。 本教程的首席审阅者是 Bernadette Leigh。 有兴趣查看我即将发布的 MSDN 文章? 如果是,请在 处放置一行 mitchell@4GuysFromRolla.com。