Topic : Common Controls
Author : Unknown
Page : << Previous 8  Next >>
Go to page :

it will not grow later (The fourth and fifth parameter of function CImageList::Create(...) are 5 and 0 respectively).

Next we use variable bmp to load each bitmap resource and add it to the list. When calling function CImageList::Add(...), we pass a COLORREF type value to its second parameter (RGB macro specifies the intensity of red, green and blue colors, and returns a COLORREF type value). This means all the white color in the image will be treated as the background. In the sample application, the background color is set to white:

m_pilCtrl->SetBkColor(RGB(255, 255, 255));

We can also change the values contained in the RGB macro to set the background to other colors.

Besides this method, we can also prepare all the images in one bitmap resource (just like the tool bar resource), and call the following versions of function CImageList::Create(...) to create the image list:

BOOL CImageList::Create(UINT nBitmapID, int cx, int nGrow, COLORREF crMask);

BOOL CImageList::Create(LPCTSTR lpszBitmapID, int cx, int nGrow, COLORREF crMask);

Here nBitmapID or lpszBitmapID specifies the bitmap resource ID, and cx specifies the horizontal dimension of an individual image. With this parameter, the system knows how to divide one big image into several small images.

After creating the image list, function CTreeCtrl::SetImageList(...) is called to assign the image list to the tree control:


m_treeCtrl.SetImageList(m_pilCtrl, TVSIL_NORMAL);


Since the image list is created dynamically, we need to release it when it is no longer in use. The best place to destroy the image list is in CDialog::OnDestroy(), when the dialog box is about to be destroyed. This function is the handler of WM_DESTROY message, which could be easily added through using Class Wizard. The following is the implementation of this function in the sample:

(Code omitted)

We call function CImageList::GetImageList(...) to obtain the pointer to the image list, then call CImageList::DeleteImageList() to delete the image list. Please note that this function releases only the images stored in the list, it does not delete CImageList type object. After the image list is deleted, we still need to use keyword "delete" to delete this object.

In the sample, a tree with the structure showed in Figure 10 is created.

This tree has 7 nodes. Node "Root" is the root node, it has one child node "Doc". Node "Doc" has a child node "Folder", and node "Folder" has four child nodes "Leaf1", "Leaf2", "Leaf3" and "Leaf4". The following portion of function CCCtlDlg::OnInitDialog() shows how the node "Root" is created in the sample:

(Code omitted)

Variable tvInsertStruct is declared at the beginning of function CCCtlDlg::OnInitDialog(), it is a TV_INSERTSTRUCT type object. To create a specific node, we must stuff this object with node information and call function CTreeCtrl::InsertItem(...). This function returns a handle to the newly created node, which is stored in variable hTreeItem and will be used to create its child node. The following portion of function CCCtlDlg::OnInitDialog() shows how the child node is created:

(Code omitted)

This procedure is exactly the same for other nodes. For different nodes, the only difference of this procedure is that each node has different parent node, uses different image index and text string. For all nodes, their normal states and selected states are represented by the same image (member iImage and iSelectedImage are assigned the same image index), so the image will not change if we select a node.

With the above implementations, the tree control can work. By compiling and executing the application at this point, we will see a tree with seven nodes, which are represented by different labels and images. A node can be expanded or collapsed with mouse clicking if it has child node(s).

13 Handling Tree Control Messages

There are many messages associated with the tree control. We need to write message handlers for the tree control in order to customize its default behavior. In sample 13\CCtl we will demonstrates two methods of customizing a tree control: 1) How to change a node's associated image dynamically. 2) How to enable label editing.

Sample 13\CCtl is based on sample 12\CCtl. In this sample the image associated with node "Folder" will be changed automatically according to its current state (expanded or collapsed). If it is expanded, image IDB_BITMAP_OPENFOLDER will be associated with this node; if it is collapsed, image IDB_BITMAP_CLOSED_FOLDER will be used. Also, the application supports dynamic label editing: if the user clicks mouse's left button on the label of a node, that node will enter editing mode, and we can edit the text string as if we were using an edit box.

The messages associated with node expanding and collapsing are TVN_ITEMEXPANDING and TVN_ITEMEXPANDED. The former message is sent when a node is about to be expanded or collapsed, and the latter message is sent after such action is completed. In our case, we need to handle the former message to change a node's image before its state changes.

Handling TVN_ITEMEXPANDING to Change a Node's Associated Image

In MFC, message TVN_ITEMEXPANDING can be mapped to a member function as follows:

void CTreeCtrl::OnItemexpanding(NMHDR *pNMHDR, LRESULT *pResult)



*pResult = 0;


Variable pNMTreeView is a pointer to NM_TREEVIEW type object obtained from the message parameters, it contains the information about the node being clicked:

typedef struct _NM_TREEVIEW{

NMHDR hdr;

UINT action;

TV_ITEM itemOld;

TV_ITEM itemNew;

POINT ptDrag;


The most important member of this structure is action, it could be either TVE_EXPAND (indicating the node is about to expand) or TVE_COLLAPSE (indicating the node is about to collapse). Two other useful members are itemOld and itemNew, both of them are TV_ITEM type objects and contain old and new states of the node respectively. We can check iImage member of itemNew to see if the associated image is 2 or 3 (Indices 2 and 3 correspond to image IDB_BITMAP_CLOSED_FOLDER and IDB_BITMAP_OPENFOLDER respectively, which indicate that the node represents a folder. In the sample, we will not change other node's image when they are being expanded or collapsed), if so, we need to call function CTreeCtrl::SetItemImage(...) to change the image of the node if necessary.

We can handle this message either within class CTreeCtrl or CDialog. Handling the message in CTreeCtrl has the advantage that once the feature is implemented, we can reuse this class in other applications without adding additional code.

In the sample, a new class MCTreeCtrl is designed for this purpose. It is added to the application through using Class Wizard. Also, message handlers MCTreeCtrl::OnItemexpanding(...) and MCTreeCtrl::OnEndlabeledit(...) are added to dynamically change node's associated images and enable label editing (Label editing will be discussed later).

The following is the implementation of function MCTreeCtrl::OnItemexpanding(...):

(Code omitted)

If the node is about to expand and its associated image is 2, we associate image 3 with this node. This is implemented through calling function CTreeCtrl::SetItemImage(...), which has the following format:

BOOL CTreeCtrl::SetItemImage(HTREEITEM hItem, int nImage, int nSelectedImage);

The first parameter of this function is the handle of tree control, which can be obtained from pNMTreeView->itemNew.hItem. Similarly, if the node is about to collapse and its associated image is 3, we call this function to associate image 2 with this node.

Handling TVN_ENDLABELEDIT to Enable Label Editing

The next feature we want to add is label editing. If we are familiar with "Explorer" application in Windows95(, we know that the file or directory names (which are node labels) can be edited dynamically by single clicking on it.

The first step of enabling label editing is to set "Edit labels" style when adding tree control resource to the dialog template. The following lsts necessary steps of doing this: 1) Invoke "Tree Control Properties" property sheet, click "Styles" tab. 2) Check "Edit labels" check box (Figure 11).

Label editing will be enabled if this style is selected. However, if we do not add code to change the label at the end of editing, the label will remain unchanged after it is edited. To make this happen, we must handle message TVN_ENDLABELEDIT.

Standard TVN_ENDLABELEDIT message handler added by Class Wizard will have the following format:

void MCTreeCtrl::OnEndlabeledit(NMHDR *pNMHDR, LRESULT *pResult)





Here pTVDispInfo is a pointer to TV_DISPINFO type object, which can be obtained from the message parameter. The most useful member of TV_DISPINFO is item, which is a TV_ITEM type object. Three members of item contain valid information: hItem, lParam, and pszText. We could use hItem to identify the node and use pszText to obtain the updated text string. If pszText is a NULL pointer, this means the editing is canceled (Label editing can be canceled through pressing ESC key). Otherwise it will contain a NULL-terminated string. The following is the implementation of this message handler:

(Code omitted)

If the editing is not canceled, we need to call function CTreeCtrl::SetItemText(...) to set the node's new text, which has the following format:

BOOL CTreeCtrl::SetItemText(HTREEITEM hItem, LPCTSTR lpszItem);

This function is similar to CTreeCtrl::SetItemImage(...). Its first parameter is the handle of tree control, and the second parameter is a string pointer to the new label text.

There are other messages associated with label editing, one useful message is TVN_BEGINLABELEDIT, which will be sent when the editing is about to begin. We can handle this message to disable label editing for certain nodes. In the message handler, if we assign a non-zero value to

Page : << Previous 8  Next >>