您的位置:首页 > 其它

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

2017-04-23 10:01 351 查看


微软的MFC提供了强大的界面设计功能,但因为其很多函数过于繁琐,对初学者来说不好上手,而我自己在学习过程中也碰到了很多问题,查了很多文章,在此要特地感谢鸡琢米以及各位在CSDN上些小教程的各位前辈大佬。因此我打算分享一下我的学习经验。
1.第一步在Visual Studio 2017中创建一个对话框工程,并删除原有的3个控件


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



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



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);
之后运行,得到如下效果



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

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

void CMFC_Study_TreeDlg::OnTvnSelchangedWebtree(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(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.到此,一个简单的可自由增删节点的树便设计好啦
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐