The Common Controls
A prime Windows CE target niche—small personal productivity devices—has driven the requirements for the common controls in Windows CE. The frequent need for time and date references for schedule and task management applications has led to inclusion of the date and time picker control and the month calendar control. The small screens of personal productivity devices inspired the space-saving command bar. Mating the command bar with the rebar control that was created for Internet Explorer 3.0 has produced the command bands control. The command bands control provides even more room for menus, buttons, and other controls across the top of a Windows CE application.Finally, Pocket PC developers are familiar with the menu bar. This control was added to the Windows CE common controls in Windows CE .NET 4.2. I'll cover the menu bar after I discuss the command bar and command bands controls.
The Command Bar
Briefly, a command bar control combines a menu and a toolbar. This combination is valuable because the combination of a menu and toolbar on one line saves screen real estate on space-constrained Windows CE displays. To the programmer, the command bar looks like a toolbar with a number of helper functions that make programming the command bar a breeze. In addition to the command bar functions, you can also use most toolbar messages when you're working with command bars. A window with a command bar is shown in Figure 5-1.

Figure 5-1: A window with a command bar control
Creating a Command Bar
You build a command bar in a number of steps, each defined by a particular function. The command bar is created, the menu is added, buttons are added, other controls are added, tooltips are added, and finally, the Close and Help buttons are appended to the right side of the command bar.You begin the process of creating a command bar with a call to
HWND CommandBar_Create (HINSTANCE hInst, HWND hwndParent,
int idCmdBar);
The function requires the program's instance handle, the handle of the parent window, and an ID value for the control. If successful, the function returns the handle to the newly created command bar control. But a bare command bar isn't much use to the application. It takes a menu and a few buttons to jazz it up.
Command Bar Menus
You can add a menu to a command bar by calling one of two functions. The first function is this:
BOOL CommandBar_InsertMenubar (HWND hwndCB, HINSTANCE hInst,
WORD idMenu, int iButton);
The first two parameters of this function are the handle of the command bar and the instance handle of the application. The idMenu parameter is the resource ID of the menu to be loaded into the command bar. The last parameter is the index of the button to the immediate left of the menu. Because the Windows CE guidelines specify that the menu should be at the left end of the command bar, this parameter should be set to 0, which indicates that all the buttons are to the right of the menu.A shortcoming of the CommandBar_InsertMenubar function is that it requires the menu to be loaded from a resource. You can't configure the menu on the fly. Of course, it would be possible to load a dummy menu and manipulate the contents of the menu with the various menu functions, but here's an easier method.The function
BOOL CommandBar_InsertMenubarEx (HWND hwndCB, HINSTANCE hInst,
LPTSTR pszMenu, int iButton);
has a set of parameters similar to CommandBar_InsertMenubar with the exception of the third parameter, pszMenu. This parameter can be either the name of a menu resource or the handle to a menu previously created by the program. If the pszMenu parameter is a menu handle, the hInst parameter must be NULL.Once a menu has been loaded into a command bar, the handle to the menu can be retrieved at any time using
HMENU CommandBar_GetMenu (HWND hwndCB, int iButton);
The second parameter, iButton, is the index of the button to the immediate left of the menu. This mechanism provides the ability to identify more than one menu on the command bar. However, given the Windows CE design guidelines, you should see only one menu on the bar. With the menu handle, you can manipulate the structure of the menu using the many menu functions available.If an application modifies the menu on the command bar, the application must call
BOOL CommandBar_DrawMenuBar (HWND hwndCB, int iButton);
which forces the menu on the command bar to be redrawn. Here again, the parameters are the handle to the command bar and the index of the button to the left of the menu. Under Windows CE, you must use CommandBar_DrawMenuBar instead of DrawMenuBar, which is the standard function used to redraw the menu under other versions of Windows.
Command Bar Buttons
Adding buttons to a command bar is a two-step process and is similar to adding buttons to a toolbar. First the bitmap images for the buttons must be added to the command bar. Second the buttons are added, with each of the buttons referencing one of the images in the bitmap list that was previously added.The command bar maintains its own list of bitmaps for the buttons in an internal image list. Bitmaps can be added to this image list one at a time or as a group of images contained in a long and narrow bitmap. For example, for a bitmap to contain four 16-by-16-pixel images, the dimensions of the bitmap added to the command bar would be 64 by 16 pixels. Figure 5-2 shows this bitmap image layout.

Figure 5-2: Layout of a bitmap that contains four 16-by-16-pixel images
Loading an image bitmap is accomplished using
int CommandBar_AddBitmap (HWND hwndCB, HINSTANCE hInst, int idBitmap,
int iNumImages, int iImageWidth, int iImageHeight);
The first two parameters are, as is usual with a command bar function, the handle to the command bar and the instance handle of the executable. The third parameter, idBitmap, is the resource ID of the bitmap image. The fourth parameter, iNumImages, should contain the number of images in the bitmap being loaded. Multiple bitmap images can be loaded into the same command bar by calling CommandBar_AddBitmap as many times as is needed. The last two parameters are the dimensions of the images within the bitmap; set both these parameters to 16.
Two predefined bitmaps provide a number of images that are commonly used in command bars and toolbars. You load these images by setting the hInst parameter in CommandBar_AddBitmap to HINST_COMMCTRL and setting the idBitmap parameter to either IDB_STD_SMALL_COLOR or IDB_VIEW_SMALL_COLOR. The images contained in these bitmaps are shown in Figure 5-3. The buttons on the top line contain the bitmaps from the standard bitmap, while the second-line buttons contain the bitmaps from the standard view bitmap.

Figure 5-3: Images in the two standard bitmaps provided by the common control DLL
The index values to these images are defined in CommCtrl.h, so you don't need to know the exact order in the bitmaps. The constants are
Constants to access the standard bitmap
STD_CUT Edit/Cut button image
STD_COPY Edit/Copy button image
STD_PASTE Edit/Paste button image
STD_UNDO Edit/Undo button image
STD_REDOW Edit/Redo button image
STD_DELETE Edit/Delete button image
STD_FILENEW File/New button image
STD_FILEOPEN File/Open button image
STD_filesAVE File/Save button image
STD_PRINTPRE Print preview button image
STD_PROPERTIES Properties button image
STD_HELP Help button (Use Commandbar_Addadornments
function to add a help button to the
command bar.)
STD_FIND Find button image
STD_REPLACE Replace button image
STD_PRINT Print button image
Constants to access the standard view bitmap
VIEW_LARGEICONS View/Large Icons button image
VIEW_SMALLICONS View/Small Icons button image
VIEW_LIST View/List button image
VIEW_DETAILS View/Details button image
VIEW_SORTNAME Sort by name button image
VIEW_SORTSIZE Sort by size button image
VIEW_SORTDATE Sort by date button image
VIEW_SORTTYPE Sort by type button image
VIEW_PARENTFOLDER Go to Parent folder button image
VIEW_NETCONNECT Connect network drive button image
VIEW_NETDISCONNECT Disconnect network drive button image
VIEW_NEWFOLDER Create new folder button image
Referencing Images
The images loaded into the command bar are referenced by their index into the list of images. For example, if the bitmap loaded contained five images, and the image to be referenced was the fourth image into the bitmap, the zero-based index value would be 3.If more than one set of bitmap images was added to the command bar using multiple calls to CommandBar_AddBitmap, the images' subsequent lists are referenced according to the previous count of images plus the index into that list. For example, if two calls were made to CommandBar_AddBitmap to add two sets of images, with the first call adding five images and the second adding four images, the third image of the second set would be referenced with the total number of images added in the first bitmap (5) plus the index into the second bitmap (2), resulting in an index value of 5 + 2 = 7.Once the bitmaps have been loaded, the buttons can be added using one of two functions. The first function is this one:
BOOL CommandBar_AddButtons (HWND hwndCB, UINT uNumButtons,
LPTBBUTTON lpButtons);
CommandBar_AddButtons adds a series of buttons to the command bar at one time. The function is passed a count of buttons and a pointer to an array of TBBUTTON structures. Each element of the array describes one button. The TBBUTTON structure is defined as the following:
typedef struct {
int iBitmap;
int idCommand;
BYTE fsState;
BYTE fsStyle;
DWORD dwData;
int iString;
} TBBUTTON;
The iBitmap field specifies the bitmap image to be used by the button. This is, as I just explained, the zero-based index into the list of images. The second parameter is the command ID of the button. This ID value is sent via a WM_COMMAND message to the parent when a user clicks the button.The fsState field specifies the initial state of the button. The allowable values in this field are the following:
TBSTATE_ENABLEDThe button is enabled. If this flag isn't specified, the button is disabled and is grayed.
TBSTATE_HIDDENThe button isn't visible on the command bar.
TBSTATE_PRESSEDThis button is displayed in a depressed state.
TBSTATE_CHECKEDThe button is initially checked. This state can be used only if the button has the TBSTYLE_CHECKED style.
TBSTATE_INDETERMINATEThe button is grayed.
One last flag is specified in the documentation, TBSTATE_WRAP, but it doesn't have a valid use in a command bar. This flag is used by toolbars when a toolbar wraps across more than one line.The fsStyle field specifies the initial style of the button, which defines how the button acts. The button can be defined as a standard push button, a check button, a drop-down button, or a check button that resembles a radio button but allows only one button in a group to be checked. The possible flags for the fsStyle field are the following:
TBSTYLE_BUTTONThe button looks like a standard push button.
TBSTYLE_CHECKThe button is a check button that toggles between checked and unchecked states each time the user clicks the button.
TBSTYLE_GROUPDefines the start of a group of buttons.
TBSTYLE_CHECKGROUPThe button is a member of a group of check buttons that act like radio buttons in that only one button in the group is checked at any one time.
TBSTYLE_DROPDOWNThe button is a drop-down list button.
TBSTYLE_AUTOSIZEThe button's size is defined by the button text.
TBSTYLE_SEPDefines a separator (instead of a button) that inserts a small space between buttons.
The dwData field of the TBBUTTON structure is an application-defined value. This value can be set and queried by the application using the TB_SETBUTTONINFO and TB_GETBUTTONINFO messages. The iString field defines the index into the command bar string array that contains the text for the button. The iString field can also be filled with a pointer to a string that contains the text for the button.The other function that adds buttons to a command bar is this one:
BOOL CommandBar_InsertButton (HWND hwndCB, int iButton,
LPTBBUTTON lpButton);
This function inserts one button into the command bar to the left of the button referenced by the iButton parameter. The parameters in this function mimic the parameters in CommandBar_AddButtons with the exception that the lpButton parameter points to a single TBBUTTON structure. The iButton parameter specifies the position on the command bar of the new button.
Working with Command Bar Buttons
When a user presses a command bar button other than a drop-down button, the command bar sends a WM_COMMAND message to the parent window of the command bar. So handling button clicks on the command bar is just like handling menu commands. In fact, since many of the buttons on the command bar have menu command equivalents, it's customary to use the same command IDs for the buttons and the like functioning menus, thus removing the need for any special processing for the command bar buttons.The command bar maintains the checked and unchecked state of check and checkgroup buttons. After the buttons have been added to the command bar, their states can be queried or set using two messages, TB_ISBUTTONCHECKED and TB_CHECKBUTTON. (The TB_ prefix in these messages indicates the close relationship between the command bar and the toolbar controls.) The TB_ISBUTTONCHECKED message is sent with the ID of the button to be queried passed in the wParam parameter this way:
fChecked = SendMessage (hwndCB, TB_ISBUTTONCHECKED, wID, 0);
where hwndCB is the handle to the command bar containing the button. If the return value from the TB_ISBUTTONCHECKED message is nonzero, the button is checked. To place a button in the checked state, send a TB_CHECKBUTTON message to the command bar, as in
SendMessage (hwndCB, TB_CHECKBUTTON, wID, TRUE);
To uncheck a checked button, replace the TRUE value in lParam with FALSE.
Disabled Buttons
Windows CE allows you to easily modify the way a command bar or toolbar button looks when the button is disabled. Command bars and toolbars maintain two image lists: the standard image list that I described previously and a disabled image list used to store bitmaps that you can employ for disabled buttons.
To use this feature, you need to create and load a second image list for disabled buttons. The easiest way to do this is to create the image list for the normal states of the buttons using the techniques I described when I talked about CommandBar_AddBitmap. (Image lists in toolbars are loaded with the message TB_LOADIMAGES.) Once that image list is complete, simply copy the original image list and modify the bitmaps of the images to create disabled counterparts to the original images. Then load the new image list back into the command bar or toolbar. A short code fragment that accomplishes this chore is shown here.
HBITMAP hBmp, hMask;
HIMAGELIST hilDisabled, hilEnabled;
// Load the bitmap and mask to be used in the disabled image list.
hBmp = LoadBitmap (hInst, TEXT ("DisCross"));
hMask = LoadBitmap (hInst, TEXT ("DisMask"));
// Get the standard image list and copy it.
hilEnabled = (HIMAGELIST)SendMessage (hwndCB, TB_GETIMAGELIST, 0, 0);
hilDisabled = ImageList_Duplicate (hilEnabled);
// Replace one bitmap in the disabled list.
ImageList_Replace (hilDisabled, VIEW_LIST, hBmp, hMask);
// Set the disabled image list.
SendMessage (hwndCB, TB_SETDISABLEDIMAGELIST, 0, (LPARAM) hilDisabled);
The code fragment first loads a bitmap and a mask bitmap that will replace one of the images in the disabled image list. You retrieve the current image list by sending a TB_GETIMAGELIST message to the command bar, and then you duplicate it using ImageList_Duplicate. One image in the image list is then replaced by the bitmap that was loaded earlier.This example replaces only one image, but in a real-world example many images might be replaced. If all the images were replaced, it might be easier to build the disabled image list from scratch instead of copying the standard image list and replacing a few bitmaps in it. Once the new image list is created, you load it into the command bar by sending a TB_SETDISABLEDIMAGELIST message. The code that I just showed you works just as well for toolbars under Windows CE as it does for command bars.
Drop-Down Buttons
The drop-down list button is a more complex animal than the standard button on a command bar. The button looks to the user like a button that, when pressed, displays a list of items for the user to select from. To the programmer, a drop-down button is actually a combination of a button and a menu that is displayed when the user clicks on the button. Unfortunately, the command bar does little to support a drop-down button except to modify the button appearance to indicate that the button is a drop-down button and to send a special notification when the button is clicked by the user. It's up to the application to display the menu.The notification of the user clicking a drop-down button is sent to the parent window of the command bar by a WM_NOTIFY message with the notification value TBN_DROPDOWN. When the parent window receives the TBN_DROPDOWN notification, it must create a pop-up menu immediately below the drop-down button identified in the notification. The menu is filled by the parent window with whatever selections are appropriate for the button. When one of the menu items is selected, the menu will send a WM_COMMAND message indicating the menu item picked, and the menu will be dismissed. The easiest way to understand how to handle a drop-down button notification is to look at the following procedure that handles a TBN_DROPDOWN notification.
LRESULT DoNotifyMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
LPNMHDR pNotifyHeader;
LPNMTOOLBAR pNotifyToolBar;
RECT rect;
TPMPARAMS tpm;
HMENU hMenu;
// Get pointer to notify message header.
pNotifyHeader = (LPNMHDR)lParam;
if (pNotifyHeader->code == TBN_DROPDOWN) {
// Get pointer to toolbar notify structure.
pNotifyToolBar = (LPNMTOOLBAR)lParam;
// Get the rectangle of the drop-down button.
SendMessage (pNotifyHeader->hwndFrom, TB_GETRECT,
pNotifyToolBar->iItem, (LPARAM)&rect);
// Convert rect to screen coordinates. The rect is
// considered here to be an array of 2 POINT structures.
MapWindowPoints (pNotifyHeader->hwndFrom, HWND_DESKTOP,
(LPPOINT)&rect, 2);
// Prevent the menu from covering the button.
tpm.cbSize = sizeof (tpm);
CopyRect (&tpm.rcExclude, &rect);
// Load the menu resource to display under the button.
hMenu = GetSubMenu (LoadMenu (hInst, TEXT ("popmenu")),0);
// Display the menu. This function returns after the
// user makes a selection or dismisses the menu.
TrackPopupMenuEx (hMenu, TPM_LEFTALIGN | TPM_VERTICAL,
rect.left, rect.bottom, hWnd, &tpm);
}
return 0;
}
After the code determines that the message is a TBN_DROPDOWN notification, the first task of the notification handler code is to get the rectangle of the drop-down button. The rectangle is queried so that the drop-down menu can be positioned immediately below the button. To do this, the routine sends a TB_GETRECT message to the command bar with the ID of the drop-down button passed in wParam and a pointer to a rectangle structure in lParam.Because the rectangle returned is in the coordinate base of the parent window, and pop-up menus are positioned in screen coordinates, the coordinates must be converted from one basis to the other. You accomplish this using the function
MapWindowPoints (HWND hwndFrom, HWND hwndTo,
LPPOINT lppoints, UINT cPoints);
The first parameter is the handle of the window in which the coordinates are originally based. The second parameter is the handle of the window to which you want to map the coordinates. The third parameter is a pointer to an array of points to be translated; the last parameter is the number of points in the array. In the routine I just showed you, the window handles are the command bar handle and the desktop window handle, respectively.Once the rectangle has been translated into desktop coordinates, the pop-up, or context, menu can be created. You do this by first loading the menu from the resource and then displaying the menu with a call to TrackPopupMenuEx. If you recall the discussion of TrackPopupMenuEx from the preceding chapter, the TPMPARAMS structure contains a rectangle that won't be covered up by the menu when it's displayed. For our purposes, this rectangle is set to the dimensions of the drop-down button so that the button won't be covered by the pop-up menu. The fuFlags field can contain a number of values that define the placement of the menu. For drop-down buttons, the only flag needed is TPM_VERTICAL. If TPM_VERTICAL is set, the menu leaves uncovered as much of the horizontal area of the exclude rectangle as possible. The TrackPopupMenuEx function doesn't return until an item on the menu has been selected or the menu has been dismissed by the user tapping on another part of the screen.
Combo Boxes on the Command Bar
Combo boxes on a command bar are much easier to implement than drop-down buttons. You add a combo box by calling
HWND CommandBar_InsertComboBox (HWND hwndCB, HINSTANCE hInst,
int iWidth, UINT dwStyle,
WORD idComboBox,
int iButton);
This function inserts a combo box on the command bar to the left of the button indicated by the iButton parameter. The width of the combo box is specified, in pixels, by the iWidth parameter. The dwStyle parameter specifies the style of the combo box. The allowable style flags are any valid Windows CE combo box style and window styles. The function automatically adds the WS_CHILD and WS_VISIBLE flags when creating the combo box. The idComboBox parameter is the ID for the combo box that will be used when WM_COMMAND messages are sent notifying the parent window of a combo box event. Experienced Windows programmers will be happy to know that CommandBar_InsertComboBox takes care of all the "parenting" problems that occur when a control is added to a standard Windows toolbar. That one function call is all that is needed to create a properly functioning combo box on the command bar.Once a combo box is created, you program it on the command bar the same way you would a standalone combo box. Since the combo box is a child of the command bar, you must query the window handle of the combo box by passing the handle of the command bar to GetDlgItem with the ID value of the combo box, as in the following code:
hwndCombobox = GetDlgItem (GetDlgItem (hWnd, IDC_CMDBAR),
IDC_COMBO));
However, the WM_COMMAND messages from the combo box are sent directly to the parent of the command bar, so handling combo box events is identical to handling them from a combo box created as a child of the application's top-level window.
Command Bar Tooltips
Tooltips are small windows that display descriptive text that labels a command bar button when the stylus is held down over the control. The command bar implements tooltips in its own unique way.You add tooltips to a command bar by using this function:
BOOL CommandBar_AddToolTips (HWND hwndCB, UINT uNumToolTips,
LPTSTR lpToolTips);
The lpToolTips parameter must point to an array of pointers to strings. The uNumToolTips parameter should be set to the number of elements in the string pointer array. The CommandBar_AddToolTips function doesn't copy the strings into its own storage. Instead, the location of the string array is saved. This means that the block of memory containing the string array must not be released until the command bar is destroyed.Each string in the array becomes the tooltip text for a control or separator on the command bar, excluding the menu. The first string in the array becomes the tooltip for the first control or separator, the second string is assigned to the second control or separator, and so on. So even though combo boxes and separators don't display tooltips, they must have entries in the string array so that all the text lines up with the proper buttons.
Other Command Bar Functions
A number of other functions assist in command bar management. The CommandBar_Height function returns the height of the command bar and is used in all the example programs that use the command bar. Likewise, the CommandBar_AddAdornments function is also used whenever a command bar is used. This function, prototyped as
BOOL CommandBar_AddAdornments (HWND hwndCB, DWORD dwFlags,
DWORD dwReserved);
places a Close button and, if you want, a Help button and an OK button on the extreme right of the command bar. You pass a CMDBAR_HELP flag to the dwFlags parameter to add a Help button, and you pass a CMDBAR_OK flag to add an OK button.The Help button is treated differently from other buttons on the command bar. When the Help button is pressed, the command bar sends a WM_HELP message to the owner of the command bar instead of the standard WM_COMMAND message. The OK button's action is more traditional. When you tap it, you send a WM_COMMAND message with the control ID IDOK. The CommandBar_AddAdornments function must be called after all other controls of the command bar have been added.If your top-level window is resizeable, you must notifiy the command bar of resize during the WM_SIZE message by sending a TB_AUTOSIZE message to the command bar and then calling
BOOL CommandBar_AlignAdornments (HWND hwndCB);
The only parameter is the handle to the command bar. A command bar can be hidden by calling
BOOL CommandBar_Show (HWND hwndCB, BOOL fShow);
The fShow parameter is set to TRUE to show a command bar and FALSE to hide a command bar. The visibility of a command bar can be queried with this:
BOOL CommandBar_IsVisible (HWND hwndCB);
Finally, a command bar can be destroyed using this:
void CommandBar_Destroy (HWND hwndCB);
Although a command bar is automatically destroyed when its parent window is destroyed, sometimes it's more convenient to destroy a command bar manually. This is often done if a new command bar is needed for a different mode of the application. Of course, you can create multiple command bars, hiding all but one and switching between them by showing only one at a time, but this isn't good programming practice under Windows CE because all those hidden command bars take up valuable RAM that could be used elsewhere. The proper method is to destroy and create command bars on the fly. You can create a command bar fast enough so that a user shouldn't notice any delay in the application when a new command bar is created.
Design Guidelines for Command Bars
Because command bars are a major element of Windows CE applications, it's not surprising that Microsoft has a rather strong set of rules for their use. Many of these rules are similar to the design guidelines for other versions of Windows, such as the recommendations for the ordering of main menu items and the use of tooltips. Most of these guidelines are already second nature to Windows programmers.The menu should be the leftmost item on the command bar. The order of the main menu items should be from left to right: File, Edit, View, Insert, Format, Tools, and Window. Of course, most applications have all of those menu items, but the order of the items used should follow the suggested order. For buttons, the order is from left to right: New, Open, Save, and Print for file actions; and Bold, Italic, and Underline for font style.
The CmdBar Example
The CmdBar example demonstrates the basics of command bar operation. On startup, the example creates a bar with only a menu and a close button. Selecting the different items from the view menu creates various command bars showing the capabilities of the command bar control. The source code for CmdBar is shown in Listing 5-1.
Listing 5-1: The CmdBar program
CmdBar.rc
//======================================================================
// Resource file
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
#include "windows.h"
#include "CmdBar.h" // Program-specific stuff
//----------------------------------------------------------------------
// Icons and bitmaps
//
ID_ICON ICON "cmdbar.ico" // Program icon
DisCross BITMAP "cross.bmp" // Disabled button image
DisMask BITMAP "mask.bmp" // Disabled button image mask
SortDropBtn BITMAP "sortdrop.bmp" // Sort drop-down button image
//----------------------------------------------------------------------
// Menu
//
ID_MENU MENU DISCARDABLE
BEGIN
POPUP "&File"
BEGIN
MENUITEM "E&xit", IDM_EXIT
END
POPUP "&View"
BEGIN
MENUITEM "&Standard", IDM_STDBAR
MENUITEM "&View", IDM_VIEWBAR
MENUITEM "&Combination", IDM_COMBOBAR
END
POPUP "&Help"
BEGIN
MENUITEM "&About...", IDM_ABOUT
END
END
popmenu MENU DISCARDABLE
BEGIN
POPUP "&Sort"
BEGIN
MENUITEM "&Name", IDC_SNAME
MENUITEM "&Type", IDC_STYPE
MENUITEM "&Size", IDC_SSIZE
MENUITEM "&Date", IDC_SDATE
END
END
//----------------------------------------------------------------------
// About box dialog template
//
aboutbox DIALOG discardable 10, 10, 160, 45
STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU |
DS_CENTER | DS_MODALFRAME
CAPTION "About"
BEGIN
ICON ID_ICON, -1, 5, 5, 10, 10
LTEXT "CmdBar - Written for the book Programming Windows CE Copyright 2003 Douglas Boling"
-1, 40, 5, 110, 35
END
CmdBar.h
//======================================================================
// Header file
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
// Returns number of elements
#define dim(x) (sizeof(x) / sizeof(x[0]))
//----------------------------------------------------------------------
// Generic defines and data types
//
struct decodeUINT { // Structure associates
UINT Code; // messages
// with a function.
LRESULT (*Fxn)(HWND, UINT, WPARAM, LPARAM);
};
struct decodeCMD { // Structure associates
UINT Code; // menu IDs with a
LRESULT (*Fxn)(HWND, WORD, HWND, WORD); // function.
};
//----------------------------------------------------------------------
// Generic defines used by application
#define IDC_CMDBAR 1 // Command band ID
#define ID_ICON 10 // Icon resource ID
#define ID_MENU 11 // Main menu resource ID
#define IDC_COMBO 12 // Combo box on cmd bar ID
// Menu item IDs
#define IDM_EXIT 101 // File menu
#define IDM_STDBAR 111 // View menu
#define IDM_VIEWBAR 112
#define IDM_COMBOBAR 113
#define IDM_ABOUT 120 // Help menu
// Command bar button IDs
#define IDC_NEW 201
#define IDC_OPEN 202
#define IDC_SAVE 203
#define IDC_CUT 204
#define IDC_COPY 205
#define IDC_PASTE 206
#define IDC_PROP 207
#define IDC_LICON 301
#define IDC_SICON 302
#define IDC_LIST 303
#define IDC_RPT 304
#define IDC_SNAME 305
#define IDC_STYPE 306
#define IDC_SSIZE 307
#define IDC_SDATE 308
#define IDC_DPSORT 350
#define STD_BMPS (STD_PRINT+1) // Number of bmps in
// std imglist
#define VIEW_BMPS (VIEW_NEWFOLDER+1) // Number of bmps in
// view imglist
//----------------------------------------------------------------------
// Function prototypes
//
HWND InitInstance (HINSTANCE, LPWSTR, int);
int TermInstance (HINSTANCE, int);
// Window procedures
LRESULT CALLBACK MainWndProc (HWND, UINT, WPARAM, LPARAM);
// Message handlers
LRESULT DoCreateMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoSizeMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoCommandMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoNotifyMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoDestroyMain (HWND, UINT, WPARAM, LPARAM);
// Command functions
LPARAM DoMainCommandExit (HWND, WORD, HWND, WORD);
LPARAM DoMainCommandVStd (HWND, WORD, HWND, WORD);
LPARAM DoMainCommandVView (HWND, WORD, HWND, WORD);
LPARAM DoMainCommandVCombo (HWND, WORD, HWND, WORD);
LPARAM DoMainCommandAbout (HWND, WORD, HWND, WORD);
// Dialog procedures
BOOL CALLBACK AboutDlgProc (HWND, UINT, WPARAM, LPARAM);
CmdBar.cpp
//======================================================================
// CmdBar - Command bar demonstration
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
#include <windows.h> // For all that Windows stuff
#include <commctrl.h> // Command bar includes
#include "CmdBar.h" // Program-specific stuff
//----------------------------------------------------------------------
// Global data
//
const TCHAR szAppName[] = TEXT ("CmdBar");
HINSTANCE hInst; // Program instance handle
// Message dispatch table for MainWindowProc
const struct decodeUINT MainMessages[] = {
WM_CREATE, DoCreateMain,
WM_SIZE, DoSizeMain,
WM_COMMAND, DoCommandMain,
WM_NOTIFY, DoNotifyMain,
WM_DESTROY, DoDestroyMain,
};
// Command Message dispatch for MainWindowProc
const struct decodeCMD MainCommandItems[] = {
IDM_EXIT, DoMainCommandExit,
IDM_STDBAR, DoMainCommandVStd,
IDM_VIEWBAR, DoMainCommandVView,
IDM_COMBOBAR, DoMainCommandVCombo,
IDM_ABOUT, DoMainCommandAbout,
};
// Standard file bar button structure
const TBBUTTON tbCBStdBtns[] = {
// BitmapIndex Command State Style UserData String
{0, 0, 0, TBSTYLE_SEP, 0, 0},
{STD_FILENEW, IDC_NEW, TBSTATE_ENABLED,
TBSTYLE_BUTTON, 0, 0},
{STD_FILEOPEN, IDC_OPEN, TBSTATE_ENABLED,
TBSTYLE_BUTTON, 0, 0},
{STD_filesAVE, IDC_SAVE, TBSTATE_ENABLED,
TBSTYLE_BUTTON, 0, 0},
{0, 0, 0, TBSTYLE_SEP, 0, 0},
{STD_CUT, IDC_CUT, TBSTATE_ENABLED,
TBSTYLE_BUTTON, 0, 0},
{STD_COPY, IDC_COPY, TBSTATE_ENABLED,
TBSTYLE_BUTTON, 0, 0},
{STD_PASTE, IDC_PASTE, TBSTATE_ENABLED,
TBSTYLE_BUTTON, 0, 0},
{0, 0, 0, TBSTYLE_SEP, 0, 0},
{STD_PROPERTIES, IDC_PROP, TBSTATE_ENABLED,
TBSTYLE_BUTTON, 0, 0}
};
// Standard view bar button structure
const TBBUTTON tbCBViewBtns[] = {
// BitmapIndex Command State Style UserData String
{0, 0, 0, TBSTYLE_SEP, 0, 0},
{VIEW_LARGEICONS, IDC_LICON, TBSTATE_ENABLED | TBSTATE_CHECKED,
TBSTYLE_CHECKGROUP, 0, 0},
{VIEW_SMALLICONS, IDC_SICON, TBSTATE_ENABLED,
TBSTYLE_CHECKGROUP, 0, 0},
{VIEW_LIST, IDC_LIST, 0, TBSTYLE_CHECKGROUP, 0, 0},
{VIEW_DETAILS, IDC_RPT, TBSTATE_ENABLED,
TBSTYLE_CHECKGROUP, 0, 0},
{0, 0, TBSTATE_ENABLED,
TBSTYLE_SEP, 0, 0},
{VIEW_SORTNAME, IDC_SNAME, TBSTATE_ENABLED | TBSTATE_CHECKED,
TBSTYLE_CHECKGROUP, 0, 0},
{VIEW_SORTTYPE, IDC_STYPE, TBSTATE_ENABLED,
TBSTYLE_CHECKGROUP, 0, 0},
{VIEW_SORTSIZE, IDC_SSIZE, TBSTATE_ENABLED,
TBSTYLE_CHECKGROUP, 0, 0},
{VIEW_SORTDATE, IDC_SDATE, TBSTATE_ENABLED,
TBSTYLE_CHECKGROUP, 0, 0},
{0, 0, 0, TBSTYLE_SEP, 0, 0},
};
// Tooltip string list for view bar
const TCHAR *pViewTips[] = {TEXT ("), TEXT ("Large"), TEXT ("Small"),
TEXT ("List"), TEXT ("Details"), TEXT ("),
TEXT ("Sort by Name"), TEXT ("Sort by Type"),
TEXT ("Sort by Size"), TEXT ("Sort by Date"),
};
// Combination standard and view bar button structure
const TBBUTTON tbCBCmboBtns[] = {
// BitmapIndex Command State Style UserData String
{0, 0, 0, TBSTYLE_SEP, 0, 0},
{STD_FILENEW, IDC_NEW, TBSTATE_ENABLED,
TBSTYLE_BUTTON, 0, 0},
{STD_FILEOPEN, IDC_OPEN, TBSTATE_ENABLED,
TBSTYLE_BUTTON, 0, 0},
{STD_PROPERTIES, IDC_PROP, TBSTATE_ENABLED,
TBSTYLE_BUTTON, 0, 0},
{0, 0, 0, TBSTYLE_SEP, 0, 0},
{STD_CUT, IDC_CUT, TBSTATE_ENABLED,
TBSTYLE_BUTTON, 0, 0},
{STD_COPY, IDC_COPY, TBSTATE_ENABLED,
TBSTYLE_BUTTON, 0, 0},
{STD_PASTE, IDC_PASTE, TBSTATE_ENABLED,
TBSTYLE_BUTTON, 0, 0},
{0, 0, 0, TBSTYLE_SEP, 0, 0},
{STD_BMPS + VIEW_LARGEICONS,
IDC_LICON, TBSTATE_ENABLED | TBSTATE_CHECKED,
TBSTYLE_CHECKGROUP, 0, 0},
{STD_BMPS + VIEW_SMALLICONS,
IDC_SICON, TBSTATE_ENABLED,
TBSTYLE_CHECKGROUP, 0, 0},
{STD_BMPS + VIEW_LIST,
IDC_LIST, TBSTATE_ENABLED,
TBSTYLE_CHECKGROUP, 0, 0},
{STD_BMPS + VIEW_DETAILS,
IDC_RPT, TBSTATE_ENABLED,
TBSTYLE_CHECKGROUP, 0, 0},
{0, 0, 0, TBSTYLE_SEP, 0, 0},
{STD_BMPS + VIEW_BMPS,
IDC_DPSORT,TBSTATE_ENABLED,
TBSTYLE_DROPDOWN, 0, 0}
};
//======================================================================
// Program entry point
//
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPWSTR lpCmdLine, int nCmdShow) {
HWND hwndMain;
MSG msg;
int rc = 0;
// Initialize application.
hwndMain = InitInstance (hInstance, lpCmdLine, nCmdShow);
if (hwndMain == 0) return 0x10;
// Application message loop
while (GetMessage (&msg, NULL, 0, 0)) {
TranslateMessage (&msg);
DispatchMessage (&msg);
}
// Instance cleanup
return TermInstance (hInstance, msg.wParam);
}
//----------------------------------------------------------------------
// InitInstance - Instance initialization
//
HWND InitInstance (HINSTANCE hInstance, LPWSTR lpCmdLine, int nCmdShow){
HWND hWnd;
DWORD dwStyle = WS_VISIBLE;
int x = CW_USEDEFAULT, y = CW_USEDEFAULT;
int cx = CW_USEDEFAULT, cy = CW_USEDEFAULT;
WNDCLASS wc;
INITCOMMONCONTROLSEX icex;
#if defined(WIN32_PLATFORM_PSPC)
// If Pocket PC, allow only one instance of the application.
hWnd = FindWindow (szAppName, NULL);
if (hWnd) {
SetForegroundWindow ((HWND)(((DWORD)hWnd) | 0x01));
return 0;
}
#endif
// Register application main window class.
wc.style = 0; // Window style
wc.lpfnWndProc = MainWndProc; // Callback function
wc.cbClsExtra = 0; // Extra class data
wc.cbWndExtra = 0; // Extra window data
wc.hInstance = hInstance; // Owner handle
wc.hIcon = NULL, // Application icon
wc.hCursor = LoadCursor (NULL, IDC_ARROW);// Default cursor
wc.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
wc.lpszMenuName = NULL; // Menu name
wc.lpszClassName = szAppName; // Window class name
if (RegisterClass (&wc) == 0) return 0;
// Load the command bar common control class.
icex.dwSize = sizeof (INITCOMMONCONTROLSEX);
icex.dwICC = ICC_BAR_CLASSES;
InitCommonControlsEx (&icex);
#ifndef WIN32_PLATFORM_PSPC
dwStyle |= WS_CAPTION | WS_SIZEBOX | WS_MAXIMIZEBOX | WS_MINIMIZEBOX;
x = y = 10;
cx = GetSystemMetrics (SM_CXSCREEN) - 30;
cy = GetSystemMetrics (SM_CYSCREEN) - 50
#endif
// Save program instance handle in global variable.
hInst = hInstance;
// Create main window.
hWnd = CreateWindow (szAppName, TEXT ("CmdBar Demo"), dwStyle,
x, y, cx, cy, NULL, NULL, hInstance, NULL);
// Return fail code if window not created.
if (!IsWindow (hWnd)) return 0;
// Standard show and update calls
ShowWindow (hWnd, nCmdShow);
UpdateWindow (hWnd);
return hWnd;
}
//----------------------------------------------------------------------
// TermInstance - Program cleanup
//
int TermInstance (HINSTANCE hInstance, int nDefRC) {
return nDefRC;
}
//======================================================================
// Message handling procedures for MainWindow
//----------------------------------------------------------------------
// MainWndProc - Callback function for application window
//
LRESULT CALLBACK MainWndProc (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
int i;
//
// Search message list to see if we need to handle this
// message. If in list, call procedure.
//
for (i = 0; i < dim(MainMessages); i++) {
if (wMsg == MainMessages[i].Code)
return (*MainMessages[i].Fxn)(hWnd, wMsg, wParam, lParam);
}
return DefWindowProc (hWnd, wMsg, wParam, lParam);
}
//----------------------------------------------------------------------
// DoCreateMain - Process WM_CREATE message for window.
//
LRESULT DoCreateMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
HWND hwndCB;
// Create a minimal command bar that has only a menu and an
// exit button.
hwndCB = CommandBar_Create (hInst, hWnd, IDC_CMDBAR);
// Insert the menu.
CommandBar_InsertMenubar (hwndCB, hInst, ID_MENU, 0);
// Add exit button to command bar.
CommandBar_AddAdornments (hwndCB, 0, 0);
return 0;
}
//----------------------------------------------------------------------
// DoSizeMain - Process WM_SIZE message for window.
//
LRESULT DoSizeMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
#ifndef WIN32_PLATFORM_PSPC
HWND hwndCB = GetDlgItem (hWnd, IDC_CMDBAR);
// Tell the command bar to resize itself and reposition Close button.
SendMessage(hwndCB, TB_AUTOSIZE, 0L, 0L);
CommandBar_AlignAdornments(hwndCB);
#endif //WIN32_PLATFORM_PSPC
return 0;
}
//----------------------------------------------------------------------
// DoCommandMain - Process WM_COMMAND message for window.
//
LRESULT DoCommandMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
WORD idItem, wNotifyCode;
HWND hwndCtl;
INT i;
// Parse the parameters.
idItem = (WORD) LOWORD (wParam);
wNotifyCode = (WORD) HIWORD (wParam);
hwndCtl = (HWND) lParam;
// Call routine to handle control message.
for (i = 0; i < dim(MainCommandItems); i++) {
if (idItem == MainCommandItems[i].Code)
return (*MainCommandItems[i].Fxn)(hWnd, idItem, hwndCtl,
wNotifyCode);
}
return 0;
}
//----------------------------------------------------------------------
// DoNotifyMain - Process WM_NOTIFY message for window.
//
LRESULT DoNotifyMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
LPNMHDR pNotifyHeader;
LPNMTOOLBAR pNotifyToolBar;
RECT rect;
TPMPARAMS tpm;
HMENU hMenu;
// Get pointer to notify message header.
pNotifyHeader = (LPNMHDR)lParam;
if (pNotifyHeader->code == TBN_DROPDOWN) {
// Get pointer to toolbar notify structure.
pNotifyToolBar = (LPNMTOOLBAR)lParam;
if (pNotifyToolBar->iItem == IDC_DPSORT) {
// Get the rectangle of the drop-down button.
SendMessage (pNotifyHeader->hwndFrom, TB_GETRECT,
pNotifyToolBar->iItem, (LPARAM)&rect);
// Convert rect to screen coordinates. The rect is
// considered here to be an array of 2 POINT structures.
MapWindowPoints (pNotifyHeader->hwndFrom, HWND_DESKTOP,
(LPPOINT)&rect, 2);
// Prevent the menu from covering the button.
tpm.cbSize = sizeof (tpm);
CopyRect (&tpm.rcExclude, &rect);
hMenu = GetSubMenu (LoadMenu (hInst, TEXT ("popmenu")),0);
TrackPopupMenuEx (hMenu, TPM_LEFTALIGN | TPM_VERTICAL,
rect.left, rect.bottom, hWnd, &tpm);
}
}
return 0;
}
//----------------------------------------------------------------------
// DoDestroyMain - Process WM_DESTROY message for window.
//
LRESULT DoDestroyMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
PostQuitMessage (0);
return 0;
}
//======================================================================
// Command handler routines
//----------------------------------------------------------------------
// DoMainCommandExit - Process Program Exit command.
//
LPARAM DoMainCommandExit (HWND hWnd, WORD idItem, HWND hwndCtl,
WORD wNotifyCode) {
SendMessage (hWnd, WM_CLOSE, 0, 0);
return 0;
}
//----------------------------------------------------------------------
// DoMainCommandViewStd - Displays a standard edit-centric command bar
//
LPARAM DoMainCommandVStd (HWND hWnd, WORD idItem, HWND hwndCtl,
WORD wNotifyCode) {
HWND hwndCB;
// If a command bar exists, kill it.
if (hwndCB = GetDlgItem (hWnd, IDC_CMDBAR))
CommandBar_Destroy (hwndCB);
// Create a command bar.
hwndCB = CommandBar_Create (hInst, hWnd, IDC_CMDBAR);
// Insert a menu.
CommandBar_InsertMenubar (hwndCB, hInst, ID_MENU, 0);
// Insert buttons.
CommandBar_AddBitmap (hwndCB, HINST_COMMCTRL, IDB_STD_SMALL_COLOR,
STD_BMPS, 0, 0);
CommandBar_AddButtons (hwndCB, dim(tbCBStdBtns), tbCBStdBtns);
// Add exit button to command bar.
CommandBar_AddAdornments (hwndCB, 0, 0);
return 0;
}
//----------------------------------------------------------------------
// DoMainCommandVView - Displays a standard edit-centric command bar
//
LPARAM DoMainCommandVView (HWND hWnd, WORD idItem, HWND hwndCtl,
WORD wNotifyCode) {
INT i;
HWND hwndCB;
TCHAR szTmp[64];
HBITMAP hBmp, hMask;
HIMAGELIST hilDisabled, hilEnabled;
// If a command bar exists, kill it.
if (hwndCB = GetDlgItem (hWnd, IDC_CMDBAR))
CommandBar_Destroy (hwndCB);
// Create a command bar.
hwndCB = CommandBar_Create (hInst, hWnd, IDC_CMDBAR);
// Insert a menu.
CommandBar_InsertMenubar (hwndCB, hInst, ID_MENU, 0);
// Insert buttons, first add a bitmap and then the buttons.
CommandBar_AddBitmap (hwndCB, HINST_COMMCTRL, IDB_VIEW_SMALL_COLOR,
VIEW_BMPS, 0, 0);
// Load bitmaps for disabled image.
hBmp = LoadBitmap (hInst, TEXT ("DisCross"));
hMask = LoadBitmap (hInst, TEXT ("DisMask"));
// Get the current image list and copy.
hilEnabled = (HIMAGELIST)SendMessage (hwndCB, TB_GETIMAGELIST, 0, 0);
hilDisabled = ImageList_Duplicate (hilEnabled);
// Replace a button image with the disabled image.
ImageList_Replace (hilDisabled, VIEW_LIST, hBmp, hMask);
// Set disabled image list.
SendMessage (hwndCB, TB_SETDISABLEDIMAGELIST, 0,
(LPARAM)hilDisabled);
// Add buttons to the command bar.
CommandBar_AddButtons (hwndCB, dim(tbCBViewBtns), tbCBViewBtns);
// Add tooltips to the command bar.
CommandBar_AddToolTips (hwndCB, dim(pViewTips), pViewTips);
// Add a combo box between the view icons and the sort icons.
CommandBar_InsertComboBox (hwndCB, hInst, 75,
CBS_DROPDOWNLIST | WS_VSCROLL,
IDC_COMBO, 6);
// Fill in combo box.
for (i = 0; i < 10; i++) {
wsprintf (szTmp, TEXT ("Item %d"), i);
SendDlgItemMessage (hwndCB, IDC_COMBO, CB_INSERTSTRING, -1,
(LPARAM)szTmp);
}
SendDlgItemMessage (hwndCB, IDC_COMBO, CB_SETCURSEL, 0, 0);
// Add exit button to command bar.
CommandBar_AddAdornments (hwndCB, 0, 0);
return 0;
}
//----------------------------------------------------------------------
// DoMainCommandVCombo - Displays a combination of file and edit buttons
//
LPARAM DoMainCommandVCombo (HWND hWnd, WORD idItem, HWND hwndCtl,
WORD wNotifyCode) {
HWND hwndCB;
// If a command bar exists, kill it.
if (hwndCB = GetDlgItem (hWnd, IDC_CMDBAR))
CommandBar_Destroy (hwndCB);
// Create a command bar.
hwndCB = CommandBar_Create (hInst, hWnd, IDC_CMDBAR);
// Insert a menu.
CommandBar_InsertMenubar (hwndCB, hInst, ID_MENU, 0);
// Add two bitmap lists plus custom bmp for drop-down button.
CommandBar_AddBitmap (hwndCB, HINST_COMMCTRL, IDB_STD_SMALL_COLOR,
STD_BMPS, 0, 0);
CommandBar_AddBitmap (hwndCB, HINST_COMMCTRL, IDB_VIEW_SMALL_COLOR,
VIEW_BMPS, 0, 0);
CommandBar_AddBitmap (hwndCB, NULL,
(int)LoadBitmap (hInst, TEXT ("SortDropBtn")),
1, 0, 0);
CommandBar_AddButtons (hwndCB, dim(tbCBCmboBtns), tbCBCmboBtns);
// Add exit button to command bar.
CommandBar_AddAdornments (hwndCB, 0, 0);
return 0;
}
//----------------------------------------------------------------------
// DoMainCommandAbout - Process the Help | About menu command.
//
LPARAM DoMainCommandAbout(HWND hWnd, WORD idItem, HWND hwndCtl,
WORD wNotifyCode) {
// Use DialogBox to create modal dialog box.
DialogBox (hInst, TEXT ("aboutbox"), hWnd, AboutDlgProc);
return 0;
}
//======================================================================
// About Dialog procedure
//
BOOL CALLBACK AboutDlgProc (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
switch (wMsg) {
case WM_COMMAND:
switch (LOWORD (wParam)) {
case IDOK:
case IDCANCEL:
EndDialog (hWnd, 0);
return TRUE;
}
break;
}
return FALSE;
}
Each of the three command bars created in CmdBar demonstrates different capabilities of the command bar control. The first command bar, created in the routine DoMainCommandVStd, creates a vanilla command bar with a menu and a set of buttons. The button structure for this command bar is defined in the array tbCBStdBtns, which is defined near the top of

The DoMainCommandVCombo routine creates the third command bar. It uses both the standard and view bitmap images as well as a custom bitmap for a drop-down button. This command bar demonstrates the technique of referencing the images in an image list that contains multiple bitmaps. The drop-down button is serviced by the DoNotifyMain routine, where a pop-up menu is loaded and displayed when a TBN_DROPDOWN notification is received.Finally, when CmdBar is compiled for an embedded version of Windows CE, it looks a bit different because of the style flags in CreateWindow. The main window has a caption bar and doesn't fill the entire screen. You can size the window by dragging the edge of the window and move the window by dragging the caption bar. This program shows off the ability of a command bar to resize itself with a little help from some code in the WM_SIZE message handler.
Command Bands
A command bands control is a rebar control that, by default, contains a command bar in each band of the control. The rebar control is a container of controls that the user can drag around the application window. Given that command bands are nothing more than command bars in a rebar control, knowing how to program a command bar is most of the battle when learning how to program the command bands control.Each individual band of the command bands control can have a "gripper" that can be used to drag the band to a new position. A band can be in a minimized state, showing only its gripper and, if you want, an icon; in a maximized state, covering up the other bands on the line; or restored, sharing space with the other bands on the same line. You can even move bands to a new row, creating a multiple-row command band. Figure 5-4 shows a window with a command bands control in two rows across the top of the window.

Figure 5-4: A window with a command bands control
The standard use of a command bands control is to break up the elements of a command bar—menu, buttons, and other controls—into separate bands. This allows users to rearrange these elements as they see fit. Users can also expose or overlap separate bands as needed in order to provide a larger total area for menus, buttons, and other controls.
Creating a Command Bands Control
Creating a command bands control is straightforward, if a bit more complicated than creating a command bar control. You create the control by calling
HWND CommandBands_Create (HINSTANCE hinst, HWND hwndParent, UINT wID,
DWORD dwStyles, HIMAGELIST himl);
The dwStyles parameter accepts a number of flags that define the look and operation of the command bands control. These styles match the rebar styles; the command bands control is, after all, closely related to the rebar control.
RBS_AUTOSIZEBands are automatically reformatted if the size or position of the control is changed.
RBS_BANDBORDERSEach band is drawn with lines to separate adjacent bands.
I touched on image lists earlier. Command bars and toolbars use image lists internally to manage the images used on buttons. Image lists can be managed in a standalone image list control. This control is basically a helper control that assists applications in managing a series of like-size images. The image list control in Windows CE is identical to the image list control under Windows 2000 and Windows Me, with the exception that the Windows CE version can't contain cursors for systems built without mouse/cursor support. For the purposes of the command bands control, the image list just needs to be created and a set of bitmaps added that will represent the individual bands when they're minimized. An example of the minimal code required for this is shown here:
himl = ImageList_Create (16, 16, ILC_COLOR, 2, 0);
hBmp = LoadBitmap (hInst, TEXT ("CmdBarBmps"));
ImageList_Add (himl, hBmp, NULL);
DeleteObject (hBmp);
The ImageList_Create function takes the dimensions of the images to be loaded, the format of the images (ILC_COLOR is the default), the number of images initially in the list, and the number to be added. The two images are then added by loading a double-wide bitmap that contains two images and calling ImageList_Add. After the bitmap has been loaded into the image list, it should be deleted.
RBS_FIXEDORDERBands can be moved but always remain in the same order.
RBS_SMARTLABELSWhen minimized, a band is displayed with its icon. When the band is restored or maximized, its label text is displayed.
RBS_VARHEIGHTEach row in the control is vertically sized to the minimum required by the bands on that row. Without this flag, the height of every row is defined by the height of the tallest band in the control.
CCS_VERTCreates a vertical command bands control.
RBS_VERTICALGRIPPERDisplays a gripper appropriate for a vertical command bar. This flag is ignored unless CCS_VERT is set.
Of these styles, RBS_SMARTLABELS and RBS_VARHEIGHT are the two most frequently used flags. The RBS_SMARTLABELS flag lets you choose an attractive appearance for the command bands control without requiring any effort from the application. The RBS_VARHEIGHT flag is important if you use controls in a band other than the default command bar. The CCS_VERT style creates a vertical command bands control, but because Windows CE doesn't support vertical menus, any band with a menu won't be displayed correctly in a vertical band. As you'll see, however, you can hide a particular band when the control is oriented vertically.
Adding Bands
You can add bands to your application by passing an array of REBARBANDINFO structures that describe each band to the control. The function is
BOOL CommandBands_AddBands (HWND hwndCmdBands, HINSTANCE hinst,
UINT cBands, LPREBARBANDINFO prbbi);
Before you call this function, you must fill out a REBARBANDINFO structure for each of the bands to be added to the control. The structure is defined as
typedef struct tagREBARBANDINFO{
UINT cbSize;
UINT fMask;
UINT fStyle;
COLORREF clrFore;
COLORREF clrBack;
LPTSTR lpText;
UINT cch;
int iImage;
HWND hwndChild;
UINT cxMinChild;
UINT cyMinChild;
UINT cyMinChild;
UINT cx;
HBITMAP hbmBack;
UINT wID;
UINT cyChild;
UINT cyMaxChild;
UINT cyIntegral;
UINT cxIdeal;
LPARAM lParam;
} REBARBANDINFO;
Fortunately, although this structure looks imposing, many of the fields can be ignored because there are default actions for uninitialized fields. As usual with a Windows structure, the cbSize field must be filled with the size of the structure as a fail-safe measure when the structure is passed to Windows. The fMask field is filled with a number of flags that indicate which of the remaining fields in the structure are filled with valid information. I'll describe the flags as I cover each of the fields.The fStyle field must be filled with the style flags for the band if the RBBIM_STYLE flag is set in the fMask field. The allowable flags are the following:
RBBS_BREAKThe band will start on a new line.
RBBS_FIXEDSIZEThe band can't be sized. When this flag is specified, the gripper for the band isn't displayed.
RBBS_HIDDENThe band won't be visible when the command band is created.
RBBS_GRIPPERALWAYSThe band will have a sizing grip, even if it's the only band in the command band.
RBBS_NOGRIPPERThe band won't have a sizing grip. The band therefore can't be moved by the user.
RBBS_NOVERTThe band won't be displayed if the command bands control is displayed vertically due to the CCS_VERT style.
RBBS_CHILDEDGEThe band will be drawn with an edge at the top and bottom of the band.
RBBS_FIXEDBMPThe background bitmap of the band doesn't move when the band is resized.
For the most part, these flags are self-explanatory. Although command bands are usually displayed across the top of a window, they can be created as vertical bands and displayed down the left side of a window. In that case, the RBBS_NOVERT style allows the programmer to specify which bands won't be displayed when the command band is in a vertical orientation. Bands containing menus or wide controls are candidates for this flag because they won't be displayed correctly on vertical bands.You can fill the clrFore and clrBack fields with a color that the command band will use for the foreground and background colors when your application draws the band. These fields are used only if the RBBIM_COLORS flag is set in the mask field. These fields, along with the hbmBack field, which specifies a background bitmap for the band, are useful only if the band contains a transparent command bar. Otherwise, the command bar covers most of the area of the band, obscuring any background bitmap or special colors. I'll explain how to make a command bar transparent in the section "Configuring Individual Bands."The lpText field specifies the optional text that labels the individual band. This text is displayed at the left end of the bar immediately to the right of the gripper. The iImage field is used to specify a bitmap that will also be displayed on the left end of the bar. The iImage field is filled with an index to the list of images contained in the image list control. The text and bitmap fields take added significance when paired with the RBS_SMARTLABELS style of the command band control. When that style is specified, the text is displayed when the band is restored or maximized and the bitmap is displayed when the band is minimized. This technique is used by the H/PC Explorer on its command band control.The wID field should be set to an ID value that you use to identify the band. The band ID is important if you plan on configuring the bands after they have been created or if you think you'll be querying their state. Even if you don't plan to use band IDs in your program, it's important that each band ID be unique because the control itself uses the IDs to manage the bands. This field is checked only if the RBBIM_ID flag is set in the fMask field.The hwndChild field is used if the default command bar control in a band is replaced by another control. To replace the command bar control, the new control must first be created and the window handle of the control then placed in the hwndChild field. The hwndChild field is checked only if the RBBIM_CHILD flag is set in the fMask field.The cxMinChild and cyMinChild fields define the minimum dimensions to which a band can shrink. When you're using a control other than the default command bar, these fields are useful for defining the height and minimum width (the width when minimized) of the band. These two fields are checked only if the RBBIM_CHILDSIZE flag is set.
The cxIdeal field is used when a band is maximized by the user. If this field isn't initialized, a maximized command band stretches across the entire width of the control. By setting cxIdeal, the application can limit the maximized width of a band, which is handy if the controls on the band take up only part of the total width of the control. This field is checked only if the RBBIM_IDEALSIZE flag is set in the fMask field.The lParam field gives you a space to store an application-defined value with the band information. This field is checked only if the RBBIM_LPARAM flag is set in the fMask field. The other fields in REBARBANDINFO apply to the more flexible rebar control, not the command band control. The code below creates a command bands control, initializes an array of three REBARBANDINFO structures, and adds the bands to the control.
// Create a command bands control.
hwndCB = CommandBands_Create (hInst, hWnd, IDC_CMDBAND, RBS_SMARTLABELS |
RBS_VARHEIGHT, himl);
// Initialize common REBARBANDINFO structure fields.
for (i = 0; i < dim(rbi); i++) {
rbi[i].cbSize = sizeof (REBARBANDINFO);
rbi[i].fMask = RBBIM_ID | RBBIM_IMAGE | RBBIM_SIZE | RBBIM_STYLE;
rbi[i].fStyle = RBBS_FIXEDBMP;
rbi[i].wID = IDB_CMDBAND+i;
}
// Initialize REBARBANDINFO structure for each band.
// 1. Menu band.
rbi[0].fStyle |= RBBS_NOGRIPPER;
rbi[0].cx = 130;
rbi[0].iImage = 0;
// 2. Standard button band.
rbi[1].fMask |= RBBIM_TEXT;
rbi[1].cx = 200;
rbi[1].iImage = 1;
rbi[1].lpText = TEXT ("Std Btns");
// 3. Edit control band.
hwndChild = CreateWindow (TEXT ("edit"), TEXT ("edit ctl"),
WS_VISIBLE | WS_CHILD | WS_BORDER,
0, 0, 10, 5, hWnd, (HMENU)IDC_EDITCTL,
hInst, NULL);
rbi[2].fMask |= RBBIM_TEXT | RBBIM_STYLE | RBBIM_CHILDSIZE | RBBIM_CHILD;
rbi[2].fStyle |= RBBS_CHILDEDGE;
rbi[2].hwndChild = hwndChild;
rbi[2].cxMinChild = 0;
rbi[2].cyMinChild = 25;
rbi[2].cyChild = 55;
rbi[2].cx = 130;
rbi[2].iImage = 2;
rbi[2].lpText = TEXT ("Edit field");
// Add bands.
CommandBands_AddBands (hwndCB, hInst, 3, rbi);
The command bands control created in the preceding code has three bands, one containing a menu, one containing a set of buttons, and one containing an edit control instead of a command bar. The control is created with the RBS_SMARTLABELS and RBS_VARHEIGHT styles. The smart labels display an icon when the bar is minimized and a text label when the band isn't minimized. The RBS_VARHEIGHT style allows each line on the control to have a different height.The common fields of the REBARBANDINFO structures are then initialized in a loop. Then the remaining fields of the structures are customized for each band on the control. The third band, containing the edit control, is the most complex to initialize. This band needs more initialization since the edit control needs to be properly sized to match the standard height of the command bar controls in the other bands.The iImage field for each band is initialized using an index into an image list that was created and passed to the CommandBands_Create function. The text fields for the second and third bands are filled with labels for those bands. The first band, which contains a menu, doesn't contain a text label because there's no need to label the menu. You also use the RBBS_NOGRIPPER style for the first band so that it can't be moved around the control. This fixes the menu band at its proper place in the control.Now that we've created the bands, it's time to see how to initialize them.
Configuring Individual Bands
At this point in the process, the command bands control has been created and the individual bands have been added to the control. We have one more task, which is to configure the individual command bar controls in each band. (Actually, there's little more to configuring the command bar controls than what I've already described for command bars.)The handle to a command bar contained in a band is retrieved using
HWND CommandBands_GetCommandBar (HWND hwndCmdBands, UINT uBand);
The uBand parameter is the zero-based band index for the band containing the command bar. If you call this function when the command bands control is being initialized, the index value correlates directly with the order in which the bands were added to the control. However, once the user has a chance to drag the bands into a new order, your application must obtain this index indirectly by sending an RB_IDTOINDEX message to the command bands control, as in
nIndex = SendMessage (hwndCmdBands, RB_IDTOINDEX, ID_BAND, 0);
This message is critical for managing the bands because many of the functions and messages for the control require the band index as the method to identify the band. The problem is that the index values are fluid. As the user moves the bands around, these index values change. You can't even count on the index values being consecutive. So as a rule, never blindly use the index value without first querying the proper value by translating an ID value to an index value with RB_IDTOINDEX.Once you have the window handle to the command bar, simply add the menu or buttons to the bar using the standard command bar control functions and messages. Most of the time, you'll specify only a menu in the first bar, only buttons in the second bar, and other controls in the third and subsequent bars.The following code completes the creation process shown in the earlier code fragments. This code initializes the command bar controls in the first two bands. Since the third band has an edit control, you don't need to initialize that band. The final act necessary to complete the command band control initialization is to add the close box to the control using a call to CommandBands_AddAdornments.
// Add menu to first band.
hwndBand = CommandBands_GetCommandBar (hwndCB, 0);
CommandBar_InsertMenubar (hwndBand, hInst, ID_MENU, 0);
// Add standard buttons to second band.
hwndBand = CommandBands_GetCommandBar (hwndCB, 1);
CommandBar_AddBitmap (hwndBand, HINST_COMMCTRL, IDB_STD_SMALL_COLOR,
15, 0, 0);
CommandBar_AddButtons (hwndBand, dim(tbCBStdBtns), tbCBStdBtns);
// Add exit button to command band.
CommandBands_AddAdornments (hwndCB, hInst, 0, NULL);
Saving the Band Layout
The configurability of the command bands control presents a problem to the programmer. Users who rearrange the bands expect their customized layout to be restored the next time the application is started. This task is supposed to be made easy using the following function.
BOOL CommandBands_GetRestoreInformation (HWND hwndCmdBands,
UINT uBand, LPCOMMANDBANDSRESTOREINFO pcbr);
This function saves the positioning information from an individual band into a COMMANDBANDSRESTOREINFO structure. The function takes the handle of the command bands control and an index value for the band to be queried. The following code fragment shows how to query the information from each of the bands in a command band control.
// Get the handle of the command bands control.
hwndCB = GetDlgItem (hWnd, IDC_CMDBAND);
// Get information for each band.
for (i = 0; i < NUMBANDS; i++) {
// Get band index from ID value.
nBand = SendMessage (hwndCB, RB_IDTOINDEX, IDB_CMDBAND+i, 0);
// Initialize the size field, and get the restore information.
cbr[i].cbSize = sizeof (COMMANDBANDSRESTOREINFO);
CommandBands_GetRestoreInformation (hwndCB, nBand, &cbr[i]);
}
The preceding code uses the RB_IDTOINDEX messageChapter 8, "Files and the Registry."The restore information should be read from the registry when the application is restarted, and used when creating the command bands control.
// Restore configuration to a command band.
COMMANDBANDSRESTOREINFO cbr[NUMBANDS];
REBARBANDINFO rbi;
// Initialize size field.
rbi.cbSize = sizeof (REBARBANDINFO);
// Set only style and size fields.
rbi.fMask = RBBIM_STYLE | RBBIM_SIZE;
// Set the size and style for all bands.
for (i = 0; i < NUMBANDS; i++) {
rbi.cx = cbr[i].cxRestored;
rbi.fStyle = cbr[i].fStyle;
nBand = SendMessage (hwndCB, RB_IDTOINDEX, cbr[i].wID, 0);
SendMessage (hwndCB, RB_SETBANDINFO, nBand, (LPARAM)&rbi);
}
// Only after the size is set for all bands can the bands
// needing maximizing be maximized.
for (i = 0; i < NUMBANDS; i++) {
if (cbr[i].fMaximized) {
nBand = SendMessage (hwndCB, RB_IDTOINDEX, cbr[i].wID, 0);
SendMessage (hwndCB, RB_MAXIMIZEBAND, nBand, TRUE);
}
}
This code assumes that the command bands control has already been created in its default configuration. In a real-world application, the restore information for the size and style could be used when first creating the control. In that case, all that would remain would be to maximize the bands depending on the state of the fMaximized field in the COMMANDBANDSRESTOREINFO structure. This last step must take place only after all bands have been created and properly resized.One limitation of this system of saving and restoring the band layout is that you have no method for determining the order of the bands in the control. The band index isn't likely to provide reliable clues because after the user has rearranged the bands a few times, the indexes are neither consecutive nor in any defined order. The only way around this problem is to constrain the arrangement of the bands so that the user can't reorder the bands. You do this by setting the RBS_FIXEDORDER style. This solves your problem but doesn't help users if they want a different order. In the example program at the end of this section, I use the band index value to guess at the order. But this method isn't guaranteed to work.
Handling Command Band Messages
The command bands control needs a bit more maintenance than a command bar. The difference is that the control can change height, and thus the window containing the command bands control must monitor the control and redraw and perhaps reformat its client area when the control is resized.The command bands control sends a number of different WM_NOTIFY messages when the user rearranges the control. To monitor the height of the control, your application needs to check for an RBN_HEIGHTCHANGE notification and react accordingly. The code below does just that:
// This code is inside a WM_NOTIFY message handler.
LPNMHDR pnmh;
pnmh = (LPNMHDR)lParam;
if (pnmh->code == RBN_HEIGHTCHANGE) {
InvalidateRect (hWnd, NULL, TRUE);
}
If an RBN_HEIGHTCHANGE notification is detected, the routine simply invalidates the client area of the window forcing a WM_PAINT message. The code in the paint message then calls
UINT CommandBands_Height (HWND hwndCmdBands);
to query the height of the command bands control and subtracts this height from the client area rectangle.As with the command bar, the command bands control can be hidden and shown with a helper function:
BOOL CommandBands_Show (HWND hwndCmdBands, BOOL fShow);
The visibility state of the control can be queried using
BOOL CommandBands_IsVisible (HWND hwndCmdBands);
The CmdBand Example
The CmdBand program demonstrates a fairly complete command bands control. The example creates three bands: a fixed menu band, a band containing a number of buttons, and a band containing an edit control. Transparent command bars and a background bitmap in each band are used to create a command bands control with a background image.You can use the View menu to replace the command bands control with a simple command bar by choosing Command Bar from the View menu. You can then re-create and restore the command bands control to its last configuration by choosing Command Bands from the View menu. The code for the CmdBand program is shown in Listing 5-2.Listing 5-2: The CmdBand program
CmdBand.rc
//======================================================================
// Resource file
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
#include "windows.h"
#include "CmdBand.h" // Program-specific stuff
//----------------------------------------------------------------------
// Icons and bitmaps
//
ID_ICON ICON "cmdband.ico" // Program icon
CmdBarBmps BITMAP "cbarbmps.bmp" // Bmp used in cmdband image list
CmdBarEditBmp BITMAP "cbarbmp2.bmp" // Bmp used in cmdband image list
CmdBarBack BITMAP "backg2.bmp" // Bmp used for cmdband background
//----------------------------------------------------------------------
// Menu
//
ID_MENU MENU DISCARDABLE
BEGIN
POPUP "&File"
BEGIN
MENUITEM "E&xit", IDM_EXIT
END
POPUP "&View"
BEGIN
MENUITEM "Command Bar", IDM_VIEWCMDBAR
MENUITEM "Command Band", IDM_VIEWCMDBAND
END
POPUP "&Help"
BEGIN
MENUITEM "&About...", IDM_ABOUT
END
END
//----------------------------------------------------------------------
// About box dialog template
//
aboutbox DIALOG discardable 10, 10, 160, 40
STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | DS_CENTER |
DS_MODALFRAME
CAPTION "About"
BEGIN
ICON ID_ICON, -1, 5, 5, 10, 10
LTEXT "CmdBand - Written for the book Programming Windows CE Copyright 2003 Douglas Boling"
-1, 40, 5, 110, 30
END
CmdBand.h
//======================================================================
// Header file
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
// Returns number of elements
#define dim(x) (sizeof(x) / sizeof(x[0]))
//----------------------------------------------------------------------
// Generic defines and data types
//
struct decodeUINT { // Structure associates
UINT Code; // messages
// with a function.
LRESULT (*Fxn)(HWND, UINT, WPARAM, LPARAM);
};
struct decodeCMD { // Structure associates
UINT Code; // menu IDs with a
LRESULT (*Fxn)(HWND, WORD, HWND, WORD); // function.
};
//----------------------------------------------------------------------
// Defines used by application
//
#define IDC_CMDBAND 1 // Command band ID
#define IDC_CMDBAR 2 // Command bar ID
#define ID_ICON 10 // Icon ID
#define ID_MENU 11 // Main menu resource ID
#define IDC_EDITCTL 12
#define IDB_CMDBAND 50 // Base ID for bands
#define IDB_CMDBANDMENU 50 // Menu band ID
#define IDB_CMDBANDBTN 51 // Button band ID
#define IDB_CMDBANDEDIT 52 // Edit control band ID
// Menu item IDs
#define IDM_EXIT 100
#define IDM_VIEWCMDBAR 110
#define IDM_VIEWCMDBAND 111
#define IDM_ABOUT 120
#define NUMBANDS 3
//----------------------------------------------------------------------
// Function prototypes
//
int CreateCommandBand (HWND hWnd, BOOL fFirst);
int DestroyCommandBand (HWND hWnd);
HWND InitInstance (HINSTANCE, LPWSTR, int);
int TermInstance (HINSTANCE, int);
// Window procedures
LRESULT CALLBACK MainWndProc (HWND, UINT, WPARAM, LPARAM);
// Message handlers
LRESULT DoCreateMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoPaintMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoNotifyMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoCommandMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoDestroyMain (HWND, UINT, WPARAM, LPARAM);
// Command functions
LPARAM DoMainCommandViewCmdBar (HWND, WORD, HWND, WORD);
LPARAM DoMainCommandVCmdBand (HWND, WORD, HWND, WORD);
LPARAM DoMainCommandExit (HWND, WORD, HWND, WORD);
LPARAM DoMainCommandAbout (HWND, WORD, HWND, WORD);
// Dialog procedures
BOOL CALLBACK AboutDlgProc (HWND, UINT, WPARAM, LPARAM);
CmdBand.cpp
//======================================================================
// CmdBand - Dialog box demonstration
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
#include <windows.h> // For all that Windows stuff
#include <commctrl.h> // Command bar includes
#include "CmdBand.h" // Program-specific stuff
//----------------------------------------------------------------------
// Global data
//
const TCHAR szAppName[] = TEXT ("CmdBand");
HINSTANCE hInst; // Program instance handle
// Message dispatch table for MainWindowProc
const struct decodeUINT MainMessages[] = {
WM_CREATE, DoCreateMain,
WM_PAINT, DoPaintMain,
WM_NOTIFY, DoNotifyMain,
WM_COMMAND, DoCommandMain,
WM_DESTROY, DoDestroyMain,
};
// Command message dispatch for MainWindowProc
const struct decodeCMD MainCommandItems[] = {
IDM_VIEWCMDBAR, DoMainCommandViewCmdBar,
IDM_VIEWCMDBAND, DoMainCommandVCmdBand,
IDM_EXIT, DoMainCommandExit,
IDM_ABOUT, DoMainCommandAbout,
};
// Command band button initialization structure
const TBBUTTON tbCBStdBtns[] = {
// BitmapIndex Command State Style UserData String
{STD_FILENEW, 210, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0},
{STD_FILEOPEN, 211, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0},
{STD_filesAVE, 212, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0},
{0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0},
{STD_CUT, 213, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0},
{STD_COPY, 214, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0},
{STD_PASTE, 215, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0},
{0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0},
{STD_PROPERTIES, 216, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0},
};
// Command bar initialization structure
const TBBUTTON tbCBViewBtns[] = {
// BitmapIndex Command State Style UserData String
{0, 0, 0,
TBSTYLE_SEP, 0, 0},
{VIEW_LARGEICONS, 210, TBSTATE_ENABLED | TBSTATE_CHECKED,
TBSTYLE_CHECKGROUP, 0, 0},
{VIEW_SMALLICONS, 211, TBSTATE_ENABLED,
TBSTYLE_CHECKGROUP, 0, 0},
{VIEW_LIST, 212, TBSTATE_ENABLED,
TBSTYLE_CHECKGROUP, 0, 0},
{VIEW_DETAILS, 213, TBSTATE_ENABLED,
TBSTYLE_CHECKGROUP, 0, 0},
{0, 0, 0, TBSTYLE_SEP, 0, 0},
{VIEW_SORTNAME, 214, TBSTATE_ENABLED | TBSTATE_CHECKED,
TBSTYLE_CHECKGROUP, 0, 0},
{VIEW_SORTTYPE, 215, TBSTATE_ENABLED,
TBSTYLE_CHECKGROUP, 0, 0},
{VIEW_SORTSIZE, 216, TBSTATE_ENABLED,
TBSTYLE_CHECKGROUP, 0, 0},
{VIEW_SORTDATE, 217, TBSTATE_ENABLED,
TBSTYLE_CHECKGROUP, 0, 0}
};
// Array that stores the band configuration
COMMANDBANDSRESTOREINFO cbr[NUMBANDS];
INT nBandOrder[NUMBANDS];
//======================================================================
// Program entry point
//
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPWSTR lpCmdLine, int nCmdShow) {
HWND hwndMain;
MSG msg;
// Initialize application.
hwndMain = InitInstance (hInstance, lpCmdLine, nCmdShow);
if (hwndMain == 0)
return 0x10;
// Application message loop
while (GetMessage (&msg, NULL, 0, 0)) {
TranslateMessage (&msg);
DispatchMessage (&msg);
}
// Instance cleanup
return TermInstance (hInstance, msg.wParam);
}
//----------------------------------------------------------------------
// InitInstance - Instance initialization
//
HWND InitInstance (HINSTANCE hInstance, LPWSTR lpCmdLine, int nCmdShow){
HWND hWnd;
WNDCLASS wc;
INITCOMMONCONTROLSEX icex;
// Save program instance handle in global variable.
hInst = hInstance;
#if defined(WIN32_PLATFORM_PSPC)
// If Pocket PC, allow only one instance of the application.
hWnd = FindWindow (szAppName, NULL);
if (hWnd) {
SetForegroundWindow ((HWND)(((DWORD)hWnd) | 0x01));
return 0;
}
#endif
// Register application main window class.
wc.style = 0; // Window style
wc.lpfnWndProc = MainWndProc; // Callback function
wc.cbClsExtra = 0; // Extra class data
wc.cbWndExtra = 0; // Extra window data
wc.hInstance = hInstance; // Owner handle
wc.hIcon = NULL, // Application icon
wc.hCursor = LoadCursor (NULL, IDC_ARROW);// Default cursor
wc.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
wc.lpszMenuName = NULL; // Menu name
wc.lpszClassName = szAppName; // Window class name
if (RegisterClass (&wc) == 0) return 0;
// Load the command bar common control class.
icex.dwSize = sizeof (INITCOMMONCONTROLSEX);
icex.dwICC = ICC_COOL_CLASSES;
InitCommonControlsEx (&icex);
// Create main window.
hWnd = CreateWindow (szAppName, TEXT ("CmdBand Demo"), WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
// Return fail code if window not created.
if (!IsWindow (hWnd)) return 0;
// Standard show and update calls
ShowWindow (hWnd, nCmdShow);
UpdateWindow (hWnd);
return hWnd;
}
//----------------------------------------------------------------------
// TermInstance - Program cleanup
//
int TermInstance (HINSTANCE hInstance, int nDefRC) {
return nDefRC;
}
//======================================================================
// Message handling procedures for MainWindow
//----------------------------------------------------------------------
// MainWndProc - Callback function for application window
//
LRESULT CALLBACK MainWndProc (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
INT i;
//
// Search message list to see if we need to handle this
// message. If in list, call procedure.
//
for (i = 0; i < dim(MainMessages); i++) {
if (wMsg == MainMessages[i].Code)
return (*MainMessages[i].Fxn)(hWnd, wMsg, wParam, lParam);
}
return DefWindowProc (hWnd, wMsg, wParam, lParam);
}
//----------------------------------------------------------------------
// DoCreateMain - Process WM_CREATE message for window.
//
LRESULT DoCreateMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
CreateCommandBand (hWnd, TRUE);
return 0;
}
//----------------------------------------------------------------------
// DoPaintMain - Process WM_PAINT message for window.
//
LRESULT DoPaintMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
PAINTSTRUCT ps;
HWND hwndCB;
RECT rect;
HDC hdc;
POINT ptArray[2];
// Adjust the size of the client rect to take into account
// the command bar or command bands height.
GetClientRect (hWnd, &rect);
if (hwndCB = GetDlgItem (hWnd, IDC_CMDBAND))
rect.top += CommandBands_Height (hwndCB);
else
rect.top += CommandBar_Height (GetDlgItem (hWnd, IDC_CMDBAR));
hdc = BeginPaint (hWnd, &ps);
ptArray[0].x = rect.left;
ptArray[0].y = rect.top;
ptArray[1].x = rect.right;
ptArray[1].y = rect.bottom;
Polyline (hdc, ptArray, 2);
ptArray[0].x = rect.right;
ptArray[1].x = rect.left;
Polyline (hdc, ptArray, 2);
EndPaint (hWnd, &ps);
return 0;
}
//----------------------------------------------------------------------
// DoCommandMain - Process WM_COMMAND message for window.
//
LRESULT DoCommandMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
WORD idItem, wNotifyCode;
HWND hwndCtl;
INT i;
// Parse the parameters.
idItem = (WORD) LOWORD (wParam);
wNotifyCode = (WORD) HIWORD (wParam);
hwndCtl = (HWND) lParam;
// Call routine to handle control message.
for (i = 0; i < dim(MainCommandItems); i++) {
if (idItem == MainCommandItems[i].Code)
return (*MainCommandItems[i].Fxn)(hWnd, idItem, hwndCtl,
wNotifyCode);
}
return 0;
}
//----------------------------------------------------------------------
// DoNotifyMain - Process WM_NOTIFY message for window.
//
LRESULT DoNotifyMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
LPNMHDR pnmh;
// Parse the parameters.
pnmh = (LPNMHDR)lParam;
if (pnmh->code == RBN_HEIGHTCHANGE) {
InvalidateRect (hWnd, NULL, TRUE);
}
return 0;
}
//----------------------------------------------------------------------
// DoDestroyMain - Process WM_DESTROY message for window.
//
LRESULT DoDestroyMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
PostQuitMessage (0);
return 0;
}
//======================================================================
// Command handler routines
//----------------------------------------------------------------------
// DoMainCommandExit - Process Program Exit command.
//
LPARAM DoMainCommandExit (HWND hWnd, WORD idItem, HWND hwndCtl,
WORD wNotifyCode) {
SendMessage (hWnd, WM_CLOSE, 0, 0);
return 0;
}
//----------------------------------------------------------------------
// DoMainCommandVCmdBarStd - Process View | Std Command bar command.
//
LPARAM DoMainCommandViewCmdBar (HWND hWnd, WORD idItem, HWND hwndCtl,
WORD wNotifyCode) {
HWND hwndCB;
hwndCB = GetDlgItem (hWnd, IDC_CMDBAND);
if (hwndCB)
DestroyCommandBand (hWnd);
else
return 0;
// Create a minimal command bar that has only a menu and
// an exit button.
hwndCB = CommandBar_Create (hInst, hWnd, IDC_CMDBAR);
// Insert the menu.
CommandBar_InsertMenubar (hwndCB, hInst, ID_MENU, 0);
// Add exit button to command bar.
CommandBar_AddAdornments (hwndCB, 0, 0);
InvalidateRect (hWnd, NULL, TRUE);
return 0;
}
//----------------------------------------------------------------------
// DoMainCommandVCmdBand - Process View | Command band command.
//
LPARAM DoMainCommandVCmdBand (HWND hWnd, WORD idItem, HWND hwndCtl,
WORD wNotifyCode) {
HWND hwndCB;
hwndCB = GetDlgItem (hWnd, IDC_CMDBAR);
if (hwndCB)
CommandBar_Destroy (hwndCB);
else
return 0;
CreateCommandBand (hWnd, FALSE);
InvalidateRect (hWnd, NULL, TRUE);
return 0;
}
//----------------------------------------------------------------------
// DoMainCommandAbout - Process the Help | About menu command.
//
LPARAM DoMainCommandAbout(HWND hWnd, WORD idItem, HWND hwndCtl,
WORD wNotifyCode) {
// Use DialogBox to create modal dialog box.
DialogBox (hInst, TEXT ("aboutbox"), hWnd, AboutDlgProc);
return 0;
}
//======================================================================
// About Dialog procedure
//
BOOL CALLBACK AboutDlgProc (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
switch (wMsg) {
case WM_COMMAND:
switch (LOWORD (wParam)) {
case IDOK:
case IDCANCEL:
EndDialog (hWnd, 0);
return TRUE;
}
break;
}
return FALSE;
}
//----------------------------------------------------------------------
// DestroyCommandBand - Destroy command band control after saving
// the current configuration.
//
int DestroyCommandBand (HWND hWnd) {
HWND hwndCB;
INT i, nBand, nMaxBand = 0;
hwndCB = GetDlgItem (hWnd, IDC_CMDBAND);
for (i = 0; i < NUMBANDS; i++) {
// Get band index from ID value.
nBand = SendMessage (hwndCB, RB_IDTOINDEX, IDB_CMDBAND+i, 0);
// Save the band number to save order of bands.
nBandOrder[i] = nBand;
// Get the restore information.
cbr[i].cbSize = sizeof (COMMANDBANDSRESTOREINFO);
CommandBands_GetRestoreInformation (hwndCB, nBand, &cbr[i]);
}
DestroyWindow (hwndCB);
return 0;
}
//----------------------------------------------------------------------
// CreateCommandBand - Create a formatted command band control.
//
int CreateCommandBand (HWND hWnd, BOOL fFirst) {
HWND hwndCB, hwndBand, hwndChild;
INT i, nBand, nBtnIndex, nEditIndex;
LONG lStyle;
HBITMAP hBmp;
HIMAGELIST himl;
REBARBANDINFO rbi[NUMBANDS];
// Create image list control for bitmaps for minimized bands.
himl = ImageList_Create (16, 16, ILC_COLOR, 3, 0);
// Load first two images from one bitmap.
hBmp = LoadBitmap (hInst, TEXT ("CmdBarBmps"));
ImageList_Add (himl, hBmp, NULL);
DeleteObject (hBmp);
// Load third image as a single bitmap.
hBmp = LoadBitmap (hInst, TEXT ("CmdBarEditBmp"));
ImageList_Add (himl, hBmp, NULL);
DeleteObject (hBmp);
// Create a command band.
hwndCB = CommandBands_Create (hInst, hWnd, IDC_CMDBAND,
RBS_SMARTLABELS |
RBS_AUTOSIZE | RBS_VARHEIGHT, himl);
// Load bitmap used as background for command bar.
hBmp = LoadBitmap (hInst, TEXT ("CmdBarBack"));
// Initialize common REBARBANDINFO structure fields.
for (i = 0; i < dim(rbi); i++) {
rbi[i].cbSize = sizeof (REBARBANDINFO);
rbi[i].fMask = RBBIM_ID | RBBIM_IMAGE | RBBIM_SIZE |
RBBIM_BACKGROUND | RBBIM_STYLE;
rbi[i].wID = IDB_CMDBAND+i;
rbi[i].hbmBack = hBmp;
}
// If first time, initialize the restore structure since it is
// used to initialize the band size and style fields.
if (fFirst) {
nBtnIndex = 1;
nEditIndex = 2;
cbr[0].cxRestored = 130;
cbr[1].cxRestored = 210;
cbr[1].fStyle = RBBS_FIXEDBMP;
cbr[2].cxRestored = 130;
cbr[2].fStyle = RBBS_FIXEDBMP | RBBS_CHILDEDGE;
} else {
// If not first time, set order of bands depending on
// the last order.
if (nBandOrder[1] < nBandOrder[2]) {
nBtnIndex = 1;
nEditIndex = 2;
} else {
nBtnIndex = 2;
nEditIndex = 1;
}
}
// Initialize REBARBANDINFO structure for each band.
// 1. Menu band
rbi[0].fStyle = RBBS_FIXEDBMP | RBBS_NOGRIPPER;
rbi[0].cx = cbr[0].cxRestored;
rbi[0].iImage = 0;
// 2. Standard button band
rbi[nBtnIndex].fMask |= RBBIM_TEXT;
rbi[nBtnIndex].iImage = 1;
rbi[nBtnIndex].lpText = TEXT ("Std Btns");
// The next two parameters are initialized from saved data.
rbi[nBtnIndex].cx = cbr[1].cxRestored;
rbi[nBtnIndex].fStyle = cbr[1].fStyle;
// 3. Edit control band
hwndChild = CreateWindow (TEXT ("edit"), TEXT ("edit ctl"),
WS_VISIBLE | WS_CHILD | ES_MULTILINE | WS_BORDER,
0, 0, 10, 5, hWnd, (HMENU)IDC_EDITCTL, hInst, NULL);
rbi[nEditIndex].fMask |= RBBIM_TEXT | RBBIM_STYLE |
RBBIM_CHILDSIZE | RBBIM_CHILD;
rbi[nEditIndex].hwndChild = hwndChild;
rbi[nEditIndex].cxMinChild = 0;
rbi[nEditIndex].cyMinChild = 23;
rbi[nEditIndex].cyChild = 55;
rbi[nEditIndex].iImage = 2;
rbi[nEditIndex].lpText = TEXT ("Edit field");
// The next two parameters are initialized from saved data.
rbi[nEditIndex].cx = cbr[2].cxRestored;
rbi[nEditIndex].fStyle = cbr[2].fStyle;
// Add bands.
CommandBands_AddBands (hwndCB, hInst, 3, rbi);
// Add menu to first band.
hwndBand = CommandBands_GetCommandBar (hwndCB, 0);
CommandBar_InsertMenubar (hwndBand, hInst, ID_MENU, 0);
// Add standard buttons to second band.
hwndBand = CommandBands_GetCommandBar (hwndCB, nBtnIndex);
// Insert buttons
CommandBar_AddBitmap (hwndBand, HINST_COMMCTRL, IDB_STD_SMALL_COLOR,
16, 0, 0);
CommandBar_AddButtons (hwndBand, dim(tbCBStdBtns), tbCBStdBtns);
// Modify the style flags of each command bar to make transparent.
for (i = 0; i < NUMBANDS; i++) {
hwndBand = CommandBands_GetCommandBar (hwndCB, i);
lStyle = SendMessage (hwndBand, TB_GETSTYLE, 0, 0);
lStyle |= TBSTYLE_TRANSPARENT;
SendMessage (hwndBand, TB_SETSTYLE, 0, lStyle);
}
// If not the first time the command band has been created, restore
// the user's last configuration.
if (!fFirst) {
for (i = 0; i < NUMBANDS; i++) {
if (cbr[i].fMaximized) {
nBand = SendMessage (hwndCB, RB_IDTOINDEX,
cbr[i].wID, 0);
SendMessage (hwndCB, RB_MAXIMIZEBAND, nBand, TRUE);
}
}
}
// Add exit button to command band.
CommandBands_AddAdornments (hwndCB, hInst, 0, NULL);
return 0;
}
CmdBand creates the command band in the CreateCommandBand routine. This routine is initially called in DoCreateMain and later in the DoMain-CommandVCmdBand menu handler. The program creates the command bands control using the RBS_SMARTLABELS style along with an image list and text labels to identify each band when it's minimized and when it's restored or maximized. An image list is created and initialized with the bitmaps that are used when the bands are minimized.The array of REBARBANDINFO structures is initialized to define each of the three bands. If the control has previously been destroyed, data from the COMMANDBANDSRESTOREINFO structure is used to initialize the style and cx fields. The CreateCommandBand routine also makes a guess at the order of the button and edit bands by looking at the band indexes saved when the control was last destroyed. While this method isn't completely reliable for determining the previous order of the bands, it gives you a good estimate.
When the command bands control is created, the command bars in each band are also modified to set the TBS_TRANSPARENT style. This process, along with a background bitmap defined for each band, demonstrates how you can use a background bitmap to make the command bands control have just the right look.When CmdBand replaces the command bands control with a command bar, the application first calls the DestroyCommandBand function to save the current configuration and then destroy the command bands control. This function uses the CommandBands_GetRestoreInformation to query the size and style of each of the bands. The function also saves the band index for each band to supply the data for the guess on the current order of the button and edit bands. The first band, the menu band, is fixed with the RBBS_NOGRIPPER style, so there's no issue as to its position.
The Menu Bar
The menu bar control was introduced in the Pocket PC 2000. In look, the menu bar differs from the command bar in that it sits on the bottom of the window, not the top. To the programmer, however, the menu bar has a vastly different programming interface. Because of the popularity of the Pocket PC and the desire of OEMs to be able to create embedded systems that are software compatible with the Pocket PC, the menu bar is now distributed with the embedded versions of Windows CE starting with Windows CE .NET 4.2.The menu bar control is a subtly complex control that does not lend itself to manual programming. The designers of the menu bar control seem to have intended that most programming and resource generation for the menu bar control would be done through code wizards and the resource editor. Although this is the way most Windows programmers code, it's still important to know how the menu bar control actually works, especially for situations in which the tools aren't quite up to the job. For this reason, I'm going to present the menu bar at the basic API level in this section. I can therefore present exactly what the control is looking for, especially in the way of resources. For later examples in the book, when I use the menu bar in examples, I'll use the code wizards to generate the menu bar menus.Before I jump into programming the menu bar, I'd like to say a few words about how the control is designed. The menu bar control differs in a number of ways from the command bar control used on other Windows CE systems. First, the menu is not managed as a single unit on the menu bar. Instead, while the menu is specified as a single resource, it is managed by the menu bar as a series of separate submenus. Each submenu is displayed as a properly positioned pop-up menu when a particular button on the menu bar is tapped. So in this sense, the menu bar is more like a toolbar than its cousin the command bar.
A user sees little difference between a menu bar and a command bar because the menu buttons are positioned as expected—next to each other on the far left side of the bar. However, to the programmer, understanding this difference is the key to understanding how to manage and manipulate the menu bar.Another difference is that unlike the command bar, the menu bar is not a true child of the window that creates it. The control itself is a pop-up window created by the system and placed at the bottom of the screen. The window that creates a menu bar can accidentally obscure the menu bar by covering it. Alternatively, parts of a menu bar can be drawn on top of its owner. To avoid this, the application must size its window to leave room for the menu bar on the desktop. This dance with the menu bar is the reason why applications that use the menu bar control manually resize their main windows.Figure 5-5 shows a menu bar on a Pocket PC, while Figure 5-6 shows the same application running on an embedded system. Subtle differences exist between the look of the two menu bars that should be discussed.

Figure 5-5: A menu bar on a Pocket PC device

Figure 5-6: A menu bar on an embedded system
The menu bar on the Pocket PC contains the soft input panel (SIP) button on the far left of the control. On the embedded device, the SIP button is on the taskbar, not on the menu bar. In place of the SIP button, the menu bar on the embedded device has a Close button, in contrast with the Pocket PC, which has a smart Minimize button on the Navigation bar across the top of the screen. Finally, even though the very same application, with the same menu bar resource, was used to create both menu bars, the Pocket PC version has a menu named New on the far left of the bar. The New menu is an extension of the shell, which this embedded device doesn't support. Because of this lack of support in the shell, the menu bar doesn't create a New menu, even though the resource used to create the menu bar specifies one.Another menu bar difference between the Pocket PC and embedded systems is the height of the menu bar. Since the height of the menu bar can be different on different systems, determining the height of the menu bar must be done programmatically. Older Pocket PC applications, including those in earlier versions of this book, made the then-valid assumption that the menu bar was 26 pixels high. Now that the menu bar control appears on a variety of systems, that assumption is no longer valid. One easy way to compute the height of the menu bar is to call GetWindowRect on the handle of the menu bar. In the following code, the height is computed just after the menu bar is created in the WM_CREATE message handler.
RECT rectMB;
GetWindowRect (hwndMenuBar, &rectMB);
nMBHeight = (rectMB.bottom - rectMB.top);
Creating a Menu Bar
To create a menu bar, call
BOOL SHCreateMenuBar (SHMENUBARINFO *pmb);
The only parameter is the address of an SHMENUBARINFO structure, which is defined as
typedef struct tagSHMENUBARINFO {
DWORD cbSize;
HWND hwndParent;
DWORD dwFlags;
UINT nToolBarId;
HINSTANCE hInstRes;
int nBmpId;
int cBmpImages;
HWND hwndMB;
COLORREF clrBk;
} SHMENUBARINFO;
The cbSize field must be filled with the size of the SHMENUBARINFO structure. The second field, hwndParent, should be set to the window that is creating the menu bar. The dwFlags field can be set to a combination of three flags:
SHCMBF_EMPTYBARUsed to create a menu bar with no menu
SHCMBF_HIDDENCreates a menu bar that is initially hidden
SHCMBF_HIDESIPBUTTONCreates the menu bar without a SIP button on the right-hand side of the bar
SHCMBF_COLORBKSpecifies that the clrBk field contains a valid color to use when filling the menu bar background
SHCMBF_HMENUSpecifies that the resource is a menu resource, not a menu bar resource
Unless you specify the SHCMBF_EMPTYBAR flag, you must set the nToolBarId field to the resource that describes the menu and button structure of the menu bar. Unless the SHCMBF_HMENU flag is used, this resource is not a simple menu resource. It is a combination of a generic resource data block and a menu resource that together describe the menus and the positions of the buttons on the menu bar. I'll describe this resource later in this section.The next field, hInstRes, should be set to the instance handle of the module that contains the menu bar resource. The next two fields, nBmpId and cBmpImages, describe the bitmap images that can be used to define the look of buttons on the menu bar. If the menu bar is to have graphical buttons, you can set the field nBmpId to a bitmap resource ID. This bitmap should be 16 pixels in height and each image in the bitmap should be 16 pixels wide. Thus if the bitmap has three images, it should be 48 pixels wide by 16 pixels high. The cBmpImages field should be set to the number of images in the bitmap. For you graphic artists out there, consult the latest application guidelines for instructions regarding the look the graphics should take to blend in with the other parts of the shell.The SHCreateMenuBar function returns TRUE if the menu bar was successfully created. If so, the hwndMB field of SHMENUBARINFO will contain the handle of the menu bar. You need to save this window handle since there is no other way to determine the menu bar handle after it has been created.
Menu Bar Resources
As I mentioned earlier, the menu bar acts like a toolbar control in many ways. Some differences between these objects are apparent when you look at the resources that the menu bar uses. A simple menu bar might resemble the one shown in Figure 5-7.

Figure 5-7: A simple menu bar with the Edit menu open
When a menu bar is created, the nToolBarId field of SHMENUBARINFO is appropriately named since the resource identified by nToolBarID is not a menu resource but a custom resource used by the menu bar control. To create the menu bar shown in Figure 5-7, the resource editor created the following text in the .RC file:
///////////////////////////////////////////////////////////////////////////
// Data
//
IDM_MENU SHMENUBAR MOVEABLE PURE
BEGIN
IDM_MENU, 4,
I_IMAGENONE, IDM_SHAREDNEW, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE, IDS_SHNEW,
0, NOMENU,
I_IMAGENONE, ID_EDIT, TBSTATE_ENABLED,
TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_CAP_EDIT, 0, 0,
I_IMAGENONE, IDM_MAIN_COMMAND1, TBSTATE_ENABLED,
TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_HELP, 0, 1,
0, ID_BACKBTN, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE, 0, ID_BACKBTN, 2,
END
///////////////////////////////////////////////////////////////////////////
// Menu bar
//
IDM_MENU MENU DISCARDABLE
BEGIN
POPUP "Edit"
BEGIN
MENUITEM "Cut", ID_EDIT_CUT
MENUITEM "Copy", ID_EDIT_COPY
MENUITEM "Paste", ID_EDIT_PASTE
END
POPUP "Tools"
BEGIN
MENUITEM "About", IDM_HELP_ABOUT
MENUITEM "Options", ID_TOOLS_OPTIONS
END
END
Most times, you won't need to know exactly what the resource editor is placing in the resource. However, you should know the format, both to ease updating applications for using a menu bar and when writing to devices for which the resource editor doesn't create menu bar controls. The resource is essentially a description of the buttons on a toolbar. The following code offers a more formatted view of the preceding data:
IDM_MENU SHMENUBAR MOVEABLE PURE
BEGIN
IDM_MENU, 4,
I_IMAGENONE, IDM_SHAREDNEW, TBSTATE_ENABLED,
TBSTYLE_AUTOSIZE, IDS_SHNEW, 0, NOMENU,
I_IMAGENONE, ID_EDIT, TBSTATE_ENABLED,
TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_CAP_EDIT, 0, 0,
I_IMAGENONE, IDM_MAIN_COMMAND1, TBSTATE_ENABLED,
TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_HELP, 0, 1,
0, ID_BACKBTN, TBSTATE_ENABLED,
TBSTYLE_AUTOSIZE, 0, ID_BACKBTN, 2,
END
The first line in the resource identifies the resource ID, IDM_MENU, its resource type, SHMENUBAR, and the resource flags, MOVEABLE and PURE. The IDM_MENU is the ID that is passed to SHCreateMenuBar in the SHMENUBARINFO structure. The resource type SHMENUBAR is actually defined in the wizard as RCDATA, which the resource compiler understands as a simple block of resource data used by an application. This is important information, since SHMENUBAR isn't defined by the Pocket PC include files; it is included only if you use the Pocket PC AppWizard to create a menu bar resource. So, for nonwizard-generated resource files that define menu bars, you might need to add the following line to your .RC file:
#define SHMENUBAR RCDATA
The first line of the data inside the BEGIN / END block is shown here:
IDM_MENU, 4,
This line defines the menu resource that will be used to create the individual pop-up menus displayed from the menu bar. The number 4 indicates the number of items in the remaining SHMENUBAR resource. Each item represents either a menu pop-up or a button on the menu bar.The formatted view of the preceding resource breaks each item's resource description into two lines because of this book's format. Let's look at the last item from the resource, which describes the Back button item.
0, ID_BACKBTN, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE, 0, ID_BACKBTN, 2,
Broken vertically to insert comments, the resource looks like this:
0, // Bitmap index
ID_BACKBTN, // WM_COMMAND ID value
TBSTATE_ENABLED, // Initial state of "button"
TBSTYLE_AUTOSIZE, // Style of "button"
0, // String resource ID of text label
ID_BACKBTN, // String resource ID of tooltip
2, // Submenu index
The first field contains the index into the bitmap array for this item's image on the menu bar. For items without bitmaps, set this field to I_IMAGENONE. In the preceding example, the image used is the first image in the bitmap. The next field contains the ID value for the item. For buttons, this is the ID value that will be sent to the parent window in a WM_COMMAND message when the button is tapped. For menus, you can use this ID to identify the submenu when querying the submenu handle. Because the shell uses its own set of IDs in the menu bar, applications shouldn't use values below 100. This rule applies to values for menu and button IDs as well as string resource IDs.The menu bar uses two predefined menu item IDs: IDM_SHAREDNEW and IDM_SHAREDNEWDEFAULT. Both of these IDs will cause a New menu item to be added that displays the menu items registered by other applications. The difference between these two IDs is that IDM_SHAREDNEWDEFAULT displays the new menu with a simple tap of the menu item. Using IDM_SHAREDNEW turns the New menu into a button with an adjoining down arrow. Tapping on the New button sends a WM_COMMAND message to the parent indicating that a new document should be created. Tapping on the adjoining up arrow displays the new menu itself. For non–Pocket PC systems, the New menu is displayed on the menu bar only if the shell for the system provides New menu support; otherwise, the predefined new menu item IDs are ignored.The next two fields in the resource are the initial state of the button, or root menu item, and its style. This state is described in toolbar state flags such as TBSTATE_ENABLED and TBSTATE_CHECKED. For menus, this state is almost always TBSTATE_ENABLED. The style field is also specified in toolbar flags with styles such as TBSTYLE_BUTTON for a button, or TBSTYLE_DROPDOWN, which is used for menu items. Items that have text instead of a bitmap—as well as items that include a bitmap—will also typically have the TBSTYLE_AUTOSIZE flag set to tell the menu bar to size the button to fit the text of the menu item.
The next field is set to the resource ID of a string resource used to label the item. This text is used alongside any bitmap image specified in the first field of the item. In our example, the item is a simple bitmap button, so no string resource is specified. For menu items, this is the string resource—not the submenu name specified in the menu resource—that will label the menu. You can use seven predefined string IDs if needed. They are defined with self-explanatory constants in the Aygshell.h file:
#define IDS_SHNEW 1
#define IDS_SHEDIT 2
#define IDS_SHTOOLS 3
#define IDS_SHVIEW 4
#define IDS_SHFILE 5
#define IDS_SHGO 6
#define IDS_SHFAVORITES 7
#define IDS_SHOPEN 8
If you need a different text label, your application must define the text as a string resource and pass that ID in this field. Following the label field is a tool tip field. You must also fill this field with the ID of a string resource.The final field specifies the submenu that can pop up if the user taps the item. This submenu value is valid only if the style field contains TBSTYLE_DROPDOWN, which indicates the item has a menu attached. This value represents the index into the menu resource of the submenus. The example presented earlier in this section has two submenus: Edit, with Cut, Copy, and Paste items; and Tools, with About and Options items. The text that's displayed on the button is the string from the bar resource, not the string in the menu resource. For example, the menu resource could be modified as shown in the following code without changing the text on the menu bar.
///////////////////////////////////////////////////////////////////////////
// Menu bar
//
IDM_MENU MENU DISCARDABLE
BEGIN
POPUP "Cat"
BEGIN
MENUITEM "Cut", ID_EDIT_CUT
MENUITEM "Copy", ID_EDIT_COPY
MENUITEM "Paste", ID_EDIT_PASTE
END
POPUP "Dog"
BEGIN
MENUITEM "About", IDM_HELP_ABOUT
MENUITEM "Options", ID_TOOLS_OPTIONS
END
END
Notice that the root menu names are now Cat and Dog, not Edit and Options. Because the menu bar takes the names from the menu bar item and not the menu resource, the change has no effect on the application.This relatively long-winded explanation of the menu bar resource is meant as foundation material. Only on the rarest of occasions should you really have to manually tweak this resource. However, this knowledge can still be quite handy.
Working with a Menu Bar
Once you've created the menu bar, you still might need to configure it. Although the menu bar looks different from a command bar, it is built upon the same toolbar foundation. So while you can't expect a menu bar to always act like a command bar, you can use some of the command bar functions and toolbar messages. For example, one handy feature of the common controls is that they contain a series of bitmaps for commonly used toolbar buttons. Instead of creating these images yourself—and thereby possibly creating a non-standard image—you can use the system-provided images for actions such as cut, copy, and paste.Using the Common Control Bitmaps in a Menu BarTo use the system-provided bitmaps, simply add them to the menu bar as you would add them to a command bar. These images are added to the menu bar after the addition of any bitmap specified in the SHMENUBARINFO structure when the menu bar was created. So, if you had a bitmap of three images, and you added the standard set of images, the Cut bitmap image would be specified as STD_CUT+3. In the following code fragment, the menu bar is created and the set of standard images is added to the bar.
if (!SHCreateMenuBar(&mbi))
return NULL;
CommandBar_AddBitmap (mbi.hwndMB, HINST_COMMCTRL,
IDB_STD_SMALL_COLOR,
STD_PRINT, 16, 16);
The simplest way to use these images is to specify the correct index in the button item in the menu bar resource. Remember that the first field in the menu bar item resource is the index to the bitmap image. Just set that bitmap index to point to the proper bitmap for the button.Working with Menu Bar MenusSometimes applications need to manipulate menus by setting or clearing check marks or by enabling or disabling items. The standard set of menu functions (CheckMenuItem, for example) works as expected on menus maintained by a menu bar. The trick is to get the handle of the menu so that you can modify its items. The menu bar supports three messages you can use to get and set menu handles: SHCMBM_GETMENU, SHCMBM_GETSUBMENU, and SHCMBM_SETSUBMENU. The messages SHCMBM_GETMENU and SHCMBM_GETSUBMENU can be sent to the menu bar to query the menu handle or a specific submenu. The following line shows how to query the root menu handle using SHCMBM_GETMENU.
hMenu = (HMENU)SendMessage (hwndMenuBar, SHCMBM_GETMENU, 0, 0);
You can then use this menu handle to modify any of the menu items that the menu bar might display. To query a submenu attached to a specific menu bar item, use SHCMBM_GETSUBMENU, as in
hSubMenu = (HMENU)SendMessage (hwndMenuBar, SHCMBM_GETSUBMENU, 0,
ID_VIEWMENU);
The lParam value is set to the ID of a specific button on the menu bar—in this example, it's the menu handle attached to the button with the ID_VIEWMENU ID value.To change the menu of a particular button on the menu bar, you can use SHCMBM_SETSUBMENU with wParam set to the ID of the button and lParam set to the new menu handle, as in
hOldMenu = (HMENU)SendMessage (hwndMenuBar, SHCMBM_SETSUBMENU,
ID_VIEWMENU, (LPARAM)hNewMenu);
The MenuBar Example
The MenuBar example demonstrates a number of the menu bar techniques described in the preceding section. The example switches between two menu bars. Each menu bar has its own set of buttons, each with a different set of styles. The example displays all WM_COMMAND and WM_NOTIFY messages in a list box in its main window. This list box allows you to see what the application sees in terms of notifications and command messages.
When run on systems that support New menus, the menu bars have a unique New menu for each bar, one with a shared New menu and another with a simple New menu. In addition, the New menu is also extended with a custom menu item. When used with the NewMenuX example in Chapter 17, MenuBar demonstrates how to intercept permanent Pocket PC New menu item selections by fielding the NMN_INVOKECOMMAND notification and asking the user whether Calc should be launched.Figure 5-8 shows a Pocket PC running MenuBar. Notice that the three rightmost buttons on the menu bar use the predefined Cut, Copy, and Paste bitmap images.

Figure 5-8: The MenuBar example uses standard common control bitmap images.
Listing 5-3 contains the source code for MenuBar. As usual, it is divided into

MenuBar.rc
//======================================================================
// Resource file
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
#include "windows.h" // Windows stuff
#include "commctrl.h" // Common ctl stuff
#include "aygshell.h" // Pocket PC stuff
#include "MenuBar.h" // Program-specific stuff
//----------------------------------------------------------------------
// Icons and bitmaps
//
ID_ICON ICON "MenuBar.ico" // Program icon
ID_TOOLBMPS BITMAP DISCARDABLE "btns.bmp"
//----------------------------------------------------------------------
// Accelerator keys
//
ID_ACCEL ACCELERATORS DISCARDABLE
BEGIN
"Q", IDM_EXIT, VIRTKEY, CONTROL, NOINVERT
END
//----------------------------------------------------------------------
// MenuBar resources
//
#define SHMENUBAR RCDATA
// MenuBar resource with simple new menu
ID_TOOLBAR1 SHMENUBAR MOVEABLE PURE
BEGIN
ID_MENU, 5,
I_IMAGENONE, IDM_SHAREDNEWDEFAULT, TBSTATE_ENABLED,
TBSTYLE_AUTOSIZE, IDS_SHNEW, IDS_SNEWTT, NOMENU,
I_IMAGENONE, ID_VIEWMENU, TBSTATE_ENABLED,
TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_VIEWMENUNAME, 0, 0,
I_IMAGENONE, ID_TOOLMENU, TBSTATE_ENABLED,
TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_TOOLMENUNAME, 0, 1,
0, IDM_ABOUT, TBSTATE_ENABLED,
TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE, 0, IDS_BTNTOOLTT, 0,
2, ID_MENU3, TBSTATE_ENABLED,
TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, 0, IDS_BTNTOOLTT, 2,
END
// MenuBar resource with shared new
ID_TOOLBAR2 SHMENUBAR MOVEABLE PURE
BEGIN
ID_MENU, 8,
I_IMAGENONE, IDM_SHAREDNEW, TBSTATE_ENABLED,
TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE, IDS_SHNEW, IDS_NEWTT, NOMENU,
I_IMAGENONE, ID_VIEWMENU, TBSTATE_ENABLED,
TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_VIEWMENUNAME, 0, 0,
I_IMAGENONE, ID_TOOLMENU, TBSTATE_ENABLED,
TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_TOOLMENUNAME, 0, 1,
1, IDM_ABOUT, TBSTATE_ENABLED,
TBSTYLE_CHECK | TBSTYLE_AUTOSIZE, 0, IDS_BTNTOOLTT, 0,
I_IMAGENONE, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0,
3+STD_CUT, IDM_CUT, TBSTATE_ENABLED,
TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE, 0, IDS_BTNCUTTT, 0,
3+STD_COPY, IDM_COPY, TBSTATE_ENABLED,
TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE, 0, IDS_BTNCOPYTT, 0,
3+STD_PASTE, IDM_PASTE, TBSTATE_ENABLED,
TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE, 0, IDS_BTNPASTETT, 0,
END
ID_MENU MENU DISCARDABLE
BEGIN
POPUP "&Menu1"
BEGIN
MENUITEM "Shared New", IDM_DOSHAREDNEW
MENUITEM "Simple New", IDM_DOSIMPLENEW
MENUITEM SEPARATOR
MENUITEM "Exit", IDM_EXIT
END
POPUP "&Menu2"
BEGIN
MENUITEM "&About...", IDM_ABOUT
END
POPUP "&Menu3"
BEGIN
MENUITEM "Menu item 1", IDM_ITEM1
MENUITEM "Menu item 2", IDM_ITEM2
MENUITEM "Menu item 3", IDM_ITEM3
MENUITEM "Menu item 4", IDM_ITEM4
MENUITEM "Menu item 5", IDM_ITEM5
MENUITEM "Menu item 6", IDM_ITEM6
END
END
//----------------------------------------------------------------------
// String resource table
//
STRINGTABLE DISCARDABLE
BEGIN
IDS_VIEWMENUNAME "View"
IDS_TOOLMENUNAME "Tools"
IDS_SNEWTT "New menu tooltip text"
IDS_NEWTT "New doc + shared menu tooltip"
IDS_BTNTOOLTT "Button tooltip"
IDS_BTNCUTTT "Cut"
IDS_BTNCOPYTT "Copy"
IDS_BTNPASTETT "Paste"
END
//----------------------------------------------------------------------
// About box dialog template
//
aboutbox DIALOG discardable 10, 10, 135, 40
STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | DS_CENTER |
DS_MODALFRAME
CAPTION "About"
BEGIN
ICON ID_ICON, -1, 3, 5, 10, 10
LTEXT "MenuBar - Written for the book Programming Windows CE Copyright 2003 Douglas Boling"
-1, 30, 5, 102, 37
END
MenuBar.h
//======================================================================
// Header file
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//
//================================================================
// Returns number of elements
#define dim(x) (sizeof(x) / sizeof(x[0]))
//----------------------------------------------------------------------
// Generic defines and data types
//
struct decodeUINT { // Structure associates
UINT Code; // messages
// with a function.
LRESULT (*Fxn)(HWND, UINT, WPARAM, LPARAM);
};
struct decodeCMD { // Structure associates
UINT Code; // menu IDs with a
LRESULT (*Fxn)(HWND, WORD, HWND, WORD); // function.
};
//----------------------------------------------------------------------
// Generic defines used by application
#define ID_ACCEL 1 // Accelerator table ID
#define ID_TOOLBMPS 2
#define ID_ICON 3
#define ID_TOOLBAR1 100
#define ID_TOOLBAR2 101
#define ID_MENU 102
#define IDC_RPTLIST 103
#define ID_VIEWMENU 50
#define ID_TOOLMENU 51
#define ID_MENU3 52
#define IDM_EXIT 200
#define IDM_DOSHAREDNEW 201
#define IDM_DOSIMPLENEW 202
#define IDM_ABOUT 210
#define IDM_ITEM1 220
#define IDM_ITEM2 221
#define IDM_ITEM3 222
#define IDM_ITEM4 223
#define IDM_ITEM5 224
#define IDM_ITEM6 225
#define IDM_CUT 230
#define IDM_COPY 231
#define IDM_PASTE 232
#define IDM_MYNEWMENUITEM (IDM_NEWMENUMAX+1) // New Menu custom item
#define IDS_VIEWMENUNAME 256 // String table IDs
#define IDS_TOOLMENUNAME 257
#define IDS_SNEWTT 258
#define IDS_NEWTT 259
#define IDS_BTNTOOLTT 260
#define IDS_BTN3TEXT 261
#define IDS_BTNCUTTT 262
#define IDS_BTNCOPYTT 263
#define IDS_BTNPASTETT 264
//----------------------------------------------------------------------
// Function prototypes
//
HWND InitInstance (HINSTANCE, LPWSTR, int);
int TermInstance (HINSTANCE, int);
HWND MyCreateMenuBar (HWND hWnd, int idToolbar);
void MyCheckMenu (int idMenu);
void Add2List (HWND hWnd, LPTSTR lpszFormat, ...);
// Window procedures
LRESULT CALLBACK MainWndProc (HWND, UINT, WPARAM, LPARAM);
// Message handlers
LRESULT DoCreateMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoSizeMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoNotifyMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoCommandMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoSettingChangeMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoActivateMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoDestroyMain (HWND, UINT, WPARAM, LPARAM);
// WM_COMMAND message handlers
LPARAM DoMainCommandExit (HWND, WORD, HWND, WORD);
LPARAM DoMainCommandSharedNew (HWND, WORD, HWND, WORD);
LPARAM DoMainCommandSimpleNew (HWND, WORD, HWND, WORD);
LPARAM DoMainCommandAbout (HWND, WORD, HWND, WORD);
// Dialog procedures
BOOL CALLBACK AboutDlgProc (HWND, UINT, WPARAM, LPARAM);
ExPPCIncs.h
//======================================================================
// Extra Aygshell includes - This file is necessary to add back defines
// removed from the Pocket PC SDK in 2002 and 2003. These defines allow
// an application to intercept action from the New menu.
//
#ifdef __cplusplus
extern "C" {
#endif
#ifndef NMN_GETAPPREGKEY
//++++++
//
// New menu notifications
//
// get the application specific reg key for "new" menu items
#define NMN_GETAPPREGKEY 1101
// Sent to app before shared new menu is destroyed.
#define NMN_NEWMENUDESTROY 1102
// Sent to app before COM object is instantiated.
#define NMN_INVOKECOMMAND 1103
// Sent to app when new button style changes
#define NMN_NEWBUTTONUPDATED 1104
typedef struct tagNMNEWMENU
{
NMHDR hdr;
TCHAR szReg[80];
HMENU hMenu;
CLSID clsid;
} NMNEWMENU, *PNMNEWMENU;
// For application added menu items.
#define IDM_NEWMENUMAX 3000
//
// End New menu notifications
//
//------
#endif
#ifdef __cplusplus
}
#endif
MenuBar.cpp
//======================================================================
// MenuBar - Demonstrates a Pocket PC menu bar
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//
//======================================================================
#include <windows.h> // For all that Windows stuff
#include <commctrl.h> // Command bar includes
#include <aygshell.h> // Pocket PC includes
#include "MenuBar.h" // Program-specific stuff
#include "ExPPCIncs.h" // Adds back PPC SDK stuff
//----------------------------------------------------------------------
// Global data
//
// Get the new menu extension guid when compiling for Pocket PC
#if defined(WIN32_PLATFORM_PSPC)
// This guid must match the one in the NewMenuX example
static const GUID CLSID_NewMenuX =
{0x130f6e46,0xc3f9,0x4fa8,{0xb8,0xbc,0x75,0x72,0xb,0xc7,0x32,0x31}};
#endif WIN32_PLATFORM_PSPC
const TCHAR szAppName[] = TEXT("MenuBar");
HINSTANCE hInst; // Program instance handle
HWND hwndMenuBar = NULL; // Handle of menu bar control
SHACTIVATEINFO sai; // Used to adjust window for SIP
// Message dispatch table for MainWindowProc
const struct decodeUINT MainMessages[] = {
WM_CREATE, DoCreateMain,
WM_SIZE, DoSizeMain,
WM_COMMAND, DoCommandMain,
WM_NOTIFY, DoNotifyMain,
WM_SETTINGCHANGE, DoSettingChangeMain,
WM_ACTIVATE, DoActivateMain,
WM_DESTROY, DoDestroyMain,
};
// Command Message dispatch for MainWindowProc
const struct decodeCMD MainCommandItems[] = {
IDM_EXIT, DoMainCommandExit,
IDM_DOSHAREDNEW, DoMainCommandSharedNew,
IDM_DOSIMPLENEW, DoMainCommandSimpleNew,
IDM_ABOUT, DoMainCommandAbout,
};
//======================================================================
// Program entry point
//
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPWSTR lpCmdLine, int nCmdShow) {
MSG msg;
int rc = 0;
HWND hwndMain;
HACCEL hAccel;
// Initialize application.
hwndMain = InitInstance (hInstance, lpCmdLine, nCmdShow);
if (hwndMain == 0) return 0x10;
hAccel = LoadAccelerators(hInstance, MAKEINTRESOURCE (ID_ACCEL));
// Application message loop
while (GetMessage (&msg, NULL, 0, 0)) {
// Translate accelerator keys.
if (!TranslateAccelerator(hwndMain, hAccel, &msg)) {
TranslateMessage (&msg);
DispatchMessage (&msg);
}
}
// Instance cleanup
return TermInstance (hInstance, msg.wParam);
}
//------------------------------------------------------------------
// InitInstance - Instance initialization
//
HWND InitInstance (HINSTANCE hInstance, LPWSTR lpCmdLine, int nCmdShow) {
HWND hWnd;
WNDCLASS wc;
// Allow only one instance of the application.
hWnd = FindWindow (szAppName, NULL);
if (hWnd) {
SetForegroundWindow ((HWND)(((DWORD)hWnd) | 0x01));
return 0;
}
// Register application main window class.
wc.style = CS_VREDRAW | CS_HREDRAW; // Window style
wc.lpfnWndProc = MainWndProc; // Callback function
wc.cbClsExtra = 0; // Extra class data
wc.cbWndExtra = 0; // Extra window data
wc.hInstance = hInstance; // Owner handle
wc.hIcon = NULL, // Application icon
wc.hCursor = LoadCursor (NULL, IDC_ARROW); // Default cursor
wc.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
wc.lpszMenuName = NULL; // Menu name
wc.lpszClassName = szAppName; // Window class name
if (RegisterClass (&wc) == 0) return 0;
// Save program instance handle in global variable.
hInst = hInstance;
// Create main window.
hWnd = CreateWindow (szAppName, TEXT("Menu Bar"), WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
if (!IsWindow (hWnd)) return 0; // Fail if not created.
// Standard show and update calls
ShowWindow (hWnd, nCmdShow);
UpdateWindow (hWnd);
return hWnd;
}
//----------------------------------------------------------------------
// TermInstance - Program cleanup
//
int TermInstance (HINSTANCE hInstance, int nDefRC) {
return nDefRC;
}
//======================================================================
// Message handling procedures for main window
//
//----------------------------------------------------------------------
// MainWndProc - Callback function for application window
//
LRESULT CALLBACK MainWndProc (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
INT i;
//
// Search message list to see if we need to handle this
// message. If in list, call procedure.
//
for (i = 0; i < dim(MainMessages); i++) {
if (wMsg == MainMessages[i].Code)
return (*MainMessages[i].Fxn)(hWnd, wMsg, wParam, lParam);
}
return DefWindowProc (hWnd, wMsg, wParam, lParam);
}
//----------------------------------------------------------------------
// DoCreateMain - Process WM_CREATE message for window.
//
LRESULT DoCreateMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
SIPINFO si;
HWND hwndChild;
int i, cx, cy;
// Initialize the shell to activate info structure.
memset (&sai, 0, sizeof (sai));
sai.cbSize = sizeof (sai);
// Create menu bar and check for errors.
hwndMenuBar = MyCreateMenuBar (hWnd, ID_TOOLBAR1);
if (!hwndMenuBar) {
MessageBox (hWnd, TEXT("Couldn\'t create MenuBar"),
szAppName, MB_OK);
DestroyWindow (hWnd);
return 0;
}
// Set menu check mark.
MyCheckMenu (IDM_DOSIMPLENEW);
// Create report window. It will be sized in the WM_SIZE handler.
hwndChild = CreateWindowEx (0, TEXT ("listbox"), TEXT ("),
WS_VISIBLE | WS_CHILD | WS_VSCROLL |
LBS_USETABSTOPS | LBS_NOINTEGRALHEIGHT,
0, 0, 0, 0, hWnd, (HMENU)IDC_RPTLIST,
hInst, NULL);
// Destroy frame if window not created.
if (!IsWindow (hwndChild)) {
DestroyWindow (hWnd);
return 0;
}
// Initialize tab stops for display list box.
i = 8;
SendMessage (hwndChild, LB_SETTABSTOPS, 1, (LPARAM)&i);
// Query the sip state and size our window appropriately.
memset (&si, 0, sizeof (si));
si.cbSize = sizeof (si);
SHSipInfo(SPI_GETSIPINFO, 0, (PVOID)&si, FALSE);
cx = si.rcVisibleDesktop.right - si.rcVisibleDesktop.left;
cy = si.rcVisibleDesktop.bottom - si.rcVisibleDesktop.top;
// If the sip is not shown, or is showing but not docked, the
// desktop rect doesn't include the height of the menu bar.
if (!(si.fdwFlags & SIPF_ON) ||
((si.fdwFlags & SIPF_ON) && !(si.fdwFlags & SIPF_DOCKED))) {
RECT rectMB;
GetWindowRect (hwndMenuBar, &rectMB);
cy -= (rectMB.bottom - rectMB.top);
}
SetWindowPos (hWnd, NULL, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER);
return 0;
}
//----------------------------------------------------------------------
// DoSizeMain - Process WM_SIZE message for window.
//
LRESULT DoSizeMain (HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam){
RECT rect;
GetClientRect (hWnd, &rect);
SetWindowPos (GetDlgItem (hWnd, IDC_RPTLIST), NULL, 0, 0,
rect.right - rect.left, rect.bottom - rect.top,
SWP_NOZORDER);
return 0;
}
//----------------------------------------------------------------------
// DoCommandMain - Process WM_COMMAND message for window.
//
LRESULT DoCommandMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
WORD idItem, wNotifyCode;
HWND hwndCtl;
INT i;
// Parse the parameters.
idItem = (WORD) LOWORD (wParam);
wNotifyCode = (WORD) HIWORD (wParam);
hwndCtl = (HWND) lParam;
Add2List (hWnd, TEXT ("WM_COMMAND id:%d code:%d"), idItem,
wNotifyCode);
// Call routine to handle control message.
for (i = 0; i < dim(MainCommandItems); i++) {
if (idItem == MainCommandItems[i].Code)
return (*MainCommandItems[i].Fxn)(hWnd, idItem, hwndCtl,
wNotifyCode);
}
return 0;
}
//----------------------------------------------------------------------
// DoNotifyMain - Process WM_NOTIFY message for window.
//
LRESULT DoNotifyMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
PNMNEWMENU lpNewMenu;
LPNMHDR lpnhr = (LPNMHDR)lParam;
Add2List (hWnd, TEXT ("WM_NOTIFY id:%d event:%d"), lpnhr->idFrom,
lpnhr->code);
// This code only works when compiling on a Pocket PC
#if defined(WIN32_PLATFORM_PSPC) // See if new menu being displayed.
if (lpnhr->code == NMN_GETAPPREGKEY) {
lpNewMenu = (PNMNEWMENU) lParam;
AppendMenu (lpNewMenu->hMenu, MF_ENABLED, IDM_MYNEWMENUITEM,
TEXT("My own New menu item"));
AppendMenu (lpNewMenu->hMenu, MF_SEPARATOR, 0, 0);
// Permanent new menu item selected
} else if (lpnhr->code == NMN_INVOKECOMMAND) {
lpNewMenu = (PNMNEWMENU) lParam;
// See if it is NewMenuX.
if (IsEqualIID (lpNewMenu->clsid, CLSID_NewMenuX)) {
int rc = MessageBox (hWnd,
TEXT ("Do you want to launch Calc?"),
szAppName, MB_YESNO);
if (rc == IDYES)
return 0;
else
return 1;
}
}
#endif
return 0;
}
//----------------------------------------------------------------------
// DoSettingChangeMain - Process WM_SETTINGCHANGE message for window.
//
LRESULT DoSettingChangeMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
// Notify shell of our WM_SETTINGCHANGE message.
SHHandleWMSettingChange(hWnd, wParam, lParam, &sai);
return 0;
}
//----------------------------------------------------------------------
// DoActivateMain - Process WM_ACTIVATE message for window.
//
LRESULT DoActivateMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
// Notify shell of our activate message.
SHHandleWMActivate(hWnd, wParam, lParam, &sai, 0);
return 0;
}
//----------------------------------------------------------------------
// DoDestroyMain - Process WM_DESTROY message for window.
//
LRESULT DoDestroyMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
PostQuitMessage (0);
return 0;
}
//======================================================================
// Command handler routines
//----------------------------------------------------------------------
// DoMainCommandExit - Process Program Exit command.
//
LPARAM DoMainCommandExit (HWND hWnd, WORD idItem, HWND hwndCtl,
WORD wNotifyCode) {
SendMessage (hWnd, WM_CLOSE, 0, 0);
return 0;
}
//----------------------------------------------------------------------
// DoMainCommandAbout - Process Tools About command.
//
LPARAM DoMainCommandAbout (HWND hWnd, WORD idItem, HWND hwndCtl,
WORD wNotifyCode) {
// Use DialogBox to create modal dialog.
DialogBox (hInst, TEXT ("aboutbox"), hWnd, AboutDlgProc);
return 0;
}
//----------------------------------------------------------------------
// DoMainCommandSimpleNew - Process Simple new menu command.
//
LPARAM DoMainCommandSimpleNew (HWND hWnd, WORD idItem, HWND hwndCtl,
WORD wNotifyCode) {
if (IsWindow (hwndMenuBar))
DestroyWindow (hwndMenuBar);
// Create a menu bar.
hwndMenuBar = MyCreateMenuBar (hWnd, ID_TOOLBAR1);
MyCheckMenu (IDM_DOSIMPLENEW);
return 0;
}
//----------------------------------------------------------------------
// DoMainCommandSharedNew - Process Shared new menu command.
//
LPARAM DoMainCommandSharedNew (HWND hWnd, WORD idItem, HWND hwndCtl,
WORD wNotifyCode) {
// Delete the old menu bar.
if (IsWindow (hwndMenuBar))
DestroyWindow (hwndMenuBar);
// Create the menu bar.
hwndMenuBar = MyCreateMenuBar (hWnd, ID_TOOLBAR2);
// Add the standard view bitmap.
CommandBar_AddBitmap (hwndMenuBar, HINST_COMMCTRL,
IDB_STD_SMALL_COLOR, STD_PRINT, 16, 16);
MyCheckMenu (IDM_DOSHAREDNEW); // Set menu checkmark.
return 0;
}
//======================================================================
// About Dialog procedure
//
BOOL CALLBACK AboutDlgProc (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
switch (wMsg) {
case WM_INITDIALOG:
{
SHINITDLGINFO idi;
idi.dwMask = SHIDIM_FLAGS;
idi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIZEDLGFULLSCREEN |
SHIDIF_SIPDOWN;
idi.hDlg = hWnd;
SHInitDialog (&idi);
}
break;
case WM_COMMAND:
switch (LOWORD (wParam)) {
case IDOK:
case IDCANCEL:
EndDialog (hWnd, 0);
return TRUE;
}
break;
}
return FALSE;
}
//----------------------------------------------------------------------
// MyCreateMenuBar - Creates a menu bar
//
HWND MyCreateMenuBar (HWND hWnd, int idToolbar) {
SHMENUBARINFO mbi;
// Create a menu bar.
memset(&mbi, 0, sizeof(SHMENUBARINFO)); // Zero structure
mbi.cbSize = sizeof(SHMENUBARINFO); // Size field
mbi.hwndParent = hWnd; // Parent window
mbi.nToolBarId = idToolbar; // ID of toolbar resource
mbi.hInstRes = hInst; // Inst handle of app
mbi.nBmpId = ID_TOOLBMPS; // ID of bitmap resource
mbi.cBmpImages = 3; // Num of images in bitmap
SHCreateMenuBar(&mbi);
return mbi.hwndMB; // Return the menu bar handle.
}
//----------------------------------------------------------------------
// MyCheckMenu - Places a check next to a menu item
//
void MyCheckMenu (int idMenu) {
HMENU hSubMenu;
// The handle for the view menu
hSubMenu = (HMENU)SendMessage (hwndMenuBar, SHCMBM_GETMENU, 0, 0);
if (idMenu == IDM_DOSIMPLENEW) {
CheckMenuItem (hSubMenu, IDM_DOSIMPLENEW, MF_BYCOMMAND |
MFS_CHECKED);
CheckMenuItem (hSubMenu, IDM_DOSHAREDNEW, MF_BYCOMMAND |
MFS_UNCHECKED);
} else {
CheckMenuItem (hSubMenu, IDM_DOSIMPLENEW, MF_BYCOMMAND |
MFS_UNCHECKED);
CheckMenuItem (hSubMenu, IDM_DOSHAREDNEW, MF_BYCOMMAND |
MFS_CHECKED);
}
return;
}
//----------------------------------------------------------------------
// Add2List - Add string to the report list box.
//
void Add2List (HWND hWnd, LPTSTR lpszFormat, ...) {
int nBuf, i;
TCHAR szBuffer[512];
va_list args;
va_start(args, lpszFormat);
nBuf = _vstprintf(szBuffer, lpszFormat, args);
i = SendDlgItemMessage (hWnd, IDC_RPTLIST, LB_ADDSTRING, 0,
(LPARAM)(LPCTSTR)szBuffer);
if (i != LB_ERR)
SendDlgItemMessage (hWnd, IDC_RPTLIST, LB_SETTOPINDEX, i,
(LPARAM)(LPCTSTR)szBuffer);
va_end(args);
}
The MenuBar example creates its menu bar in a common routine called MyCreateMenuBar. The two parameters provide the handle of the window that will own the menu bar and the ID of the resource specifying the menu bar configuration.

The two menu bars are switched simply by destroying one bar and creating another. The creation of a menu bar happens so quickly that the user doesn't even notice it. This solution is better than creating two menu bars and alternately showing one and hiding the other, since having two controls consumes extra memory that is better used elsewhere.When the menu bar with the shared new menu button is created, a call is made to CommandBar_Addbitmap to add the common control bitmaps that include the cut, copy, and paste images. This menu bar also includes a check box–style button that is tapped once to set and tapped again to clear. The simple menu bar has a button with a bitmap—the bitmap with the artistic C that when tapped displays a menu. This button shows that it's just as easy to display a menu from a button with a bitmap as it is with a text label.This completes the discussion of the "menu" controls. I talk about these controls at length because you'll need one of them for almost every Windows CE application.For the remainder of the chapter, I'll cover the highlights of some of the other controls. These other controls are similar to but have somewhat less function than their counterparts under Windows XP. I'll spend more time on the controls I think you'll need when writing a Windows CE application. I'll start with the month calendar and the time and date picker controls. These controls are rather new to the common control set and have a direct application to the PIM-like applications that are appropriate for many Windows CE systems. I'll also spend some time covering the list view control, concentrating on features of use to Windows CE developers. I'll cover just briefly the remainder of the common controls.
The Month Calendar Control
The month calendar control gives you a handy month-view calendar that can be manipulated by users to look up any month, week, or day as far back as the adoption of the Gregorian calendar in September 1752. The control can display as many months as will fit into the size of the control. The days of the month can be highlighted to indicate appointments. The weeks can indicate the current week throughout the year. Users can spin through the months by tapping on the name of the month or change years by tapping on the year displayed.Before using the month calendar control, you must initialize the common control library by calling InitCommonControlsEx with the ICC_DATE_CLASSES flag. You create the control by calling CreateWindow with the MONTHCAL_CLASS flag. The style flags for the control are shown here:
MCS_MULTISELECTThe control allows multiple selection of days.
MCS_NOTODAYThe control won't display today's date under the calendar.
MCS_NOTODAYCIRCLEThe control won't circle today's date.
MCS_WEEKNUMBERSThe control displays the week number (1 through 52) to the left of each week in the calendar.
MCS_DAYSTATEThe control sends notification messages to the parent requesting the days of the month that should be displayed in bold. You use this style to indicate which days have appointments or events scheduled.
Initializing the Control
In addition to the styles I just described, you can use a number of messages or their corresponding wrapper macros to configure the month calendar control. You can use an MCM_SETFIRSTDAYOFWEEK message to display a different starting day of the week. You can also use the MCM_SETRANGE message to display dates within a given range in the control. You can configure date selection to allow the user to choose only single dates or to set a limit to the range of dates that a user can select at any one time. The single/multiple date selection ability is defined by the MCS_MULTISELECT style. If you set this style, you use the MCM_SETMAXSELCOUNT message to set the maximum number of days that can be selected at any one time.You can set the background and text colors of the control by using the MCM_SETCOLOR message. This message can individually set colors for the different regions within the controls, including the calendar text and background, the header text and background, and the color of the days that precede and follow the days of the month being displayed. This message takes a flag indicating the part of the control to set and a COLORREF value to specify the color.The month calendar control is designed to display months on an integral basis. That is, if the control is big enough for one and a half months, it displays only one month, centered in the control. You can use the MCM_GETMINREQRECT message to compute the minimum size necessary to display one month. Because the control must first be created before the MCM_GETMINREQRECT can be sent, properly sizing the control is a roundabout process. You must create the control, send the MCM_GETMINREQRECT message, and then resize the control using the data returned from the message.
Month Calendar Notifications
The month calendar control has only three notification messages to send to its parent. Of these, the MCN_GETDAYSTATE notification is the most important. This notification is sent when the control needs to know what days of a month to display in bold. This is done by querying the parent for a series of bit field values encoded in a MONTHDAYSTATE variable. This value is nothing more than a 32-bit value with bits 1 through 31 representing the days 1 through 31 of the month.When the control needs to display a month, it sends an MCN_GETDAYSTATE notification with a pointer to an NMDAYSTATE structure defined as the following:
typedef struct {
NMHDR nmhdr;
SYSTEMTIME stStart;
int cDayState;
LPMONTHDAYSTATE prgDayState;
} NMDAYSTATE;
The nmbhdr field is simply the NMHDR structure that's passed with every WM_NOTIFY message. The stStart field contains the starting date for which the control is requesting information. This date is encoded in a standard SYSTEMTIME structure used by all versions of Windows. It's detailed here:
typedef struct {
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
} SYSTEMTIME;
For this notification, only the wMonth, wDay, and wYear fields are significant.The cDayState field contains the number of entries in an array of MONTHDAYSTATE values. Even if a month calendar control is displaying only one month, it could request information about the previous and following months if days of those months are needed to fill in the top or bottom lines of the calendar.
The month calendar control sends an MCN_SELCHANGE notification when the user changes the days that are selected in the control. The structure passed with this notification, NMSELCHANGE, contains the newly highlighted starting and ending days. The MCN_SELECT notification is sent when the user double-taps on a day. The same NMSELCHANGE structure is passed with this notification to indicate the days that have been selected.
The Date and Time Picker Control
The date and time picker control looks deceptively simple but is a great tool for any application that needs to ask the user to specify a date. Any programmer who has had to parse, validate, and translate a string into a valid system date or time will appreciate this control.When used to select a date, the control resembles a combo box, which is an edit field with a down arrow button on the right side. Clicking on the arrow, however, displays a month calendar control showing the current month. Selecting a day in the month dismisses the month calendar control and fills the date and time picker control with that date. When you configure it to query for a time, the date and time picker control resembles an edit field with a spin button on the right end of the control.The date and time picker control has three default formats: two for displaying the date and one for displaying the time. The control also allows you to provide a formatting string so that users can completely customize the fields in the control. The control even lets you insert application-defined fields in the control.
Creating a Date and Time Picker Control
Before you can create the date and time picker control, the common control library must be initialized. If InitCommonControlsEx is used, it must be passed an ICC_DATE_CLASSES flag. The control is created by using CreateWindow with the class DATETIMEPICK_CLASS. The control defines the following styles:
DTS_LONGDATEFORMATThe control displays a date in long format, as in Friday, September 19, 2003. The actual long date format is defined in the system registry.
DTS_SHORTDATEFORMATThe control displays a date in short format, as in 9/19/03. The actual short date format is defined in the system registry.
DTS_TIMEFORMATThe control displays the time in a format such as 5:50:28 PM. The actual time format is defined in the system registry.
DTS_SHOWNONEThe control has a check box to indicate that the date is valid.
DTS_UPDOWNAn up-down control replaces the drop-down button that displays a month calendar control in date view.
DTS_APPCANPARSEAllows the user to directly enter text into the control. The control sends a DTN_USERSTRING notification when the user is finished.
The first three styles simply specify a default format string. These formats are based on the regional settings in the registry. Since these formats can change if the user picks different regional settings in the Control Panel, the date and time picker control needs to know when these formats change. The system informs top-level windows of these types of changes by sending a WM_SETTINGCHANGE message. An application that uses the date and time picker control and uses one of these default fonts should forward the WM_SETTINGCHANGE message to the control if one is sent. This causes the control to reconfigure the default formats for the new regional settings.The DTS_APPCANPARSE style enables the user to directly edit the text in the control. If this isn't set, the allowable keys are limited to the cursor keys and the numbers. When a field, such as a month, is highlighted in the edit field and the user presses the 6 key, the month changes to June. With the DTS_APPCANPARSE style, the user can directly type any character in the edit field of the control. When the user has finished, the control sends a DTN_USERSTRING notification to the parent window so that the text can be verified.
Customizing the Format
To customize the display format, all you need to do is create a format string and send it to the control using a DTM_SETFORMAT message. The format string can be made up of any of the following codes:
String Description
fragment
"d" One- or two-digit day.
"dd" Two-digit day. Single digits have a leading zero.
"ddd" The three-character weekday abbreviation. As in Sun, Mon...
"dddd" The full weekday name.
"h" One- or two-digit hour (12-hour format).
"hh" Two-digit hour (12-hour format). Single digits have a leading zero.
"H" One- or two-digit hour (24-hour format).
"HH" Two-digit hour (24-hour format). Single digits have a leading zero.
"m" One- or two-digit minute.
"mm" Two-digit minute. Single digits have a leading zero.
"M" One- or two-digit month.
"MM" Two-digit month. Single digits have a leading zero.
"MMM" Three-character month abbreviation.
"MMMM" Full month name.
"t" The one-letter AM/PM abbreviation. As in A or P.
"tt" The two-letter AM/PM abbreviation. As in AM or PM.
"X" Specifies a callback field that must be parsed by the application.
"y" One-digit year. As in 1 for 2001.
"yy" Two-digit year. As in 01 for 2001.
"yyy" Full four-digit year. As in 2001.
Literal strings can be included in the format string by enclosing them in single quotes. For example, to display the string Today is: Saturday, December 5, 2001 the format string would be
'Today is: 'dddd', 'MMMM' 'd', 'yyy
The single quotes enclose the strings that aren't parsed. That includes the Today is: as well as all the separator characters, such as spaces and commas.The callback field, designated by a series of X characters, provides for the application the greatest degree of flexibility for configuring the display of the date. When the control detects an X field in the format string, it sends a series of notification messages to its owner asking what to display in that field. A format string can have any number of X fields. For example, the following string has two X fields.
'Today 'XX' is: ' dddd', 'MMMM' 'd', 'yyy' and is 'XXX' birthday'
The number of X characters is used by the application only to differentiate the application-defined fields; it doesn't indicate the number of characters that should be displayed in the fields. When the control sends a notification asking for information about an X field, it includes a pointer to the X string so that the application can determine which field is being referenced.When the date and time picker control needs to display an application-defined X field, it sends two notifications: DTN_FORMATQUERY and DTN_FORMAT. The DTN_FORMATQUERY notification is sent to get the maximum size of the text to be displayed. The DTN_FORMAT notification is then sent to get the actual text for the field. A third notification, DTN_WMKEYDOWN, is sent when the user highlights an application-defined field and presses a key. The application is responsible for determining which keys are valid and modifying the date if an appropriate key is pressed.
The List View Control
The list view control is arguably the most complex of the common controls. It displays a list of items in one of four modes: large icon, small icon, list, and report. The Windows CE version of the list view control supports many, but not all, of the common control library functions released with Internet Explorer 4.0. Some of these functions are a great help in the memory-constrained environment of Windows CE. These features include the ability to manage virtual lists of almost any size, headers that can have images and be rearranged using drag and drop, the ability to indent an entry, and new styles for report mode. The list view control also supports the new custom draw interface, which allows a fairly easy way of changing the appearance of the control.You register the list view control either by calling InitCommonControls or by calling an InitCommonControls using an ICC_LISTVIEW_CLASSES flag. You create the control by calling CreateWindow using the class filled with WC_LISTVIEW. Under Windows CE, the list view control supports all the styles supported by other versions of Windows, including the LVS_OWNERDATA style that designates the control as a virtual list view control.
Styles in Report Mode
In addition to the standard list view styles that you can use when creating the list view, the list view control supports a number of extended styles. This rather unfortunate term doesn't refer to the extended styles field in the CreateWindowsEx function. Instead, two messages, LVM_GETEXTENDEDLISTVIEWSTYLE and LVM_SETEXTENDEDLISTVIEWSTYLE, are used to get and set these extended list view styles. The extended styles supported by Windows CE are listed below.
LVS_EX_CHECKBOXESThe control places check boxes next to each item in the control.
LVS_EX_HEADERDRAGDROPAllows headers to be rearranged by the user using drag and drop.
LVS_EX_GRIDLINESThe control draws grid lines around the items in report mode.
LVS_EX_SUBITEMIMAGESThe control displays images in the subitem columns in report mode.
LVS_EX_FULLROWSELECTThe control highlights the item's entire row in report mode when that item is selected.
LVS_EX_ONECLICKACTIVATEThe control activates an item with a single tap instead of requiring a double tap.
Aside from the LVS_EX_CHECKBOXES and LVS_EX_ONECLICKACTIVATE extended styles, which work in all display modes, these new styles all affect the actions of the list view when in report mode. The effort here has clearly been to make the list view control an excellent control for displaying large lists of data.Note that the list view control under Windows CE doesn't support other extended list view styles, such as LVS_EX_INFOTIP, LVS_EX_ONECLICKACTIVATE, LVS_EX_TWOCLICKACTIVATE, LVS_EX_TRACKSELECT, LVS_EX_REGIONAL, or LVS_EX_FLATSB, supported in some versions of the common control library.
Virtual List View
The virtual list view mode of the list view control is a huge help for Windows CE devices. In this mode, the list view control tracks only the selection and focus state of the items. The application maintains all the other data for the items in the control. This mode is handy for two reasons. First, virtual list view controls are fast. The initialization of the control is almost instantaneous because all that's required is that you set the number of items in the control. The list view control also gives you hints about what items it will be looking for in the near term. This allows applications to cache necessary data in RAM and leave the remainder of the data in a database or file. Without a virtual list view, an application would have to load an entire database or list of items in the list view when it's initialized. With the virtual list view, the application loads only what the control requires to display at any one time.
The second advantage of the virtual list view is RAM savings. Because the virtual list view control maintains little information on each item, the control doesn't keep a huge data array in RAM to support the data. The application manages what data is in RAM with some help from the virtual list view's cache hint mechanism.The virtual list view has some limitations. The LVS_OWNERDATA Chapter 9, the virtual list view control is demonstrated in the AlbumDB example. Check out that source to see how the virtual list view is used in practice.
The CapEdit Control
The CapEdit control is an edit box that capitalizes the first letter in the first or every word in the control. This control is great for edit controls that will receive proper names but are on keyboardless devices, where tapping the Shift key isn't convenient for the user.To create the CapEdit control, create a window with the WC_CAPEDIT class name. Since CapEdit uses the edit control's window procedure for its base function, you can configure the control like an edit control by sending it standard edit control messages. The only message that's unique to this control is CEM_UPCASEALLWORDS. If wParam isn't 0, the control will capitalize the first letter in every word. Sending this message with wParam equal to 0 will cause the control to capitalize only the first word in the control.