(一)樹控制的主要功能
樹控制和視(Tree Control&View)主要用來顯示具有一定層次結構的數據項,如資源管理器中的磁盤目錄等,以供用戶在其中進行各種選擇。樹控制中的每個數據項包括數據項名稱的文本字符串和用于表示該數據項的圖像,每個數據項下面均可包含各種子項,整個結構就象目錄樹一樣。對于包含各種子項的數據項,可通過鼠標雙擊來展開或合攏,這可以通過控制樹的不同風格來實現樹控制的不同顯示形態(tài)。這些風格主要包括:
TVS_HASLINES表示用連線來連接父項和它下面的各個子項,這可以使樹的顯示層次結構更加清晰,但在無父項的各子項之間并沒有連線;
TVS_LINESATROOT表示在無父項的各子項即根下面的各子項之間存在連線;
TVS_HASBUTTONS表示在帶有子項的父項前面增加一個帶“+”或“-”的按鈕,這使得用戶也可以通過單擊這個小按鈕來實現子項的展開和合攏,當存在子項時,按鈕的初始狀態(tài)為“+”,當子項被展開時,按小按鈕由“+”變?yōu)?#8220;-”號,當子項合攏時,小按鈕由“-”變?yōu)?#8220;+”號,這一風格同樣對于根項無效,如果需要可通過組合TVS_LINESATROOT風格來實現;
TVS_EDITLABELS表示允許讓用戶單擊具有輸入焦點的數據項來修改其名稱。
對于樹控制,MFC中也以兩種形式來封裝,即樹控制(CTREECTRL)和樹視(CTREEVIEW),來滿足用戶的不同需求,對于一般要求的用戶如在對話框中應用,使用樹控制比較方便,而對于具有較高要求的用戶,在使用樹視時還具有視窗口的各種方便特性,可以更好地滿足文檔/視結構的要求。當在窗口中使用樹視時,樹視會占滿兩個窗口的客戶區(qū)域并自動隨窗口的框架結構的調整而調整,并能夠很好地處理諸如菜單、加速鍵和工具條中的各種命令消息。在使用樹視時只要利用其成員函數CtreeView取得其一個引用,就可以象樹控制一樣方便地應用:CtreeCtrl &treeCtrl = GetTreeCtrl()。
(二)樹控制的對象結構
1、樹控制的建立方法
CtreeCtrl&treeCtrl 建立樹控制對象結構
Create 建立樹控制并綁定對象
樹控制CTreeCtrl::Create的調用格式如下:
BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );
其中參數dwStyle用來確定樹控制的類型;rect用來確定樹控制的大小和位置;pParentWnd用來確定樹控制的父窗口,通用是一個對話框并且不能為NULL;nID用來確定樹控制的標識。樹控制的風格可以是下列值的組合:
TVS_HASLINES 表示樹控制在各子項之間存在連線;
TVS_LINESATROOT 表示樹控制在根項之間存在連線;
TVS_HASBUTTONS 表示樹控制視在父項左側存在展開合攏控制按鈕;
TVS_EDITLABELS 表示可以控制鼠標單擊修改樹項的名稱;
TVS_SHOWSELALWAYS 表示選中項即使在窗口失去輸入焦點時仍然保持選中狀態(tài);
TVS_DISABLEDRAGDROP表示禁止樹控制發(fā)送TVN_BEGINDRAG消息
2、樹控制的屬性類
樹控制屬性類包括取得樹控制中項數GetCount、取得樹控制中項相對于父項的偏移值GetIndent、取得樹控制圖像列表控制句柄GetImageList、設置樹控制圖像列表控制句柄SetImageList、取得匹配下一個樹項GetNextItem、判斷給定樹項是否包含子項ItemHasChildren、取得樹項子項GetChildItem、取得下一個同屬樹項GetNextSiblingItem、取得前一個同屬樹項GetPrevSiblingItem、取得父樹項GetParentItem、取得第一個可視樹項GetFirstVisibleItem、取得下一個可視樹項GetNextVisible Item、取得前一個可視的樹項GetPrevVisibleItem、取得被選中的樹項GetSelectedItem、取得根樹項GetRootItem、取得樹項的屬性GetItem、設置樹項的屬性SetItem、取得樹項的狀態(tài)GetItemState、設置樹項的狀態(tài)SetItemState、取得與樹項關聯(lián)圖像GetItemImage、設置與樹項關聯(lián)圖像SetItemImage、取得樹項文本GetItemText、設置樹項文本SetItemText和取得樹項編輯控制句柄GetEditControl等。
3、樹控制的操作方法
樹控制的操作方法包括插入一個樹項InsertItem、刪除一個樹項DeleteItem、刪除所有樹項DeleteAllItems、展開或合攏樹項的子項Expand、選中特定樹項SelectItem、選擇一個樹項作為第一個可視樹項SelectSetFirstVisible、編輯一個可視的樹項EditLabel和排序給定父樹項的子樹項SortChildren等。
(三)樹控制的數據結構
在使用樹控制時需要了解兩個個非常重要的數據結構TV_ITEM和TV_INSERTSTRUCT,前一個數據結構是用來表示樹控制的樹項信息,后一個數據結構是用來定義將樹項增加到數據控制中所需要的數據內容。另外,還需要NM_TREEVIEW、TV_DISPINFO和TV_HITTESTINFO三個數據結構,這幾個數據結構的定義方法如下:
①基本數據項結構
typedef struct _TV_ITEM {
UINT mask; //結構成員有效性屏蔽位
HTREEITEM hItem; //數據項控制句柄
UINT state; //數據項狀態(tài)
UINT stateMask; //狀態(tài)有效性屏蔽位
LPSTR pszText; //數據項名稱字符串
int cchTextMax; //數據項名稱的最大長度
int iImage; //數據項圖標索引號
int iSelectedImage;//選中數據項圖標索引號
int cChildren; //子項標識
LPARAM lParam; //程序定義的32位數據
} TV_ITEM, FAR *LPTV_ITEM;
②插入樹項結構
typedef struct _TV_INSER TSTRUCT {
HTREEITEM hParent; //父項控制句柄
HTREEITEM hInsertAfter; //插入樹項的位置
TV_ITEM item; //數據項的結構
} TV_INSERTSTRUCT, FAR *LPTV_INSERTSTRUCT;
其中插入的位置如果是TVI_FIRST 或TVI_LAST ,則分別插入到樹控制的最前面或最后面,如果是TVI_SORT ,則插入的樹項自動插入到合適的位置。
③樹控制通知消息結構
typedef struct _NM_TREEVIEW {
NMHDR hdr; //通知消息句柄
UINT action; //通知消息標志
TV_ITEM itemOld; //原來的數據結構
TV_ITEM itemNew; //新的數據結構
POINT ptDrag; //拖動指針
} NM_TREEVIEW;
④取得或設置數據結構
typedef struct _TV_DISPINFO { tvdi
NMHDR hdr; //通知消息控制句柄
TV_ITEM item; //數據項結構
} TV_DISPINFO;
⑤指針測試數據結構
typedef struct _TVHITTESTINFO { tvhtst
POINT pt; //客戶區(qū)域屏幕坐標指針
UINT flags; //存放測試結果的變量
HTREEITEM hItem; //測試的數據項結構
} TV_HITTESTINFO, FAR *LPTV_HITTESTINFO;
其中flags測試結果可以是如下值:
TVHT_ABOVE 在客戶區(qū)域上面
TVHT_BELOW 在客戶區(qū)域下面
TVHT_NOWHERE 在客戶區(qū)域中并在最后一項下面
TVHT_ONITEM 在與樹項關聯(lián)的位圖或標簽內
TVHT_ONITEMBUTTON 在與樹項關聯(lián)的按鈕上
TVHT_ONITEMICON 在與樹項關聯(lián)的位圖上
TVHT_ONITEMINDENT 在與樹項關聯(lián)的聯(lián)線上
TVHT_ONITEMLABEL 在與樹項關聯(lián)的標簽上
TVHT_ONITEMRIGHT 在樹項的右側區(qū)域中
TVHT_ONITEMSTATEICON 在用戶定義的狀態(tài)圖標上
TVHT_TOLEFT 在客戶區(qū)域的左側
TVHT_TORIGHT 在客戶區(qū)域的右側
(四)樹控制的應用技巧示例
這里仍以基于對話框演示實例來具體介紹樹控制及其和圖像列表相結構的應用技巧:
通過“FILE->NEW->PROJECTS->MFC AppWizard(EXE)”建立名為VCTREE的工程,在建立過程中選擇基于對話框(Dialog based)的應用;將對話框中的默認控件刪除,并將所有對話框屬性中的Language域設置為Chinese(P.R.C.),以使應用程序支持中文;建立兩個圖標IDI_PM和IDI_CJ,用來表示圖標的選中和非選中狀態(tài),對于每個圖標都應建立32X32和16X16兩種大小,以保證程序的需要;在對話框窗口中添加樹控制對象(TREE CONTROL),并設置五個按鈕“增加|刪除|查看|排序|關閉”,其對應標識分別如下:
控制名稱 標題名稱 標識符號
樹控制 IDC_TREECTRL
按鈕 增 加 IDC_ADD
刪 除 IDC_DEL
查 看 IDC_VIEW
排 序 IDC_SORT
關 閉 IDOK
5、選中樹控制控件,選擇“VIEW->ClassWizard->Memory Variables。 骺刂艻DC_TREECTRL 引入成員變量,其變量類型為:
變量名 種類 變量類型
m_TreeCtrl Control CTreeCtrl
同時利用“MESSAGES MAP”為各命令按鈕增加控制功能函數。
6、然后在代碼文件VCTREEDlg.CPP中分別加入如下控制代碼:
(1)在文件開始處增加圖像列表定義
CImageList Cil1,Cil2;//大小圖標像列表
(2)在初始化文件開始處增加代碼
BOOL CVCTREEDlg::OnInitDialog()
{ CDialog::OnInitDialog();
......//原來其它代碼
// TODO: Add extra initialization here
// 此處開始增加代碼
CVCTREEApp *pApp=(CVCTREEApp *)AfxGetApp();//創(chuàng)建圖象列表
Cil1.Create(16,16,ILC_COLOR,2,2);
Cil1.Add(pApp->LoadIcon(IDI_PM));
Cil1.Add(pApp->LoadIcon(IDI_CJ));
m_TreeCtrl.SetImageList(&Cil1,TVSIL_NORMAL); //設置圖象列表
DWORD dwStyles=GetWindowLong(m_TreeCtrl.m_hWnd,GWL_STYLE);//獲取樹控制原風格
dwStyles|=TVS_EDITLABELS|TVS_HASBUTTONS|TVS_HASLINES|TVS_LINESATROOT;
SetWindowLong(m_TreeCtrl.m_hWnd,GWL_STYLE,dwStyles);//設置風格
char * CJ[4]={"玉溪卷煙廠","云南卷煙廠","沈陽卷煙廠","成都卷煙廠"};//根數據名稱
char * PM[4][5]={
{"紅梅一","紅梅二","紅梅三","紅梅四","紅梅五"},//產品數據項
{"白梅一","白梅二","白梅三","白梅四","白梅五"},
{"綠梅一","綠梅二","綠梅三","綠梅四","綠梅五"},
{"青梅一","青梅二","青梅三","青梅四","青梅五"}};
int i,j;
HTREEITEM hRoot,hCur;//樹控制項目句柄
TV_INSERTSTRUCT TCItem;//插入數據項數據結構
TCItem.hParent=TVI_ROOT;//增加根項
TCItem.hInsertAfter=TVI_LAST;//在最后項之后
TCItem.item.mask=TVIF_TEXT|TVIF_PARAM|TVIF_IMAGE|TVIF_SELECTEDIMAGE;//設屏蔽
TCItem.item.pszText="數據選擇";
TCItem.item.lParam=0;//序號
TCItem.item.iImage=0;//正常圖標
TCItem.item.iSelectedImage=1;//選中時圖標
hRoot=m_TreeCtrl.InsertItem(&TCItem);//返回根項句柄
for(i=0;i<4;i++){//增加各廠家
TCItem.hParent=hRoot;
TCItem.item.pszText=CJ[i];
TCItem.item.lParam=(i+1)*10;//子項序號
hCur=m_TreeCtrl.InsertItem(&TCItem);
for(j=0;j<5;j++){//增加各產品
TCItem.hParent=hCur;
TCItem.item.pszText=PM[i][j];
TCItem.item.lParam=(i+1)*10+(j+1);//子項序號
m_TreeCtrl.InsertItem(&TCItem);
}
m_TreeCtrl.Expand(hCur,TVE_EXPAND);//展開樹
}
m_TreeCtrl.Expand(hRoot,TVE_EXPAND);//展開上一級樹
return TRUE; // return TRUE unless you set the focus to a control
}
(3)增加樹項功能的實現
在增加樹項功能時,除了需要定義和設置插入樹項的數據結構之外,還需要注意的是新增樹項的名稱初始時均為“新增數據”,增加后允許用戶給數據項設置自定義名稱。在編程時應特別注意m_TreeCtrl.EditLabel(hInsert);后面不能跟任何其它程序命令,否則這條編輯指令無效。
void CVCTREEDlg::OnAdd()
{ //增加子項功能函數
HTREEITEM hSel=m_TreeCtrl.GetSelectedItem();//取得選擇項句柄
if(hSel==NULL) return;//無任何選項則返回
static int nAddNo=100;//編號大于100為新增數據
TV_INSERTSTRUCT TCItem;//定義插入項數據結構
TCItem.hParent=hSel; //設置父項句柄
TCItem.hInsertAfter=TVI_LAST;//在最后增加
TCItem.item.mask=TVIF_TEXT|TVIF_PARAM|TVIF_IMAGE|TVIF_SELECTEDIMAGE;//設屏蔽
TCItem.item.pszText="新增數據";
TCItem.item.lParam=nAddNo++;//索引號增加
TCItem.item.iImage=0;//正常圖標
TCItem.item.iSelectedImage=1;//選中時圖標
HTREEITEM hInsert=m_TreeCtrl.InsertItem(&TCItem);//增加
m_TreeCtrl.Expand(hSel,TVE_EXPAND);
m_TreeCtrl.EditLabel(hInsert);//修改增加的數據
}
(4)刪除樹項功能的實現
在實現刪除功能時,應對存在子項的樹項進行提示,以警告用戶是否連同其子項一起刪除。
void CVCTREEDlg::OnDel()
{ //刪除子項功能函數
HTREEITEM hSel=m_TreeCtrl.GetSelectedItem();//取得選項句柄;
if(hSel==NULL) return;//無任何選項則返回
if(m_TreeCtrl.ItemHasChildren(hSel))//判斷是否有子項
if(MessageBox("廠家下存在品名,一同刪除?","警告",MB_YESNO)==IDNO) return;
m_TreeCtrl.DeleteItem(hSel);
}
(5)排序功能的實現
排序功能是對所選中的樹項的所有子項按字符中順序進行排序,如果想要按照其它規(guī)則進行排序,應利用SortChildrenItemBC()函數進行自行開發(fā)排序程序,這個自行開發(fā)的函數與列表控制中實現的函數基本相同,可興趣的讀可以試驗。
void CVCTREEDlg::OnSort()
{ //排序子項功能函數
HTREEITEM hSel=m_TreeCtrl.GetSelectedItem();//取得選項句柄;
if(hSel==NULL) return;//無任何選項則返回
m_TreeCtrl.SortChildren(hSel);
}
(6)查看功能的實現
查看功能用來查看選中樹項的有關信息,函數中中顯示了樹項的文本名稱和標識號,可以將這兩個信息作為查找關鍵字,來查看其它更詳細的信息。
void CVCTREEDlg::OnView()
{ //查看選中項功能函數
HTREEITEM hSel=m_TreeCtrl.GetSelectedItem();//取得選項句柄;
if(hSel==NULL) return;//無任何選項則返回
CString cText=m_TreeCtrl.GetItemText(hSel);//取得數據項名
LONG IDs=m_TreeCtrl.GetItemData(hSel);//取得數據項序號
char temp[100];
wsprintf(temp,"廠家:%s 編號:%05d",cText,IDs);
MessageBox(temp,"選擇信息");
}
(7)修改功能的實現
如果不進行其它處理,當修改樹項的文本名稱后,就會發(fā)現其未被修改,這是因為程序中沒有對修改結果進行保存處理,這就要利用TV_DISPINFO結構和SetItemText函數對TVN_ENDLABELEDIT進行處理,這樣就可以正確地實現修改功能。
void CVCTREEDlg::OnEndlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult)
{ TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
// TODO: Add your control notification handler code here
if(pTVDispInfo->item.pszText==0) return;//用戶取消修改操作
m_TreeCtrl.SetItemText(pTVDispInfo->item.hItem,
pTVDispInfo->item.pszText);//設置新數據
*pResult = 0;
}
7、樹視的演練技巧
樹視的應用技巧在使用樹視時,其方法與樹控制基本相同,只不過樹視是在窗口中來實現的而樹控制是在對話框中實現,樹視的各種功能是通過菜單來實現的而樹控制是通過按鈕等方式來實現的,樹控制需要在對話框中創(chuàng)建樹控制控件而樹視直接占據整個窗口,在設計過程中只要將按鈕和樹控制設計過程變?yōu)椴藛卧O計,并注意在功能函數是在類向導中是通過菜單命令來操作,同時在每個功能函數前面增加取得列表視引用的命令(CTreeCtrl& TreeCtrl = GetTreeCtrl()),而其余數據結構和代碼均不需要修改,實現起來比較容易。
轉至:http://blog.csdn.net/tg2003/archive/2008/12/24/3586661.aspx