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