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.
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 Section 18, "List View Controls," tree view controls put the user in charge. As you see in an example later in this section, 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.
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 section, you use CTreeView as the main view in an MFC-based application.
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.
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.
The main view of the TreeEx example.
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.
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 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.
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.
| Control ID | Variable Name | Category | Type |
| IDC_TREE | m_tree | Control | CTreeCtrl |
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 |
// 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.
The modified About dialog box from TreeEx.
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.
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.
The modified About dialog box from TreeEx.
| 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 Section 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:
In addition to setting the tree view control style, there are two messages that relate to label editing:
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.
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);
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?
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.
Download Sample Programs - TreeEx.zip
|
|
|
|