目录
- 为什么需要自定义服务器控件?
- 自定义服务器控件的类型
- 复合控件
- 派生控件
- 非视觉控件
- 创建一个简单的复合控件(示例)
- 步骤 1:创建类库项目
- 步骤 2:继承
CompositeControl或WebControl - 步骤 3:重写
CreateChildControls方法 - 步骤 4:添加属性和事件
- 步骤 5:注册并使用控件
- 创建一个派生的控件(示例)
- 继承现有控件(如
TextBox) - 重写方法(如
RenderContents)
- 继承现有控件(如
- 高级主题
- 视态管理
- 回发事件处理
- 控件状态 (ViewState)
- 设计时支持
- 最佳实践与注意事项
为什么需要自定义服务器控件?
当你发现以下情况时,就应该考虑创建自定义服务器控件:

- 代码重用:你需要在多个页面或多个项目中重复使用一段功能相似的 UI 和逻辑,一个标准化的“搜索框”或“用户信息卡片”。
- 封装复杂性:一个功能由多个 ASP.NET 控件组合而成,并涉及复杂的后台逻辑,将这些细节封装起来,可以让页面开发者像使用一个普通控件(如
Button或GridView)一样简单。 - 统一外观和行为:为了保持整个网站 UI/UX 的一致性,你可以创建自定义控件来强制执行统一的样式、布局和验证规则。
- 扩展功能:你需要修改或扩展现有 ASP.NET 控件的功能,为
TextBox添加水印提示,或为Button添加一个确认对话框。
自定义服务器控件的类型
主要分为以下三类:
a. 复合控件
这是最常见的一种,它不直接渲染 HTML,而是通过组合和编排其他现有服务器控件来构建其 UI。
- 特点:
- 继承自
System.Web.UI.WebControls.CompositeControl或System.Web.UI.WebControls.WebControl。 - 在
CreateChildControls方法中创建并添加子控件。 - 适合构建复杂的、由多个部分组成的 UI 组件。
- 继承自
- 示例:一个包含
Label、TextBox和RequiredFieldValidator的“带验证的登录框”控件。
b. 派生控件
这种控件继承自一个现有的 ASP.NET 控件(如 TextBox, Button, GridView),并在此基础上添加新功能或修改现有行为。
- 特点:
- 继承自具体的控件类,如
System.Web.UI.WebControls.TextBox。 - 通过重写
RenderContents或Render方法来改变其输出的 HTML。 - 通过重写事件处理方法(如
OnLoad,OnPreRender)来修改其生命周期行为。
- 继承自具体的控件类,如
- 示例:一个
WatermarkedTextBox,它继承自TextBox,并在为空时显示灰色水印文本。
c. 非视觉控件
这种控件不产生任何可见的 UI,主要用于在页面生命周期中执行任务。

- 特点:
- 继承自
System.Web.UI.Control。 - 通常用于处理数据、管理资源或与其他控件交互。
- 继承自
- 示例:
AdRotator(虽然它有输出,但逻辑更重)、Xml控件等。
创建一个简单的复合控件(示例)
我们将创建一个名为 SearchBox 的复合控件,它包含一个 TextBox、一个 Button 和一个 Click 事件。
步骤 1:创建类库项目
- 打开 Visual Studio。
- 创建新项目 -> 选择 "类库" (.NET Framework) 模板。
- 将项目命名为
MyCustomControls。 - 删除默认的
Class1.cs文件。
步骤 2:继承 CompositeControl
在项目中添加一个新类,命名为 SearchBox.cs,并让它继承自 CompositeControl。
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace MyCustomControls
{
[ToolboxData("<{0}:SearchBox runat=server></{0}:SearchBox>")]
public class SearchBox : CompositeControl
{
// ... 代码将在下面添加
}
}
[ToolboxData]特性:这个特性允许控件在工具箱中显示,并指定其默认的标记。{0}会被控件类名替换。
步骤 3:重写 CreateChildControls 方法
这是复合控件的核心,我们将在这里创建并添加子控件。
protected override void CreateChildControls()
{
// 1. 清除现有的子控件(防止重复添加)
Controls.Clear();
// 2. 创建子控件
TextBox txtSearch = new TextBox
{
ID = "txtSearch",
CssClass = "form-control" // 假设我们使用 Bootstrap
};
Button btnSearch = new Button
{
ID = "btnSearch",
Text = "搜索",
CssClass = "btn btn-primary"
};
// 3. 将子控件添加到控件的 Controls 集合中
Controls.Add(txtSearch);
Controls.Add(btnSearch);
// 4. 调用基类方法,标记子控件已创建
base.CreateChildControls();
}
步骤 4:添加属性和事件
我们需要一个属性来获取搜索文本,以及一个事件来通知页面按钮被点击了。

// 公共属性,用于获取/设置搜索文本
public string SearchText
{
get
{
// EnsureChildControls 确保子控件已被创建
EnsureChildControls();
TextBox txt = (TextBox)Controls[0];
return txt.Text;
}
set
{
EnsureChildControls();
TextBox txt = (TextBox)Controls[0];
txt.Text = value;
}
}
// 声明一个 Click 事件
public event EventHandler Click;
// 重写 OnPreRender 方法来捕获按钮的点击事件
protected override void OnPreRender(EventArgs e)
{
EnsureChildControls();
// 获取按钮控件
Button btn = (Button)Controls[1];
// 为按钮的 Click 事件添加一个处理程序
btn.Click += Btn_Click;
base.OnPreRender(e);
}
// 按钮点击的实际处理程序
private void Btn_Click(object sender, EventArgs e)
{
// 触发自定义的 Click 事件
OnClick(EventArgs.Empty);
}
// 受保护的虚方法,用于触发事件
protected virtual void OnClick(EventArgs e)
{
if (Click != null)
{
Click(this, e);
}
}
步骤 5:注册并使用控件
-
生成 DLL:右键点击
MyCustomControls项目 -> "生成" (Build),这会在bin/Debug(或bin/Release) 文件夹中生成MyCustomControls.dll。 -
创建一个测试网站:
- 在同一个解决方案中,添加一个新的 "ASP.NET Web 应用程序" 项目。
- 在网站的
Web.config文件的<pages>节点中添加控件注册:<pages> <controls> <add tagPrefix="my" namespace="MyCustomControls" assembly="MyCustomControls" /> </controls> </pages>tagPrefix:你在 aspx 页面中使用的前缀,如<my:SearchBox>。namespace:自定义控件的命名空间。assembly:控件的 DLL 文件名(不带 .dll 扩展名)。
-
在页面中使用: 打开一个 ASPX 页面(如
Default.aspx),在源视图中添加以下代码:<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication1._Default" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>自定义控件测试</title> <!-- 引入 Bootstrap 样式 --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" /> </head> <body> <form id="form1" runat="server"> <div class="container mt-5"> <h1>自定义 SearchBox 控件</h1> <my:SearchBox ID="mySearchBox" runat="server" OnClick="MySearchBox_Click" /> <hr /> <asp:Label ID="lblResult" runat="server" Text="等待搜索..." CssClass="h4"></asp:Label> </div> </form> </body> </html> -
编写事件处理代码: 在
Default.aspx.cs中,为MySearchBox_Click事件添加处理程序:protected void MySearchBox_Click(object sender, EventArgs e) { // 通过 FindControl 获取我们的自定义控件实例 SearchBox searchBox = (SearchBox)Page.FindControl("mySearchBox"); if (searchBox != null) { // 获取 SearchText 属性的值并显示 lblResult.Text = "你搜索的是: <strong>" + Server.HtmlEncode(searchBox.SearchText) + "</strong>"; } }
现在运行网站,你就能看到一个由 SearchBox 控件渲染的输入框和按钮,点击按钮后,页面会显示你输入的搜索词。
创建一个派生的控件(示例)
创建一个 WatermarkedTextBox,它继承自 TextBox。
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace MyCustomControls
{
[ToolboxData("<{0}:WatermarkedTextBox runat=server></{0}:WatermarkedTextBox>")]
public class WatermarkedTextBox : TextBox
{
// 水印文本属性
public string Watermark
{
get { return (string)ViewState["Watermark"] ?? string.Empty; }
set { ViewState["Watermark"] = value; }
}
// 重写 RenderContents 方法来输出自定义 HTML
protected override void RenderContents(HtmlTextWriter writer)
{
// 当文本为空且控件未获得焦点时,显示水印
if (string.IsNullOrEmpty(this.Text) && !this.Page.ClientScript.IsClientScriptBlockRegistered("Watermark_" + this.ClientID))
{
writer.WriteBeginTag("span");
writer.WriteAttribute("style", "color: #999;"); // 水印样式
writer.WriteAttribute("id", this.ClientID + "_watermark");
writer.WriteAttribute("class", "watermark-text"); // 方便用 JS 控制
writer.Write(">");
writer.Write(this.Watermark);
writer.WriteEndTag("span");
}
// 调用基类的 RenderContents 来渲染标准的 TextBox 输出
base.RenderContents(writer);
}
}
}
使用方式:
在 ASPX 页面中,你可以像使用普通 TextBox 一样使用它,并设置 Watermark 属性。
<my:WatermarkedTextBox ID="wtbEmail" runat="server" Watermark="请输入您的邮箱" />
注意:上面的
RenderContents方法只是一个简单示例,一个完整的水印控件通常需要结合 JavaScript 来在用户点击/输入时隐藏水印,在失去焦点且文本为空时显示水印。
高级主题
- 视态管理:使用
WebControl的Visible属性可以控制控件的显示和隐藏。CompositeControl内部会处理子控件的Visible属性,确保它们不会被渲染。 - 回发事件处理:对于复合控件,通常需要将子控件的事件(如
Button.Click)“冒泡”到父控件的自定义事件上,如我们在SearchBox示例中所做的那样。 - 控件状态:
ViewState是 ASP.NET 控件用来在回发之间保持其状态(如属性值)的机制,自定义属性需要将值存储在ViewState字典中,如Watermark属性所示。 - 设计时支持:为了让控件在 Visual Studio 的设计视图中正常显示和提供智能感知,你可以使用
[Browsable(true)]、[DefaultValue("")]、[Description("...")]等特性,并实现ControlDesigner类,这是一个更高级的话题。
最佳实践与注意事项
- 优先考虑用户控件:如果你的控件只在一个项目内使用,那么创建一个用户控件 (
.ascx文件) 通常是更简单、更快捷的选择,用户控件不能被其他项目重用。 - 何时选择自定义服务器控件:当你需要跨项目、跨团队重用,或者需要提供非常丰富的设计时体验时,才选择创建自定义服务器控件。
- 性能考虑:自定义服务器控件会增加页面的处理开销,因为它们需要被实例化、初始化和呈现,避免创建过于复杂或重量级的控件。
- 文档:为你的控件编写清晰的 XML 注释,这样其他开发者在使用时可以获得智能感知提示。
- 测试:务必对你的控件进行充分的单元测试和集成测试,尤其是在处理回发和状态管理时。
希望这份详细的指南能帮助你理解并开始使用 ASP.NET 自定义服务器控件!
