Responding to Tree Control Notification Messages

In our last article, we initialized and handled the Walk Registry Tree Page. Once you've added all of the items, the tree control manages most of the visual work of collapsing and expanding the tree control. However, there are some parts of the tree control's behavior over which we want to exert control. First, we need to change the appearance of the icon next to the tree item when its selection state changes. We also must add a newly selected key's values (or a message saying it has no values) to the list box control.

In order to do this, we need to:

  • Detect and handle tree control notification when the selected item changes.
  • Detect and handle tree control notification when the tree item is expanding.
  • Retrieve the handle to the opened registry key from the item's lParam value.
  • Enumerate the key values.
  • Interpret and display the key values in the list control.

Here's our plan of attack:

We created message handlers in CWalkReg to process the notification message from the tree control. Each of these functions was added to CWalkReg using the ClassWizard, which generated the message map entry shown below:

// Implementation
   // Generated message map functions
   afx_msg void OnItemexpandingRegTree(NMHDR* pNMHDR,
                                       LRESULT* pResult);
   afx_msg void OnSelchangedRegTree(NMHDR* pNMHDR, 
                                    LRESULT* pResult);


We'll get the handle to the Registry keys for which we want to enumerate values from the lParam of the selected item's TVITEM structure. (Recall that we stored the corresponding opened key's handle in each item when we initialized the tree control.)

We'll retrieve the keys' values by looping through them with the RAPI function CeRegEnumValue().

Finally, we'll display the values in the list control, using the CWalkReg member InterpretKeyValue (). This member detects the Registry-defined data type of the value, writes it to a properly formatted string, and inserts it in the list control.

Handling Item Expansion

Let's start with a look at our simplest tree control notification handler, OnItemexpandingRegTree(). You'll notice that like ordinary Windows messages handling functions, notification handlers have a standard form. Here are the first two lines of our TVN_ITEMEXPANDING notification handler:

void CWalkReg::OnItemexpandingRegTree(NMHDR* pNMHDR,
                                      LRESULT* pResult)


The parameters, in the order shown, are a pointer to a notification message header structure, and a pointer to a variable through which this function can return a result. Notifications extend the windows messaging architecture by allowing controls to send more information than could be encapsulated in a Windows message, and by providing a standard from of communication that is consistent across various control types. To better understand this, let's look more closely at the structures used to communicate the notification.

The NMHDR typedef looks like this:

typedef struct tagNMHDR {
HWND hwndFrom;    // handle of control sending message
UINT idFrom;      // identifier of control sending message
UINT code;        // control specific notification code

Examining the definition shown above, it is apparent that the NMHDR structure explicitly provides the handler function with both the window handle and the ID of the control sending the notification. Note this subtlety: The code member may be used to deliver either a code or a pointer to additional data.

In our example, we handle the TVN_ITEMEXPANDING notification because it is sent after the user has opened an item in the tree control. When this happens, we need to toggle the icon image. If it was the "selected" icon, we'll set the "unselected" icon, and vice versa.

To change the icon, we need a pointer to the tree view control object. We get this pointer by casting the NMHDR structure pointer to an NM_TREEVIEW* and assigning the pointer to pNMTreeView.

void CTreeWalker::OnSelchanged(NMHDR* pNMHDR, LRESULT* pResult)

Here's where the notification message magic comes in. At the least, all NOTIFY messages have a populated NMHDR structure, but many control notifications are actually a larger more complex structure that includes NMHDR as the first member of the control specific notification message. Here's the typedef of the NM_TREEVIEW notification structure:

typedef struct tagNMTREEVIEW {
        NMHDR     hdr;         //the NMHDR structure for this
        UINT      action;      //code for action taken
        TVITEM    itemOld;     //item losing selection
        TVITEM    itemNew;     //item receiving selection
        POINT     ptDrag;      //point where user started dragging

For the purposes at hand, what is important about the NM_TREEVIEW structure is that it contains a fully populated TVITEM structure for the item receiving selection. To manipulate the item icon, we get the item's handle from itemNew:

// get key handle from tree item
TVITEM tvitem = (TVITEM)pNMTreeView->itemNew;

We set the new image with the CTreeCtrl member SetItemImage(). The parameters to SetItemImage(), in the order shown, are the handle to the tree control item, the zero based image list index for the new icon, and the index of the item's image when the item is selected.

   if(tvitem.iImage == 0 )
       m_RegTree.SetItemImage( tvitem.hItem, 1, 0 );
       m_RegTree.SetItemImage( tvitem.hItem, 0, 0 );

   *pResult = 0;

Responding to Tree Control Notification Messages

Handling A Change of Item Selection

We chose to handle the TVN_SELCHANGED notification because it is sent after the user has selected a new Registry key from the tree control pane. At the least, this means that we have to clear the list control, because we only display values for one key at a time. If the newly selected Registry key has values, we also have to update the list view with the key's values.

The first step is to get a pointer to the tree view control object. We get a pointer to the treeview by casting the NMHDR structure pointer to an NM_TREEVIEW* and assigning the pointer to pNMTreeView.

void CWalkReg::OnSelchangedRegTree(NMHDR* pNMHDR, LRESULT* pResult)

Recall that when the item was created, we stored the handle of the registry key it represents in the lParam member of TVITEM. To access the key, we get its handle from itemNew.lParam:

// get key handle from tree item
HKEY hRegKey = (HKEY)pNMTreeView->itemNew.lParam;

We clear the list control with the ClistBox member ResetContent().

// clear list control

Now, we'll use the handle to the Registry key to see if the key has any values. We use the function CeRegEnumValue() to do this. The parameters, in the order shown, are the handle to a Registry key, an index that indicates which value to return, the address of a Unicode string buffer in which to return the name of the key, the size of the name buffer in Unicode characters, a NULL placeholder, the address of a DWORD variable in which to return the registry data type for this value, the address of a buffer in which to return the key data, and the size of the data buffer in bytes.

//does this key have values?
int iValueIndex  = 0;
WCHAR wszValueName[MAX_PATH];
BYTE  bValueData[1024];
DWORD dwValueNameSize = dim(wszValueName);
DWORD dwValueDataSize = dim(bValueData);
DWORD dwRegDataType;

memset( bValueData, 0x0, sizeof(bValueData));
LONG rc = CeRegEnumValue (hRegKey, iValueIndex,
                          wszValueName, &dwValueNameSize,
                          NULL, &dwRegDataType,
                          (PBYTE)&bValueData, &dwValueDataSize);

If CeRegEnumValue() returns without finding a value, we insert a message in the list control and return from OnSelchangedRegTree().

if( rc != ERROR_SUCCESS )
   pWalkRegPage->m_RegList.InsertString( -1, "No values exist for
                                              this key");
   *pResult = 0;

If there are key values found, we pass the value name, data, data size, and Registry data type to the CWalkReg member InterpretKeyValue(). This function formats the key data according to its Registry data type and inserts it in the list. (We'll explore Registry data types exhaustively when we examine InterpretKeyValue().)

Notice that before making the next call to CeRegEnumValue(), increment the value index, reinitialize dwValueNameSize and dwValueDataSize, and initialize the value data buffer. These steps are critical to iterating through the values. The index parameter moves the scan through the key's list of values. The name size and data size parameters are used in two ways: When passed in to the function, they establish the size of the buffers. When passed back, they report the size of the data that was actually copied to the buffers.

We continue to loop, testing for ERROR_SUCCESS. When the test fails, there are no more keys.

   while (rc == ERROR_SUCCESS) 
      InterpretKeyValue(  wszValueName, (PBYTE)&bValueData,
                          dwValueDataSize, dwRegDataType);

      dwValueNameSize = dim(wszValueName);
      dwValueDataSize = dim(bValueData);
      memset( bValueData, 0x0, sizeof(bValueData));

      rc = CeRegEnumValue (hRegKey, iValueIndex,
                           wszValueName, &dwValueNameSize,
                           NULL, &dwRegDataType,
                           (PBYTE)&bValueData, &dwValueDataSize);

   *pResult = 0;

Looking Ahead

Notification messages are useful because they allow great flexibility and sophistication in communication between objects, while retaining the standard format of windows messaging. In the next installment, we'll see how to insert values in the tree control.

About the Author

Nancy Nicolaisen

Nancy Nicolaisen is a software engineer who has designed and implemented highly modular Windows CE products that include features such as full remote diagnostics, CE-side data compression, dynamically constructed user interface, automatic screen size detection, and entry time data validation. In addition to writing for and CodeGuru, she has written several books, including Making Win 32 Applications Mobile.


  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

  • Protecting business operations means shifting the priorities around availability from disaster recovery to business continuity. Enterprises are shifting their focus from recovery from a disaster to preventing the disaster in the first place. With this change in mindset, disaster recovery is no longer the first line of defense; the organizations with a smarter business continuity practice are less impacted when disasters strike. This SmartSelect will provide insight to help guide your enterprise toward better …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds