使用 Client-Side 脚本

 

斯科特·米切尔
4GuysFromRolla.com

2004 年 8 月

摘要:虽然 ASP.NET 在服务器上执行大部分处理,但客户端处理可以更好地处理某些操作。 Scott Mitchell 展示了 ASP.NET 页和控件如何添加客户端代码。 ) (27 个打印页

下载本文的源代码

目录

简介
创建基类作为添加Client-Side脚本的基础
从 Code-Behind 类添加Client-Side脚本
执行Client-Side代码以响应用户操作
实现常见Client-Side功能
结论
相关书籍

简介

使用基于 Web 的动态脚本技术(如经典 ASP 或 PHP)时,开发人员必须敏锐地了解客户端和服务器之间的逻辑、临时和物理分离。 例如,对于触发服务器端代码执行的用户操作,使用经典 ASP 的开发人员必须显式导致用户的浏览器向 Web 服务器发出请求。 创建此类交互很容易消耗大量开发时间,并导致代码不可读。

Microsoft ASP.NET 通过使用Web Forms(模糊了客户端和服务器之间的界限)帮助减轻了将用户事件绑定到特定服务器端代码执行的负担。 开发人员可以使用 ASP.NET 和最少的工作量快速创建具有各种交互式用户界面元素(按钮、下拉列表等)的网页,根据最终用户的操作,这些元素可能会导致选择性服务器端代码运行。 例如,使用 ASP.NET 添加一个下拉列表,每当所选下拉列表项发生更改时执行某些操作,只需添加 DropDownList Web 控件,将其 AutoPostBack 属性设置为 True,并为下拉列表创建 SelectedIndexChanged 事件处理程序。 使用经典 ASP 完成此功能需要编写复杂的 HTML、客户端 JavaScript 和服务器端脚本代码;使用 ASP.NET,可为你提供必要的脚本代码和服务器端事件模型。

虽然 ASP.NET 中的Web Forms大大简化了执行客户端操作时服务器端脚本的运行,但如果滥用这种电源,可能会导致性能不可接受的。 虽然Web Forms隐藏所涉及的复杂性,但每次需要执行服务器端代码时,最终用户的浏览器都必须通过重新提交表单向 Web 服务器发出请求。 提交表单时,所有窗体字段(文本框、下拉列表、检查框等)也必须发送回其值。 此外,页面的视图状态将发送回 Web 服务器。 总共,每次发回网页时,可能需要将数 KB 的数据发送回 Web 服务器。 因此,频繁的回发会快速导致 Web 应用程序不可用,尤其是那些仍然卡在拨号上的用户。 通过将功能推送到客户端,可以减少对频繁回发的需求。

注意 SP.NET Web Forms发出标题为 VIEWSTATE 的隐藏窗体字段,其中包含 Web 窗体中 Web 控件已更改状态的 base-64 编码表示形式。 根据存在的 Web 控件,视图状态的范围可以从几十个字节到几十 KB 不等。 若要了解有关视图状态的详细信息,检查我的文章了解 ASP.NET 视图状态

使用经典 ASP 时,添加数据驱动的自定义客户端脚本非常简单,尽管不太可读。 例如,若要在经典 ASP 中显示加载基于某些 ID 字段的 URL 的弹出窗口,请使用 语法插入 ID 字段的值, <%=id%> 键入相应的客户端脚本。 ASP.NET 允许使用 Page 类中的一些各种方法创建此类数据驱动的客户端脚本。

本文介绍将客户端脚本添加到 ASP.NET 网页的技术。 顾名思义,客户端脚本是在访问者的浏览器中运行的脚本代码。 我们将了解如何完成常见的客户端任务,例如显示警报、确认框和弹出窗口。 (客户端脚本的main用法之一(表单字段验证)是 ASP.NET 的一个有争议的主题,因为验证器 Web 控件提供现成的客户端表单验证。) 本文的重点是用于注入客户端脚本的服务器端类、方法和技术;我们不会详细检查实际的客户端脚本, 因为该信息在 Web 上的许多其他文章和网站中进行了介绍。

创建基类作为添加Client-Side脚本的基础

经典 ASP 和 ASP.NET 之间的主要区别之一是每种技术的编程模型。 ASP 页面是每次访问页面时解释的原子过程脚本。 但是,ASP.NET 是一种完全面向对象的编程技术。 所有 ASP.NET 网页都是具有属性、方法和事件的类。 所有网页都直接或间接派生自 System.Web.UI 命名空间中的 Page 类;Page 类包含 ASP.NET 网页的基本功能。

面向对象的编程的概念之一是 继承。 继承允许创建一个新类来扩展另一个类的功能。 (如果类 B 继承类 A,则表示扩展类 A;类 A 是基类。) 使用代码隐藏模型创建 ASP.NET 网页时,可以清楚地看到代码隐藏类继承 了 Page 类:

Public Class WebForm1
    Inherits System.Web.UI.Page

   ...
End Class

通过让代码隐藏类继承 Page 类,它会自动接收 Page 类中固有的功能,例如 RequestResponseSessionViewState 对象,以及 InitLoadRender 等常见事件。 如本文所述,如果需要为所有 ASP.NET 网页提供一些常见功能,一种方法是创建派生自 Page 类的类,并具有其他方法和属性来实现这些所需的增强功能。 然后,若要使 ASP.NET 网页利用这些增强功能,只需更新页面代码隐藏类中的 Inherits 语句,以使用扩展 Page 类的类。

在本文中,我们将创建一个名为 ClientSidePage 的类,该类派生自 Page 类,并提供额外的方法来帮助执行常见的客户端任务。 通过让代码隐藏类继承 ClientSidePage 而不是 Page,添加脚本代码将像调用方法并传入几个参数一样简单。 具体而言,此类将包含用于:

  • 显示模式客户端对话框。
  • 在页面加载时将焦点设置为特定窗体字段。
  • 使用模式确认对话框确定用户是否要回发表单。
  • 显示弹出窗口。

在深入探讨 ClientSidePage 类之前,让我们首先了解 Page 类中用于将客户端脚本注入网页的相关方法。 介绍这些 Page 方法后,我们将跳转到使用 ClientSidePage 类扩展其功能,并了解如何将所有内容捆绑在一起并在 ASP.NET 网页中使用扩展类。

从 Code-Behind 类添加Client-Side脚本

所有 ASP.NET 网页都必须直接或间接派生自 System.Web.UI 命名空间中的 Page 类。 Page 类包含正常运行的网页所需的基本方法、属性和事件集。 类的许多方法中,有一些方法专为将客户端脚本注入呈现的 HTML 而设计。 这些方法是从代码隐藏类调用的,因此可用于发出数据驱动的客户端脚本。 以下用于发出客户端脚本的相关 Page 类方法。

基类派生自 System.Web.UI.Page 类,因此可以通过直接从代码隐藏类调用 来访问 Page 类的公共方法。

注意 若要访问 Page 类的方法,可以直接键入方法名称,或通过输入 MyBase 在 Microsoft Visual Studio .NET 中使用 IntelliSense。 (Microsoft Visual Basic .NET) ,这。 (C#) ,或 Page。 (C# 或 Visual Basic .NET) 。 如果使用 Visual Basic .NET 作为所选编程语言,请确保将 Visual Studio .NET 配置为不隐藏高级成员,否则看不到这些客户端脚本方法。 (若要显示高级成员,请转到 工具 |选项 |文本编辑器 |基本 和取消选中 “隐藏高级成员”。)

RegisterClientScriptBlock (密钥脚本)

RegisterClientScriptBlock 方法在 Web 窗体呈现的<表单>元素之后,在 Web 窗体中包含的任何 Web 控件之前添加一个客户端脚本块。 使用密钥输入参数可以指定与此脚本块关联的唯一键,而脚本参数包含要发出的完整脚本代码。 (此 脚本 参数应包括实际的 <脚本> 元素,以及客户端 JavaScript 或 Microsoft VBScript.)

通过 ASP.NET 网页的代码隐藏类发出客户端脚本时, 参数的值通常不是最重要的。 只需选择一个描述性键值即可。 通过已编译的自定义服务器控件注入客户端脚本代码时, 参数更相关。 在某些情况下,编译的控件需要一组客户端函数。 一个页面上的多个服务器控件实例可能能够共享这些常见的客户端脚本函数,因此这些函数只需针对整个页面发出一次,而不需要为每个控件实例发出一次。 例如,验证控件利用客户端代码来增强用户体验。 如果页面上有任何验证控件,则必须存在此客户端代码,但如果有多个验证控件,则所有控件都可以使用这组共享函数。

通过为脚本块提供一个密钥,生成一个利用一组常见客户端函数的控件的控件开发人员可以检查,以查看页面上的控件的另一个实例是否已添加所需的通用函数集。 如果是这样,则无需重新添加通用脚本。 若要检查是否已使用同一键添加脚本块,请使用 IsClientScriptBlockRegistered (key) 方法,该方法将返回一个布尔值,指示是否已注册具有相同密钥的脚本块。 请注意,无需先检查脚本块是否已注册即可添加脚本块。 如果尝试使用已注册的密钥添加脚本块,则添加的脚本块将被忽略,原始脚本块将仍然分配给该密钥。

注意IsClientScriptBlockRegistered 方法在两种情况下特别有用。 首先,添加类似但唯一的脚本块时,它很方便,需要确保为每个脚本块提供唯一键。 本文稍后将介绍的代码演示了“is registered”方法的实用工具。 第二个用途是在生成需要一些常见脚本的控件时,尤其是在脚本不是简单生成的时。 通过使用 IsClientScriptBlockRegistered 方法,可以确保每个页面加载仅生成一次页面上服务器控件的所有实例通用的脚本,而不是每个页面上的控件实例生成一次。

RegisterClientScriptBlock 方法可用于添加不依赖于 Web 窗体中存在的任何窗体字段的客户端脚本。 此方法的常见用途是显示客户端警报框。 例如,假设你有一个包含一些 TextBox Web 控件和保存按钮的网页。 TextBox 控件可能具有数据库中的特定值。 假设此页面允许用户修改这些值,并通过单击“保存”按钮提交更改。 单击“保存”时,将回发网页,并触发 Button 的 Click 事件。 可以为此事件创建一个服务器端事件处理程序,用于更新数据库。 若要让用户知道其更改已保存,你可能想要显示一个警告框,显示“你的更改已保存”。这可以通过将以下代码行添加到 Button 的 Click 事件处理程序来实现:

RegisterClientScriptBlock("showSaveMessage", _
  "<script language=""JavaScript""> _
      alert('Your changes have been saved.'); _
   </script>")

上述内容将在页面的 <窗体>中添加指定的脚本内容,但在窗体中的内容之前添加。 当页面在用户的浏览器中呈现时,他们将在页面加载时看到一个客户端警报框,如图 1 所示。

<form method="post" ...>
   <script language="JavaScript">
        alert('Your changes have been saved.');
      </script>
   ...
</form>

Aa479302.clientside_fig01 (en-us,MSDN.10) .gif

图 1. 客户端 JavaScript 的结果

注意 上述示例的一个潜在不良副作用是,警报框将在浏览器收到 <表单> 标记后立即显示。 浏览器将暂停呈现网页,直到用户单击警报框的“确定”按钮。 这意味着用户将看到白色浏览器页面,直到他们单击“确定”。 如果希望在显示警报框之前完全显示页面,则可以使用 RegisterStartupScript 方法将 JavaScript 插入到 form> 元素的<末尾,我们接下来将对此进行讨论。

RegisterStartupScript (密钥脚本)

RegisterStartupScript 方法与 RegisterClientScriptBlock 方法非常相似。 main区别在于发出客户端脚本的位置。 回想一下,使用 RegisterClientScriptBlock 时,脚本在表单>元素的<开始之后发出,但在窗体的内容之前发出。 另一方面,RegisterStartupScript 会将指定的脚本添加到窗体末尾的所有窗体字段之后。 使用 RegisterStartupScript 放置与呈现的 HTML 元素交互的客户端脚本。 (稍后我们将查看在页面加载时将焦点设置为窗体字段的示例;为此,你将使用 RegisterStartupScript 方法。)

RegisterClientScriptBlock 一样, RegisterStartupScript 添加的脚本块需要唯一的键值。 同样,此键值主要由自定义控件开发人员使用。 毫不奇怪,还有一个 IsStartupScriptRegistered (键) 方法,该方法返回一个布尔值,指示是否已注册具有指定 的脚本块。

注意 有关在创建自定义编译服务器控件时使用 RegisterStartupScriptRegisterClientScriptBlock 的详细信息,请阅读我的前面文章: 从 ASP.NET 服务器控件注入Client-Side脚本

RegisterArrayDeclaration (arrayNamearrayValue)

如果需要创建具有一些设置值的客户端 JavaScript Array 对象,请使用此方法向特定数组添加值。 例如,在 ASP.NET 网页中使用验证控件时,将生成一个 Array 对象 (Page_Validators) ,其中包含对页面上验证控件集的引用。 提交表单时,将枚举此数组以检查各种验证控件是否有效。

若要将值 1、2 和 3 添加到名为 FavoriteNumbers 的客户端 Array 对象,请使用以下服务器端代码:

RegisterArrayDeclaration("FavoriteNumbers", "1")
RegisterArrayDeclaration("FavoriteNumbers", "2")
RegisterArrayDeclaration("FavoriteNumbers", "3")

此代码将发出以下客户端脚本:

<script language="javascript">
<!--
   var FavoriteNumbers =  new Array(1, 2, 3);
      // -->
</script>

请注意,传入的每个数组值都必须是一个字符串;但是,呈现的客户端脚本将 Array 对象的值设置为字符串的内容。 也就是说,如果要创建字符串值为“Scott”和“Jisun”的 数组 ,可以使用:

RegisterArrayDeclaration("FavoriteFolks", "'Scott'")
RegisterArrayDeclaration("FavoriteFolks ", "'Jisun'")

请注意,第二个输入参数是包含 'Scott''Jisun'的字符串(由单个撇号分隔的文本)。 这会呈现以下客户端脚本:

<script language="javascript">
<!--
   var FavoriteFolks =  new Array('Scott', 'Jisun');
      // -->
</script>

RegisterHiddenField (hiddenFieldNamehiddenFieldValue)

在经典 ASP 中,通常需要将各种信息位从一个页面传递到另一个页面。 实现此目的的一种常见方法是使用隐藏的窗体域。 (隐藏的窗体域是未显示但值在表单提交上发送的窗体域。用于创建隐藏窗体域的语法为 <input type="hidden" name="name" value="value" />.) 由于页面中控件的状态会自动保留,因此在 ASP.NET 中通过自定义隐藏窗体域传递信息的需求大大减少。 但是,如果发现需要创建自定义隐藏窗体域,可以通过 RegisterHiddenField 方法执行此操作。

RegisterHiddenField 方法接受两个输入参数:隐藏字段的名称和值。 例如,若要创建名为 foo 和值栏的隐藏窗体字段,请使用以下代码:

RegisterHiddenField("foo", "bar")

这会在页面的窗体元素中添加隐藏的 <窗体> 字段,如下所示:

<form name="_ctl0" method="post" action="test.aspx" id="_ctl0">
<input type="hidden" name="foo" value="bar" />
   ...
</form>

了解如何呈现Client-Side元素

Page 类包含两个内部方法,这些内部方法负责呈现在上述方法中注册的客户端脚本:OnFormRenderOnFormPostRender。 (标记为 internal 的方法只能由同一程序集中的其他类调用。因此,不能从 ASP.NET Web 应用程序中的代码隐藏类调用 Page的内部 方法。) 这两种方法都是在 HtmlForm 类的 RenderChildren 方法中调用的。 System.Web.UI.HtmlControls 命名空间中的 HtmlForm 类表示 Web 窗体;也就是说,ASP.NET 网页中的服务器端窗体在页面<form runat="server">...</form>的实例化阶段作为 HtmlForm 类的实例加载。

由于 由 Page 类的多种方法注册的客户端脚本呈现在 OnFormRenderOnFormPostRender 方法中,并且由于这些方法仅由 HtmlForm 类调用,因此仅当网页包含 Web 窗体时,才会呈现通过这些方法以编程方式添加的客户端脚本。 也就是说,仅当 ASP.NET 网页包含服务器端表单 () 时,你通过上述任何讨论的方法以编程方式添加的任何脚本元素才会在页面的最终标记中 <form runat="server">...</form> 发出。

ASP.NET 网页上的 Web 窗体是通过首先添加起始窗体>元素来呈现的<。 之后,调用 Web 窗体的 RenderChildren 方法,其中包含三行代码:

Page.OnFormRender(...)
MyBase.RenderChildren(...)
Page.OnFormPostRender(...)

调用 Page 类的 OnFormRender 方法会添加以下标记:

  • 通过调用 RegisterHiddenField 添加的任何隐藏窗体字段。
  • 名为 __VIEWSTATE 的隐藏窗体字段中的 base-64 编码视图状态。
  • 通过调用 RegisterClientScriptBlock 添加的任何脚本阻止。

Web 窗体的 RenderChildren 方法中的第二行代码调用基类的 RenderChildren 方法,该方法呈现 Web 窗体中的内容。 呈现表单的所有内容后,调用 Page 类的 OnFormPostRender 方法,该方法添加以下客户端内容:

  • RegisterArrayDeclaration 方法添加的任何 Array 对象。
  • 通过调用 RegisterStartupScript 添加任何脚本块。

最后,Web 窗体的 RenderChildren 方法完成后,将呈现结束窗体标记 (</form>) 。 图 2 以图形方式说明了此呈现过程。

注意 图 2 假设你对 ASP.NET 页生命周期有些熟悉。 如果有兴趣了解有关页面生命周期的详细信息,请考虑阅读 了解 ASP.NET 视图状态,重点介绍标题为“ASP.NET 页面生命周期”的部分。

单击此处查看更大的图像。

图 2. ASP.NET 中的页面呈现

检查脚本块的呈现顺序

乍一看 Page 类的 register 方法,似乎在网页中呈现已注册元素的顺序与它们在代码中的添加顺序相对应。 也就是说,假设在 ASP.NET 网页的代码隐藏类中具有以下两行代码:

RegisterClientScriptBlock("showSaveMessage", _
  "<script language=""JavaScript"">var name='" & _
  someDataDrivenValue & "'; </script>")
RegisterClientScriptBlock("showSaveMessage", _
  "<script language=""JavaScript"">alert('Hello, ' + name + '!'); 
</script>")

如果你发现页面呈现了以下客户端脚本块, (假设 someDataDriveValue 的值为 Sam) ,则不会太惊讶:

<script language="JavaScript">var name='Sam';</script>
<script language="JavaScript">alert('Hello, ' + name + '!');</script>

访问此页面的用户将看到一个警告框,显示“你好,山姆!”

然后,根据此测试,可以假设脚本块在 HTML 页中发出的顺序始终是它们在服务器端代码中指定的顺序。 但是,这是一个错误的假设,并且可能导致页面中断。 例如,假设上面添加的脚本块以相反的顺序在 HTML 页面中发出。 然后,你将:

<script language="JavaScript">alert('Hello, ' + name + '!');</script>
<script language="JavaScript">var name='Sam';</script>

这将显示一个显示“Hello, !”的警报框,因为尚未为变量 名称 分配值。 显然,有时发出脚本块的顺序非常重要。

Page 类的 register 方法(RegisterClientScriptBlockRegisterStartupScriptRegisterArrayDeclarationRegisterHiddenFields)全部将提供的脚本内容写入内部 HybridDictionaryHybridDictionary 是在 System.Collections.Specialized 命名空间中找到的数据结构,旨在将项存储在字典中,其中字典中的项数未知。 对于较小的项集合, ListDictionary 是最有效的数据结构,但对于较大的字典, 哈希表 更有效。 HybridDictionary 拆分差异 - 它首先使用 ListDictionary 存储项。 添加 ListDictionary 的第九项后, HybridDictionary 将从使用 ListDictionary 切换到使用 哈希表

虽然此方法非常适合性能,但如果使用几个脚本块,其中脚本块的顺序很重要,则可能会造成破坏。 这是因为虽然 ListDictionary 维护元素的添加顺序, 但 Hashtable 不会。 因此,如果将八个或更少的项添加到任何一个特定寄存器方法中,则项将按添加的顺序发出。 但是,如果添加第九项,则脚本的发出顺序似乎是随机的。

注意ListDictionary 使用链接列表存储其元素,而 Hashtable 将其元素存储在数组中,其中内容按 sting 键的哈希值排序。 有关链接列表和哈希表的深入讨论远远超出了本文的讨论范围。 有关详细信息,包括对其性能的分析,请考虑阅读 数据结构的广泛检查,特别是 第 2 部分 和第 4 部分

如果计划使用特定寄存器方法添加的客户端元素可能超过 8 个,并且元素的显示顺序很重要,则可能需要查看 Peter Blum 的免费 RegisterScripts 库。 RegisterScript 可以更好地控制发出客户端元素的顺序,还提供无需手动添加 <脚本> 标记的选项,在将客户端脚本与 RegisterClientScriptBlockRegisterStartupScript 方法一起包括时必须添加这些标记。

执行Client-Side代码以响应用户操作

Page 类的 register 方法非常适合用于注入在页面加载时运行的客户端代码,但在许多情况下,我们希望运行代码以响应最终用户的操作。 例如,我们可能希望在用户单击按钮时显示确认对话框,或者在下拉列表的选定项发生更改时调用特定的客户端 JavaScript 函数。

HTML 元素具有各种客户端事件,你可以利用这些事件,并在事件触发时执行客户端代码。 所需的标记只是作为属性进入 HTML 元素的 标记。 例如,若要在单击按钮时显示警报框,可以执行以下操作:

<input type="button" 
  value="Click me to see an alert box!" 
  onclick="alert('Here it is!');" />

若要在客户端事件发生时运行客户端代码,可以将相应的属性添加到 HTML 元素。 对于 Web 控件,可以使用 Attributes 集合以编程方式添加客户端属性。 例如,假设你有一个 TextBox Web 控件,每当呈现的文本框获得焦点时,该控件就会突出显示为黄色。 为此,需要 TextBox Web 控件呈现的 HTML 如下所示:

<input type="text" 
  onfocus="this.style.backgroundColor='yellow';" 
  onblur="this.style.backgroundColor='white';" />

若要实现此标记,可以通过 Attributes 集合以编程方式设置 TextBox Web 控件的 onfocusonblur 客户端属性,如下所示:

TextBoxControl.Attributes("onfocus") = "this.style.backgroundColor='yellow';"
TextBoxControl.Attributes("onblur") = "this.style.backgroundColor='white';"

这种将客户端代码与客户端事件结合的技术通常用于提供丰富的交互式用户体验。 本文稍后将介绍如何使用此方法根据用户操作显示确认对话框。

实现常见Client-Side功能

现在,我们已经了解了向网页动态添加客户端脚本所涉及的 ASP.NET 方法,接下来让我们将注意力转向应用此知识。 本文的其余部分重点介绍常见的客户端任务,例如显示警报框、确认框、弹出窗口等。 具体而言,我们将创建一个类,其中包含一组方法,这些方法可用于 ASP.NET 项目,以便快速轻松地提供此类功能。

本文的代码下载中提供了我们将在整个本文剩余时间中检查的 Visual Basic .NET 代码。

显示警报框

常见的客户端要求是显示警报框。 警报框是客户端模式对话框,通常用于向最终用户提供一些重要的信息。 图 1 中提供了警报框的示例。 警报框通过客户端 JavaScript 警报 函数显示,该函数接受单个参数(要显示的消息)。 显示警报框相当简单明了:事实上,本文前面显示了一个示例。

为了让页面开发人员尽可能轻松地显示警报框,让我们创建一个名为 ClientSidePage 的类,其中包含一个名为 DisplayAlert 的方法, (消息) 。 此类将继承 Page 类。 想要使用这些客户端帮助程序方法的页面开发人员需要让其代码隐藏类继承此 ClientSidePage 类,而不是默认 的 Page 类。 以下代码显示此 ClientSidePage 类及其第一个方法 DisplayAlert

Public Class ClientSidePage
    Inherits System.Web.UI.Page

    Public Sub DisplayAlert(ByVal message As String)
        RegisterClientScriptBlock(Guid.NewGuid().ToString(), _
                         "<script language=""JavaScript"">" & GetAlertScript(message) & "</script>")
    End Sub

    Public Function GetAlertScript(ByVal message As String) As String
        Return "alert('" & message.Replace("'", "\'") & "');"
    End Function

End Class

请注意,此类派生自 System.Web.UI.Page 类。 DisplayAlert 方法仅使用 RegisterClientScriptBlock 方法在警报框中显示提供的消息。 由于此方法可由单个页面多次调用,因此每次调用都使用 GUID (Globally Unique Identifier) 作为其键。 传递给 警报 函数的字符串由撇号分隔, 消息 中的任何撇号都必须转义 (JavaScript 将撇号转义为 \') 。

若要在 ASP.NET Web 应用程序中使用此代码,需要向 ASP.NET 应用程序添加新类。 在 Visual Studio .NET 中,右键单击解决方案资源管理器中的 ASP.NET Web 应用程序项目名称,然后选择添加新类。 然后,将上述代码剪切并粘贴到 类中。 接下来,在要利用此代码的 ASP.NET 网页中,需要修改代码隐藏类,使其继承自 ClientSidePage 类,而不是从 Page 继承。 以下代码显示了派生自 ClientSidePage 并使用 DisplayAlert 方法的示例代码隐藏类。

Public Class WebForm1
    Inherits ClientSidePage

    Private Sub Page_Load(ByVal sender As System.Object, _
      ByVal e As System.EventArgs) _
      Handles MyBase.Load
        DisplayAlert("Hello, World!")
    End Sub

    ...
End Class

请注意,ClientSidePage 类不仅具有可生成完整客户端<脚本>元素的 DisplayAlert 方法,而且具有 GetAlertScript 方法(仅返回客户端脚本)不<具有脚本>标记。 如果想要根据某些客户端事件显示警报,则可以使用第二种方法。 例如,如果你想要在收到特定文本框焦点时显示警报,可以将以下代码添加到服务器端代码隐藏类:

TextBoxControlID.Attributes("onfocus") = GetAlertScript(message)

在页面加载时将焦点设置为窗体字段

你是否曾经注意到,在访问 Google 时,焦点会自动设置为搜索文本框? 这一小“功能”使搜索 Google 更加快速 - 访问 Google 时,无需花费第二到两个来移动鼠标并单击文本框。 相反,只需在加载页面时开始键入。 将焦点设置为窗体字段(无论是文本框、单选按钮、检查框还是下拉列表),只需几行客户端 JavaScript 代码。 让我们向 ClientSidePage 类添加一个方法,该方法将在页面加载时自动将焦点添加到指定的 Web 控件。 此方法需要发出如下所示的客户端脚本:

<script language="JavaScript">
  function CSP_focus(id) {
    var o = document.getElementById(id);
    if (o != null)
      o.focus();
  }
</script>

... Form fields ...
<input type="..." id="id of element to focus" ... />
... Form fields ...

<script language="JavaScript">
  CSP_focus(id of element to focus);
</script>

客户端函数 CSP_focus 接受字符串参数、要设置为焦点的表单字段 的 ID ,并从 DOM 检索 HTML 元素。 然后调用检索到的元素的 focus () 函数。 在网页底部,指定所有表单字段后,我们需要调用 CSP_focus 方法,该方法传入要设置焦点的窗体字段的 ID

以下方法 GiveFocus (Control) 使用 RegisterClientScriptBlockRegisterStartupScript 方法生成所需的客户端脚本。

Public Sub GiveFocus(ByVal c As Control)
    RegisterClientScriptBlock("CSP-focus-function", _
            "<script language=""JavaScript"">" & vbCrLf & _
            "function CSP_focus(id) {" & _
            "  var o = document.getElementById(id); " & _
            "if (o != null) o.focus(); " & _
            "}" & vbCrLf & _
            "</script>")

    RegisterStartupScript("CSP-focus", _
      "<script language=""JavaScript"">CSP_focus('" & _
       c.ClientID & "');</script>")
End Sub

若要从代码隐藏类继承 ClientSidePage 的 ASP.NET 网页中使用 GiveFocus 方法,只需在 Page_Load 事件处理程序中调用 GiveFocus,并传入应在页面加载时设置其焦点的 Web 控件。 例如,若要将焦点设置为 TextBox Web 控件 TextBoxControl,请使用以下 Page_Load 事件处理程序:

Private Sub Page_Load(ByVal sender As System.Object, _
  ByVal e As System.EventArgs) _
  Handles MyBase.Load
     GiveFocus(TextBoxControl)
End Sub

打开弹出窗口

虽然弹出窗口作为广告商的邪恶工具在互联网上获得了糟糕的说唱,但弹出窗口在许多 Web 应用程序中都得到了很好的使用。 例如,你可能想要一个页面,该页面显示 DataGrid 中的数据库项列表,其中包含用于编辑每个特定项的链接。 当用户选择编辑 DataGrid 时,您可能希望打开一个弹出窗口,而不是使用 DataGrid 的内联编辑功能,其中弹出窗口包含包含 DataGrid 的可编辑字段的文本框列表。 (你可能想要执行此操作的一个原因是,可能有非常大量的可编辑字段,但你只想在 DataGrid 中显示最相关的字段,从而消除了使用 DataGrid 的内置编辑功能的可能性。)

若要显示弹出窗口,请使用 JavaScript 函数 window.open () ,该函数采用许多可选输入参数,这三个德语参数为:

  • 要加载到弹出窗口中的 URL。
  • 弹出窗口的字符串名称。
  • 弹出窗口的功能,例如其高度和宽度,无论窗口大小是否可调整,等等。

对 window.open () 函数的全面讨论超出了本文的讨论范围;有关详细信息,请参阅技术文档

与用于显示警报框的方法一样, ClientSidePage 类包含两个用于显示弹出窗口的方法:一个用于呈现显示窗口的自包含 <脚本> 块,一个只返回 JavaScript 脚本本身。 除了用于打开弹出窗口的方法外,还有一组用于关闭当前窗口的方法。 (你可能希望根据某些客户端或服务器端事件以编程方式关闭弹出窗口。)

Public Sub DisplayPopup(ByVal url As String, ByVal options As String)
    RegisterStartupScript(Guid.NewGuid().ToString(), _
             "<script language=""JavaScript"">" & _
               GetPopupScript(url, options) & _
               "</script>")
End Sub

Public Function GetPopupScript(ByVal url As String, _
  ByVal options As String) As String
    Return "var w = window.open(""" & _
                     url & """, null, """ & options & """);"
End Function

Public Sub CloseWindow(Optional ByVal refreshParent As Boolean = False)
    RegisterClientScriptBlock("CSP-close-popup", _
      "<script language=""JavaScript"">" & _
       GetCloseWindowScript(refreshParent) & "</script>")
End Sub

Public Function GetCloseWindowScript(Optional _
  ByVal refreshParent As Boolean = False) As String
    Dim script As String
    If refreshParent Then
        script &= "window.opener.location.reload();"
    End If

    Return "self.close();"
End Function

在本文的代码下载中可以看到此代码在操作中的示例。 你将在此处找到一个示例网页,其中包含 一个 DataGrid ,其中列出了与 ASP.NET 网页位于同一目录中的文件。 此 DataGrid 有两列:一个 TemplateColumn,它显示超链接,单击该超链接时会打开一个弹出窗口,其中显示了所选文件的内容;和文件的名称 (请参阅图 3) 。

Aa479302.clientside_fig03 (en-us,MSDN.10) .gif

图 3. 带有弹出窗口的 DataGrid

DataGrid 的标记利用 GetPopupScript 方法,如下所示:

<asp:DataGrid id="dgFiles" runat="server" ...>
   <Columns>
      <asp:TemplateColumn HeaderText="View">
         <ItemTemplate>
            <a href='javascript:<%# 
             GetPopupScript("ViewFile.aspx?FileName=" & 
             DataBinder.Eval(Container.DataItem, "Name"), 
             "scrollbars=yes,resizable=yes,width=500,height=400") 
             %>'>
               View File</a>
         </ItemTemplate>
      </asp:TemplateColumn>
      <asp:BoundColumn DataField="Name" 
        HeaderText="Filename"></asp:BoundColumn>
   </Columns>
</asp:DataGrid>

ASP.NET 网页 ViewFile.aspx 将打开在 querystring 中指定其名称的文件,并显示其内容 (请参阅图 4) 。

Aa479302.clientside_fig04 (en-us,MSDN.10) .gif

图 4。 在弹出窗口中显示Web.config的内容

注意 弹出窗口最适用于 Intranet 应用程序,因为许多 Internet 用户使用某种弹出窗口阻止软件,例如 Google 工具栏。 事实上,使用 Microsoft Windows XP Service Pack 2 时,Microsoft Internet Explorer 将默认配置为阻止弹出窗口。 但是,当用户访问受信任的站点或本地 Intranet 区域中的站点时,弹出窗口仍有效。 有关 Windows XP Service Pack 2 中 Internet Explorer 的弹出窗口阻止功能的详细信息,请务必阅读 Microsoft Windows XP Service Pack 2 中的功能更改

在回发之前确认

本文前面部分介绍了如何显示客户端警报框,这是一个带有“确定”按钮的模式对话框。 JavaScript 提供了警报框更交互式的风格,称为确认对话框。 确认对话框使用 确认 (消息) 函数显示,其效果是显示包含 消息 输入参数指定的文本以及“确定”和“取消”按钮的对话框。 如果用户单击 “确定”,则确认 (消息) 函数返回 true;如果用户单击“取消”,则返回 false。

通常,确认对话框用于确保用户在提交表单之前想要继续。 单击 HTML 元素以提交表单 ((例如提交按钮) )时,如果该 HTML 元素触发返回 false 的客户端事件处理程序,则会取消表单提交。 通常,确认对话框在网页中使用,如下所示:

<form ...>
  <input type="submit" value="Click Me to Submit the Form"
       onclick="return confirm('Are you sure you want 
        to submit this form?');" />
</form>

当用户单击“单击我提交表单”按钮时,他们将看到一个确认对话框,询问他们是否确定要提交表单 (请参阅图 5) 。 如果用户单击“确定”, 则 confirm () 将返回 true,并且将提交表单。 但是,如果他们单击“取消”, 则 confirm () 将返回 false,表单提交将被取消。

Aa479302.clientside_fig05 (en-us,MSDN.10) .gif

图 5。 确认 JavaScript 的结果

假设你有一个 DataGrid ,其中包含一列标记为“删除”的按钮。单击此按钮后,窗体将回发,所选记录将被删除。 在这种情况下,可能需要对用户真正想要删除此记录的双检查。 此处是使用客户端确认对话框的绝佳位置。 可以使用一个对话框提示用户:“这将永久删除记录。 确定要继续吗?如果用户单击“确定”,表单将回发并删除记录;如果单击“取消”,则不会回发窗体,因此不会删除记录。

若要添加在按钮单击时显示确认对话框所需的客户端 JavaScript,只需使用 Attributes 集合添加客户端 onclick 事件处理程序。 具体而言,将 onclick 事件处理程序代码设置为: return confirm(message);。 若要为 DataGridButtonColumn 提供此类功能,需要在 DataGridItemCreatedItemDataBound 事件处理程序中以编程方式引用 ButtonLinkButton 控件,并在其中设置 onclick 属性。 有关详细信息,请参阅 http://aspnet.4guysfromrolla.com/articles/090402-1.aspx

使用 AutoPostBack DropDownLists 确认

虽然确认对话框通常在单击按钮时使用,但也可以在更改下拉列表时使用。 例如,你可能有一个网页,当特定的 DropDownList Web 控件发生更改时,该网页会自动回发。 (DropDownList Web 控件具有 AutoPostBack 属性,如果设置为 True,则每当 DropDownList 的选定项发生更改时,该属性都会使窗体回发。)

直观地认为,为 DropDownList 添加确认对话框与为 Button Web 控件添加此类对话框相同。 也就是说,只需将 DropDownList 的客户端 onchange 属性设置为如下所示的内容: return confirm(...);。 使用:

DropDownListID.Attributes("onchange") = "return confirm(...);"

遗憾的是,这无法按预期工作,因为 AutoPostBackDropDownListonchange 属性将设置为导致回发的 JavaScript,即调用客户端 __doPostBack 函数。 以编程方式自行设置 onchange 属性时,最终结果是呈现的客户端 onchange 事件处理程序具有代码和 对 __doPostBack的调用:

<select onchange="return confirm(...);__doPostBack(...);">
   ...
</select>

注意到这一点,我们真正想要做的是,如果确认返回 true,则调用 __doPostBack 函数,因为随后页面将发回。 可以通过将 Attributes 集合的 onchange 事件设置为 来实现此目的:if (confirm(...)),这将呈现以下标记,这就是我们要遵循的标记:

<select onchange="if (confirm(...)) __doPostBack(...);">
   ...
</select>

乍一看,这似乎具有所需的效果。 如果用户从下拉列表中选择了其他项,则会显示一个确认框。 如果用户单击“确定”,表单将回发;如果用户单击“取消”,则窗体的回发将停止。 不过,问题在于下拉列表会保留用户为启动下拉列表的 onchange 事件而选择的项。 例如,假设下拉列表加载时选择了项 x ,然后用户选择项 y。 这将触发下拉列表的客户端 onchange 事件,该事件将显示确认对话框。 现在,假设用户点击了“取消”,仍会在项 y 上选择下拉列表。 我们想要的是将所选内容恢复为项 x

为此,需要执行两项操作:

  1. 编写一个 JavaScript 函数,用于“记住”所选的下拉列表项。
  2. 在下拉列表的客户端 onchange 事件中,如果用户单击“取消”,则需要将下拉列表还原回“已记住”值。

步骤 1 需要为下拉列表创建一个全局脚本变量,以及一个在页面加载时运行的函数,该函数将记录下拉列表的值。 步骤 2 要求更改下拉列表的客户端 onchange 属性,如下所示:

if (!confirm(...)) resetDDLIndex(); else __doPostBack();

其中 ,resetDDLIndex 是一个 JavaScript 函数,它将下拉列表的选定值还原为“已记住”值。 此的客户端脚本需要如下所示:

<select id="ddlID" 
  onchange="if (!confirm(...)) resetDDLIndex(); else __doPostBack(...);">
   ...
</select>

<script language="JavaScript">
  var savedDDLID = document.getElementById("ddlID").value;

  function resetDDLIndex() {
     document.getElementById("ddlID").value = savedDDLID;
  }
</script>

通过在 ClientSidePage 类中创建帮助程序方法,可以轻松生成此必要的脚本。

Public Sub ConfirmOnChange(ByVal ddl As DropDownList, ByVal message As String)
    'Register the script block
    If Not IsStartupScriptRegistered("CSP-ddl-onchange") Then
        RegisterStartupScript("CSP-ddl-onchange", _
            "<script language=""JavaScript"">" & _
            "var CSP_savedDDLID = " & _
             document.getElementById('" & _
             ddl.ClientID & "').value;" & vbCrLf & _
            "function resetDDLIndex() {" & vbCrLf & _
            "   document.getElementById('" & " & _
            " ddl.ClientID & "').value = CSP_savedDDLID;" & 
            vbCrLf & _
            "}" & vbCrLf & _
            "</script>")
    End If

    ddl.Attributes("onchange") = _
    "if (!confirm('" & message.Replace("'", "\'") & _
    "')) resetDDLIndex(); else "
End Sub

若要使用此方法,只需为要在其所选项目更改时显示确认对话框的网页上的每个 AutoPostBackDropDownList 调用此方法。

确认何时退出而不保存

在我创建的每个数据驱动 Web 应用程序中,始终都有一些页面,用户可以编辑数据库中的特定信息位。 一个非常简单的示例可能是包含一系列 TextBoxDropDownList Web 控件的页面,这些控件中填充了数据库数据。 用户可以进行任何合适的更改,然后单击“保存”按钮将其更改保存到数据库。

创建这些页面时,通常使用两个 按钮 Web 控件结束页面:一个“保存”按钮和一个“取消”按钮。 “保存”按钮将所有更改保存回数据库,而“取消”按钮退出页面而不保留任何更改。 虽然两个按钮可能看起来是完美的设计,但有时当用户打算单击“保存”按钮时,会意外单击“取消”按钮,从而丢失对数据所做的任何更改。 若要防止这种情况发生,可以使用“取消”按钮上的确认框,该框仅在网页上的任何文本框或下拉列表已更改时才显示。 也就是说,如果用户对数据进行任何更改,然后单击“取消”,则确认框将提示他们查看是否确定自己希望在不保存的情况下存在。 (如果用户只单击“取消”而不更改任何数据,则不会显示此类确认框。)

这种用户体验可以通过一些客户端 JavaScript 来实现。 基本上,它需要一个 JavaScript 全局变量 isDirty,该变量最初为 false,但在任何表单字段的 onchange 事件触发时设置为 true。 还有一个 JavaScript 函数,如果 isDirty 为 true,则显示确认对话框。 取消按钮的 onclick 客户端事件处理程序连接起来,以便从此 JavaScript 函数返回结果。 以下 HTML 说明了此概念:

<script language="JavaScript">
var isDirty= false;
function checkForChange(msg) {
  if (isDirty) return confirm(msg); else return true;
}
</script>

Name: <input type="text" onchange="isDirty = true;" />

<input type="submit" name="btnSave" value="Save" id="btnSave" />
<input type="submit" name="btnCancel" value="Cancel" 
  id="btnCancel" 
  onclick="return checkForChange('You have made changes to the data 
    since last saving.  If you continue, you will lose these 
    changes.');" />

通过将此脚本的生成移动到 ClientSidePage 类,可以轻松生成此脚本。 具体而言,我们可以创建以下三种方法:

Protected Sub RegisterOnchangeScript()
    If Not IsClientScriptBlockRegistered("CSP-onchange-function") Then
        RegisterClientScriptBlock("CSP-onchange-function", _
          "<script language=""JavaScript"">" & _
                 "var isDirty= false;" & vbCrLf & _
                 "function CSP_checkForChange(msg) {" & vbCrLf & _
                 "  if (isDirty) return confirm(msg); " & _
                 "else return true;" & vbCrLf & _
                 "}" & vbCrLf & _
              "</script>")
    End If
End Sub

Public Sub MonitorChanges(ByVal c As WebControl)
    RegisterOnchangeScript()
    If TypeOf c Is CheckBox Or TypeOf c Is CheckBoxList _
      Or TypeOf c Is RadioButtonList Then
        c.Attributes("onclick") = "isDirty = true;"
    Else
        c.Attributes("onchange") = "isDirty = true;"
    End If
End Sub

Public Sub ConfirmOnExit(ByVal c As WebControl, ByVal message As String)
    RegisterOnchangeScript()
    c.Attributes("onclick") = _
      "return CSP_checkForChange('" & message.Replace("'", "\'") & 
      "');"
End Sub

若要创建显示此行为的网页,我们只需要使其服务器端代码隐藏类派生自 ClientSidePage,并在Page_Load事件处理程序中为每个需要客户端 onchange 事件的 Web 控件调用 MonitorChanges,并为每个 ButtonLinkButtonImageButton 调用 ConfirmOnExit单击后,如果用户已进行更改并退出页面,则应显示警告。

注意请注意,对于 CheckBox、CheckBoxListRadioButtonList Web 控件,MonitorChanges 方法使用 onclick 客户端事件而不是 onchange。 这是因为这些控件在检查框或一系列检查框或单选按钮周围环绕< span> 标记或表>。< 在对 Internet Explorer 的测试中,我发现 onchange 事件在应用于 <span><表>时,在单击检查框或单选按钮时没有被选取,而是引发了 onclick 事件。

图 6 显示了一个示例 ASP.NET 包含两个 TextBox Web 控件、 一个 DropDownList Web 控件和一个 CheckBox Web 控件的网页。 如下面的 Page_Load 事件处理程序所示,正在监视所有这些 Web 控件的更改。 “取消”按钮 btnCancel 已配置,以便在进行更改后单击该按钮时,将显示确认对话框。

Aa479302.clientside_fig06 (en-us,MSDN.10) .gif

图 6。 带有确认的对话框

Public Class ConfirmOnExit
    Inherits ClientSidePage

    Private Sub Page_Load(ByVal sender As System.Object, _
      ByVal e As System.EventArgs) _
      Handles MyBase.Load
        'Specify what controls to check for changes
        MonitorChanges(name)
        MonitorChanges(age)
        MonitorChanges(favColor)
        MonitorChanges(chkSilly)

        ConfirmOnExit(btnCancel, _
          "You have made changes to the data since last saving." & _
          "  If you continue, you will lose these changes.")
    End Sub
    
    ...
End Class

注意 客户端 onchange 事件在旧版 Netscape 中不起作用。 此外,Internet Explorer 5.0 还报告了 onchange 事件 (的问题,这些问题已在 Internet Explorer 5.01、SP 1) 中修复。

此外,此方法不适用于 AutoPostBack 设置为 True 的 DropDownList Web 控件,因为回发将重置 isDirty 的值。 此问题有几种解决方法,例如使用一个隐藏的窗体字段来指示是否脏回发表单数据开头。 我将实现此作为读者的练习。

创建Client-Side MessageBox 控件

虽然“确认”对话框是防止意外单击并可能减少发回 Web 服务器的次数的好方法,但在某些情况下,你可能希望显示确认对话框,并能够在服务器端确定他们单击了“确定”还是“取消”。 (回想一下,在确认对话框中,如果用户单击“取消”,则窗体不会发回。) 此外,JavaScript 中的警报和确认框的外观受到相当限制。 幸运的是,客户端 VBScript 通过其 MsgBox 函数提供更丰富的消息框体验。

在以前的项目中,我需要客户端模式消息框,无论单击什么按钮,该消息框都会导致回发。 作为响应,我构建了满足这些要求的自定义编译 ASP.NET 服务器控件。 此外,客户端消息框使用 VBScript 的 MsgBox 函数提供更丰富的消息框体验。

注意 VBScript 仅在 Microsoft 的 Internet Explorer 浏览器中用作客户端脚本语言。 考虑到这一点,如果访问的浏览器是 Internet Explorer,我的服务器控件仅使用 VBScript。 非 Internet Explorer 浏览器将发送 JavaScript。

对此自定义服务器控件的彻底讨论本身可以保证整篇文章,因此,让我们来看看如何在 ASP.NET 网页中使用 MessageBox 控件,而不是专注于控件的内部工作。 (本文的 download.ASP.NET )

若要在 ASP.NET 网页中使用 MessageBox 控件,请先将 MessageBox 控件添加到 Visual Studio .NET 工具箱。 这可以通过右键单击“工具箱”并选择“添加/删除工具箱中的项”,然后浏览到 MessageBox 程序集来实现。 若要将客户端消息框添加到网页,只需将其从工具箱拖动到Designer。 图 7 显示了 Visual Studio .NET Designer中的 MessageBox 控件。

单击此处查看更大的图像。

图 7。 显示模式消息框

MessageBox 类具有许多属性,你可以配置这些属性来调整消息框的外观:

  • 按钮. 指定显示哪些按钮。 选项在 ButtonOptions 枚举中定义,可以是:OkOnly、、OkCancelAbortRetryIgnoreYesNoCancelYesNoRetryCancel
  • DisplayWhenButtonClicked. 单击按钮 Web 控件时将显示客户端消息框的 ID 。 如果希望由于单击特定按钮而显示消息框,请使用此属性。
  • 图标. 消息框中显示的图标;选项在 IconOptions 枚举中定义。 合法值为: CriticalQuestionExclamationInformation
  • 提示. 消息框中显示的文本。
  • 标题. 消息框的标题。

MessageBox 控件添加到 ASP.NET 网页后,下一个挑战是由于某些客户端操作而显示它。 DisplayWhenButtonClicked 属性允许你在页面上指定按钮 Web 控件的 ID,单击该按钮时,该控件将导致显示消息框。 或者,可以通过调用客户端函数来显示消息框,mb_show (id) ,其中 IDMessageBox 控件的 ID

无论选择在消息框中显示的按钮配置是什么,单击任何按钮时,都会发生回发, 并且 MessageBox 控件的 Click 事件将触发。 只需双击Designer中的 MessageBox,即可为此事件创建事件处理程序。 事件处理程序的第二个输入参数的类型为 MessageBoxClickedEventArgs,其中包含 一个 ButtonClicked 属性,该属性返回有关用户单击的消息框按钮的信息。

在想要向用户快速显示模式对话框的情况下, MessageBox 控件非常有用,无论用户的选择如何,该对话框都会产生回发。 若要查看 MessageBox 控件的实际应用,检查源代码下载中的 MsgBoxDemo.aspx 页。

结论

本文首先介绍网页中客户端脚本的常见用法,然后介绍将客户端脚本注入 ASP.NET 网页的方法和技术。 正如我们所看到的, Page 类包含许多用于以编程方式从服务器端代码隐藏类插入客户端脚本块的方法。 这些方法也常从自定义的已编译服务器控件中使用,如上一篇文章中所述: 从 ASP.NET 服务器控件注入Client-Side脚本

除了添加脚本块外,客户端功能通常还必须绑定到由某些 HTML 元素引发的客户端事件。 若要通过服务器端代码隐藏类以编程方式指定 Web 控件的客户端事件处理程序,请使用 Attributes 集合,该集合可用作所有 Web 控件的属性。

本文的后半部分应用了上半部分介绍的主题,演示如何在 ASP.NET 网页中实现常见的客户端功能。 我们了解了如何扩展 Page 类,以便从代码隐藏类中轻松显示警报框、将页面加载的焦点设置为特定的 Web 控件、如何显示弹出窗口以及如何显示确认对话框。 我们还介绍了如何创建自定义服务器控件,该控件使用 VBScript 来提供更丰富的客户端消息框用户体验,无论单击的按钮如何,都会导致回发。

编程愉快!

特别感谢...

在将我的文章提交到我的 MSDN 编辑器之前,我让一些志愿者帮助校对文章,并提供有关文章内容、语法和方向的反馈。 本文评审过程的主要贡献者包括 马克西姆·卡波夫卡洛斯·桑托斯米兰·内戈万和卡尔·兰布雷希特。 如果你有兴趣加入不断增长的审阅者列表,请在 上给我添加一行 mitchell@4guysfromrolla.com

 

关于作者

Scott Mitchell 是五本书的作者,4GuysFromRolla.com 的创始人,在过去的五年里一直从事 Microsoft Web 技术工作。 Scott 担任独立顾问、培训师和作家。 可以在 上或通过他的博客联系 mitchell@4guysfromrolla.com 他,该博客可在 中找到 http://ScottOnWriting.NET

© Microsoft Corporation. 保留所有权利。