一步一步教你实现CTreeCtrl自绘 源代码下载兼送分。。

2020-02-14 21:22栏目:竞技宝app
TAG:

竞技宝app 1

最近因工作需求,需要自绘CTreeCtrl。由于原来从来没有自绘过,开始在网上搜索资料,查询(因此本文有些知识可能不全面,或许还有更好的办法来实现,还请大家多多指教。)经过一段时间的编写,终于写好了。在此,感谢网友bunpkin提供的实例参考。

竞技宝app 2
竞技宝app 3

一步一步教你实现CTreeCtrl自绘源代码下载blog:最近因工作需求,需要自绘CTreeCtrl。由于原来从来没有自绘过,开始在网上搜索资料,查询经过一段时间的编写,终于写好了。在此,感谢网友everbeing提供的实例参考。先贴上效果图,如果觉得还不错,那就继续往下看吧。如果觉得不行的,请飘过。如何你看见这句话我会很高兴,因为至少我写的东西对你还是有一点点的吸引了。在此谢过。很好,那现在让我们来说说为什么要自绘CTreeCtrl。我总结了以下2点需要自绘的情况。1.当系统自带的树形控件已不满足我们的要求时,我们需要自绘。就像上图一样我们需要在后面显示我们额外的图标。2.当你是一个追求界面美观的人时,我们需要自绘我们需要自绘CTreeCtrl控件,我们就必须先了解一下自绘的方法,CTreeCtrl自绘有2种方法可以实现。第一种:通过从写NM_CUSTORMDRAW反射消息实现自绘。第二种通过重写ON_PAINT实现自绘。两中方法的实现原理:第一种:通过从写NM_CUSTORMDRAW反射消息实现自绘。从这个消息的英文单词我们翻译过来就是自定义绘制。当CTreeCtrl控件需要绘制就会触发这个消息。需要注意的是这个函数被调用的时候只是绘制了当前的某一个节点,意思就是当我们的CTreeCtrl有10个节点需要绘制的时候这个函数就需要调用10次。这个是函数原型voidCMyTreeCtrl::OnNMCustomdraw(NMHDR*pNMHDR,LRESULT*pResult)这个函数会给我们传入一个pNMHDR指针,这个指针有我们很关心的数据,如当前的HDC,RECT,和当前的节点信息,但是必须要通过转换。下面是转换语句。NMTVCUSTOMDRAW*ptvTreeCtrl=(NMTVCUSTOMDRAW*)pNMHDR可能有的朋友会问为什么需要类型转换了,这是由于在我们的程序中收到NM_CUSTORMDRAW消息的不止CTreeCtrl一个,其它的控件也能收到,这里我们我为了区分是哪个控件收到的消息所以我们需要对应的类型转换。下面是常见控件的类型转换类型。ControlStructureListviewNMLVCUSTOMDRAWToolTipsNMTTCUSTOMDRAWTreeviewNMTVCUSTOMDRAWAllothersupportedcontrolsNMCUSTOMDRAW很明显我们根据上面的图一眼就能看出CTreeCtrl对应的类型是NMTVCUSTOMDRAW。下面我们在来看看我们最关心的NMTVCUSTOMDRAW结构里面存的是什么数据。NMTVCUSTOMDRAW结构定义:typedefstructtagNMTVCUSTOMDRAW{NMCUSTOMDRAWnmcd;//包含控件的基本信息COLORREFclrText;//节点的文本颜色COLORREFclrTextBk;//文本背景色}NMTVCUSTOMDRAW,*LPNMTVCUSTOMDRAW;NMCUSTOMDRAW结构定义:typedefstructtagNMCUSTOMDRAWINFO{NMHDRhdr;//跟pNMHDR一样,我基本没用到DWORDdwDrawStage;//绘画段,某项被檫出前,后,绘制前,后HDChdc;//控件的设备上下文句柄RECTrc;//要绘制的区域DWORDdwItemSpec;//树控件不需要这个变量UINTuItemState;//项的状态,只要是点击选中LPARAMlItemlParam//项关联的数据,通过SetItemData函数设置的。}NMCUSTOMDRAW,FAR*LPNMCUSTOMDRAW;uItemState项的状态(来自MSDN)Specifiesthecurrentitemstate.Itcanbeacombinationofthefollowingvalues.ValueDescriptionCDIS_CHECKEDTheitemischecked.项被核记了CDIS_DEFAULTTheitemisinitsdefaultstate.默认状态CDIS_DISABLEDTheitemisdisabled.项被禁止了CDIS_FOCUSTheitemisinfocus.项具有焦点CDIS_GRAYEDTheitemisgrayed.项为灰颜色,CDIS_HOTTheitemiscurrentlyunderthepointer(hot).鼠标当前停留在这个项上CDIS_SELECTEDTheitemisselected.项被选中了以上就是我们自绘需要知道的数据结构,如果你了解这些数据结构所代表的意思,那下面我们就可以开始绘制了。绘制方法NMCUSTOMDRAW消息自绘,使你可以决定在什么绘画端来绘制,比较常用的是在绘制前的阶段来绘制,如果你只是用了这种方法来绘制画,那么恭喜你选择对了一半,但是绘制失败了,因为你将什么也看不见。不急,让我们慢慢给你说明原因,因为你在绘制前的阶段绘制了,紧接这系统还会调用一次默认绘制,那么你原来的绘制就被覆盖了。正确的方法是在绘制前绘制,然后过滤点系统的默认绘制,使之不在调用。这样我们所绘制就能看见了。于是乎在我们的OnNMCustomdraw函数中多了一下几句代码。if(lpnmcd-nmcd.dwDrawStage==CDDS_PREPAINT){*pResult=CDRF_NOTIFYITEMDRAW;return;}elseif(lpnmcd-nmcd.dwDrawStage==CDDS_ITEMPREPAINT){//自定义绘制*pResult=CDRF_DODEFAULT;return;}很好,现在你已经知道了绘制的基本方法了,那么接下来你就可以加上你自己的绘制了。大致思路如下。获取当前绘制节点的信息。如:节点状态,节点区域,节点文字等信息------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------如过你讨厌记住以上的这么多数据,那么恭喜你,你可以继续网下看,下面介绍另一种实现自绘的方法通过重写ON_PAINT消息来实现自绘,也是我最终实现自绘的方法。因为我发现通过上面的方法来实现自绘解决不了我的问题。闪烁和热点,也许是我自己的能力有限。原理:我们获取树形控件的数据结构和DC,然后我们自己来定义绘制的规则。这里就可以发挥你的DIY兴趣了,想怎么画就怎么画。很好很好,下面进入另一种方法的介绍首先我们要明白ON_PAINT消息在什么情况下触发,在win32程序中,当窗口需要重绘的时候会触发ON_PAINT消息,还有一种情况就是我们自己手动触发,手动触发的消息有两种,第一种是调用窗口无效函数Invalidate(FALSE),第2种是手动发送wm_paint消息。明白了这个我们才知道我们以后需要窗口重绘的时候怎么处理,这里说点题外话,原来在做扫雷游戏的时候,鼠标单击之后要过20多毫秒才有反应,后来查了半天原因才发现是带用重绘的问题,因为我开始单机之后没有立即调用重绘,而是没隔50毫秒调用一次。我们通过重写ON_PAINT消息就能获取当当前窗口的DC,在这里我们就是整个树形控件的DC,不向上面那种方法一样只获取到树形控件某节点的DC,获取整个DC要比上面那种方法操作方便一些。voidCMyCtreeCtrl::OnPaint(){CPaintDCdc(this);//这句就是获取绘制的DC现在有了DC我们就可以绘制了,在这里我们为了让它绘制的时候不闪烁我们决定用双缓冲,于是乎我们有了下面的代码。CPaintDCdc(this);//devicecontextforpaintingGetClientRect(m_ClientRect);CBitmapbitmap;CDCMemeDc;MemeDc.CreateCompatibleDC(dc);bitmap.CreateCompatibleBitmap(dc,m_ClientRect.Width(),m_ClientRect.Height());CBitmap*pOldBitmap=MemeDc.SelectObject(bitmap);DrawBack(MemeDc);DrawItem(MemeDc);//dc.BitBlt(m_ClientRect.left,m_ClientRect.top,m_ClientRect.Width(),m_ClientRect.Height(),MemeDc,0,0,SRCAND);dc.BitBlt(m_ClientRect.left,m_ClientRect.top,m_ClientRect.Width(),m_ClientRect.Height(),MemeDc,0,0,SRCCOPY);//dc.BitBlt(m_rect.left,m_rect.top,m_rect.Width(),m_rect.Height(),MemeDc,0,0,SRCCOPY);MemeDc.SelectObject(pOldBitmap);MemeDc.DeleteDC();}在这里我们基本的东西已经具备了现在我们就差数据了,首先我们要实现我们最开始的那种图,我们需要定义一个结构体来存储这些数据。structTREE_STRUCT{ints_FirstImage;//第一张图片的信息ints_SecondImage;//第二张图片的信息ints_ThreeImage;//第三张图片的信息ints_FourImage;//第四张图片的信息COLORREFs_TextColor;//文字的颜色ints_PeopleNum;//人的数目CStrings_ItemStr;//每一项的文字CStrings_StrUrl;//每一项对应的URL地址}我们在定义一个mapmapHTREEITEM,TREE_STRUCTm_mapTree;这是为了我们以后的绘制和判断热点用。有了数据结构,我们现在就可以插入数据使之成为一颗拥有节点的数,这个插入我们也需要自己重写,因为插入的数据是我们自己定义的。voidCMyCtreeCtrl::DrawItem(CDC*pDc){HTREEITEMcurrentItem,parentItem;//当前的句柄,和它的父节点的句柄DWORDtreeStyle;//数的类型CRectitemRect;//每一项的区域intitemState;//某项的状态//boolselected;//True:表示是需要高亮ImageAttributesalphaAttribut;alphaAttribut.SetColorKey(Color::Fuchsia,Color::Fuchsia);treeStyle=::GetWindowLong(m_hWnd,GWL_STYLE);//获取数的类型currentItem=GetFirstVisibleItem();//获取第一个课可见的项do{if(GetItemRect(currentItem,itemRect,TRUE)){itemRect.left=itemRect.left-19;CRectfillRect(0,itemRect.top,m_ClientRect.right,itemRect.bottom);itemState=GetItemState(currentItem,TVIF_STATE);if(itemRect.topm_ClientRect.bottom)//说明这一项已超出窗口的边界,所以不绘制,很好理解吧!不用我说了撒{break;}//绘制鼠标热点if(currentItem==m_MouseMoveItemItemHasChildren(currentItem)==NULL){m_Gdiplus.usFillRectangle(pDc-m_hDC,fillRect,0xB7F0FE,0xB7F0FE,edoVertical,true);}if(itemStateTVIS_SELECTED){m_Gdiplus.usFillRectangle(pDc-m_hDC,fillRect,0xFF00BB,0xFF00BB,edoVertical,true);}//绘制展开图片if(ItemHasChildren(currentItem)){CPointpoint;point.x=itemRect.left;point.y=itemRect.top+(itemRect.Height()-m_OpenHigh)/2;if(itemStateTVIS_EXPANDED){m_IconList.Draw(pDc,1,point,ILD_TRANSPARENT);}else{m_IconList.Draw(pDc,0,point,ILD_TRANSPARENT);}}itemRect.left+=m_OpenWidth+2;itemRect.right+=m_OpenWidth+8;//绘制图标1m_iter=m_mapTree.find(currentItem);GraphicstempGraphics(pDc-m_hDC);RectrcDes;if(m_iter-second.s_FirstImage=0m_iter-second.s_FirstImagem_竞技宝app,IconNum){rcDes=Rect(itemRect.left,fillRect.top+m_IconSpacing,m_IconWidt,m_IconHigh);tempGraphics.DrawImage(m_IconBitmap,rcDes,m_iter-second.s_FirstImage*m_IconWidt,0,m_IconWidt,m_IconHigh,UnitPixel,alphaAttribut);itemRect.left=itemRect.left+m_IconWidt;itemRect.right=itemRect.right+m_IconWidt;}//绘制文字DrawItemText(pDc,currentItem,itemRect);//绘制后面的第2,3,4项图标CSizefontSize;fontSize=pDc-GetTextExtent(GetItemText(currentItem));itemRect.left+=fontSize.cx;if(m_iter-second.s_SecondImage=0m_iter-second.s_SecondImagem_IconNum){rcDes=Rect(itemRect.left,fillRect.top+m_IconSpacing,m_IconWidt,m_IconHigh);tempGraphics.DrawImage(m_IconBitmap,rcDes,m_iter-second.s_SecondImage*m_IconWidt,0,m_IconWidt,m_IconHigh,UnitPixel,alphaAttribut);itemRect.left=itemRect.left+m_IconWidt+4;}}}while((currentItem=GetNextVisibleItem(currentItem))!=NULL);}函数的主要思路首先通过GetFirstVisibleItem();获取第一个可见的节点,如果节点存在则按照我们的绘制顺序绘制,然后调用GetNextVisibleItem获得下一个可见的节点,一直循环,直到下一个可见节点为NULL的时候退出绘制函数。下面讲一下获取项热点的方法。同样我们需要重写消息,这次是ON_MOUSEMOVE消息。我们首先定义一个数据来记录当前鼠标移动到的节点的句柄。具体看代码。HTREEITEMm_MouseMoveItem;//鼠标移动到的项voidCMyCtreeCtrl::OnMouseMove(UINTnFlags,CPointpoint){//TODO:在此添加消息处理程序代码和/或调用默认值m_ptOldMouse=point;HTREEITEMhItem=HitTest(point);if(hItem!=NULLhItem!=m_MouseMoveItem){m_MouseMoveItem=hItem;Invalidate(FALSE);}//CTreeCtrl::OnMouseMove(nFlags,point);}接下来我们通过获取当前绘制的节点和m_MouseMoveItem比对,如果相同则设置当前的背景颜色,从而实现热点时间//绘制鼠标热点if(currentItem==m_MouseMoveItemItemHasChildren(currentItem)==NULL){m_Gdiplus.usFillRectangle(pDc-m_hDC,fillRect,0xB7F0FE,0xB7F0FE,edoVertical,true);}//通过获取当前状态来判断单击事件。if(itemStateTVIS_SELECTED){m_Gdiplus.usFillRectangle(pDc-m_hDC,fillRect,0xFF00BB,0xFF00BB,edoVertical,true);}这2句代码都在DrawItem函数中。这种重写ON_PAINT消息实现自绘的方法就是这些了,有没有觉得比我们前面的那种方法要简单好理解一些了。下面说说我为什么选择这种方法而不用上面一种方法的原因,首先ON_PAINT可以获取到树形控件的整个DC,感觉绘制的时候方便一些,我想绘制到什么地方就绘制到什么地方,容易控制。其次是这种方法需要掌握的数据结构比较的少就需要知道几个常见的函数就OK了。

先贴上效果图,如果觉得还不错,那就继续往下看吧。如果觉得不行的,请飘过。

使用MFC实现上面的按钮半透明效果能看到父窗口中的内容,上面是效果图(一个是带背景图片的、另一个是不带的)。
控件继承自CWnd类(彩色的部分是窗口的背景图片、按钮是PNG图片,第二个图标是鼠标指向时的效果)。
图标的绘制使用GDI+绘制PNG图片,在此不多说了(处理WM_PAINT消息):
[cpp]
1. void PNGButton::OnPaint() 
2.  {   
3.   CPaintDC dc(this);     
4.   Graphics g(dc.m_hDC);    
5.   if(DrawBorder){    
6.      g.DrawImage(hoverBg,0,0);//画鼠标指向时的亮色背景   
7.   }  
8.   g.DrawImage(this->bg,0,0);//画按钮图标 
9.    g.ReleaseHDC(dc.m_hDC); 
10.  } 

竞技宝app 4

透明的关键:注意后面调用此方法的代码
关键在于InvalidateRect函数:通知父窗口重新绘制特定区域,执行此函数后按钮所在区域就被父窗口绘制的内容覆盖.在父窗口绘制完成后,
按钮也会收到WM_PAINT消息,执行上面的一段OnPaint代码.
[cpp] 
1. void PNGButton::PaintParent() 
2.  {   
3.   CRect   rect;  
4.   GetWindowRect(&rect);  
5.   GetParent()-> ScreenToClient(&rect);  
6.   GetParent()-> InvalidateRect(&rect); 
7.  } 
 
捕获鼠标指向或移出事件(处理WM_MOUSEMOVE,WM_MOUSEOVER,WM_MOUSELEAVE消息):
[cpp]
1. void PNGButton::OnMouseHover(UINT nFlags, CPoint point) 
2.  {     
3.   DrawBorder=true; 
4.   PaintParent();//通知父窗口重绘特定区域,会引发控件自身的重绘 
5.  } 
6.   
7.   
8.   void PNGButton::OnMouseLeave() 
9.  {     
10.   m_is_mouse_over =   false;  
11.   m_is_tracked =   false;   
12.   DrawBorder=false; 
13.   PaintParent();  //通知父窗口重绘特定区域,会引发控件自身的重绘 
14.   CWnd::OnMouseLeave(); 
15.  } 
16.   
17.   
18.   void PNGButton::OnMouseMove(UINT nFlags, CPoint point) 
19.  { 
20.   m_is_mouse_over   =   true;   
21.   if(!m_is_tracked)  
22.   {  
23.    TRACKMOUSEEVENT   tme;  
24.    tme.cbSize  =   sizeof(TRACKMOUSEEVENT);  
25.    tme.dwFlags  =   TME_LEAVE|TME_HOVER;   
26.    tme.hwndTrack   =   GetSafeHwnd();  
27.    tme.dwHoverTime =   80;  
28.    _TrackMouseEvent(&tme);   
29.    m_is_tracked   =   true;     
30.   }    
31.   CWnd::OnMouseMove(nFlags, point); 
32.  } 

 如何你看见这句话我会很高兴,因为至少我写的东西对你还是有一点点的吸引了。在此谢过!

附:
从资源加载PNG图片
附:
从资源加载PNG图片
 
[cpp] 
1. View Code  
2.  #pragma once 
3.  #include "stdafx.h" 
4.   using namespace Gdiplus;  
5.   
6.   static bool ImageFromIDResource(UINT nID, LPCTSTR sTR,Image * &pImg) 
7.  { 
8.       HINSTANCE hInst = AfxGetResourceHandle(); 
9.       HRSRC hRsrc = ::FindResource (hInst,MAKEINTRESOURCE(nID),sTR); // type 
10.        if (!hRsrc) 
11.        return FALSE; 
12.       // load resource into memory 
13.        DWORD len = SizeofResource(hInst, hRsrc); 
14.       BYTE* lpRsrc = (BYTE*)LoadResource(hInst, hRsrc); 
15.       if (!lpRsrc) 
16.        return FALSE; 
17.       // Allocate global memory on which to create stream 
18.        HGLOBAL m_hMem = GlobalAlloc(GMEM_FIXED, len); 
19.       BYTE* pmem = (BYTE*)GlobalLock(m_hMem); 
20.       memcpy(pmem,lpRsrc,len); 
21.       IStream* pstm; 
22.       CreateStreamOnHGlobal(m_hMem,FALSE,&pstm); 
23.       // load from stream 
24.        pImg=Gdiplus::Image::FromStream(pstm); 
25.       // free/release stuff 
26.        GlobalUnlock(m_hMem); 
27.       pstm->Release(); 
28.       FreeResource(lpRsrc); 
29.       return TRUE; 
30.  } 
平铺图片的代码
[cpp] 
1. CPaintDC dc(this);  
2.      CRect rect;  
3.      GetClientRect(rect); 
4.      CBrush bs(RGB(240,240,240));//窗口背景色 
5.       dc.FillRect(&rect,&bs);        //窗口着色 
6.      //填充背景图片:平铺 
7.       Graphics g(dc.m_hDC);     
8.      if(has_bg) g.DrawImage(this->bg,0,0);  
9.      Gdiplus::TextureBrush bbs(this->img); 
10.      g.FillRectangle(&bbs,0,0,rect.Width(),this->img->GetHeight());  
11.      g.ReleaseHDC(dc.m_hDC); 
12.      //TRACE(L"CMainFrame::OnPaintrn"); 

很好,那现在让我们来说说为什么要自绘CTreeCtrl。我总结了以下2点需要自绘的情况。

 

1.当系统自带的树形控件已不满足我们的要求时,我们需要自绘。就像上图一样我们需要在后面显示我们额外的图标。

作者:eit520

2.当你是一个追求界面美观的人时,我们需要自绘

 我们需要自绘CTreeCtrl控件,我们就必须先了解一下自绘的方法,

CTreeCtrl自绘有2种方法可以实现。

第一种:通过从写NM_CUSTORMDRAW反射消息实现自绘。

第二种: 通过重写ON_PAINT实现自绘。

 二种方法都是通过继承CTreeCtrl类,然后重写虚函数实现。

  下面分别介绍每一种的方法:

 第一种:通过从写NM_CUSTORMDRAW反射消息实现自绘。从这个消息的英文单词我们翻译过来就是自定义绘制。当CTreeCtrl控件需要绘制就会触发这个消息。需要注意的是这个函数被调用的时候只是绘制了当前的某一个节点,意思就是当我们的CTreeCtrl有10个节点需要绘制的时候这个函数就需要调用10次。

这个是函数原型

void CMyTreeCtrl::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult)

这个函数会给我们传入一个pNMHDR指针,这个指针有我们很关心的数据,如当前的HDC,RECT,和当前的节点信息,但是必须要通过转换。下面是转换语句。

NMTVCUSTOMDRAW *ptvTreeCtrl=(NMTVCUSTOMDRAW *)pNMHDR

 可能有的朋友会问为什么需要类型转换了,这是由于在我们的程序中收到NM_CUSTORMDRAW消息的不止CTreeCtrl一个,其它的控件也能收到,这里我们我为了区分是哪个控件收到的消息所以我们需要对应的类型转换。下面是常见控件的类型转换类型。

 

Control Structure
List view NMLVCUSTOMDRAW
ToolTips NMTTCUSTOMDRAW
Tree view NMTVCUSTOMDRAW
All other supported controls NMCUSTOMDRAW

 

 很明显我们根据上面的图一眼就能看出CTreeCtrl对应的类型是NMTVCUSTOMDRAW。

下面我们在来看看我们最关心的NMTVCUSTOMDRAW 结构里面存的是什么数据。

NMTVCUSTOMDRAW结构定义: typedef struct tagNMTVCUSTOMDRAW {  NMCUSTOMDRAW nmcd;//包含控件的基本信息(见下表)

 COLORREF clrText;//节点的文本颜色

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

 NMCUSTOMDRAW结构定义: typedef struct tagNMCUSTOMDRAWINFO {  NMHDR hdr;//跟pNMHDR一样,我基本没用到

 DWORD dwDrawStage;//绘画段,某项被檫出前,后,绘制前,后

  HDC hdc;//控件的设备上下文句柄  RECT rc;//要绘制的区域  DWORD dwItemSpec;//树控件不需要这个变量  UINT uItemState;//项的状态,只要是点击选中 

LPARAM lItemlParam //项关联的数据,通过SetItemData函数设置的。 }NMCUSTOMDRAW, FAR* LPNMCUSTOMDRAW;

  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.  项被选中了

 以上就是我们自绘需要知道的数据结构,如果你了解这些数据结构所代表的意思,那下面我们就可以开始绘制了。

 

绘制方法

NMCUSTOMDRAW消息自绘,使你可以决定在什么绘画端(就是NMCUSTOMDRAW 中的DWORD dwDrawStage)来绘制,比较常用的是在绘制前的阶段来绘制,如果你只是用了这种方法来绘制画,那么恭喜你选择对了一半,但是绘制失败了,因为你将什么也看不见。不急,让我们慢慢给你说明原因,因为你在绘制前的阶段绘制了,紧接这系统还会调用一次默认绘制,那么你原来的绘制就被覆盖了。

 正确的方法是在绘制前绘制,然后过滤点系统的默认绘制,使之不在调用。这样我们所绘制就能看见了。于是乎在我们的OnNMCustomdraw函数中多了一下几句代码。(过滤系统的默认绘制) 

[cpp] view plaincopy

 

  1. if (lpnmcd ->nmcd.dwDrawStage == CDDS_PREPAINT)    
  2.  {    
  3.     *pResult = CDRF_NOTIFYITEMDRAW;    
  4.      return;    
  5.   }    
  6. else if (lpnmcd->nmcd.dwDrawStage == CDDS_ITEMPREPAINT)    
  7.   {    
  8. //自定义绘制  
  9. *pResult = CDRF_DODEFAULT;    
  10.    return;    
  11.  }  

很好,现在你已经知道了绘制的基本方法了,那么接下来你就可以加上你自己的绘制了。

版权声明:本文由龙竞技官网发布于竞技宝app,转载请注明出处:一步一步教你实现CTreeCtrl自绘 源代码下载兼送分。。