|
|||||||||||||||||||||||||
|
|||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
Note: This is an unedited contribution. If this article is inappropriate,
needs attention or copies someone else's work without reference then please
Report This Article
IntroductionThe CColumnTreeCtrl control can be used in MFC projects where a hybrid of tree and list is needed (see the picture above). This code is based on Michal Mecinski's control described in Multi-Column Tree View article available on CodeGuru.com. This control has several improvements and bug fixes but the general idea is the same - use standard CTreeCtrl as multi-column tree body, and CHeaderCtrl as its header. I've added owner-drawing code (thanks to VividTree - A Colorful and Picturesque Owner Drawn CTreeCtrl article from CodeProject.com), so you can control every aspect of CColumnTreeCtrl's appearance (tree buttons, lines, background and so on). If you want to use standard drawing code - just disable owner drawing You can do everything you do with standard CTreeCtrl and some things related to the CListCtrl. See Using the Code for more information. ImprovementsThe control described in Michal Mecinski's article already has everything you need, but there were several bugs and several missing methods (from my point of view), so I've tried to improve Michal's control a little.Here is the list of improvements.
Control internalsThe CColumnTreeCtrl control is implemented as container for the following child controls (see the figure below):
It was very interesting idea proposed by Michal Mecinsky - to use standard CTreeCtrl to store items and subitems and to use custom drawing to display them properly. So, for example, the first item in the figure above is stored as single string "3.5\" Floppy (A:)\tRemovable\tFAT12\t1.44Mb"The tabulation symbol '\t' is used as subitems separator. When WM_PAINT message is processed by CTreeCtrl, it doesn't draws items by itself, but sends custom draw notifications to the parent container, so it is possible to draw subitems as we wish. Using the codeLinking to your MFC projectYou can use this control in Visual C++ 2003 and higher. I tried to compile it under Visual C++ 6.0, but it seems that it has too old SDK and doesn't support common controls library ver. 6 (distributed with IE6). Follow these simple steps to add CColumnTreeCtrl to your MFC dialog:
Owner-drawing supportOwner-drawing is the technique when all drawing is performed by user's drawing procedure. Although this is rather difficult to paint entire control, this gives you the opportunity to change its appearance as you wish. When owner-drawing is disabled, standard CTreeCtrl's drawing code plus additional custom drawing code is used. What you can do without owner-drawing? The answer is everything. However, there are following benefits of owner-drawing:
By default owner-drawing is disabled. To enable it, uncomment this line in //#define _OWNER_DRAWN_TREE // comment this line if you want to use standard drawing code
Adding and deleting columnsYou should use these methods to add/remove columns: // Inserts new column int CColumnTreeCtrl::InsertColumn( int nCol, // zero-based column index LPCTSTR lpszColumnHeading, // column heading int nFormat=0, // column formating (as in CListViewCtrl) int nWidth=-1, // column width int nSubItem=-1 // not used ); // Removes existing column BOOL CColumnTreeCtrl::DeleteColumn( int nCol // zero-based column index ); Example: // insert two columns
m_columnTree.InsertColumn(0, _T("Name"), LVCFMT_LEFT, 180);
m_columnTree.InsertColumn(1, _T("Type"), LVCFMT_LEFT, 80);
// remove last column
m_columnTree.DeleteColumn(1);
Adding/Removing itemsExample: HTREEITEM hParent; // parent item
// add an item
HTREEITEM hItem = m_columnTree.GetTreeCtrl().InsertItem( _T("3.5\" Floppy (A:)"), hParent );
// remove
m_columnTree.GetTreeCtrl().DeleteItem(hItem);
Retrieving and setting item textYou should use // gets item text
CString
CColumnTreeCtrl::GetItemText(
HTREEITEM hItem, // item handle
int nSubItem // zero-based subitem index
);
// sets item text
void
CColumnTreeCtrl::SetItemText(
HTREEITEM hItem, // item handle
int nSubItem, // zero-based subitem index
LPCTSTR lpszText // pointer to text buffer
);
// set text for the first and the second subitems
m_columnTree.SetItemText(hRoot, 1, _T("Removable"));
m_columnTree.SetItemText(hRoot, 2, _T("FAT12"));
Setting minimum width for the first column// sets the minimum avaliable width for the first column
void
CColumnTreeCtrl::SetFirstColumnMinWidth(
UINT uMinWidth // min. width
);
Determining which item is under the mouse cursorYou can use // CTVHITTESTINFO structure contains information used to determine the
// location of a point relative to a CColumnTreeCtrl control.
typedef struct _CTVHITTESTINFO {
POINT pt; // Client coordinates of the point to test.
UINT flags; // Standard TVHT -prefixed flags.
HTREEITEM hItem; // Handle to the item that occupies the point.
int iSubItem; // Zero-based subitem index.
} CTVHITTESTINFO;
// Call this function to determine the location of the specified point
// relative to the client area of a CColumnTreeCtrl control.
HTREEITEM
CColumnTreeCtrl::HitTest(
CPoint pt,
UINT* pFlags
) const;
HTREEITEM
CColumnTreeCtrl::HitTest(
CTVHITTESTINFO* pHitTestInfo
) const;
Example: // NM_RCLICK notification handler
void CMainDlg::OnRclickedColumntree(LPNMHDR pNMHDR, LRESULT* pResult)
{
CPoint pt;
::GetCursorPos(&pt);
m_columnTree.ScreenToClient(&pt);
CTVHITTESTINFO htinfo;
htinfo.pt = pt;
HTREEITEM hItem = m_columnTree.HitTest(&htinfo);
if(hItem)
{
CString szState;
if(htinfo.flags&TVHT_ONITEMBUTTON)
szState += _T("Clicked on item's button.");
// ... check other flags
CString szItemText = m_columnTree.GetItemText(hItem, htinfo.iSubItem);
MessageBox(szState + _T(" Item text: ") + szItemText);
}
}
Accessing child CTreeCtrl and CHeaderCtrlYou can access child tree control and header control and call their methods as usual: // returns reference to the child tree ctrl
CCustomTreeChildCtrl&
CColumnTreeCtrl::GetTreeCtrl();
// returns reference to the child header ctrl
CHeaderCtrl&
CColumnTreeCtrl::GetHeaderCtrl();Example: // modify style of child tree control to enable lines
m_columnTree.GetTreeCtrl().ModifyStyle(TVS_HASLINES, 0);
Setting background bitmapIf owner-drawing is enabled you can specify what background bitmap to use. Standard // gets background image BOOL CCustomTreeChildCtrl::GetBkImage( LVBKIMAGE* plvbkImage // structure describing image and its position ) const; // sets background image BOOL CCustomTreeChildCtrl::SetBkImage( LVBKIMAGE* plvbkImage // structure describing image and its position ); Example: LVBKIMAGE bk;
bk.xOffsetPercent = bk.yOffsetPercent = 70;
bk.hbm = LoadBitmap(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDB_BKGND));
m_columnTree.GetTreeCtrl().SetBkImage(&bk); Processing child tree and header notificationsYou can easily process notifications from child tree and header controls in your dialog's message map, because all notifications are forwarded to the parent. Example: //.. fragment of the message map
ON_NOTIFY(NM_CLICK , IDC_COLUMNTREE, OnTreeClk)
//.. notify handler
void CMainDlg::OnTreeClk(NMHDR *pNMHDR, LRESULT *pResult)
{
NMTREEVIEW& nm = *(LPNMTREEVIEW)pNMHDR;
// which item was selected?
HTREEITEM hItem = m_columnTree.GetTreeCtrl().GetSelectedItem();
if(!hItem) return;
// the rest of processing code..
}
History
Known IssuesThese issues were reported so far:
Report a bugI hope this control will be useful, but if you find a bug, please let me know. You can post a message to the discussion thread below or email me: olegkrivtsov@mail.ru Please do not forget to specify how I can reproduce the incorrect behavior of control, describe what you were doing when incorrect behavior occurred, specify your Windows version and your current desktop theme.
|
||||||||||||||||||||||||