ASP.NET 网页中的客户端脚本

更新:2007 年 11 月

ASP.NET Web 应用程序并不局限于基于服务器的工具和语言。通过将 ECMAScript(JavaScript 或 JScript)包括在 ASP.NET 网页中,可以创建基于浏览器的丰富功能。ASP.NET 提供了多种功能来支持客户端脚本。

一种选择是创建客户端脚本的各个代码段并将其添加到 ASP.NET 网页中,以支持为应用程序设计的浏览器行为。如果您只想包括一些小段的 JavaScript 代码,或者希望使用已有的 JavaScript 代码,则可以采用这一选择。此选择还有助于在最大程度上减小 ASP.NET 网页的大小。

另外,您也可以利用 ASP.NET 中强大的 AJAX 功能。这些 AJAX 功能包括一个支持以下方面的完整框架:

  • 使用 JavaScript 进行面向对象的编程。

  • 异步回发。

  • 用于身份验证和配置文件的应用程序 Web 服务。

  • 通过扩展的调试和跟踪支持管理客户端脚本的服务器控件。

通过 ASP.NET AJAX 功能,只需手动编写较少的代码即可利用 JavaScript 功能。它提供了扩展的 JavaScript 功能,同时提供了一个框架,用于创建集成到 ASP.NET 中的客户端功能。

本主题概述在 ASP.NET 网页中添加客户端脚本的各种选择。它包含下列部分:

  • 方案

  • 在 ASP.NET 页面中包括自定义客户端脚本

  • 使用 ASP.NET AJAX 功能扩展 JavaScript

  • 依赖客户端脚本的 ASP.NET 服务器控件

  • 向 ASP.NET 服务器控件添加客户端脚本事件

  • 在客户端脚本中引用服务器控件

  • 引发服务器控件的客户端单击事件

  • 在客户端脚本与服务器代码之间通信

  • 创建客户端脚本组件

  • 向服务器控件添加客户端功能

方案

当您希望执行以下操作时,请使用客户端脚本:

  • 使网页的功能更为丰富,对用户操作的响应能力更强,行为更类似于传统的客户端应用程序,从而改善用户的网页体验。例如,您可以使用客户端脚本检查在文本框中键入每个字符时的键盘输入。

  • 向 ASP.NET 网页中添加 AJAX 功能,这样可以实现下列目的:

    • 通过在页面上动态封装 DOM 元素,减少整页刷新次数并避免页面闪动。

    • 更新并管理用户界面 (UI),而无需向服务器执行回发。

    • 按客户端组件组织代码。客户端组件是一些可重用的对象,用于封装基于 Microsoft AJAX Library 的 JavaScript。另外,还可以使用自定义服务器端控件管理客户端组件,或者将这些组件与现有的服务器控件关联起来,以添加客户端功能。

在 ASP.NET 页面中包括自定义客户端脚本

由于 ASP.NET 网页只呈现 HTML 标记,因此您可以在 ASP.NET 页面中添加自己的客户端脚本。由于在 ASP.NET 页中提供了对客户端脚本的支持,因此,请求相应页面的浏览器也支持客户端脚本。如果页面在移动电话或其他设备上的浏览器中运行,对客户端脚本的支持程度就会因浏览器的不同而有所不同。

在 ASP.NET 页面中包括客户端脚本有若干选择:

  • 以静态方式将客户端脚本包括在含有代码或使用 include 属性引用 JavaScript (.js) 文件的 script 块中。使用此选项,可在脚本块或 JavaScript 文件中插入无需动态创建,亦不需要 Microsoft AJAX Library 提供的附加 AJAX 功能的客户端脚本。

  • 使用 ClientScriptManager 类以动态方式创建客户端脚本并添加至 ASP.NET 网页。如果要创建的脚本依赖仅在运行时才可用的信息,请使用此选项。

  • 如果您计划利用 ASP.NET 的 AJAX 功能,则可以使用 ScriptManager 服务器控件管理客户端脚本文件。ScriptManager 服务器控件还可以确保,浏览器在运行您的脚本之前已加载 Microsoft AJAX Library。有关详细信息,请参见 ASP.NET AJAX 概述

包括静态客户端脚本块

就像在任何 HTML 页面上一样,您也可以向 ASP.NET 页面中添加 script 块。可以使用客户端脚本来编写客户端事件(如页面的 onload 事件)的事件处理程序。当 ASP.NET 页面在浏览器中运行时,页面上的标记元素均可以在客户端脚本中寻址。它们会像在 HTML 页面中那样引发所有客户端事件。

3hc29e2a.alert_note(zh-cn,VS.90).gif说明:

您可以在客户端脚本中引用 ASP.NET 服务器控件。有关更多信息,请参见本主题后面的在客户端脚本中引用服务器控件。

ASP.NET 网页还可以通过在 <script> 标记的 src 属性中引用脚本文件来对其进行访问,如下面的示例所示:

<script type="text/javascript" src="MyScript.js"></script>

将客户端脚本保存在外部 .js 文件而不是页面本身中,有助于组织客户端脚本。这样还可以简化客户端脚本的版本控制管理,并便于在页面之间共享它们。

外部 .js 文件由浏览器缓存,其方式与网页和图像的缓存方式类似。当浏览器将脚本作为外部文件加载后,任何需要该脚本的其他网页均可以从缓存中获取它。这有助于提高 Web 应用程序的性能。

动态创建客户端脚本

在许多情况下,都可以通过声明方式为页面创建客户端脚本,该脚本通常作为脚本块使用。但是,有时也可能需要以动态方式创建客户端脚本。如果脚本依赖的信息只在运行时提供,则这种方式将非常有用。例如,您可以在页面中插入客户端脚本来为名称 (ID) 未知的服务器控件寻址,或者创建依赖用户提供的值的脚本。

通过调用 ClientScriptManager 类的方法,可按动态方式创建客户端脚本并将其插入已呈现的页面,例如:

下面的示例演示如何以动态方式将生成的客户端脚本添加到页面中。此代码检查是否已选中名为 checkDisplayCount 的复选框。如果是,此代码将执行下列任务:

  • 创建一个客户端脚本函数,以使用 span 元素显示名为 TextBox1 的 TextBox 控件中的字符数。

  • TextBox 控件添加一个客户端事件。

  • 生成 span 元素。

此代码假定该页面包含一个名为 checkDisplayCount 的复选框以及一个名为 PlaceHolder1 的 PlaceHolder 控件,并且该复选框的 AutoPostBack 属性已设置为 true。

Protected Sub Page_Load(ByVal sender As Object, _
        ByVal e As System.EventArgs)
    If checkDisplayCount.Checked Then
        Dim scriptText As String = ""
        scriptText &= "function DisplayCharCount(){"
        scriptText &= "   getElementByID("spanCounter").innerText = " & _
            "document.forms[0].TextBox1.value.length"
        scriptText &= "}"
        ClientScriptManager.RegisterClientScriptBlock(Me.GetType(), _
            "CounterScript", scriptText, True)
        TextBox1.Attributes.Add("onkeyup", "DisplayCharCount()")
        Dim spanLiteral As New LiteralControl()
        spanLiteral.Text = "<span id=""spanCounter""></span>"
        PlaceHolder1.Controls.Add(spanLiteral)
    End If
End Sub

void Page_Load(object sender, EventArgs e)
{
    if(checkDisplayCount.Checked)
    {
        String scriptText = "";
        scriptText += "function DisplayCharCount(){";
        scriptText += "   spanCounter.innerText = " + 
            " document.forms[0].TextBox1.value.length";
        scriptText += "}";
        ClientScriptManager.RegisterClientScriptBlock(this.GetType(), 
           "CounterScript", scriptText, true);
        TextBox1.Attributes.Add("onkeyup", "DisplayCharCount()");
        LiteralControl spanLiteral = new 
            LiteralControl("<span id=\"spanCounter\"></span>");
        PlaceHolder1.Controls.Add(spanLiteral);
    }
}

有关更多信息,请参见如何:向 ASP.NET 网页动态添加客户端脚本

应为 ClientScriptManager 类方法提供一个与脚本块关联的类型,同时为该脚本块提供一个键。如果页面和控件(自定义控件或用户控件)都向同一页面添加脚本块,则提供类型有助于避免发生冲突。使用键则有助于避免重复。当调用一种方法来添加脚本时,如果已存在与要添加的脚本具有相同键和相同类型的脚本,则不会再次添加该脚本。

所提供的类型和键有助于避免不必要的重复。因此,一般不需要显式确定是否已存在某个脚本块。但是,如果在应用程序中检查现有脚本块能够起到帮助作用,则可以调用下列方法:

使用 ScriptManager 控件添加客户端脚本

使用 ASP.NET AJAX 功能时,ScriptManager 控件可提供一种在页面中添加脚本文件的途径。这包括磁盘上的静态脚本文件(.js 文件)以及作为资源嵌入在程序集中的脚本文件。在此控件中,可以用声明方式指定脚本文件。另外,还可以使用 ScriptManager 控件的注册方法。通过这些方法,可以用编程方式管理现有的客户端脚本文件,并使用支持部分页更新的脚本。

有关 ScriptManager 控件的概述,请参见 ScriptManager 控件概述。有关如何将 JavaScript 文件作为资源嵌入程序集以及如何在网页中使用 JavaScript 文件的信息,请参见演练:将 JavaScript 文件作为资源嵌入到程序集中

3hc29e2a.alert_note(zh-cn,VS.90).gif说明:

如果使用的 ASP.NET AJAX 功能不是基于服务器的功能,则可以使用 ClientScriptManager 类的方法将脚本插入 ASP.NET 网页。例如,如果未对脚本使用 UpdatePanel 控件或本地化功能,则无需使用 ScriptManager 控件。这样,将无法调用其方法来在页面中插入客户端脚本。通常情况下,使用 ClientScriptManager 类的方法比使用 ScriptManager 控件方法更为有效。这是因为,ClientScriptManager 类方法不需要额外处理由 ScriptManager 控件所支持的基于服务器的功能。如果必须以动态方式创建脚本(而不只是管理现有脚本),请使用 ClientScriptManager 类在页面中插入客户端脚本。例如,如果要基于预先存在的脚本文件所无法引用的信息来以编程方式创建客户端脚本,则应使用 ClientScriptManager 类。

使用 ASP.NET AJAX 功能扩展 JavaScript

ASP.NET 支持 AJAX 功能,后者允许使用客户端脚本在 ASP.NET 网页中添加丰富的功能。这包括异步回发、响应迅速的用户界面,等等。AJAX 功能由 Microsoft AJAX Library 实现,后者包含大量集跨浏览器 ECMAScript (JavaScript) 和动态 HTML (DHTML) 技术于一身的客户端脚本库。AJAX 功能与基于 ASP.NET 服务器的开发平台集成在一起。

通过使用 Microsoft AJAX Library 的类型系统、面向对象的功能,以及对 JavaScript 对象的扩展,可以为创建自定义客户端脚本提供下列功能:

  • 命名空间

  • 继承

  • 接口

  • 枚举

  • 反射

  • 调试帮助器

  • 跟踪

  • 类型化异常处理

  • 针对字符串和数组的帮助器方法。

    3hc29e2a.alert_note(zh-cn,VS.90).gif说明:

    即使您不想使用 ASP.NET 的基于服务器的 AJAX 功能,也可以使用 Microsoft AJAX Library。

有关 Microsoft AJAX Library 如何扩展 JavaScript 的更多信息,请参见下列主题:

AJAX 功能的浏览器兼容性

Microsoft AJAX Library 中的浏览器兼容性层可为最常用的浏览器(包括 Microsoft Internet Explorer、Mozilla Firefox 和 Apple Safari)提供 AJAX 脚本兼容性。这样,您可以始终编写同一脚本,而不用考虑要针对的是哪种受支持的浏览器。有关更多信息,请参见 ASP.NET AJAX 概述

依赖客户端脚本的 ASP.NET 服务器控件

有些 ASP.NET 服务器控件依赖于客户端脚本进行正常工作。例如,LinkButton 控件需要客户端脚本来支持其回发行为。在呈现页面时,ASP.NET Web 服务器控件所需的客户端脚本会自动添加到相应页面中。为这些控件生成的客户端脚本与您创建的任何客户端脚本无关。

有关更多信息,请参见使用客户端脚本的 ASP.NET Web 服务器控件

向 ASP.NET 服务器控件添加客户端脚本事件

ASP.NET 控件以页面元素的形式呈现(控件所呈现的具体元素取决于页面的标记语言,这可能包括 HTML、XHTML 或其他语言)。因此,可以像使用页面上的任何元素一样,将客户端脚本事件处理添加到控件中。但是,在某些情况下,您必须知道控件输出的呈现方式,以及控件为其自身保留的属性。

有关更多信息,请参见如何:向 ASP.NET Web 服务器控件添加客户端脚本事件

以声明方式添加客户端事件处理程序

在 ASP.NET 服务器控件的标记中,可以使用属性 (Attribute) 来设置属性 (Property) 值。例如,若要设置 TextBox 控件的 Text 属性,可以创建类似以下内容的标记:

<asp:textbox id="TextBox1" runat="server" text="Sample Text" />
3hc29e2a.alert_security(zh-cn,VS.90).gif安全说明:

TextBox 接受用户输入,这将是一个潜在的安全威胁。默认情况下,ASP.NET 网页验证用户输入是否不包括脚本或 HTML 元素。有关更多信息,请参见脚本侵入概述

如果包括的属性 (Attribute) 未映射到控件的属性 (Property),ASP.NET 将在服务器处理期间忽略该属性 (Attribute)。它会将该属性 (Attribute) 作为该控件所生成的标记的一部分按原样传递给浏览器。例如,TextBox 控件不具有名为 onkeyup 的属性。因此,如果在 TextBox 控件的标记中包括了 onkeyup 属性,该属性便会被传递给浏览器。此行为允许您在标记中声明事件绑定,从而向服务器控件添加事件绑定。例如,当用户在文本框中按下任意键时,TextBox 控件的以下标记便会使文本框显示名为 spanCounter 的 span 元素中的当前文本长度:

<asp:textbox id="TextBox1" runat="server" text="Sample Text" 
  onkeyup="spanCounter.innerText=this.value.length;" />

另外,该事件也可以调用在页面其他位置的客户端脚本中定义的方法:

<asp:textbox id="TextBox1" runat="server" text="Sample Text" 
  onkeyup="DisplayCount(this);" />

请注意,服务器代码所用的语言(Visual Basic 或 C#)不会影响客户端脚本,因为客户端脚本始终都是用 ECMAScript(JavaScript 或 JScript)编写。

有关更多信息,请参见如何:向 ASP.NET Web 服务器控件添加客户端脚本事件

在服务器代码中添加属性

除了以声明的方式向控件添加传递属性外,还可以使用服务器代码以编程的方式添加属性。如果只有在运行时才知道要添加的属性的值,则可以使用这种方式。有关详细信息,请参见如何:在 ASP.NET 网页中设置控件的 HTML 属性

在客户端脚本中引用服务器控件

呈现 ASP.NET 服务器控件时,该控件的 ClientID 属性 (Property) 将作为结果元素的 id 属性 (Attribute) 和 name 属性 (Attribute) 呈现于页面上。(ClientID 属性是从您所设置的 ID 属性中自动生成的。)例如,您可能会创建以下 ASP.NET 服务器控件:

<asp:textbox id="TextBox1" runat="server" text="Sample Text" />

ClientID 属性设置为 TextBox1,其结果元素在基于 HTML 的浏览器中类似于以下内容:

<input name="TextBox1" type="text" value="Sample Text" id="TextBox1" />
3hc29e2a.alert_note(zh-cn,VS.90).gif说明:

form 元素只呈现 ID 属性,而不呈现 name 属性。

因此,可以使用这些属性在客户端脚本中引用服务器控件。通常,控件寻址可通过在客户端脚本中使用完全限定的引用来完成。如果该控件是页面元素 form 的子级,一般可通过使用如以下示例所示的语法在客户端脚本中引用该控件:

var tb1 = document.getElementById("TextBox1");
tb1.value = "New value";

下面的示例假定页面上的 form 元素已将其 id 属性设置为 form1。

document.form1.TextBox1.value = "New value";

引用服务器控件所需的确切语法取决于您所使用的控件,以及其是否为另一控件的子控件。如果不确定如何引用控件,则请运行页面,查看其源文件,然后确定该控件的呈现方式,这可能会有所帮助。

引用在其他控件内部呈现的控件

有些控件将子控件呈现在页面中。这些控件包括数据列表控件(例如 ListViewGridViewDetailsViewFormViewDataListRepeater 控件)、用户控件和 Web 部件控件。

在这些情况下,子控件可能没有唯一的 ID。这可能是因为,定义子控件的模板为每个数据行都生成新的控件实例(数据列表控件)。也可能是因为可以从外部源向页面添加父控件(用户控件和 Web 部件控件)。因此,这些父控件都是命名容器(它们实现 INamingContainer)。命名容器可保证其子控件在页面上拥有唯一的 ID。

例如,可以在 DataList 控件中创建一个 ItemTemplate 属性。在模板中,可以添加一个将 ID 设置为 checkEnabled 的 CheckBox 控件。这样,在呈现 DataList 控件时,就会为每个数据项呈现一个新的 checkEnabled 控件。呈现的页面不能包含 checkEnabled 元素的多个实例。因此,DataList 控件会为它的每个子控件创建唯一的标识。

命名容器子控件的唯一标识通过呈现两个属性的方式来生成。对于每个子控件:

  • 控件的 UniqueID 属性 (Property) 被呈现为 name 属性 (Attribute)。

  • 控件的 ClientID 属性 (Property) 被呈现为 id 属性 (Attribute)。

ClientID 和 UniqueID 属性都基于原始的 ID 属性,并用足够的信息进行了修改,以保证页面中结果的唯一性。ClientID(即所呈现元素中的 id 属性)的值可在客户端脚本中引用。

当在浏览器中显示包含命名容器的页面时,将可以查看该页面的源文件,进而找到作为命名容器子控件的 name 属性和 id 属性生成的唯一 ID。但是,我们建议不要依赖于直接引用浏览器中显示的 ID。因为用于生成子控件唯一 ID 的公式可能会发生变化。因此,应在服务器模式下获取子控件的 ClientID 属性值,并使用该值引用该子控件。例如,您可能会在页面中动态创建客户端脚本。如果客户端脚本引用了一个子控件,则应获取该子控件的 ClientID 属性,并将其嵌入到动态脚本中。

引发服务器控件的客户端单击事件

如前文所述,通过向控件的标记中添加 onclick 属性,可以将客户端的单击事件添加到大多数 ASP.NET 服务器控件中。但是,有些控件保留了 onclick 属性,用于定义与服务器事件的绑定。这些控件包括 ButtonLinkButtonImageButton 控件。在这些控件中,不能以声明方式使用 onclick 属性来向控件添加客户端脚本处理程序。

可以通过两种方式为单击事件添加客户端脚本处理程序:

  • 将控件的 OnClientClick 属性设置为要执行的脚本。这样,在呈现按钮控件时,OnClientClick 值将变成 onclick 属性。

  • 通过调用控件的 Attributes 集合的 Add 方法,用编程的方式添加 onclick 属性。

    3hc29e2a.alert_note(zh-cn,VS.90).gif说明:

    如果服务器控件已经使用 onclick 作为其基本功能,则不能以编程方式向该控件中添加 onclick 属性,例如 LinkButton

下面的代码示例显示引发客户端和服务器 Click 两种事件的 Button 控件:

<asp:button id="Button1" 
  runat="server" 
  OnClientClick="return confirm('Ok to post?')" 
  onclick="Button1_Click" 
  Text="Click!" />

在客户端脚本与服务器代码之间通信

除了使用标准回发外,ASP.NET 网页还可以通过多种方式在客户端脚本与服务器代码之间通信。ASP.NET 中的 AJAX 功能(例如 UpdatePanel 服务器控件)可自动为您执行异步部分页更新。另外,ASP.NET AJAX 还支持异步调用 Web 服务。

如果不使用 ASP.NET 的 AJAX 功能,您可以直接调用自定义客户端回调,并通过多种方法在浏览器与服务器之间共享信息。以下部分提供有关各种可用选择的信息。

直接从客户端脚本中调用服务器代码

客户端脚本可通过实现客户端回调直接调用服务器代码。在 ASP.NET 网页的一般序列中,运行服务器代码的每项用户操作都要求回发。但是,您也可以在不完全回发的情况下,从浏览器中调用服务器处理。在此方案中,浏览器并不是先向服务器发送整个页面,然后在服务器响应时再重新加载该页面。实际上,浏览器只向服务器发送一小部分数据。当服务器发送响应时,浏览器中的客户端脚本将处理返回的数据,而不是重新加载该页面。在服务器处理期间,将保留所有客户端状态(例如本地变量)。此过程称为异步回发,这是部分页呈现的关键部分。

通过下列方法,可以不通过回发而直接在客户端脚本中调用服务器方法:

  • 使用 ASP.NET UpdatePanel 服务器控件。此控件是 ASP.NET AJAX 功能的一部分。借助它可以定义将通过部分页更新刷新的页面区域。使用 UpdatePanel 控件时,您不必自己编写任何客户端脚本,即可异步调用部分页更新。有关更多信息,请参见部分页呈现概述UpdatePanel 控件概述

  • 利用 ASP.NET AJAX 对 Web 服务通信的支持,编写调用 Web 服务器方法的客户端脚本。从概念上讲,这种方法类似于编写自己的客户端脚本回调来调用 Web 服务器方法。但是,Microsoft AJAX Library 将负责处理调用服务器方法的细节,同时会提供更为可靠的客户端脚本库来执行并处理调用。有关更多信息,请参见 ASP.NET AJAX 中的 Web 服务

  • 实现客户端回调。在此方案中,需要编写发送请求和处理结果的客户端代码。最常见的方法是在客户端脚本中创建一个调用函数,以及一个在服务器返回结果时调用的回调函数。有关更多信息,请参见在 ASP.NET 网页中不经过回发而以编程方式实现客户端回调

上述方法各有优点。通常,自行创建客户端回调的开销最低,所得到的页面也最小。使用 UpdatePanel 服务器控件可以实现部分页更新,而无需自行编写任何客户端脚本。使用 AJAX 调用 Web 服务同样可以省去执行异步 Web 服务调用时需要手动编写的大部分客户端脚本。

使用 HiddenField 控件共享数据

通过向页面中添加 HiddenField 控件,可以在客户端脚本与服务器代码之间共享信息。在客户端代码中,可通过 ID 来引用该隐藏字段。您也可以在服务器代码中引用该隐藏字段。这样可以将值存储在一个代码块中,然后从另一个代码块中读取它们。若要从服务器代码向客户端脚本传递信息,可以使用 RegisterHiddenField 方法以编程方式创建一个隐藏字段。此方法允许为该字段指定 ID 和值。使用该字段可按客户端脚本能够读取的方式存储页面中的动态值。

通过使用 Cookie,也可以在服务器代码和客户端代码之间共享值。有关如何在服务器模式下编写和读取 Cookie 的详细信息,请参见如何:编写 Cookie如何:读取 Cookie。有关如何使用 JavaScript 读取和编写 Cookie 的详细信息,请参见位于 MSDN 网站上的 JavaScript and cookies(JavaScript 和 Cookie)。

在异步回发期间共享数据

使用 ASP.NET AJAX 功能时,可以使用 RegisterDataItem 方法在异步回发(部分页呈现)期间以字符串形式从服务器向客户端发送自定义数据。利用 RegisterDataItem 方法,可以使用服务器代码注册附加到控件中的字段。通过 ASP.NET AJAX Sys.WebForms.PageLoadingEventArgsSys.WebForms.PageLoadedEventArgsSys.WebForms.PageLoadedEventArgs 对象,可以在浏览器的客户端脚本中访问该字段的值。RegisterDataItem 方法只能在异步回发期间进行调用。

有关如何使用 RegisterDataItem 方法在服务器和客户端之间共享数据的信息,请参见下列主题:

创建客户端脚本组件

客户端组件是一些可重用的对象,用于封装基于 Microsoft AJAX Library 的 JavaScript。该库提供下列基客户端对象类型类:Component(非可视组件的基类)、ControlBehavior。您可以从这些类进行派生,以提供丰富的客户端功能,包括:

  • 获取对组件生命周期(从初始化到释放)的访问。这包括属性值更改时所引发的事件。

  • 将 DOM 元素表示为具有新功能的客户端对象。这样可扩展 DOM 元素的行为。例如,可以添加能够附加到现有文本框的水印行为。

有关更多信息,请参见下列主题:

向服务器控件添加客户端功能

任何现有或自定义的 ASP.NET 服务器控件都可以转换为 ASP.NET AJAX 扩展程序控件。扩展程序控件是一种 Web 服务器控件,它可以使用 Web 浏览器的 ECMAScript (JavaScript)、DHTML 和 AJAX 功能添加与服务器进行交互和异步通信等类似的功能。

有关更多信息,请参见下列主题:

请参见

概念

ASP.NET Web 服务器控件和浏览器功能

ASP.NET Web 服务器控件事件模型