凌峰创科服务平台

asp.net自定义服务器控件

目录

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

为什么需要自定义服务器控件?

当你发现以下情况时,就应该考虑创建自定义服务器控件:

asp.net自定义服务器控件-图1
(图片来源网络,侵删)
  • 代码重用:你需要在多个页面或多个项目中重复使用一段功能相似的 UI 和逻辑,一个标准化的“搜索框”或“用户信息卡片”。
  • 封装复杂性:一个功能由多个 ASP.NET 控件组合而成,并涉及复杂的后台逻辑,将这些细节封装起来,可以让页面开发者像使用一个普通控件(如 ButtonGridView)一样简单。
  • 统一外观和行为:为了保持整个网站 UI/UX 的一致性,你可以创建自定义控件来强制执行统一的样式、布局和验证规则。
  • 扩展功能:你需要修改或扩展现有 ASP.NET 控件的功能,为 TextBox 添加水印提示,或为 Button 添加一个确认对话框。

自定义服务器控件的类型

主要分为以下三类:

a. 复合控件

这是最常见的一种,它不直接渲染 HTML,而是通过组合和编排其他现有服务器控件来构建其 UI。

  • 特点
    • 继承自 System.Web.UI.WebControls.CompositeControlSystem.Web.UI.WebControls.WebControl
    • CreateChildControls 方法中创建并添加子控件。
    • 适合构建复杂的、由多个部分组成的 UI 组件。
  • 示例:一个包含 LabelTextBoxRequiredFieldValidator 的“带验证的登录框”控件。

b. 派生控件

这种控件继承自一个现有的 ASP.NET 控件(如 TextBox, Button, GridView),并在此基础上添加新功能或修改现有行为。

  • 特点
    • 继承自具体的控件类,如 System.Web.UI.WebControls.TextBox
    • 通过重写 RenderContentsRender 方法来改变其输出的 HTML。
    • 通过重写事件处理方法(如 OnLoad, OnPreRender)来修改其生命周期行为。
  • 示例:一个 WatermarkedTextBox,它继承自 TextBox,并在为空时显示灰色水印文本。

c. 非视觉控件

这种控件不产生任何可见的 UI,主要用于在页面生命周期中执行任务。

asp.net自定义服务器控件-图2
(图片来源网络,侵删)
  • 特点
    • 继承自 System.Web.UI.Control
    • 通常用于处理数据、管理资源或与其他控件交互。
  • 示例AdRotator(虽然它有输出,但逻辑更重)、Xml 控件等。

创建一个简单的复合控件(示例)

我们将创建一个名为 SearchBox 的复合控件,它包含一个 TextBox、一个 Button 和一个 Click 事件。

步骤 1:创建类库项目

  1. 打开 Visual Studio。
  2. 创建新项目 -> 选择 "类库" (.NET Framework) 模板。
  3. 将项目命名为 MyCustomControls
  4. 删除默认的 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:添加属性和事件

我们需要一个属性来获取搜索文本,以及一个事件来通知页面按钮被点击了。

asp.net自定义服务器控件-图3
(图片来源网络,侵删)
// 公共属性,用于获取/设置搜索文本
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:注册并使用控件

  1. 生成 DLL:右键点击 MyCustomControls 项目 -> "生成" (Build),这会在 bin/Debug (或 bin/Release) 文件夹中生成 MyCustomControls.dll

  2. 创建一个测试网站

    • 在同一个解决方案中,添加一个新的 "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 扩展名)。
  3. 在页面中使用: 打开一个 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>
  4. 编写事件处理代码: 在 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 来在用户点击/输入时隐藏水印,在失去焦点且文本为空时显示水印。


高级主题

  • 视态管理:使用 WebControlVisible 属性可以控制控件的显示和隐藏。CompositeControl 内部会处理子控件的 Visible 属性,确保它们不会被渲染。
  • 回发事件处理:对于复合控件,通常需要将子控件的事件(如 Button.Click)“冒泡”到父控件的自定义事件上,如我们在 SearchBox 示例中所做的那样。
  • 控件状态ViewState 是 ASP.NET 控件用来在回发之间保持其状态(如属性值)的机制,自定义属性需要将值存储在 ViewState 字典中,如 Watermark 属性所示。
  • 设计时支持:为了让控件在 Visual Studio 的设计视图中正常显示和提供智能感知,你可以使用 [Browsable(true)][DefaultValue("")][Description("...")] 等特性,并实现 ControlDesigner 类,这是一个更高级的话题。

最佳实践与注意事项

  1. 优先考虑用户控件:如果你的控件只在一个项目内使用,那么创建一个用户控件 (.ascx 文件) 通常是更简单、更快捷的选择,用户控件不能被其他项目重用。
  2. 何时选择自定义服务器控件:当你需要跨项目、跨团队重用,或者需要提供非常丰富的设计时体验时,才选择创建自定义服务器控件。
  3. 性能考虑:自定义服务器控件会增加页面的处理开销,因为它们需要被实例化、初始化和呈现,避免创建过于复杂或重量级的控件。
  4. 文档:为你的控件编写清晰的 XML 注释,这样其他开发者在使用时可以获得智能感知提示。
  5. 测试:务必对你的控件进行充分的单元测试和集成测试,尤其是在处理回发和状态管理时。

希望这份详细的指南能帮助你理解并开始使用 ASP.NET 自定义服务器控件!

分享:
扫描分享到社交APP
上一篇
下一篇