关于自定义绘图

本部分包含有关自定义绘制功能的一般信息,并提供应用程序如何支持自定义绘制的概念概述。 目前,以下控件支持自定义绘制功能:

  • 标头控件
  • 列表视图控件
  • Rebar 控制
  • 工具栏控件
  • 工具提示控件
  • 跟踪条控件
  • 树视图控件

关于自定义绘制通知消息

所有支持自定义绘制的常用控件在绘制操作期间的特定点发送 NM_CUSTOMDRAW 通知代码。 这些通知代码描述应用于整个控件的绘制操作,以及特定于控件内项的绘制操作。 与许多通知代码一样,NM_CUSTOMDRAW 通知作为 WM_NOTIFY 消息发送。

自定义绘制通知的 lParam 参数将是 NMCUSTOMDRAW 结构或包含 NMCUSTOMDRAW 结构作为其第一个成员的控件特定结构的地址。 下表说明了控件及其使用的结构之间的关系。

结构 使用者
NMCUSTOMDRAW Rebar、跟踪条和标头控件
NMLVCUSTOMDRAW 列表视图控件
NMTBCUSTOMDRAW 工具栏控件
NMTTCUSTOMDRAW 工具提示控件
NMTVCUSTOMDRAW 树视图控件

 

绘制周期、绘制阶段和通知消息

与所有 Windows 应用程序一样,常用控件会根据从系统或其他应用程序接收的消息定期绘制和擦除自身。 控制绘制或擦除本身的过程称为绘制周期。 支持自定义绘制的控件在每个绘制周期中定期发送 NM_CUSTOMDRAW 通知代码。 此通知代码附带一个 NMCUSTOMDRAW 结构或另一个包含 NMCUSTOMDRAW 结构作为其第一个成员的结构。

NMCUSTOMDRAW 结构包含的一条信息是绘制周期的当前阶段。 这称为绘制阶段,由结构的 dwDrawStage 成员中的值表示。 控件通知其父控件四个基本绘制阶段。 这些基本或全局绘制阶段在结构中由以下标志值(在 Commctrl.h 中定义)表示。

全局绘制阶段值 说明
CDDS_PREPAINT 在绘制周期开始之前。
CDDS_POSTPAINT 绘制周期完成后。
CDDS_PREERASE 擦除周期开始之前。
CDDS_POSTERASE 擦除周期完成后。

 

上述每个值都可以与 CDDS_ITEM 标志组合使用,以指定特定于项目的绘制阶段。 为了方便起见,Commctrl.h 包含以下项目特定值。

特定于项目的绘制阶段值 说明
CDDS_ITEMPREPAINT 绘制项目之前。
CDDS_ITEMPOSTPAINT 绘制项目之后。
CDDS_ITEMPREERASE 擦除项目之前。
CDDS_ITEMPOSTERASE 擦除项目之后。
CDDS_SUBITEM 常用控件版本 4.71. 如果要绘制子项,则标记与 CDDS_ITEMPREPAINT 或 CDDS_ITEMPOSTPAINT 结合使用。 只有当从 CDDS_PREPAINT 返回 CDRF_NOTIFYITEMDRAW 时,才会设置此项。

 

应用程序必须处理 NM_CUSTOMDRAW 通知代码,然后返回一个特定值,告知控件必须执行的操作。 有关这些返回值的更多信息,请参阅以下部分。

利用自定义绘制服务

利用自定义绘制功能的关键是响应控件发送的 NM_CUSTOMDRAW 通知代码。 应用程序为响应这些通知而发送的返回值决定了该绘制周期的控件行为。

本部分包含有关应用程序如何使用 NM_CUSTOMDRAW 通知返回值来确定控件的行为的信息。

详细信息分为以下主题:

响应预付费通知

在每个绘制周期的开头,控件发送 NM_CUSTOMDRAW 通知代码,并在随附 NM_CUSTOMDRAW 结构的 dwDrawStage 成员中指定 CDDS_PREPAINT 值。 应用程序返回到此第一个通知的值决定了控件如何以及何时为该绘制周期的其余部分发送后续自定义绘制通知。 应用程序可以返回以下标志的组合,以响应第一个通知。

返回值 效果
CDRF_DODEFAULT 控件将自行绘制。 它不会为此绘制周期发送其他 NM_CUSTOMDRAW 通知。 此标志不能与任何其他标志一起使用。
CDRF_DOERASE 该控件将仅绘制背景。
CDRF_NEWFONT 应用程序为项目指定了新字体;控件将使用新字体。 有关更改字体的详细信息,请参阅更改字体和颜色。 当 dwDrawStage 等于 CDDS_ITEMPREPAINT 时,会出现这种情况。
CDRF_NOTIFYITEMDRAW 控件将通知父对象任何特定于项目的绘制操作。 它将在绘制项目之前和之后发送 NM_CUSTOMDRAW 通知代码。当 dwDrawStage 等于 CDDS_PREPAINT 时,会出现这种情况。
CDRF_NOTIFYPOSTERASE 控件将在擦除项目后通知父项。 当 dwDrawStage 等于 CDDS_PREPAINT 时,会出现这种情况。
CDRF_NOTIFYPOSTPAINT 当整个控件的绘制周期完成时,该控件将发送 NM_CUSTOMDRAW 通知。 当 dwDrawStage 等于 CDDS_PREPAINT 时,会出现这种情况。
CDRF_NOTIFYSUBITEMDRAW 版本 4.71。 在绘制每个列表视图子项之前,应用程序将收到 NM_CUSTOMDRAW 通知,其中 dwDrawStage 设置为 CDDS_ITEMPREPAINT | CDDS_SUBITEM。 然后,可以分别为每个子项指定字体和颜色,或者返回 CDRF_DODEFAULT 进行默认处理。 当 dwDrawStage 等于 CDDS_ITEMPREPAINT 时,会出现这种情况。
CDRF_SKIPDEFAULT 应用程序手动绘制项目。 控件不会绘制项目。 当 dwDrawStage 等于 CDDS_ITEMPREPAINT 时,会出现这种情况。
CDRF_SKIPPOSTPAINT 控件不会在项目周围绘制焦点矩形。

 

请求特定于项目的通知

如果应用程序将 CDRF_NOTIFYITEMDRAW 返回到初始预绘制自定义绘制通知,该控件将在该绘制周期内为其绘制的每个项目发送通知。 这些特定于项的通知将在随附的 NMCUSTOMDRAW 结构的 dwDrawStage 成员中具有 CDDS_ITEMPREPAINT 值。 可以通过将 CDRF_NOTIFYPOSTPAINT 返回到这些特定于项的通知,来请求控件在绘制完项目后发送另一个通知。 否则,返回 CDRF_DODEFAULT,控件在开始绘制下一项之前不会通知父窗口。

自行绘制项目

如果应用程序绘制整个项,则返回 CDRF_SKIPDEFAULT。 这允许控件跳过不需要绘制的项目,从而减少系统开销。 请记住,返回此值意味着控件不会绘制项目的任何部分。

更改字体和颜色

应用程序可以使用自定义绘制来更改项目的字体。 只需在与自定义绘制通知相关联的 NMCUSTOMDRAW 结构的 hdc 成员指定的设备上下文中选择所需的 HFONT。 由于选择的字体可能与默认字体具有不同的度量标准,请确保在通知消息的返回值中包含 CDRF_NEWFONT 位。 有关使用此功能的详细信息,请参阅使用自定义绘制中的示例代码。 应用程序指定的字体用于在未选中该项目时显示该项目。 自定义绘制不允许更改所选项目的字体属性。

要更改除列表视图和树视图之外的所有支持自定义绘制的控件的文本颜色,只需在自定义绘制通知结构中提供的设备上下文中使用 SetTextColorSetBkColor 函数设置所需的文本和背景颜色。 若要修改列表视图或树视图中的文本颜色,需要将所需的颜色值放置在 NMLVCUSTOMDRAWNMTVCUSTOMDRAW 结构的 clrTextclrTextBk 成员中。

注意

在公共控件版本 6.0 之前,工具栏将忽略 CDRF_NEWFONT 标志。 版本 6.0 支持 CDRF_NEWFONT 标志,你可以使用它为工具栏选择其他字体。 但是,当视觉样式处于活动状态时,不能更改工具栏的颜色。 若要在版本 6.0 中更改工具栏的颜色,必须先通过调用 SetWindowTheme,并指定无视觉样式来禁用视觉样式:

 

SetWindowTheme (hwnd, "", "");

使用列表视图和树视图控件自定义绘制

大多数常见的控件可以以基本相同的方式进行处理。 但是,列表视图和树视图控件具有一些功能,这些功能需要使用不同的自定义绘制方法。

对于版本 5.0,如果通过返回 CDRF_NEWFONT 来更改字体,则这两个控件可能会显示剪裁的文本。 此行为对于与早期版本的常用控件的向后兼容性是必要的。 如果要更改列表视图或树视图控件的字体,如果在向控件添加任何项目之前发送 CCM_SETVERSION 消息,其中 wParam 值设置为 5,则会得到更好的结果。 有关使用自定义绘制的树视图控件的示例,请参阅知识库文章示例:CustDTv 演示 TreeView 中的自定义绘图 (Q248496)

使用列表视图控件自定义绘制

由于列表视图控件具有子项和多个显示模式,因此需要处理 NM_CUSTOMDRAW 通知的方式与其他常见控件稍有不同。

对于报告模式,请使用以下过程。

  1. 第一个 NM_CUSTOMDRAW 通知将相关 NMCUSTOMDRAW 结构的 dwDrawStage 成员设置为 CDDS_PREPAINT。 返回 CDRF_NOTIFYITEMDRAW
  2. 然后,将收到一条 NM_CUSTOMDRAW 通知,dwDrawStage 设置为 CDDS_ITEMPREPAINT。 如果指定新的字体或颜色并返回 CDRF_NEWFONT,则项目的所有子项都将更改。 如果要单独处理每个子项,请返回 CDRF_NOTIFYSUBITEMDRAW
  3. 如果在上一步中返回 CDRF_NOTIFYSUBITEMDRAW,则会收到一条 NM_CUSTOMDRAW 通知,其中 dwDrawStage 设置为 CDDS_SUBITEM | CDDS_ITEMPREPAINT。 若要更改该子项的字体或颜色,请指定新的字体或颜色并返回 CDRF_NEWFONT

对于大图标、小图标和列表模式,请使用以下过程。

  1. 第一个 NM_CUSTOMDRAW 通知将相关 NMCUSTOMDRAW 结构的 dwDrawStage 成员设置为 CDDS_PREPAINT。 返回 CDRF_NOTIFYITEMDRAW
  2. 然后,将收到一条 NM_CUSTOMDRAW 通知,dwDrawStage 设置为 CDDS_ITEMPREPAINT。 可以通过指定新字体和颜色并返回 CDRF_NEWFONT 来更改项的字体或颜色。 由于这些模式没有子项,因此不会收到任何其他 NM_CUSTOMDRAW 通知。

有关列表视图 NM_CUSTOMDRAW 通知处理程序的示例,请参阅使用自定义绘图

Conceptual

使用自定义绘图

自定义绘图参考

其他资源

示例:CustDTv 演示 TreeView 中的自定义绘图 (Q248496)