删除时添加客户端确认

本文档是 Visual C# 教程 (切换到 Visual Basic 教程)

在我们目前所创建的界面上,如果本想单击 Edit 按钮的用户在不经意间误点了 Delete 按钮后,便可能意外地删除数据。本教程中,我们将讲述如何加入一个客户端确认对话框,当用户单击了 Delete 按钮后它将弹出。

« 前一篇教程  |  下一篇教程 »

简介

在前几篇教程中我们已经学习了,为了提供新增、修改和删除功能,怎样利用我们应用程序的构架,以及ObjectDataSource 和 Web 数据控件。到目前为止我们所探讨过的删除界面,是由一个Delete 按钮构成;当单击它以后,将产生一个回传并调用ObjectDataSource 的 Delete() 方法。随后,Delete() 方法从业务逻辑层调用经配置的方法,然后再把这一指令下传到数据访问层,对数据库发出实际的DELETE 语句。

尽管这个用户界面能够让访问者通过 GridView 、DetailsView 或 FormView 控件删除记录,但当用户单击 Delete 按钮后,却没有任何一种确认手段。如果用户本想单击 Edit 时却误点了 Delete 按钮,则原本要修改的记录将被删除。为了防止这个问题,在本教程中我们将讨论如何增加一个客户端确认对话框,它在单击Delete 按钮后弹出。

JavaScript confirm(string) 函数将其字符串输入参数显示为一个模式对话框上的文本,这个对话框上带有两个按钮 :OK 和 Cancel ( 参见图 1 )。 confirm(string) 函数根据用户单击的按钮返回一个布尔值(如果用户单击 OK 则返回 true ,如果单击 Cancel 则返回 false )。

图1 :JavaScript confirm(string) 方法显示客户端模式对话框

在表单的提交过程中,如果从客户端 Event Handler 返回一个 false 值,则表单的提交被取消。应用这一特性,我们可以让Delete 按钮的客户端onclick Event Handler 返回调用confirm("Are you sure you want to delete this product?") 的值。 如果用户单击 Cancel ,confirm(string) 将返回 false , 从而取消表单提交,没有回传,则不会删除单击了 Delete 按钮的产品记录。然而,如果用户在确认对话框中单击了OK ,回传将继续,而产品记录被删除。有关本技巧的更多信息,请参阅使用 JavaScript 的 confirm() 方法控制表单提交

在使用模板时和使用 CommandField 时,加入必需的客户端脚本稍微有所不同。所以,在本教程中我们将对 FormView 和GridView 的例子都进行探讨。

注意:本教程中所讨论的客户端确认方法,假设用户使用的是支持 JavaScript 的浏览器,并且已经启用了 JavaScript 。如果某用户那里这两个假设的其中任何一个没有满足,则单击Delete 按钮将立刻激起一个回传(并不显示确认对话框)。

步骤1:创建一个支持删除功能的FormView

首先,添加一个 FormView 到EditInsertDelete 文件夹里的ConfirmationOnDelete.aspx 页面,并将它绑定到一个新的ObjectDataSource ,它通过 ProductsBLL 类的 GetProducts() 方法调出产品信息。并且设置 ObjectDataSource ,把 ProductsBLL 类的DeleteProduct(productID) 方法指定为使用ObjectDataSource 的Delete() 方法;确认 INSERT 和 UPDATE 标签的下拉框列表设为 (None) 。最后,在 FormView 的智能标记里勾选中 Enable Paging 复选框。

完成这几个步骤以后,新的 ObjectDataSource 的声明式标记如下:

<asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
    DeleteMethod="DeleteProduct" OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetProducts" TypeName="ProductsBLL">
    <DeleteParameters>
        <asp:Parameter Name="productID" Type="Int32" />
    </DeleteParameters>
</asp:ObjectDataSource>

因为在我们以前的例子中都没有使用并发优化,所以我们先清除ObjectDataSource 的 OldValuesParameterFormatString 属性。

由于它已经绑定到只支持删除功能的 ObjectDataSource 控件,FormView 的 ItemTemplate 中就只有 Delete 按钮,没有 New 和 Update 按钮。但是 FormView 的声明式标记中包含多余的 EditItemTemplate 和 InsertItemTemplate ,可以把它们删掉。用一点时间来修改ItemTemplate ,让它只显示一部分产品数据字段。我是这样配置ItemTemplate 的:把产品名称放在样式为 <h3> 的标题行中,下面列出它的供应商和类别名称(以及Delete 按钮)。

<asp:FormView ID="FormView1" AllowPaging="True" DataKeyNames="ProductID"
    DataSourceID="ObjectDataSource1" runat="server">
    <ItemTemplate>
        <h3><i><%# Eval("ProductName") %></i></h3>
        <b>Category:</b>
        <asp:Label ID="CategoryNameLabel" runat="server"
            Text='<%# Eval("CategoryName") %>'>
        </asp:Label><br />
        <b>Supplier:</b>
        <asp:Label ID="SupplierNameLabel" runat="server"
            Text='<%# Eval("SupplierName") %>'>
        </asp:Label><br />
        <asp:LinkButton ID="DeleteButton" runat="server" CausesValidation="False"
            CommandName="Delete" Text="Delete">
        </asp:LinkButton>
    </ItemTemplate>
</asp:FormView>

做了这些修改之后,我们便有了一个具备一定功能的网页,用户可以在产品序列中翻阅,一次停到一个产品上,简单地按下Delete 按钮就可以删除这个产品记录。图 2 显示了我们目前在浏览器窗口中看到的内容。

图2 :FormView 显示单个产品信息

步骤2:由 Delete 按钮的客户端 onclick 事件调用 confirm(string) 函数

建起 FormView 之后,最后一步就是配置 Delete 按钮,当用户按下它时便调用JavaScript confirm(string) 函数。向 Button 、 LinkButton 或 ImageButton 的客户端 onclick 事件中加入客户端脚本,可以通过设置OnClientClick 属性 来完成,这是 ASP.NET 2.0 的一个新功能。因为我们想要的是 confirm(string) 函数的返回值,就把这个属性设为:return confirm('Are you certain that you want to delete this product?'); 做完这个修改之后,Delete LinkButton 的声明式语句如下:

<asp:LinkButton ID="DeleteButton" runat="server" CausesValidation="False"
    CommandName="Delete" Text="Delete"
    OnClientClick="return confirm('Are you certain you want to delete this product?');">
</asp:LinkButton>

这样就可以了!图 3 就是屏幕上正在显示的确认提示。单击Delete 按钮激发确认对话框。如果用户单击了Cancel ,回传则取消,产品记录没有被删除。但如果用户单击了OK ,回传将继续进行,调用ObjectDataSource 的Delete() 方法,最终导致数据库中的记录被删除。

注意:放在confirm(string) JavaScript 函数中字符串,是用单引号(而不是用双引号)括起来的。在JavaScript 中,这两种符号都可以用作字符串的分隔符。我们在这里使用单引号,是为了让放到confirm(string) 中的字符串分隔符不与 OnClientClick 属性值中用的分隔符相混淆。

图3 :单击 Delete 按钮后显示确认对话框

步骤3 :设置 CommandField 中Delete 按钮的OnClientClick 属性

当在模板上操作 Button 、LinkButton 或 ImageButton 时,通过设置它们的 OnClientClick 属性,令其返回 JavaScript confirm(string) 函数的结果,就可以调出一个确认对话框。可是,CommandField (它把一个有 Delete 按钮的列加到GridView 或 DetailsView 中)却并没有 OnClientClick 属性可供直接设置。对此,我们必须在GridView 或 DetailsView 的适当的DataBound Event Handler 中用代码引用 Delete 按钮,把它的 OnClientClick 属性设在那里。

注意:当我们在适当的 DataBound Event Handler 中设置 Delete 按钮的OnClientClick 属性时,我们已经访问到绑定至当前记录的数据。因此,可以扩展我们的确认信息,使其包含这个特定记录的具体内容,例如"Are you sure you want to delete the Chai product?" 。在模板中使用 databinding 句法也可以进行这样的定制。

为了练习在 CommandField 中设置 Delete 按钮的OnClientClick 属性,让我们把一个 GridView 加到页面中。设置这个 GridView ,让它使用与FormView 相同的 ObjectDataSource 控件。还要限制 GridView 的 BoundFields ,让它只包括产品名称、类别和供应商。最后,在GridView 的智能标记中检查 Enable Deleting 复选框是否被勾选上。这样,就把一个CommandField 加到 GridView 的列集合中,它的 ShowDeleteButton 属性设为 true 。

在做完这些修改以后,GridView 的声明式语句如下:

<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
    DataKeyNames="ProductID" DataSourceID="ObjectDataSource1">
    <Columns>
        <asp:CommandField ShowDeleteButton="True" />
        <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" />
    </Columns>
</asp:GridView>

这个CommandField 包含一个 Delete LinkButton 实例,它能够从 GridView 的 RowDataBound 的 Event Handler 通过代码访问。一旦被引用,我们就可以相应地设置它的 OnClientClick 属性。创建 RowDataBound 事件的 Event Handler ,使用以下代码:

protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        // reference the Delete LinkButton
        LinkButton db = (LinkButton)e.Row.Cells[0].Controls[0];
        // Get information about the product bound to the row
        Northwind.ProductsRow product =
            (Northwind.ProductsRow) ((System.Data.DataRowView) e.Row.DataItem).Row;
        db.OnClientClick = string.Format(
            "return confirm('Are you certain you want to delete the {0} product?');",
            product.ProductName.Replace("'", @"\'"));
    }
}

这个Event Handler 工作于各个数据行(那些有 Delete 按钮的行),并通过代码操作 Delete 按钮。一般使用下列格式:

ButtonType obj = (ButtonType) e.Row.Cells[commandFieldIndex].Controls[controlIndex];

ButtonType 是 CommandField 使用的按钮类型――Button 、LinkButton 或 ImageButton 。默认情况下,CommandField 使用LinkButtons ,但这可以通过 CommandField 的 ButtonType 属性 定制。commandFieldIndex 是CommandField 在 GridView 的列集合里的序号,而 controlIndex 是 Delete 按钮在 CommandField 的控件集合里的序号。controlIndex 的值取决于该按钮在 CommandField 中相对于其他按钮的位置。例如,如果在CommandField 中只有一个按钮,即 Delete 按钮,则使用序号 0 。但如果在 Delete 按钮前面还有一个 Edit 按钮,则使用序号 2 。使用序号 2 是因为 CommandField 在 Delete 按钮之前添加了两个控件:Edit 按钮和 LiteralControl ,后者用来在 Edit 和 Delete 按钮之间加入一些空格。

在我们所用的特定例子中,CommandField 使用LinkButtons ,而且作为最靠左的一列,commandFieldIndex 的值为 0 。因为在这个 CommandField 中只有一个 Delete 按钮,没有其他按钮,controlIndex 的值也是 0 。

在操作了 CommandField 中的 Delete 按钮之后,下一步我们要获取绑定到GridView 当前行的产品信息。最后,我们设置 Delete 按钮的 OnClientClick 属性到相应的 JavaScript 字符串,使其包含产品名称。因为放到confirm(string) 函数中的 JavaScript 字符串是用单引号括起来的,我们必须避开产品名称中的任何单引号。特别是,产品名称中的任何单引号都用"\'" 替代。

完成这些修改以后,单击 GridView 中的 Delete 按钮,便弹出一个定制的确认对话框(参见图4 )。与FormView 中的确认对话框一样,如果用户单击 Cancel ,回传便取消了,从而防止了删除的真正执行。

注意:在 DetailsView 的 CommandField 中也可以应用这一技巧,方法是通过代码访问Delete 按钮。可是对于DetailsView ,必须为 DataBound 事件创建一个 Event Handler ,因为 DetailsView 没有RowDataBound 事件。

图4:单击GridView 的 Delete 按钮弹出一个定制的确认对话框

使用TemplateFields

CommandField 的一个缺点,是它的按钮必须通过序号来访问,而且结果对象必须转换为相应的按钮类型(Button 、LinkButton 或 ImageButton )。若使用“魔数”(magic numbers )或硬编码类型,可能存在不到运行时便难以发现的潜在问题。例如,如果您或是无论哪个编程人员,在未来某个时间向CommandField 中加入了一个新按钮(比如 Edit 按钮),或是改变了ButtonType 属性,现存的代码在编译中仍不会有错误,但访问网页时却可能出现异常或意外,取决于您怎样写的代码和做了什么改变。

替代的方法之一是把 GridView 和DetailsView 的 CommandFields 转换到 TemplateFields 。这将生成一个带 ItemTemplate 的 TemplateFields ,对 CommandField 中的每个按钮它有一个 LinkButton (或 Button 或 ImageButton )。这些按钮的 OnClientClick 属性是可以直接设置的,就像我们在 FormView 那里看到的一样,或者也可以在适当的DataBound Event Handler 中通过代码访问;使用以下格式:

ButtonType obj = (ButtonType) e.Row.FindControl("controlID");

其中,controlID 是该按钮的 ID 属性的值。虽然这种格式仍然需要用硬编码的方式做转换,但它不需要编排序号,允许布局的改变而不会导致在运行时出错。

小结

JavaScript confirm(string) 函数是用于控制表单提交工作流的常用工具。执行这个函数后,显示出一个客户端模式对话框,其中包含两个按钮(OK 和 Cancel )。如果用户单击 OK ,confirm(string) 函数返回 true ;单击 Cancel 则返回 false 。这个函数的功能,加上浏览器的以下特性:在表单提交时若某Event Handler 返回 false 则取消提交,可以用来在删除一条记录前显示确认对话框。

我们能够把 confirm(string) 函数与一个Web 按钮控件的客户端onclick Event Handler 联系起来,方法是通过该控件的OnClientClick 属性。当在一个模板中操作一个 Delete 按钮时,无论是 FormView 的模板或是 DetailsView 或GridView 中的 TemplateField ,这个属性都是可以设置的:或是直接设置,或是通过编程,正像我们在本教程中学到的。

快乐编程!





下一篇教程