CTreeCtrl中使用CxImage添加背景图片,以及给字添加背景图片

来源:互联网 发布:什么绘画软件好 编辑:程序博客网 时间:2024/05/17 07:44

在CTreeCtrl中加载背景图片,网上有很多例子,有的可行有的不行,这两天一边看资料一边整理,自己写了一个用CxImage加载图片的方法,大家可以参考下。有的地方还没有完善,不过基本功能可以实现,而且添加图片后屏幕不闪烁。已经试过了。

SetReDraw():保证其不要在子节点弹出时重画,而是在子节点已经扩展后重画

在做程序时,遇到了一个很白痴的问题,就是我想要实现鼠标滚动消息时,写了之后调试代码进不去,经过我查看,把ON_WM_MOUSEWHELL放到前面就可以了偷笑

(一)使用CxImage可以添加任意的图片

1、在.h中添加静态库

#include "ximage.h"#pragma comment(lib,"cximage.lib")#pragma comment(lib,"Jpeg.lib")#pragma comment(lib,"png.lib")#pragma comment(lib,"zlib.lib")

2、并且声明两个关于CxImage的变量,用来存放背景图片

CxImage *m_TreeBkImage;//TreeCtrl的背景图片

CxImage *m_TextBkImage; //字的背景图片

3、定义消息防止屏幕闪烁

afx_msg BOOL OnEraseBkgnd(CDC *pDC);

afx_msg void OnItemexpanding(NMHDR* pNMHDR, LRESULT* pResult);
afx_msg void OnItemexpanded(NMHDR* pNMHDR, LRESULT* pResult);

(二)在.cpp中初始化定义的变量

m_TreeBkImage = new CxImage(); //图片背景

m_TextBkImage = new CxImage(); //字的背景


ON_WM_ERASEBKGND()

ON_NOTIFY_REFLECT(TVN_ITEMEXPANDING,OnItemexpanding) 
ON_NOTIFY_REFLECT(TVN_ITEMEXPANDED,OnItemexpanded)


1、在OnPaint()调用整体的图片、字体等

void UITreeCtrl::OnPaint(){CPaintDC dc(this); // device context for paintingGetClientRect(&m_ClientRect);CBitmap bitmap;CDC MemeDc;MemeDc.CreateCompatibleDC(&dc);bitmap.CreateCompatibleBitmap(&dc, m_ClientRect.Width(), m_ClientRect.Height());CBitmap *pOldBitmap = MemeDc.SelectObject(&bitmap);DrawBack(&MemeDc);DrawItem(&MemeDc);
2、画图片背景

void UITreeCtrl::DrawBack(CDC* pDC){if(m_IsDrawBack == TRUE){m_LoadDC.CreateCompatibleDC(NULL);//创建兼容DCm_LoadDC.SelectObject(&m_bitmap);//将兼容位图选入兼容DC,m_LoadDC.FillSolidRect(&m_ClientRect,RGB(255,255,255));//首先将bakeBitmap(客户区)填充成背景颜色,这里用的是白色。PaintImage(pDC,&m_ClientRect,&m_LoadDC);m_LoadDC.DeleteDC();}elsepDC->FillSolidRect(&m_ClientRect,m_TreeBkcolor);}dc.BitBlt( m_ClientRect.left, m_ClientRect.top, m_ClientRect.Width(), m_ClientRect.Height(), &MemeDc, 0, 0,SRCCOPY);MemeDc.SelectObject(pOldBitmap);MemeDc.DeleteDC();}

3、重绘每一项

void UITreeCtrl::DrawItem(CDC* pDc){HTREEITEM currentItem,parentItem;//当前的句柄,和它的父节点的句柄DWORD    treeStyle;// 数的类型CRect    itemRect;//每一项的区域int      itemState;//某项的状态currentItem = GetFirstVisibleItem();//获取第一个课可见的项do {if (GetItemRect(currentItem,itemRect,TRUE)){itemRect.left=itemRect.left-15;CRect   fillRect(0,itemRect.top,m_ClientRect.right,itemRect.bottom);itemState = GetItemState(currentItem,TVIF_STATE);if (itemRect.top>m_ClientRect.bottom)  //说明这一项已超出窗口的边界,所以不绘制break;DrawItemText(pDc,currentItem,itemRect);}} while ((currentItem=GetNextVisibleItem(currentItem)) != NULL);}

4、重绘字体以及”+“”-“连线

void UITreeCtrl::DrawItemText(CDC * pDc,HTREEITEM hItem,CRect  pRect){int    itemState;//某项的状态DWORD  dwStyle = GetStyle();CRect  rcItem,rcTemp,SelRect;//文字位置CPoint ptTemp;UINT   indent = GetIndent(); SelRect = pRect;SelRect.left += 15;pDc->SelectObject(&m_font);//字的大小itemState = GetItemState(hItem,CDIS_SELECTED); if(itemState &TVIS_SELECTED)//选中时的背景颜色和字体颜色改变{ pDc->SetTextColor(m_STextcolor); DrawTextImage(pDc,&SelRect); } elsepDc->SetTextColor(m_textcolor);CString ItemText = GetItemText(hItem);CSize  fontSize;fontSize= pDc->GetTextExtent(ItemText);rcTemp = pRect;rcTemp.left += 18;rcTemp.top  += 2;pDc->SetBkMode(TRANSPARENT);pDc->DrawText(ItemText,rcTemp,DT_LEFT|DT_TOP);//显示项文本   GetItemRect(hItem,&rcItem,TRUE);//取得Item的文本矩形范围//2、如果要画线  if(dwStyle & TVS_HASLINES)  {  //创建一个真正的点线画笔  LOGBRUSH logBrush;  logBrush.lbColor = m_textcolor;  logBrush.lbStyle = BS_SOLID;  CPen pen(PS_COSMETIC | PS_ALTERNATE, 1, &logBrush);  CPen* oldPen = pDc->SelectObject(&pen);  rcTemp = rcItem;  rcTemp.left -= indent;//从当前向左移动一个 缩进 的量  rcTemp.right = rcTemp.left + indent;  ptTemp = rcTemp.CenterPoint();  //如果 Item 有父 Item 则在自己面前画一个'L'型的线,拐点正是缩进矩形的中心点  if( GetParentItem(hItem) != NULL )  {  //如果 Item 有一个弟弟节点(哥哥排上面), 则在自己面前画一条竖线, 否则画半条pDc->MoveTo( ptTemp.x - 1,rcTemp.top - 1 );  if(GetNextSiblingItem(hItem) != NULL)  pDc->LineTo( ptTemp.x - 1, rcTemp.bottom );  else pDc->LineTo( ptTemp.x - 1, ptTemp.y - 1 );  pDc->MoveTo( rcTemp.right, ptTemp.y - 1 );  pDc->LineTo( ptTemp.x - 1, ptTemp.y - 1 );  }  //依次绘制各个 Item 的父节点与叔叔节点之间被撑开的部分的连线  HTREEITEM hItemTemp = hItem;  while( hItemTemp = GetParentItem(hItemTemp) )  {  rcTemp.OffsetRect(-indent, 0);  ptTemp = rcTemp.CenterPoint();  if(GetNextSiblingItem(hItemTemp))  {  pDc->MoveTo( ptTemp.x - 1, rcTemp.top );  pDc->LineTo( ptTemp.x - 1, rcTemp.bottom );  }  }  //显示删除 MFC GDI 对象, 因为以前版本的 MFC 貌似有个BUG, 如果你不显示删除它就不会帮你删除, 这个BUG好像还存在  pDc->SelectObject(oldPen);  pen.DeleteObject();  pen.DeleteTempMap();  }  //3、绘制小'+'框  if(dwStyle & TVS_HASBUTTONS)  {  rcItem.left -= indent;  rcItem.right = rcItem.left + indent;  //确定绘制范围  rcTemp.SetRect(0, 0, 9, 9);  rcTemp.OffsetRect(rcItem.CenterPoint());  rcTemp.OffsetRect(-5, -5);  //绘制, 因为使用的是MFC10.0, 因此实际绘制工作可交给 CMFCVisualManager 完成,其实... 自己画难度也不大  if( ItemHasChildren(hItem) ) CMFCVisualManager::GetInstance()->OnDrawExpandingBox(pDc, rcTemp, ( GetItemState(hItem, TVIS_EXPANDED) & TVIS_EXPANDED ?TRUE:FALSE), RGB(0, 0, 0));  }  }

5、从本地加载图片需要知道图片的路径,定义函数专门加载图片的路径
CString UITreeCtrl::ReturnPicLoad(){//定义路径CString path;//获取系统参数GetModuleFileName(NULL,path.GetBufferSetLength(MAX_PATH+1),MAX_PATH);path.ReleaseBuffer();int pos = path.ReverseFind('\\');m_exePath = path.Left(pos+1);return m_exePath;}

6、知道了图片的路径才能加载图片,如果路径不正确,则没有图片出现

void UITreeCtrl::AddLoadPicture(CString pSrc){if(pPic == pSrc)return;elsem_IsDrawBack = TRUE;pPic    = pSrc;m_TreeBkImage->Load(m_exePath+pPic,FindType(pPic));m_TreeBkImage->Draw(pDC->GetSafeHdc(),pRect->left,pRect->top,pRect->Width(),pRect->Height(),0,true);pDC->BitBlt(pRect->left,pRect->top,pRect->Width(),pRect->Height(),pSrc,0,0,SRCCOPY);}

7、在程序运行过程中会出现闪烁问题,虽然网上有很多解决方式大同小异,但是还是想写下,这样比较代码比较完善

void UITreeCtrl::OnItemexpanding(NMHDR* pNMHDR, LRESULT* pResult) {NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;SetRedraw(FALSE);*pResult = 0;}void UITreeCtrl::OnItemexpanded(NMHDR* pNMHDR, LRESULT* pResult) {NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;Invalidate();SetRedraw(TRUE);*pResult = 0;}

8、很多朋友看到上面那个FindType这个函数没有定义 ,这个主要是根据加载的图片可以找到图片的后缀类型

int UITreeCtrl::FindType(CString filename){//根据图片找到图片的后缀类型CString ext = filename.Right(filename.GetLength()-filename.ReverseFind('.')-1);int type = 0;if (ext == _T("bmp"))type = CXIMAGE_FORMAT_BMP;#if CXIMAGE_SUPPORT_JPGelse if (ext == _T("jpg") || ext == _T("jpeg"))  type = CXIMAGE_FORMAT_JPG;#endif#if CXIMAGE_SUPPORT_GIFelse if (ext == _T("gif")) type = CXIMAGE_FORMAT_GIF;#endif #if CXIMAGE_SUPPORT_PNG else if (ext == _T("png"))    type = CXIMAGE_FORMAT_PNG; #endifelse type = CXIMAGE_FORMAT_MNG;
<span style="white-space: pre;"></span>return type;<span style="white-space: pre;"></span>
}
BOOL UITreeCtrl::OnEraseBkgnd(CDC *pDC){return true;}
最核心的内容就是上面的了

下面我再介绍一种方式,可以重绘TreeCtrl的这两种方式我都试过的。可行。

void UITreeCtrl::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult){LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);      LPNMTVCUSTOMDRAW pCustomDraw = (LPNMTVCUSTOMDRAW) pNMCD; NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );HTREEITEM hItem = (HTREEITEM) pLVCD->nmcd.dwItemSpec;*pResult  =  CDRF_DODEFAULT;  CDC     *pDC = CDC::FromHandle(pLVCD->nmcd.hdc);switch   (pLVCD-> nmcd.dwDrawStage) {  case   CDDS_PREPAINT:    //绘制控件前 { *pResult = CDRF_NOTIFYITEMDRAW;break; }case   CDDS_ITEMPREPAINT://一个项被绘制前{CRect rcItem(pNMCD->rc);   rcClient = rcItem;CPoint ptItem(rcItem.left + 1, rcItem.top + 1); if (!hItem)return;//画文字、线、"+"号DrawItem(hItem,pCustomDraw);*pResult = CDRF_SKIPDEFAULT;//告诉系统跳过默认处理return;}break;} }

void UITreeCtrl::DrawItem(HTREEITEM hItem,LPNMTVCUSTOMDRAW  lpNMTVCD){DWORD dwStyle = GetStyle();CRect rcItem,rcTemp;CPoint ptTemp;UINT indent = GetIndent();//取得 Item 文本的矩形范围  GetItemRect(hItem, &rcItem, TRUE);  CDC dc;  dc.Attach(lpNMTVCD->nmcd.hdc);if(lpNMTVCD->nmcd.uItemState & CDIS_SELECTED)  {    //如果 Item 被选中dc.SetTextColor(RGB(0,0,255));dc.FillSolidRect(&rcItem,m_STextcolor);} else {dc.SetTextColor(m_textcolor);  }dc.SetBkMode(TRANSPARENT);rcTemp = rcItem;  rcTemp.left += 2;  rcTemp.top += 2;  //1、绘制文本  dc.DrawText(GetItemText(hItem), &rcTemp, DT_LEFT | DT_SINGLELINE | DT_VCENTER);  //2、如果要画线  if(dwStyle & TVS_HASLINES)  {  //创建一个真正的点线画笔  LOGBRUSH logBrush;  logBrush.lbColor = m_textcolor;  logBrush.lbStyle = BS_SOLID;  CPen pen(PS_COSMETIC | PS_ALTERNATE, 1, &logBrush);  CPen* oldPen = dc.SelectObject(&pen);  rcTemp = rcItem;  rcTemp.left -= indent;//从当前向左移动一个 缩进 的量  rcTemp.right = rcTemp.left + indent;  ptTemp = rcTemp.CenterPoint();  //如果 Item 有父 Item 则在自己面前画一个'L'型的线,拐点正是缩进矩形的中心点  if( GetParentItem(hItem) != NULL )  {  //如果 Item 有一个弟弟节点(哥哥排上面), 则在自己面前画一条竖线, 否则画半条dc.MoveTo( ptTemp.x - 1,rcTemp.top - 1 );  if(GetNextSiblingItem(hItem) != NULL)  dc.LineTo( ptTemp.x - 1, rcTemp.bottom );  else dc.LineTo( ptTemp.x - 1, ptTemp.y - 1 );  dc.MoveTo( rcTemp.right, ptTemp.y - 1 );  dc.LineTo( ptTemp.x - 1, ptTemp.y - 1 );  }  //依次绘制各个 Item 的父节点与叔叔节点之间被撑开的部分的连线  HTREEITEM hItemTemp = hItem;  while( hItemTemp = GetParentItem(hItemTemp) )  {  rcTemp.OffsetRect(-indent, 0);  ptTemp = rcTemp.CenterPoint();  if(GetNextSiblingItem(hItemTemp))  {  dc.MoveTo( ptTemp.x - 1, rcTemp.top );  dc.LineTo( ptTemp.x - 1, rcTemp.bottom );  }  }  //显示删除 MFC GDI 对象, 因为以前版本的 MFC 貌似有个BUG, 如果你不显示删除它就不会帮你删除, 这个BUG好像还存在  dc.SelectObject(oldPen);  pen.DeleteObject();  pen.DeleteTempMap();  }  //3、绘制小'+'框  if(dwStyle & TVS_HASBUTTONS)  {  rcItem.left -= indent;  rcItem.right = rcItem.left + indent;  //确定绘制范围  rcTemp.SetRect(0, 0, 9, 9);  rcTemp.OffsetRect(rcItem.CenterPoint());  rcTemp.OffsetRect(-5, -5);  //绘制, 因为使用的是MFC10.0, 因此实际绘制工作可交给 CMFCVisualManager 完成,其实... 自己画难度也不大  if( ItemHasChildren(hItem) ) CMFCVisualManager::GetInstance()->OnDrawExpandingBox(&dc, rcTemp, ( GetItemState(hItem, TVIS_EXPANDED) & TVIS_EXPANDED ?TRUE:FALSE), RGB(0, 0, 0));  }  //完成dc.Detach();}




0 0