带有LBS_OWNERDRAWFIXED 样式的列表框,系统在什么时候向窗口发送WM_DRAWITEM 和WM_MEASUREITEM消息de呢?
说明:以下内容主要来自腾讯元宝。
以下是 带有 LBS_OWNERDRAWFIXED
样式的列表框触发 WM_MEASUREITEM
和 WM_DRAWITEM
消息的具体时机和场景:
1. WM_MEASUREITEM 消息的触发时机
WM_MEASUREITEM
在以下情况下被系统发送:
**(1) 列表框首次创建时**
- 当窗口过程通过
CreateWindow
创建列表框后,系统会立即发送WM_MEASUREITEM
消息,以确定列表项的尺寸(尤其是高度)。
**(2) 列表框窗口大小调整时**
- 如果列表框的父窗口大小发生变化,导致列表框的布局需要重新计算,系统可能触发
WM_MEASUREITEM
。
**(3) 列表框样式动态修改时**
- 如果运行时修改了列表框的样式(如添加
LBS_OWNERDRAWFIXED
),系统可能发送WM_MEASUREITEM
。
**(4) 通过代码显式触发**
- 调用
InvalidateRect
或UpdateWindow
强制重绘列表框窗口时,可能间接触发WM_MEASUREITEM
。
2. WM_DRAWITEM 消息的触发时机
WM_DRAWITEM
在以下情况下被系统发送:
**(1) 列表框首次显示时**
- 当窗口过程调用
ShowWindow
或UpdateWindow
显示列表框时,系统会触发WM_PAINT
消息,进而引发WM_DRAWITEM
,以绘制所有列表项。
**(2) 列表项内容发生变化时**
- 添加/删除项:调用
LB_ADDSTRING
、LB_DELETESTRING
等函数后,若列表框需要刷新显示,系统会发送WM_DRAWITEM
。 - 示例代码:
SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)_T("中国")); // 添加项 InvalidateRect(hWnd, NULL, FALSE); // 强制重绘,触发 WM_PAINT → WM_DRAWITEM
**(3) 用户交互触发**
- 滚动列表框:滚动时系统会动态发送
WM_DRAWITEM
,仅绘制当前可见区域的项。 - 窗口激活/最小化恢复:窗口状态变化时触发重绘。
**(4) 程序主动触发**
- 通过
SendMessage
发送LB_RESETCONTENT
清空列表框后重新填充数据,强制触发重绘。
3. LBS_OWNERDRAWFIXED 样式的特殊行为
-
固定高度:
LBS_OWNERDRAWFIXED
样式要求所有列表项的高度统一。系统在首次创建列表框时会发送一次WM_MEASUREITEM
,开发者需在此消息中设置固定高度(如lpmis->itemHeight = 30
)。后续即使项的内容变化,也不会重复发送WM_MEASUREITEM
(除非列表框窗口被销毁并重新创建)。 -
自定义绘制:
每次列表项需要显示时(如窗口滚动、刷新),系统都会发送WM_DRAWITEM
,开发者需在此消息中完成背景和文本的自定义绘制。
4. 消息触发流程示例
步骤 1:创建列表框(启用 LBS_OWNERDRAWFIXED
)
hListBox = CreateWindow(
_T("ListBox"), NULL,
WS_CHILD | WS_VISIBLE | WS_VSCROLL | LBS_OWNERDRAWFIXED | LBS_HASSTRINGS,
20, 20, 60, 120,
hWnd, (HMENU)ID_MYLIST,
hInst, NULL);
步骤 2:触发 WM_MEASUREITEM(首次创建时自动触发)
- 系统发送
WM_MEASUREITEM
,窗口过程设置项高度为 30 像素。
步骤 3:触发 WM_DRAWITEM(首次显示时自动触发)
- 系统发送
WM_PAINT
,窗口过程调用DefWindowProc
,进而触发WM_DRAWITEM
,绘制所有列表项。
步骤 4:动态添加项并触发 WM_DRAWITEM
SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)_T("英国"));
InvalidateRect(hWnd, NULL, FALSE); // 触发重绘
5. 对比 LBS_STANDARD 和 LBS_OWNERDRAWFIXED
样式 | WM_MEASUREITEM 触发条件 | WM_DRAWITEM 触发条件 |
---|---|---|
LBS_STANDARD | 从不触发(系统自动管理项尺寸) | 从不触发(系统自动绘制项内容) |
LBS_OWNERDRAWFIXED | 创建时、窗口大小调整时、样式修改时 | 显示时、项变化时、滚动时、程序触发重绘时 |
6. 注意事项
-
必须处理 WM_MEASUREITEM:
若未处理此消息,列表项的高度可能为默认值(基于字体大小),导致布局错乱。 -
必须处理 WM_DRAWITEM:
若未处理此消息,列表项将无法显示内容,甚至导致窗口崩溃。 -
性能优化:
- 在
WM_DRAWITEM
中避免重复创建 GDI 对象(如刷子),可缓存常用资源。 - 仅在必要时调用
InvalidateRect
,避免频繁重绘。
- 在
总结
- WM_MEASUREITEM:用于初始化或调整列表项的高度(仅在
LBS_OWNERDRAWFIXED
或LBS_OWNERDRAWVARIABLE
样式下触发)。 - WM_DRAWITEM:用于自定义绘制列表项的内容(每次项需要显示时触发)。
- LBS_OWNERDRAWFIXED 的列表框需通过这两个消息实现完全的自定义控制。