许多控件都可以收到NM_CUSTOMDRAW消息,但结构却不完全相同。
Control Custom Draw Structure
Rebar, trackbar, and header NMCUSTOMDRAW
List view NMLVCUSTOMDRAW
Tooltip NMTTCUSTOMDRAW
Tree view NMTVCUSTOMDRAW
Toolbar NMTBCUSTOMDRAW
typedef struct tagNMCUSTOMDRAWINFO {
NMHDR hdr; //NMHDR结构包含关于此通知的代码信息。这个结构变量跟OnCustomDraw函数的第一个参数pNMHDR是一样的.
DWORD dwDrawStage; //绘画段,某项被檫出前、后,绘制前、后。这个要着重了解,后面介绍,篇幅较大
HDC hdc; //控件的设备上下文句柄
RECT rc; //要绘制的区域,大小,一般是一个项的区域,如果是大循环则是整个控件的大小。
DWORD_PTR dwItemSpec; //项索引,依据控件而来(如CListCtrl),树控件不需要这个变量
UINT uItemState; //项的状态,详见后面
LPARAM lItemlParam; //项关联的数据,通过SetItemData函数设置的。
} NMCUSTOMDRAW, *LPNMCUSTOMDRAW;
先来看看关于dwDrawState在MSDN的取值:
dwDrawStage成员可以有以下值:
CDDS_POSTERASE:清除循环完成后
CDDS_POSTPAINT:绘制循环完成后
CDDS_PREERASE: 清除循环开始时
CDDS_PREPAINT: 绘制循环开始时
如前述的每个值可以结合的CDDS_ITEM标志来指定具体项目的绘制阶段
项目具体数值:
CDDS_ITEM:指明dwItemSpec, uItemState, and lItemlParam成员有效。
CDDS_ITEMPOSTERASE:清除an item后
CDDS_ITEMPOSTPAINT:绘制an item后
CDDS_ITEMPREERASE: 清除an item前
CDDS_ITEMPREPAINT: 绘制an item前
CDDS_SUBITEM: Version 4.71. Flag combined with CDDS_ITEMPREPAINT or CDDS_ITEMPOSTPAINT if a subitem is being drawn. This will only be set if CDRF_NOTIFYITEMDRAW is returned from CDDS_PREPAINT.标志结合 CDDS_ITEMPREPAINT 或 CDDS_ITEMPOSTPAINT 如果正在绘制一个子项。这将只设置如果 CDRF_NOTIFYITEMDRAW 从 CDDS_PREPAINT 返回。
你的应用程序必须处理NM_CUSTOMDRAW通知代码,然后返回一个特定的值,通知控制它必须做什么。
NMCUSTOMDRAW消息自绘,使你可以选择在哪个阶段来自绘,比如我在绘制前这个阶段绘制了这个控件,默认的话,是看不到结果的,因为会被控件重新绘制,覆盖掉的。那么我就在绘制后这个阶段来绘制,那么这样就可以看到结果了,但这样,会有两次绘制,徒增了一次绘制,降低了效率,而且还会产生闪烁。解决的方法是在一项被绘制前,指定*pResult为CDRF_SKIPDEFAULT,跳过默认绘制。这样就可以在绘制前这一个阶段来绘制了,不会被覆盖。
再来说说擦除和绘制的顺序,一次绘制控件里的循环顺序,是以CDDS_PREPAINT开始,CDDS_POSTPAINT结束的。那么在这个过程中,控件就会给父窗口发送很多消息,通知父窗口,现在是一个项绘制前这个阶段,现在又到了一项被擦除后这个阶段了。可是有时候我们并不需要处理所有的阶段,比如我只要在一个项被绘制前处理一下,那要怎么办呢,当然,你也可以直接用IF语句,判断dwDrawStage就行了,如果是CDDS_ITEMPREPAINT就处理,这也可以,但我所要做的是,指定控件只有在绘制一个项前的时候发送通知消息给父窗口,其余的就不需要了,这个也是由OnCustomDraw函数的第二个参数pResult来指定,我这样做只是想让大家能更好的理解pResult这个参数所存在的意义。擦除和绘制的顺序很好理解,谁先谁后,试想,比如我要绘制一个控件。那么这个状态就是绘制前,然后再擦除。所以顺序是CDDS_PREPAINT->CDDS_PREERASE->CDDS_POSTERASE->CDDS_POSTPAINT这是一个绘制控件的大循环。
前面说过了,pResult指定控件在哪个阶段发送通知过来,而CDDS_PRPAINT是自绘的开端,所以就有这样的规定,当dwDrawStage为CDDS_PRPAINT的时候,必须指定pResult的值,以告诉控件我要处理哪些阶段。(PS:上面那个大循环,我只能接收到CDDS_PREPAINT这个阶段,剩下的三个我根本就接不到,我不知道是什么原因,难道是在CDDS_PREPAINT时候,必须指定pResult的值,但没有一个对应着后面的三个阶段,搞不清楚,不过也没关系,这完全不影响自绘一个控件,我也不想去找了)。每一次绘画循环开始,控件发送 NM_CUSTOMDRAW 通知码,在随附的 NM_CUSTOMDRAW 结构的 dwDrawStage 成员中指定 CDDS_PREPAINT 值。您的应用程序返回这第一个通知决定随后如何以及何时该控件发送绘制循环其余的custom draw 自绘制通知。您的应用程序可以在第一次通知响应返回下列标志的组合:
CDRF_DODEFAULT 该控件将绘制自身。他不会发送额外的CUSTOMDRAW通知,对于这次绘制循环来说。该值为默认值,此标志不能与其他标志一起使用。
CDRF_DOERASE 该控件将只绘制背景。
CDRF_NEWFONT 当代码更改绘制项/子项的字体时使用。关于更改字体的详细信息,请参阅更改字体和颜色。DwDrawStage 等于 CDDS_ITEMPREPAINT 时,将发生这种情况。
CDRF_NOTIFYITEMDRAW 该控件将通知父级的任何特定项目的绘图操作。 绘制items前、后都发送NM_CUSTOMDRAW通知码。DwDrawStage 等于 CDDS_PREPAINT 时,将发生这种情况。
CDRF_NOTIFYPOSTERASE 该控件将擦除an item后通知父。DwDrawStage 等于 CDDS_PREPAINT 时,将发生这种情况。
CDRF_NOTIFYPOSTPAINT 使通知信息在控件或每个项/子项绘制后发送。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 控件将不绘制一个项周围的焦点矩形
uItemState项的状态(来自MSDN)
Specifies the current item state. It can be a combination of the following values.
Value Description
CDIS_CHECKED The item is checked. 项被核记了(如果有核计功能)
CDIS_DEFAULT The item is in its default state. 默认状态(平常的状态)
CDIS_DISABLED The item is disabled. 项被禁止了(不可用)
CDIS_FOCUS The item is in focus. 项具有焦点
CDIS_GRAYED The item is grayed. 项为灰颜色?
CDIS_HOT The item is currently under the pointer (hot).鼠标当前停留在这个项上(热点)
CDIS_INDETERMINATE The item is in an indeterminate state. 不确定状态
CDIS_MARKED The item is marked. The meaning of this is determined by the implementation. 该项被标记,这意思是由执行决定的
CDIS_SELECTED The item is selected. 项被选中了(单击) Note This flag does not work correctly for owner-drawn list-view controls that have the LVS_SHOWSELALWAYS style. For these controls, you can determine whether an item is selected by using LVM_GETITEMSTATE (or ListView_GetItemState) and checking for the LVIS_SELECTED flag.此标志不正确的所有者绘制的列表视图控件有的LVS_SHOWSELALWAYS风格。对于这些控件,您可以使用LVM_GETITEMSTATE (还是ListView_GetItemState )检查的LVIS_SELECTED标志,确定一个项目是否被选中。
CDIS_SHOWKEYBOARDCUES Version 6.0.The item is showing its keyboard cues. 版本 6.0.The 项正在显示其键盘提示。Note that Comctl32 version 6 is not redistributable, but it is included with Windows XP or later operating systems. To use Comctl32.dll version 6, specify it in the manifest. For more information on manifests, see Enabling Visual Styles.
CDIS_NEARHOT The item is part of a control that is currently under the mouse pointer ("hot"), but the item is not "hot" itself. The meaning of this is determined by the implementation.该项目是目前 ("热点"),将鼠标指针放在控件的一部分,但该项目本身不是"热"。这意思是由执行决定的。
CDIS_OTHERSIDEHOT The item is part of a splitbutton that is currently under the mouse pointer ("hot"), but the item is not "hot" itself. The meaning of this is determined by the implementation.该项目是目前正在 ("热点"),将鼠标指针的拆分按钮的一部分,但该项目本身不是"热"。这意思是由执行决定的。
CDIS_DROPHILITED The item is currently the drop target of a drag-and-drop operation.该项目目前是拖放操作的放置目标