NM_CUSTOMDRAW的自绘如树的节点颜色list某行颜色的改变

自绘树控件的关键之处在于NM_CUSTOMDRAW消息,比如tree节点的颜色,list某行的字体的颜色

可以看到在CTree 的NM_CUSTOMDRAW消息处理函数里有这样一句:

void CNewTreeCtrl::OnCustomDraw(NMHDR *pNMHDR, LRESULT *pResult)
{
 NMTVCUSTOMDRAW *ptvTreeCtrl=(NMTVCUSTOMDRAW *)pNMHDR;

//一个细小失误

}

void CNewTreeCtrl::OnCustomdrawFilelist(NMHDR *pNMHDR, LRESULT *pResult)
{
  
    NMLVCUSTOMDRAW* pLVCD = (NMLVCUSTOMDRAW*)(pNMHDR);


   此函数由类向导产生   注意去掉*pResult = 0;  否则无效果


这个是类型转换,为什么要类型转换呢,因为能收到NM_CUSTOMDRAW不止树控件一个,其它的控件也能收到,这样里面的结构就不尽相同,所以不同的控件,对于函数的第一个参数pNMHDR有不同的转换。这里给一张从MSDN复制过来的的表,各控件对应的转化结构。

结构对应一览表:(表格没复制过来,看着不差就行了,将就一下)

ControlStructure
List viewNMLVCUSTOMDRAW
ToolTipsNMTTCUSTOMDRAW
Tree viewNMTVCUSTOMDRAW
All other supported controlsNMCUSTOMDRAW

------------------------------------------------------------------------------------------------------

可见树控件对应的是NMTVCUSTOMDRAW结构,这里我们就来了解一下这个结构

NMTVCUSTOMDRAW结构定义:
typedef struct tagNMTVCUSTOMDRAW {
 NMCUSTOMDRAW nmcd;//一个包含控件各项信息的结构,如设备上下方句柄,项状态,大小。(参见后面)

 COLORREF clrText;//如果项有文本,这个指明文本颜色

 COLORREF clrTextBk;//文本背景色
}NMTVCUSTOMDRAW, *LPNMTVCUSTOMDRAW;

--------------------------------------------------------------------------------------------------------------------

NMCUSTOMDRAW结构定义:
typedef struct tagNMCUSTOMDRAWINFO {
 NMHDR hdr;//这个结构变量跟OnCustomDraw函数的第一个参数是一样的,我指的是,它们是一块内存的,也就是说
  //(DWORD)&pTreeCtrl->nmcd.hdr和(DWORD)pNMHDR的值是相等的,转来转去又转回来了。
 DWORD dwDrawStage;//绘画段,这个要着重了解,后面介绍,篇幅较大
  HDC hdc;//控件的设备上下文句柄
 RECT rc;//要绘制的区域,大小,一般是一个项的区域,如果是大循环则是整个控件的大小。
 DWORD dwItemSpec;//项索引,依据控件而来(如CListCtrl),树控件不需要这个变量
 UINT uItemState;//项的状态,详见后面
 LPARAM lItemlParam //项关联的数据,通过SetItemData函数设置的。
}NMCUSTOMDRAW, FAR* LPNMCUSTOMDRAW;

----------------------------------------------------------------------------------------------------------------------

先来看看关于dwDrawState在MSDN的取值:

The following table shows the global Drawstage values.
Value                    Description
CDDS_POSTERASE           After the erasing cycle is complete. 擦除控件后
CDDS_POSTPAINT           After the painting cycle is complete.  绘制控件后
CDDS_PREERASE            Before the erasing cycle begins. 擦除控件前
CDDS_PREPAINT            Before the painting cycle begins. 绘制控件前

The following table shows the global Drawstage values.
Value                    Description
CDDS_ITEM Indicates     that the dwItemSpec, uItemState, and lItemParam members are valid. 
CDDS_ITEMPOSTERASE      After an item has been erased. 一个项被擦除后
CDDS_ITEMPOSTPAINT      After an item has been drawn.  一个项被绘制好后
CDDS_ITEMPREERASE       Before an item is erased.  一个项被擦除前
CDDS_ITEMPREPAINT       Before an item is drawn. 一个项被绘制前


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的值,但没有一个对应着后面的三个阶段,搞不清楚,不过也没关系,这完全不影响自绘一个控件,我也不想去找了)

当dwDrawStage为CDDS_PRPAINT时pResult必须指定下面的一个值:(从msdn复制过来的)

CDRF_DODEFAULT    告诉控件我什么阶段也不处理,大循环结束了。没了意义。
The control will draw itself. It will not send any additional NM_CUSTOMDRAW messages for this paint cycle.

CDRF_NOTIFYITEMDRAW   处理关于一个项所有的阶段,擦除前,擦除后,绘制前,绘制后。(就是小循环,一个项的绘制,但像大循环那样,我只能接到一个项绘制前的消息。)
The control will notify the parent of any item-related drawing operations. It will send NM_CUSTOMDRAW messages before and after drawing items.

CDRF_NOTIFYITEMERASE   只处理一个项擦除前
The control will notify the parent when an item will be erased. It will send NM_CUSTOMDRAW messages before and after erasing items.

CDRF_NOTIFYPOSTERASE  只处理一个项擦除后
The control will notify the parent after erasing an item.

CDRF_NOTIFYPOSTPAINT  只处理一个项绘制后
The control will notify the parent after painting an item.

(另:擦除也算是绘制的一部分,我估计擦除就是调用类似FillRect的函数用白色画刷填充)



 又有当dwDrawStage为 CDDS_ITEMPREPAINT(一个项绘制前)的时候,pReslt必须指定下面的一个值:

CDRF_NEWFONT  项使用新字体,也就是使ptvTreeCtrl->clrText,还有背景色有效。。
Your application specified a new font for the item; the control will use the new font.

CDRF_SKIPDEFAULT  跳过默认项绘制,自己来绘制。
Your application drew the item manually. The control will not draw the item.

 

 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_SELECTED      The item is selected.  项被选中了(单击)

--------------------------------------------------------------------------------------

另外一些 CTreeCtrl类里需要了解的函数:

HTREEITEM HitTest( CPoint pt, UINT* pFlags=NULL );//根据鼠标位置获取其所在的项句柄,后面那个参数,它指明具体在一个项的哪里,图标上,还是文本上。这个参数值默认为NULL,我们也不需要获取。

CString GetItemText( HTREEITEM hItem ) const; //这个根据树项句柄获取项文本内容。

BOOL ItemHasChildren( HTREEITEM hItem );//判断一个项是否有子项,返回假没有子项。

UINT GetItemState( HTREEITEM hItem, UINT nStateMask ) const;//获取一个项的状态。

GetItemState函数第二个参数指明获取哪些状态,为NULL也可以,我这里是要用这个函数来判断一个父项是否为展开状态

也就是:if(GetItemState(hTreeItem,TVIS_EXPANDED)&TVIS_EXPANDED)//为真的话,这个父项处于展开状态

BOOL Expand( HTREEETEM hItem, UINT nCode );//展开或收缩一个项,关于第二个参数nCode有以下解释:

TVE_COLLAPSE  收缩列表

TVE_EXPAND  展开列表

TVE_TOGGLE 如果列表是展开状态,则收缩,反之则展开。

BOOL SelectItem( HTREEITEM hItem );//选中一个项


参考  :http://zhanshaoji.blog.163.com/blog/static/174816245201241710435781/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值