Tree-View Controls
A tree-view control is a window that displays a hierarchical list of items, such as the headings in a document, the entries in an index, or the files and directories on a disk. Each item consists of a label and an optional bitmapped image, and each item can have a list of subitems associated with it. By clicking an item, the user can expand or collapse the associated list of subitems.
The following topics are discussed.
- About Tree-View Controls
- Using Tree-View Controls
- Tree-View Control Updates in Internet Explorer
- Related Topics
About Tree-View Controls
You create a tree-view control by using the
After creating a tree-view control, you add, remove, arrange, or otherwise manipulate items by sending messages to the control. Each message has one or more corresponding macros that you can use instead of sending the message explicitly.
Tree-View Styles
Tree-view styles govern aspects of a tree-view control's appearance. You set the initial styles when you create the tree-view control. You can retrieve and change the styles after creating the tree-view control by using the
The
The user can expand or collapse a parent item's list of child items by double-clicking the parent item. A tree-view control that has the
The
The
The
Parent and Child Items
Any item in a tree-view control can have a list of subitems—called child items—associated with it. An item that has one or more child items is called a parent item. A child item is displayed below its parent item and is indented to indicate that it is subordinate to the parent. An item that has no parent appears at the top of the hierarchy and is called a root item.
To add an item to a tree-view control, send the
At any given time, the state of a parent item's list of child items can be either expanded or collapsed. When the state is expanded, the child items are displayed below the parent item. When it is collapsed, the child items are not displayed. The list automatically toggles between the expanded and collapsed states when the user double-clicks the parent item or, if the parent has the TVS_HASBUTTONS style, when the user clicks the button associated with the parent item. An application can expand or collapse the child items by using the
A tree-view control sends the parent window a
When a list of child items is expanded, it is indented relative to the parent item. You can set the amount of indentation by using the
A tree-view control uses memory allocated from the heap of the process that creates the tree-view control. The maximum number of items in a tree view is based on the amount of memory available in the heap.
Item Labels
You typically specify the text of an item's label when adding the item to the tree-view control. The
A tree-view control allocates memory for storing each item; the text of the item labels takes up a significant portion of this memory. If your application maintains a copy of the strings in the tree-view control, you can decrease the memory requirements of the control by specifying the LPSTR_TEXTCALLBACK value in the pszText member of TVITEM instead of passing actual strings to the tree view. Using LPSTR_TEXTCALLBACK causes the tree-view control to retrieve the text of an item's label from the parent window whenever the item needs to be redrawn. To retrieve the text, the tree-view control sends a
Tree-View Label Editing
The user can directly edit the labels of items in a tree-view control that has the
When label editing begins, a tree-view control sends its parent window a
When label editing is canceled or completed, a tree-view control sends its parent window a
During label editing, typically in response to the TVN_BEGINLABELEDIT notification message, you can retrieve the handle to the edit control used for label editing by using the
Tree-View Item Position
An item's initial position is set when the item is added to the tree-view control by using the
When TVI_FIRST or TVI_LAST is specified, the tree-view control places the new item at the beginning or end of the given parent item's list of child items. When TVI_SORT is specified, the tree-view control inserts the new item into the list of child items in alphabetical order based on the text of the item labels.
You can put a parent item's list of child items in alphabetical order by using the
The
Tree-View Item States Overview
Each item in a tree-view control has a current state. The state information for each item includes a set of bit flags as well as image list indexes that indicate the item's state image and overlay image. The bit flags indicate whether the item is selected, disabled, expanded, and so on. For the most part, a tree-view control automatically sets an item's state to reflect user actions, such as selection of an item. However, you can also set an item's state by using the
An item's current state is specified by the state member of the
When you specify or change an item's state, the statemask member of TVITEM specifies which state bits to set, and the state member contains the new values for those bits.
To set an item's overlay image, statemask must include the TVIS_OVERLAYMASK value, and state must include the one-based index of the overlay image shifted left 8 bits by using the
A state image is displayed next to an item's icon to indicate an application-defined state. State images are contained in a state image list that is specified by sending a
To set the state image index, use
Item Selection
A tree-view control notifies the parent window when the selection changes from one item to another by sending the
An application can change the selection by sending the
Item Information
Tree-view controls support a number of messages that retrieve information about items in the control.
The
The
The
Tree-View Image Lists
Each item in a tree-view control can have four bitmapped images associated with it.
- An image, such as an open folder, displayed when the item is selected.
- An image, such as a closed folder, displayed when the item is not selected.
- An overlay image that is drawn transparently over the selected or nonselected image.
- A state image, which is an additional image displayed to the left of the selected or nonselected image. You can use state images, such as checked and cleared check boxes, to indicate application-defined item states.
By default, a tree-view control does not display item images. To display item images, you must create image lists and associate them with the control. For more information about image lists, see
A tree-view control can have two image lists: a normal image list and a state image list. A normal image list stores the selected, nonselected, and overlay images. A state image list stores state images. Use the
In addition to the selected and nonselected images, a tree-view control's normal image list can contain up to four overlay images. Overlay images are identified by a one-based index and are designed to be drawn transparently over the selected and nonselected images. To assign an overlay mask index to an image in the normal image list, call the
By default, all items display the first image in the normal image list for both the selected and nonselected states. Also, by default, items do not display overlay images or state images. You can change these default behaviors for an item by sending the
To specify an item's selected and nonselected images, set the TVIF_SELECTEDIMAGE and TVIF_IMAGE bits in the mask member of the TVITEM structure and specify indexes from the control's normal image list in the iSelectImage and iImage members. Alternatively, you can specify the I_IMAGECALLBACK value in iSelectImage and iImage instead of specifying indexes. This causes the control to query its parent window for an image list index each time the item is about to be redrawn. The control sends the
To associate an overlay image with an item, use the
State images are stored in a separate state image list and identified by their index. To specify the state image list, send a TVM_SETIMAGELIST message. Unlike the
Drag-and-Drop Operations
A tree-view control notifies the parent window when the user starts to drag an item. The parent window receives a
You obtain an image to display during a drag operation by using the
You must provide the code that actually drags the item. This typically involves using the dragging capabilities of the image list functions and processing the
If items in a tree-view control are to be the targets of drag-and-drop operations, you need to know when the mouse pointer is on a target item. You can find out by using the
You can indicate that an item is the target of a drag-and-drop operation by using the
About Tree-View Control Notification Messages
A tree-view control sends the following notification messages to its parent window in the form of
Notification | Description |
---|---|
Signals the start of a drag-and-drop operation. | |
Signals the start of in-place label editing. | |
Signals that the right mouse button has started a drag-and-drop operation. | |
Signals the deletion of a specific item. | |
Signals the end of label editing. | |
Requests information that the tree-view control requires to display an item. | |
Signals that a parent item's list of child items was expanded or collapsed. | |
Signals that a parent item's list of child items is about to be expanded or collapsed. | |
Signals a keyboard event. | |
Signals that the selection has changed from one item to another. | |
Signals that the selection is about to be changed from one item to another. | |
Notifies a parent window that it must update the information it maintains for an item. |
Default Tree-View Control Message Processing
This section describes the window message processing performed by a tree-view control. Messages specific to tree-view controls are discussed in other sections of this document, so they are not included here.
Message | Processing performed |
---|---|
Processes the |
|
Allocates memory and initializes internal data structures. It returns zero if successful, or -1 otherwise. | |
Frees all system resources associated with the control. It returns zero. | |
Enables or disables the control. | |
Erases the window background using the current background color for the tree-view control. It returns TRUE. | |
Returns a combination of the DLGC_WANTARROWS and DLGC_WANTCHARS values. | |
Returns the handle to the current label font. | |
Scrolls the tree-view control. It returns TRUE if scrolling occurs, or FALSE otherwise. | |
Sends the |
|
Repaints the focused item, if any, and sends an |
|
Cancels label editing and, if an item was double-clicked, sends the |
|
Toggles the expanded state if the user clicked the button associated with a parent item. If the user clicked an item label, the tree-view control selects and sets the focus to the item. If the user moves the mouse before releasing the mouse button, the tree-view control begins a drag-and-drop operation. There is no return value. | |
Paints the invalid region of the tree-view control. It returns zero. If the wParam parameter is non-NULL, the control assumes that the value is a handle to a device context (HDC) and paints using that device context. | |
Checks to see if an item was clicked and a drag operation was begun. If the operation has begun, it sends a |
|
Repaints the focused item, if any, and sends an |
|
Saves the specified font handle and repaints the tree-view control using the new font. | |
Sets or clears the redraw flag. The tree-view control is redrawn after the redraw flag is set. It returns zero. | |
Recomputes internal variables that depend on the size of the tree-view control's client area. It returns TRUE. | |
Cancels label editing and redraws the tree-view control using the new styles. It returns zero. | |
Redraws the tree-view control using the new color if the redraw flag is set. There is no return value. | |
Begins editing an item label. If the user clicks the label of the focused item, the tree-view control sets a timer instead of entering edit mode immediately. The timer makes it possible for the tree view to avoid entering edit mode if the user double-clicks the label. It returns zero. | |
Scrolls the tree-view control. It returns TRUE if scrolling occurs, or FALSE otherwise. |
Using Tree-View Controls
This section contains examples that demonstrate the following tasks.
- Creating a Tree-View Control
- Initializing the Image List
- Adding Tree-View Items
- Dragging a Tree-View Item
Creating a Tree-View Control
To create a tree-view control, use the
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.
// 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 Lists functions to create an image list and add images to it. Then associate the image list with the tree-view control by using the
The following example creates an image list, adds three bitmaps to the list, and associates the image list with a tree-view control.
// 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
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.
// 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 = sizeof(tvi.pszText)/sizeof(tvi.pszText[0]);
// 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
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
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 is an application-defined function Main_OnBeginDrag(hwndTV, (LPNMTREEVIEW)lParam); break; // Handle other cases here. } break;
Beginning the drag operation involves using the
Because the drag image replaces the mouse pointer for the duration of 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
// 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);
ImageList_DragEnter(hwndTV, 50, 50);
// Hide the mouse pointer, 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
// 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 pointer.
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 pointer.
ImageList_DragMove(xCur, yCur);
// Find out if the pointer 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
// mouse capture, and shows the mouse pointer.
//
// 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.
{
TVITEM tvItem;
tvItem.mask = TVIF_HANDLE | TVIF_STATE;
tvItem.hItem = hItem;
tvItem.stateMask = TVIS_STATEIMAGEMASK;
// Image 1 in the tree-view check box image list is the
// unchecked box. Image 2 is the checked box.
tvItem.state = INDEXTOSTATEIMAGEMASK((fCheck ? 2 : 1));
return TreeView_SetItem(hwndTreeView, &tvItem);
}
The following example function illustrates how to retrieve an item's check state.
{
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);
}