- 前言
- 准备工作
- 初始化树形控件
- 导入图标
- 初始化树形控件
- 带图标初始化树形控件
- 选中节点时的事件
- 移动到节点上方时的事件
- 选中复选框的事件
前言
创建了一个基于对话框的MFC应用程序,中间放置了一个树形控件,可以实现复选框,并实现级联操作,即当子节点选满时,父节点自动选中,当选中父节点时,子节点自动全选

准备工作
(VC100是项目需要,VS2019是更好的代码和用户界面)
在VS2010中创建MFC应用程序,选择应用程序类型为“基于对话框”,其他选项均为默认,点击“完成”。

创建完成后,使用VS2019打开工程,选择保留v100工具集(不升级),然后找到“资源视图”,双击Dialog
-IDD_工程名_DIALOG
,进入对话框布局设置,找到“工具箱”,各拖一个Tree Control
、Static Text
、Edit Control
到界面中,右键每一个控件分别设置属性:
Tree Control
: ID设为IDC_TREE1
,复选框设置为True
,具有按钮设置为True
,具有行设置为True
,信息提示设置为True
,行在跟处设置为True
,其余为默认选项;
Static Text
: 设置描述文字为“节点名称:”;
Edit Control
:ID设为IDC_ITEM_SEL_TEXT
,只读设置为True
;
添加完控件按下F5调试按钮,不出意外会弹出一个基本对话框。
初始化树形控件
右键对话框中的树形控件,添加变量,名称为m_Tree
导入图标
不需要显示图标可以跳过这一步
在树形控件变量所在的.h文件中添加图像列表,用于存放树形控件单项前面的图标
将提前准备好的.ico文件拷贝到工程目录下的res文件夹中,打开资源视图,右键Icon,添加资源,导入,选择res目录下的.ico文件,导入,可以为其设置ID。
在对话框类的OnInitDialog()
函数中使用以下代码导入图标:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| BOOL CTestDlg::OnInitDialog() { CDialogEx::OnInitDialog();
HICON hIcon[3];
hIcon[0] = theApp.LoadIconW(IDI_ICON1); hIcon[1] = theApp.LoadIconW(IDI_ICON2); hIcon[2] = theApp.LoadIconW(IDI_ICON3);
m_ImageList.Create(16, 16, ILC_COLOR32, 3, 3); for (int i = 0; i < 3; i++) { m_ImageList.Add(hIcon[i]); }
return TRUE; }
|
其中,m_ImageList.Create
前两个参数为图标指定了宽高,这决定了其在树形控件中显示的大小。
初始化树形控件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| BOOL CTestDlg::OnInitDialog() { CDialogEx::OnInitDialog();
HTREEITEM hRoot; HTREEITEM hFirst; HTREEITEM hSecond;
hRoot = m_Tree.InsertItem(_T("根节点"));
int count = 1; CString strText; for (int i = 1; i <= 5; i++) { strText.Format(_T("子节点%d"), i); hFirst = m_Tree.InsertItem(strText, hRoot, TVI_LAST); m_Tree.SetItemData(hFirst, count++); for (int j = 1; j <= 5; j++) { strText.Format(_T("子子节点 %d"), j); hSecond = m_Tree.InsertItem(strText, hFirst, TVI_LAST); m_Tree.SetItemData(hSecond, count++); } m_Tree.Expand(hFirst, TVE_EXPAND); } m_Tree.Expand(hRoot, TVE_EXPAND);
return TRUE; }
|
带图标初始化树形控件
为树形控件设置图像序列:
1
| m_Tree.SetImageList(&m_ImageList, TVSIL_NORMAL);
|
添加完图像序列后,若图像序列中有图像,那么默认情况下,每一个树形控件的节点都将使用第一张图像作为图标,也可以指定,只需要将插入节点的函数写为:
1
| m_Tree.InsertItem(strText, 0, 0, hRoot, TVI_LAST);
|
其中区别在于增加了第二、第三个参数,数字含义是在图像列表中的序号。第二个参数指定了节点的图标,第三个参数指定了节点选中时的图标。
选中节点时的事件
主要目的是选中时将节点的文字显示到下方的文本框中
右键树形控件,为消息TVN_SELCHANGED
添加响应函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| void CTestDlg::OnTvnSelchangedTree1(NMHDR* pNMHDR, LRESULT* pResult) { LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR); *pResult = 0;
CString strText;
HTREEITEM hItem = m_Tree.GetSelectedItem(); strText = m_Tree.GetItemText(hItem); SetDlgItemText(IDC_ITEM_SEL_TEXT, strText); }
|
移动到节点上方时的事件
展示了如何在鼠标移动到节点上方时,显示有关内容;
以及如何为节点附加32位数据;
右键树形控件,为消息TVN_GETINFOTIP
添加响应函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| void CTestDlg::OnTvnGetInfoTipTree1(NMHDR* pNMHDR, LRESULT* pResult) { LPNMTVGETINFOTIP pGetInfoTip = reinterpret_cast<LPNMTVGETINFOTIP>(pNMHDR); *pResult = 0;
NMTVGETINFOTIP* pTVTipInfo = (NMTVGETINFOTIP*)pNMHDR;
HTREEITEM hRoot = m_Tree.GetRootItem();
CString strText;
if (pTVTipInfo->hItem == hRoot) { strText = _T(""); } else { strText.Format(_T("%d"), pTVTipInfo->lParam); }
wcscpy_s(pTVTipInfo->pszText, strText.GetLength() + 1, strText); }
|
选中复选框的事件
使用事件递归的方式来处理父节点和子节点的选中问题
右键树形控件,为消息NM_TVSTATEIMAGECHANGING
添加响应函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| void CTestDlg::OnNMTVStateImageChangingTree1(NMHDR* pNMHDR, LRESULT* pResult) { *pResult = 0;
CPoint point; UINT uFlag; GetCursorPos(&point); this->m_Tree.ScreenToClient(&point);
HTREEITEM hItem = m_Tree.HitTest(point, &uFlag);
if (hItem == NULL) return; BOOL bCheck = m_Tree.GetCheck(hItem); CString itemText = m_Tree.GetItemText(hItem);
CString str; str.Format(_T("点击的点: x=%d, y=%d, 项目文本为: %s, 点击时选中状态为: %d\n"), point.x, point.y, itemText, bCheck); OutputDebugString(str);
bCheck = !bCheck; m_Tree.SelectItem(hItem); m_Tree.SetCheck(hItem, bCheck);
this->SetParentCheck(hItem, bCheck); this->SetChildenCheck(hItem, bCheck); }
|
处理父节点的选中状态:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| void CTestDlg::SetParentCheck(HTREEITEM hItem, BOOL bCheck) { if (hItem == NULL) return;
HTREEITEM hParent = m_Tree.GetParentItem(hItem); if (hParent == NULL) return;
if (!bCheck) { m_Tree.SetCheck(hParent, bCheck); } else { HTREEITEM hSibling = m_Tree.GetNextSiblingItem(hItem); BOOL nFlag = TRUE;
while (hSibling) { if (!m_Tree.GetCheck(hSibling)) { nFlag = FALSE; break; } hSibling = m_Tree.GetNextSiblingItem(hSibling); }
if (nFlag) { hSibling = m_Tree.GetPrevSiblingItem(hItem); while (hSibling) { if (!m_Tree.GetCheck(hSibling)) { nFlag = FALSE; break; } hSibling = m_Tree.GetPrevSiblingItem(hSibling); } }
if (nFlag) { m_Tree.SetCheck(hParent, TRUE); } }
this->SetParentCheck(hParent, m_Tree.GetCheck(hParent)); }
|
处理子节点的选中状态:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| void CTestDlg::SetChildenCheck(HTREEITEM hItem, BOOL bCheck) { if (hItem == NULL) return;
HTREEITEM hChild = m_Tree.GetChildItem(hItem);
while (hChild) { m_Tree.SetCheck(hChild, bCheck);
this->SetChildenCheck(hChild, bCheck);
hChild = m_Tree.GetNextSiblingItem(hChild); } }
|