自定义控件
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);