创建简单的自定义控件

本文介绍如何创建自定义 Windows 窗体控件。 本文开发的简单控件会在控件的左侧、居中或右侧输出控件的 Text。 可以更改文本的对齐方式。 此控件不会引发或处理事件。

本文介绍如何执行以下操作:

  • 添加属性和字段来处理文本的水平对齐设置。
  • 用于 OnTextChanged 使控件失效。
  • OnPaint 方法中提供代码以在控件表面上绘制文本。

添加自定义控件

第一步是向项目添加自定义控件。

  1. 在 Visual Studio 中,找到 “解决方案资源管理器” 窗口。 右键单击项目,然后选择“ 添加新>”。

    Visual Studio 的图像。在“解决方案资源管理器”窗口中,右键单击项目以显示菜单。在菜单中突出显示的是“添加”菜单项,该菜单项已展开,显示子菜单。在子菜单中,突出显示了“新建项”菜单项。

  2. 搜索 自定义控件 并选择它。

  3. 将文件名设置为 FirstControl ,然后选择“ 添加”。

  4. 如果控件 的设计 模式可见,请切换到代码视图。 按 F7 或选择 切换到代码视图 链接。

    小窍门

    还可以右键单击 解决方案资源管理器 窗口中的文件,然后选择“ 查看代码”。

现在应查看自定义控件的源代码,该代码片段类似于以下代码片段:

public partial class FirstControl : Control
{
    public FirstControl()
    {
        InitializeComponent();
    }

    protected override void OnPaint(PaintEventArgs pe)
    {
        base.OnPaint(pe);
    }
}
Public Class FirstControl

    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaint(e)

        'Add your custom paint code here
    End Sub

End Class

添加属性

在名为 . TextAlignment. 的控件上创建新属性。 此属性将调整在控件上绘制文本的位置。 使用FirstControl类,执行以下步骤:

  1. 添加一个名为 _textAlignment 类型的 HorizontalAlignment字段。

    private HorizontalAlignment _textAlignment = HorizontalAlignment.Left;
    
    Private _textAlignment As HorizontalAlignment = HorizontalAlignment.Left
    
  2. 将字段封装在名为 TextAlignment 的属性中。 设置属性时,调用 Invalidate 方法以强制控件重新重绘自身。

    public HorizontalAlignment TextAlignment
    {
        get => _textAlignment;
        set
        {
            _textAlignment = value;
            Invalidate();
        }
    }
    
    Public Property TextAlignment As HorizontalAlignment
        Get
            Return _textAlignment
        End Get
    
        Set(value As HorizontalAlignment)
            _textAlignment = value
            Invalidate()
        End Set
    End Property
    
  3. 将以下属性添加到属性,以将其与 Visual Studio 中的 “属性” 窗口集成。

    • Category— 应用于属性的类别。

    • Description— 属性的说明。

    • DefaultValue— 属性的默认值。

      默认值使属性可由设计器 重置 。 它还帮助确定何时应将属性序列化到代码后置中,因为默认值不会被序列化。

    [System.ComponentModel.Category("Alignment"),
    System.ComponentModel.Description("Specifies the alignment of text."),
    System.ComponentModel.DefaultValue(HorizontalAlignment.Left)]
    public HorizontalAlignment TextAlignment
    
    <System.ComponentModel.Category("Alignment"),
    System.ComponentModel.Description("Specifies the alignment of text."),
    System.ComponentModel.DefaultValue(HorizontalAlignment.Left)>
    Public Property TextAlignment As HorizontalAlignment
    

处理修改过的文本

属性 TextAlignment 调用 Invalidate 从而使控件重绘。 这可确保在呈现 Text 控件时立即使用正确的对齐方式。 但是,如果 Text 属性发生更改,则不会更新任何内容,因为 Text 不会调用 Invalidate。 不过,该属性确实会调用 OnTextChanged 方法,你可以重写这个方法以调用 Invalidate,从而强制控件重新绘制自身。

FirstControl类中,执行以下步骤:

  1. 重写 OnTextChanged 方法。
  2. 调用 base.OnTextChanged 以便 TextChanged 按控件使用者的预期方式引发事件。
  3. 调用Invalidate方法以强制重新绘制。

代码应类似于以下代码片段:

protected override void OnTextChanged(EventArgs e)
{
    base.OnTextChanged(e);
    Invalidate();
}
Protected Overrides Sub OnTextChanged(e As EventArgs)
    MyBase.OnTextChanged(e)
    Invalidate()
End Sub

绘制控件

自定义控件的最后一步是绘制。 使用FirstControl类,执行以下步骤:

  1. 找到模板生成的OnPaint方法。 如果缺少,请从基类重写它。

  2. 创建名为 StringFormat 的新 style 变量。

    StringFormat style = new();
    
    Dim style As New StringFormat
    

    System.Drawing.StringFormat 类型封装文本布局信息,并提供对齐方式的访问权限。

  3. 基于 TextAlignment该属性,将 style.Alignment 属性设置为适当的值。

    style.Alignment = TextAlignment switch
    {
        // Map the HorizontalAlignment enum to the StringAlignment enum
        HorizontalAlignment.Left => StringAlignment.Near,
        HorizontalAlignment.Right => StringAlignment.Far,
        HorizontalAlignment.Center => StringAlignment.Center,
        
        // Default to Near alignment
        _ => StringAlignment.Near
    };
    
    'Map the HorizontalAlignment enum to the StringAlignment enum
    Select Case TextAlignment
        Case HorizontalAlignment.Left
            style.Alignment = StringAlignment.Near
        Case HorizontalAlignment.Right
            style.Alignment = StringAlignment.Far
        Case HorizontalAlignment.Center
            style.Alignment = StringAlignment.Center
    End Select
    
  4. Graphics.DrawString 绘制 Text 属性。

    // Create the brush and automatically dispose it.
    using SolidBrush foreBrush = new(ForeColor);
    
    // Call the DrawString method to write text.
    // Text, Font, and ClientRectangle are inherited properties.
    pe.Graphics.DrawString(Text, Font, foreBrush, ClientRectangle, style);
    
    'Create the brush and automatically dispose it.
    Using foreBrush As New SolidBrush(ForeColor)
        'Call the DrawString method to write text.
        'Text, Font, and ClientRectangle are inherited properties.
        e.Graphics.DrawString(Text, Font, foreBrush, ClientRectangle, style)
    End Using
    

    重要

    Graphics.DrawString 方法使用 Brush 来设定文本的颜色。 Brushes 在使用后必须处置。

    该方法 Graphics.DrawString 使用文本、字体、颜色和格式设置选项来绘制字符串。

  5. 为了确保 Paint 事件被触发,请调用 base.OnPaint

    base.OnPaint(pe);
    
    MyBase.OnPaint(e)
    
  6. 保存代码文件并编译项目。 编译项目后,Visual Studio 会在打开视觉设计器时将自定义控件添加到工具箱窗口中。

代码应类似于以下代码片段:

public partial class FirstControl : Control
{
    private HorizontalAlignment _textAlignment = HorizontalAlignment.Left;

    [System.ComponentModel.Category("Alignment"),
    System.ComponentModel.Description("Specifies the alignment of text."),
    System.ComponentModel.DefaultValue(HorizontalAlignment.Left)]
    public HorizontalAlignment TextAlignment
    {
        get => _textAlignment;
        set
        {
            _textAlignment = value;
            Invalidate();
        }
    }

    public FirstControl()
    {
        InitializeComponent();
    }

    protected override void OnTextChanged(EventArgs e)
    {
        base.OnTextChanged(e);
        Invalidate();
    }

    protected override void OnPaint(PaintEventArgs pe)
    {
        StringFormat style = new();

        style.Alignment = TextAlignment switch
        {
            // Map the HorizontalAlignment enum to the StringAlignment enum
            HorizontalAlignment.Left => StringAlignment.Near,
            HorizontalAlignment.Right => StringAlignment.Far,
            HorizontalAlignment.Center => StringAlignment.Center,
            
            // Default to Near alignment
            _ => StringAlignment.Near
        };

        // Create the brush and automatically dispose it.
        using SolidBrush foreBrush = new(ForeColor);

        // Call the DrawString method to write text.
        // Text, Font, and ClientRectangle are inherited properties.
        pe.Graphics.DrawString(Text, Font, foreBrush, ClientRectangle, style);

        base.OnPaint(pe);
    }
}
Public Class FirstControl

    Private _textAlignment As HorizontalAlignment = HorizontalAlignment.Left

    <System.ComponentModel.Category("Alignment"),
    System.ComponentModel.Description("Specifies the alignment of text."),
    System.ComponentModel.DefaultValue(HorizontalAlignment.Left)>
    Public Property TextAlignment As HorizontalAlignment
        Get
            Return _textAlignment
        End Get

        Set(value As HorizontalAlignment)
            _textAlignment = value
            Invalidate()
        End Set
    End Property

    Protected Overrides Sub OnTextChanged(e As EventArgs)
        MyBase.OnTextChanged(e)
        Invalidate()
    End Sub

    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        Dim style As New StringFormat

        'Map the HorizontalAlignment enum to the StringAlignment enum
        Select Case TextAlignment
            Case HorizontalAlignment.Left
                style.Alignment = StringAlignment.Near
            Case HorizontalAlignment.Right
                style.Alignment = StringAlignment.Far
            Case HorizontalAlignment.Center
                style.Alignment = StringAlignment.Center
        End Select

        'Create the brush and automatically dispose it.
        Using foreBrush As New SolidBrush(ForeColor)
            'Call the DrawString method to write text.
            'Text, Font, and ClientRectangle are inherited properties.
            e.Graphics.DrawString(Text, Font, foreBrush, ClientRectangle, style)
        End Using
        MyBase.OnPaint(e)
    End Sub

End Class