﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-javaGrowing-随笔分类-asp.net</title><link>http://www.blogjava.net/juhongtao/category/6731.html</link><description /><language>zh-cn</language><lastBuildDate>Wed, 28 Feb 2007 07:45:23 GMT</lastBuildDate><pubDate>Wed, 28 Feb 2007 07:45:23 GMT</pubDate><ttl>60</ttl><item><title>从 ASP.NET 服务器控件插入客户端脚本</title><link>http://www.blogjava.net/juhongtao/archive/2006/01/05/26725.html</link><dc:creator>javaGrowing</dc:creator><author>javaGrowing</author><pubDate>Thu, 05 Jan 2006 07:14:00 GMT</pubDate><guid>http://www.blogjava.net/juhongtao/archive/2006/01/05/26725.html</guid><wfw:comment>http://www.blogjava.net/juhongtao/comments/26725.html</wfw:comment><comments>http://www.blogjava.net/juhongtao/archive/2006/01/05/26725.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/juhongtao/comments/commentRss/26725.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/juhongtao/services/trackbacks/26725.html</trackback:ping><description><![CDATA[<strong>从 ASP.NET 服务器控件插入客户端脚本</strong> 
<p>Scott Mitchell</p>
<p>2003 年 8 月</p>
<p>适用于：<br>&nbsp;&nbsp;&nbsp;&nbsp;Microsoft® ASP.NET</p>
<p><b>前提条件：</b>本文假设读者熟悉 ASP.NET。</p>
<p><b>难度：</b> 2</p>
<p><b>摘要：</b>尽管从技术角度讲，ASP.NET 服务器控件的所有功能都可以在服务器端执行，但通常情况下通过添加客户端脚本可以大大增强服务器控件的可用性。本文将探讨服务器控件发送客户端脚本的两种方法，还将构建两个使用这些技术的服务器控件：<code class="ce">PopupGreeting</code>，一个在首次加载的 Web 页面上显示带有特定消息的客户端模式对话框的服务器控件；<code class="ce">ConfirmButton</code>，一个增强的 Button Web 控件，如果用户点击此按钮，则在发回 Web 窗体前向用户显示一个 JavaScript <code class="ce">confirm()</code> 的对话框。（本文包含一些指向英文站点的链接。）</p>
<p>下载 <a href="http://download.microsoft.com/download/9/a/d/9add8674-137a-415f-9f86-b617402069e8/InjectingClientSideScript.msi">InjectingClientSideScript.msi</a>。</p>
<h4 class="dtH1">目录</h4>
<p><a href="http://www.microsoft.com/china/msdn/library/dnaspp/html/aspnet-injectclientsidesc.asp#aspnet-injectclientsidesc_topic1" target="_self">简介</a><br><a href="http://www.microsoft.com/china/msdn/library/dnaspp/html/aspnet-injectclientsidesc.asp#aspnet-injectclientsidesc_topic2" target="_self">使用 RegisterStartupScript() 和 RegisterClientScriptBlock() 添加客户端脚本块</a><br><a href="http://www.microsoft.com/china/msdn/library/dnaspp/html/aspnet-injectclientsidesc.asp#aspnet-injectclientsidesc_topic3" target="_self">探讨 IsStartupScriptRegistered() 和 IsClientScriptBlockRegistered()</a><br><a href="http://www.microsoft.com/china/msdn/library/dnaspp/html/aspnet-injectclientsidesc.asp#aspnet-injectclientsidesc_topic4" target="_self">从 ASP.NET 服务器控件发送客户端脚本块</a><br><a href="http://www.microsoft.com/china/msdn/library/dnaspp/html/aspnet-injectclientsidesc.asp#aspnet-injectclientsidesc_topic5" target="_self">发送 ASP.NET 服务器 Web 控件的 HTML 属性</a><br><a href="http://www.microsoft.com/china/msdn/library/dnaspp/html/aspnet-injectclientsidesc.asp#aspnet-injectclientsidesc_topic6" target="_self">小结</a></p><font class="105v"><a name="aspnet-injectclientsidesc_topic1"></a>简介</font> 
<p>尽管从技术角度讲，Microsoft® ASP.NET
服务器控件的所有功能都可以在服务器端执行，但通常情况下通过添加客户端脚本可以大大增强服务器控件的可用性。例如，ASP.NET 验证 Web
控件可以在服务器端执行所有的验证检查。但是，对于高版本浏览器，验证 Web
控件也会发送客户端脚本，以在客户端进行验证。这就是说，这些浏览器的用户可以获得响应效果更好的动态体验。</p>
<p>在开发 ASP.NET 服务器控件时，您不妨问问自己，如何才能通过使用客户端脚本来增强可用性。一旦找到可行的方案，其他要做的就是增强服务器控件的功能，以使其发送合适的客户端脚本。</p>
<p>ASP.NET 服务器控件可以发送两种客户端脚本：</p>
<ul><li>客户端脚本块 
</li><li>客户端 HTML 属性 </li></ul>
<p>客户端脚本块通常是用 JavaScript 编写的，其中通常包含在发生特定的客户端事件时执行的函数。客户端 HTML 属性提供将客户端事件与客户端脚本联系在一起的方法。例如，以下的 HTML 页面中包含了客户端脚本块，脚本块中包含了名为 <code>doClick()</code> 的函数。该页面同时还包含一个按钮（通过 <code>&lt;input&gt;</code> HTML 元素创建），这个按钮的 <code>onclick</code> 属性与 <code>doClick()</code> 函数绑定。也就是说，只要用户单击该按钮，就开始执行 <code>doClick()</code> 函数中的客户端代码。在本示例中，将显示一个弹出式对话框（图 1）。</p><pre class="code">&lt;html&gt;<br>  &lt;body&gt;<br>    &lt;form&gt;<br>      &lt;script language="JavaScript"&gt;<br>      &lt;!--<br>         function doClick() {<br>            alert("You clicked me!");<br>         }<br>      // --&gt;<br>      &lt;/script&gt;<br><br>      &lt;input type="button" onclick="doClick()" value="Click Me!" /&gt;<br>    &lt;/form&gt;<br>  &lt;/body&gt;<br>&lt;/html&gt;<br></pre>
<p>图 1 是单击“Click Me!”按钮时 HTML 页面的屏幕快照。</p>
<p class="fig"><img alt="" src="http://www.microsoft.com/china/msdn/library/dnaspp/images/aspnet-injectclientsidescript-01.gif" border="0"></p>
<p class="label"><b>图 1：单击“Click Me!”按钮时显示的弹出式对话框</b></p>
<p>对于以上 HTML 页面中的客户端脚本，有几点值得注意。首先，客户端脚本块包含在 HTML 注释（<code>&lt;!--</code> 和 <code>--&gt;</code>）中。之所以这样，是因为如果不将脚本块放入 HTML 注释中，那些不能识别脚本的旧式浏览器就会显示 <code>&lt;script&gt;</code> 块的内容。此外，还要注意，脚本块中 HTML 注释的结束标记前有一个 JavaScript 注释，即 <code>//</code>。这是因为旧版本的 Netscape 在遇到 <code>--&gt;</code> 时，会抛出 JavaScript 分析异常，因此必须将其注释掉。幸运的是，现代的浏览器已不需要这一额外操作，所以在为 Intranet 或其他由浏览器控制的环境开发 Web 页面时，您就不必采取此类预防措施了。</p>
<p>如果您对客户端脚本不是很熟悉，alert(<i>string</i>) 函数的作用就是显示一个模式弹出式对话框，对话框中包含的消息由 <i>string</i> 参数指定。所有 HTML 元素都有若干个可以绑定一段客户端 JavaScript 代码的客户端属性（例如，<code>onclick</code>、<code>onmouseover</code>、<code>onmouseout</code>、<code>onfocus</code> 和 <code>onblur</code> 等等）。例如，在上面的 HTML 页面中，<code>&lt;input&gt;</code> 元素的 <code>onclick</code> 属性绑定到 <code>doClick()</code> 函数，因此在单击该按钮时将执行 <code>doClick()</code> 函数。有关 JavaScript 事件及其关联的 HTML 属性的列表，请参阅 <a href="http://msdn.microsoft.com/asp.net/default.aspx?pull=/workshop/author/dhtml/dhtml.asp">Introduction to Dynamic HTML</a> 一文。有关客户端 JavaScript 的详细信息，请参阅 <a href="http://msdn.microsoft.com/asp.net/default.aspx?pull=/workshop/author/dhtml/dhtml_node_entry.asp">HTML and Dynamic HTML</a> 一文。</p>
<p>在本文中，我们将学习如何在 ASP.NET 服务器控件中发送客户端脚本块和 HTML 元素属性。我们首先讨论如何使用 <code>System.Web.UI.Page</code> 类中的两个方法来向 ASP.NET Web 页面添加客户端脚本块，这两个方法是 <code>RegisterStartupScript()</code> 和 <code>RegisterClientScriptBlock()</code>。
掌握这一知识后，我们将构建一个简单的服务器控件，让这个控件在每次加载页面时显示一个客户端弹出式对话框。之后，我们再来了解如何将 HTML
属性添加到 ASP.NET 服务器控件的 HTML 元素。最后，我们将归纳所有知识，实际构建一个 ConfirmButton Web
控件，当单击这个控件时，将向用户提示一个对话框，询问用户是否要继续。</p><font class="105v"><a name="aspnet-injectclientsidesc_topic2"></a>使用 RegisterStartupScript() 和 RegisterClientScriptBlock() 添加客户端脚本块</font> 
<p>System.Web.UI.Page 类包含的两个方法可以将客户端脚本代码发送到由 ASP.NET Web 页面提供的 HTML 中：</p>
<ul><li>RegisterStartupScript(<i>key</i>, <i>script</i>) 
</li><li>RegisterClientScriptBlock(<i>key</i>, <i>script</i>) </li></ul>
<p>这两个方法都接受两个字符串作为输入。第二个参数 <i>script</i> 是要插入到页面中的客户端脚本，包括 <code>&lt;script&gt;</code> 的起始标记和终止标记。第一个参数 <i>key</i> 是插入的客户端脚本的唯一标识符。</p>
<p>这两个方法唯一的不同之处在于从“何处”发送脚本块。<code>RegisterClientScriptBlock()</code> 在 Web 窗体的开始处（紧接着 <code>&lt;form runat="server"&gt;</code> 标识之后）发送脚本块，而 <code>RegisterStartupScript()</code> 在 Web 窗体的结尾处（在 <code>&lt;/form&gt;</code> 标识之前）发送脚本块。</p>
<p>为什么会有两种不同的方法来发送客户端脚本？要更好地了解这一点，我们必须首先了解，客户端脚本可以分为两类：一类是在加载页面后立即运行的代码，
一类是在发生某些客户端事件时才运行的代码。前者的常见示例是将焦点设置到文本框的客户端代码。例如，当您访问 Google
时，在页面加载后就会执行一小段客户端代码，以自动将焦点设置到搜索文本框。</p>
<p>以下是后一类代码（为响应客户端事件而运行的代码）的示例。具体而言，在该示例中，单击按钮时将显示一个弹出式对话框：</p><pre class="code">&lt;html&gt;<br>  &lt;body&gt;<br>    &lt;form&gt;<br>      &lt;script language="JavaScript"&gt;<br>      &lt;!--<br>        function displayPopup() {<br>            alert("Hello, world.");<br>        }<br>      // --&gt;<br>      &lt;/script&gt;<br><br>      &lt;input type="button" value="Click Me!" onclick="displayPopup()" /&gt;<br>    &lt;/form&gt;<br>  &lt;/body&gt;<br>&lt;/html&gt;<br></pre>
<p>在这段代码中，<code>&lt;input&gt;</code> 标记中的 <code>onclick="displayPopup()"</code> 用于指明在单击按钮时，JavaScript 函数 <code>displayPopup()</code> 应该运行。</p>
<p><code>RegisterStartupScript()</code>
方法可用于添加要在加载页面后运行的脚本块。通过这种方法添加的脚本块位于 Web 窗体的结尾处，因为必须在脚本运行前定义脚本要修改的 HTML
元素。也就是说，如果您要使用客户端脚本将焦点设置到文本框，必须确保文本框的 HTML 标记位于设置该文本框的焦点的脚本之前。例如，下面的
HTML 将显示一个文本框，并将焦点设置到该文本框：</p><pre class="code">&lt;input type="text" id="myTextBox" /&gt;<br><br>&lt;script language="JavaScript"&gt;<br>&lt;!--<br>   document.getElementById("myTextBox").focus();<br>// --&gt;<br>&lt;/script&gt;<br></pre>
<p>相反，以下 HTML 不会将焦点设置到文本框，因为文本框是在脚本块“之后”定义的：</p><pre class="code">&lt;script language="JavaScript"&gt;<br>&lt;!--<br>   document.getElementById("myTextBox").focus();<br>// --&gt;<br>&lt;/script&gt;<br><br>&lt;input type="text" id="myTextBox" /&gt;<br></pre>
<p>因此，<code>RegisterStartupScript()</code> 方法将 <code>&lt;script&gt;</code> 块置于 Web 窗体的结尾处，以保证在执行客户端脚本之前已声明 Web 窗体中的所有 HTML 元素。</p>
<p><code>RegisterClientScriptBlock()</code> 方法用于为响应客户端事件而执行的脚本代码。通过此方法发送的脚本块位于 Web 页面的开始处，因为这种方法不要求将脚本块置于所有 HTML 元素之后。</p><font class="105v"><a name="aspnet-injectclientsidesc_topic3"></a>探讨 IsStartupScriptRegistered() 和 IsClientScriptBlockRegistered()</font> 
<p>除 <code>RegisterStartupScript()</code> 和 <code>RegisterClientScriptBlock()</code> 方法之外，<code>Page</code> 类还包含两个在发送客户端脚本时常用的辅助方法：</p>
<ul><li>IsStartupScriptRegistered(<i>key</i>) 
</li><li>IsClientScriptBlockRegistered(<i>key</i>) </li></ul>
<p>如上所述，在使用 <code>RegisterStartupScript()</code> 或 <code>RegisterClientScriptBlock()</code> 插入客户端脚本块时，提供了一个唯一标识脚本块的关键字。这两个方法都接受一个输入（字符串 <i>key</i>），并返回一个布尔值，以指示带有指定关键字的脚本块是否已添加到页面中。具体地说，如果带有特定 <i>key</i> 的脚本块已经注册，这些方法将返回 True，否则将返回 False。</p>
<p>要了解如何使用这两个方法，可以看一看 ASP.NET 验证 Web 控件，如 RequiredFieldValidator、RegularExpressionValidator 等等。这些控件都会用到一个常用的验证 JavaScript 文件 (<code>WebValidation.js</code>)，该文件位于 ASP.NET Web 应用程序的 <code>aspnet_client/system_web/<i>版本号</i></code> 目录中。因此，所有这些控件都会发送相同的脚本块，这个脚本块将调用在 <code>WebValidation.js</code> 文件中定义的相应的 JavaScript 函数，以启动客户端的验证过程。要完成这个过程，这些控件会使用 <code>Page</code> 类的 <code>RegisterClientScriptBlock()</code> 方法，并使用关键字 <code>ValidatorIncludeScript</code>。</p>
<p>接下来要考虑的是，如果一个 ASP.NET Web 页面中包含多个验证 Web 控件，会出现什么情况呢？所有这些 Web 控件都要使用相同的关键字发送相同的脚本块。如果使用这个关键字调用两次 <code>RegisterClientScriptBlock()</code> 或 <code>RegisterStartupScript()</code> 方法，则第二次调用会被认为是复制脚本块而被忽略。因此，即使一个 Web 页面上有多个验证控件，也只是发送一个公共脚本块的实例。但是，请注意，除第一个控件之外的其他所有验证 Web 控件都会构建要发送的公共客户端脚本，而这只是在浪费时间。</p>
<p>这时就应该使用 <code>IsClientScriptBlock()</code> 和 <code>IsStartupScript()</code> 方法。这样一来，验证 Web 控件就不会先花时间构建要发送的客户端代码，而是先检查是否已经存在使用关键字 <code>ValidatorIncludeScript</code> 注册的脚本。如果存在，控件就会放弃构建客户端脚本块，因为脚本块已经由页面上的其他验证控件构建了。</p>
<p>因此，每次构建客户端脚本时，应该首先调用 <code>IsClientScriptBlock()</code> 或 <code>IsStartupScript()</code> 方法，以确定是否需要生成客户端脚本。在下面一节，我们将看到一些示例，在这些示例中，<code>IsClientScriptBlock()</code>、<code>IsStartupScript()</code> 方法先后与 <code>RegisterClientScriptBlock()</code> 和 <code>RegisterStartupScript()</code> 方法结合使用。</p><font class="105v"><a name="aspnet-injectclientsidesc_topic4"></a>从 ASP.NET 服务器控件发送客户端脚本块</font> 
<p>请记住，<code>RegisterStartupScript()</code> 和 <code>RegisterClientScriptBlock()</code> 方法是 <code>System.Web.UI.Page</code> 类的方法。幸运的是，可以容易地从 ASP.NET 服务器控件调用这两个方法，因为 <code>System.Web.UI.Control</code> 类（所有 ASP.NET 服务器控件都直接或间接地从这个类导出）有一个包含对 <code>Page</code> 实例的引用的 <code>Page</code> 属性，而这个 <code>Page</code> 实例包含服务器控件。因此，要从 ASP.NET 服务器控件添加客户端脚本块，您只需使用下面的语法：</p><pre class="code">this.Page.RegisterClientScriptBlock(<i>key</i>, <i>script</i>);</pre>
<p>通常，添加客户端脚本块这个任务会使用 <code>OnPreRender()</code> 方法来处理，这个方法在控件生命周期的预呈现阶段执行。</p>
<p>让我们创建一个只显示客户端弹出式对话框的 ASP.NET 服务器控件。此示例将说明构建一个发送客户端脚本的控件是很容易的。</p>
<p>首先，在 Microsoft® Visual Studio® .NET 中创建一个新的 Web Control Library（Web 控件库）项目。这将创建一个只有一个类的新项目，这个类从 <code>System.Web.UI.WebControls.WebControl</code> 导出。但是，我们希望这个类从 <code>System.Web.UI.Control</code> 类导出。为什么呢？因为 <code>WebControl</code> 类用于支持显示为 HTML 元素的服务器控件，而 <code>Control</code> 类则用于不会显示为 HTML 元素的服务器控件。</p>
<p>大多数内置的 ASP.NET 服务器控件都会发送一个 HTML 元素。例如，TextBox Web 控件发送一个 <code>&lt;input&gt;</code> 元素，其类型属性设置为 text；DataGrid Web 控件发送一个 <code>&lt;table&gt;</code> 元素，为每条要显示的记录发送 <code>&lt;tr&gt;</code> 元素，为每个字段发送 <code>&lt;td&gt;</code>
列。但是，不是所有的控件都需要发送 HTML 元素。例如，Literal 控件只是按原样输出它的 Text 属性，而不将这个属性放在 HTML
元素中。同样，Repeater 也不将其输出放在 HTML 元素中。那些显示为 HTML 元素的服务器控件，如
TextBox、Button、DataGrid 等等，是从 <code>System.Web.UI.WebControls.WebControl</code> 类导出的，而那些<b>不</b>产生 HTML 元素的控件，如 Literal、Repeater 等，是从 <code>System.Web.UI.Control</code> 类导出的。</p>
<p>既然我们要创建的服务器控件不可见（它只是发送一个显示弹出式控件的客户端脚本块），这个控件最好从 <code>System.Web.UI.Control</code> 导出，而不是从 <code>System.Web.UI.WebControls.WebControl</code> 导出。</p>
<p>这个控件只需要两个属性：</p>
<ul><li><code>PopupMessage</code> - 表示要在弹出式对话框中显示的消息的字符串。 
</li><li><code>Enabled</code> - 表示是否启用控件的布尔值。如果启用控件，则显示弹出式对话框，否则不显示。（必须添加一个 <code>Enabled</code> 属性，是因为导出该控件的 <code>Control</code> 类不包括 <code>Enabled</code> 属性，此属性只是隐含地由那些从 <code>WebControl</code> 导出的控件使用。）</li></ul>
<p>除了这两种属性之外，我们需要覆盖 <code>OnPreRender()</code> 方法。在这里，我们需要调用 <code>RegisterStartupScript()</code>，并传递控件唯一的关键字和恰当的客户端脚本以显示弹出式对话框。这个类的完整代码如下所示：</p><pre class="code">using System;<br>using System.Web.UI;<br>using System.Web.UI.WebControls;<br>using System.ComponentModel;<br><br>namespace ClientSideScript<br>{<br>   /// &lt;summary&gt;<br>   /// WebCustomControl1 的摘要描述。<br>   /// &lt;/summary&gt;<br>   [DefaultProperty("Text"),<br>      ToolboxData("&lt;{0}<img alt="吐舌笑脸" src="http://messenger.china.msn.com/Resource/emoticons/tongue_smile.gif">opupGreeting runat=server&gt;&lt;/{0}<img alt="吐舌笑脸" src="http://messenger.china.msn.com/Resource/emoticons/tongue_smile.gif">opupGreeting&gt;")]<br>   public class PopupGreeting : System.Web.UI.Control<br>   {<br>      [Bindable(true),<br>         Category("Appearance"),<br>         DefaultValue("")]<br>      public string PopupMessage<br>      {<br>         get<br>         {<br>            // 检查 ViewState 中是否存在该项目<br>            object popupMessage = this.ViewState["PopupMessage"];<br>            if (popupMessage != null)<br>               return this.ViewState["PopupMessage"].ToString();<br>            else<br>               return "Welcome to my Web site!";<br>         }<br><br>         set<br>         {<br>            // 指定 ViewState 变量<br>            ViewState["PopupMessage"] = value;<br>         }<br>      }<br><br>      [Bindable(true),<br>      Category("Appearance"),<br>      DefaultValue("")]<br>      public bool Enabled<br>      {<br>         get<br>         {<br>            // 检查 ViewState 中是否存在该项目<br>            object enabled = this.ViewState["Enabled"];<br>            if (enabled != null)<br>               return (bool) this.ViewState["Enabled"];<br>            else<br>               return true;<br>         }<br><br>         set<br>         {<br>            // 指定 ViewState 变量<br>            ViewState["Enabled"] = value;<br>         }<br>      }<br><br><br>      protected override void OnPreRender(EventArgs e)<br>      {<br>         base.OnPreRender<img alt="电子邮件" src="http://messenger.china.msn.com/Resource/emoticons/envelope.gif">;<br><br>string scriptKey = "intoPopupMessage:" + this.UniqueID;<br><br>         if (!Page.IsStartupScriptRegistered(scriptKey) &amp;&amp; this.Enabled &amp;&amp;<br>                      !Page.IsPostBack)<br>         {<br>            string scriptBlock = <br>               @"&lt;script language=""JavaScript""&gt;<br>               &lt;!--<br>                  alert(""%%POPUP_MESSAGE%%"");<br>               // --&gt;<br>               &lt;/script&gt;";<br>            scriptBlock = scriptBlock.Replace("%%POPUP_MESSAGE%%", this.PopupMessage);<br><br>            Page.RegisterStartupScript(scriptKey, scriptBlock);<br>         }<br>      }<br>   }<br>}<br></pre>
<p>请记住下面两件事：首先，<code>Enabled</code> 和 <code>PopupMessage</code> 属性保存在 <code>ViewState</code> 中，这样在回传时这些值可以始终保持一致； 其次，在 <code>OnPreRender()</code> 方法中，用于脚本块的关键字是文本 <code>intoPopupMessage:</code> 加上控件的 <code>UniqueID</code> 属性。如果使用一个硬编码的关键字，则当页面中有多个控件时，只有第一个控件能够注册其脚本块，因此只显示一个弹出式对话框。通过在脚本块关键字中使用 <code>UniqueID</code>，就能保证该控件的每个实例都能获取其脚本块。</p>
<p>在注册脚本块之前，代码首先检查三个条件：</p>
<ol><li>没有使用同一关键字注册的脚本。这当然是不可能的，因为每个控件实例都应该有一个 UniqueID 属性值。但是，不妨先练习使用 <code>IsStartupScriptRegistered()</code> 方法，然后再花时间创建和注册启动脚本。 
</li><li>控件的 Enabled 属性为 True。 
</li><li>页面没有被回传。这段代码只允许弹出式对话框在第一次加载页面时显示，而不是在每次回传页面时都显示。我们还可以增添更为灵活的功能，即为该控件添加一个布尔属性，以允许用户指定是否在回传时也生成弹出式对话框。</li></ol>
<p>如果满足这三个条件，则脚本被指定，并且 <code>PopupMessage</code> 属性值被插入到脚本中适当的位置。最后，调用 <code>Page</code> 属性的 <code>RegisterStartupScript()</code> 方法，传入关键字及脚本代码。</p>
<p>PopupGreeting 代码可以从本文结尾处提供的下载中获得。该下载包括名为 ClientSideControlsAndTester 的 Visual Studio .NET 解决方案，其中包含两个项目：</p>
<ul><li>ClientSideControls，包含 PopupGreeting 服务器控件 
</li><li>ClientSideTester，包括一个为测试 ClientSideControls 而设计的 ASP.NET Web 应用程序 </li></ul>
<p>ClientSideControls 项目编译后的程序集名为 <code>ClientSideControls.dll</code>。要在您自己的 ASP.NET Web 应用程序中使用 PopupGreeting 服务器控件，请将 <code>ClientSideControls.dll</code> 文件添加到您的 Web 应用程序的引用中。然后，在设计器中，右键单击 Toolbox（工具箱）并选择“Add/Remove Items&nbsp;.&nbsp;.&nbsp;.”（添加/删除项），再次选择 <code>ClientSideControls.dll</code> 文件。这样就向 Toolbox（工具箱）中添加了名为 PopupGreeting 的新项。然后，您可以从 Toolbox（工具箱）将该控件拖到设计器中。</p>
<p>图 2 显示了 PopupGreeting 控件添加到 Toolbox（工具箱）并添加到设计器后，Visual Studio .NET
的屏幕快照。Toolbox（工具箱）中的 PopupGreeting 控件用红色线圈出，设计器中的 PopupGreeting
输出用蓝色线圈出，在屏幕快照右侧的“Properties”（属性）窗格中可以查看 PopupGreeting 的属性。</p>
<p class="fig"><img alt="" src="http://www.microsoft.com/china/msdn/library/dnaspp/images/aspnet-injectclientsidescript-02.gif" border="0"></p>
<p class="label"><b>图 2：PopupGreeting 服务器控件已添加到 ASP.NET Web 窗体页面</b></p><font class="105v"><a name="aspnet-injectclientsidesc_topic5"></a>发送 ASP.NET 服务器 Web 控件的 HTML 属性</font> 
<p>如上所述，有两种方法可以通过服务器控件发送客户端脚本：</p>
<ul><li>通过使用客户端脚本块 
</li><li>通过 HTML 元素属性 </li></ul>
<p>在上一节中，我们探讨了如何使用 <code>Page</code> 类的 <code>RegisterStartupScript()</code> 和 <code>RegisterClientScriptBlock()</code> 方法向 ASP.NET Web 页面添加客户端脚本块。在最后这一节，我们了解如何将 HTML 元素属性添加到服务器控件的 HTML 元素。</p>
<p>在开始之前，请注意这种方法通常只适用于从 <code>System.Web.UI.WebControls.WebControl</code> 类导出的服务器控件，因为从这个类导出的控件会发送某些 HTML 元素。不发送 HTML 元素的服务器控件（如上一节中的 PopupGreeting 服务器控件），则不必写出 HTML 元素属性，因为这些控件运行时不会写出 HTML 元素。</p>
<p><code>WebControl</code> 类包含一个将 HTML 元素属性添加到由 Web 控件发出的 HTML 元素的方法。该方法称为 <code>AddAttributesToRender()</code>，它只有一个输入参数，即 <code>HtmlTextWriter</code> 的实例。要向 Web 控件添加 HTML 属性，您可以使用 <code>HtmlTextWriter</code> 的以下两个方法之一：</p>
<ul><li>AddAttribute() 
</li><li>AddStyleAttribute() </li></ul>
<p><code>AddAttribute()</code> 方法用于将 <code>title</code>、<code>class</code>、<code>style</code> 和 <code>onclick</code> 等 HTML 属性添加到 HTML 元素。<code>AddStyleAttribute()</code> 用于将样式设置添加到 HTML 元素，如 <code>background-color</code>、<code>color</code> 和 <code>font-size</code> 等。</p>
<p><code>AddAttribute()</code> 有几个重载窗体，但在代码中，我们将使用以下窗体：<code>AddAttribute(<i>HtmlTextWriterAttribute</i>, <i>value</i>)</code>。第一个参数，即 <b>HtmlTextWriterAttribute</b>，应该是 <code>HtmlTextWriterAttribute</code> 枚举的成员。该枚举包含像 <code>Align</code>、<code>Bgcolor</code>、<code>Class</code> 和 <code>Onclick</code> 等项。您可以在 .NET Framework Class Library，<a href="http://msdn.microsoft.com/asp.net/default.aspx?pull=/library/en-us/cpref/html/frlrfSystemWebUIHtmlTextWriterAttributeClassTopic.asp">HtmlTextWriterAttribute Enumeration</a> 中找到完整的列表。<i>value</i> 输入参数用于指定分配给特定 HTML 属性的值。最后，如果您想添加一个 <code>HtmlTextWriterAttribute</code> 枚举中未定义的 HTML 属性，可以使用 <code>AddAttribute()</code> 方法的替代形式 <code>AddAttribute(<i>attributeName</i>, <i>value</i>)</code>，其中的 <i>attributeName</i> 和 <i>value</i> 均为字符串。</p>
<p>为了运用该信息，我们创建一个作为确认按钮的服务器 Web
控件。确认按钮是一种提交按钮，当用户单击此按钮时，将显示一个弹出式对话框，询问用户是否确定要继续操作。用户可以单击“取消”，不提交窗体。此项功能
对用于删除信息的按钮特别有用，因为最终用户（或网站管理员）可能会在无意中单击鼠标删除数据库中的条目，如果没有机会取消，将是非常令人烦恼的事。</p>
<p>为了减少工作量，我们从 <code>System.Web.UI.WebControls.Button</code> 类中导出 ConfirmButton Web 控件，因为这个类本身已完成了涉及呈现提交按钮的所有繁重工作。在导出的类中，我们只需添加一个属性，这样用户可以指定确认消息，然后覆盖按钮的 <code>AddAttributesToRender()</code> 方法，并添加一个属性以处理客户端事件 <code>onclick</code>。</p>
<p>首先，在 Visual Studio .NET 中创建一个新的 Web Control Library（Web 控件库）项目，或者在
ClientSideControls 项目中添加一个新的 Web Custom Control（Web
自定义控件）。ConfirmButton 类的完整源代码如下所示：</p><pre class="code">using System;<br>using System.Web.UI;<br>using System.Web.UI.WebControls;<br>using System.ComponentModel;<br><br>namespace ClientSideControls<br>{<br>   /// &lt;summary&gt;<br>   /// ConfirmButton 的摘要描述。<br>   /// &lt;/summary&gt;<br>   [DefaultProperty("Text"), <br>      ToolboxData("&lt;{0}:ConfirmButton runat=server&gt;&lt;/{0}:ConfirmButton&gt;")]<br>   public class ConfirmButton : Button<br>   {<br>      [Bindable(true), <br>         Category("Appearance"), <br>         DefaultValue("")] <br>      public string PopupMessage<br>      {<br>         get<br>         {<br>            // 检查 ViewState 中是否存在该项目<br>            object popupMessage = this.ViewState["PopupMessage"];<br>            if (popupMessage != null)<br>               return this.ViewState["PopupMessage"].ToString();<br>            else<br>               return "Are you sure you want to continue?";<br>         }<br><br>         set<br>         {<br>            // 指定 ViewState 变量<br>            ViewState["PopupMessage"] = value;<br>         }<br>      }<br><br><br>      protected override void AddAttributesToRender(HtmlTextWriter writer)<br>      {<br>         base.AddAttributesToRender(writer);<br><br>         string script = @"return confirm(""%%POPUP_MESSAGE%%"");";<br>         script = script.Replace("%%POPUP_MESSAGE%%", <br>            this.PopupMessage.Replace("\"", "\\\""));<br><br>         writer.AddAttribute(HtmlTextWriterAttribute.Onclick, script);<br>      }<br>   }<br>}<br></pre>
<p>首先要注意的是，<code>ConfirmButton</code> 类是从 <code>Button</code> 类导出的。由于 Button 类已包含 Button Web 控件使用的所有属性和方法，因此我们所做的只是添加属性和方法，以在用户单击按钮时显示一个确认对话框。现在我们需要一个属性，即 <code>PopupMessage</code>，
它是要在确认弹出式对话框中显示的消息。默认情况下，这条消息是“Are you sure you want to
continue?”（您确定要继续吗？）如果使用 ConfirmButton 来确认删除，可能需要将该消息更改为“This action
will permanently delete the selected item. Are you sure you want to do
this?”（此操作将永久删除所选项。您确定要继续吗？）</p>
<p>我们只需覆盖一个方法，即 <code>AddAttributesToRender()</code>。在此方法中，我们只要构建当触发 <code>&lt;input&gt;</code> 元素的 <code>onclick</code> 事件时要执行的客户端 JavaScript，然后通过传入的 <code>HtmlTextWriter</code> 对象的 <code>AddAttribute()</code> 方法添加这段 JavaScript。关于这个方法，有一点要注意，必须将 <code>PopupMessage</code> 属性值中的所有双引号实例替换为转义双引号（即 <code>\"</code>）。另外还要注意，默认情况下，<code>AddAttribute()</code> 会对第二个参数中的字符进行 HTML 编码。也就是说，ASP.NET Web 页面中如果包含 <code>PopupMessage</code> 属性被设置为“Do you want to continue?”（要继续吗？）的 ConfirmButton，该页面将发送以下 HTML 标记：</p><pre class="code">&lt;input type="submit" name="ConfirmButton1" <br>value="Click Me!" id="ConfirmButton1" onclick="return confirm<br>(&amp;quot;Do you want to continue?&amp;quot<img alt="眨眼笑脸" src="http://messenger.msn.com/Resource/emoticons/wink_smile.gif">;" /&gt;<br></pre>
<p>如果您不熟悉 JavaScript 的 <code>confirm(<i>string</i>)</code> 函数，那么请您注意，该函数只接受一个字符串参数，并显示一个带有特定字符串的模式对话框。该对话框中包含两个按钮：“确定”和“取消”。如果单击“确定”，<code>confirm()</code> 函数返回 True，否则返回 False。请注意，<code>onclick</code> 事件将返回 <code>confirm()</code> 函数调用的结果。当通过单击提交按钮来提交表单时，如果提交按钮的 <code>onclick</code> 事件返回 False，则表单未被提交。因此，只有在用户确认后，可以使用 <code>confirm()</code> 函数提交表单。有关 <code>confirm()</code> 的详细信息，请参阅 ASP Warrior 网站中的 <a href="http://www.shiningstar.net/articles/articles/javascript/confirmsubmit.asp?ID=AW">Javascript Confirm Form Submission</a>。</p>
<p class="fig"><img alt="" src="http://www.microsoft.com/china/msdn/library/dnaspp/images/aspnet-injectclientsidescript-03.gif" border="0"></p>
<p class="label"><b>图 3：操作中的 ConfirmButton</b></p>
<p>ConfirmButton 在按钮的 <code>onclick</code> 事件处理程序中使用了内嵌的 JavaScript，还可以在 ConfirmButton 的 <code>OnPreRender()</code> 方法的客户端脚本块中创建一个函数，然后调整 <code>onclick</code> 属性以调用该函数。</p><font class="105v"><a name="aspnet-injectclientsidesc_topic6"></a>小结</font> 
<p>在本文中，我们探讨了两种通过 ASP.NET 服务器控件插入客户端脚本的方法。第一种方法是使用 Page 类的 <code>RegisterStartupScript()</code> 和 <code>RegisterClientScriptBlock()</code> 方法插入客户端脚本块。第二种方法是向 HTML 元素的属性添加客户端脚本。后者通过覆盖 Web 服务器控件的 <code>AddAttributesToRender()</code> 方法，并使用 <code>HtmlTextWriter</code> 的 <code>AddAttribute()</code> 方法来完成。</p>
<p>我们还在文中介绍了两个简单的服务器控件，它们都利用了客户端脚本来改进其功能。PopupGreeting 控件在页面首次加载时显示一个模式弹出式对话框，ConfirmButton Web 控件在用户单击按钮提交表单时，提示用户进行确认。</p>
<p>您可以在自己的服务器控件中插入客户端脚本，这将显著改善用户体验。本文提供的两个服务器控件相对比较简单，在可用性和独创性上没有什么突出之处。<a href="http://www.metabuilders.com/">MetaBuilders.com</a>
中展示了很多利用从 ASP.NET 服务器控件中插入客户端脚本而实现的功能，这些功能会给您留下深刻印象。在
MetaBuilders.com，您可以找到一些服务器控件，它们有的可以自动将焦点添加到文本框，有的可以在两个下拉列表之间传递条目，有的可以向下
拉列表中添加或删除条目，还有的可以在一系列下拉列表中显示父子关系的数据，等等。最大的好处是，这些控件是免费的，并包括完整的源代码。</p>
<p>祝大家编程快乐！</p>
<h4 class="dtH1">作者简介</h4>
<p>Scott Mitchell 著有五本关于 ASP/ASP.NET 的书籍，是 4GuysFromRolla.com
网站的创始人，过去五年来一直从事 Microsoft Web 技术方面的研究。Scott 是 ASP 和 ASP.NET
社区非常活跃的一名成员，十分热爱 ASP 和 ASP.NET 技术，并非常愿意帮助其他人了解这些令人振奋的技术。有关
DataGrid、DataList 和 Repeater 控件的详细信息，请参阅 Scott 的著作《ASP.NET Data Web
Controls Kick Start》（ISBN 为 0672325012）。</p>
<h4 class="dtH1">推荐链接：</h4>
<ul type="disc"><li><a href="http://www.metabuilders.com/">MetaBuilders.com</a> 客户端 Web 控件 
</li><li>.NET Framework Class Library，<a href="http://msdn.microsoft.com/asp.net/default.aspx?pull=/library/en-us/cpref/html/frlrfSystemWebUIPageClassRegisterClientScriptBlockTopic.asp">Page.RegisterClientScriptBlock Method Technical Documentation</a> 
</li><li>.NET Framework Class Library，<a href="http://msdn.microsoft.com/asp.net/default.aspx?pull=/library/en-us/cpref/html/frlrfSystemWebUIPageClassRegisterStartupScriptTopic.asp">Page.RegisterStartupScript Method Technical Documentation</a> 
</li><li>Visual Studio Magazine，<a href="http://www.fawcette.com/vsm/2002_07/magazine/columns/aspnet/default_pf.asp">Implement Client Scripts</a> 
</li><li>ASP.NET Forums 的 <a href="http://asp.net/Forums/ShowForum.aspx?ForumID=19">Building Controls Forum</a> </li></ul><img src ="http://www.blogjava.net/juhongtao/aggbug/26725.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/juhongtao/" target="_blank">javaGrowing</a> 2006-01-05 15:14 <a href="http://www.blogjava.net/juhongtao/archive/2006/01/05/26725.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ASP.NET Web 页面生命中的一天</title><link>http://www.blogjava.net/juhongtao/archive/2006/01/05/26722.html</link><dc:creator>javaGrowing</dc:creator><author>javaGrowing</author><pubDate>Thu, 05 Jan 2006 07:03:00 GMT</pubDate><guid>http://www.blogjava.net/juhongtao/archive/2006/01/05/26722.html</guid><wfw:comment>http://www.blogjava.net/juhongtao/comments/26722.html</wfw:comment><comments>http://www.blogjava.net/juhongtao/archive/2006/01/05/26722.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/juhongtao/comments/commentRss/26722.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/juhongtao/services/trackbacks/26722.html</trackback:ping><description><![CDATA[<p>Dino Esposito<br>Wintellect</p>
<p>2003 年 8 月</p>
<p>适用于：<br>&nbsp;&nbsp;&nbsp;&nbsp;Microsoft® ASP.NET</p>
<p><b>摘要</b>：了解为 ASP.NET Web 页面建立的事件模型，以及 Web 页面转变为 HTML
过程中的各个阶段。ASP.NET HTTP 运行时负责管理对象管道，这些对象首先将请求的 URL 转换成 Page
类的具体实例，然后再将这些实例转换成纯 HTML
文本。本文将探讨那些作为页面生命周期标志的事件，以及控件和页面编写者如何干预并改变标准行为。（本文包含一些指向英文站点的链接。）</p>
<h4 class="dtH1">目录</h4>
<p><a href="http://www.microsoft.com/china/msdn/library/dnaspp/html/aspnet-pageobjectmodel.asp#aspnet-pageobjectmodel_topic1" target="_self">简介</a> <br><a href="http://www.microsoft.com/china/msdn/library/dnaspp/html/aspnet-pageobjectmodel.asp#aspnet-pageobjectmodel_topic2" target="_self">真正的 Page 类</a> <br><a href="http://www.microsoft.com/china/msdn/library/dnaspp/html/aspnet-pageobjectmodel.asp#aspnet-pageobjectmodel_topic3" target="_self">页面的生命周期</a> <br><a href="http://www.microsoft.com/china/msdn/library/dnaspp/html/aspnet-pageobjectmodel.asp#aspnet-pageobjectmodel_topic4" target="_self">执行的各个阶段</a> <br><a href="http://www.microsoft.com/china/msdn/library/dnaspp/html/aspnet-pageobjectmodel.asp#aspnet-pageobjectmodel_topic5" target="_self">小结</a></p><font class="105v"><a name="aspnet-pageobjectmodel_topic1"></a>简介</font> 
<p>对由 Microsoft® Internet 信息服务 (IIS) 处理的 Microsoft® ASP.NET
页面的每个请求都会被移交到 ASP.NET HTTP 管道。HTTP 管道由一系列托管对象组成，这些托管对象按顺序处理请求，并将 URL
转换为纯 HTML 文本。HTTP 管道的入口是 <b>HttpRuntime</b> 类。ASP.NET 结构为辅助进程中的每个 AppDomain 创建一个此类的实例。（请注意，辅助进程为每个当前正在运行的 ASP.NET 应用程序维护一个特定的 AppDomain。）</p>
<p><b>HttpRuntime</b> 类从内部池中获取 <b>HttpApplication</b> 对象，并安排此对象来处理请求。HTTP 应用程序管理器完成的主要任务就是找到将真正处理请求的类。当请求 .aspx 资源时，处理程序就是页面处理程序，即从 <b>Page</b> 继承的类的实例。资源类型和处理程序类型之间的关联关系存储在应用程序的配置文件中。更确切地说，默认的映射集是在 machine.config 文件的 <b>&lt;httpHandlers&gt;</b> 部分定义的。但是，应用程序可以在本地的 web.config 文件中自定义自己的 HTTP 处理程序列表。以下这一行代码就是用来为 .aspx 资源定义 HTTP 处理程序的。</p><pre class="code">&lt;add verb="*" path="*.aspx" type="System.Web.UI.PageHandlerFactory"/&gt;<br></pre>
<p>扩展名可以与处理程序类相关联，并且更多是与处理程序工厂类相关联。在所有情况下，负责处理请求的 <b>HttpApplication</b> 对象都会获得一个实现 <b>IHttpHandler</b> 接口的对象。如果根据 HTTP 处理程序来解析关联的资源/类，则返回的类将直接实现接口。如果资源被绑定到处理程序工厂，则还需要额外的步骤。处理程序工厂类实现 <b>IHttpHandlerFactory</b> 接口，此接口的 <b>GetHandler</b> 方法将返回一个基于 IHttpHandler 的对象。</p>
<p>HTTP 运行时是如何结束这个循环并处理页面请求的？<b>ProcessRequest</b> 方法在 <b>IHttpHandler</b> 接口中非常重要。通过对代表被请求页面的对象调用此方法，ASP.NET 结构会启动将生成浏览器输出的进程。</p><font class="105v"><a name="aspnet-pageobjectmodel_topic2"></a>真正的 Page 类</font> 
<p>特定页面的 HTTP 处理程序类型取决于 URL。首次调用 URL 时，将构建一个新的类，这个类被动态编译为一个程序集。检查 .aspx
资源的分析进程的结果是类的源代码。该类被定义为命名空间 ASP 的组成部分，并且被赋予了一个模拟原始 URL 的名称。例如，如果 URL
的终点是 page.aspx，则类的名称就是 <b>ASP.Page_aspx</b>。不过，类的名称可以通过编程方式来控制，方法是在 <b>@Page</b> 指令中设置 <b>ClassName</b> 属性。</p>
<p>HTTP 处理程序的基类是 <b>Page</b>。这个类定义了由所有页面处理程序共享的方法和属性的最小集合。<b>Page</b> 类实现 <b>IHttpHandler</b> 接口。</p>
<p>在很多情况下，实际处理程序的基类并不是 <b>Page</b>，而是其他的类。例如，如果使用了代码分离，就会出现这种情况。代码分离是一项开
发技术，它可以将页面所需的代码隔离到单独的 C# 和 Microsoft Visual Basic® .NET
类中。页面的代码是一组事件处理程序和辅助方法，这些处理程序和方法真正决定了页面的行为。可以使用 <b>&lt;script runat=server&gt;</b> 标记对此代码进行内联定义，或者将其放置在外部类（代码分离类）中。代码分离类是从 <b>Page</b> 继承并使用额外的方法的类，被指定用作 HTTP 处理程序的基类。</p>
<p>还有一种情况，HTTP 处理程序也不是基于 <b>Page</b> 的，即在应用程序配置文件的 <b>&lt;pages&gt;</b> 部分中，包含了 <b>PageBaseType</b> 属性的重新定义。</p><pre class="code">&lt;pages PageBaseType="Classes.MyPage, mypage" /&gt;<br></pre>
<p><b>PageBaseType</b> 属性指明包含页面处理程序的基类的类型和程序集。从 <b>Page</b> 导出的这个类可以自动赋予处理程序扩展的自定义方法和属性集。</p><font class="105v"><a name="aspnet-pageobjectmodel_topic3"></a>页面的生命周期</font> 
<p>完全识别 HTTP 页面处理程序类后，ASP.NET 运行时将调用处理程序的 <b>ProcessRequest</b> 方法来处理请求。通常情况下，无需更改此方法的实现，因为它是由 <b>Page</b> 类提供的。</p>
<p>此实现将从调用为页面构建控件树的 <b>FrameworkInitialize</b> 方法开始。<b>FrameworkInitialize</b> 方法是 <b>TemplateControl</b> 类（<b>Page</b> 本身从此类导出）的一个受保护的虚拟成员。所有为 .aspx 资源动态生成的处理程序都将覆盖 <b>FrameworkInitialize</b>。在此方法中，构建了页面的整个控件树。</p>
<p>接下来，<b>ProcessRequest</b> 使页面经历了各个阶段：初始化、加载视图状态信息和回发数据、加载页面的用户代码以及执行回发服务器端事件。之后，页面进入显示模式：收集更新的视图状态，生成 HTML 代码并随后将代码发送到输出控制台。最后，卸载页面，并认为请求处理完毕。</p>
<p>在各个阶段中，页面会触发少数几个事件，这些事件可以由 Web 控件和用户定义的代码截取并进行处理。其中的一些事件是嵌入式控件专用的，因此无法在 .aspx 代码级进行处理。</p>
<p>要处理特定事件的页面应该明确注册一个适合的处理程序。不过，为了向后兼容早期的 Visual Basic 编程风格，ASP.NET
也支持隐式事件挂钩的形式。默认情况下，页面会尝试将特定的方法名称与事件相匹配，如果实现匹配，则认为此方法就是匹配事件的处理程序。ASP.NET
提供了六种方法名称的特定识别，它们是 <b>Page_Init</b>、<b>Page_Load</b>、<b>Page_DataBind</b>、<b>Page_PreRender</b> 和 <b>Page_Unload</b>。这些方法被认为是由 <b>Page</b> 类提供的相应事件的处理程序。HTTP 运行时会自动将这些方法绑定到页面事件，这样，开发人员就不必再编写所需的粘接代码了。例如，如果命名为 <b>Page_Load</b> 的方法绑定到页面的 <b>Load</b> 事件，则可省去以下代码。</p><pre class="code">this.Load += new EventHandler(this.Page_Load);<br></pre>
<p>对特定名称的自动识别是由 <b>@Page</b> 指令的 <b>AutoEventWireup</b> 属性控制的。如果该属性设置为 false，则要处理事件的所有应用程序都需要明确连接到页面事件。不使用自动绑定事件的页面性能会稍好一些，因为不需要额外匹配名称与事件。请注意，所有 Microsoft Visual Studio® .NET 项目都是在禁用 <b>AutoEventWireup</b> 属性的情况下创建的。但是，该属性的默认设置是 true，即 <b>Page_Load</b> 等方法会被识别，并被绑定到相关联的事件。</p>
<p>下表中按顺序列出了页面的执行包括的几个阶段，执行的标志是一些应用程序级的事件和/或受保护并可覆盖的方法。</p>
<p class="label"><b>表 1：ASP.NET 页面生命中的关键事件</b></p>
<table style="font-size: 12px;" bordercolordark="#ffffff" bordercolorlight="#cccccc" border="1" cellpadding="3" cellspacing="2" width="95%">
<tbody>
<tr align="left" valign="bottom">
<th width="20%">阶段</th>
<th width="20%">页面事件</th>
<th>可覆盖的方法</th></tr>
<tr valign="bottom">
<td>页面初始化</td>
<td><b>Init</b></td>
<td>&nbsp;</td></tr>
<tr valign="bottom">
<td>加载视图状态</td>
<td>&nbsp;</td>
<td><b>LoadViewState</b></td></tr>
<tr valign="bottom">
<td>处理回发数据</td>
<td>&nbsp;</td>
<td>任意实现 <b>IPostBackDataHandler</b> 接口的控件中的 <b>LoadPostData</b> 方法</td></tr>
<tr valign="bottom">
<td>加载页面</td>
<td><b>Load</b></td>
<td>&nbsp;</td></tr>
<tr valign="bottom">
<td>回发更改通知</td>
<td>&nbsp;</td>
<td>任意实现 <b>IPostBackDataHandler</b> 接口的控件中的 <b>RaisePostDataChangedEvent</b> 方法</td></tr>
<tr valign="bottom">
<td>处理回发事件</td>
<td>由控件定义的任意回发事件</td>
<td>任意实现 <b>IPostBackDataHandler</b> 接口的控件中的 <b>RaisePostBackEvent</b> 方法 </td></tr>
<tr valign="bottom">
<td>页面显示前阶段</td>
<td><b>PreRender</b></td>
<td>&nbsp;</td></tr>
<tr valign="bottom">
<td>保存视图状态</td>
<td>&nbsp;</td>
<td><b>SaveViewState</b></td></tr>
<tr valign="bottom">
<td>显示页面</td>
<td>&nbsp;</td>
<td><b>Render</b></td></tr>
<tr valign="bottom">
<td>卸载页面</td>
<td><b>Unload</b></td>
<td>&nbsp;</td></tr></tbody></table>
<p>以上所列的阶段中有些在页面级是不可见的，并且仅对服务器控件的编写者和要创建从 <b>Page</b> 导出的类的开发人员有意义。<b>Init</b>、<b>Load</b>、<b>PreRender</b>、<b>Unload</b>，再加上由嵌入式控件定义的所有回发事件，就构成了向外发送页面的各个阶段标记。</p><font class="105v"><a name="aspnet-pageobjectmodel_topic4"></a>执行的各个阶段</font> 
<p>页面生命周期中的第一个阶段是初始化。这个阶段的标志是 <b>Init</b> 事件。在成功创建页面的控件树后，将对应用程序触发此事件。换句话说，当 <b>Init</b> 事件发生时，.aspx 源文件中静态声明的所有控件都已实例化并采用各自的默认值。控件可以截取 <b>Init</b> 事件以初始化在传入的 Web 请求的生命周期内所需的所有设置。例如，这时控件可以加载外部模板文件或设置事件的处理程序。请注意，这时视图状态信息尚不可用。</p>
<p>初始化之后，页面框架将加载页面的视图状态。视图状态是名称/值对的集合，在此集合中，控件和页面本身存储了对所有 Web
请求都必须始终有效的全部信息。视图状态代表了页面的调用上下文。通常，它包含上次在服务器上处理页面时控件的状态。首次在会话中请求页面时，视图状态为
空。默认情况下，视图状态存储在静默添加到页面的隐藏字段中，该字段的名称是 <b>__VIEWSTATE</b>。通过覆盖 <b>LoadViewState</b> 方法（<b>Control</b> 类的受保护、可覆盖方法），组件开发人员可以控制视图状态的存储方式以及视图状态的内容映射到内部状态的方式。</p>
<p>有些方法（如 <b>LoadPageStateFromPersistenceMedium</b> 以及其对应的 <b>SavePageStateToPersistenceMedium</b>），可以用来将视图状态加载并保存到其他存储介质（例如会话、数据库或服务器端文件）中。与 <b>LoadViewState</b> 不同，上述方法只能在从 <b>Page</b> 导出的类中使用。</p>
<p>存储视图状态之后，页面树中控件的状态与页面最后一次显示在浏览器中的状态相同。下一步是更新它们的状态以加入客户端的更改。处理回发数据阶段使控件有机会更新其状态，从而准确反映客户端相应的 HTML 元素的状态。例如，服务器的 <b>TextBox</b> 控件对应的 HTML 元素是 <b>&lt;input type=text&gt;</b>。在回发数据阶段，TextBox 控件将检索 &lt;input&gt; 标记的当前值，并使用该值来刷新自己内部的状态。每个控件都要从回发的数据中提取值并更新自己的部分属性。<b>TextBox</b> 控件将更新它的 <b>Text</b> 属性，而 <b>CheckBox</b> 控件将刷新它的 <b>Checked</b> 属性。服务器控件和 HTML 元素的对应关系可以通过二者的 ID 找到。</p>
<p>在处理回发数据阶段的最后，页面中的所有控件的状态都将使用客户端输入的更改来更新前一状态。这时，将对页面触发 <b>Load</b> 事件。</p>
<p>页面中可能会有一些控件，当其某个敏感属性在两个不同的请求中被修改时，需要完成特定的任务。例如，如果 <b>TextBox</b> 控件的文本在客户端被修改，则此控件将触发 <b>TextChanged</b> 事件。每个控件在其一个或多个属性被修改为客户端输入的值时都可以决定触发相应的事件。对于这些更改对其非常关键的控件，控件实现 <b>IPostBackDataHandler</b> 接口，此接口的 <b>LoadPostData</b> 方法是在 <b>Load</b> 事件后立即调用的。通过对 <b>LoadPostData</b> 方法进行编码，控件将验证自上次请求后是否发生了关键更改，并触发自己的更改事件。</p>
<p>页面生命周期中的关键事件是被调用以执行服务器端代码的事件，此代码与客户端触发的事件相关联。当用户单击按钮时，将回发页面。回发值的集合中包括启动整个操作的按钮的 ID。如果控件实现 <b>IPostBackEventHandler</b> 接口（如按钮和链接按钮），页面框架将调用 <b>RaisePostBackEvent</b> 方法。此方法的行为取决于控件的类型。就按钮和链接按钮而言，此方法将查找 <b>Click</b> 事件处理程序并运行相关的委托。</p>
<p>处理完回发事件之后，页面就可以显示了。这个阶段的标志是 <b>PreRender</b> 事件。控件可以利用这段时间来执行那些需要在保存视图状态和显示输出的前一刻执行的更新操作。下一个状态是 <b>SaveViewState</b>，在此状态中，所有控件和页面本身都将更新自己 <b>ViewState</b> 集合的内容。然后，将得到序列化、散列、Base64 编码的视图状态，而且此视图状态与隐藏字段 __VIEWSTATE 相关联。</p>
<p>通过覆盖 <b>Render</b> 方法可以改变各个控件的显示机制。此方法接受 HTML 书写器对象，并使用此对象来积累所有要为控件生成的 HTML 文本。<b>Page</b> 类的 <b>Render</b> 方法的默认实现包括对所有成员控件的递归调用。对于每个控件，页面都将调用 <b>Render</b> 方法，并缓存 HTML 输出。</p>
<p>页面生命中的最后一个标志是 <b>Unload</b> 事件，在页面对象消除之前发生。在此事件中，您应该释放所有可能占用的关键资源（例如文件、图形对象、数据库连接等）。</p>
<p>在此事件之后，也就是最后，浏览器接收 HTTP 响应数据包并显示页面。</p><font class="105v"><a name="aspnet-pageobjectmodel_topic5"></a>小结</font> 
<p>ASP.NET 页面对象模型因其事件机制而显得格外新颖独特。Web 页面由控件组成，这些控件既可以产生丰富的基于 HTML
的用户界面，又可以通过事件与用户交互。以前，在 Web
应用程序的上下文中设置事件模型是件有挑战性的工作。可我们惊奇的看到，客户端生成的事件可以由服务器端的代码来解决，而且只进行一些相应的修改后，此过
程仍可以输出相同的 HTML 页面。</p>
<p>掌握这个模型对于了解页面生命周期的各个阶段，以及页面对象如何被 HTTP 运行时实例化并使用是非常重要的。</p>
<h3 class="dtH1">关于作者</h3>
<p><b>Dino Esposito </b>是一位来自意大利罗马的培训教师和顾问。作为 <a href="http://www.wintellect.com/">Wintellect</a> 团队的成员，Dino 专门研究 ASP.NET 和 ADO.NET，主要在欧洲和美国从事教学和咨询工作。此外，Dino 还负责管理 Wintellect 的 ADO.NET 课件，并为 <a href="http://msdn.microsoft.com/msdnmag">MSDN 期刊</a>的“Cutting Edge”专栏撰写文章。要与他联系，请向 <a href="mailto:%20dinoe@wintellect.com">dinoe@wintellect.com</a> 发送电子邮件。</p>
		<p class="postfoot">
			发表于 2003年11月4日 8:18
		</p><img src ="http://www.blogjava.net/juhongtao/aggbug/26722.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/juhongtao/" target="_blank">javaGrowing</a> 2006-01-05 15:03 <a href="http://www.blogjava.net/juhongtao/archive/2006/01/05/26722.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>