One of the most popular controls released with Windows 95 is the tree view control. In this hour, you will learn
Tree controls are similar to the list box controls discussed in Hour 7, "Using List Box and Combo Box Controls," except that they display information in a tree, or hierarchy. Tree view controls are often used to display disk directories or the contents of books or other documents.
New Term: In a tree view, parent items are located at the root, or top level, of the tree. In a tree view, child items are located under parent items.
Items in a tree view control are arranged into groups, with child items located under parent items. Child items are also indented, or nested, under a parent. A child item at one level can be the parent of child items at lower levels. The Windows Explorer is one of the applications that uses the new tree control, shown in Figure 19.1.
Figure 19.1.
The Windows Explorer is one of the many applications that use tree view controls.
The tree control is a popular control because it enables you to display a great deal of information in a hierarchy. Unlike a list box, a small amount of high-level information can be presented initially, enabling the user to decide which parts of the tree should be expanded. The tree control also enables information to be displayed so that relationships between different items in the control can be seen. For example, in the Explorer, subdirectories are nested in order to show their positions in the directory.
Tree views are also popular because they offer a wide range of options. As with list view controls, which were discussed in Hour 18, "List View Controls," tree view controls put the user in charge. As you see in an example later in this hour, it's very easy to enable a user to perform drag-and-drop operations in a tree view control. With a few lines of code, you can also enable a user to edit the labels for individual tree view items.
You can create tree view controls with several different styles. For example, many tree view controls display a bitmap next to each item. Many also display a tree control button next to each item. This button contains a plus sign if an item can be expanded. If the button is clicked, the tree view expands to display the item's children. When it is expanded, the item displays a button with a minus sign.
Tree controls often contain a large amount of information. The user can control the amount of information displayed by expanding or collapsing tree items. When more horizontal or vertical room is needed, the tree control automatically displays scrollbars.
There are two ways to use tree controls in your MFC-based programs. When a tree control is used in a dialog box, the control is added just as buttons, list boxes, and other controls have been added in previous hours. The MFC class CTreeCtrl is used to interact with tree controls and is associated with a tree view control using ClassWizard.
Tree view controls can also be used in a view. The CTreeView class is a specialized view that consists of a single tree control. The CTreeView class is derived from CCtrlView, which is itself derived from CView.
Because CTreeView is derived from CView, it can be used just like CView. For the first example in this hour, you use CTreeView as the main view in an MFC-based application.
For this example, create an SDI project named TreeEx using AppWizard. In AppWizard step six, a checkered flag is displayed along with a list box containing classes that are generated for the application. Follow these steps to use a tree view as the application's main view:
You can compile and run the TreeEx application; however, no items have been added to the tree control yet. In the next section, you see how items are added to a tree view.
As discussed earlier, the CTreeView class is derived from CView and contains a tree view control that covers the entire view surface. To get access to the tree view control, you use the GetTreeControl function.
CTreeCtrl& tree = GetTreeCtrl();
Note that the return value from GetTreeCtrl is a reference to a CTreeCtrl object. This means that the return value must be assigned, or bound, to a CTreeCtrl reference variable.
After you have access to the tree view control, items can be added to the control in several different ways. The simplest methods are used when adding simple text items without bitmap images to the tree view control. When adding simple items to a tree control, only the label for the item must be provided:
HTREEITEM hItem = tree.InsertItem( "Foo" );
This line adds an item to the tree control at the first, or root, level. The return value from InsertItem is a handle to the new item if it was inserted successfully or NULL if the item could not be inserted.
To add an item as a child, pass the parent's handle as a parameter when inserting the item.
tree.InsertItem( "Bar", hItem );
The source code provided in Listing 19.1 uses the functions discussed previously to add eight items in the CTreeExView::OnInitialUpdate function.
void CTreeExView::OnInitialUpdate() { CTreeView::OnInitialUpdate(); CTreeCtrl& tree = GetTreeCtrl(); HTREEITEM hChapter = tree.InsertItem( "Chapter 1" ); tree.InsertItem( "What", hChapter); tree.InsertItem( "Why", hChapter ); tree.InsertItem( "How", hChapter ); hChapter = tree.InsertItem( "Chapter 2" ); tree.InsertItem( "What", hChapter ); tree.InsertItem( "Why", hChapter ); tree.InsertItem( "How", hChapter );
} After you add the source code from Listing 19.1, compile and run the TreeEx project. This version of the tree view control is a minimal tree control, as shown in Figure 19.2. There are no connecting lines, no bitmaps, and no pushbuttons; in short, it's fairly simple.
Figure 19.2.
The main view of the TreeEx example.
In addition to other available view and window styles, you can apply four style options specifically to a tree view control:
You usually don't need to get involved with defining the styles for a normal view; the default settings are good enough 99 percent of the time. For a tree view, however, you might want to select one or more of the optional styles by modifying the CTreeExView::PreCreateWindow function. The source code in Listing 19.2 applies all the optional attributes except for TVS_EDITLABELS.
BOOL CTreeExView::PreCreateWindow(CREATESTRUCT& cs) { cs.style |= ( TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS ); return CTreeView::PreCreateWindow(cs);
} Compile and run the TreeEx example, and you'll see that the example now has lines and buttons, as shown in Figure 19.3. It might sound like a small addition, but it makes the control much easier to use, especially if the tree must be expanded and collapsed frequently.
Figure 19.3.
The TreeEx application after modifying the Tree View styles.
Just a Minute: At first glance, you might think that the list view and tree view controls are almost identical as far as the API goes. The key word, unfortunately, is almost.The biggest difference between the list view and tree view controls is in how individual items are referenced. In the list view control, an item index is used when communicating with the control. Because tree view controls allow items to be expanded and collapsed, however, the idea of an absolute index doesn't work. An item handle, or HTREEITEM, is used when referring to a tree view item.
In addition, several smaller differences can tend to be a bit aggravating. For example, CListCtrl::CreateDragImage takes two parameters, whereas the equivalent CTreeCtrl function takes only one parameter.
For the second example in this hour, you add a tree view control to the TreeEx About dialog box. This version of the tree view control supports drag and drop and also displays bitmaps for each item.
Adding a tree control to a dialog box is almost exactly like adding any other control to a dialog box. Select the ResourceView tab in the project workspace window and open the Dialog folder. Open the IDD_ABOUTBOX dialog box resource by double-clicking the IDD_ABOUTBOX icon or by right-clicking the icon and selecting Open from the pop-up menu.
Remove the current controls except for the OK button from IDD_ABOUTBOX. Add a tree view control to the dialog box by dragging the tree view icon onto the dialog box or by selecting a tree view control and clicking on the dialog box. The modified dialog box is shown in Fig-ure 19.4.
As shown in Figure 19.4, the tree view control displays a simulated tree to assist in sizing the control.
Figure 19.4.
Adding a tree view control to a dialog box.
Just a Minute: A tree control is often larger than a list box due to the space required for indenting the nested child items.
The tree view control's properties are set using the Properties dialog box. Some of the properties available for tree view controls are also available for list boxes. The tree control property options include the following:
Open the Properties dialog box for the tree view control and change the resource ID to IDC_TREE. All other properties should be set to their default values except for the following items, which should be checked:
Using ClassWizard, associate a CTreeCtrl member variable with the new tree control, using the values from Table 19.1.
Control ID | Variable Name | Category | Type |
IDC_TREE | m_tree | Control | CTreeCtrl |
The version of the tree view control contained in the About dialog box displays two bitmaps next to tree view items:
As discussed in Hour 17, "Using Image Lists and Bitmaps," an image list can consist of a single bitmap that has one or more segments. The bitmap shown in Figure 19.5 contains both images used by the tree view control.
Figure 19.5.
Bitmaps displayed in the tree view control.
Use the image editor to create the bitmap in Figure 19.5. Use red as a background color for the bitmap to make it easier to draw the bitmap transparently. Use the values from Table 19.2 for the bitmap.
Resource ID | Height | Item Width | Total Width |
IDB_TREE | 14 | 14 | 28 |
The CAboutDlg class must be modified in order to handle the tree view control. You must add a total of four new member variables to the CAboutDlg class:
Add the source code provided in Listing 19.3 to the implementation section of the CAboutDlg class declaration.
// Implementation protected: CImageList m_imageList; BOOL m_bIsDragging; HTREEITEM m_dragItem;
HTREEITEM m_dragTarget; The tree control is initialized when the CAboutDlg class receives the WM_INITDIALOG message. Using ClassWizard, add a message-handling function for WM_INITDIALOG and accept the suggested name of OnInitDialog. Add the source code in Listing 19.4 to the OnInitDialog member function. A little cut-and-paste editing can save you some typing here because this source code is similar to the source code used earlier in Listing 19.1.
BOOL CAboutDlg::OnInitDialog() { CDialog::OnInitDialog(); m_bIsDragging = FALSE; m_dragTarget = NULL; m_dragItem = NULL; m_imageList.Create( IDB_TREE, 14, 1, RGB(255,0,0) ); m_tree.SetImageList( &m_imageList, TVSIL_NORMAL ); HTREEITEM hChapter; hChapter = m_tree.InsertItem( "Chapter 1", 0, 0 ); m_tree.InsertItem( "What", 1, 1, hChapter ); m_tree.InsertItem( "Why", 1, 1, hChapter ); m_tree.InsertItem( "How", 1, 1, hChapter ); hChapter = m_tree.InsertItem( "Chapter 2", 0, 0 ); m_tree.InsertItem( "What", 1, 1, hChapter ); m_tree.InsertItem( "Why", 1, 1, hChapter ); m_tree.InsertItem( "How", 1, 1, hChapter ); return TRUE;
} There are a few small differences between Listing 19.1 and Listing 19.4. In Listing 19.4, an image list is first created and then associated with the tree view control by calling the SetImageList function. In addition, the InsertItem function uses two extra parameters.
m_tree.InsertItem( "How", 1, 1, hChapter );
As in Listing 19.1, the first parameter is the text label associated with the tree item. The second parameter is the image index associated with the item when it's not selected; the third parameter is the selected image index. This enables you to specify different images for selected and non-selected items. As before, the last parameter is a handle to the item's parent item, or it can be omitted if the item is added at the root level.
Compile and run the TreeEx project. The modified TreeEx About dialog box is shown in Figure 19.6.
Figure 19.6.
The modified About dialog box from TreeEx.
Of course, any control that enables items to be inserted must also enable them to be removed. The CTreeCtrl::DeleteItem member function is used to delete an item from the tree view control:
BOOL fResult = m_tree.DeleteItem(hTreeItem);
CAUTION: When an item is removed from the tree control, any child items that are nested below it are also removed.
The return value from DeleteItem is FALSE, or zero, if the item could not be deleted, or non-zero if the item was deleted successfully.
Time Saver: To delete all the items in a tree view control, use the CTreeCtrl::DeleteAllItems member function:BOOL fResult = m_tree.DeleteAllItems();
To show how these functions are used in a real application, add two buttons to the About dialog box. Figure 19.7 shows the About dialog box after adding Remove and Remove All buttons.
Figure 19.7.
The About dialog box after adding new push- button controls.
Use the values from Table 19.3 to assign properties to the new controls added to the About dialog box.
Control | Resource ID | Caption |
Remove button | IDC_REMOVE | &Remove |
Remove All button | IDC_REMOVEALL | Remove &All |
Use ClassWizard to add message-handling functions for the new controls, as shown in Table 19.4.
Class Name | Object ID | Message | Function |
CAboutDlg | IDC_REMOVE | BN_CLICKED | OnRemove |
CAboutDlg | IDC_REMOVEALL | BN_CLICKED | OnRemoveall |
The source code used to implement the Remove and Removeall functions is provided in Listing 19.5. Add this source code to the CAboutDlg::Remove and CAboutDlg::Removeall functions found in TreeEx.cpp.
void CAboutDlg::OnRemove() { HTREEITEM hItem = m_tree.GetSelectedItem(); if(hItem != NULL) { VERIFY(m_tree.DeleteItem(hItem)); } else { AfxMessageBox("Please select an item first"); } } void CAboutDlg::OnRemoveall() { m_tree.DeleteAllItems();
} Compile and run the TreeEx project. The modified About TreeEx dialog box is shown in Figure 19.8. Experiment with removing items from the dialog box. Note that removing a node at the root level also removes all of its children.
Like the list view control discussed in Hour 18, tree view controls communicate with their parent windows using notification messages. In the next section, you learn about TVN_BEGINDRAG, the message used to start a drag-and-drop process.
Figure 19.8.
The modified About dialog box from TreeEx.
In order to handle drag-and-drop functionality inside a tree view control, you must handle the following three messages:
Using ClassWizard, add message-handling functions for these messages to the CAboutDlg class, using the values from Table 19.5.
Object ID | Message | Function |
IDC_TREE | TVN_BEGINDRAG | OnBegindragTree |
CAboutDlg | WM_MOUSEMOVE | OnMouseMove |
CAboutDlg | WM_LBUTTONUP | OnLButtonUp |
The source code for the three functions is provided in Listing 19.6.
void CAboutDlg::OnBegindragTree(NMHDR* pNMHDR, LRESULT* pResult) { NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR; m_dragItem = pNMTreeView->itemNew.hItem; if( m_tree.GetParentItem( m_dragItem ) != NULL ) { CImageList* pDragImage; pDragImage = m_tree.CreateDragImage( m_dragItem ); m_tree.SelectItem( m_dragItem ); pDragImage->BeginDrag( 0, CPoint(0,0) ); pDragImage->DragEnter( &m_tree, pNMTreeView->ptDrag ); SetCapture(); m_bIsDragging = TRUE; delete pDragImage; } *pResult = 0 ; } void CAboutDlg::OnMouseMove(UINT nFlags, CPoint point) { if( m_bIsDragging != FALSE ) { CPoint ptTree( point ); MapWindowPoints( &m_tree, &ptTree, 1 ); CImageList::DragMove( ptTree ); UINT uHitTest = TVHT_ONITEM; m_dragTarget = m_tree.HitTest( ptTree, &uHitTest ); } CDialog::OnMouseMove(nFlags, point); } void CAboutDlg::OnLButtonUp(UINT nFlags, CPoint point) { if( m_bIsDragging != FALSE ) { CImageList::DragLeave( &m_tree ); CImageList::EndDrag(); ReleaseCapture(); m_bIsDragging = FALSE; if( m_dragTarget != NULL ) { HTREEITEM hParent; hParent = m_tree.GetParentItem( m_dragTarget ); CString szLabel = m_tree.GetItemText( m_dragItem ); if( hParent != NULL ) m_tree.InsertItem( szLabel, 1, 1, hParent, m_dragTarget ); else m_tree.InsertItem( szLabel, 1, 1, m_dragTarget, TVI_FIRST ); m_tree.DeleteItem( m_dragItem ); } } else CDialog::OnLButtonUp(nFlags, point);
} The source code in Listing 19.6 is all you need to perform a drag-and-drop operation inside a single control. The drag sequence begins with the tree view control sending the TVN_DRAGBEGIN to the control's parent--in this case, the TreeEx About dialog box. The MFC framework translates this message into a CAboutDlg::OnBegindragTree function call. Inside this function, the handle to the drag item is stored in m_dragItem.
Just a Minute: In this example, you aren't dragging items at the first, or root level, so GetParentItem is used to get a handle to the drag item's parent; if NULL is returned, the item is at the root level, and the drag never starts.
As the mouse is moved across the screen during the drag and drop, WM_MOUSEMOVE messages are sent to the dialog box, just as in the ListEx example from Hour 18.
At some point, the user releases the left mouse button, resulting in a WM_LBUTTONUP message, which is translated into a call to the CAboutDlg::OnLButtonUp function. If a drag and drop is in progress, the operation is completed by calling the DragLeave and EndDrag functions. The mouse capture is released, and the m_bIsDragging flag is set to FALSE.
The completion of a drag and drop is slightly more complicated in a tree view control than in a list view control. If the drop target is a second-level item, the drag item is inserted just after the drop target. If the drop target is a root-level item, the drag item is inserted as the first child item. These calls to the InsertItem function use a fourth parameter, which is a handle for the item just before the new item. This parameter can be one of three predefined values:
After the drag item has been inserted in a new position, the old position is removed using the DeleteItem function. In order to implement a copy-drag, just eliminate the call to DeleteItem.
Like the list view control, the tree view control offers a built-in Edit control that you can use to edit items contained in the control. In order to take advantage of this capability, the tree view control must have its Edit labels property checked.
In addition to setting the tree view control style, there are two messages that relate to label editing:
These messages are handled exactly as they are for a list view control. When you receive TVN_BEGINLABELEDIT, you can prevent a label from being edited by setting *pResult to TRUE, and you can allow editing to proceed by setting *pResult to FALSE. In addition, you can use the TVN_BEGINLABELEDIT message to take control of the tree view's edit control.
The TVN_ENDLABELEDIT message is used exactly like the LVN_ENDLABELEDIT message is used with a list view control.
CAUTION: If you use the newly edited label text in your application, make sure to look out for situations in which the user has cancelled the label editing operation. When this happens, the TV_ITEM pszText member variable is set to NULL, or the iItem member variable is set to -1.
Add new message-handling functions for the label editing messages using the values in Table 19.6.
Class Name | Object ID | Message | Function |
CAboutDlg | IDC_TREE | TVN_BEGINLABELEDIT | OnBeginlabeleditTree |
CAboutDlg | IDC_TREE | TVN_ENDLABELEDIT | OnEndlabeleditTree |
The source code for these functions is provided in Listing 19.7.
void CAboutDlg::OnBeginlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult) { *pResult = FALSE; } void CAboutDlg::OnEndlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult) { *pResult = TRUE;
} Compile and run the TreeEx project. Experiment with editing item labels, drag and drop, and removing items in the About dialog box.
In this hour, you learned about the tree view control and created two examples: a tree view used as an SDI main view and a tree view control in a dialog box. You also learned about drag and drop as well as edit notifications.
m_tree.EnsureVisible(hItem);
The Workshop is designed to help you anticipate possible questions, review what you've learned, and begin thinking ahead to putting your knowledge into practice. The answers to the quiz are in Appendix B, "Quiz Answers."
© Copyright, Macmillan Computer Publishing. All rights reserved.