Using Tree View Controls

Using Tree View Controls


This section contains examples that demonstrate the following tasks:

Creating a Tree View Control

To create a tree view control, use the CreateWindowEx function, specifying the WC_TREEVIEW value for the window class. The tree view window class is registered in the application's address space when the common control dynamic-link library (DLL) is loaded. To ensure that the DLL is loaded, use the InitCommonControls function.

The following example creates a tree view control that is sized to fit the client area of the parent window. It also uses application-defined functions to associate an image list with the control and add items to the control.

	// CreateATreeView - creates a tree view control. 
	// Returns the handle to the new control if successful, or NULL
	//     otherwise. 
	// hwndParent - handle to the control's parent window. 
	// lpszFileName - name of the file to parse for tree view items. 
	HWND CreateATreeView(HWND hwndParent, LPSTR lpszFileName) 
	{ 
    RECT rcClient;  // dimensions of client area 
    HWND hwndTV;    // handle to tree view control 
 
    // Ensure that the common control DLL is loaded. 
    InitCommonControls(); 
 
    // Get the dimensions of the parent window's client area, and create 
    // the tree view control. 
    GetClientRect(hwndParent, &rcClient); 
    hwndTV = CreateWindowEx(0, WC_TREEVIEW, "Tree View", 
        WS_VISIBLE | WS_CHILD | WS_BORDER | TVS_HASLINES, 
        0, 0, rcClient.right, rcClient.bottom, 
        hwndParent, (HMENU) ID_TREEVIEW, g_hinst, NULL); 
 
    // Initialize the image list, and add items to the control. 
    // InitTreeViewImageLists and InitTreeViewItems are application- 
    // defined functions. 
    if (!InitTreeViewImageLists(hwndTV) || 
            !InitTreeViewItems(hwndTV, lpszFileName)) { 
        DestroyWindow(hwndTV); 
        return FALSE; 
    } 
    return hwndTV; 
	} 
 

Initializing the Image List

Every item in a tree view control can have two images associated with it. An item displays one image when it is selected and the other when it is not. To include images with tree view items, first use the image list functions to create an image list and add images to it. Then associate the image list with the tree view control by using the TVM_SETIMAGELIST message.

The following example creates an image list, adds three bitmaps to the list, and associates the image list with a tree view control.

// InitTreeViewImageLists - creates an image list, adds three bitmaps 
// to it, and associates the image list with a tree view control. 
// Returns TRUE if successful, or FALSE otherwise. 
// hwndTV - handle to the tree view control. 
//
// Global variables and constants 
//     g_nOpen, g_nClosed, and g_nDocument - integer variables for 
//         indexes of the images. 
//     CX_BITMAP and CY_BITMAP - width and height of an icon. 
//     NUM_BITMAPS - number of bitmaps to add to the image list. 
BOOL InitTreeViewImageLists(HWND hwndTV) 
    { 
    HIMAGELIST himl;  // handle to image list 
    HBITMAP hbmp;     // handle to bitmap 

    // Create the image list. 
    if ((himl = ImageList_Create(CX_BITMAP, CY_BITMAP, 
        FALSE, NUM_BITMAPS, 0)) == NULL) 
        return FALSE; 

    // Add the open file, closed file, and document bitmaps. 
    hbmp = LoadBitmap(g_hinst, MAKEINTRESOURCE(IDB_OPEN_FILE)); 
    g_nOpen = ImageList_Add(himl, hbmp, (HBITMAP) NULL); 
    DeleteObject(hbmp); 

    hbmp = LoadBitmap(g_hinst, MAKEINTRESOURCE(IDB_CLOSED_FILE)); 
    g_nClosed = ImageList_Add(himl, hbmp, (HBITMAP) NULL); 
    DeleteObject(hbmp); 

    hbmp = LoadBitmap(g_hinst, MAKEINTRESOURCE(IDB_DOCUMENT)); 
    g_nDocument = ImageList_Add(himl, hbmp, (HBITMAP) NULL); 
    DeleteObject(hbmp); 

    // Fail if not all of the images were added. 
    if (ImageList_GetImageCount(himl) < 3) 
        return FALSE; 

    // Associate the image list with the tree view control. 
    TreeView_SetImageList(hwndTV, himl, TVSIL_NORMAL); 

    return TRUE; 
    } 

Adding Tree View Items

You add an item to a tree view control by sending the TVM_INSERTITEM message to the control. The message includes the address of a TVINSERTSTRUCT structure, specifying the parent item, the item after which the new item is inserted, and a TVITEM structure that defines the attributes of the item. The attributes include the item's label, its selected and nonselected images, and a 32-bit application-defined value.

The example in this section demonstrates how to create a table of contents based on the information in a text file. The example includes two functions. The first function searches a file for headings. When it finds one, it extracts the text of the heading and the value that indicates the level of the heading and then passes them to the second function.

The second function adds an item to a tree view control, using the heading text as the item's label and the heading level to determine the parent item for the new item. A level one heading is added to the root of the tree view control, a level two heading is added as a child item of the previous level one item, and so on. The function assigns an image to an item based on whether it has any child items. If an item has child items, it gets an image representing a closed folder. Otherwise, it gets an image representing a document. An item uses the same image for both the selected and nonselected states.

	// InitTreeViewItems - extracts headings from the specified file and 
	//    passes them to a function that adds them to a tree view control. 
	// Returns TRUE if successful, or FALSE otherwise. 
	// hwndTV - handle to the tree view control. 
	// lpszFileName - name of file with headings. 
	BOOL InitTreeViewItems(HWND hwndTV, LPSTR lpszFileName) 
	{ 
    HANDLE hf;            // handle to file 
    char szItemText[128]; // label text of tree view item 
    int nLevel;           // heading level 
 
    // Open the file to parse. 
    if ((hf = CreateFile(lpszFileName, GENERIC_READ, 
            FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES) NULL, 
            OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 
            (HANDLE) NULL)) == (HANDLE) INVALID_HANDLE_VALUE) 
        return FALSE; 
    
    // Call private function to parse the file looking for headings.
    while ( GetNextHeadingAndLevelFromFile(hf, szItemText, &nLevel) )
         // Add the item to the tree view control. 
         AddItemToTree(hwndTV, szItemText, nLevel); 
 
    return TRUE; 
	} 
 
	// AddItemToTree - adds items to a tree view control. 
	// Returns the handle to the newly added item. 
	// hwndTV - handle to the tree view control. 
	// lpszItem - text of the item to add. 
	// nLevel - level at which to add the item. 
	HTREEITEM AddItemToTree(HWND hwndTV, LPSTR lpszItem, int nLevel) 
	{ 
    TVITEM tvi; 
    TVINSERTSTRUCT tvins; 
    static HTREEITEM hPrev = (HTREEITEM) TVI_FIRST; 
    static HTREEITEM hPrevRootItem = NULL; 
    static HTREEITEM hPrevLev2Item = NULL; 
    HTREEITEM hti; 
 
    tvi.mask = TVIF_TEXT | TVIF_IMAGE 
        | TVIF_SELECTEDIMAGE | TVIF_PARAM; 
 
    // Set the text of the item. 
    tvi.pszText = lpszItem; 
    tvi.cchTextMax = lstrlen(lpszItem); 
 
    // Assume the item is not a parent item, so give it a 
    // document image. 
    tvi.iImage = g_nDocument; 
    tvi.iSelectedImage = g_nDocument; 
 
    // Save the heading level in the item's application-defined 
    // data area. 
    tvi.lParam = (LPARAM) nLevel; 
 
    tvins.item = tvi; 
    tvins.hInsertAfter = hPrev; 
 
    // Set the parent item based on the specified level. 
    if (nLevel == 1) 
        tvins.hParent = TVI_ROOT; 
    else if (nLevel == 2) 
        tvins.hParent = hPrevRootItem; 
    else 
        tvins.hParent = hPrevLev2Item; 
 
    // Add the item to the tree view control. 
    hPrev = (HTREEITEM) SendMessage(hwndTV, TVM_INSERTITEM, 0, 
         (LPARAM) (LPTVINSERTSTRUCT) &tvins); 
 
    // Save the handle to the item. 
    if (nLevel == 1) 
        hPrevRootItem = hPrev; 
    else if (nLevel == 2) 
        hPrevLev2Item = hPrev; 
 
    // The new item is a child item. Give the parent item a 
    // closed folder bitmap to indicate it now has child items. 
    if (nLevel > 1) { 
        hti = TreeView_GetParent(hwndTV, hPrev); 
        tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE; 
        tvi.hItem = hti; 
        tvi.iImage = g_nClosed; 
        tvi.iSelectedImage = g_nClosed; 
        TreeView_SetItem(hwndTV, &tvi); 
    } 
 
    return hPrev; 
	} 
 

Dragging a Tree View Item

Dragging a tree view item typically involves processing the TVN_BEGINDRAG (or TVN_BEGINRDRAG) notification message, the WM_MOUSEMOVE message, and the WM_LBUTTONUP (or WM_RBUTTONUP) message. It also involves using the image list functions to draw the item as it is being dragged.

The remainder of this section provides an example that demonstrates how to drag a tree view item. The example consists of three functions. The first function begins the drag operation, the second drags the image, and the third ends the drag operation.

Beginning the tree view drag operation

A tree view control sends the parent window a TVN_BEGINDRAG (or TVN_BEGINRDRAG) notification message whenever the user starts to drag an item. The parent window receives the notification in the form of a WM_NOTIFY message whose lParam parameter is the address of an NMTREEVIEW structure. The members of this structure include the screen coordinates of the mouse cursor and a TVITEM structure that contains information about the item to be dragged.

The following example shows how to process the WM_NOTIFY message to obtain TVN_BEGINDRAG.

	case WM_NOTIFY: 
	  switch (((LPNMHDR) lParam)->code) { 
	    case TVN_BEGINDRAG: 
	       Main_OnBeginDrag(            // application-defined function 
	            hwndTV, (LPNMTREEVIEW) lParam); 
	       break; 
	    . 
	    .  // Handle other notifications here. 
	    . 
	  } 
	  break; 
	 

Beginning the drag operation involves using the ImageList_BeginDrag function. The function's parameters include the handle to the image list containing the image to use during the drag operation and the index of the image. You can either provide your own image list and image, or you can have the tree view control create them for you by using the TVM_CREATEDRAGIMAGE message.

Because the drag image replaces the mouse cursor for the duration of the drag operation, ImageList_BeginDrag requires you to specify a hot spot within the image. The coordinates of the hot spot are relative to the upper left corner of the image. ImageList_BeginDrag also requires you to specify the initial location of the drag image. An application typically sets the initial location so that the hot spot of the drag image corresponds to that of the mouse cursor at the time the user began the drag operation.

The following function demonstrates how to begin dragging a tree view item. It uses the drag image provided by the tree view control and obtains the bounding rectangle of the item to determine the appropriate point for the hot spot. (The dimensions of the bounding rectangle are the same as those of the image.) Note that the bounding rectangle does not account for the indentation of child items. For this reason, the function adds the amount of indentation to the x-coordinate of the hot spot.

The function captures mouse input, causing mouse messages to be sent to the parent window. The parent window needs the subsequent WM_MOUSEMOVE messages to determine where to drag the image and the WM_LBUTTONUP message to determine when to end the drag operation.

	// Main_OnBeginDrag - begins dragging an item in a tree view control. 
	// hwndTV - handle to the image list. 
	// lpnmtv - address of information about the item being dragged. 
	void Main_OnBeginDrag(HWND hwndTV, LPNMTREEVIEW lpnmtv) 
	{ 
	    HIMAGELIST himl;    // handle to image list 
	    RECT rcItem;        // bounding rectangle of item 
    DWORD dwLevel;      // heading level of item 
    DWORD dwIndent;     // amount that child items are indented 
 
    // Tell the tree view control to create an image to use 
    // for dragging. 
    himl = TreeView_CreateDragImage(hwndTV, lpnmtv->itemNew.hItem); 
 
    // Get the bounding rectangle of the item being dragged. 
    TreeView_GetItemRect(hwndTV, lpnmtv->itemNew.hItem, &rcItem, TRUE); 
 
    // Get the heading level and the amount that the child items are 
    // indented. 
    dwLevel = lpnmtv->itemNew.lParam; 
    dwIndent = (DWORD) SendMessage(hwndTV, TVM_GETINDENT, 0, 0); 
 
    // Start the drag operation. 
    ImageList_BeginDrag(himl, 0, 0, 0); 
 
    // Hide the mouse cursor, and direct mouse input to the 
    // parent window. 
    ShowCursor(FALSE); 
    SetCapture(GetParent(hwndTV)); 
    g_fDragging = TRUE; 
    return; 
	} 
 

Dragging the tree view item

You drag a tree view item by calling the ImageList_DragMove function when the parent window receives a WM_MOUSEMOVE message, as the following example shows. The example also demonstrates how to perform hit testing during the drag operation to determine whether to highlight other items in the tree view as targets of a drag-and-drop operation.

	// Main_OnMouseMove - drags an item in a tree view control, 
	//     highlighting the item that is the target. 
	// hwndParent - handle to the parent window. 
	// hwndTV - handle to the tree view control. 
	// xCur and yCur - x- and y-coordinates of the mouse cursor. 
	void Main_OnMouseMove(HWND hwndParent, HWND hwndTV, LONG xCur, LONG yCur) 
	{ 
    HTREEITEM htiTarget;  // handle to target item 
    TVHITTESTINFO tvht;  // hit test information 
 
    if (g_fDragging) { 
 
        // Drag the item to the current position of the mouse cursor. 
        ImageList_DragMove(xCur, yCur); 
 
        // Find out if the cursor is on the item. If it is, highlight 
        // the item as a drop target. 
        tvht.pt.x = xCur; 
        tvht.pt.y = yCur; 
        if ((htiTarget = TreeView_HitTest(hwndTV, &tvht)) != NULL) { 
            TreeView_SelectDropTarget(hwndTV, htiTarget); 
        } 
    } 
    return; 
	} 
 

Ending the tree view drag operation

The following example shows how to end a drag operation. The ImageList_EndDrag function is called when the parent window receives a WM_LBUTTONUP message.

	// Main_OnLButtonUp - stops dragging a tree view item, releases the 
	//     mouse capture, and shows the mouse cursor. 
	//
	// Global variable 
	//     g_fDragging - indicates whether a drag operation is underway. 
	void Main_OnLButtonUp(void) 
	{ 
    if (g_fDragging) { 
        ImageList_EndDrag(); 
        ReleaseCapture(); 
        ShowCursor(TRUE); 
        g_fDragging = FALSE; 
    } 
    return; 
	} 
 

Working with state image indexes

There is often confusion about how to set and retrieve the state image index in a tree view control. The following examples demonstrate the proper method for setting and retrieving the state image index. The examples assume that there are only two state image indexes in the tree view control, unchecked and checked. If your application contains more than two, these functions will need to be modified to handle that case.

The following example function illustrates how to set an item's check state.

BOOL TreeView_SetCheckState(HWND hwndTreeView, HTREEITEM hItem, BOOL fCheck)
    {
    TVITEM tvItem;

    tvItem.mask = TVIF_HANDLE | TVIF_STATE;
    tvItem.hItem = hItem;
    tvItem.stateMask = TVIS_STATEIMAGEMASK;

    /*
    Since state images are one-based, 1 in this macro turns the check off, and 
    2 turns it on.
    */
    tvItem.state = INDEXTOSTATEIMAGEMASK((fCheck ? 2 : 1));

    return TreeView_SetItem(hwndTreeView, &tvItem);
    }

The following example function illustrates how to retrieve an item's check state.

BOOL TreeView_GetCheckState(HWND hwndTreeView, HTREEITEM hItem)
    {
    TVITEM tvItem;

    // Prepare to receive the desired information.
    tvItem.mask = TVIF_HANDLE | TVIF_STATE;
    tvItem.hItem = hItem;
    tvItem.stateMask = TVIS_STATEIMAGEMASK;

    // Request the information.
    TreeView_GetItem(hwndTreeView, &tvItem);

    // Return zero if it's not checked, or nonzero otherwise.
    return ((BOOL)(tvItem.state >> 12) -1);
}

© 1997 Microsoft Corporation. All rights reserved. Terms of Use.