VC中實現帶有背景位圖的樹型控件

當前許多應用程序都在使用樹型控件時爲其添加了背景位圖,加強的控件的魅力,然而對於Visual C++編程愛好者來講,使用Visual C++MFC提供的樹型控件(CTreeCtrl)自己就是一個難點,至於如何使該控件可以帶有背景位圖,那就更加是一個使人困惑的問題了。本實例對CTreeCtrl類進行了加強,不只使它帶有背景位圖,並且解決了在點擊樹型控件時背景位圖閃動的問題,另外,經過在對話框中使用該控件來顯示三級目錄,演示了樹型控件的基本使用方法。下圖爲程序編譯後的運行效果圖:


圖1、帶背景圖的樹型控件效果圖


   1、實現方法

  在實現樹型控件的背景位圖以前,咱們首先介紹一下樹型控件的基本使用方法。樹形控件在系統中大量被使用,例如Windows資源管理器就是一個典型的例子。樹形控件能夠用於樹形的結構,其中有一個根接點(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表示的結點的後面,返回值爲當前建立的結點的句柄。

  若是你但願在每一個結點前添加一個小圖標,就必需先調用CTreeCtrl類的成員函數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爲結點被選中時所使用圖片序號。

  此外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;

  對於Visual C++ MFC提供的標準樹型控件CTreeCtrl來講,並不支持背景位圖,因此若是須要實現背景位圖就須要先讓其在內存CDC對象上對TREEVIEW缺省繪圖,而後在選擇背景位圖,與缺省位圖合成,即採用貼圖的方式,把標準的TREEVIEW窗口貼在底圖上。這個操做在內存中完成。同時爲了不閃爍,必須重載OnItemexpanding()和OnItemexpanded()這兩個函數。SetRedraw函數主要保證其不要在子節點彈出時重畫,而是在子節點已經擴展後重畫。爲此,例程中定義了一個CTreeCtrl類的子類CmyTreeCtrl,並重載瞭如下幾個成員函數:

BOOL CMyTreeCtrl::SetBKImage(LPCTSTR LpszResource)
void CMyTreeCtrl::OnPaint()
void CMyTreeCtrl::OnItemexpanding(NMHDR* pNMHDR, LRESULT* pResult)
void CMyTreeCtrl::OnItemexpanded(NMHDR* pNMHDR, LRESULT* pResult)
BOOL CMyTreeCtrl::OnEraseBkgnd(CDC* pDC)

   2、編程步驟

  一、 啓動Visual C++6.0,生成一個基於對話框的項目Tree,在框架上放置一個樹形控制件,其ID標誌符爲:IDC_TREE1;

  二、 建立CmyTreeCtrl類後,使用CLASSWIZARD爲其添加消息映射:

ON_NOTIFY_REFLECT(TVN_ITEMEXPANDED,OnItemexpanded) ON_NOTIFY_REFLECT(TVN_ITEMEXPANDING, OnItemexpanding)
  消息響應函數:

afx_msg void OnItemexpanded(NMHDR* pNMHDR, LRESULT* pResult);
afx_msg void OnItemexpanding(NMHDR* pNMHDR, LRESULT* pResult);

  三、 將樹型控件與CmyTreeCtrl類創建關聯,在對話框中添加變量CMyTreeCtrl m_CtrlTree;

  四、 製做一個準備做爲樹形控件背景的位圖;

  五、 修改對話框的初始化函數BOOL CTreeDlg::OnInitDialog();

  六、 添加代碼,編譯運行程序。 陳剛 編程

3、實現代碼

/////////////////////////////////////////////////
#if !defined(AFX_TREEDLG_H__D82DB384_F574_44A7_96DA_6EC9068E22B1__INCLUDED_)
#define AFX_TREEDLG_H__D82DB384_F574_44A7_96DA_6EC9068E22B1__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

/////////////////////////////////////////// CTreeDlg dialog
#include "MyTreeCtrl.h"
class CTreeDlg : public CDialog
{
 // Construction
 public:
  CTreeDlg(CWnd* pParent = NULL); // standard constructor

  // Dialog Data
  //{{AFX_DATA(CTreeDlg)
   enum { IDD = IDD_TREE_DIALOG };
   CMyTreeCtrl m_CtrlTree;
  //}}AFX_DATA

  // ClassWizard generated virtual function overrides
  //{{AFX_VIRTUAL(CTreeDlg)
   protected:
    virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
  //}}AFX_VIRTUAL

  // Implementation
 protected:
  HICON m_hIcon;

  // Generated message map functions
  //{{AFX_MSG(CTreeDlg)
   virtual BOOL OnInitDialog();
   afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
   afx_msg void OnPaint();
   afx_msg HCURSOR OnQueryDragIcon();
  //}}AFX_MSG
  DECLARE_MESSAGE_MAP()
};

#endif

////////////////////////////////// MyTreeCtrl.cpp : implementation file

#include "StdAfx.h"
#include "Tree.h"
#include "MyTreeCtrl.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

////////////////////////////// CMyTreeCtrl

CMyTreeCtrl::CMyTreeCtrl()
{}

CMyTreeCtrl::~CMyTreeCtrl()
{}

BEGIN_MESSAGE_MAP(CMyTreeCtrl, CTreeCtrl)
 //{{AFX_MSG_MAP(CMyTreeCtrl)
  ON_WM_PAINT()
  ON_WM_ERASEBKGND()
  ON_NOTIFY_REFLECT(TVN_ITEMEXPANDED, OnItemexpanded)
  ON_NOTIFY_REFLECT(TVN_ITEMEXPANDING, OnItemexpanding)
 //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CMyTreeCtrl message handlers
BOOL CMyTreeCtrl::SetBKImage(LPCTSTR LpszResource)
{
 // if this is not the first call then delete gdi objects
 if( m_bitmap.m_hObject != NULL )
  m_bitmap.DeleteObject();

  HBITMAP hbmp = (HBITMAP)::LoadImage(AfxGetInstanceHandle(),
LpszResource, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION|LR_LOADFROMFILE);

 if( hbmp == NULL )
  return FALSE;
 m_bitmap.Attach( hbmp );
 return TRUE;
}

LRESULT CMyTreeCtrl::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
 // TODO: Add your specialized code here and/or call the base class
 return CTreeCtrl::WindowProc(message, wParam, lParam);
}

void CMyTreeCtrl::OnPaint()
{
 CPaintDC dc(this); // device context for painting
 CRect rcclient;
 GetClientRect(&rcclient);

 // create a compatible memory dc
 CDC memdc;
 memdc.CreateCompatibleDC(&dc);
 CBitmap bitmap;
 bitmap.CreateCompatibleBitmap(&dc, rcclient.Width(), rcclient.Height());
 memdc.SelectObject( &bitmap );
 CWnd::DefWindowProc(WM_PAINT, (WPARAM)memdc.m_hDC , 0);
 CDC maskdc;
 maskdc.CreateCompatibleDC(&dc);
 CBitmap maskbitmap;
 maskbitmap.CreateBitmap(rcclient.Width(), rcclient.Height(), 1, 1, NULL);
 maskdc.SelectObject( &maskbitmap );
 maskdc.BitBlt( 0, 0, rcclient.Width(), rcclient.Height(), &memdc,
 rcclient.left, rcclient.top, SRCCOPY);
 CBrush brush;
 brush.CreatePatternBrush(&m_bitmap);
 dc.FillRect(rcclient, &brush);
 memdc.SetBkColor(RGB(0,0,0));
 memdc.SetTextColor(RGB(255,255,255));
 memdc.BitBlt(rcclient.left, rcclient.top, rcclient.Width(), rcclient.Height(), &maskdc, rcclient.left,  rcclient.top, SRCAND);
 dc.SetBkColor(RGB(255,255,255));
 dc.SetTextColor(RGB(0,0,0));
 dc.BitBlt(rcclient.left, rcclient.top, rcclient.Width(), rcclient.Height(), &maskdc, rcclient.left, rcclient.top, SRCAND);
 dc.BitBlt(rcclient.left, rcclient.top, rcclient.Width(), rcclient.Height(), &memdc, rcclient.left, rcclient.top,SRCPAINT);
 brush.DeleteObject();
}

BOOL CMyTreeCtrl::OnEraseBkgnd(CDC* pDC)
{
 // TODO: Add your message handler code here and/or call default
 return TRUE;
}

void CMyTreeCtrl::OnItemexpanded(NMHDR* pNMHDR, LRESULT* pResult)
{
 NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
 // TODO: Add your control notification handler code here
 Invalidate();
 SetRedraw(TRUE);
 *pResult = 0;
}

void CMyTreeCtrl::OnItemexpanding(NMHDR* pNMHDR, LRESULT* pResult)
{
 NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
 // TODO: Add your control notification handler code here
 SetRedraw(FALSE);

 *pResult = 0;
}

///////////////////////////////////////////////////////
BOOL CTreeDlg::OnInitDialog()
{
 CDialog::OnInitDialog();
 // Add "About..." menu item to system menu.
 // IDM_ABOUTBOX must be in the system command range.
 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
 ASSERT(IDM_ABOUTBOX < 0xF000);
 CMenu* pSysMenu = GetSystemMenu(FALSE);
 if (pSysMenu != NULL)
 {
  CString strAboutMenu;
  strAboutMenu.LoadString(IDS_ABOUTBOX);
  if (!strAboutMenu.IsEmpty())
  {
   pSysMenu-> AppendMenu(MF_SEPARATOR);
   pSysMenu-> AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
  }
 }
 // Set the icon for this dialog. The framework does this automatically
 // when the application's main window is not a dialog
 SetIcon(m_hIcon, TRUE); // Set big icon
 // TODO: Add extra initialization here
 m_CtrlTree.SetBKImage("IDB_BITMAP1");
 SetIcon(m_hIcon, FALSE); // Set small icon
 TVINSERTSTRUCT tvInsert;
 tvInsert.hParent = NULL;
 tvInsert.hInsertAfter = NULL;
 tvInsert.item.mask = TVIF_TEXT;
 tvInsert.item.pszText = _T("Visual C++編程實例");
 HTREEITEM hCountry = m_CtrlTree.InsertItem(&tvInsert);
 HTREEITEM hPA = m_CtrlTree.InsertItem(TVIF_TEXT,
 _T("文章中心"), 0, 0, 0, 0, 0, hCountry, NULL);
 HTREEITEM hWA = m_CtrlTree.InsertItem(_T("代碼中心"),0, 0, hCountry, hPA);
 m_CtrlTree.InsertItem(_T("全屏幕程序的實現"), hPA, TVI_SORT);
 m_CtrlTree.InsertItem(_T("實現窗口的任意分割"), hPA, TVI_SORT);
 m_CtrlTree.InsertItem(_T("實現菜單的自繪"), hPA, TVI_SORT);
 m_CtrlTree.InsertItem(_T("實現全屏幕顯示的代碼"), hWA, TVI_SORT);
 m_CtrlTree.InsertItem(_T("窗口任意分割的代碼"), hWA, TVI_SORT);
 m_CtrlTree.InsertItem(_T("菜單自繪代碼"), hWA, TVI_SORT);
 m_CtrlTree.Expand(hCountry,TVE_EXPAND);
 return TRUE; // return TRUE unless you set the focus to a control
}

   4、小結

  到此爲止,本例經過實現樹形控件的背景位圖介紹了一些樹視圖控件編程方法,包括樹視圖控件的創建、節點值的賦予等。固然,它應用的方面很廣,使用方法也不少。這裏僅僅是涉及到了構建樹視圖控件的基本框架,讀者朋友們能夠在此基礎上,可進行擴展,從而完成更強大的功能,感興趣的讀者不妨本身擴展該控件試試。陳剛 數據結構

相關文章
相關標籤/搜索