drawline需在onpaint中用e.graphics调用并配合using释放pen,禁用creategraphics;坐标基于clientsize;鼠标绘图须通过invalidate触发重绘。

DrawLine 是 Windows Forms 中最直接、最常用的画线方法,但它不是“调了就能稳定显示”的万能函数——用错时机、错的 Graphics 来源、漏掉资源释放,都会导致线条不出现、闪烁、或只闪一下就消失。
别在 CreateGraphics() 里画线
很多新手一上来就写 this.CreateGraphics().DrawLine(...),结果鼠标一动、窗体一最小化再还原,线就没了。这是因为 CreateGraphics() 返回的是临时设备上下文,不参与系统重绘机制,也不受 Paint 事件管理。
正确做法是:把画线逻辑放进 OnPaint 重写方法,或 Paint 事件处理器里,确保每次系统要求重绘时都能重新执行。
- ✅ 正确:在
OnPaint中用e.Graphics - ❌ 危险:用
this.CreateGraphics()后不缓存、不重绘 - ⚠️ 注意:
CreateGraphics()返回的对象不能跨帧复用,也不能长期持有
Pen 对象必须用 using 包裹
Pen 是 GDI+ 资源,底层关联 Windows GDI 句柄。不显式释放会导致句柄泄漏,程序跑久了可能卡死或抛出 OutOfMemoryException(尤其在频繁重绘场景下)。
即使你只画一条线,也得这么写:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
using (Pen pen = new Pen(Color.Red, 2))
{
e.Graphics.DrawLine(pen, 10, 10, 100, 100);
}
}
- ✅ 必须用
using或手动调pen.Dispose() - ❌ 不要写成
Pen pen = new Pen(...); e.Graphics.DrawLine(pen, ...);(没释放) - ⚠️
Pen不是轻量对象,重复 new + 不 dispose 是 WinForms 常见内存隐患
坐标系原点在左上角,且 Y 轴向下增长
这不是 bug,是 GDI+ 的默认行为。如果你习惯数学坐标系(Y 向上),直接传入负值或翻转计算会出错。
例如想从窗体底部往上画一条线,别硬算 y = Height - 50 再传进去——容易漏减边框、标题栏高度;更稳妥的是统一用客户端坐标(ClientRectangle)做参考:
- 起始点
(0, ClientRectangle.Height - 10)是底边向上 10 像素处 - 终点
(ClientRectangle.Width, ClientRectangle.Height - 10)是底边右端 - 所有坐标都应基于
this.ClientSize或e.ClipRectangle,而非Screen或Bounds
需要鼠标拖动画线?别在 MouseMove 里直接 DrawLine
直接在 MouseMove 中调 CreateGraphics().DrawLine() 看似可行,但会严重撕裂、残留旧线、无法响应最小化/遮挡恢复——因为没走重绘管道。
真正可维护的做法是:
- 在
MouseDown记下起点startPoint - 在
MouseMove更新终点endPoint,并调用Invalidate()触发重绘 - 在
OnPaint里用当前startPoint和endPoint画线 - 如果要支持多段线,就用
List<point></point>缓存所有线段端点,OnPaint中遍历绘制
关键点:所有视觉更新必须收敛到 OnPaint,其他事件只负责状态变更和触发重绘。
真正的难点不在“怎么调 DrawLine”,而在于理解 GDI+ 绘图生命周期——它不是立即渲染的命令式 API,而是依赖系统消息循环与重绘契约的协作机制。漏掉 Invalidate、乱用 CreateGraphics、忽略 Dispose,三者任一都会让线条“看起来没画出来”。










