目录

带有LBS_OWNERDRAWFIXED-样式的列表框,系统在什么时候向窗口发送WM_DRAWITEM-和WM_MEASUREITEM消息de呢

带有LBS_OWNERDRAWFIXED 样式的列表框,系统在什么时候向窗口发送WM_DRAWITEM 和WM_MEASUREITEM消息de呢?

说明:以下内容主要来自腾讯元宝。

以下是 ​ 带有 LBS_OWNERDRAWFIXED 样式的列表框 触发 WM_MEASUREITEMWM_DRAWITEM 消息的具体时机和场景:


1. WM_MEASUREITEM 消息的触发时机

WM_MEASUREITEM 在以下情况下被系统发送:

​**(1) 列表框首次创建时**
  • 当窗口过程通过 CreateWindow 创建列表框 后,系统会立即发送 WM_MEASUREITEM 消息,以确定列表项的尺寸(尤其是高度)。
​**(2) 列表框窗口大小调整时**
  • 如果 列表框的父窗口大小发生变化 ,导致列表框的布局需要重新计算,系统可能触发 WM_MEASUREITEM
​**(3) 列表框样式动态修改时**
  • 如果运行时 修改了列表框的样式 (如添加 LBS_OWNERDRAWFIXED ),系统可能发送 WM_MEASUREITEM
​**(4) 通过代码显式触发**
  • 调用 InvalidateRectUpdateWindow 强制重绘列表框窗口时,可能间接触发 WM_MEASUREITEM

2. WM_DRAWITEM 消息的触发时机

WM_DRAWITEM 在以下情况下被系统发送:

​**(1) 列表框首次显示时**
  • 当窗口过程调用 ShowWindowUpdateWindow 显示列表框 时,系统会触发 WM_PAINT 消息,进而引发 WM_DRAWITEM ,以绘制所有列表项。
​**(2) 列表项内容发生变化时**
  • 添加/删除项 :调用 LB_ADDSTRINGLB_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. 注意事项

  1. 必须处理 WM_MEASUREITEM

    若未处理此消息,列表项的高度可能为默认值(基于字体大小),导致布局错乱。

  2. 必须处理 WM_DRAWITEM

    若未处理此消息,列表项将无法显示内容,甚至导致窗口崩溃。

  3. 性能优化

    • WM_DRAWITEM 中避免重复创建 GDI 对象(如刷子),可缓存常用资源。
    • 仅在必要时调用 InvalidateRect ,避免频繁重绘。

总结

  • WM_MEASUREITEM :用于初始化或调整列表项的高度(仅在 LBS_OWNERDRAWFIXEDLBS_OWNERDRAWVARIABLE 样式下触发)。
  • WM_DRAWITEM :用于自定义绘制列表项的内容(每次项需要显示时触发)。
  • LBS_OWNERDRAWFIXED 的列表框需通过这两个消息实现完全的自定义控制。