TechBlog
首页分类标签搜索关于

© 2025 TechBlog. All rights reserved.

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

03/11/2025
从入门到精通VisualC2017#Windows#Mfc

微信小程序星海飞驰

带有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. 注意事项

  1. ​ 必须处理 WM_MEASUREITEM :

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

  2. ​ 必须处理 WM_DRAWITEM :

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

  3. ​ 性能优化 :

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

​ 总结

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

微信小程序星海飞驰