本演练演示如何创建和测试自定义 ASP.NET Web 服务器控件。

在本演练中,您将学会如何执行以下任务:

  • 创建一个 ASP.NET Web 服务器控件。
  • 通过向该控件添加元数据来指定安全设置和设计时行为。
  • 在配置文件和该控件的程序集中指定一个标记前缀。
  • 指定一个供该控件在 Visual Studio 工具箱中使用的图标。
  • 将该控件编译为程序集并添加项目引用,以便在同一解决方案的另一个项目中使用它。
  • 在网页中测试该控件,并使用“属性”窗口或 IntelliSense 访问其属性。

​Download​​(下载)主题附带含源代码的 Visual Studio 解决方案。


创建服务器控件



您将创建一个从标准 ​​Label​​ 控件派生的简单控件。 该控件名为 WelcomeLabel。 它会将用户名称追加到 Text 例如,如果页开发人员将“Hello”设置为 Text 属性的值,则 WelcomeLabel 控件将呈现“Hello, 用户名!”

该控件定义了 DefaultUserName 例如,如果页开发人员将 Text 属性设置为“Hello”,并将 DefaultUserName

创建自定义服务器控件



  1. 从“文件”菜单中选择“新建项目”。
    将显示“新建项目”对话框。
  2. 在“已安装的模板”下,展开“Visual Basic”或“Visual C#”,然后选择“Web”。
  3. 选择“ASP.NET 服务器控件”模板。
  4. 在“名称”框中输入 ServerControl1。
    “新建项目”对话框与下图类似。
  5. 单击“确定”。
    Visual Studio 会创建一个服务器控件项目,该项目具有一个名为 ServerControl1.cs 或 ServerControl1.vb 的类文件。
  6. 在“服务器资源管理器”中,将 ServerControl1.cs 或 ServerControl1.vb 重命名为 WelcomeLabel.cs 或 WelcomeLabel.vb。
    将出现一个对话框,询问您是否要重命名对 ServerControl1 的所有引用,如下图所示。
  7. 单击“是”。
  8. 打开 WelcomeLabel.cs 或 WelcomeLabel.vb。
  9. 更改 WelcomeLabel 类,以便该类从​​System.Web.UI.WebControls.Label​​ 而非 ​​System.Web.UI.WebControls.WebControl​​ 继承。
  10. 在 WelcomeLabel 类的 ToolboxData
  11. 删除 WelcomeLabel 类中的代码,并插入 DefaultUserName 属性和 RenderContents


    VB



' WelcomeLabel.vb
Option Strict On
Imports System
Imports System.ComponentModel
Imports System.Security.Permissions
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls

Namespace Samples.AspNet.VB.Controls
< _
AspNetHostingPermission(SecurityAction.Demand, _
Level:=AspNetHostingPermissionLevel.Minimal), _
AspNetHostingPermission(SecurityAction.InheritanceDemand, _
Level:=AspNetHostingPermissionLevel.Minimal), _
DefaultProperty("Text"), _
ToolboxData( _
"<{0}:WelcomeLabel runat=""server""> </{0}:WelcomeLabel>") _
> _
Public Class WelcomeLabel
Inherits WebControl
< _
Bindable(True), _
Category("Appearance"), _
DefaultValue(""), _
Description("The welcome message text."), _
Localizable(True) _
> _
Public Overridable Property Text() As String
Get
Dim s As String = CStr(ViewState("Text"))
If s Is Nothing Then s = String.Empty
Return s
End Get
Set(ByVal value As String)
ViewState("Text") = value
End Set
End Property

Protected Overrides Sub RenderContents( _
ByVal writer As HtmlTextWriter)
writer.WriteEncodedText(Text)
If Context IsNot Nothing Then
Dim s As String = Context.User.Identity.Name
If (s IsNot Nothing) AndAlso (s <> String.Empty) Then
Dim split() As String = s.Split("\".ToCharArray)
Dim n As Integer = split.Length - 1
If (split(n) <> String.Empty) Then
writer.Write(", ")
writer.Write(split(n))
End If
End If
End If
writer.Write("!")
End Sub
End Class
End Namespace

C#

​VB​

// WelcomeLabel.cs
using System;
using System.ComponentModel;
using System.Security.Permissions;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Samples.AspNet.CS.Controls
{
[
AspNetHostingPermission(SecurityAction.Demand,
Level = AspNetHostingPermissionLevel.Minimal),
AspNetHostingPermission(SecurityAction.InheritanceDemand,
Level=AspNetHostingPermissionLevel.Minimal),
DefaultProperty("Text"),
ToolboxData("<{0}:WelcomeLabel runat=\"server\"> </{0}:WelcomeLabel>")
]
public class WelcomeLabel : WebControl
{
[
Bindable(true),
Category("Appearance"),
DefaultValue(""),
Description("The welcome message text."),
Localizable(true)
]
public virtual string Text
{
get
{
string s = (string)ViewState["Text"];
return (s == null) ? String.Empty : s;
}
set
{
ViewState["Text"] = value;
}
}

protected override void RenderContents(HtmlTextWriter writer)
{
writer.WriteEncodedText(Text);
if (Context != null)
{
string s = Context.User.Identity.Name;
if (s != null && s != String.Empty)
{
string[] split = s.Split('\\');
int n = split.Length - 1;
if (split[n] != String.Empty)
{
writer.Write(", ");
writer.Write(split[n]);
}
}
}
writer.Write("!");
}
}
}

C#

using System.Drawing;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace ServerControl1
{
[DefaultProperty("Text")]
[ToolboxData("<{0}:WelcomeLabel runat=server></{0}:WelcomeLabel>")]
public class WelcomeLabel : Label
{
[
Bindable(true),
Category("Appearance"),
DefaultValue(""),
Description("The text to display when the user is not logged in."),
Localizable(true)
]
public virtual string DefaultUserName
{
get
{
string s = (string)ViewState["DefaultUserName"];
return (s == null) ? String.Empty : s;
}
set
{
ViewState["DefaultUserName"] = value;
}
}

protected override void RenderContents(HtmlTextWriter writer)
{
writer.WriteEncodedText(Text);

string displayUserName = DefaultUserName;
if (Context != null)
{
string userName = Context.User.Identity.Name;
if (!String.IsNullOrEmpty(userName))
{
displayUserName = userName;
}
}

if (!String.IsNullOrEmpty(displayUserName))
{
writer.Write(", ");
writer.WriteEncodedText(displayUserName);
}

writer.Write("!");
}
}
}
  1. 在“解决方案资源管理器”中,展开“属性”或“我的项目”,然后打开 AssemblyInfo.cs 或 AssemblyInfo.vb。
  2. 在文件开头添加下面的行:

    C#





    Using System.Web.UI;





    VB



Imports

下一个步骤中将添加的 ​​TagPrefixAttribute​​ 特性需要此命名空间。

  1. 在文件末尾添加下面的行:

    C#



[assembly: TagPrefix("ServerControl1", "aspSample")]

VB

<Assembly: TagPrefix("ServerControl1", "aspSample")>

此 ​​TagPrefixAttribute​​ 特性在命名空间 ServerControl1 与前缀 aspSample

  1. 保存 WelcomeLabel.cs 或 WelcomeLabel.vb 文件。



代码讨论



以下各节说明了您在上一过程中创建的代码。

从 WebControl 类继承



如果控件要呈现用户界面 (UI) 元素或任何其他客户端可见的元素,则应从 ​​System.Web.UI.WebControls.WebControl​​​(或从其派生的类)派生该控件。 (在此示例中,自定义控件派生自 ​​Label​​​,而后者派生自 ​​System.Web.UI.WebControls.WebControl​​​。)如果控件要呈现在客户端浏览器中不可见的元素(如隐藏元素或 meta 元素),则应从 ​​System.Web.UI.Control​​​ 派生该控件。 ​​WebControl​​​ 类从 ​​Control​​​ 派生,并添加了与样式相关的属性,如 ​​Font​​​、​​ForeColor​​​ 和 ​​BackColor​​​。 此外,一个从 ​​WebControl​​ 派生的控件也自行参与到 ASP.NET 的主题功能。



DefaultUserName 属性



WelcomeLabel 控件定义了一个 DefaultUserName 每次回发时,将重新创建页并从视图状态还原值。 如果 DefaultUserName 属性值并未存储在视图状态中,则在每次回发时会将值设置为其默认的 ​​Empty​​​。 ​​ViewState​​​ 属性继承自 ​​WebControl​​​,是保存数据值的字典。 通过使用 ​​String​​​ 键,可输入和检索值。 此示例中将“DefaultUserName”用作键。 字典中的项被类型化为 ​​Object​​​,然后当您访问这些项时,必须将其强制转换为实际属性类型。 有关视图状态的更多信息,请参见 ​​ASP.NET 状态管理概述​​。



RenderContents 方法



WelcomeLabel 控件通过重写继承的 ​​RenderContents​​​ 方法将文本写入响应流中。 传入 ​​RenderContents​​​ 方法的参数是 ​​HtmlTextWriter​​ 类型的对象,是具有呈现 HTML 的方法的类。

注意,WelcomeLabel 控件会连续调用 ​​HtmlTextWriter​​​ 对象的 ​​Write​​​ 方法,而不是先执行字符串串联然后调用 ​​Write​​​ 方法。 由于 ​​HtmlTextWriter​​ 对象直接写入输出流,因此这样可以提高性能。 字符串串联需要时间和内存来创建字符串,然后写入流。

通常,在从 ​​WebControl​​​ 派生控件并呈现单个元素时,应重写 ​​RenderContents​​​ 方法(而不是 ​​Render​​​ 方法)。 在呈现控件及其样式特性的开始标记之后,​​WebControl​​​ 的 ​​Render​​​ 方法将调用 ​​RenderContents​​​。 如果重写 ​​Render​​​ 方法以写入内容,则控件将丢失生成到 ​​WebControl​​​ 的 ​​Render​​​ 方法中的样式呈现逻辑。 有关呈现从 ​​WebControl​​​ 派生的控件的更多信息,请参见 ​​Web 控件呈现示例​​。



控件的特性



应用于 WelcomeLabel 控件和 DefaultUserName WelcomeLabel :

  • ​DefaultPropertyAttribute​​ . 此特性是设计时特性,它指定控件的默认属性。 在可视化设计器中,当页开发人员在设计图面上单击控件时,属性浏览器通常突出显示此默认属性。
  • ​ToolboxDataAttribute​​​ . 此特性指定元素的格式字符串。 如果在工具箱中双击控件或将其从工具箱拖动到设计图面上时,该字符串将成为控件的标记。 WelcomeLabel,该字符串创建以下元素:





    <aspSample:WelcomeLabel runat="server"> </aspSample:WelcomeLabel>



WelcomeLabel 控件还从 ​​WebControl​​​ 基类继承了两个特性:​​ParseChildrenAttribute​​​ 和 ​​PersistChildrenAttribute​​​。 这些特性将作为 ParseChildren(true) 和 PersistChildren(false) 这些特性一起与 ​​ToolboxDataAttribute​​ 特性共同使用,这样可将子元素解释为属性,并将属性作为特性保留。



DefaultUserName 属性的特性



以下应用于 WelcomeLabel 的 DefaultUserName

  • ​BindableAttribute​​ . 此特性指定将属性绑定到数据对可视化设计器是否有意义。 例如,在 Visual Studio 中,如果属性标记为 Bindable(true),则该属性可显示在“数据绑定”对话框中。 Bindable(false)。
  • ​CategoryAttribute​​ . 此特性指定如何在可视化设计器的属性浏览器中对属性进行分类。 例如,当页开发人员使用属性浏览器的分类视图时,Category("Appearance") 将告知属性浏览器在“外观”类别中显示属性。 可以指定一个对应于属性浏览器中的现有类别的字符串参数,也可以创建自己的类别。
  • ​DescriptionAttribute​​ . 此特性指定属性的简短描述。 “属性”窗口底部显示选定的属性的描述。
  • ​DefaultValueAttribute​​​ . 此特性指定属性的默认值。 此值应与从属性访问器 (getter) 返回的默认值相同。 在 Visual Studio 中,​​DefaultValueAttribute​​ 特性允许页开发人员通过在“属性”窗口中显示快捷菜单,然后单击“重置”按钮将属性值重置为其默认值。
  • ​LocalizableAttribute​​ . 此特性指定本地化属性对可视化设计器是否有意义。 Localizable(true) 对控件轮询可本地化的属性时,设计器会将此属性值保存到非特定于区域性的资源文件或另一个本地化源中。

应用于控件及其成员的设计时特性不会影响控件在运行时工作的方式。 但在可视化设计器中使用该控件时,设计时特性能改进开发人员体验。 有关服务器控件的设计时和运行时特性的完整列表,可参见​​自定义服务器控件的元数据特性​​。



标记前缀



标记前缀是指在页中以声明方式创建控件时出现在控件类型名称前面的前缀(如 <asp:Table /> 若要在页中以声明方式使用您的控件,则必须将一个标记前缀映射到该控件的命名空间。 通过在每个使用自定义控件的页上添加一个 ​​ @ Register​​ 指令,页开发人员可提供标记前缀/命名空间映射,如以下示例所示:





<%@ Register Assembly="ServerControl" TagPrefix="aspSample" Namespace="ServerControl"%>



将自定义服务器控件从工具箱拖动到 .aspx 页时,Visual Studio 将自动添加 Register

除了在每个 .aspx 页中使用 ​​ @ Register​​ 指令,您还可以在 Web.config 文件中指定标记前缀/命名空间映射。 如果将在 Web 应用程序的多个页中使用自定义控件,则该方法非常有用。 下面的示例显示了一个 Web.config 文件,该文件指定了 ServerControl1





<?xml version="1.0"?> <configuration> <system.web> <pages> <controls> <add tagPrefix="aspSample" Assembly="ServerControl" namespace="ServerControl"> </add> </controls> </pages> </system.web> </configuration>


测试自定义服务器控件



在下面的过程中,您将完成以下任务:

  • 创建一个网站,您将使用此网站测试在上一过程中创建的服务器控件。
  • 将网站项目中的引用添加到服务器控件项目。
  • 将 WelcomeLabel 控件添加到“工具箱”中。
  • 将 WelcomeLabel
  • 运行 Default.aspx 页以查看 WelcomeLabel

首先,创建可用于测试的网站。

本演练使用网站项目。 您可以改用 Web 应用程序项目。 有关这些 Web 项目类型之间的差异的信息,请参见 ​​Web 应用程序项目与网站项目​​。

创建网站项目以测试自定义服务器控件



  1. 在“文件”菜单上,单击“添加”,然后选择“新建网站”。
    显示“新建网站”对话框。
  2. 在“已安装的模板”下,选择“Visual Basic”或“Visual C#”,然后选择“ASP.NET 网站”模板。
  3. 将网站命名为 TestWebSite,并将它保存在 ServerControl1 解决方案文件夹下的一个新文件夹中。
    “新建网站”对话框与下图类似:
  4. 单击“确定”。
    Visual Studio 将创建一个网站项目,并将该项目添加到 ServerControl1 解决方案,然后在“源”视图中打开 Default.aspx 页。
  5. 在“服务器资源浏览器”中右击该网站项目,并选择“设为启动项目”。
  6. 在“服务器资源浏览器”中右击该网站项目,并选择“添加引用”。
    显示“添加引用”对话框。
  7. 选择“项目”选项卡,再选择“ServerControl1”项目,然后单击“确定”。 下图显示了“添加引用”对话框:



下一个步骤是将服务器控件添加到工具箱中,以便您能在测试网站的网页中使用它。

将 WelcomeLabel 控件添加到工具箱中



  1. 从“生成”菜单中选择“生成解决方案”。
    Visual Studio 将编译该解决方案,并将 ServerControl1 项目创建的程序集复制到 TestWebSite 项目的 Bin 文件夹中。
  2. 如果 Default.aspx 页仍处于打开状态,请打开“工具箱”窗口。
  3. 右击“工具箱”窗口中的任意位置,再单击“添加选项卡”。
  4. 将新选项卡命名为 Server Control1。
  5. 右击“Server Control1”选项卡,然后单击“选择项”。
  6. 选择“浏览”,然后浏览到 TestWebSite 项目的 Bin 文件夹。
  7. 选择 ServerControl1.dll,然后单击“打开”。
    ServerControl1 程序集将添加到“选择工具箱项”对话框的“.NET Framework 组件”选项卡,如下图所示:
  8. 单击“确定”。
    WelcomeLabel 控件即显示在“工具箱”的“Server Control1”选项卡中。



现在可以将该服务器控件添加到一个网页中,然后对该网页进行测试。

测试 WelcomeLabel 控件



  1. 在 Default.aspx 页中,删除“Welcome to ASP.NET!”文本,并从“工具箱”中将 WelcomeLabel
  2. 将 Text 和 DefaultUserName





    <aspSample:WelcomeLabel ID="WelcomeLabel1" runat="server" Text="Welcome" DefaultUserName="Guest"> </aspSample:WelcomeLabel>



  3. 按 Ctrl-F5 在浏览器中显示该网页。
    WelcomeLabel 控件显示“Welcome!”,如下图所示:
  4. 单击“登录”超链接。
    此时将显示“登录”页。
  5. 单击“注册”超链接。
    将显示“创建新帐户”页,如下图所示。
  6. 输入 newuser 作为用户名,输入 newuser@asp.net 作为电子邮件地址,输入密码,然后单击“创建用户”按钮。
    ASP.NET 创建新用户帐户,让您以新用户身份登录,然后返回主页。 WelcomeLabel 控件现在显示“Welcome, newuser!”,如下图所示:


为工具箱指定一个图标



Visual Studio 通常在工具箱中使用默认图标来显示控件,如下图所示:



作为控件的一个选项,可以通过在控件的程序集中嵌入一个 16*16 像素的位图来自定义控件在工具箱中的外观。

为工具箱指定一个图标



  1. 创建或获取一个 16*16 像素的位图,将其作为控件的工具箱图标。
  2. 将该位图命名为 WelcomeLabel.bmp。
  3. 在“解决方案资源管理器”中右击 ServerControl1 项目,选择“添加现有项”,然后选择位图文件。
  4. 在“属性”窗口中,将位图文件的“生成操作”属性更改为“嵌入的资源”。
  5. 在“解决方案资源管理器”中,右击 ServerControl1 项目,然后选择“添加新项”。
    显示“添加新项”对话框。
  6. 选择“类”模板,将文件命名为 ResourceFinder.cs 或 ResourceFinder.vb,然后单击“添加”。
    打开 ResourceFinder.cs 或 Resourcefinder.vb 文件。
  7. 使 ResourceFinder 类成为 sealed 和 internal(在 Visual Basic 中为 Friend 和 NotInheritable),如下面的示例所示。


    VB



Friend NotInheritable Class ResourceFinder

End Class

C#

​VB​

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ServerControl1
{
internal sealed class

C#

<%@ Page Language="C#"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>WelcomeLabel Test</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<aspSample:WelcomeLabel Text="Hello" ID="WelcomeLabel1"
runat="server" BackColor="Wheat" ForeColor="SaddleBrown"
  1. 保存并关闭文件。
  2. 打开 WelcomeLabel.cs 或 WelcomeLabel.vb 文件,然后将以下行添加到文件的开头:

    C#​​VB​





    Using System.Drawing;



    下一个步骤中将添加的​​ToolboxBitmapAttribute​​ 特性需要此命名空间。
  3. 在 WelcomeLabel 类中,添加​​ToolboxBitmapAttribute​​ 特性,并指定具有 ServerControl1 命名空间前缀的 WelcomeLabel.bmp 文件名,如下面的示例所示:

    C#​​VB​


[DefaultProperty("Text")]
[ToolboxData("<{0}:WelcomeLabel runat=server></{0}:WelcomeLabel>")]
[ToolboxBitmap(typeof(ResourceFinder), "ServerControl1.WelcomeLabel.bmp")]
public class
  1. 保存并关闭文件。
  2. 从“生成”菜单中选择“生成解决方案”。
  3. 在“TestWebSite”项目中,打开 Default.aspx 文件。
  4. 在“工具箱”中,右击“WelcomeLabel”控件,然后选择“删除”。
  5. 右击“Server Control1”选项卡,然后单击“选择项”。
  6. 选择“浏览”,然后浏览到 TestWebSite 项目的 Bin 文件夹。
  7. 选择 ServerControl1.dll,然后单击“打开”。
    ServerControl1 程序集将添加到“选择工具箱项”对话框的“.NET Framework 组件”选项卡中。
  8. 在“选择项”对话框中,单击“确定”。
    WelcomeLabel 控件即重新显示在“工具箱”的“Server Control1”选项卡中。 所选图标将显示在名称“WelcomeLabel”的左侧,如下图所示。


后续步骤



本演练演示了如何开发简单的自定义 ASP.NET 服务器控件以及如何在页中使用该控件。 有关更多信息,包括有关呈现、定义属性、维护状态、处理不同的浏览器或设备类型以及实现复合控件的信息,请参见​​开发自定义 ASP.NET 服务器控件​​。

通过使用在设计时和运行时提供不同用户界面的可视化设计器类,复杂控件(如 ​​GridView​​​ 控件)进一步增强了其设计时体验。 有关如何为控件实现自定义设计器类的更多信息,请参见 ​​ASP.NET 控件设计器概述​​。


请参见



其他资源



​开发自定义 ASP.NET 服务器控件​



​Web Custom Control Behavior and Authoring​