自定义 DataList 的编辑界面 (C#)

作者 :Scott Mitchell

下载 PDF

在本教程中,我们将为 DataList 创建更丰富的编辑界面,其中包括 DropDownLists 和 CheckBox。

简介

DataList 中的 EditItemTemplate 标记和 Web 控件定义其可编辑接口。 在我们到目前为止检查的所有可编辑 DataList 示例中,可编辑的界面都由 TextBox Web 控件组成。 在 前面的教程中 ,我们通过添加验证控件改进了编辑时用户体验。

EditItemTemplate可以进一步扩展,以包含除 TextBox 以外的 Web 控件,例如 DropDownLists、RadioButtonLists、Calendars 等。 与 TextBox 一样,在自定义编辑界面以包含其他 Web 控件时,请执行以下步骤:

  1. 将 Web 控件添加到 EditItemTemplate
  2. 使用数据绑定语法将相应的数据字段值分配给相应的属性。
  3. UpdateCommand 事件处理程序中,以编程方式访问 Web 控件值,并将其传递到相应的 BLL 方法中。

在本教程中,我们将为 DataList 创建更丰富的编辑界面,其中包括 DropDownLists 和 CheckBox。 具体而言,我们将创建一个 DataList,用于列出产品信息,并允许更新产品名称、供应商、类别和停产状态 (见图 1) 。

编辑界面包括一个 TextBox、两个 DropDownList 和一个 CheckBox

图 1:编辑界面包括一个 TextBox、两个 DropDownList 和一个 CheckBox (单击以查看全尺寸图像)

步骤 1:显示产品信息

在创建 DataList 的可编辑接口之前,首先需要生成只读接口。 首先从 文件夹中打开CustomizedUI.aspx页面,然后从 Designer向页面添加 DataList,并将其 ID 属性设置为 ProductsEditDeleteDataList 在 DataList 的智能标记中,创建新的 ObjectDataSource。 将此新的 ObjectDataSource ProductsDataSource 命名为 ,并将其配置为从 ProductsBLL 类方法 GetProducts 检索数据。 与前面的可编辑 DataList 教程一样,我们将直接转到业务逻辑层来更新已编辑的产品信息。 相应地,将“更新”、“插入”和“删除”选项卡中的下拉列表设置为 (“无”) 。

将 UPDATE、INSERT 和 DELETE 选项卡 Drop-Down Lists 设置为 (None)

图 2:将“更新”、“插入”和“删除”选项卡 Drop-Down Lists 设置为“无 () (单击以查看全尺寸图像)

配置 ObjectDataSource 后,Visual Studio 将为 DataList 创建一个默认值 ItemTemplate ,其中列出了返回的每个数据字段的名称和值。 ItemTemplate修改 ,使模板列出元素中的<h4>产品名称以及类别名称、供应商名称、价格和停产状态。 此外,添加“编辑”按钮,确保其 CommandName 属性设置为“编辑”。 我的 ItemTemplate 声明性标记如下所示:

<ItemTemplate>
    <h4>
        <asp:Label ID="ProductNameLabel" runat="server"
            Text='<%# Eval("ProductName") %>' />
    </h4>
    <table border="0">
        <tr>
            <td class="ProductPropertyLabel">Category:</td>
            <td class="ProductPropertyValue">
                <asp:Label ID="CategoryNameLabel" runat="server"
                    Text='<%# Eval("CategoryName") %>' />
            </td>
            <td class="ProductPropertyLabel">Supplier:</td>
            <td class="ProductPropertyValue">
                <asp:Label ID="SupplierNameLabel" runat="server"
                    Text='<%# Eval("SupplierName") %>' />
            </td>
        </tr>
        <tr>
            <td class="ProductPropertyLabel">Discontinued:</td>
            <td class="ProductPropertyValue">
                <asp:Label ID="DiscontinuedLabel" runat="server"
                    Text='<%# Eval("Discontinued") %>' />
            </td>
            <td class="ProductPropertyLabel">Price:</td>
            <td class="ProductPropertyValue">
                <asp:Label ID="UnitPriceLabel" runat="server"
                    Text='<%# Eval("UnitPrice", "{0:C}") %>' />
            </td>
        </tr>
        <tr>
            <td colspan="4">
                <asp:Button runat="Server" ID="EditButton"
                    Text="Edit" CommandName="Edit" />
            </td>
        </tr>
    </table>
    <br />
</ItemTemplate>

上述标记使用 h4> 标题作为产品名称,使用<四列<table>表示其余字段的产品信息。 ProductPropertyLabel在 中Styles.css定义的 和 ProductPropertyValue CSS 类已在前面的教程中进行了讨论。 图 3 显示了通过浏览器查看时的进度。

将显示每个产品的名称、供应商、类别、停产状态和价格

图 3:显示每个产品的名称、供应商、类别、停产状态和价格 (单击以查看全尺寸图像)

步骤 2:将 Web 控件添加到编辑界面

生成自定义 DataList 编辑界面的第一步是将所需的 Web 控件添加到 EditItemTemplate。 具体而言,我们需要一个 DropDownList 用于类别,另一个用于供应商,以及用于停止使用状态的 CheckBox。 由于本示例中的产品价格不可编辑,因此我们可以继续使用标签 Web 控件显示它。

若要自定义编辑界面,请单击 DataList 智能标记中的“编辑模板”链接, EditItemTemplate 然后从下拉列表中选择选项。 将 DropDownList 添加到 , EditItemTemplate 并将其 ID 设置为 Categories

为类别添加 DropDownList

图 4:为类别添加 DropDownList (单击以查看全尺寸图像)

接下来,从 DropDownList 的智能标记中选择“选择数据源”选项,并创建名为 CategoriesDataSource的新 ObjectDataSource。 将此 ObjectDataSource 配置为使用 CategoriesBLLGetCategories() 方法, (请参阅图 5) 。 接下来,DropDownList 数据源配置向导会提示输入要用于每个 ListItemValue 属性的数据Text字段。 让 DropDownList 显示 CategoryName 数据字段并使用 CategoryID 作为值,如图 6 所示。

新建名为 CategoriesDataSource 的 ObjectDataSource

图 5:创建名为 CategoriesDataSource 的新对象DataSource (单击以查看全尺寸图像)

配置 DropDownList 的显示字段和值字段

图 6:配置 DropDownList 的显示字段和值字段 (单击以查看全尺寸图像)

重复这一系列步骤,为供应商创建 DropDownList。 ID将此 DropDownList 的 设置为 Suppliers ,并将其命名为 ObjectDataSource SuppliersDataSource

添加两个 DropDownList 后,为已停用状态添加 CheckBox,为产品名称添加 TextBox。 ID将 CheckBox 和 TextBox 的 分别设置为 DiscontinuedProductName。 添加 RequiredFieldValidator 以确保用户为产品名称提供值。

最后,添加“更新”和“取消”按钮。 请记住,对于这两个按钮,必须分别将其 CommandName 属性设置为“更新”和“取消”。

可以随意设置编辑界面的布局,只要你喜欢。 我已选择从只读界面使用相同的四列 <table> 布局,如以下声明性语法和屏幕截图所示:

<EditItemTemplate>
    <h4>
        <asp:Label ID="ProductNameLabel" runat="server"
            Text='<%# Eval("ProductName") %>' />
    </h4>
    <table border="0">
        <tr>
            <td class="ProductPropertyLabel">Name:</td>
            <td colspan="3" class="ProductPropertyValue">
                <asp:TextBox runat="server" ID="ProductName" Width="90%" />
                <asp:RequiredFieldValidator ID="RequiredFieldValidator1"
                    ControlToValidate="ProductName"
                    ErrorMessage="You must enter a name for the product."
                    runat="server">*</asp:RequiredFieldValidator>
            </td>
        </tr>
        <tr>
            <td class="ProductPropertyLabel">Category:</td>
            <td class="ProductPropertyValue">
                <asp:DropDownList ID="Categories" runat="server"
                    DataSourceID="CategoriesDataSource"
                    DataTextField="CategoryName" DataValueField="CategoryID" />
            </td>
            <td class="ProductPropertyLabel">Supplier:</td>
            <td class="ProductPropertyValue">
                <asp:DropDownList ID="Suppliers" DataTextField="CompanyName"
                    DataSourceID="SuppliersDataSource"
                    DataValueField="SupplierID" runat="server" />
            </td>
        </tr>
        <tr>
            <td class="ProductPropertyLabel">Discontinued:</td>
            <td class="ProductPropertyValue">
                <asp:CheckBox runat="server" id="Discontinued" />
            </td>
            <td class="ProductPropertyLabel">Price:</td>
            <td class="ProductPropertyValue">
                <asp:Label ID="UnitPriceLabel" runat="server"
                    Text='<%# Eval("UnitPrice", "{0:C}") %>' />
            </td>
        </tr>
        <tr>
            <td colspan="4">
                <asp:Button runat="Server" ID="UpdateButton" CommandName="Update"
                    Text="Update" />
                 
                <asp:Button runat="Server" ID="CancelButton" CommandName="Cancel"
                    Text="Cancel" CausesValidation="False" />
            </td>
        </tr>
    </table>
    <br />
    <asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
        OldValuesParameterFormatString="original_{0}" SelectMethod="GetCategories"
        TypeName="CategoriesBLL">
    </asp:ObjectDataSource>
    <asp:ObjectDataSource ID="SuppliersDataSource" runat="server"
        OldValuesParameterFormatString="original_{0}" SelectMethod="GetSuppliers"
        TypeName="SuppliersBLL">
    </asp:ObjectDataSource>
</EditItemTemplate>

编辑界面的布局类似于 Read-Only 接口

图 7:编辑界面的布局类似于 Read-Only 界面 (单击以查看全尺寸图像)

步骤 3:创建 EditCommand 和 CancelCommand 事件处理程序

目前,除了 从 ItemTemplate) UnitPriceLabel逐字复制的 之外, (中没有数据绑定语法EditItemTemplate。 我们将暂时添加数据绑定语法,但首先让我们为 DataList EditCommandCancelCommand 事件创建事件处理程序。 回想一下,事件处理程序的职责 EditCommand 是呈现单击了“编辑”按钮的 DataList 项的编辑界面,而 CancelCommand 作业是将 DataList 返回到其预编辑状态。

创建这两个事件处理程序,并让它们使用以下代码:

protected void Products_EditCommand(object source, DataListCommandEventArgs e)
{
    // Set the DataList's EditItemIndex property and rebind the data
    Products.EditItemIndex = e.Item.ItemIndex;
    Products.DataBind();
}
protected void Products_CancelCommand(object source, DataListCommandEventArgs e)
{
    // Return to DataList to its pre-editing state
    Products.EditItemIndex = -1;
    Products.DataBind();
}

完成这两个事件处理程序后,单击“编辑”按钮将显示编辑界面,单击“取消”按钮会将已编辑的项返回到只读模式。 图 8 显示了在为 Chef Anton s Gumbo Mix 单击“编辑”按钮之后的 DataList。 由于我们尚未向编辑界面添加任何数据绑定语法, ProductName TextBox 为空, Discontinued CheckBox 未选中,并且从 CategoriesSuppliers DropDownLists 中选择的第一个项目。

显示添加 EditCommand 和 CancelCommand 事件处理程序并选择“编辑”按钮后 DataList EditItemTemplate 的屏幕截图。

图 8:单击“编辑”按钮显示编辑界面 (单击以查看全尺寸图像)

步骤 4:将 DataBinding 语法添加到编辑接口

若要使编辑界面显示当前产品的 值,我们需要使用数据绑定语法将数据字段值分配给相应的 Web 控件值。 可以通过Designer应用数据绑定语法,方法是转到“编辑模板”屏幕,然后从 Web 控件智能标记中选择“编辑数据绑定”链接。 或者,可以直接将数据绑定语法添加到声明性标记。

将数据 ProductName 字段值分配给 ProductName TextBox 属性 Text ,将 CategoryIDSupplierID 数据字段值分配给 CategoriesSuppliers DropDownLists SelectedValue 属性,并将 Discontinued 数据字段值分配给 Discontinued CheckBox 属性 Checked 。 进行这些更改后,无论是通过Designer还是直接通过声明性标记,通过浏览器重新访问页面,然后单击 Chef Anton 的 Gumbo Mix 的“编辑”按钮。 如图 9 所示,数据绑定语法已将当前值添加到 TextBox、DropDownLists 和 CheckBox 中。

显示添加 DataBinding 语法并选择“编辑”按钮后 DataList EditItemTemplate 的屏幕截图。

图 9:单击“编辑”按钮显示编辑界面 (单击以查看全尺寸图像)

步骤 5:在 UpdateCommand 事件处理程序中保存用户更改

当用户编辑产品并单击“更新”按钮时,将发生回发并触发 DataList 事件 UpdateCommand 。 在事件处理程序中,我们需要从 和 与 BLL 接口的 EditItemTemplate Web 控件中读取值,以更新数据库中的产品。 正如我们在前面的教程中看到的, ProductID 可通过 集合访问 DataKeys 更新产品的 。 通过使用 以编程方式引用 Web 控件 FindControl("controlID")来访问用户输入的字段,如以下代码所示:

protected void Products_UpdateCommand(object source, DataListCommandEventArgs e)
{
    // Make sure the page is valid...
    if (!Page.IsValid)
        return;
    // Read in the ProductID from the DataKeys collection
    int productID = Convert.ToInt32(Products.DataKeys[e.Item.ItemIndex]);
    // Read in the product name and price values
    TextBox productName = (TextBox)e.Item.FindControl("ProductName");
    DropDownList categories = (DropDownList)e.Item.FindControl("Categories");
    DropDownList suppliers = (DropDownList)e.Item.FindControl("Suppliers");
    CheckBox discontinued = (CheckBox)e.Item.FindControl("Discontinued");
    string productNameValue = null;
    if (productName.Text.Trim().Length > 0)
        productNameValue = productName.Text.Trim();
    int categoryIDValue = Convert.ToInt32(categories.SelectedValue);
    int supplierIDValue = Convert.ToInt32(suppliers.SelectedValue);
    bool discontinuedValue = discontinued.Checked;
    // Call the ProductsBLL's UpdateProduct method...
    ProductsBLL productsAPI = new ProductsBLL();
    productsAPI.UpdateProduct(productNameValue, categoryIDValue, supplierIDValue,
                              discontinuedValue, productID);
    // Revert the DataList back to its pre-editing state
    Products.EditItemIndex = -1;
    Products.DataBind();
}

代码首先通过查询 Page.IsValid 属性来确保页面上的所有验证控件都有效。 如果 Page.IsValidTrue,则从集合中DataKeys读取已编辑的产品 ProductID 值,并且以编程方式引用 中的数据EditItemTemplate条目 Web 控件。 接下来,将这些 Web 控件中的值读入变量,然后传递到相应的 UpdateProduct 重载中。 更新数据后,DataList 将返回到其预编辑状态。

注意

我省略了 处理 BLL- 和 DAL-Level 异常教程中添加的异常 处理逻辑,以便使代码和此示例保持专注。 作为练习,请在完成本教程后添加此功能。

步骤 6:处理 NULL CategoryID 和 SupplierID 值

Northwind 数据库允许 NULLProductsCategoryIDSupplierID 列的值。 但是,我们的编辑界面当前不能容纳 NULL 值。 如果我们尝试编辑具有其 或 列的值的产品NULL,我们将得到一个ArgumentOutOfRangeException,其中包含类似于:“Categories”的 SelectedValue,因为它不存在于项列表中。SupplierIDCategoryID此外,目前无法将产品类别或供应商价值从非NULL值更改为NULL非值。

若要支持 NULL 类别和供应商 DropDownLists 的值,我们需要添加其他 ListItem。 我已选择使用 (None) 作为 Text 此值 ListItem,但你可以根据需要将其更改为其他 (,例如空字符串) 。 最后,请记住将 DropDownList 设置为 AppendDataBoundItemsTrue;如果忘记这样做,绑定到 DropDownList 的类别和供应商将覆盖静态添加 ListItem的 。

进行这些更改后,DataList 中的 EditItemTemplate DropDownLists 标记应如下所示:

<asp:DropDownList ID="Categories" DataSourceID="CategoriesDataSource"
    DataTextField="CategoryName" DataValueField="CategoryID" runat="server"
    SelectedValue='<%# Eval("CategoryID") %>' AppendDataBoundItems="True">
    <asp:ListItem Value=" Selected="True">(None)</asp:ListItem>
</asp:DropDownList>
...
<asp:DropDownList ID="Suppliers" DataSourceID="SuppliersDataSource"
    DataTextField="CompanyName" DataValueField="SupplierID" runat="server"
    SelectedValue='<%# Eval("SupplierID") %>' AppendDataBoundItems="True">
    <asp:ListItem Value=" Selected="True">(None)</asp:ListItem>
</asp:DropDownList>

注意

ListItem静态 可以通过Designer或直接通过声明性语法添加到 DropDownList。 添加 DropDownList 项以表示数据库 NULL 值时,请务必通过声明性语法添加 ListItem 。 如果在Designer中使用ListItem集合编辑器,则当分配空字符串时,生成的声明性语法将完全省略Value该设置,从而创建声明性标记,如:<asp:ListItem>(None)</asp:ListItem>。 虽然这看起来可能无害,但缺失 Value 会导致 DropDownList 在其位置使用 Text 属性值。 这意味着,如果 NULLListItem 选择此选项,则将尝试将值 (None) 分配给产品数据字段 (CategoryIDSupplierID,在本教程) ,这将导致异常。 通过显式设置 Value="",当选择 时NULLListItem,将向产品数据字段分配一个NULL值。

花点时间通过浏览器查看进度。 编辑产品时,请注意 Categories ,和 Suppliers DropDownLists 在 DropDownList 的开头都有一个“ (无) ”选项。

“类别”和“供应商”下拉列表列表包括“无 (”) 选项

图 10:和 CategoriesSuppliers DropDownLists 包括“无 (”) 选项 (单击以查看全尺寸图像)

若要将“ (无) ”选项保存为数据库 NULL 值,需要返回到 UpdateCommand 事件处理程序。 将 categoryIDValuesupplierIDValue 变量更改为可以为 null 的整数,并仅当 DropDownList s SelectedValue 不是空字符串时为其赋值Nothing

int? categoryIDValue = null;
if (!string.IsNullOrEmpty(categories.SelectedValue))
    categoryIDValue = Convert.ToInt32(categories.SelectedValue);
int? supplierIDValue = null;
if (!string.IsNullOrEmpty(suppliers.SelectedValue))
    supplierIDValue = Convert.ToInt32(suppliers.SelectedValue);

通过此更改,如果用户从任一Nothing下拉列表中选择了“无) (”选项(对应于NULL数据库值),则会将 UpdateProduct 值传递到 BLL 方法中。

总结

在本教程中,我们了解了如何创建更复杂的 DataList 编辑界面,该界面包括三个不同的输入 Web 控件一个 TextBox、两个 DropDownList 和一个 CheckBox 以及验证控件。 生成编辑界面时,无论使用何种 Web 控件,步骤都是相同的:首先将 Web 控件添加到 DataList; EditItemTemplate使用数据绑定语法为相应的数据字段值分配相应的 Web 控件属性;并在事件处理程序中 UpdateCommand 以编程方式访问 Web 控件及其相应属性, 将其值传递到 BLL。

创建编辑界面时,无论它只由 TextBox 或不同 Web 控件的集合组成,请务必正确处理数据库 NULL 值。 在考虑 NULL 时,必须不仅在编辑界面中正确显示现有 NULL 值,而且必须提供一种将值标记为 NULL的方法。 对于 DataLists 中的 DropDownLists,这通常意味着添加一个静态 ListItem ,其 Value 属性显式设置为空字符串 (Value="") ,并将一些代码添加到 UpdateCommand 事件处理程序,以确定是否 NULL``ListItem 选择了 。

编程快乐!

关于作者

斯科特·米切尔是七本 ASP/ASP.NET 书籍的作者和 4GuysFromRolla.com 的创始人,自 1998 年以来一直在使用 Microsoft Web 技术。 Scott 担任独立顾问、培训师和作家。 他的最新一本书是 山姆斯在 24 小时内 ASP.NET 2.0。 可以在 上mitchell@4GuysFromRolla.com联系他,也可以通过他的博客(可在 中找到http://ScottOnWriting.NET)。

特别感谢

本教程系列由许多有用的审阅者审阅。 本教程的主要审阅者是丹尼斯·帕特森、大卫·苏鲁和兰迪·施密特。 有兴趣查看我即将发布的 MSDN 文章? 如果是,请在 处mitchell@4GuysFromRolla.com放置一行。