MFC之TreeCtrl控件使用經驗總結

    樹形控件能夠用於樹形的結構,其中有一個根接點(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),同時添加結點時必須提供的參數是該結點的父結點句柄,(其中根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函數


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);this

hItem = m_tree.InsertItem("Parent2",TVI_ROOT,hItem);   
hItem = m_tree.InsertItem("Parent3",TVI_ROOT,hItem);   spa

若是你但願在每一個結點前添加一個小圖標,就必需先調用CImageList* SetImageList( CImageList * pImageList, int nImageListType );
CImageList指向一個CImageList對象,若是這個值爲空,則CTreeCtrl中的Image將被移除。
nImageListType有兩種,TVSIL_NORMAL-包含選擇和被選擇兩個狀態的Image,TVSIL_STATE-用戶定義狀態的Image。
在調用完成後控件中使用圖片以設置的ImageList中圖片爲準。而後調用
HTREEITEM InsertItem( LPCTSTR lpszItem, int nImage, int nSelectedImage, HTREEITEM hParent = TVI_ROOT, HTREEITEM hInsertAfter = TVI_LAST);添加結點,
nImage爲結點沒被選中時所使用圖片序號,nSelectedImage爲結點被選中時所使用圖片序號。
下面的代碼演示了ImageList的設置。

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.net


Example2:orm

CImageList imglist;
CBitmap     bitmap;
imglist.Create(16, 16, ILC_MASK, 1, 1);
bitmap.LoadBitmap( IDB_COMPUTER );
imglist.Add(&bitmap, (COLORREF)0xFFFFFF);
bitmap.DeleteObject();
treectrl.SetImageList(&m_imgList, TVSIL_NORMAL);對象


此外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( );將刪除全部結點。blog

此外若是想遍歷樹能夠使用下面的函數:
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的全部消息都須要在父窗口中處理)。

文章轉自:http://blog.csdn.net/dongle2001/articles/429704.aspx


1.樹視圖風格:
TVS_HASBUTTONS;    //在父項旁邊顯示(+)和(-)
TVS_HASLINES;     //使用線條顯示各項之間的層次
TVS_LINESATROOT;//使用線條連接樹視圖控件根部各項

2.處理單擊事件:TVN_SELCHANGED
void CTreeCtrlDlg::OnTvnSelchangedTree1(NMHDR *pNMHDR, LRESULT *pResult)
{
      LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
      // TODO: 在此添加控件通知處理程序代碼
      HTREEITEM ht=m_treeCtrl.GetSelectedItem();
      CString strSelect=m_treeCtrl.GetItemText(ht);
      m_strTreeVal=strSelect;
      UpdateData(FALSE);
      *pResult = 0;
}

3.同時讓本身派生的CMyTreeCtrl類和對話框處理TVN_SELCHANGED消息:
只須在CMyTreeCtrl中處理如下消息,並返回FALSE就OK了ON_NOTIFY_REFLECT_EX(TVN_SELCHANGED, OnTvnSelchanged) OnTvnSelchanged的簽名以下
BOOL CMyTreeCtrl::OnTvnSelchanged(NMHDR *pNMHDR,LRESULT *pResult)

4.編輯標籤:要容許編輯樹視圖控件中的文本,能夠設置如下三個步驟
      (1).設置樹視圖的TVS_EDITLABELS風格
TVS_EDITLABE能夠經過資源編輯框內部修改(Edit labels),也能夠用代碼的方式修改,以下
long lStyle=::GetWindowLong(m_treeCtrl.GetSafeHwnd(),GWL_STYLE);
      lStyle|=TVS_EDITLABELS;
      ::SetWindowLong(m_treeCtrl.GetSafeHwnd(),GWL_STYLE,lStyle)
      (2).處理TVN_BEGINLABELEDIT通知消息
       //設置相關限制,如限制編輯框字符串長度
      CEdit*pEdit=GetEditControl();     //獲取當前選中結點編輯框
      ASSERT(pEdit);
      if (pEdit)
      {
          pEdit->LimitText(15);//設置編輯框文本長度爲15個字符串
          *pResult = 0;      
      }
      (3).處理TVN_ENDLABLEEDIT通知消息
      HTREEITEM hParent=GetParentItem(pTVDispInfo->item.hItem); //獲取父結點 
      HTREEITEM hChild=GetChildItem(hParent?hParent:TVI_ROOT); //獲取第一個根結點
      hChild=GetNextSiblingItem(hChild);            //獲取下一個兄弟節點
      if (pTVDispInfo->item.hItem!=hChild)          //判斷是否與當前節點相同
      pTVDispInfo->item.pszText                     //獲取當前節點的字符串
      CString strText=GetItemText(hChild);          //獲取節點的字符串

5.讓樹視圖處理Esc和Enter鍵
重載PreTranslateMessage函數
BOOL bHandleMsg=FALSE;
      switch(pMsg->message) {
      case VK_ESCAPE:
      case VK_RETURN:
          if (::GetKeyState(VK_CONTROL)&0x8000)
          {
              break;
          }
          if (GetEditControl())
          {
              ::TranslateMessage(pMsg);
              ::DispatchMessage(pMsg);
              bHandleMsg=TRUE;
          }
          break;
      }

4.實現上下文菜單
在WM_RBUTTONDOWN消息處理函數上實現上下文菜單

5.展開和收起樹視圖結點:
HTREEITEM hItem=GetRootItem();                //獲取根結點,可能會有多個根結點
ItemHasChildren(hParent)                      //判斷結點是否有孩子結點
hItem=GetChildItem(hParent);                  //獲取第一個子結點
hItem=GetNextSiblingItem(hItem));             //獲取下一個兄弟結點結點
Expand(hItem,bExpand?TVE_EXPAND:TVE_COLLAPSE);//展開/疊起結點
Select(hItem,TVGN_FIRSTVISIBLE);                  //設置選中結點
CString str=GetItemText(hChild);              //獲取結點字符串信息
HTREEITEM hCurrSel = GetSelectedItem();       //獲取當前選中結點
SelectItem(hNewSel);
HTREEITEM hNewSel = HitTest(pt, &nFlags);         //判斷座標是否在當前結點範圍內
HTREEITEM hItem=InsertItem(dlg.m_strItemText,hItemParent);    //插入結點


#pragma once
//定義文件MyTreeCtrl.h
// CMyTreeCtrl
class CMyTreeCtrl : public CTreeCtrl
{
      DECLARE_DYNAMIC(CMyTreeCtrl)
public:
      CMyTreeCtrl();
      virtual ~CMyTreeCtrl();
protected:
      DECLARE_MESSAGE_MAP()
      void ExpandBranch(HTREEITEM hItem,BOOL bExpand =TRUE);
public:
      void ExpandAllBranches(BOOL bExpand =TRUE);
      BOOL DoesItemExist(HTREEITEM hItemParent, CString const& strItem);
      afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
      afx_msg void OnAddItem();
      virtual BOOL PreTranslateMessage(MSG* pMsg);
      afx_msg void OnTvnBeginlabeledit(NMHDR *pNMHDR, LRESULT *pResult);
      afx_msg void OnTvnEndlabeledit(NMHDR *pNMHDR, LRESULT *pResult);
   
      afx_msg BOOL OnTvnSelchanged(NMHDR *pNMHDR, LRESULT *pResult);
};
// MyTreeCtrl.cpp : 實現文件
#include "stdafx.h"
#include "TreeCtrl.h"
#include "MyTreeCtrl.h"
#include ".mytreectrl.h"
#include "AddItemDlg.h"
// CMyTreeCtrl
IMPLEMENT_DYNAMIC(CMyTreeCtrl, CTreeCtrl)
CMyTreeCtrl::CMyTreeCtrl()
{
}
CMyTreeCtrl::~CMyTreeCtrl()
{
}
BEGIN_MESSAGE_MAP(CMyTreeCtrl, CTreeCtrl)
      ON_WM_RBUTTONDOWN()
      ON_COMMAND(IDR_ADD_ITEM, OnAddItem)
      ON_NOTIFY_REFLECT(TVN_ENDLABELEDIT, OnTvnEndlabeledit)
      ON_NOTIFY_REFLECT(TVN_BEGINLABELEDIT, OnTvnBeginlabeledit)
      ON_NOTIFY_REFLECT_EX(TVN_SELCHANGED, OnTvnSelchanged)
END_MESSAGE_MAP()

// CMyTreeCtrl 消息處理程序
void CMyTreeCtrl::ExpandBranch(HTREEITEM hItem,BOOL bExpand )
//展開
{
      if (ItemHasChildren(hItem))    //判斷是否有孩子結點
      {
          Expand(hItem,bExpand?TVE_EXPAND:TVE_COLLAPSE);
          //展開/疊起結點
          hItem=GetChildItem(hItem);//獲取第一個子結點
          do{
              ExpandBranch(hItem);
          } while(hItem=GetNextSiblingItem(hItem));//獲取兄弟結點
      }
}
void CMyTreeCtrl::ExpandAllBranches(BOOL bExpand )
{
      HTREEITEM hItem=GetRootItem();//獲取根結點,可能會有多個根結點
      do{
          ExpandBranch(hItem,bExpand);
      } while(hItem=GetNextSiblingItem(hItem));
      Select(hItem,TVGN_FIRSTVISIBLE);//設置選中結點
}
BOOL CMyTreeCtrl::DoesItemExist(HTREEITEM hItemParent,
                                  CString const& strItem)
{
      BOOL bDoesItemExist=FALSE;
      ASSERT(strItem.GetLength());
      HTREEITEM hChild=GetChildItem(hItemParent?hItemParent:TVI_ROOT);
      while (NULL!=hChild&&!bDoesItemExist)
      {
          CString str=GetItemText(hChild);//獲取結點字符串信息
          if (0==str.CompareNoCase(strItem))
          {
              bDoesItemExist=TRUE;
          }
          else
          {
              hChild=GetNextSiblingItem(hChild);
          }
      }
      return bDoesItemExist;
}
void CMyTreeCtrl::OnRButtonDown(UINT nFlags, CPoint point)
{
      // TODO: 在此添加消息處理程序代碼和/或調用默認值   
      // set focus to the tree control
      SetFocus();

      // map the point that is passed to the
      // function from client coordinates
      // to screen coordinates
      ClientToScreen(&point);
      // Get the currently selected item
      HTREEITEM hCurrSel = GetSelectedItem();//獲取當前選中結點
      // Figure out which item was right clicked
      CPoint pt(0, 0);
      ::GetCursorPos(&pt);
      ScreenToClient (&pt);
      HTREEITEM hNewSel = HitTest(pt, &nFlags);
      // Set the selection to the item that the
      // mouse was over when the user right
      // clicked
      if (NULL == hNewSel)
      {
          SelectItem(NULL);
      }
      else if (hCurrSel != hNewSel)
      {
          SelectItem(hNewSel);
          SetFocus();
      }

      // Load the context menu
      CMenu Menu;
      if (Menu.LoadMenu(IDM_CONTEXT_MENU))
      {
          CMenu* pSubMenu = Menu.GetSubMenu(0);
          if (pSubMenu!=NULL)
          {
              // Display the context menu
              pSubMenu->TrackPopupMenu(
                  TPM_LEFTALIGN | TPM_RIGHTBUTTON,
                  point.x, point.y, this);
          }
      }  
}
void CMyTreeCtrl::OnAddItem()
//添加上下文菜單
{
      // TODO: 在此添加命令處理程序代碼
      HTREEITEM hItemParent=GetSelectedItem();
      //獲取當前選中結點
      CAddItemDlg dlg;
      if (dlg.DoModal()==IDOK)
      {
          if (!DoesItemExist(hItemParent,dlg.m_strItemText))
          {
              HTREEITEM hItem=InsertItem(dlg.m_strItemText,hItemParent);
              //插入結點
              SelectItem(hItem);
          }
          else
          {
              AfxMessageBox("已存在相同結點");
          }
      }
  
}
BOOL CMyTreeCtrl::PreTranslateMessage(MSG* pMsg)
{
      // TODO: 在此添加專用代碼和/或調用基類
      BOOL bHandledMsg = FALSE;

      switch (pMsg->message)
      {
          case WM_KEYDOWN:
          {
              switch (pMsg->wParam)
              {
              case VK_ESCAPE:
              case VK_RETURN:   
                  if (::GetKeyState(VK_CONTROL) & 0x8000)
                  {
                      break;
                  }
                  if (GetEditControl())
                  {
                      ::TranslateMessage(pMsg);
                      ::DispatchMessage(pMsg);
                      bHandledMsg = TRUE;
                  }
                  break;
              default: break;
              } // switch (pMsg->wParam)
          } // WM_KEYDOWN
          break;
      default: break;
      } // switch (pMsg->message)                  
      // continue normal translation and dispatching             
      return (bHandledMsg ?TRUE : CTreeCtrl::PreTranslateMessage(pMsg));

}
void CMyTreeCtrl::OnTvnBeginlabeledit(NMHDR *pNMHDR, LRESULT *pResult)
{
      LPNMTVDISPINFO pTVDispInfo = reinterpret_cast<LPNMTVDISPINFO>(pNMHDR);
      // TODO: 在此添加控件通知處理程序代碼
      *pResult=1;
      CEdit*pEdit=GetEditControl();
      ASSERT(pEdit);
      if (pEdit)
      {
          pEdit->LimitText(15);
          *pResult=0;
      }  
}
void CMyTreeCtrl::OnTvnEndlabeledit(NMHDR *pNMHDR, LRESULT *pResult)
{
      LPNMTVDISPINFO pTVDispInfo = reinterpret_cast<LPNMTVDISPINFO>(pNMHDR);
      // TODO: 在此添加控件通知處理程序代碼

      BOOL bValidItem=FALSE;      CString strItem=pTVDispInfo->item.pszText;      if (0<strItem.GetLength())      {          HTREEITEM hParent=GetParentItem(pTVDispInfo->item.hItem);          bValidItem=!DoesItemExist(hParent,strItem);          }      *pResult = bValidItem;}BOOL CMyTreeCtrl::OnTvnSelchanged(NMHDR *pNMHDR, LRESULT *pResult){      LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);      // TODO: 在此添加控件通知處理程序代碼      TRACE(GetItemText(pNMTreeView->itemNew.hItem));      TRACE(" ");      *pResult = 0;      return FALSE;         //返回FALSE可讓父窗口進行進一步的處理}

相關文章
相關標籤/搜索