先上一张效果图,有兴趣就继续看,没兴趣就闪了\(^o^)/~。
先贴一个链接。 CTreeCtrl加背景 。 我是在此基础上加的功能,所谓站在巨人的肩膀上看世界。
实现步骤:
1、增加 =NM_CUSTOMDRAW 消息映射
增加消息映射后
void CMyTreeCtrl::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);
*pResult = 0;
}
2、增加绘制项(也就是树控件的节点)结束处理,利用 CDRF_NOTIFYPOSTPAINT 定制消息通知
void CMyTreeCtrl::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult)
{
//LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);
LPNMTVCUSTOMDRAW pNMCD = reinterpret_cast<LPNMTVCUSTOMDRAW>(pNMHDR);
LPNMCUSTOMDRAW custom_draw = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);
switch (pNMCD->nmcd.dwDrawStage)
{
case CDDS_PREPAINT:
*pResult = CDRF_NOTIFYITEMDRAW ;//订阅绘制项前通知
return;
case CDDS_ITEMPREPAINT:
*pResult = CDRF_NOTIFYPOSTPAINT;//订阅绘制结束通知
return;
case CDDS_ITEMPOSTPAINT:
//此处处理项绘制结束的额外处理
break;
}
*pResult = 0;
}
3、获得 -/+ 按钮的位置
利用GetIndent()获得缩进增量,该增量需要乘以一个变量才能获得-/+按钮的实际区域,
这个需要乘的变量就是 NMTVCUSTOMDRAW::iLevel
4、我们目前有个问题是:
当 NMTVCUSTOMDRAW::nmcd.dwDrawStage == CDDS_ITEMPOSTPAINT时,
NMTVCUSTOMDRAW::iLevel的值始终为0,
所以,需要在NMTVCUSTOMDRAW::nmcd.dwDrawStage == CDDS_ITEMPREPAINT时,
保存好iLevel
因为项不止一个,所以我定义了一个链表来保存这个值 :
std::list<std::pair<HTREEITEM, int> > m_items;
附上最后修正后的OnNMCustomdraw()的实现
void CMyTreeCtrl::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult)
{
//LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);
LPNMTVCUSTOMDRAW pNMCD = reinterpret_cast<LPNMTVCUSTOMDRAW>(pNMHDR);
LPNMCUSTOMDRAW custom_draw = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);
switch (pNMCD->nmcd.dwDrawStage)
{
case CDDS_PREPAINT:
*pResult = CDRF_NOTIFYITEMDRAW ;//订阅绘制项前通知
m_items.clear();
return;
case CDDS_ITEMPREPAINT://项绘制前通知
pNMCD->clrText = RGB(0, 255, 0); //修改文字颜色
*pResult = CDRF_NEWFONT | CDRF_NOTIFYPOSTPAINT;//订阅绘制结束通知
m_items.push_back(std::make_pair((HTREEITEM)pNMCD->nmcd.dwItemSpec, pNMCD->iLevel));
return;
case CDDS_ITEMPOSTPAINT://项绘制结束通知
{
UINT unt = GetIndent();
HTREEITEM htreeitem = (HTREEITEM)pNMCD->nmcd.dwItemSpec;
auto itfind = std::find_if(m_items.begin(), m_items.end(), [&](const std::pair<HTREEITEM, int>&val )
{
return val.first == htreeitem;
});
if (itfind != m_items.end())
{
CRect button_rect = custom_draw->rc;
button_rect.right = button_rect.left + unt;
int off = unt*itfind->second;
button_rect.DeflateRect(off, 0, -off, 0);
CString button_content = GetChildItem(htreeitem) ? _T("√"):_T("×");
::SetTextColor(custom_draw->hdc, RGB(255,0,0));
DrawText(custom_draw->hdc, button_content, button_content.GetLength(), button_rect, DT_SINGLELINE | DT_VCENTER | DT_CENTER);
*pResult = CDRF_SKIPDEFAULT;
return;
}
}
break;
}
*pResult = 0;
}
作为程序猿,说那么多都是空的,附上demo源码才是王道
不过我不知道怎么附加文件(想把工程打包整上来)
附上.h和.cpp的全部内容吧
MyTreeCtrl.h
#pragma once
#include <list>
// CMyTreeCtrl
class CMyTreeCtrl : public CTreeCtrl
{
private:
std::list<std::pair<HTREEITEM, int> > m_items;
// Construction
public:
CMyTreeCtrl();
// Attributes
public:
BOOL SetBKImage(LPCTSTR LpszResource);
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CMyTreeCtrl)
protected:
virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CMyTreeCtrl();
// Generated message map functions
protected:
CBitmap m_bitmap;
//{{AFX_MSG(CMyTreeCtrl)
afx_msg void OnPaint();
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
afx_msg void OnItemexpanded(NMHDR* pNMHDR, LRESULT* pResult);
afx_msg void OnItemexpanding(NMHDR* pNMHDR, LRESULT* pResult);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult);
};
MyTreeCtrl.cpp
// MyTreeCtrl.cpp : 实现文件
//
#include "stdafx.h"
#include "TreeCtrlBKG.h"//这个可以删除 是我的demo工程的主app类(MFC对话框类)
#include "MyTreeCtrl.h"
#include <algorithm>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/
// CMyTreeCtrl
CMyTreeCtrl::CMyTreeCtrl()
{
}
CMyTreeCtrl::~CMyTreeCtrl()
{
}
BEGIN_MESSAGE_MAP(CMyTreeCtrl, CTreeCtrl)
//{{AFX_MSG_MAP(CMyTreeCtrl)
ON_WM_PAINT()
ON_WM_ERASEBKGND()
ON_NOTIFY_REFLECT(TVN_ITEMEXPANDED, OnItemexpanded)
ON_NOTIFY_REFLECT(TVN_ITEMEXPANDING, OnItemexpanding)
//}}AFX_MSG_MAP
ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, &CMyTreeCtrl::OnNMCustomdraw)
END_MESSAGE_MAP()
/
// CMyTreeCtrl message handlers
BOOL CMyTreeCtrl::SetBKImage(LPCTSTR LpszResource)
{
// if this is not the first call then delete gdi objects
if (m_bitmap.m_hObject != NULL)
m_bitmap.DeleteObject();
HBITMAP hbmp = (HBITMAP)::LoadImage(AfxGetInstanceHandle(),
LpszResource, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE);
if (hbmp == NULL)
return FALSE;
m_bitmap.Attach(hbmp);
return TRUE;
}
LRESULT CMyTreeCtrl::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// TODO: Add your specialized code here and/or call the base class
return CTreeCtrl::WindowProc(message, wParam, lParam);
}
void CMyTreeCtrl::OnPaint()
{
CPaintDC dc(this); // device context for painting
CRect rcclient;
GetClientRect(&rcclient);
// create a compatible memory dc
CDC memdc;
memdc.CreateCompatibleDC(&dc);
CBitmap bitmap;
bitmap.CreateCompatibleBitmap(&dc, rcclient.Width(), rcclient.Height());
memdc.SelectObject(&bitmap);
CWnd::DefWindowProc(WM_PAINT, (WPARAM)memdc.m_hDC, 0);
CDC maskdc;
maskdc.CreateCompatibleDC(&dc);
CBitmap maskbitmap;
maskbitmap.CreateBitmap(rcclient.Width(), rcclient.Height(), 1, 1, NULL);
maskdc.SelectObject(&maskbitmap);
maskdc.BitBlt(0, 0, rcclient.Width(), rcclient.Height(), &memdc,
rcclient.left, rcclient.top, SRCCOPY);
CBrush brush;
brush.CreatePatternBrush(&m_bitmap);
dc.FillRect(rcclient, &brush);
memdc.SetBkColor(RGB(0, 0, 0));
memdc.SetTextColor(RGB(255, 255, 255));
memdc.BitBlt(rcclient.left, rcclient.top, rcclient.Width(), rcclient.Height(), &maskdc, rcclient.left, rcclient.top, SRCAND);
dc.SetBkColor(RGB(255, 255, 255));
dc.SetTextColor(RGB(0, 0, 0));
dc.BitBlt(rcclient.left, rcclient.top, rcclient.Width(), rcclient.Height(), &maskdc, rcclient.left, rcclient.top, SRCAND);
dc.BitBlt(rcclient.left, rcclient.top, rcclient.Width(), rcclient.Height(), &memdc,
rcclient.left, rcclient.top, SRCPAINT);
brush.DeleteObject();
}
BOOL CMyTreeCtrl::OnEraseBkgnd(CDC* pDC)
{
// TODO: Add your message handler code here and/or call default
return TRUE;
}
void CMyTreeCtrl::OnItemexpanded(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
// TODO: Add your control notification handler code here
Invalidate();
SetRedraw(TRUE);
*pResult = 0;
}
void CMyTreeCtrl::OnItemexpanding(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
// TODO: Add your control notification handler code here
SetRedraw(FALSE);
*pResult = 0;
}
void CMyTreeCtrl::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult)
{
//LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);
LPNMTVCUSTOMDRAW pNMCD = reinterpret_cast<LPNMTVCUSTOMDRAW>(pNMHDR);
LPNMCUSTOMDRAW custom_draw = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);
switch (pNMCD->nmcd.dwDrawStage)
{
case CDDS_PREPAINT:
*pResult = CDRF_NOTIFYITEMDRAW ;//订阅绘制项前通知
m_items.clear();
return;
case CDDS_ITEMPREPAINT://项绘制前通知
pNMCD->clrText = RGB(0, 255, 0); //修改文字颜色
*pResult = CDRF_NEWFONT | CDRF_NOTIFYPOSTPAINT;//订阅绘制结束通知
m_items.push_back(std::make_pair((HTREEITEM)pNMCD->nmcd.dwItemSpec, pNMCD->iLevel));
return;
case CDDS_ITEMPOSTPAINT://项绘制结束通知
{
UINT unt = GetIndent();
HTREEITEM htreeitem = (HTREEITEM)pNMCD->nmcd.dwItemSpec;
auto itfind = std::find_if(m_items.begin(), m_items.end(), [&](const std::pair<HTREEITEM, int>&val )
{
return val.first == htreeitem;
});
if (itfind != m_items.end())
{
CRect button_rect = custom_draw->rc;
button_rect.right = button_rect.left + unt;
int off = unt*itfind->second;
button_rect.DeflateRect(off, 0, -off, 0);
CString button_content = GetChildItem(htreeitem) ? _T("√"):_T("×");
::SetTextColor(custom_draw->hdc, RGB(255,0,0));
DrawText(custom_draw->hdc, button_content, button_content.GetLength(), button_rect, DT_SINGLELINE | DT_VCENTER | DT_CENTER);
*pResult = CDRF_SKIPDEFAULT;
return;
}
}
break;
}
*pResult = 0;
}
头一回写这玩意,不到之处,还望海涵!