热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

CTreeCtrl树控件:如何给MFC中的CTreeCtrl树控件添加复选框及选中状态

1、效果如下:2、首先需要重写树控件类CCheckTreeCtrl.h#pragmaoncetypedefenumTREE_STATE{STATE_NONE,ST

1、效果如下:
在这里插入图片描述
2、首先需要重写树控件类
CCheckTreeCtrl.h

#pragma oncetypedef enum TREE_STATE
{STATE_NONE,STATE_UNCHECKED,STATE_CHECKED,STATE_INTERMEDIATE,STATE_DISABLED
};//当树中某项的选中状态被改变时,会触发NM_CHECKSTATECHANGED消息,数据类型为TREEINFO
//ON_NOTIFY(NM_CHECKSTATECHANGED, IDC_TREE_CURRES, OnCheckStateChangeTreeCurres)
#define NM_CHECKSTATECHANGED WM_USER+0x6D5//当树中某项被点击时,会触发NM_MYCLICK消息(代替NM_CLICK)
//ON_NOTIFY(NM_MYCLICK, IDC_TREE_CURRES, OnClickTreeCurres)
#define NM_MYCLICK (NM_FIRST-1533)// CCheckTreeCtrl
class CCheckTreeCtrl : public CTreeCtrl
{DECLARE_DYNAMIC(CCheckTreeCtrl)public:CCheckTreeCtrl();virtual ~CCheckTreeCtrl();int GetItemCheck(HTREEITEM hItem) const;BOOL SetItemCheck(HTREEITEM hItem, int nState, BOOL bRecursion = FALSE);public:virtual BOOL PreTranslateMessage(MSG* pMsg);afx_msg void OnLButtonDown(UINT nFlags, CPoint point);afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);protected:virtual void PreSubclassWindow();BOOL ToggleCheckState(HTREEITEM hItem, UINT uFlags);void SetChildrenItemCheck(HTREEITEM hItem);void SetParentItemCheck(HTREEITEM hItem);
protected:CImageList m_ilStateImages;DECLARE_MESSAGE_MAP()
};

CCheckTreeCtrl.cpp

// CheckTreeCtrl.cpp : 实现文件
//#include "stdafx.h"
#include "CheckTreeCtrl.h"
#include "resource.h"// CCheckTreeCtrlIMPLEMENT_DYNAMIC(CCheckTreeCtrl, CTreeCtrl)CCheckTreeCtrl::CCheckTreeCtrl()
{
}CCheckTreeCtrl::~CCheckTreeCtrl()
{
}BEGIN_MESSAGE_MAP(CCheckTreeCtrl, CTreeCtrl)ON_WM_LBUTTONDOWN()ON_WM_LBUTTONDBLCLK()ON_WM_CHAR()
END_MESSAGE_MAP()// CCheckTreeCtrl 消息处理程序
void CCheckTreeCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{TVHITTESTINFO tvHitTest;tvHitTest.pt = point;HTREEITEM hItem = HitTest(&tvHitTest);if (hItem != NULL){if (STATE_DISABLED == GetItemCheck(hItem))return;if (ToggleCheckState(hItem, tvHitTest.flags)){return;}}CTreeCtrl::OnLButtonDown(nFlags, point);
}void CCheckTreeCtrl::OnLButtonDblClk(UINT nFlags, CPoint point)
{OnLButtonDown(nFlags, point);
}BOOL CCheckTreeCtrl::ToggleCheckState(HTREEITEM hItem, UINT uFlags)
{BOOL bOnStateIcon = ((uFlags & TVHT_ONITEMSTATEICON) == TVHT_ONITEMSTATEICON);if ((bOnStateIcon == FALSE) || (hItem == NULL)) {return FALSE;}int nState = GetItemCheck(hItem) + 1;if (nState > STATE_CHECKED/*STATE_INTERMEDIATE*/)nState = STATE_UNCHECKED;SelectItem(hItem);BOOL bRet = SetItemCheck(hItem, nState);if (bRet){SetChildrenItemCheck(hItem);SetParentItemCheck(hItem);// 发送消息,通知状态改变if(GetParent()){CWnd* pWnd1 = GetParent();if (pWnd1 != NULL){CWnd* pWnd2 = GetParent()->GetParent();}GetParent()->SendMessage(NM_CHECKSTATECHANGED, (WPARAM)hItem,(LPARAM)(nState == STATE_CHECKED ? 1 : 0));}}return bRet;
}void CCheckTreeCtrl::SetChildrenItemCheck(HTREEITEM hItem)
{int nState = GetItemCheck(hItem);HTREEITEM hChildItem = GetChildItem(hItem);while (NULL != hChildItem){SetItemCheck(hChildItem, nState);SetChildrenItemCheck(hChildItem);hChildItem = GetNextSiblingItem(hChildItem);}
}void CCheckTreeCtrl::SetParentItemCheck(HTREEITEM hItem)
{HTREEITEM hParentItem = GetParentItem(hItem);if (NULL == hParentItem)return;HTREEITEM hSiblingItem = GetChildItem(hParentItem);int nState = GetItemCheck(hSiblingItem);hSiblingItem = GetNextSiblingItem(hSiblingItem);while (NULL != hSiblingItem){if (nState != GetItemCheck(hSiblingItem)){nState = STATE_INTERMEDIATE;break;}hSiblingItem = GetNextSiblingItem(hSiblingItem);}SetItemCheck(hParentItem, nState);SetParentItemCheck(hParentItem);
}int CCheckTreeCtrl::GetItemCheck(HTREEITEM hItem) const
{return (hItem == NULL)? STATE_NONE: (GetItemState(hItem, TVIS_STATEIMAGEMASK)>>12);
}BOOL CCheckTreeCtrl::SetItemCheck(HTREEITEM hItem, int nState, BOOL bRecursion/* = FALSE*/)
{BOOL bRet = SetItemState(hItem, INDEXTOSTATEIMAGEMASK(nState), TVIS_STATEIMAGEMASK);if (!bRecursion || !bRet)return bRet;SetChildrenItemCheck(hItem);return TRUE;
}void CCheckTreeCtrl::PreSubclassWindow()
{CTreeCtrl::PreSubclassWindow();m_ilStateImages.Create(IDB_STATE_IMAGES, 13, 1, RGB(255,255,255));SetImageList(&m_ilStateImages, TVSIL_STATE);
}BOOL CCheckTreeCtrl::PreTranslateMessage(MSG* pMsg)
{if ((pMsg->message == WM_KEYDOWN) && (VK_SPACE == pMsg->wParam)){HTREEITEM hItem = GetSelectedItem();if (NULL != hItem){int nState = GetItemCheck(hItem) + 1;if (nState > STATE_CHECKED/*STATE_INTERMEDIATE*/)nState = STATE_UNCHECKED;if (SetItemCheck(hItem, nState)){SetChildrenItemCheck(hItem);SetParentItemCheck(hItem);}}return TRUE;}return CTreeCtrl::PreTranslateMessage(pMsg);
}

上述会用到.bmp图片资源:IDB_STATE_IMAGES
在这里插入图片描述

3.控件的使用

//声明及初始化
CCheckTreeCtrl m_treeProLayers;CRect treeViewRect;
GetDlgItem(IDC_TREE_LAYERS)->GetWindowRect(treeViewRect);
ScreenToClient(treeViewRect);
m_treeProLayers.MoveWindow(treeViewRect);
m_treeProLayers.ShowWindow(SW_SHOW);// 图层信息结构体
struct LAYER_INFO
{LAYER_INFO(){};~LAYER_INFO(){};LAYER_INFO& operator &#61; (const LAYER_INFO& src){strLayerName &#61; src.strLayerName;strProjName &#61; src.strProjName;strProjCode &#61; src.strProjCode;strPath &#61; src.strPath;subVecLayerInfo &#61; src.subVecLayerInfo;return* this;};CString strLayerName;CString strProjName;CString strProjCode;CString strPath;std::vector<LAYER_INFO> subVecLayerInfo; // 子图层数据
};typedef std::vector<LAYER_INFO> vecProjLayers;// 树控件内容填充&#xff0c;为节点绑定数据
void CDatabaseDlg::SetTreeCtrlContent()
{// TODO: 在此添加控件通知处理程序代码CString strProjName;m_combProNames.GetWindowText(strProjName);m_sDataProName &#61; strProjName;// 清空树节点m_treeProLayers.SetRedraw(FALSE);m_treeProLayers.DeleteAllItems();m_treeProLayers.SetRedraw(TRUE);m_treeProLayers.RedrawWindow();std::vector<LAYER_INFO> vecLayers;for (auto proItor &#61; m_mapProjLayers.begin(); proItor !&#61; m_mapProjLayers.end(); &#43;&#43;proItor){if (proItor->first.CompareNoCase(strProjName) &#61;&#61; 0){vecLayers &#61; proItor->second;break;}}// 插入根节点 HTREEITEM hRoot &#61; m_treeProLayers.InsertItem(strProjName,TVI_ROOT, TVI_FIRST);for (int i &#61; 0; i < vecLayers.size();i&#43;&#43;){LAYER_INFO& pInfo &#61; vecLayers.at(i);HTREEITEM hChildItem &#61; m_treeProLayers.InsertItem(pInfo.strLayerName,hRoot);LAYER_INFO* player &#61; new LAYER_INFO;player->strLayerName &#61; pInfo.strLayerName;player->strPath &#61; pInfo.strPath;player->strProjName &#61; pInfo.strProjName;player->strProjCode &#61; pInfo.strProjCode;m_treeProLayers.SetItemData(hChildItem,(DWORD)player);for (int j &#61; 0; j < pInfo.subVecLayerInfo.size();j&#43;&#43;){AddTreeItemData(pInfo.subVecLayerInfo.at(j),hChildItem);}}// 展开所有树节点ExpandAllTreeItem(hRoot);m_treeProLayers.SetFocus();
}// 增加叶子节点及信息
void CDatabaseDlg::AddTreeItemData(const LAYER_INFO& stuLayer,HTREEITEM& pParent)
{CString strLayerName &#61; stuLayer.strLayerName;HTREEITEM hChildItem &#61; m_treeProLayers.InsertItem(strLayerName,pParent);LAYER_INFO* player &#61; new LAYER_INFO;player->strLayerName &#61; strLayerName;player->strPath &#61; stuLayer.strPath;player->strProjName &#61; stuLayer.strProjName;player->strProjCode &#61; stuLayer.strProjCode;m_treeProLayers.SetItemData(hChildItem,(DWORD)player);// 递归获取if (stuLayer.subVecLayerInfo.size() > 0){for (int i &#61; 0; i < stuLayer.subVecLayerInfo.size(); i&#43;&#43;){AddTreeItemData(stuLayer.subVecLayerInfo[i], hChildItem);}}
}
// 展开所有叶子节点
void CDatabaseDlg::ExpandAllTreeItem(HTREEITEM hTreeItem)
{if(!m_treeProLayers.ItemHasChildren(hTreeItem)){return;}//若树控件的根节点有子节点则获取根节点的子节点HTREEITEM hNextItem &#61; m_treeProLayers.GetChildItem(hTreeItem);while (hNextItem){//递归&#xff0c;展开子节点下的所有子节点ExpandAllTreeItem(hNextItem);hNextItem &#61; m_treeProLayers.GetNextItem(hNextItem, TVGN_NEXT);}m_treeProLayers.Expand(hTreeItem,TVE_EXPAND);
}// 如何获取选中的叶子节点对象及获取附加信息
void CDatabaseDlg::OnGetSelectTreeNode()
{// TODO: 在此添加控件通知处理程序代码std::vector<LAYER_INFO> vecLayerInfo;HTREEITEM hRootItem &#61; m_treeProLayers.GetRootItem();// 如果根节点被选中&#xff0c;则只获取根节点自己的child节点&#xff0c;不再向下查找if (m_treeProLayers.GetItemCheck(hRootItem) &#61;&#61; STATE_CHECKED){HTREEITEM hSubItem &#61; m_treeProLayers.GetChildItem(hRootItem);while(hSubItem){LAYER_INFO* pLayerInfo &#61; (LAYER_INFO *) m_treeProLayers.GetItemData(hSubItem);LAYER_INFO layerInfo &#61; *pLayerInfo;vecLayerInfo.push_back(layerInfo);hSubItem &#61; m_treeProLayers.GetNextSiblingItem(hSubItem);}}else if (m_treeProLayers.GetItemCheck(hRootItem) &#61;&#61; STATE_INTERMEDIATE){GetSelectTreeNodes(hRootItem,vecLayerInfo);}if (vecLayerInfo.size() &#61;&#61; 0){AfxMessageBox(_T("未选中任何图层树节点&#xff01;"), MB_OK | MB_ICONINFORMATION);return;}
}void CDatabaseDlg::GetSelectTreeNodes(HTREEITEM& hItem,std::vector<LAYER_INFO>& vecLayerInfo)
{while (hItem){int nStatus &#61; m_treeProLayers.GetItemCheck(hItem);// 子节点部分被选中if (nStatus &#61;&#61; STATE_INTERMEDIATE){HTREEITEM hSubItem &#61; m_treeProLayers.GetChildItem(hItem);if (!hSubItem){if (m_treeProLayers.GetCheck(hItem)){LAYER_INFO* pLayerInfo &#61; (LAYER_INFO *) m_treeProLayers.GetItemData(hItem);LAYER_INFO layerInfo &#61; *pLayerInfo;vecLayerInfo.push_back(layerInfo);}}// 递归while (hSubItem){GetSelectTreeNodes(hSubItem,vecLayerInfo);}}// 子节点全部被选中&#xff0c;不再向下查找child节点else if(nStatus &#61;&#61; STATE_CHECKED){LAYER_INFO* pLayerInfo &#61; (LAYER_INFO *) m_treeProLayers.GetItemData(hItem);LAYER_INFO layerInfo &#61; *pLayerInfo;vecLayerInfo.push_back(layerInfo);}hItem &#61; m_treeProLayers.GetNextSiblingItem(hItem);}
}

4、如果对树控件的叶子节点使用了SetItemData(),最后一定要释放掉内存&#xff0c;释放方式如下&#xff1a;

afx_msg void OnDeleteItem(NMHDR* pNMHDR, LRESULT* pResult);BEGIN_MESSAGE_MAP(CPublicDatabaseDlg, CDialog)ON_WM_CLOSE()ON_NOTIFY(TVN_DELETEITEM, IDC_TREE_LAYERS, &CDatabaseDlg::OnDeleteItem)
END_MESSAGE_MAP()void CDatabaseDlg::OnDeleteItem(NMHDR* pNMHDR, LRESULT* pResult)
{//reinterpret_cast 强制把 NMHDR类型的指针转换为LPNMTREEVIEW类型的指针。LPNMTREEVIEW pNMTreeView &#61; reinterpret_cast<LPNMTREEVIEW>(pNMHDR);// TODO: 在此添加控件通知处理程序代码TVITEM& item &#61; pNMTreeView->itemOld;if (item.lParam !&#61; 0) {delete (struct LAYER_INFO*)item.lParam;}*pResult &#61; 0;
}


推荐阅读
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • vue使用
    关键词: ... [详细]
  • 电话号码的字母组合解题思路和代码示例
    本文介绍了力扣题目《电话号码的字母组合》的解题思路和代码示例。通过使用哈希表和递归求解的方法,可以将给定的电话号码转换为对应的字母组合。详细的解题思路和代码示例可以帮助读者更好地理解和实现该题目。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • PHP图片截取方法及应用实例
    本文介绍了使用PHP动态切割JPEG图片的方法,并提供了应用实例,包括截取视频图、提取文章内容中的图片地址、裁切图片等问题。详细介绍了相关的PHP函数和参数的使用,以及图片切割的具体步骤。同时,还提供了一些注意事项和优化建议。通过本文的学习,读者可以掌握PHP图片截取的技巧,实现自己的需求。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
author-avatar
buddha覀mito_438
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有