The Smartphone's MenuBar Control
The Smartphone MenuBar is a simplified version of the MenuBar control used by the Pocket PC. Because the Smartphone lacks a touch screen, the user interacts with the Smartphone MenuBar using two buttons at the base of the screen. The two buttons are aligned with the two possible buttons on the control. The buttons can either be implemented to display a menu or to perform an action directly.As I mentioned earlier, when a menu is displayed, the first 10 items on the menu are automatically prefixed with a number from 1 through 9 and then 0 corresponding to the 10 digits on the phone keyboard. When the menu is displayed the user can easily select an item by pressing a key on the phone. With the automatic addition of the menu item numbers, there's no reason to specify underlined navigation characters in the menu items.Although it's possible to put more than 10 items on a menu, the small size of the phone necessitates that the menu scroll, which isn't a very friendly interface design. Another less-than-friendly interface design is cascaded menus. The Smartphone MenuBar control does support cascaded menus, but the extra level of action required by the user causes more work than a cascaded menu provides benefits.
Creating a Smartphone MenuBar Control
Just as on the Pocket PC, the MenuBar creation function SHCreateMenuBar is used to create a MenuBar on the Smartphone. The format of the parameters is the same as in the Pocket PC, as is the format of the SHMENUBARINFO structure used to define the structure of the control. The combination toolbar and menu resource used by the Smartphone MenuBar is also the same. The difference is that on the Smartphone, the MenuBar is much less flexible. The toolbar resource must define one or two buttons. The MenuBar buttons can be designed either to provide a direct action or to display a menu when the associated button is pressed.For review, the prototype of SHCreateMenuBar and SHMENUBARINFO is shown here:
BOOL SHCreateMenuBar (SHMENUBARINFO *pmb);
typedef struct tagSHMENUBARINFO {
DWORD cbSize;
HWND hwndParent;
DWORD dwFlags;
UINT nToolBarId;
HINSTANCE hInstRes;
int nBmpId;
int cBmpImages;
HWND hwndMB;
COLORREF clrBk;
} SHMENUBARINFO;
In the Smartphone MenuBar control, the cbSize, hwndParent, hInstRes, and hwndMB fieldsChapter 5. That format is shown here.
<Menu ID>, <Number of buttons (1 or 2)>,
I_IMAGENONE, <Cmd1ID>, <Btn1State>, <Btn1Style>, <String1ID>, 0, <Menu1Index>
I_IMAGENONE, <Cmd2ID>, <Btn2State>, <Btn2Style>, <String2ID>, 0, <Menu2Index>
The first field of the resource is the resource ID of any menu resource being used by the control. The second field can be either 1 or 2 depending on if the control will have one or two buttons on the bar. The remainder of the resource describes one or both buttons on the control. In the previous example, the second and third lines describe the two buttons.The first field should be set to I_IMAGENONE to indicate that the button is text, not a bitmap. Bitmaps are not supported on the Smartphone MenuBar. The second field contains the ID value that will be sent to the owner window if the button is not a menu and is pressed. The third field describes the initial state of the button using toolbar state flags. The fourth field describes the style of the button using toolbar style flags. The String ID field must refer to a valid string resource ID that contains the text for that button. The next to last field should be set to 0, and the last field is the submenu index into the menu identified by the first field in the resource. This field can be NOMENU if the button doesn't display a menu when pressed.An example SoftKeyBar control is shown in Figure 19-4.

Figure 19-4: A SoftKeyBar control where the menu button has been pressed
Figure 19-4 shows a Smartphone MenuBar with a Done button on the left and a Menu button on the right. This placement of direct action assigned to the left button and an optional menu assigned to the right button is the recommended arrangement suggested by the Smartphone user interface guide. In the figure, the Menu button has been pressed, displaying a short menu on the screen. The two-part resource that describes the MenuBar control in Figure 19-4 is shown here:
ID_BTNBARRES RCDATA MOVEABLE PURE
BEGIN
ID_MENU, 2,
I_IMAGENONE, IDM_EXIT, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE | TBSTYLE_BUTTON,
IDS_DONE, 0, NOMENU,
I_IMAGENONE, IDM_POP, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE | TBSTYLE_DROPDOWN,
IDS_MENU, 0, 0,
END
ID_MENU MENU DISCARDABLE
BEGIN
POPUP "&Dummy"
BEGIN
MENUITEM "Week View", IDM_VIEWWEEK
MENUITEM "Month View" IDM_VIEWMONTH
MENUITEM "Year View", IDM_VIEWYEAR
MENUITEM SEPARATOR
MENUITEM "Options", IDM_OPTIONS
END
END
The ID_BTNBARRES resource describes the two buttons. The first button is a direct action button and the second is a button that displays a menu. One difference between the two buttons, aside from the string resource references, is the different style flag, TBSTYLE_BUTTON vs. TBSTYLE_DROPDOWN. Another difference is the NOMENU index in the first button description, in which the second button uses a menu index of 0. The 0 index indicates the first, and in this case only, submenu of the ID_MENU menu resource.
Working with the Buttons and Menus
As on the Pocket PC, the buttons and menus hosted by the MenuBar control can be modified after the control has been created. The key to modifying MenuBar is the use of the TB_GETBUTTONINFO and TB_SETBUTTONINFO messages. These toolbar control messages work on the MenuBar control because it's derived from the toolbar.
To query the current settings for a button send a TB_GETBUTTONINFO message to the MenuBar. The wParam parameter should have the command ID identifying the button, and lParam should point to a TBBUTTONINFO structure. The TBBUTTONINFO structure is defined as
typedef struct {
UINT cbSize;
DWORD dwMask;
int idCommand;
int iImage;
BYTE fsState;
BYTE fsStyle;
WORD cx;
DWORD lParam;
LPTSTR pszText;
int cchText;
} TBBUTTONINFO, *LPTBBUTTONINFO;
The cbSize and dwMask fields of TBBUTTONINFO should be initialized before the message is sent. The cbSize field should be filled with the size of the structure. The dwMask field should be set with flags indicating what data is being queried from the control. For the MenuBar control, the only flags allowed are TBIF_TEXT, TBIF_STATE, TBIF_LPARAM, and TBIF_COMMAND, which specify the text, state, user-defined parameter, and command ID, respectively.Setting the parameters of a button is just as simple with the use of a TB_SETBUTTONINFO message. Here again, wParam should contain the ID of the button, and lParam should point to a TBBUTTONINFO structure. The following code disables a button by first querying the state of the button and then clearing the TBSTATE_ENABLED flag in the fsState field.
int DisableButton (HWND hwndMainWnd, DWORD dwID) {
HWND hwndMB = SHFindMenuBar (hwndMainWnd);
TBBUTTONINFO tbi;
if (!hwndMB)
return -1;
memset (&tbi, 0, sizeof (tbi));
tbi.cbSize = sizeof (tbi);
tbi.dwMask = TBIF_STATE;
if(!SendMessage (hwndMB, TB_GETBUTTONINFO, dwID, (LPARAM)&tbi))
return -2;
tbi.fsState &= ~TBSTATE_ENABLED;
SendMessage (hwndMB, TB_SETBUTTONINFO, dwID, (LPARAM)&tbi);
return 0;
}
The menus displayed by the Smartphone MenuBar can also be modified by the application. The same messages used to get the menu handles for the MenuBar control can be used in the Smartphone version of the control. Sending a SHCMBM_GETMENU message to the MenuBar control returns the menu attached to the control. The wParam and lParam parameters are ignored in this message. Sending SHCMBM_GETSUBMENU to the control returns the submenu attached to a specific button. In this case, lParam should contain the ID of the button, whereas wParam is ignored. The following code disables the Options menu item by first getting the handle of the submenu attached to the menu button and then using EnableMenu to disable the menu item.
HWND hwndMB = SHFindMenuBar (hwndMainWnd);
if (hwndMB) {
HMENU hMenu;
hMenu = (HMENU)SendMessage (hwndMB, SHCMBM_GETSUBMENU, 0, IDM_POP);
EnableMenuItem (hMenu, IDM_OPTIONS, MF_BYCOMMAND | MF_GRAYED);
}
The other menu functions can be used to manipulate the menus on the MenuBar as easily as EnableMenu was used in the previous example.
The Back Button and Other Interesting Buttons
One of the more important components of the Smartphone interface is the Back button. This button allows the user to return to the previous screen from the current screen at any time. For the most part, the Back button works as designed without any assistance from the foreground application. There are, however, times when the Back button needs to act differently, and that's when the application has to do a bit of work.The rules for the operation of the Back button are as follows:
If the current window is not a dialog box and does not have an edit box, the Back button activates the window that was displayed before the current window was activated. The current window isn't destroyed, it's simply covered by the previous window, which now becomes the active window.
If the current window is a message box or a modal dialog box without an edit control, the Back button dismisses the message box or dialog box and returns the cancel return code. For message boxes, this value is either IDNO, IDCANCEL, or IDOK for message boxes with only an OK button. For dialog boxes, a WM_COMMAND message is sent to the dialog window with the command ID IDCANCEL.
If the window currently displayed contains an edit control, the Back button erases the last character in the control and moves the entry cursor one character to the left.
In the case of the first two rules, the system will provide the default action for the Back button. For the final rule, concerning edit boxes, the application must override the default action and forward the key to the appropriate child control in the window. Fortunately, the Smartphone shell does most of the work through a couple of helper functions.If a window contains an edit box, it must override the default action of the Back key in order to pass the key to the child control. To do this, the window sends a SHCMBM_OVERRIDEKEY message to the MenuBar control for the window. The wParam parameter defines the key to override. The following keys are supported for override:
Key | Meaning |
---|---|
VK_TBACK | Back button |
VK_TSOFT1 | Left SoftKeyBar button |
VK_TSOFT2 | Right SoftKeyBar button |
VK_TVOLUMEUP | Up volume button |
VK_TVOLUMEDOWN | Down volume button |
VK_TRECORD | Record button |
The lParam parameter designates the keys to override and the action to take. The lower word of lParam contains a mask of flags that designates which flags are valid. The action flags are in the upper word. The first flag that can be set indicates whether a key is to be overridden and the second indicates whether a WM_HOTKEY message is sent to the window when the key is pressed. For example, to override the action of the Back button and to be notified by a WM_HOTKEY message, a window would send the following message to the MenuBar:
SendMessage (SHFindMenuBar (hWnd), SHCMBM_OVERRIDEKEY, VK_TBACK,
MAKELPARAM (SHMBOF_NODEFAULT | SHMBOF_NOTIFY,
SHMBOF_NODEFAULT | SHMBOF_NOTIFY));
This line sends a SHCMBM_OVERRIDEKEY message to the MenuBar control owned by hWnd. The wParam parameter, VK_TBACK, indicates the key being overridden. The MAKELPARAM macro forms a DWORD from two 16-bit words. The first parameter of MAKELPARAM, which will become the low word of lParam, is the mask field. Here, the SHMBOF_NODEFAULT flag is used to indicate that the override state of the Back key is to be set or cleared. The SHMBOF_NOTIFY flag indicates that the notification flag will also be set or cleared. The upper word of lParam is created from the second parameter of the MAKELPARAM macro. Here, the flag SHMBOF_NODEFAULT indicates that the Back key will be overridden. The second flag, SHMBOF_NOTIFY, tells the MenuBar to notify its parent when the Back key is pressed.To override the Back key but not be notified when it's pressed, the following line could be used:
SendMessage (SHFindMenuBar (hWnd), SHCMBM_OVERRIDEKEY, VK_TBACK,
MAKELPARAM (SHMBOF_NODEFAULT | SHMBOF_NOTIFY,
SHMBOF_NODEFAULT));
To restore the Back key to its original function, the following line could be used:
SendMessage (SHFindMenuBar (hWnd), SHCMBM_OVERRIDEKEY, VK_TBACK,
MAKELPARAM (SHMBOF_NODEFAULT | SHMBOF_NOTIFY,
0));
In the previous line, the first parameter of MAKELPARAM (the mask bits) indicates that the message is going to set the state of both the default action and the notification state of the button. The second MAKELPARAM parameter is 0, which indicates that the default action is to be restored and that notification is to be sent to the MenuBar owner.When WM_HOTKEY is received by the window, the wParam value contains an ID value for the key that was pressed. The IDs reported by the MenuBar are
Key | Value |
---|---|
VK_TSOFT1 | 0 |
VK_TSOFT2 | 1 |
VK_TBACK | 2 |
VK_TVOLUMEUP | 3 |
VK_TVOLUMEDOWN | 4 |
VK_TRECORD | 5 |
The lParam value also indicates the key but in a different way. The high word of lParam will contain the virtual key code of the key that was pressed. The lower word of lParam contains the modifier flags for the key. The only flag that is of interest on the Smartphone is MOD_KEYUP, which is set if the key is being released. Other modifier flags are documented, such as the Shift, Alt, and Control keys, but those flags are seldom used because the Smartphone doesn't have these keys.For windows that contain edit controls, the notification of a Back key press needs to be forwarded to the proper child control. This forwarding is done by using a SHCMBM_OVERRIDEKEY message to redirect the Back key. Once the key is overridden, a press of the key sends a WM_HOTKEY message to the owner of the MenuBar. To fully support passing the backspace to the edit control, each WM_HOTKEY message received as a result of a Back key press needs to be forwarded to the control with the function SHSendBackToFocusWindow, defined as
void SHSendBackToFocusWindow (UINT uMsg, WPARAM wp, LPARAM lp);
The three parameters are the message being handled as well as the wParam and lParam values for the message. The following code fragment shows the use of this function.
case WM_HOTKEY:
if (HIWORD (lParam) == VK_TBACK)
SHSendBackToFocusWindow (wMsg, wParam, lParam);
}
The override state of a key changed by a SHCMBM_OVERRIDEKEY message is removed when the corresponding MenuBar control is destroyed, so there's no need to manually restore the default state before the window is destroyed.