跳转至

自定义控件

WinForms 提供了丰富的内置控件,足以满足大部分常规开发需求。但有时,我们需要根据特定业务场景开发特殊用途的自定义控件。例如,在工业控制或设备模拟软件中,标准的按钮和文本框无法满足需求,这时就需要我们自己绘制控件。

GDI+ 和 Graphics 对象

WinForms 中所有控件的视觉界面都是通过 GDI+ (Graphics Device Interface) 绘制的。核心绘图类是 System.Drawing.Graphics

要创建一个自定义控件,我们通常继承自 System.Windows.Forms.Control 类,并重写其 OnPaint 方法。OnPaint 方法在控件需要被重绘时由系统自动调用。

OnPaint 方法的参数 PaintEventArgs 中包含一个 Graphics 对象,它就是我们的“画布”,提供了绘制图形和文本的所有方法。

绘制基础

绘制线条

下面的代码通过继承 Control 创建了一个自定义控件,并重写 OnPaint 方法来调用 Graphics 对象的 DrawLine 方法画一条线。

using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;

// 自定义一个 Control 的子类
public class MyWidget : Control
{
    // 重写 OnPaint 方法,当控件需要重绘时,系统会调用此方法
    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e); // 调用基类的方法

        Graphics g = e.Graphics;

        // 设置抗锯齿,使线条更平滑
        g.SmoothingMode = SmoothingMode.AntiAlias;

        // 创建一支黑色的笔,宽度为1像素
        Pen blackPen = new Pen(Color.Black, 1);

        // 画一条线,起点坐标 (50, 80),终点坐标 (200, 300)
        g.DrawLine(blackPen, 50, 80, 200, 300);

        // 释放资源
        blackPen.Dispose();
    }
}

// --- 如何使用 ---
// 在你的 Form 中:
// var myWidget = new MyWidget();
// myWidget.Dock = DockStyle.Fill;
// this.Controls.Add(myWidget);

画笔 (Pen) - 指定粗细和颜色

System.Drawing.Pen 对象用于定义线条的颜色、宽度和样式。我们可以创建一个 Pen 对象并用它来绘制。

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
    Graphics g = e.Graphics;
    g.SmoothingMode = SmoothingMode.AntiAlias;

    // 创建一支红色的笔,宽度为3像素
    Pen redPen = new Pen(Color.Red, 3);

    g.DrawLine(redPen, 50, 80, 200, 300);

    redPen.Dispose();
}

绘制矩形

调用 Graphics 对象的 DrawRectangle 方法可以绘制一个矩形轮廓。

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
    Graphics g = e.Graphics;

    Pen blackPen = new Pen(Color.Black, 1);

    // 画一个矩形,左上角坐标 (50, 50)
    // 宽度300像素,高度300像素
    g.DrawRectangle(blackPen, 50, 50, 300, 300);

    blackPen.Dispose();
}

绘制椭圆和圆形

调用 DrawEllipse 方法可以绘制一个内切于指定矩形区域的椭圆。如果矩形的宽高相等,则绘制出来的是一个正圆。

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
    Graphics g = e.Graphics;
    g.SmoothingMode = SmoothingMode.AntiAlias;

    Pen blackPen = new Pen(Color.Black, 1);

    // 为了参照,先画出外切矩形
    g.DrawRectangle(blackPen, 50, 50, 300, 200);

    // 画一个椭圆
    g.DrawEllipse(blackPen, 50, 50, 300, 200);

    blackPen.Dispose();
}

画刷 (Brush) - 填充图形

前面的 Draw... 方法只绘制轮廓。要填充图形内部,需要使用 Fill... 方法,并提供一个 Brush 对象。System.Drawing.Brush 是所有画刷的基类,最常用的是 SolidBrush,用于纯色填充。

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
    Graphics g = e.Graphics;
    g.SmoothingMode = SmoothingMode.AntiAlias;

    // 创建一个黄色的画刷
    SolidBrush yellowBrush = new SolidBrush(Color.Yellow);

    // 创建一支黑色的笔
    Pen blackPen = new Pen(Color.Black, 1);

    // 填充并绘制矩形和圆形
    g.FillRectangle(yellowBrush, 50, 50, 100, 100);
    g.DrawRectangle(blackPen, 50, 50, 100, 100);

    g.FillEllipse(yellowBrush, 200, 200, 100, 100);
    g.DrawEllipse(blackPen, 200, 200, 100, 100);

    yellowBrush.Dispose();
    blackPen.Dispose();
}

绘制文字

使用 DrawString 方法可以在控件上绘制文字。你需要指定文本内容、字体 (Font)、颜色 (Brush) 和位置。

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
    Graphics g = e.Graphics;

    // 定义字体
    Font drawFont = new Font("Microsoft YaHei", 20);

    // 定义画刷
    SolidBrush drawBrush = new SolidBrush(Color.Red);

    // 在坐标 (100, 100) 处绘制文字
    g.DrawString("你好,白月黑羽!", drawFont, drawBrush, 100, 100);

    drawFont.Dispose();
    drawBrush.Dispose();
}

自定义控件示例

现在,我们可以利用上述绘图知识,创建一个模拟充电桩插座的自定义控件。

using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;

public class OutletControl : Control
{
    private Color _outletColor;

    public Color OutletColor
    {
        get { return _outletColor; }
        set
        {
            _outletColor = value;
            this.Invalidate(); // 请求重绘控件
        }
    }

    public OutletControl()
    {
        this.OutletColor = Color.Green;
        // 控件设置为固定大小,以包含所有绘制的图形
        this.Size = new Size(70, 70);
        // 设置双缓冲,减少闪烁
        this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        Graphics g = e.Graphics;
        g.SmoothingMode = SmoothingMode.AntiAlias;

        // 使用 using 语句可以自动管理资源释放
        using (Pen borderPen = new Pen(this.OutletColor, 1))
        {
            g.DrawRectangle(borderPen, 0, 0, 60, 60);
        }

        using (Pen linePen = new Pen(this.OutletColor, 3))
        {
            g.DrawLine(linePen, 12, 10, 12, 25);
            g.DrawLine(linePen, 30, 10, 30, 25);
            g.DrawLine(linePen, 48, 10, 48, 25);

            g.DrawLine(linePen, 10, 35, 20, 50);
            g.DrawLine(linePen, 50, 35, 40, 50);
        }
    }
}

// --- 如何在 Form 中使用 ---
// var outlet1 = new OutletControl();
// outlet1.Location = new Point(10, 10);
// outlet1.OutletColor = Color.Green;

// var outlet2 = new OutletControl();
// outlet2.Location = new Point(100, 10);
// outlet2.OutletColor = Color.Red;

// this.Controls.Add(outlet1);
// this.Controls.Add(outlet2);