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

在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_Text BkImage; //字的背景图片

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 painting

	GetClientRect(&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);//创建兼容DC
		m_LoadDC.SelectObject(&m_bitmap);//将兼容位图选入兼容DC,
		m_LoadDC.FillSolidRect(&m_ClientRect,RGB(255,255,255));//首先将bakeBitmap(客户区)填充成背景颜色,这里用的是白色。

		PaintImage(pDC,&m_ClientRect,&m_LoadDC);
		m_LoadDC.DeleteDC();
	}
	else
		pDC->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);
 	}
 	else
		pDc->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;
	else	m_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_JPG
	else if (ext == _T("jpg") || ext == _T("jpeg"))  type = CXIMAGE_FORMAT_JPG;
#endif
#if CXIMAGE_SUPPORT_GIF
	else if (ext == _T("gif"))	 type = CXIMAGE_FORMAT_GIF;
#endif
 #if CXIMAGE_SUPPORT_PNG
 	else if (ext == _T("png"))    type = CXIMAGE_FORMAT_PNG;
 #endif
	else 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();

}

在CTreeCtrl设置背景图片的方法如下: 首先,需要背景图片。可以使用CImageList来图片资源,或者使用CBitmap来图片文件。假设我们使用CBitmap背景图片。 ```cpp CBitmap bmp; bmp.LoadBitmap(IDB_BACKGROUND); // 背景图片资源,IDB_BACKGROUND是资源ID ``` 然后,创建一个CImageList对象,并将载的背景图片添加到其。 ```cpp CImageList imgList; imgList.Create(1, 1, ILC_COLOR24 | ILC_MASK, 1, 1); // 创建一个包含1个图片ImageList imgList.Add(&bmp, RGB(255, 0, 255)); // 将背景图片添加ImageList,并指定透明色为紫色(255, 0, 255) ``` 接下来,将创建的CImageList对象设置为CTreeCtrl背景图片。 ```cpp m_treeCtrl.SetImageList(&imgList, TVSIL_NORMAL); // 将ImageList设置为CTreeCtrl背景图片 ``` 最后,在CTreeCtrl的绘制过程,为了让背景图片显示出来,需要处理WM_ERASEBKGND消息,并在消息处理函数返回非零值,告诉系统不需要擦除背景。 ```cpp BOOL CMyDlg::OnEraseBkgnd(CDC* pDC) { // 绘制背景图片 CRect rect; m_treeCtrl.GetClientRect(&rect); CDC memDC; memDC.CreateCompatibleDC(pDC); CBitmap* pOldBmp = memDC.SelectObject(&bmp); pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &memDC, 0, 0, SRCCOPY); memDC.SelectObject(pOldBmp); return TRUE; // 返回非零值,告诉系统不需要擦除背景 } ``` 通过以上步骤,我们就可以在CTreeCtrl设置背景图片了。注意,在使用背景图片时,需要考虑背景图片与树状控件的交互和可见性,以保证用户体验的良好。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

糯诺诺米团

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值