如何创建用户控件

本文介绍如何向项目添加用户控件,然后将该用户控件添加到窗体。 创建一个可重用的用户控件,并使其兼具视觉吸引力和功能性。 新控件对 TextBox 控件和 Button 控件进行分组。 当用户选择按钮时,将清除文本框中的文本。 有关用户控件的详细信息,请参阅用户控件概述

了解用户控制使用者

在本文中,术语 使用者 指使用用户控件的任何代码。 这包括:

  • 包含用户控件的窗体
  • 托管您的用户控件的其他控件
  • 引用用户控件库的应用程序

创建用户控件时,将生成可重用组件。 消费者是通过将组件放在窗体上、设置其属性或响应其事件来使用该组件的人。 使用者无需了解构成用户控件的内部控件(如 TextBoxButton),它们仅与你选择公开的属性和事件进行交互。

用户控件的基本代码模式

在添加详细实现之前,了解用户控件的最低可行代码模式会很有帮助。 用户控件的核心是:

  • 事件转发 - 将事件从内部控件传递到使用者。
  • 属性公开 - 允许使用者访问内部控制属性。
  • 逻辑行为 - 处理内部控件之间的交互。

以下代码演示了这些模式。 基本用户控件不需要所有这些代码,但这些模式有助于创建与设计器和使用者应用程序很好地集成的专业可重用组件。

添加新的用户控件

在 Visual Studio 中打开Windows 窗体项目后,使用 Visual Studio 模板创建用户控件:

  1. 在 Visual Studio 中,找到 “解决方案资源管理器” 窗口。 右键单击项目并选择“添加”>“用户控件(Windows 窗体)”。

    右键单击 Visual Studio 解决方案资源管理器,将用户控件添加到 Windows 窗体项目

  2. 控件的名称 设置为 ClearableTextBox,然后按 Add

    在 Visual Studio 中为 Windows 窗体添加项目对话框

创建用户控件后,Visual Studio 将打开设计器:

Visual Studio for Windows Forms 中的用户控件设计器

设计可清除文本框

用户控制由构成控件组成,这些控件是在设计图面上创建的,和设计窗体一样。 按照以下步骤添加和配置用户控件及其构成控件:

  1. 打开设计器后,用户控件设计图面应为选定对象。 如果不是,请单击设计图面以将其选中。 在“属性”窗口中,设置下列属性

    资产 价值
    MinimumSize 84, 53
    尺寸 191, 53
  2. 添加 Label 控件。 设置以下属性:

    资产 价值
    名称 lblTitle
    位置 3, 5
  3. 添加 TextBox 控件。 设置以下属性:

    资产 价值
    名称 txtValue
    锚点 Top, Left, Right
    位置 3, 23
    尺寸 148, 23
  4. 添加 Button 控件。 设置以下属性:

    资产 价值
    名称 btnClear
    锚点 Top, Right
    位置 157, 23
    尺寸 31, 23
    文本

    此控件应如下所示:

    支持 Windows 窗体的 Visual Studio,显示刚刚设计的用户控件。

  5. F7 打开 ClearableTextBox 类的代码编辑器。

  6. 执行以下代码更改:

    1. 在代码文件顶部导入 System.ComponentModel 命名空间。

    2. DefaultEvent 特性添加到该类中。 此属性设置使用者(使用此控件的窗体或应用程序)双击设计器中的控件时生成的事件。 有关属性的详细信息,请参阅特性 (C#)特性概述 (Visual Basic)

      using System.ComponentModel;
      
      namespace UserControlProject
      {
          [DefaultEvent(nameof(TextChanged))]
          public partial class ClearableTextBox : UserControl
      
      Imports System.ComponentModel
      
      <DefaultEvent("TextChanged")>
      Public Class ClearableTextBox
      
    3. 添加一个事件处理程序,将 TextBox.TextChanged 事件转发给用户控件的使用者:

      [Browsable(true)]
      public new event EventHandler? TextChanged
      {
          add => txtValue.TextChanged += value;
          remove => txtValue.TextChanged -= value;
      }
      
      <Browsable(True)>
      Public Shadows Custom Event TextChanged As EventHandler
          AddHandler(value As EventHandler)
              AddHandler txtValue.TextChanged, value
          End AddHandler
          RemoveHandler(value As EventHandler)
              RemoveHandler txtValue.TextChanged, value
          End RemoveHandler
          RaiseEvent(sender As Object, e As EventArgs)
      
          End RaiseEvent
      End Event
      

      请注意,此事件在其上声明了 Browsable 属性。 将 Browsable 应用于事件或属性后,当在设计器中选择该控件时,它可以控制该项在“属性”窗口中是否可见。 在本例中,true 作为参数传递给指示事件应可见的属性。

    4. 添加名为Text的字符串属性,用于向用户控件的使用者公开TextBox.Text属性。

      [Browsable(true)]
      [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
      public new string Text
      {
          get => txtValue.Text;
          set => txtValue.Text = value;
      }
      
      <Browsable(True)>
      <DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)>
      Public Shadows Property Text() As String
          Get
              Return txtValue.Text
          End Get
          Set(value As String)
              txtValue.Text = value
          End Set
      End Property
      
    5. 添加一个名为Title的字符串属性,以便向用户控件的使用者公开Label.Text属性。

      [Browsable(true)]
      [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
      public string Title
      {
          get => lblTitle.Text;
          set => lblTitle.Text = value;
      }
      
      <Browsable(True)>
      <DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)>
      Public Property Title() As String
          Get
              Return lblTitle.Text
          End Get
          Set(value As String)
              lblTitle.Text = value
          End Set
      End Property
      
  7. 切换回 ClearableTextBox 设计器,然后双击 btnClear 控件以生成 Click 事件的处理程序。 为处理程序添加以下代码,以清除 txtValue 文本框:

    private void btnClear_Click(object sender, EventArgs e) =>
        Text = "";
    
    Private Sub btnClear_Click(sender As Object, e As EventArgs)
        txtValue.Text = ""
    End Sub
    
  8. 最后,在解决方案资源管理器窗口中,右键单击项目并选择“生成”以生成项目。 应不会有任何错误,生成完成后,控件 ClearableTextBox 会显示在工具箱中以供使用。

下一步是使用窗体中的控件。

示例应用程序

如果您在上一节中创建了一个新项目,那么您将有一个名为Form的空白;否则,请创建一个新窗体。

  1. 解决方案资源管理器窗口中,双击窗体,打开设计器。 窗体的设计图面应为选中状态。

  2. 将窗体 Size 的属性设置为 432, 315.

  3. 打开工具箱窗口,然后双击 ClearableTextBox 控件。 此控件应列在以项目命名的部分下。

  4. 再次双击 ClearableTextBox 控件以生成第二个控件。

  5. 返回到设计器并将控件分开,以便完全显示它们。

  6. 选择一个控件并设置以下属性:

    资产 价值
    名称 ctlFirstName
    位置 12, 12
    尺寸 191, 53
    标题 First Name
  7. 选择另一个控件并设置以下属性:

    资产 价值
    名称 ctlLastName
    位置 12, 71
    尺寸 191, 53
    标题 Last Name
  8. 返回 “工具箱” 窗口,向窗体添加标签控件,并设置以下属性:

    资产 价值
    名称 lblFullName
    位置 12, 252
  9. 接下来,需要为这两个用户控件生成事件处理程序。 在设计器中,双击 ctlFirstName 控件。 此操作将生成 TextChanged 事件的事件处理程序,并打开代码编辑器。

  10. 切换回设计器并双击 ctlLastName 控件以生成第二个事件处理程序。

  11. 切换回设计器并双击窗体的标题栏。 此操作会生成 Load 事件的事件处理程序。

  12. 在代码编辑器中,添加 UpdateNameLabel 方法。 此方法将合并两个名称以创建一条消息,并将该消息分配给 lblFullName 控件。

    private void UpdateNameLabel()
    {
        if (string.IsNullOrWhiteSpace(ctlFirstName.Text) || string.IsNullOrWhiteSpace(ctlLastName.Text))
            lblFullName.Text = "Please fill out both the first name and the last name.";
        else
            lblFullName.Text = $"Hello {ctlFirstName.Text} {ctlLastName.Text}, I hope you're having a good day.";
    }
    
    Private Sub UpdateNameLabel()
        If String.IsNullOrWhiteSpace(ctlFirstName.Text) Or String.IsNullOrWhiteSpace(ctlLastName.Text) Then
            lblFullName.Text = "Please fill out both the first name and the last name."
        Else
            lblFullName.Text = $"Hello {ctlFirstName.Text} {ctlLastName.Text}, I hope you're having a good day."
        End If
    End Sub
    
  13. 对于这两个 TextChanged 事件处理程序,请调用 UpdateNameLabel 方法:

    private void ctlFirstName_TextChanged(object sender, EventArgs e) =>
        UpdateNameLabel();
    
    private void ctlLastName_TextChanged(object sender, EventArgs e) =>
        UpdateNameLabel();
    
    Private Sub ctlFirstName_TextChanged(sender As Object, e As EventArgs) Handles ctlFirstName.TextChanged
        UpdateNameLabel()
    End Sub
    
    Private Sub ctlLastName_TextChanged(sender As Object, e As EventArgs) Handles ctlLastName.TextChanged
        UpdateNameLabel()
    End Sub
    
  14. 最后,在窗体的UpdateNameLabel事件中调用Load方法。

    private void Form1_Load(object sender, EventArgs e) =>
        UpdateNameLabel();
    
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        UpdateNameLabel()
    End Sub
    

运行项目并输入名字和姓氏:

Windows 窗体应用,其中包含使用用户控件和标签创建的两个文本框。

请尝试按 按钮重置其中一个文本框。