树型控制是Win 95支持的公共控制之一,功能极强,对多数事件的响应都是自动实现的。如果 使用它来开发访问像Win 95注册库这样具有层次结构的复杂程序,则能够让您省事不少。有关树型控制及Win 95注册库本身的一些知识,请参考联机文挡。本文着重介绍采用树型控制展现Win 95注册库的有关实现方法及遇到问题时的处理过程。
一、加入树型控制和像表小位图
首先,建立以对话框为基础的主应用程序。然后,在其对话框资源中加入树型控制。可供选择的树型控制的风格属性(Styles)有:按钮(树中出现具有支持展开或收缩树层的功能的+或-操作按钮,对下面的风格属性可作类似理解)、连接线、线在根处等等。最后,加入树型控制的对应成员。例如:
CTreeCtrl m_TREECtrlREG;
像表小位图用来表示对树上的注册库关键字或子关键字被操作的状态,是可选的。为简单起见,可只用两幅小位图来区分有或无子关键字的关键字:一幅用来表示是有子关键字的关键字(类似于目录),另外一幅用来表示是无子关键字的关键字(类似于文件)。小位图的宽为15,高为16。
以上具体操作过程从略。
二、 最顶层和六个主层关键字名插入树
在对话框的初始化函数OnInitDialog()中适宜做此工作,通过OnInitRootKey()函数来实现:
BOOL CWRegTreeCtrlDlg::OnInitDialog()
{ ......
//Add extra initialization here
//像表小位图的建立
CImageList* pMyIL = new CImageList();
pMyIL->Create(16, 15, TRUE, 3, 2);
CBitmap MyBmp;
for(UINT nIndef=IDB_BMPBEG;
nIndef<=IDB_BMPEND; nIndef++)
{ MyBmp.LoadBitmap(nIndef);
pMyIL->Add(&MyBmp, (COLORREF)0xFFFFFF);
MyBmp.DeleteObject();
}
m_TREECtrlREG.SetImageList(pMyIL, TVSIL_NORMAL);
//最顶层和六个主层关键字名插入树子函数
OnInitRootKey();
return FALSE;
}
VOID CWRegTreeCtrlDlg::OnInitRootKey()
{ ......
//全部清除树视图
m_TREECtrlREG.DeleteAllItems();
//建立初始树视图:最顶层和六个主层
TV_INSERTSTRUCT TreeCtrlItem;//树控制项
TreeCtrlItem.hParent = TVI_ROOT;//最顶层
TreeCtrlItem.hInsertAfter = TVI_LAST;
TreeCtrlItem.item.iImage = 1;//目录
TreeCtrlItem.item.iSelectedImage = 1;
TreeCtrlItem.item.mask=TVIF_IMAGE|TVIF_SELECTEDIMAGE| TVIF_TEXT|TVIF_CHILDREN;
TreeCtrlItem.item.pszText =_T(strTemp0.GetBuffer(20));
//“<我的电脑>顶层”
//插入树根
hTreeItem[0]=m_TREECtrlREG.InsertItem(&TreeCtrlItem);
TreeCtrlItem.hParent =hTreeItem[0];//第一主层
TreeCtrlItem.hInsertAfter = TVI_LAST;
TreeCtrlItem.item.iImage = 1;
TreeCtrlItem.item.iSelectedImage = 1;
TreeCtrlItem.item.mask=TVIF_IMAGE|TVIF_SELECTEDIMAGE| TVIF_TEXT|TVIF_CHILDREN;
TreeCtrlItem.item.pszText =_T(rStr.GetBuffer(18) );
//“HKEY_CLASSES_ROOT”
//插入树根
hTreeItem[1]= m_TREECtrlREG.InsertItem(&TreeCtrlItem);
//插入第二到第六主层与插入第一主层“HKEY_CLASSES_ROOT”的代码类似,具体代码从略
//展开最顶层
m_TREECtrlREG.Expand(hTreeItem[0],TVE_EXPAND);
hCurItem=hTreeItem[0];
......
//访问注册库时最顶层和主层位置路径句柄的初值
hKeyRoot = 0;
return;
}
三、用鼠标单击+或-按钮或者双击树项关键字名操作的响应
在树上插入与显示注册库各子关键字,是在用户用鼠标单击了+或-按钮或者双击了树项关键字名的操作响应过程中动态完成的。由于树型控制的功能极强,所以几乎没有必要做管理树型控制的工作(例如,视图滚动等),只需编写出响应上述操作的代码就可以了。
VOID CWRegTreeCtrlDlg::OnDblclkTreereg(NMHDR*
pNMHDR, LRESULT* pResult)
{ HTREEITEM hIT =
m_TREECtrlREG.GetSelectedItem( );
if( hCurItem != hIT) { hCurItem = hIT; }
SelectItemTr = m_TREECtrlREG.GetItemText(hIT);
//1.单击了“<我的电脑>顶层”后的处理--交树型控制处理
CString MainName ,strTemp0,strTemp1;
strTemp0.LoadString(IDS_STRINGPROMPT0);
strTemp1.LoadString(IDS_STRINGPROMPT1);
if( hIT == hTreeItem[0])
{//将初值放到其它各个控制中
......
//路径,顶层句柄初始化
lpRegPath=_T("");
hKeyRoot = 0;
* pResult=0;
return;
}
//2.单击了六个主层关键字名之一后的处理
if( OnIsMianItem(hIT) )
{//主层位置是否改变
if(m_TOPPOSOUT != SelectItemTr)
{m_TOPPOSOUT = _T("");
m_TOPPOSOUT = SelectItemTr;
}
//将初值放到其它各个控制中
......
//路径,顶层句柄初始化
lpRegPath=_T(""); hKeyRoot = 0;
//新选择的关键字名
m_CURKEYNAME=SelectItemTr;
//调整访问注册库句柄值hKeyRoot
OnMainKeyHandle(hIT,hKeyRoot);
HTREEITEM hChild =
m_TREECtrlREG.GetChildItem(hIT);
//已经展开过,则交树型控制自己反转
if( hChild != NULL )
{ EnumValueName(hKeyRoot);//显示关联值名
* pResult=0;
return ;
}
UpdateData(FALSE);
//没有展开过,则通过访问注册库动态展开子关键字
QueryKeyInfo ( hKeyRoot );
* pResult=0;
return ;
}
//3.子层情况
//3.1 选择的名字是重复选择
if(
SelectItemTr==lpRegPath.Right(SelectItemTr.GetLength( )))
{ * pResult=0; return; }
//3.2 按照选择的关键字名在树上找到它的父关键字的全路径名字(不包含被选择的关键字的名字)。如果不等于lpRegPath,则要修改lpRegPath
CString strParent,strPath;
strParent = SelectItemTr;
//在树上形成该项目的全路径名字,反复往上爬
while( ! OnIsMianItem(hIT) )//是否到达主层位置
{hIT = m_TREECtrlREG.GetParentItem(hIT);
strParent=
(LPCTSTR)m_TREECtrlREG.GetItemText(hIT);
if(OnIsMianItem(hIT) )
{
int pos;
if((pos=strPath.ReverseFind('\\')) != -1)
{CString strTemp00;
strTemp00=strPath.Left(strPath.GetLength()-1);
strPath=strTemp00;
break;
}
}
else//左加上路径
{CString strTemp99;
strTemp99.Format(_T("%s\\%s"),strParent,strPath);
strPath=strTemp99;
}
}
//主关键字的位置是否改变
MainName=m_TREECtrlREG.GetItemText(hIT);
if( MainName != m_TOPPOSOUT)
{ m_TOPPOSOUT = _T("");
m_TOPPOSOUT = MainName;
//调整访问注册库句柄值hKeyRoot
OnMainKeyHandle(hIT,hKeyRoot);
}
if( strPath != lpRegPath ) lpRegPath = strPath;
// 将初值放到其它各个控制中
......
//是否要刷新
HTREEITEM hChild3 = m_TREECtrlREG.GetChildItem( hCurItem );
HTREEITEM hTemp3;
while (hChild3 != NULL)
{ hTemp3 = m_TREECtrlREG.GetNextSiblingItem(hChild3);
m_TREECtrlREG.DeleteItem(hChild3);
hChild3 = hTemp3;
}
UpdateData(FALSE);
//展开一层,即在树上插入与显示子层的直接各子层关键字名字
//参数包括:被选择的关键字名字,路径(不包含被选择的关键字名字),六个主关键字句柄之一
RegLevelExpand ( SelectItemTr,lpRegPath, hKeyRoot);
return ;
}
四、对目录或文件的处理
注册库的子关键字的名字是动态插入到树上的,首次插入到树上的关键字的下面是否又有子关键字是不知道的,因此两个像表小位图也不能确定使用哪一个。在这点上,树型控制也无能为力,它只能通过用户指定插入项目的cChildren,iImage和iSelectedImage标志值为I_CHILDRENCALLBACK,而后在需要确定值时,通过控制向父窗口发送通知TVN_GETDISPINFO来取得需要的信息,但是树型控制将+或-按钮一律设置为+按钮,显然目录和文件两种位图都被设置成了可展开的+按钮,使用户感到迷惑,从而失去了直观性(关键字一旦展开过一次以后,树型控制就可以区分它了!)。解决方法如下所述:
在访问注册库时,对展开关键字再进行检测(幸好注册库可以嵌套打开),检测到是否有子关键字后立即返回,从而可以精确地确定+或-按钮以及位图的状态,并且正确地插入到树上,其它情况树型控制均能够自动处理。
//检测函数入口,参数为:路径,六个主关键字句柄之一
DWORD CWRegTreeCtrlDlg::RegLevelPreExpand(const
CString & lpRegSub, HKEY & hPreKey)
{ HKEY hKey; DWORD RetValue,SubKeyNs;
......
//1.获得路径的句柄
RetValue = (DWORD)RegOpenKeyEx (
(HKEY)hPreKey,LPCTSTR(lpRegSub),0,
(REGSAM) KEY_ENUMERATE_SUB_KEYS|
KEY_EXECUTE ,
(PHKEY)&hKey);//接收返回打开关键字的句柄
//2.打开是否成功检测
......
//3.询问子关键字数信息
SubKeyNs=QuerySubKeyNums (hKey);
//4.关闭注册库
RegCloseKey (hKey);
return SubKeyNs;
}
延伸阅读
文章来源于领测软件测试网 https://www.ltesting.net/