在 Windows Forms 里,OwnerDraw(自绘) 是提升性能和灵活控制 UI 的关键技术之一,尤其适合你关心的"改样式但不影响性能"。
我直接给你讲清楚它的原理 + 使用方式 + 性能注意点。
一、OwnerDraw 是什么(本质)
OwnerDraw = 控件不再用系统默认绘制,而是你自己画
👉 系统默认:
- Button / ListBox / ComboBox → Windows 负责画
- 样式受限(颜色、边框、主题)
👉 OwnerDraw:
- 你接管绘制(GDI+)
- 可以完全自定义 UI(类似轻量级前端)
二、典型支持 OwnerDraw 的控件
WinForms 原生支持:
ListBoxComboBoxMenuItemTabControl(部分)ListView(高级)
三、最常见示例(ListBox OwnerDraw)
1️⃣ 开启 OwnerDraw
csharp
listBox1.DrawMode = DrawMode.OwnerDrawFixed;
2️⃣ 处理绘制事件
csharp
private void listBox1_DrawItem(object sender, DrawItemEventArgs e)
{
if (e.Index < 0) return;
e.DrawBackground();
string text = listBox1.Items[e.Index].ToString();
// 自定义颜色
Brush brush = ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
? Brushes.White
: Brushes.Black;
e.Graphics.DrawString(text, e.Font, brush, e.Bounds);
e.DrawFocusRectangle();
}
四、OwnerDraw 两种模式区别
1️⃣ OwnerDrawFixed
- 每一项高度固定
- 性能最好 ✅
- 推荐默认用这个
2️⃣ OwnerDrawVariable
- 每项高度不同
- 需要处理
MeasureItem - 性能稍差
五、性能优化关键点(重点)
✅ 1. 不要在 DrawItem 里 new 对象
错误:
csharp
Brush b = new SolidBrush(Color.Red); // ❌ 每次绘制都创建
优化:
csharp
private static readonly Brush redBrush = Brushes.Red;
✅ 2. 不要做业务逻辑
DrawItem 应该是"纯绘制"
❌ 错误:
csharp
var data = LoadFromDB(); // 绝对不要
✔ 正确:
- 数据提前准备好
- DrawItem 只读取
✅ 3. 减少字符串处理
csharp
// ❌ 每次 ToString / 拼接
string text = item.Name + " - " + item.Id;
👉 建议:
- 提前缓存显示文本
✅ 4. 控制重绘范围
不要动不动:
csharp
listBox1.Refresh(); // ❌
👉 用:
csharp
listBox1.Invalidate(rect); // 局部刷新
✅ 5. 避免透明和复杂渐变
GDI+ 中:
- 渐变刷(LinearGradientBrush)成本高
- 半透明更慢
👉 高性能方案:
- 纯色 + 简单边框
六、进阶:绘制更复杂 UI(图标 + 多行)
csharp
var item = (MyItem)listBox1.Items[e.Index];
// 图标
e.Graphics.DrawImage(item.Icon, e.Bounds.Left, e.Bounds.Top);
// 主标题
e.Graphics.DrawString(item.Title, boldFont, Brushes.Black, e.Bounds.Left + 40, e.Bounds.Top);
// 副标题
e.Graphics.DrawString(item.SubTitle, smallFont, Brushes.Gray, e.Bounds.Left + 40, e.Bounds.Top + 20);
👉 这就是"模拟现代 UI"的方式
七、OwnerDraw vs 普通控件(性能对比)
| 方式 | 控件数量 | 灵活性 | 性能 |
|---|---|---|---|
| 默认控件 | 多 | 低 | ❌ |
| 修改属性 | 多 | 中 | ⚠️ |
| OwnerDraw | 少 | 高 | ✅ |
👉 核心优势:
用一个 ListBox ≈ 替代几十个 Label
八、什么时候必须用 OwnerDraw?
建议使用场景:
✔ 大量列表(上百项)
✔ 自定义样式(颜色/图标/多行)
✔ 高频刷新 UI
✔ 想避免卡顿
九、一句话总结
OwnerDraw 的本质是:用绘制换控件数量 → 用 CPU 换 GDI 句柄 → 性能更可控