写在前面
承接篇3。本篇讲 WinForms 自定义绘制的核心:GDI+、
Graphics、OnPaint、双缓冲、自定义控件。
WinForms 的"外观"分两类:控件自带外观(Button 长啥样系统定)和自绘外观(你在 OnPaint 里画)。后者就是 GDI+ 的地盘——画文字、线条、矩形、图片,做仪表盘、图表、自定义按钮。理解 GDI+,你才能做 WinForms 里任何"非标准"的视觉。
一、GDI+ 是什么
GDI+ 是 Windows 的 2D 图形库(Win32 GDI 的升级),System.Drawing 命名空间封装它。核心是 Graphics 对象——你的"画布",所有绘制都通过它。
| |
二、OnPaint:绘制入口
控件每次需要重绘时(首次显示、被遮挡后露出、手动 Invalidate()),触发 Paint 事件,你重写 OnPaint 或挂 Paint 事件来画:
| |
PaintEventArgs 给你 Graphics 和 ClipRectangle(需要重绘的区域,优化用)。
别缓存
Graphics——每次 Paint 的 Graphics 是新的,用完即弃。CreateGraphics()能拿到一个,但别用它做持续绘制(不随重绘刷新,会被擦掉),只在响应鼠标实时画(橡皮线)时用。
三、Graphics 常用方法
| |
四、Pen/Brush/Font:GDI+ 对象必须 Dispose
Pen、Brush、Font、Bitmap 都是非托管资源(包装 GDI 句柄),不用了必须 Dispose,否则句柄泄漏。
| |
| |
经验:用了
new创建的 GDI+ 对象,都要 Dispose。系统内置(Brushes.Xxx/Pens.Xxx/SystemFonts.Xxx)是共享单例,别动。
五、双缓冲:解决闪烁
WinForms 默认直接画到屏幕,复杂绘制会闪烁(先擦白再画,肉眼可见)。解决:双缓冲——先画到内存位图,完成后一次性贴到屏幕。
| |
| |
自定义控件绘制(尤其动画/重绘频繁),默认开双缓冲。
六、自定义控件:重写 OnPaint
做个"圆形按钮"演示自绘:
| |
要点:
- 继承
Control(或Panel/Button),重写OnPaint。 SmoothingMode.AntiAlias开抗锯齿,边缘平滑。ResizeRedraw = true让尺寸变化时重绘。- 所有
new的 GDI+ 对象 Dispose。
七、触发重绘:Invalidate
OnPaint 只在系统认为需要重绘时调用。你改了数据想让画面更新,要主动触发:
| |
Invalidate是异步的(标记脏,等下次 WM_PAINT 消息处理)。要立即刷新用Update()(立即处理重绘),但通常不必。
八、DPI 感知
高 DPI 屏幕(4K 笔记本),WinForms 默认会模糊或错位。.NET 8 + WinForms 改进:app.manifest 设 DPI 感知。
| |
PerMonitorV2 是现代标准(每个显示器独立 DPI)。.NET 8 下 WinForms 对 PerMonitorV2 支持大幅改善。绘制时用
DeviceDpi缩放坐标/字号,别写死像素。
九、GDI+ vs 其他
| |
十、踩坑速记
| |
十一、小结
| |
下一篇:数据绑定——BindingSource、BindingList<T>、DataGridView、INotifyPropertyChanged,WinForms 比 WPF 弱但仍能用的绑定体系。