C# GDI+绘图方法 C#如何实现图形绘制

来源:这里教程网 时间:2026-02-21 17:40:43 作者:

Graphics 对象从哪来:别直接 new

直接调用

new Graphics()
会抛出
NotSupportedException
——
Graphics
不是普通类,它必须绑定到一个有效的绘图表面。常见合法来源只有三个:
Control.CreateGraphics()
、重写
OnPaint
中的
e.Graphics
、或从
Bitmap
创建。

Control.CreateGraphics()
返回的
Graphics
是临时的,不参与双缓冲,窗口重绘时内容会丢失;仅适合调试或一次性绘制
真正稳定的绘图入口是重写控件的
OnPaint
方法,使用参数
PaintEventArgs e
中的
e.Graphics
离屏绘制(如生成图片)要用
Graphics.FromImage(bitmap)
,记得之后调用
graphics.Dispose()

DrawLine 和 FillRectangle 的坐标系陷阱

GDI+ 默认以控件左上角为原点 (0, 0),X 向右递增,Y 向下递增——这和数学坐标系相反,但和 Windows 窗口坐标一致。容易出错的是:线条宽度影响实际绘制范围,且

FillRectangle
填充的是「内部」,而
DrawRectangle
描边是以边线中心为基准。

DrawLine(pen, 0, 0, 100, 100)
:线段起点在 (0,0),终点在 (100,100),但若
pen.Width = 3
,实际像素会覆盖从 y=−1 到 y=+2 的区域
FillRectangle(brush, 10, 10, 50, 30)
:填充区域为 x∈[10,60), y∈[10,40) —— 宽高是「内尺寸」,不包含右/下边界像素
抗锯齿开关:用
graphics.SmoothingMode = SmoothingMode.AntiAlias
可柔化斜线和圆弧,但会轻微降低性能

Pen 和 Brush 必须手动释放

Pen
Brush
类型都实现了
IDisposable
,内部持有 GDI 句柄。不释放会导致句柄泄漏,程序运行一段时间后可能抛出
OutOfMemoryException
或绘图失败。

避免在循环中反复
new SolidBrush(Color.Red)
;应复用或用
using
包裹
系统预定义画刷(如
Brushes.Red
)是静态只读对象,不用释放;但自定义
new LinearGradientBrush(...)
必须
Dispose()
典型安全写法:
using (var pen = new Pen(Color.Blue, 2f))
{
    g.DrawLine(pen, 0, 0, 100, 100);
}

双缓冲没开?闪烁就是它

OnPaint
中直接绘图却没启用双缓冲,控件在重绘时会出现明显闪烁,尤其当背景清空和图形绘制分两步进行时。

WinForms 中最简单开启方式:
this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true)
,建议在构造函数末尾调用
不要手动调用
g.Clear(Color.White)
再画内容——Clear 本身就会触发一次屏幕刷新;应让背景绘制和前景绘制在同一个缓冲区完成
若使用
CreateGraphics()
,双缓冲对其无效,因为它绕过了控件的绘制管道
GDI+ 绘图真正的难点不在 API 调用,而在资源生命周期管理与绘制时机控制。一个没
Dispose
Pen
,可能让程序跑半天才崩;一次没走
OnPaint
CreateGraphics
,会让动画看起来像幻灯片。

相关推荐