Objective Toolkit : Chapter 14 Tree Control & Tree View : Using the Tree Control Classes
Using the Tree Control Classes
The following sections describe how to use the tree control and tree view classes.
To create a tree control in a dialog
1. In the Visual Studio resource editor, create a tree control resource on the dialog resource.
2. Instantiate an SECTreeCtrl object as a member of your dialog class.
3. In the OnInitDialog() method of your dialog class, call SubclassTreeCtrlId() on the SECTreeCtrl instance. For example:
 
m_pTreeCtrl->SubclassTreeCtrlId( IDC_TREE, this );
To create a tree control dynamically
1. Create a unique control ID for the tree control. In Visual Studio, you can create an ID with the Resource Includes dialog.
2. Instantiate an SECTreeCtrl object.
3. Call the Create() method and specify the desired styles, rectangle, and parent. For example:
 
DWORD dwStyle = TVS_SHOWSELALWAYS |
TVS_HASBUTTONS | TVS_LINESATROOT |
TVS_HASLINES | TVS_EDITLABELS |
TVS_SHOWSELALWAYS | TVS_DISABLEDRAGDROP |
WS_CHILD | WS_VISIBLE;
 
DWORD dwStyleEx = TVXS_MULTISEL |
TVXS_FLYBYTOOLTIPS |
LVXS_HILIGHTSUBITEMS;
 
m_pTreeCtrl->Create( dwStyle, dwStyleEx,
rect, this, IDC_TREE);
4. If you are creating a tree control inside a control bar or other resizable window, remember to override the OnSize() method to resize the tree control with the parent window.
 
void CMyControlBar::OnSize(UINT nType, int cx,
int cy)
{
SECControlBar::OnSize(nType, cx, cy);
 
if ( ::IsWindow(m_tree) )
m_tree.SetWindowPos( NULL, 0, 0, cx, cy,
SWP_NOMOVE |
SWP_NOACTIVATE |
SWP_NOZORDER );
}
To add a tree item
1. Instantiate and initialize a TV_ITEM structure for the tree item to be added. For example the following initializes a TV_ITEM structure to be inserted at the root level of the tree.
 
TV_ITEM tvi;
memset(&tvi,0,sizeof(tvi));
tvi.mask =
TVIF_IMAGE|TVIF_TEXT|TVIF_SELECTEDIMAGE;
tvi.pszText = LPSTR_TEXTCALLBACK;
tvi.iImage = I_IMAGECALLBACK;
tvi.iSelectedImage = I_IMAGECALLBACK;
2. Call the InsertItem() method using a TV_INSERTSTRUCT structure as a parameter.
 
TV_INSERTSTRUCT tvis;
memmove(&(tvis.item), &tvi, sizeof(TV_ITEM));
tvis.hParent = TVI_ROOT;
tvis.hInsertAfter = TVI_LAST;
HTREEITEM htiItem1 = m_pTreeCtrl->
InsertItem(&tvis );
3. Alternatively, call one of the overloaded InsertItem() methods.
 
HTREEITEM htiitem2 =
m_pTreeCtrl->InsertItem(LPSTR_TEXTCALLBACK,
I_IMAGECALLBACK,
I_IMAGECALLBACK,
TVI_ROOT,
TVI_LAST );
To create multiple columns
1. Call the InsertColumn() method.
 
m_pTreeCtrl->InsertColumn( 1 /*0-based column index*/,
"Attendance", LVCFMT_CENTER, 40 /*width*/ );
m_pTreeCtrl->InsertColumn( 2 /*0-based column index*/,
"Grade", LVCFMT_CENTER, 40 /*width*/ );
2. If you want, you can mark all items as needing a remeasurement when a WM_PAINT occurs, and invalidate the control.
 
m_pTreeCtrlX->ReMeasureAllItems();
m_pTreeCtrlX->Invalidate();
To set the text on subitems of multi-column trees
In trees with multiple columns, the objects called subitems are managed by the items of the tree (in column 0). These subitems control what is displayed in the additional columns.
Method 1
1. Handle the LVN_GETDISPINFO notification.
2. In the handler for the LVN_GETDISPINFO notification, manage the subitem text storage yourself and then provide it to the control on demand via the callback.
NOTE >> This is the default method demonstrated in the TREEDEMO sample. This method provides maximum performance in large trees with many columns of data.
Method 2
1. Call StoreSubItemText(TRUE) after creation to enable the tree control to manage subitem text storage for you.
2. Call the SetItemText() or SetItemString() methods to set the text directly.
NOTE >> This is an easier approach than using the callback method, but it is not as scalable for large trees when performance is an issue. This method is demonstrated in the STATE sample.
To create a standard image list for tree items
Call the SetImageList() method.
 
SetImageList( CImageList*, TVSIL_NORMAL);
NOTE >> Use of an image list is optional. If state images are also associated with a tree item, you can assign them different widths than the standard image, but not different heights.
To create a state image list for tree items
Call the SetImageList with the TVSIL_STATE flag.
 
SetImageList( CImageList*, TVSIL_STATE );
A state image is an additional image drawn to the left of the normal image like a check box. State images are optional. If the standard images are also associated with a tree item, they can be different widths than the state image, but must be the same height.
To create a tree item with a state image
1. Create a state image list containing each state image to be displayed. See “To create a state image list for tree items”.
2. Instantiate a TV_ITEM data structure. For example,
 
TV_ITEM tvi;
3. Specify the TVIF_SELECTEDIMAGE flag in the mask data member. For example:
 
tvi.mask = TVIF_HANDLE | TVIF_TEXT |
TVIF_SELECTEDIMAGE | TVIF_STATE;
4. Specify the state image and TVIS_STATEIMAGEMASK to the state and stateMask data members, respectively.
 
tvi.state = INDEXTOSTATEIMAGEMASK(1);
tvi.stateMask = TVIS_STATEIMAGEMASK;
5. Specify any additional data members as indicated by any other flags included in the mask data member.
 
tvi.pszText = _T("Line Species 1");
tvi.iImage = m_idiFolderClosed;
tvi.iSelectedImage = m_idiFolderClosed;
tvi.lParam = (LPARAM)m_secTree.m_hWnd;
6. Instantiate a TV_INSERTITEM structure and then initialize the item data member with a pointer to the TV_ITEM data structure.
 
TV_INSERTSTRUCT tvis;
7. Fill in the remaining TV_INSERTITEM data members.
 
tvis.item = tvi;
tvis.hParent = TVI_ROOT;
tvis.hInsertAfter = TVI_LAST;
8. Create the tree item with the InsertItem method.
 
HTREEITEM htreeitem = m_tree.InsertItem(&tvis);
To change a state image on a tree item
1. Create a state image list containing each state image to be displayed. See “To create a state image list for tree items.”
2. Instantiate a TV_ITEM structure and update the data members to prepare for a request for the state image of the item. For example:
 
TV_ITEM tvi;
tvi.hItem = htiItemClicked;
tvi.mask = TVIF_STATE | TVIF_HANDLE;
tvi.stateMask = TVIS_STATEIMAGEMASK;
3. Request the state image using the GetItem() method by passing the address of the TV_ITEM structure as a parameter.
 
m_secTree.GetItem(&tvi);
UINT state = tvi.state & TVIS_STATEIMAGEMASK;
4. Change the state image in the TV_ITEM structure to the image.
 
int index = (state == INDEXTOSTATEIMAGEMASK(1)) ?
2 : // checked
1); // not checked
 
tvi.state = INDEXTOSTATEIMAGEMASK(index);
5. Call SetImage() to have the tree control use the new image.
 
m_tree.SetItem(&tvi);
To add an overlay image to a tree item
1. Obtain a HTREEITEM handle for the tree item. For example:
 
HTREEITEM hti = m_tree.InsertItem(&tvi);
2. Instantiate a TV_ITEM structure and update the members for the desired overlay image.
 
TV_ITEM tvi;
tvi.hItem = hti;
tvi.mask = TVIF_HANDLE | TVIF_STATE;
tvi.stateMask = TVIS_OVERLAYMASK;
tvi.state = INDEXTOOVERLAYMASK( iImage );
3. Call the SetItem() method using the TV_ITEM as a parameter.
 
m_secTree.SetItem( &tvi );
To find out which items are selected
Call the GetSelectionArray() method.
 
pArrSelected = m_secTree.GetSelectionArray();
To specify different colors for tree items
1. Override PickTextColor().
2. In the PickTextColor() override, assign the rgbText member of the LvPaintContext structure supplied as a parameter. For example, the following code is in the Objective Toolkit Build Wizard.
 
void CMyTreeView::PickTextColors(LvPaintContext* pPC)
{
ASSERT(pPC);
SECTreeView::PickTextColors(pPC);
if(pPC->lvi.iSubItem == 0) // subitem (column) number 0
{
// Inside tree column, get a convenient context.
TvPaintContext* pTvPC = (TvPaintContext*)pPC;
 
// Check the properties of tree item (pTvPC->tvi),
// and change color accordingly. For example,
// to change the color of root nodes:
HTREEITEM hParent=GetParentItem(pTvPC->tvi.hItem);
if(hParent == NULL)
{
pTvPC->rgbText= m_rootColor;
 
if ( GetFocus()== this && (pTvPC->lvi.state &
LVIS_SELECTED) )
pTvPC->rgbTextBkgnd = RGB(255, 255, 0); // highlight
}
}
}
To specify different fonts for tree items
1. Override the PickTextFont() method.
2. In the PickTextFont() override, assign the pFont member of the LvPaintContext structure supplied as a parameter. For example, the following code is in the Objective Toolkit Build Wizard.
 
void CMyTreeView::PickTextFont(LvPaintContext* pPC) {
ASSERT(pPC);
SECTreeView::PickTextFont(pPC);
if(pPC->lvi.iSubItem == 0) // subitem (column) number 0
{
// Inside tree column, get a convenient context.
TvPaintContext* pTvPC = (TvPaintContext*)pPC;
 
// Check the properties of tree item (pTvPC->tvi),
// and change font accordingly. For example,
// to change the font of root nodes:
HTREEITEM hParent=GetParentItem(pTvPC->tvi.hItem);
if(hParent == NULL)
pTvPC->pFont= &m_fontRoot;
}
}
To update the tree control
1. Call the following methods after making changes to the tree control/view.
 
ReMeasureAllItems();
Invalidate();
2. To update the GUI after every insert/delete, call EnableRedrawAfterInsert(TRUE). The dialog portion of the TREEDEMO sample demonstrates this. The Expand() function has an additional BOOL parameter to control redrawing of expanded/collapsed nodes.
To incorporate SECTreeCtrl into an application already using CtreeCtrl
In the header file, locate your instance of CTreeCtrl.
1. Change this from CTreeCtrl to SECTreeCtrl.
2. Population of the tree is the same as for CTreeCtrl.
NOTE >> SECTreeCtrl is for the most part drop-in compatible with the CTreeCtrl, but not when you start changing or adding behaviors such as drag scrolling.