CTreeCtrl 控件使用总结
这篇文章是11年写的,目前有32万阅读量,今天打开CSDN,发现有一条通知,你的一篇博客被删除,我一看,竟然是这篇,目前阅读量32万,评论也很好,可惜被删除不能回复了。
一 基础操作
1 插入节点
1)插入根节点
[cpp]view plaincopy
//插入根节点
HTREEITEM hRoot;
CString str=L"ROOT"
hRoot=nTreeCtrl.InsertItem(str);
//相当于
hRoot=nTreeCtrl.InsertItem(str,TVI_ROOT,TVI_LAST);
2)插入孩子节点
[cpp]view plaincopy
//添加hRoot节点的孩子节点,并且被添加的节点位于hRoot所有孩子节点的末尾
HTREEITEM hChild=nTreeCtrl.InsertItem(str,hRoot);
//相当于
HTREEITEM hChild=nTreeCtrl.InsertItem(str,hRoot,TVI_LAST);
2 获得节点句柄
[cpp]view plaincopy
//获得根节点
HTREEITEM hRootItem;
hRootItem=nTreeCtrl.GetRootItem();
//获得当前节点
HTREEITEM hCurrentItem;
hCurrentItem=nTreeCtrl.GetSelectedItem();
//获得hItem的前一个节点
HTREEITEM hPreItem;
hPreItem=nTreeCtrl.GetNextItem(hItem,TVGN_PREVIOUS);
//获得hItem的下一个节点
HTREEITEM hNextItem;
hNextItem=nTreeCtrl.GetNextItem(hItem,TVGN_NEXT);
3 判断某节点是否有孩子节点
[cpp]view plaincopy
//判断某节点是否有孩子节点
if(nTreeCtrl.ItemHasChildren(hRoot))
4 展开或收缩子节点
[cpp]view plaincopy
//展开
if(nTreeCtrl.ItemHasChildren(hRoot))
nTreeCtrl.Expand(hParentItem,TVE_EXPAND);
5 获得第一个孩子节点的句柄
[cpp]view plaincopy
//判断某节点是否有孩子节点
if(nTreeCtrl.ItemHasChildren(hRoot))
{
//获得孩子节点
HTREEITEM hChild=nTreeCtrl.GetChildItem(hRoot);
}
6 遍历hRoot下一层的所有孩子节点
[cpp]view plaincopy
//判断某节点是否有孩子节点
if(nTreeCtrl.ItemHasChildren(hRoot))
{
//获得孩子节点
HTREEITEM hChild=nTreeCtrl.GetChildItem(hRoot);
//遍历hRoot下一层的所有孩子节点
while(hChild)
{
hChild=nTreeCtrl.GetNextItem(hChild,TVGN_NEXT);
}
}
7 获得某节点上的文字
[cpp]view plaincopy
//获得某节点上的文字
CString str;
nTreeCtrl.GetItemText(hRoot);
8 选择某节点,并让其获得焦点
首先,TREE控件的样式必须设置为TVS_SHOWSELALWAYS
其次: 选择该节点
[html]view plaincopy
treeCtrl.SelectItem(hItem);
最后,设置焦点
[html]view plaincopy
treeCtrl.SetFocus();
Tree控件设置焦点后,会自动将焦点定位到选择的节点上
9 清空树控件
[cpp]view plaincopy
nTreeCtrl.DeleteAllItems();
10 将指定目录下的文件插入节点
[cpp]view plaincopy
voidInsertPath(CString path, HTREEITEM hRoot, CTreeCtrl& ctrl)
{
CFileFind nFindFile;
CString str=L"";
CString nPicFileName=L"";
BOOLIsExist=FALSE;
HTREEITEM hSubItem;
nPicFileName.Format(L"%s\\*.*",path);
IsExist = nFindFile.FindFile(nPicFileName);
while(IsExist)
{
IsExist = nFindFile.FindNextFile();
if(nFindFile.IsDots())
continue;
nPicFileName = nFindFile.GetFileName();
//路径
if(nFindFile.IsDirectory())
{
hSubItem = ctrl.InsertItem(nPicFileName,hRoot);
InsertPath(nFindFile.GetFilePath(),hSubItem,ctrl);
}
else
{
//文件
str = nPicFileName.Right(4);
if(!str.CompareNoCase(_T(".jpg")) || !str.CompareNoCase(_T(".tif")))
{
ctrl.InsertItem(nPicFileName,hRoot);
}
}
}
nFindFile.Close();
}
[cpp]view plaincopy
voidLoadPath(CString path)//path为指定目录 此函数的作用为将path目录下的文件插入树控件中
{
CTreeCtrl& ctrl = GetTreeCtrl();
ASSERT(ctrl);
ctrl.DeleteAllItems();
HTREEITEM hRoot = ctrl.InsertItem(path);
InsertPath(path,hRoot,ctrl);
ctrl.Expand(hRoot,TVE_EXPAND);
}
11 将文件列表中的文件插入树控件中
[cpp]view plaincopy
voidInsetAllFile( list& filePathList){
CTreeCtrl & nTreeCtrl=((CMyTreeView*)(((CMainFrame*)AfxGetMainWnd())->m_SplitterWnd.GetPane(0,0)))->GetTreeCtrl();
nTreeCtrl.DeleteAllItems();
list::iterator it=filePathList.begin();
HTREEITEM hRoot=NULL;
CString filePath;
CString treeRootName=L"根目录";//所有的文件都在根目录下 即:默认所有的文件都在同一个目录下
while(it!=filePathList.end())
{
filePath=*it;
if(hRoot==NULL)
hRoot=nTreeCtrl.InsertItem(treeRootName);//建立根目录
if(filePath.Find(treeRootName)==0)// 文件第一层目录与根目录相同,则截去文件第一层目录,文件从第二层目录开始
filePath=filePath.Right(filePath.GetLength()-treeRootName.GetLength()-1);
LoadPicFiles(nTreeCtrl,filePath, hRoot);
it++;
}
}
[cpp]view plaincopy
voidLoadPicFiles(CTreeCtrl& nTreeCtrl, CString nFilePath, HTREEITEM nRoot)
{
// 判断nPicFolder是目录还是文件
// 如果是文件
// 直接将文件插入到树控件中 nTreeCtrl.InsertItem(nPicFolder,nRoot);
// 如果是目录
// 获取nPicFolder的第一层目录
// 判断nRoot目录下是否已经有此层目录
// 如果有此层目录
// 递归插入其他
// 如果无此层目录
// 插入此层目录,然后递归插入其他
CString nSubFolder;//首层目录
CString nSubFilePath;//去掉首层目录后的文件名
BOOLIsExist=FALSE;
intnIndex=-1;
nIndex=nFilePath.Find(L'\\');
if(nIndex>=0)//目录
{
nSubFolder=nFilePath.Left(nIndex);
nSubFilePath=nFilePath.Right(nFilePath.GetLength()-nIndex-1);
HTREEITEM nSubRoot=NULL;
if(nTreeCtrl.ItemHasChildren(nRoot))
nSubRoot=nTreeCtrl.GetChildItem(nRoot);
CString str;
BOOLbExist=FALSE;
while(nSubRoot)
{
str=nTreeCtrl.GetItemText(nSubRoot);
if(str.CompareNoCase(nSubFolder)==0)
{
bExist=TRUE;
break;
}
nSubRoot=nTreeCtrl.GetNextSiblingItem(nSubRoot);
}
if(!bExist)
{
nSubRoot=nTreeCtrl.InsertItem(nSubFolder,nRoot);
LoadPicFiles(nTreeCtrl,nSubFilePath,nSubRoot);
}else{
LoadPicFiles(nTreeCtrl,nSubFilePath,nSubRoot);
}
}
elseif(nFilePath.Find(L".jpg")!=-1 || nFilePath.Find(L".tif")!=-1)
{
nTreeCtrl.InsertItem(nFilePath,nRoot);
}
}
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
二 扩展操作
1 响应TVN_ITEMEXPANDING 消息时 如何获得将要展开或收缩的那一个节点的句柄
MSDN:
TVN_ITEMEXPANDINGpnmtv= (NM_TREEVIEW FAR *)lParam
pnmtv
Pointer to anNM_TREEVIEWstructure. TheitemNewmember is aTVITEMstructure that contains valid information about the parent item in thehItem,state, andlParammembers. Theactionmember indicates whether the list is to expand or collapse. For a list of possible values, see the description of theTVM_EXPANDmessage.
。。。。。。。。。
[cpp]view plaincopy
typedefstruct_NM_TREEVIEW {
NMHDR hdr;
UINTaction;
TV_ITEM itemOld;
TV_ITEM itemNew;
POINT ptDrag;
} NM_TREEVIEW;
typedefNM_TREEVIEW FAR* LPNM_TREEVIEW;
[cpp]view plaincopy
typedefstruct_TV_ITEM { tvi
UINTmask;
HTREEITEM hItem;
UINTstate;
UINTstateMask;
LPSTRpszText;
intcchTextMax;
intiImage;
intiSelectedImage;
intcChildren;
LPARAMlParam; }
TV_ITEM, FAR* LPTV_ITEM;
在 TV_ITEM 的 hItem中 存放着要展开项的句柄
解决:查了这么多,其实很简单 代码如下:
[cpp]view plaincopy
voidCLeftView::OnItemexpanding(NMHDR* pNMHDR,LRESULT* pResult)
{
LPNMTREEVIEW pNMTreeView =reinterpret_cast(pNMHDR);
// TODO: 在此添加控件通知处理程序代
HTREEITEM htree=pNMTreeView->itemNew.hItem;// 这个就是 将要被扩展或收缩节点的句柄
。。。
}
2 怎么知道CTreeCtrl的一个节点是展开的还是收缩着的
解决:
方法1
[cpp]view plaincopy
(GetItemState(hItem, TVIS_EXPANDED )&TVIS_EXPANDED)!=TVIS_EXPANDED//如果相等,则说明改节点是扩展的,如果不相等,则说明该节点是收缩的
方法2
响应TVN_ITEMEXPANDING事件时:
[cpp]view plaincopy
voidCExampleDlg::OnItemexpandingTree1(NMHDR* pNMHDR,LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
if(pNMTreeView->action == TVE_COLLAPSE)//判断action的值
。。。
。。。
}
3 判断节点是否被扩展过
[cpp]view plaincopy
if((GetTreeCtrl().GetItemState(hItem,TVIS_EXPANDEDONCE )&TVIS_EXPANDEDONCE )!=0 )//判断是否扩展过一次,若!=0则说明被扩展过
4 使用 CImageList m_ImageList; 加载位图或图标,并将其与树控件联系在一起,由此便可以设置每个节点的图标
[cpp]view plaincopy
CImageList m_ImageList;
m_ImageList.Create(12,12,ILC_COLORDDB | ILC_MASK, 3, 1);
HICONhAdd=::LoadIcon(::AfxGetInstanceHandle(), (LPCTSTR)IDI_ADD);
HICONhRemove=::LoadIcon(::AfxGetInstanceHandle(), (LPCTSTR)IDI_REMOVE);
HICONhLeaf=::LoadIcon(::AfxGetInstanceHandle(), (LPCTSTR)IDI_LEAF);
m_ImageList.Add(hAdd);
m_ImageList.Add(hRemove);
m_ImageList.Add(hLeaf);
GetTreeCtrl().SetImageList(&m_ImageList,TVSIL_NORMAL);// 树控件和图像列表相连
[cpp]view plaincopy
m_treeCtrl.SetItemImage(htree,0,0)// 通过SetItemImage(htree,0,0) 设置节点的图标
5 什么时候响应OnItemexpanding 消息
当节点第一次被展开时,才响应此消息。也就是说:当以开后该节点再展开或收缩时,便不再响应此消息了。
6 设置树控件形式为 TVS_HASBUTTONS|TVS_LINESATROOT 时, 树控件节点前才会出现+ - 号
以下为综合例子: 点击按钮上一个 显示该节点的上一个兄弟节点,并更改控件焦点
设置控件样式:
[html]view plaincopy
BOOL CTreePathView::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: 在此处通过修改
// CREATESTRUCT cs 来修改窗口类或样式
cs.style|=TVS_HASLINES|TVS_SHOWSELALWAYS; //若是想用CImageList的图标 ,则不要设置为TVS_HASBUTTONS形式
return CTreeView::PreCreateWindow(cs);
}
点击按钮5(焦点移动到上一个兄弟节点)
[html]view plaincopy
void NewImageView::OnBnClickedButton5() // 上一个图
{
// TODO: 在此添加控件通知处理程序代码
CTreePathView *pTree=(CTreePathView* )(((CMainFrame *)AfxGetMainWnd())->m_wndSplitter.GetPane(0,0));
CTreeCtrl &treeCtrl=pTree->GetTreeCtrl();
HTREEITEMhItem=treeCtrl.GetSelectedItem();
if (hItem!=NULL)
{
hItem=treeCtrl.GetNextItem(hItem,TVGN_PREVIOUS);
if (hItem!=NULL)
{
CString str;
str=pTree->GetFullPath(hItem);
SetImage(str);
treeCtrl.SelectItem(hItem);
treeCtrl.SetFocus();
InvalidateRect(m_ClientRect);
}
}
}
点击按钮6(焦点移动到下一个兄弟节点)
[html]view plaincopy
void NewImageView::OnBnClickedButton6() //下一个
{
// TODO: 在此添加控件通知处理程序代码
CTreePathView *pTree=(CTreePathView* )(((CMainFrame *)AfxGetMainWnd())->m_wndSplitter.GetPane(0,0));
CTreeCtrl &treeCtrl=pTree->GetTreeCtrl();
HTREEITEMhItem=treeCtrl.GetSelectedItem();
if (hItem!=NULL)
{
hItem=treeCtrl.GetNextItem(hItem,TVGN_NEXT);
if (hItem!=NULL)
{
CString str;
str=pTree->GetFullPath(hItem);
SetImage(str);
treeCtrl.SelectItem(hItem);
treeCtrl.SetFocus();
InvalidateRect(m_ClientRect);
}
}
}
7 遍历树控件的所有节点
1) 获得根节点句柄
[cpp]view plaincopy
CTreeCtrl& nTreeCtrl=((CImportTreeView*)m_SplitterWnd.GetPane(0,0))->GetTreeCtrl();
HTREEITEM hItem;
//获得根目录节点
hItem = nTreeCtrl.GetRootItem();
//遍历树控件节点
TreeVisit(&nTreeCtrl,hItem);
2)遍历所有节点
[cpp]view plaincopy
voidTreeVisit(CTreeCtrl* pCtrl,HTREEITEM hItem)
{
if(pCtrl->ItemHasChildren(hItem))
{
HTREEITEM hChildItem = pCtrl->GetChildItem(hItem);
while(hChildItem!=NULL)
{
TreeVisit(pCtrl,hChildItem);//递归遍历孩子节点
hChildItem = pCtrl->GetNextItem(hChildItem, TVGN_NEXT);
}
}
else// 对叶子节点进行操作
Leaf(pCtrl,hItem);
}
8 获得某Item节点的全路径
[cpp]view plaincopy
CString m_ParentFolder[10];
CString m_OldParentFolder[10];
[cpp]view plaincopy
//--------------------将nParent添加到nParentFolder[10]第一位----------------------
BOOLAddParentFolder(CString nParentFolder[10], CString nParent)
{
for(inti=9;i>0;i--)
nParentFolder[i]=nParentFolder[i-1];
nParentFolder[0]=nParent;
returnTRUE;
}
//---------------------nParentFolder[10]中的有效数据整合(加\)---------------------
CString AllCString(CString nParentFolder[10])
{
CString nAllCString=L"";
for(inti=0;i<10;i++)
{
if(nParentFolder[i]==L"")break;
nAllCString+=L"\\"+nParentFolder[i];
}
returnnAllCString;
}
获得Item节点路径的函数
[cpp]view plaincopy
CString GetItemPath(CTreeCtrl* pCtrl,HTREEITEM hItem)
{
CString nSelItemName=pCtrl->GetItemText(hItem);
HTREEITEM parentItem=pCtrl->GetParentItem(hItem);
if(parentItem==NULL)//hItem即为根目录
returnnSelItemName;
//清空OLD
for(inti=0;i<10;i++) m_OldParentFolder[i]=L"";
//m_OldParentFolder 记录上一个节点的父节点
for(inti=0;i<10;i++)
m_OldParentFolder[i]=m_ParentFolder[i];
//m_ParentFolder 记录当前节点的父亲节点
for(inti=0;i<10;i++)
m_ParentFolder[i]=L"";
CString itemPath;
CString parentFolder=nSelItemName;
//将parentFolder添加到m_ParentFolder[0],其他值依次后移
AddParentFolder(m_ParentFolder,parentFolder);
// m_PicFolder 为根节点对应的名字
while(parentItem!=NULL&&pCtrl->GetItemText(parentItem).Compare(m_PicFolder))
{
parentFolder=pCtrl->GetItemText(parentItem);
AddParentFolder(m_ParentFolder,parentFolder);
parentItem=pCtrl->GetParentItem(parentItem);
}
itemPath.Format(L"%s%s",m_PicFolder,AllCString(m_ParentFolder));
//清空OLD
for(inti=0;i<10;i++) m_OldParentFolder[i]=L"";
//清空
for(inti=0;i<10;i++)
m_ParentFolder[i]=L"";
returnitemPath;
}
获得叶子节点的函数
[cpp]view plaincopy
voidLeaf(CTreeCtrl* pCtrl,HTREEITEM hItem)
{
CString itemName=pCtrl->GetItemText(hItem);
// 叶子节点是jpg文件或tif文件
if(nSelItemName.Find(L".jpg")!=-1 || nSelItemName.Find(L".tif")!=-1)
{
//m_OldParentFolder 记录上一个节点的父节点
for(inti=0;i<10;i++)
m_OldParentFolder[i]=m_ParentFolder[i];
//m_ParentFolder 记录当前节点的父亲节点
for(inti=0;i<10;i++)
m_ParentFolder[i]=L"";
CString imgPath=L"";
CString parentFolder=itemName;
//将parentFolder添加到m_ParentFolder[0],其他值依次后移
AddParentFolder(m_ParentFolder,parentFolder);
HTREEITEM parentItem=pCtrl->GetParentItem(hItem);
// m_imgPath 为根节点对应的名字
while(pCtrl->GetItemText(parentItem).Compare(m_imgPath))
{
parentFolder=pCtrl->GetItemText(parentItem);
AddParentFolder(m_ParentFolder,parentFolder);
parentItem=pCtrl->GetParentItem(parentItem)
}
// 获得叶子节点的全路径
imgPath.Format(L"%s%s",m_imgPath,AllCString(m_ParentFolder));
}
// 对imgPath 所指的文件进行操作
ShowPic(imgPath);
}
上述方法过于繁杂,再来了简洁些的
使用栈,依次将本节点-->根节点入栈 出栈时顺序便为根节点-->本节点
1)叶子节点
[cpp]view plaincopy
//本地是否存在此文章
voidCMainFrame::PostPath(CTreeCtrl& nTreeCtrl, HTREEITEM hItem,CString &path)
{
stack itemStack;
while(hItem!=nTreeCtrl.GetRootItem ())
{
itemStack.push(hItem);
hItem=nTreeCtrl.GetParentItem (hItem);
}
itemStack.push(nTreeCtrl.GetRootItem ());
CString itemName;
while(!itemStack.empty())
{
hItem=(HTREEITEM)itemStack.top();
itemStack.pop();
itemName=nTreeCtrl.GetItemText (hItem);
path+=itemName;
path+=L"\\";
}
path.TrimRight(L"\\");
path+=L".xml";
}
2)目录节点
[cpp]view plaincopy
voidCMainFrame::DirPath(CTreeCtrl& nTreeCtrl, HTREEITEM nRoot,CString &path)
{
stack itemStack;
while(hItem!=nTreeCtrl.GetRootItem ())
{
itemStack.push(hItem);
hItem=nTreeCtrl.GetParentItem (hItem);
}
itemStack.push(nTreeCtrl.GetRootItem ());
CString itemName;
while(!itemStack.empty())
{
hItem=(HTREEITEM)itemStack.top();
itemStack.pop();
itemName=nTreeCtrl.GetItemText (hItem);
path+=itemName;
path+=L"\\";
}
}
9 获得树中所有叶子节点的父目录
即:树中可能有许多枝干,获取这些枝干的路径
[cpp]view plaincopy
std::vector m_BookDirectory;//存放所有叶子节点的父目录
[cpp]view plaincopy
voidGetBookDirectory(CTreeCtrl* pCtrl,HTREEITEM hItem)
{
if(pCtrl->ItemHasChildren(hItem))
{
HTREEITEM hChildItem = pCtrl->GetChildItem(hItem);
while(hChildItem!=NULL)
{
GetBookDirectory(pCtrl,hChildItem);//递归遍历孩子节点
if(pCtrl->ItemHasChildren(hChildItem))
hChildItem = pCtrl->GetNextItem(hChildItem, TVGN_NEXT);
else
break;
}
}
else
{
HTREEITEM parentItem=pCtrl->GetParentItem(hItem);
CString bookPath=GetItemPath(pCtrl,parentItem);
m_BookDirectory.push_back(bookPath);
}
}
[cpp]view plaincopy
CTreeCtrl& nTreeCtrl=((CImportTreeView*)m_SplitterWnd.GetPane(0,0))->GetTreeCtrl();
HTREEITEM hItem;
hItem = nTreeCtrl.GetRootItem();
m_BookDirectory.clear();
GetBookDirectory(&nTreeCtrl,hItem);//获得几本书 及书的路径
10 利用InsertItem、SetItemData 存放与该节点有关的数字信息
[cpp]view plaincopy
HTREEITEM InsertItem(
LPCTSTRlpszItem,
intnImage,//实测范围0-65535
intnSelectedImage,
HTREEITEM hParent = TVI_ROOT,
HTREEITEM hInsertAfter = TVI_LAST
);
存放65535以上的大数据时 用SetItemData
A 32-bit application-specific value
[cpp]view plaincopy
BOOLSetItemData(
HTREEITEM hItem,
DWORD_PTRdwData
);
网友评论