MFC制作一个可自由添加删除节点的树(CTreeCtrl)类



微软的MFC提供了强大的界面设计功能,但因为其很多函数过于繁琐,对初学者来说不好上手,而我自己在学习过程中也碰到了很多问题,查了很多文章,在此要特地感谢鸡琢米以及各位在CSDN上些小教程的各位前辈大佬。因此我打算分享一下我的学习经验。

1.第一步在Visual Studio 2017中创建一个对话框工程,并删除原有的3个控件

2.建立完对话框工程后,放置一个Tree Ctrl控件,将Has Button设为TRUE,这样有+-小按钮,再将Has Line勾上,这样就有连接线了,最好在将Line At Root勾上,这样根节点也会有连接线,此时一个最基本的树便完成了。这是别忘了为树关联一个成员变量m_webTree用于之后的程序编写。

MFC制作一个可自由添加删除节点的树(CTreeCtrl)类_第1张图片

3.再放入一个静态文本框,一个按钮,一个Edit Control,静态文本框写上:当前选中的节点。Edit Control用于显示选中的节点和编辑名字,按钮用于确认更改名字(当然也可以不用按钮来确认)。总体效果如下图

MFC制作一个可自由添加删除节点的树(CTreeCtrl)类_第2张图片

4.在完成了上述步骤后,我们就可以开始插入自己的树啦,首先,为了美观,我们需要准备几个小图标,记得用icon转换器转换格式,之后放入res文件夹内,之后,在资源试图,右击XXX.rc项,选择添加资源,之后选择Icon,然后导入,将刚刚保存的小图片导入进来,之后为其设置ID。同时在对话框的.h文件中申明CImageList m_imageList,用于图标的控制。

之后找到系统生成的BOOL CMFC_Study_TreeDlg::OnInitDialog()函数,添加初始化树代码,使得在程序运行的时候可以显示出刚刚设置的树。

在使用图标之前,需要将导入的图标依次加入图标序列,代码如下:

//TODO:在此添加额外的初始化代码
HICON hMyIcon[3];      // 图标句柄数组   

						 // 加载图标
	hMyIcon[0] = theApp.LoadIcon(ID_ICON_0);
	hMyIcon[1] = theApp.LoadIcon(ID_ICON_1);
	hMyIcon[2] = theApp.LoadIcon(ID_ICON_2);

	// 创建图像序列CImageList对象   
	m_imageList.Create(32, 32, ILC_COLOR32, 3, 3);
	// 将三个图标添加到图像序列   
	for (int i = 0; i<3; i++)
	{
		m_imageList.Add(hMyIcon[i]);
	}

	// 为树形控件设置图像序列   
	m_webTree.SetImageList(&m_imageList, TVSIL_NORMAL);
这样一来,便将图标和树关联了起来,之后可以使用
CTreeCtrl::InsertItem函数的一个重载插入树了,该函数申明如下

  HTREEITEM InsertItem(LPCTSTR lpszItem,int nImage,int nSelectedImage,HTREEITEM hParent = TVI_ROOT,HTREEITEM hInsertAfter = TVI_LAST);

其中lpszItem为标题,nimage为未选中时的图标序号,nSelectedImage为选中时的图标序号,此序号与之前添加图像序列的顺序有关,hParent 表示插入的新树将作为hParent的子树,hParent是一个句柄,hInsertAfter 表示插入在谁之后,也是一个句柄。

CTreeCtrl::InsertItem()插入代码如下

	HTREEITEM hRoot;     // 树的根节点的句柄   
	HTREEITEM hSecondLeaf; // 第二层  
	HTREEITEM hThirdLeaf;  // 第三层

	hRoot = m_webTree.InsertItem(_T("爷爷"), 0, 0);
	// 在根节点下插入子节点   
		hSecondLeaf= m_webTree.InsertItem(_T("爸爸1号"), 1, 1, hRoot, TVI_LAST);//插入第二层节点
	hThirdLead= m_webTree.InsertItem(_T("爸爸1号的儿子1"), 2, 2, hSecondLeaf, TVI_LAST);//插入第三层节点
	hThirdLeaf= m_webTree.InsertItem(_T("爸爸1号的儿子2"), 2, 2, hSecondLeaf, TVI_LAST);
之后运行,得到如下效果
MFC制作一个可自由添加删除节点的树(CTreeCtrl)类_第3张图片

5.有了自己的树后,便可以将选中的树的名字显示到文本框中。我们可以为树形控件IDC_WEB_TREE的通知消息TVN_SELCHANGED添加消息处理函数,这样一来在我们鼠标切换节点的时候就可以进入这个消息处理函数

/*******************************************将树的标题显示到对话框中************************************/

void CMFC_Study_TreeDlg::OnTvnSelchangedWebtree(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMTREEVIEW pNMTreeView = reinterpret_cast(pNMHDR);
	// TODO: 在此添加控件通知处理程序代码
	*pResult = 0;
	CString strText; // 树节点的标签文本字符串   
	 // 获取当前选中节点的句柄   
	HTREEITEM hItem = m_webTree.GetSelectedItem();
	// 获取选中节点的标签文本字符串   
	strText = m_webTree.GetItemText(hItem);
	// 将字符串显示到编辑框中   
	SetDlgItemText(IDC_ITEM_SEL_EDIT, strText);

}

CTreeCtrl::GetSelectedItem()十分常用,用于得到选中节点的句柄,GetrItemText函数用于得到树的标签的字符,与此对应的是SetItemText,用于将字符串设置为树的标题,之后会用到。

到现在树的基本功能便实现了,接下来,我们需要为树设置右键菜单。

6.我设计的是一个3层数,根节点不能删除,第三层树不能添加子树,我使用了3个不同的菜单(当然也有更好的方法)

设置菜单在资源试图里,右键XXX.rc,选择添加资源,选择Menu,新建,之后为自己的菜单设置好ID,然后就可以调用啦。

在树的右击事件中编写如下代码,用于显示菜单

/***************************************添加右键菜单*****************************************************/
void CMFC_Study_TreeDlg::OnNMRClickWebtree(NMHDR *pNMHDR, LRESULT *pResult)
{
	// TODO: 在此添加控件通知处理程序代码
	*pResult = 0;
	CPoint point;
	GetCursorPos(&point);         //得到当前位置
	CPoint pointInTree = point;  
	m_webTree.ScreenToClient(&pointInTree);       //该函数把屏幕上指定点的屏幕坐标转换成用户坐标
	HTREEITEM item;
	UINT flag = TVHT_ONITEM;
	item = m_webTree.HitTest(pointInTree, &flag);        //函数能够得到与当前鼠标位置相关的项
	CMenu menu;
	m_webTree.SelectItem(item);        //选中当前右键树
	HTREEITEM curPosition = m_webTree.GetSelectedItem();

	if ((item != NULL) && (GetLevel(curPosition)==2))   //第二层树
	{
		menu.LoadMenu(IDR_MY_MENU2);
		menu.GetSubMenu(0)->TrackPopupMenu(TPM_LEFTALIGN |
			TPM_RIGHTBUTTON, point.x, point.y, this, NULL);
	}
	else if ((GetLevel(curPosition) == 3))
	{
		menu.LoadMenu(IDR_MENU_NOSONTREE);    //第三层树菜单
		menu.GetSubMenu(0)->TrackPopupMenu(TPM_LEFTALIGN |
			TPM_RIGHTBUTTON, point.x, point.y, this, NULL);
	}
	else   //根节点
	{
		menu.LoadMenu(IDR_MENU_ROOT);
		menu.GetSubMenu(0)->TrackPopupMenu(TPM_LEFTALIGN |
		TPM_RIGHTBUTTON, point.x, point.y, this, NULL);
	}
}
由于我也是初学并且有点智障,找了半天没找到哪个函数用于获取树的层次,于是索性自己写了一个,通过找Parent的方法判断是第几层树。

int CMFC_Study_TreeDlg::GetLevel(HTREEITEM inputTree)
{
	HTREEITEM temp;
	temp = inputTree;
	int level = 0;
	while (temp != NULL)
	{
		temp = m_webTree.GetParentItem(temp);
		++level;
	}
	return level;
}
这样我就可以通过层数来设置不同的菜单了。

7.设置好菜单后,可以为每一个菜单编辑事件啦。添加一颗树的代码如下

void CMFC_Study_TreeDlg::OnMenu_MENU_ROOT_ADDNEWNODE()
{
	// TODO: 在此添加命令处理程序代码
	HTREEITEM curPosition;
	curPosition = m_webTree.GetSelectedItem();        //将返回当前选中的结点的句柄
	int imageNum = GetLevel(curPosition);//得到设置的是第几层树,由此确定不同的图标
	m_webTree.InsertItem(_T("我是新生的结点!"), imageNum, imageNum, curPosition, TVI_LAST);
}

删除一颗树的代码如下:

void CMFC_Study_TreeDlg::OnMenuDelteThissontree() //没有子树的节点的右键删除命令
{
	// TODO: 在此添加命令处理程序代码
	HTREEITEM curPosition;
	curPosition = m_webTree.GetSelectedItem();        //将返回当前选中的结点的句柄
	m_webTree.DeleteItem(curPosition);
}

8.最后,我们来设置给树重命名

为之前的Edit Control关联一个变量TreeName,然后,为更改名字按钮设置事件,就可以在那个编辑框更改名字啦,不过千万不要忘记UpdaData,之前忘了可是纠结了好久呢

void CMFC_Study_TreeDlg::OnBnClickedButton1()
{
	// TODO: 在此添加控件通知处理程序代码
	UpdateData();
	HTREEITEM hItem = m_webTree.GetSelectedItem();
	m_webTree.SetItemText(hItem, TreeName);

}
9.到此,一个简单的可自由增删节点的树便设计好啦








你可能感兴趣的:(MFC制作一个可自由添加删除节点的树(CTreeCtrl)类)