Windows with C++

Windows Vista Control Enhancements

Kenny Kerr

Code download available at: Windows With C++2007_08.exe(165 KB)

Contents

Button Controls
Edit and Combobox Controls
Tree View Controls
Header and List View Controls
Wrapping Up

Welcome to the first installment of Windows with C++. My focus will be on Windows Vista™ and Windows Server® 2008 development using native C++. This column will showcase new and exciting features introduced in the latest version of Windows®.

There are two main reasons why this column was born. First, despite the phenomenal success of the Microsoft® .NET Framework, there are still plenty of scenarios where it makes more sense to use native C++. Adoption of .NET will continue to grow, but C++ is here to stay. Second, with the launch of a major new operating system it is unreasonable to expect that all the various application frameworks—from MFC and the Active Template Library (ATL) to .NET and more—will be able to wrap up all the new features and enhancements in the short term. C++ remains the only language that provides unfettered access to every corner of the Windows SDK, and there certainly are many new corners to explore in Windows Vista and beyond! For more info about the future of .NET, see the "What About .NET?" sidebar.

To start off this series, I’ll be writing about controls. Windows Vista doesn’t introduce many new controls (though it does provide the new Network Address Control, which allows input and validation of IPv4, IPv6, and DNS names), but it offers a number of enhancements and new features to many of the existing standard and common controls. This is good news in a way because it means you can take advantage of many of these new features in your applications with very little effort. In this column, I am going to take you on a whirlwind tour through some of the most commonly used controls and look at various new and exciting features that are provided by Windows Vista and beyond. The download for this column, available from the MSDN® Magazine Web site, provides examples of most of the control features described here.

Button Controls

The button control is responsible for a few of the seemingly new controls in Windows Vista. New style flags introduce noticeably different appearances compared to traditional buttons. Figure 1 illustrates some of the new button styles.

Figure 1 New Windows Vista Button Styles

Figure 1** New Windows Vista Button Styles **

The BS_SPLITBUTTON style creates a split button. The button’s parent window receives the BCN_DROPDOWN notification message indicating that the user clicked the dropdown arrow on the button. You are then responsible for displaying a shortcut menu. Your dropdown handler might be implemented as follows:

CRect rectangle; VERIFY(m_splitButton.GetWindowRect( &rectangle)); TPMPARAMS params = { sizeof(TPMPARAMS) }; params.rcExclude = rectangle; CMenuHandle menu = m_menu.GetSubMenu(0); VERIFY(menu.TrackPopupMenuEx(TPM_LEFTBUTTON, rectangle.left, rectangle.bottom, m_hWnd, &params));

This snippet first calculates the bounds of the button and then populates a TPMPARAMS structure to inform TrackPopupMenuEx that, if the menu cannot be displayed in the desired location, it should not overlap the button but instead be displayed on the next available edge. You can also use the BCM_GETSPLITINFO and BCM_SETSPLITINFO messages to query and adjust the appearance of the dropdown arrow, but in most cases the defaults should serve you well.

The BS_COMMANDLINK style creates a command link. Command links feature an arrow next to the button’s caption as well as an optional note displayed in a smaller font. The button’s caption is set as usual with the SetWindowText function, but the new BCM_SETNOTE message sets the note appearing underneath the caption.

Finally, you can instruct Windows to display the now famous "elevation required" shield on a button by sending it the BCM_SETSHIELD message with the LPARAM set to TRUE to include the icon and FALSE to remove it. Here’s a simple example:

button.SendMessage(BCM_SETSHIELD, 0, required);

Edit and Combobox Controls

Windows XP introduced the ability to display a textual cue on edit controls with the EM_SETCUEBANNER message. This cue prompts the user for information that disappeared when the control got the focus. Windows Vista extends this with a subtle but valuable addition, allowing you to show the cue text even when the control has the focus. This seemingly small improvement removes the necessity for static controls in many cases. Simply use the previously unused WPARAM and set it to TRUE to take advantage of this feature. The Edit_SetCueBannerTextFocused macro is provided for simplicity:

VERIFY(Edit_SetCueBannerTextFocused(m_edit, L”Cue text”, TRUE));

Windows Vista also brings this feature to comboboxes in much the same way with the CB_SETCUEBANNER message. It does not, however, provide the option of showing the cue text when the control has the focus and only the "Drop List" style comboboxes continue to show the cue text until a selection is made, as illustrated in Figure 2.

Figure 2 Combo Box Sample

Figure 2** Combo Box Sample **

The ComboBox_SetCueBannerText macro is provided for simplicity and convenience:

VERIFY(ComboBox_SetCueBannerText(m_comboBox, L”Cue text”));

Tree View Controls

The Windows developers finally bit the bullet and introduced extended styles for the tree view control. Figure 3 illustrates some of the new tree view control styles and themes in action.

Figure 3 Tree View

Figure 3** Tree View **

The TVM_GETEXTENDEDSTYLE and TVM_SETEXTENDEDSTYLE messages are provided for getting and setting the bitmask of extended styles. The TreeView_GetExtendedStyle macro wraps the TVM_GETEXTENDEDSTYLE message and can be used as follows:

DWORD style = TreeView_GetExtendedStyle(m_treeView);

Usually you won’t need to get the extended styles, however, since the TVM_SETEXTENDEDSTYLE message allows you to update a subset of style flags using a bitmask. For example, you can add the TVS_EX_AUTOHSCROLL extended style while at the same time removing the TVS_EX_MULTISELECT extended style by using the TreeView_SetExtendedStyle macro that wraps the TVM_SETEXTENDEDSTYLE message as follows:

TreeView_SetExtendedStyle(m_treeView, TVS_EX_AUTOHSCROLL, TVS_EX_AUTOHSCROLL | TVS_EX_MULTISELECT);

Undoubtedly the most exciting addition to the tree view control is support for multiple selections. Finally, developers can ditch all that code to fake multiple selections and simply use the TVS_EX_MULTISELECT extended style and let the system handle the rest. Of course, you will want to enumerate the selected items. This is possible with the introduction of a new flag for the TVM_GETNEXTITEM message. Start by getting the currently selected item as before with the TVGN_CARET flag and then use the TVGN_NEXTSELECTED flag to get remaining selected items. Here is a simple example using the Windows Template Library CTreeViewCtrlEx and CTreeItem classes:

for (CTreeItem item = m_treeView.GetSelectedItem(); 0 != item; item = item.GetNext(TVGN_NEXTSELECTED)) { CString text; item.GetText(text); TRACE(L”%s\n”, text); }

CTreeViewCtrlEx’s GetSelectedItem method sends the TVM_GETNEXTITEM message with the TVGN_CARET flag to get the first selected item and CTreeItem’s GetNext method sends the TVM_GETNEXTITEM message again with the specified flag and the tree item’s handle to provide message context.

If all you need to know is the number of selected items, then the TVM_GETSELECTEDCOUNT message will do the trick. The associated TreeView_GetSelectedCount macro wraps the message and is used as follows:

DWORD count = TreeView_GetSelectedCount(m_treeView);

There are a few more extended styles that are worth checking out. The TVS_EX_DOUBLEBUFFER extended style tells the control to paint via double-buffering. This avoids the flicker when the control is resized. The TVS_EX_AUTOHSCROLL extended style allows the control to automatically scroll a selected tree item into view. This is often combined with the TVS_NOHSCROLL style, which hides the horizontal scroll bar. Here is a simple example:

DWORD treeViewStyle = m_treeView.GetStyle(); treeViewStyle |= TVS_NOHSCROLL; m_treeView.SetWindowLong(GWL_STYLE, treeViewStyle); TreeView_SetExtendedStyle(m_treeView, TVS_EX_AUTOHSCROLL, TVS_EX_AUTOHSCROLL);

Although the default behavior is usually sufficient, you can control the auto-scroll animation characteristics using the TVM_SETAUTOSCROLLINFO message.

You may also have noticed that Windows Explorer on Windows Vista now sports new fading "expando" buttons with a new arrow bitmap in place of the older plus/minus boxes. To switch to arrows, you need to use the SetWindowTheme function to instruct Windows to use the visual styles consistent with Windows Explorer for the specified control:

COM_VERIFY(::SetWindowTheme(m_treeView, L”explorer”, 0));

Request the fade effect by using the TVS_EX_FADEINOUTEXPANDOS extended style.

Header and List View Controls

Although relatively few developers use the header control directly, virtually all Windows developers are familiar with the list view control, which relies heavily on the header control for many of its features. Both header and list view controls have received many enhancements in Windows Vista. In this section, I’ll focus on those features from the perspective of list view controls since that’s the control most developers are familiar with. Many of the features can, however, be employed without a list view control and some require direct interaction with the header control.

The list view control in Windows Vista has received a major upgrade with many new features, styles, and improvements. Unlike the tree view control, extended styles have been available for quite some time for the list view control. Windows Vista does, however, define a handful of new extended styles that enable significantly richer user interactions.

The LVS_EX_AUTOSIZECOLUMNS extended style automatically resizes the view’s columns as one column is being resized to best fit the various columns into view. This is a nice touch that users will appreciate and you can add it with no trouble at all.

The LVS_EX_AUTOCHECKSELECT style provides the kind of Select All checkbox common in tabular controls in Web applications. This corresponds to the new HDS_CHECKBOXES header control style. A checkbox is placed in the column header above the item checkboxes. Clicking the checkbox selects or deselects all the list view item checkboxes at once. Figure 4 provides an example of what this looks like.

Figure 4 List View Select All Control

Figure 4** List View Select All Control **

As with the tree view control, to get that Windows Explorer theme for your list view all you need to do is call the SetWindowTheme function to instruct Windows to use the visual styles consistent with Windows Explorer.

The LVS_EX_COLUMNSNAPPOINTS extended style works in conjunction with additions to the LVCOLUMN structure and snaps a particular column to a minimum width when the user resizes it. This behavior is also provided when using the LVS_EX_AUTOSIZECOLUMNS extended style. To take advantage of this, add either extended style and prepare the LVCOLUMN structure appropriately. For example, the following example inserts a column with an initial width of 200 pixels and a minimum width of 100 pixels:

LVCOLUMN column = { 0 }; column.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_MINWIDTH; column.pszText = L”Name”; column.cx = 200; // initial width column.cxMin = 100; // minimum width m_listView.InsertColumn(0, &column);

The new LVCF_MINWIDTH mask is used to indicate that the cxMin field is populated and represents the minimum width for the column.

Another feature in Windows Vista that you may have noticed is that Windows Explorer shows the column header in all views, not just in details view mode. By default the header control is only visible in the details view mode. Simply use the LVS_EX_HEADERINALLVIEWS extended style to show the column headers in all the view modes. Although it takes a bit of getting used to on the user’s part, it is quite a handy feature since it allows the user to sort the view in different view modes.

You may also have noticed that when in a different view mode, such as the tiles view mode, the columns in Windows Explorer cannot be resized and a horizontal scroll bar is not used to scroll any overflowing column headers into view. Instead, an overflow button is presented in the header control. Clicking the button presents a pop-up menu with a list of the header columns that are not visible and thus overflowed. There are a few steps involved in reproducing this behavior.

First, you need to set the view mode to something other than details, and you need to use the LVS_EX_HEADERINALLVIEWS extended style to show the column headers. Second, you need to add the HDS_NOSIZING and HDS_OVERFLOW styles to the list view control’s header control as follows:

CHeaderCtrl header = m_listView.GetHeader(); DWORD headerStyle = header.GetStyle(); headerStyle |= HDS_NOSIZING | HDS_OVERFLOW; header.SetWindowLong(GWL_STYLE, headerStyle);

The HDS_NOSIZING style disables resizing of all columns, and the HDS_OVERFLOW style displays an overflow button as needed. Finally, you need to handle the HDN_OVERFLOWCLICK header control notification message. It is here that you may display a pop-up window or menu of some kind to present the overflowed columns to the user. You may want to use the TrackPopupMenu function to accomplish this. The iItem member of the NMHEADER structure provided through the notification message’s LPARAM gives you the index of the first column that overflowed.

Another new feature provided by the header control is the split button. The column header appears much like the split buttons described in the section on button controls with a dropdown button appearing when the mouse is over the column header. Use the LVCFMT_SPLITBUTTON format flag when preparing the LVCOLUMN structure for a new column. This corresponds to the HDF_SPLITBUTTON format flag used with the HDITEM structure when working directly with a header control. The LVN_COLUMNDROPDOWN notification message is sent by the list view control when the dropdown buttons is pressed. Again, this corresponds to the HDN_DROPDOWN notification message if you’re working directly with a header control. The LVN_COLUMNDROPDOWN message’s LPARAM is a pointer to a NMLISTVIEW structure and the iSubItem field indicates the index of the column whose dropdown was clicked.

Another area of the list view control that received an upgrade in Windows Vista is the grouping features. Figure 5 illustrates some of the new labels that can be applied to a group. The LVGROUP structure used to define list view groups has more than doubled in size.

Figure 5 List View Grouping

Figure 5** List View Grouping **

The group illustrated in Figure 5 can be created as follows:

LVGROUP group = { sizeof(LVGROUP) }; group.mask = LVGF_GROUPID | LVGF_HEADER | LVGF_SUBTITLE | LVGF_TASK | LVGF_FOOTER | LVGF_STATE | LVGF_ALIGN; group.iGroupId = 1; group.pszHeader = L”Header”; group.pszSubtitle = L”Subtitle”; group.pszTask = L”Task”; group.pszFooter = L”Footer”; group.state = LVGS_COLLAPSIBLE; group.uAlign = LVGA_FOOTER_RIGHT | LVGA_HEADER_CENTER; m_listView.InsertGroup(-1, &group);

What about .NET?

Future versions of the .NET Framework will undoubtedly wrap up many of the features I’ve described in this column. You don’t, however, need to wait until that happens to take advantage of these features in your .NET Framework-based applications as Windows Forms provides the necessary hooks in most cases to extend them with new functionality.

Many of the features I described require sending a control a particular message. Messages are typically sent using the SendMessage function exported by the user32.dll system library and you can import it into your managed application as follows:

[DllImport("user32.dll")] static extern int SendMessage(IntPtr window, int message, IntPtr wParam, IntPtr lParam);

As an example, the section on tree view controls in this column described how to enable multiple selections. You can enable this same thing given a Windows Forms TreeView class using the following code:

int TVM_SETEXTENDEDSTYLE = 4396; int TVS_EX_MULTISELECT = 2; SendMessage(m_treeView.Handle, TVM_SETEXTENDEDSTYLE, new IntPtr(TVS_EX_MULTISELECT), new IntPtr(TVS_EX_MULTISELECT));

Here’s another example illustrating how to handle notification messages. The Windows Forms Control class provides the protected WndProc method that allows you to process specific messages as needed. Many of the messages sent by controls to inform the control’s owner of various events are sent in the form of a WM_NOTIFY message where the LPARAM is a pointer to a NMHDR structure. The NMHDR structure’s code member identifies the specific notification message being sent. Here is how you can handle a notification message in managed code:

struct NMHDR { public IntPtr hwndFrom; public IntPtr idFrom; public int code; } const int WM_NOTIFY = 0x004E; protected override void WndProc(ref Message message) { bool handled = false; if (WM_NOTIFY == message.Msg) { NMHDR header = (NMHDR)Marshal.PtrToStructure(message.LParam, typeof(NMHDR)); if (<someCode> == header.code) { // TODO: handle notification here handled = true; } } if (!handled) { base.WndProc(ref message); } }

The LVGS_COLLAPSIBLE state flag allows the group to be collapsed and expanded. Collapsing a group hides the list view items that belong to the group. The LVGS_COLLAPSED state flag can be used to collapse a group programmatically. The LVGA_ flags can be used to override the default alignment for the group labels. The header and footer text are usually left-aligned; the subtitle always appears under the heading. The task link is always right-aligned. The LVN_LINKCLICK notification message is sent when the user clicks the task link. The group that the task link belongs to is identified by the iSubItem field of the NMLVLINK pointer provided as the message’s LPARAM.

Finally, you may optionally respond to the LVN_GETEMPTYMARKUP notification message to provide some text to display when the list view control has no items. You may use the anchor tag to create links within the text. Here is an example of a function that handles the notification message:

LRESULT OnGetEmptyMarkup(LPNMHDR notifyData) { NMLVEMPTYMARKUP* markupInfo = reinterpret_cast<NMLVEMPTYMARKUP*>(notifyData); markupInfo->dwFlags = EMF_CENTERED; wcscpy_s(markupInfo->szMarkup, _countof(markupInfo->szMarkup), L”Link <A>one</A> and <A>two</A>.”); return TRUE; // set the markup }

By default the message is displayed in the top-left corner of the list view. The EMF_CENTERED flag centers it both horizontally and vertically. The list view reports a user clicking a link in the same way as group task links. A given LVN_LINKCLICK notification message handler can, however, be shared. In the case of an empty markup link, the iItem and iSubItem members of the NMLVLINK structure will be set to -1, the nested LITEM structure will be populated with the link identifier set to EmptyMarkup, and its iLink member set to the index of the link within the text.

Wrapping Up

There are many more small features and enhancements that you will discover as you explore the Windows Vista controls with the help of the Windows SDK. Many scenarios that previously required owner or custom drawing are now provided out of the box. Additionally, upcoming versions of Windows Template Library and MFC feature many improvements that allow you to more easily take advantage of the Windows Vista control improvements. For more information, see "MFC Updates for Vista Common Controls".

Send questions and comments for Kenny to mmwincpp@microsoft.com mmwincpp@microsoft.com.

Kenny Kerr is a software craftsman specializing in software development for Windows. He has a passion for writing and teaching developers about programming and software design. You can reach Kenny at weblogs.asp.net/kennykerr.