創建一個窗口,dwStyle中可以使用以下一些樹形控件的專用風格:
TVS_HASLINES 在父/子結點之間繪製連線
TVS_LINESATROOT 在根/子結點之間繪製連線
TVS_HASBUTTONS 在每一個結點前添加一個按鈕,用於表示當前結點是否已被展開
TVS_EDITLABELS 結點的顯示字符可以被編輯
TVS_SHOWSELALWAYS 在失去焦點時也顯示當前選中的結點
TVS_DISABLEDRAGDROP 不允許Drag/Drop
TVS_NOTOOLTIPS 不使用ToolTip顯示結點的顯示字符
在樹形控件中每一個結點都有一個句柄(HTREEITEM),同時添加結點時必須提供的參數是該結點的父結點句柄,(其中根Root結點只有一個,既不可以添加也不可以刪除)利用 HTREEITEM InsertItem( LPCTSTR lpszItem, HTREEITEM hParent = TVI_ROOT, HTREEITEM hInsertAfter = TVI_LAST );
可以添加一個結點,pszItem爲顯示的字符,hParent代表父結點的句柄,當前添加的結點會排在hInsertAfter表示的結點的後面,返回值爲當前創建的結點的句柄。下面的代碼會建立一個如下形式的樹形結構:
+--- Parent1
+--- Child1_1
+--- Child1_2
+--- Child1_3
+--- Parent2
+--- Parent3
/*假設m_tree爲一個CTreeCtrl對象,而且該窗口已經創建*/
HTREEITEM hItem,hSubItem;
hItem = m_tree.InsertItem("Parent1",TVI_ROOT);在根結點上添加Parent1
hSubItem = m_tree.InsertItem("Child1_1",hItem);//在Parent1上添加一個子結點
hSubItem = m_tree.InsertItem("Child1_2",hItem,hSubItem);//在Parent1上添加一個子結點,排在Child1_1後面
hSubItem = m_tree.InsertItem("Child1_3",hItem,hSubItem);
hItem = m_tree.InsertItem("Parent2",TVI_ROOT,hItem);
hItem = m_tree.InsertItem("Parent3",TVI_ROOT,hItem);
如果你希望在每個結點前添加一個小圖標,就必需先調用
CImageList* SetImageList( CImageList * pImageList, int nImageListType );
指明當前所使用的ImageList,nImageListType爲TVSIL_NORMAL。在調用完成後控件中使用圖片以設置的ImageList中圖片爲準。然後調用
HTREEITEM InsertItem( LPCTSTR lpszItem, int nImage, int nSelectedImage, HTREEITEM hParent = TVI_ROOT, HTREEITEM hInsertAfter = TVI_LAST);
添加結點,nImage爲結點沒被選中時所使用圖片序號,nSelectedImage爲結點被選中時所使用圖片序號。下面的代碼演示了ImageList的設置。
/*m_list 爲CImageList對象
IDB_TREE 爲16*(16*4)的位圖,每個圖片爲16*16共4個圖標*/
m_list.Create(IDB_TREE,16,4,RGB(0,0,0));
m_tree.SetImageList(&m_list,TVSIL_NORMAL);
m_tree.InsertItem("Parent1",0,1);//添加,選中時顯示圖標1,未選中時顯示圖標0
此外CTreeCtrl還提供了一些函數用於得到/修改控件的狀態。
HTREEITEM GetSelectedItem( );將返回當前選中的結點的句柄。
BOOL SelectItem( HTREEITEM hItem );將選中指明結點。
BOOL GetItemImage( HTREEITEM hItem, int& nImage, int& nSelectedImage ) / BOOL SetItemImage( HTREEITEM hItem, int nImage, int nSelectedImage )用於得到/修改某結點所使用圖標索引。
CString GetItemText( HTREEITEM hItem ) /BOOL SetItemText( HTREEITEM hItem, LPCTSTR lpszItem );用於得到/修改某一結點的顯示字符。
BOOL DeleteItem( HTREEITEM hItem );用於刪除某一結點,
BOOL DeleteAllItems( );將刪除所有結點。
此外如果想遍歷樹可以使用下面的函數:
HTREEITEM GetRootItem( );得到根結點。
HTREEITEM GetChildItem( HTREEITEM hItem );得到子結點。
HTREEITEM GetPrevSiblingItem/GetNextSiblingItem( HTREEITEM hItem );得到指明結點的上/下一個兄弟結點。
HTREEITEM GetParentItem( HTREEITEM hItem );得到父結點。
樹形控件的消息映射使用ON_NOTIFY宏,形式如同:ON_NOTIFY( wNotifyCode, id, memberFxn ),wNotifyCode爲通知代碼,id爲產生該消息的窗口ID,memberFxn爲處理函數,函數的原型如同void OnXXXTree(NMHDR* pNMHDR, LRESULT* pResult),其中pNMHDR爲一數據結構,在具體使用時需要轉換成其他類型的結構。對於樹形控件可能取值和對應的數據結構爲:
TVN_SELCHANGED 在所選中的結點發生改變後發送,所用結構:NMTREEVIEW
TVN_ITEMEXPANDED 在某結點被展開後發送,所用結構:NMTREEVIEW
TVN_BEGINLABELEDIT 在開始編輯結點字符時發送,所用結構:NMTVDISPINFO
TVN_ENDLABELEDIT 在結束編輯結點字符時發送,所用結構:NMTVDISPINFO
TVN_GETDISPINFO 在需要得到某結點信息時發送,(如得到結點的顯示字符)所用結構:NMTVDISPINFO
關於ON_NOTIFY有很多內容,將在以後的內容中進行詳細講解。
關於動態提供結點所顯示的字符:首先你在添加結點時需要指明lpszItem參數爲:LPSTR_TEXTCALLBACK。在控件顯示該結點時會通過發送TVN_GETDISPINFO來取得所需要的字符,在處理該消息時先將參數pNMHDR轉換爲LPNMTVDISPINFO,然後填充其中item.pszText。但是我們通過什麼來知道該結點所對應的信息呢,我的做法是在添加結點後設置其lParam參數,然後在提供信息時利用該參數來查找所對應的信息。下面的代碼說明了這種方法:
char szOut[8][3]={"No.1","No.2","No.3"};
//添加結點
HTREEITEM hItem = m_tree.InsertItem(LPSTR_TEXTCALLBACK,...)
m_tree.SetItemData(hItem, 0 );
hItem = m_tree.InsertItem(LPSTR_TEXTCALLBACK,...)
m_tree.SetItemData(hItem, 1 );
//處理消息
void CParentWnd::OnGetDispInfoTree(NMHDR* pNMHDR, LRESULT* pResult)
{
TV_DISPINFO* pTVDI = (TV_DISPINFO*)pNMHDR;
pTVDI->item.pszText=szOut[pTVDI->item.lParam];//通過lParam得到需要顯示的字符在數組中的位置
*pResult = 0;
}
關於編輯結點的顯示字符:首先需要設置樹形控件的TVS_EDITLABELS風格,在開始編輯時該控件將會發送TVN_BEGINLABELEDIT,你可以通過在處理函數中返回TRUE來取消接下來的編輯,在編輯完成後會發送TVN_ENDLABELEDIT,在處理該消息時需要將參數pNMHDR轉換爲LPNMTVDISPINFO,然後通過其中的item.pszText得到編輯後的字符,並重置顯示字符。如果編輯在中途中取消該變量爲NULL。下面的代碼說明如何處理這些消息:
//處理消息 TVN_BEGINLABELEDIT
void CParentWnd::OnBeginEditTree(NMHDR* pNMHDR, LRESULT* pResult)
{
TV_DISPINFO* pTVDI = (TV_DISPINFO*)pNMHDR;
if(pTVDI->item.lParam==0);//判斷是否取消該操作
*pResult = 1;
else
*pResult = 0;
}
//處理消息 TVN_BEGINLABELEDIT
void CParentWnd::OnBeginEditTree(NMHDR* pNMHDR, LRESULT* pResult)
{
TV_DISPINFO* pTVDI = (TV_DISPINFO*)pNMHDR;
if(pTVDI->item.pszText==NULL);//判斷是否已經取消取消編輯
m_tree.SetItemText(pTVDI->item.hItem,pTVDI->pszText);//重置顯示字符
*pResult = 0;
}
上面講述的方法所進行的消息映射必須在父窗口中進行(同樣WM_NOTIFY的所有消息都需要在父窗口中處理)。
*************************************************************************************************************************************
CTreeCtrl樹控件介紹(讀取數據庫來動態顯示)
ListCtrl在系統中大量被使用,例如Windows資源管理器就是一個典型的例子。
ListCtrl樹形控件功能描述:可以用於樹形的結構,其中有一個根接點(Root)然後下面有許多子結點,而每個子結點上有允許有一個或多個或沒有子結點。MFC中使用CTreeCtrl類來封裝樹形控件的各種操作。
調用方法:
BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );
創建一個窗口,dwStyle中可以使用以下一些樹形控件的專用風格:
TVS_HASLINES 在父/子結點之間繪製連線
TVS_LINESATROOT 在根/子結點之間繪製連線
TVS_HASBUTTONS 在每一個結點前添加一個按鈕,用於表示當前結點是否已被展開
TVS_EDITLABELS 結點的顯示字符可以被編輯
TVS_SHOWSELALWAYS 在失去焦點時也顯示當前選中的結點
TVS_DISABLEDRAGDROP 不允許Drag/Drop
TVS_NOTOOLTIPS 不使用ToolTip顯示結點的顯示字符
在樹形控件中每一個結點都有一個句柄(HTREEITEM),同時添加結點時必須提供的參數是該結點的父結點句柄,利用
HTREEITEM InsertItem( LPCTSTR lpszItem, HTREEITEM hParent = TVI_ROOT, HTREEITEM hInsertAfter = TVI_LAST );
可以添加一個結點,pszItem爲顯示的字符,hParent代表父結點的句柄,當前添加的結點會排在hInsertAfter表示的結點的後面,返回值爲當前創建的結點的句柄
樹控件圖像列表
樹控件 (Tree Control) (CTreeCtrl) 中的每項都可以有一對與之關聯的位圖化圖像。這些圖像出現在項標籤的左邊。如果選擇了該項,就會顯示一個圖像;如果沒有選擇該項,就會顯示另一個圖像。例如,某項可能在選定時顯示一個打開的文件夾,而在未選定時顯示關閉的文件夾。
若要使用項圖像,必須通過構造 CImageList 對象和使用 CImageList::Create 函數創建相關圖像列表來創建一個圖像列表。然後,將想要的位圖添加到創建的列表中, 如果你希望在每個結點前添加一個小圖標,就必需先調用CImageList* SetImageList( CImageList * pImageList, int nImageListType );指明當前所使用的ImageList,nImageListType爲TVSIL_NORMAL。在調用完成後控件中使用圖片以設置的ImageList中圖片爲準。然後調用
HTREEITEM InsertItem( LPCTSTR lpszItem, int nImage, int nSelectedImage, HTREEITEM hParent = TVI_ROOT, HTREEITEM hInsertAfter = TVI_LAST);添加結點,nImage爲結點沒被選中時所使用圖片序號,nSelectedImage爲結點被選中時所使用圖片序號。
用VC實現按數據庫記錄構建樹控件
將樹中的每一個項目作爲數據庫中的一條記錄(ACCESS2000),將程序啓動時,對數據庫進行讀操作;創建樹的各個項目時,是對數據庫進行讀操作,每次的讀取,都是在可是查尋符合條件的記錄,並將其一一添加到樹中!
實現方法:
使用ACCESS2000,創建一個數據庫,名字爲City.mdb(我們將製作一個關於省與市的樹,特別適合通訊錄);在數據庫中創建一表,表名爲TreeItem,字段內容與類型如下圖:
ID: 索引號碼(可有,可無)
Name: 項目名稱(必須)
ParentItem: 父項名稱(必須)
SecNum: 電話區號(可有,可無)
輸入一些原始數據.數據庫已經準備好,那我們就進行實地的編程階段.
程序實現:
創建一個基於對話框的工程---TreeData
一.ADO的引入和初始化
由於在程序中,我使用了ADO來連接和操作數據庫,所以要進行以下操作:
1.在Stdafx.h中添加引作ADO的代碼:
2.在TreeData.h中聲明兩個私有變量:
3.在CTreeDataApp的構造函數CTreeDataApp中添加如下代碼:
4.在CTreeDataApp的初始化函數中添加如下代碼:
二.Recordset的創建:
1.在CTreeDataDlg.h中聲明變量:
2. (1).在對話框窗口中添加一個TreeCtrl控件,一個ComboExe控件; TreeCtrl的風格設置如下圖;
(2).導入一個BMP文件,做爲Tree的項目圖標(TreeBoot.bmp),將其ID設置爲IDB_TreeBootImage;
(3).在嚮導中,爲三個控件添加連接對象.
3.在CTreeDataDlg中右擊,選擇添加一個成員函數
4.添加一個COM變量到CString變量的轉換函數:
5.同樣的方法添加另外一個成員函數
6.添加一個求當前項子項串的成員函數
7.處理TreeCtrl控件的點擊(OnClick)和改變選擇項(SelchangedTree)事件:
三.在BOOL CTreeDataDlg::OnInitDialog()中添加以下代碼: TreeAddTree();
***************************************************************************************************************************************
在MFC中應用CTreeCtrl控件的技巧
首先,我們要創建一個基本對話框的MFC工程MFC_TreeCRTL(名字隨便給一個)。然後在資源視圖中插入兩個Dialog,ID分別爲IDD_DIALOG11和IDD_DIALOG211,都更改Style屬性爲Child,Border屬性爲None,爲它們建立兩個類,分別命名爲Cdialog11和Cdialog211,並在MFC_TreeCRTLDlg.CPP文件中包含dialog11.h和dialog211.h兩個頭文件。再導入幾個資源圖標作爲樹形控件節點的圖標及裝飾面板。最後在主面板上添加一個CTreeCtrl控件,ID爲默認,並在ClassWizard中添加它的一個變量,命名爲m_mytree。
接着,我們進行具體代碼編寫。
我們必須在CMFC_TreeCRTLDlg類中加入這些變量和函數
CDialog * m_treePages[2];
CString node_name;
BOOL InitMytree();
我們還要在CMFC_TreeCRTLDlg類的構造函數中爲m_treePages[2]分配空間, m_treePages[0]=new Cdialog11;
m_treePages[1]=new Cdialog211;
InitMytree()函數爲m_mytree的初始化過程 BOOL CMFC_TreeCRTLDlg::InitMytree()
{
//節點的圖標
int i=0;
int i_count=2;
//載入圖標
HICON icon[4];
icon[0]=AfxGetApp()->LoadIcon (IDI_ICON6);
icon[1]=AfxGetApp()->LoadIcon (IDI_ICON7);
//創建圖像列表控件
CImageList *m_imagelist=new CImageList;
m_imagelist->Create(16,16,0,7,7);
m_imagelist->SetBkColor (RGB(255,255,255));
for(int n=0;n {
m_imagelist->Add(icon[n]); //把圖標載入圖像列表控件
}
m_mytree.SetImageList(m_imagelist,TVSIL_NORMAL); //爲m_mytree設置一個圖像列表,使CtreeCtrl的節點顯示不同的圖標
m_mytree.SetBkColor(RGB(0,250,255));//設置m_mytree的背景色
//創建節點
//父節點
HTREEITEM root0=m_mytree.InsertItem("Dialog1",0,1,TVI_ROOT,TVI_LAST);
HTREEITEM root1=m_mytree.InsertItem("Dialog2",0,1,TVI_ROOT,TVI_LAST);
//一層子節點
HTREEITEM sub_son0=m_mytree.InsertItem("Dialog 1-1",0,1,root0,TVI_LAST);
HTREEITEM sub_son1=m_mytree.InsertItem("Dialog 2-1",0,1,root1,TVI_LAST);
//二層孫子節點
HTREEITEM sub_m_son0=m_mytree.InsertItem("Dialog 2-1-1",0,1,sub_son1,TVI_LAST);
//建立節點對應的Dialog
m_treePages[0]->Create(IDD_DIALOG11,this);
m_treePages[1]->Create(IDD_DIALOG211,this);
m_treePages[0]->ShowWindow(SW_SHOW);
m_treePages[1]->ShowWindow(SW_HIDE);
//把Dialog移到合適位置
CRect m_rect;
GetClientRect(m_rect);
m_rect.left=200;
m_treePages[0]->MoveWindow(m_rect);
m_treePages[1]->MoveWindow(m_rect);
return true;
}
始初化完成後,我們要添加CTreeCtrl的消息響應事件,這樣才能讓它按我們的要求起作用。我們打開Class Wizard點選IDC_TREE1添加TVN_SELCHANGED消息,並在消息響應函數中寫入代碼。
void CMFC_TreeCRTLDlg::OnSelchangedTree1(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
// TODO: Add your control notification handler code here
UpdateData(true);
node_name=m_mytree.GetItemText(pNMTreeView->itemNew.hItem);
//在標題欄顯示節點信息
SetWindowText(node_name);
//切換面板
if(node_name=="Dialog 1-1"){
m_treePages[0]->ShowWindow(SW_SHOW);
m_treePages[1]->ShowWindow(SW_HIDE);
}
else if(node_name=="Dialog 2-1-1"){
m_treePages[0]->ShowWindow(SW_HIDE);
m_treePages[1]->ShowWindow(SW_SHOW);
}
UpdateData(false);
*pResult = 0;
}
最後,我們在 CMFC_TreeCRTLDlg::OnInitDialog()初始化函數裏調用InitMytree()函數。程序運行效果:
到這裏爲止,我們就把一個Dialog粘貼到了主Dialog上了,通過CTreeCtrl控件的節點的變化,讓不同的Dialog交替地粘貼在主Dialog上,從而方便於我們只用少數的窗口,調用更多的功能模塊,不必再爲每個模塊都作爲彈出窗口,而顯得繁雜。