从 GridView 页脚插入新记录 (C#)

作者 :Scott Mitchell

下载 PDF

虽然 GridView 控件不提供插入新数据记录的内置支持,但本教程演示如何扩充 GridView 以包含插入接口。

简介

插入、更新和删除数据概述 教程中所述,GridView、DetailsView 和 FormView Web 控件都包含内置的数据修改功能。 与声明性数据源控件一起使用时,可以快速轻松地将这三个 Web 控件配置为修改数据,而无需编写单行代码。 遗憾的是,只有 DetailsView 和 FormView 控件提供内置的插入、编辑和删除功能。 GridView 仅提供编辑和删除支持。 但是,使用一点肘部脂,我们可以扩充 GridView 以包含插入接口。

在向 GridView 添加插入功能时,我们负责决定如何添加新记录、创建插入接口以及编写代码以插入新记录。 本教程介绍如何将插入接口添加到 GridView 的页脚行 (请参阅图 1) 。 每列的页脚单元格包括相应的数据收集用户界面元素 (产品名称的 TextBox、供应商的 DropDownList 等) 。 我们还需要“添加”按钮的列,单击时,该列将导致回发,并使用页脚行中提供的值将 Products 新记录插入表中。

页脚行提供用于添加新产品的接口

图 1:页脚行提供了一个用于添加新产品的界面, (单击以查看全尺寸图像)

步骤 1:在 GridView 中显示产品信息

在关注在 GridView 的页脚中创建插入接口之前,让我们首先重点介绍如何将 GridView 添加到列出数据库中产品的页面。 首先打开 文件夹中的页面InsertThroughFooter.aspxEnhancedGridView,将“GridView”从“工具箱”拖到Designer,并将 GridView 的 ID 属性设置为 Products。 接下来,使用 GridView 的智能标记将其绑定到名为 ProductsDataSource的新 ObjectDataSource。

创建新的对象DataSource 名为 ProductsDataSource

图 2:新建名为 ProductsDataSource 的对象数据源 (单击以查看全尺寸图像)

将 ObjectDataSource 配置为使用 ProductsBLL 类 s GetProducts() 方法来检索产品信息。 在本教程中,让我们严格关注添加插入功能,而不必担心编辑和删除。 因此,请确保“插入”选项卡中的下拉列表设置为 AddProduct() ,并且“更新”和“删除”选项卡中的下拉列表设置为“无” () 。

将 AddProduct 方法映射到 ObjectDataSource 的 Insert () 方法

图 3:将 AddProduct 方法映射到 ObjectDataSource 方法 Insert() (单击以查看全尺寸图像)

将“更新”和“删除”选项卡 Drop-Down Lists 设置为“无 (”)

图 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>

所有产品数据字段都显示在分页 GridView 中

图 5:所有产品数据字段都显示在分页的 GridView 中 (单击以查看全尺寸图像)

除了标题行和数据行,GridView 还包括页脚行。 根据 GridView 和 ShowHeader 属性的值显示页眉和 ShowFooter 页脚行。 若要显示页脚行,只需将 ShowFooter 属性设置为 true。 如图 6 所示,将 属性设置为 ShowFootertrue 网格添加页脚行。

若要显示页脚行,请将 ShowFooter 设置为 True

图 6:若要显示页脚行,请将 设置为 ShowFooterTrue (单击 以查看全尺寸图像)

请注意,页脚行的背景色为深红色。 这是由于我们在使用 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:“空页脚行”显示在分页界面控件上方 (单击以查看全尺寸图像)

回到 GridView 控件中的使用 TemplateFields 教程中,我们了解了如何使用 TemplateFields (而不是 BoundFields 或 CheckBoxFields) 极大地自定义特定 GridView 列的显示;在 自定义数据修改接口 中,我们查看了如何使用 TemplateFields 自定义 GridView 中的编辑界面。 回想一下,TemplateField 由许多模板组成,这些模板定义用于某些类型的行的标记、Web 控件和数据绑定语法的组合。 例如,指定 ItemTemplate用于只读行的模板,而 EditItemTemplate 定义可编辑行的模板。

除了 和 EditItemTemplateItemTemplateTemplateField 还包括一个 FooterTemplate ,用于指定页脚行的内容。 因此,我们可以将每个字段插入接口所需的 Web 控件添加到 中 FooterTemplate。 若要开始,请将 GridView 中的所有字段转换为 TemplateFields。 为此,可以单击 GridView 智能标记中的“编辑列”链接,选择左下角的每个字段,然后单击“将此字段转换为 TemplateField”链接。

将每个字段转换为 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,其 ItemTemplateEditItemTemplate 包含 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。

将适当的插入接口添加到每个字段的 FooterTemplate

图 9:将适当的插入接口添加到每个字段 FooterTemplate (单击以查看全尺寸图像)

以下项目符号列表枚举 GridView 字段,指定要添加的插入接口:

  • ProductID 没有。
  • ProductName 添加 TextBox 并将其设置为 IDNewProductName。 还添加 RequiredFieldValidator 控件,以确保用户输入新产品名称的值。
  • SupplierID 没有。
  • CategoryID 没有。
  • QuantityPerUnit 添加 TextBox,并将其 ID 设置为 NewQuantityPerUnit
  • UnitPrice 添加名为 NewUnitPrice 的 TextBox 和 CompareValidator,以确保输入的值是大于或等于零的货币值。
  • UnitsInStock使用设置为 NewUnitsInStockID TextBox。 包括一个 CompareValidator,确保输入的值是大于或等于零的整数值。
  • UnitsOnOrder使用设置为 NewUnitsOnOrderID TextBox。 包括一个 CompareValidator,确保输入的值是大于或等于零的整数值。
  • ReorderLevel使用设置为 NewReorderLevelID TextBox。 包括一个 CompareValidator,确保输入的值是大于或等于零的整数值。
  • Discontinued 添加 CheckBox,并将其 ID 设置为 NewDiscontinued
  • CategoryName 添加 DropDownList 并将其设置为 IDNewCategoryID。 将其绑定到名为 CategoriesDataSource 的新 ObjectDataSource,并将其配置为使用 CategoriesBLL 类 s GetCategories() 方法。 让 DropDownList 显示ListItemCategoryName数据字段,并将CategoryID数据字段用作其值。
  • SupplierName 添加 DropDownList 并将其设置为 IDNewSupplierID。 将其绑定到名为 SuppliersDataSource 的新 ObjectDataSource,并将其配置为使用 SuppliersBLL 类 s GetSuppliers() 方法。 让 DropDownList 显示ListItemCompanyName数据字段,并将SupplierID数据字段用作其值。

对于每个验证控件,请清除 ForeColor 属性, FooterStyle 以便 CSS 类的白色前景色将用于替代默认红色。 此外, ErrorMessage 请使用 属性进行详细说明,但将 Text 属性设置为星号。 若要防止验证控件的文本导致插入接口换行为两行,请将每个使用验证控件的 FooterTemplate s 属性设置为 FooterStyleWrap false。 最后,在 GridView 下添加 ValidationSummary 控件,并将其 ShowMessageBox 属性设置为 true ,将其 ShowSummary 属性设置为 false

添加新产品时,我们需要提供 CategoryIDSupplierID。 此信息通过 和 SupplierName 字段的页脚单元格CategoryName中的 DropDownLists 捕获。 我选择使用这些字段而不是 CategoryIDSupplierID TemplateFields,因为在网格的数据行中,用户可能更感兴趣的是查看类别和供应商名称,而不是他们的 ID 值。 CategoryID由于 和 SupplierID 值现在在 和 SupplierName 字段的插入接口中CategoryName捕获,因此我们可以从 GridView 中删除 CategoryIDSupplierID TemplateFields。

同样, ProductID 添加新产品时不使用 ,因此 ProductID 也可以删除 TemplateField。 但是,让我们将 ProductID 字段保留在网格中。 除了构成插入界面的 TextBoxes、DropDownLists、CheckBoxes 和验证控件外,我们还需要一个“添加”按钮,单击该按钮时,该按钮将执行逻辑以将新产品添加到数据库。 在步骤 4 中,我们将在 TemplateField 的FooterTemplate插入界面中包含ProductID一个“添加”按钮。

随意改进各种 GridView 字段的外观。 例如,你可能想要将 UnitPrice 值格式化为货币,右对齐 UnitsInStockUnitsOnOrderReorderLevel 字段,并更新 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 演示如何使用页脚中的数据插入新记录。

GridView 页脚提供用于添加新记录的接口

图 10:GridView 页脚提供用于添加新记录的接口 (单击以查看全尺寸图像)

步骤 4:在插入界面中包含“添加”按钮

我们需要在插入界面的某个位置包含一个“添加”按钮,因为页脚行插入界面当前缺乏用户指示他们已完成输入新产品信息的方法。 这可以放置在现有 FooterTemplate 中的一个 中,或者我们可以将新列添加到网格中以实现此目的。 对于本教程,让我们将“添加”按钮放在 ProductID TemplateField s 中 FooterTemplate

在Designer,单击 GridView 智能标记中的“编辑模板”链接,然后从下拉列表中选择ProductID字段FooterTemplate。 如果希望) 模板,请将按钮 Web 控件 (、LinkButton 或 ImageButton 添加,将其 ID 设置为 AddProduct,将其 CommandName 设置为 Insert,将其 Text 属性设置为 Add,如图 11 所示。

将“添加”按钮放在 ProductID TemplateField 的 FooterTemplate 中

图 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 留空),则需要指定插入到数据库中的值应为 NULLAddProducts由于 方法接受可为空数据库字段的可为空类型,因此只需使用可为空类型,并在省略用户输入的情况下将其值设置为 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 的最后一页。 为此,可以在事件处理程序中RowCommandProductsDataSource.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 的总体宽度。 在添加新产品时,我们也许不需要收集 UnitsOnOrderUnitsInStockReorderLevel 字段,在这种情况下,可以从 GridView 中删除这些字段。

若要调整收集的数据,可以使用以下两种方法之一:

  • 继续使用 AddProduct 需要 、 UnitsInStockReorderLevel 字段值UnitsOnOrder的方法。 在事件处理程序中 Inserting ,提供硬编码的默认值,用于从插入接口中删除的这些输入。
  • 在 类中创建ProductsBLL方法的新重载,该重载AddProduct不接受 、 UnitsInStockReorderLevel 字段的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。