Teach Yourself Visual C++® 5 in 24 Hours

Previous chapterNext chapterContents


- Hour 19 -
Tree Views

One of the most popular controls released with Windows 95 is the tree view control. In this hour, you will learn

What Is a Tree View Control?

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.

MFC Support for Tree View Controls

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.

Using a Tree View Control as a View

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:

1. Select the view class in the class list box, in this case CTreeExView.

2. Select CTreeView from the Base Class combo box.

3. Click the Finish button to end the AppWizard process and display the New Project Information dialog box.

4. Click OK to generate the code for the TreeEx project.

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.

Adding Items to a CTreeView

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.

TYPE: Listing 19.1. Adding items to a CTreeView.

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.

Applying Styles to a Tree View Control

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.

TYPE: Listing 19.2. Modifying the tree view style in PreCreateWindow.

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.


Adding Tree View Controls to Dialog Boxes

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.

Setting Tree View Control Properties

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.

Table 19.1. Values used to add a CTreeCtrl member variable for CAboutDlg.

Control ID Variable Name Category Type
IDC_TREE m_tree Control CTreeCtrl

Creating an Image List Control

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.

Table 19.2. Attributes for the image list bitmap used in TreeEx.

Resource ID Height Item Width Total Width
IDB_TREE 14 14 28

Modifying the Dialog Box Class

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.

TYPE: Listing 19.3. Additions to 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.

TYPE: Listing 19.4. The CAboutDlg::OnInitDialog member function.

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.

Deleting Items from a Tree View Control

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.

Table 19.3. Property values for 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.

Table 19.4. Message-handling functions used for the new controls.

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.

TYPE: Listing 19.5. Deleting items from the About TreeEx dialog box.

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.

Tree View Control Notifications

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.

Adding Drag-and-Drop Functionality to a Tree View Control

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.

Table 19.5. Message-handling functions used for drag-and-drop processes.

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.

TYPE: Listing 19.6. Functions used to implement simple drag and drop.

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.

Performing In-Place Label Editing

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.

Table 19.6. Message-handling functions used for label editing.

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.

TYPE: Listing 19.7. Functions used to implement simple drag and drop.

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.

Summary

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.

Q&A

Q How can I allow or deny label editing based on the text the user has entered into a label?

A When you receive the TVN_ENDLABELEDIT message, you can check the value stored in the pszText member of the item structure. If the string is valid, set *pResult to TRUE, otherwise, set it to FALSE to reject the change.

Q How can I force the tree view control to scroll to a particular position?

A You can force a particular item to be visible by calling the CTreeCtrl::EnsureVisible function:
m_tree.EnsureVisible(hItem);
The tree view control will scroll if necessary to display the item referred to by hItem. If the item is hidden under a parent, the tree will be expanded to show the item.

Workshop

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."

Quiz

1. What messages are sent when a user edits a tree view label?

2. What messages must be handled to implement drag and drop?

3. Why are two image list indexes specified when adding an item that displays an image to a tree view control?

4. What CTreeCtrl member function is used to remove all items in a tree view control?

5. How do you change the properties for a tree view control that is part of the CTreeView class?

6. What properties are available specifically for tree view controls, and what are their equivalent window styles?

7. How are individual tree view items referred to?

8. What is the size of the images used by the tree view control?

9. How do you insert an item as the first child under a parent?

Exercises

1. Modify the CAboutDlg so that you can add an item to the tree view control.

2. Add an additional item to the IDB_TREE image list. Use the new image list item when a top-level tree view control item is selected.


Previous chapterNext chapterContents


Macmillan Computer Publishing USA

© Copyright, Macmillan Computer Publishing. All rights reserved.