Adapting Application Menus to the CE User Interface

Wednesday Oct 30th 2002 by Nancy Nicolaisen
Share:

Learn about the tools and techniques for porting a lavish desktop Windows user interface into a much more compact world - the one viewed through the three inch screen of a typical Pocket PC. Specifically, learn how to migrate menus.

In this tutorial, we introduce tools and techniques for porting a lavish desktop Windows user interface into a much more compact world - the one viewed through the three inch screen of a typical Pocket PC. CE's tiny screens demand a symbolic, metaphorical sort of communication with users, which is unlike the verbose styles encouraged by Win32 and MFC.

On the desktop, we use multiple layers of discursive menu captions and provide many ways to achieve a given result. By contrast, a good CE application is concise and focused, and this is immediately evident when we look at the appearance and function of a CE application's menu. Here are some useful porting guidelines for redesigning a typical Win32 application menu:

  • Never use a text caption if you can reasonably use a bitmap button instead
  • Minimize dropdown menu choices
  • Replace nested menus with dynamically constructed and destroyed command bars
  • Eliminate floating menus
  • Eliminate redundant user interface command mechanisms

Unsurprisingly, the CE menu API is an abbreviated subset of the Win32 menu constructs. CE meuns are based largely on the use of command bars and the controls they contain. In the MenuBar example, we'll show you how to program the full range of CE command bar controls. In this installment, you'll see how to:

  • Register and initialize the custom control library
  • Create the command bar
  • Dynamically modify a command bar based menu
  • Add Command Bar Buttons
  • Load button images
  • Use Standard Button Images
  • Evaluate code you are porting for inappropriate menu strategies

The MenuBar Example

Let's begin by examining the alternatives for menus that are determined to be inappropriate for direct porting from Win 32. Here is a listing of the files for the MenuBar Project, an example that demonstrates how to implement the functionality of complex or deeply nested menus using Command Bars.

MenuBar's WinMain()

A lot of the startup code for a Windows CE application is identical to that of Win32. Here's one notable exception, depicted in this code excerpt from WinMain():

    INITCOMMONCONTROLSEX icex; //this struct is used to init the                                //common controls dll    //init the structure     memset(&icex, 0x0, sizeof(INITCOMMONCONTROLSEX));    //windows ce requires explicit init of the common controls    icex.dwSize = sizeof (INITCOMMONCONTROLSEX);     //just load what we need     icex.dwICC = ICC_COOL_CLASSES;    InitCommonControlsEx (&icex);

You must explicitly register common controls classes before your program can use them. The InitCommonControlsEx() function takes the structure INITCOMMONCONTROLSEX as its only parameter. The value of icex.dwICC determines which group of controls is registered in this call. If you make more than one call to InitCommonControlsEx(), control registration is cumulative. You can OR the flags to register more than one group at time. ( e.g ICC_BAR_CLASSES | ICC_COOL_CLASSES )

Table 1 - Win CE supported control types flags

Control Type FlagsControls Registered
ICC_BAR_CLASSES Loads toolbar, status bar, trackbar and command bar classes.
ICC_COOL_CLASSES Loads rebar control class.
ICC_DATE_CLASSES Loads date and time-picker control class.
ICC_LISTVIEW_CLASSES Loads list view and header control classes.
ICC_PROGRESS_CLASS Loads progress bar control class.
ICC_TAB_CLASSES Loads tab control classes.
ICC_TREEVIEW_CLASSES Loads tree view control classes.
ICC_UPDOWN_CLASS Loads Up-Down control class.

Table 2- INITCOMMONCONTROLSEX Flags Not Supported By Windows CE

Non Portable Win32 Flags
ICC_ANIMATE_CLASS
ICC_HOTKEY_CLASS
ICC_INTERNET_CLASSES
ICC_USEREX_CLASSES

One more thing, before we go on. Notice that I used memset() to initialize the structure contents to 0x0 and sizeof (INITCOMMONCONTROLSEX) to set the size of the structure in the icex.dwsize member. Even where the documentation says it isn't necessary, I have found that initializing and setting the size member of structures passed as parameters to be a very important defense against erratic behavior.

WinMain() calls MyRegisterClass(), and InitInstance(), both of which do routine and familiar tasks. There are a few differences to note that have to do with menu creation, however. Notice this line in MyRegisterClass(), where the members of the WNDCLASSstructure are being set.

//set the pointer to the menu resource name = 0    wc.lpszMenuName      = 0;

We set this window class parameter to 0 because we are going to take over the construction and display of the menu by creating a Command Bar control and positioning it where the menu would normally appear.

Also, note these lines at the end of InitInstance():

    //now show the window and make sure it gets painted    ShowWindow(hWnd, nCmdShow);    UpdateWindow(hWnd);   //if we have a command bar, show it now    if (hwndCB)      CommandBar_Show(hwndCB, TRUE);

The reason we have to explicitly tell the command bar to show itself is that Windows doesn't automatically handle it's update and display. In contrast to a Win32 style menu, a command bar has no "blueprint" in the resource file that tells Windows how to build it and where to put it. Also, it has to be painted onto the main window after that window becomes visible.

Creating the Command Bar

The first time we create the command bar based menu is in response to the WM_CREATE Message, which we process in the message switch of WndProc().

case WM_CREATE:  hwndCB = CommandBar_Create( hInst,                               hWnd, 1);       CommandBar_InsertMenubar( hwndCB,                             hInst,                             IDM_MENU, 0);  CommandBar_AddAdornments(hwndCB, 0, 0);  break;

Here's something to keep in mind with command bars ( and also with their cousins, command bands.) Creating a command bar reserves some real estate and sets up a container in which you can place other controls and interface elements. Most of the work of your program will entail dealing with the objects you've placed in the command bar. You can only manipulate those objects by using the command bar's access functions to do so.

         hwndCB = CommandBar_Create(hInst, hWnd, 1);     

This line creates an empty command bar. The parameters, in the order shown, are the instance handle, the main window's handle, and the command bar's ID, which we set to 1. The return value is the handle to the command bar.

         CommandBar_InsertMenubar(hwndCB, hInst, IDM_MENU, 0);

This line sets a menu resource into the command bar. The parameters, in the order shown, are the handle to the command bar, the instance handle, the resource ID of the menu to load, and the zero based index of a button on the command bar. The menu will be loaded to the left of the button specified by the index. It is possible to load more than one menubar in a command bar, but this contravenes the Windows CE design guidelines, so it's not a good practice. According to the guidelines, there should be one menubar per command bar and it should be the leftmost element.

         CommandBar_AddAdornments(hwndCB, 0, 0);

This bit of code is a real powerhouse, and it has some implications of which it's worth being aware. "Adornments" are the optional controls that appear at the right hand side of a menu based on a command bar. Most command bars will include the Close button, and that is the one we are adding here. You may optionally include the OK and the Help buttons as well.

Table 3- CommandBar_AddAdornments Flags

Adornment FlagWhat It AddsCommand Message
CMDBAR_HELPHelp buttonWM_HELP
CMDBAR_OKOK buttonWM_COMMAND (with IDOK as the message identifier

The Close, OK and Help buttons exist not only to provide the main window with messages, but can also replace the same buttons in a dialog box. This means that your dialogs can be smaller. Command bar elements each occupy a specific region on the command bar and this location is important to the internal management of the command bar's controls. Always add the command bar adornments last, that is to say, after all the rest of the command bar's control elements have been added. This will ensure they have sole claim to the real estate at the rightmost edge of the command bar.

Dynamically Modifying the Command Bar Based Menu

When we initially created the command bar menu, we loaded the menu resource IDM_. This resource file fragment shows the content of the View menu item.

    POPUP "View"    BEGIN        MENUITEM "Add Menu Buttons",            IDM_MENU_BTNS        MENUITEM "Add Combo",                   IDM_COMBO        MENUITEM "Add Dropdown Button",         IDM_DROP_BTN        MENUITEM "Add Check Button",            IDM_CHECK_BTN        MENUITEM "Add Radio Buttons",           IDM_RADIO_BTNS    END

The MenuBar example let's you "modify" the command bar menu on the fly, adding the different kinds of elements shown in menu choices. In fact, what we have to do is destroy the command bar and create a new one each time we get one of these requests from the user. First we'll go thru the code that creates and disp[lays the new kinds of command bar elements, then we'll look at how the menu items communicate with the main window.

Adding Command Bar Buttons

Here's some great news about this whole command bar based menu idea. The code for adding buttons to the command bar, any kind or number of buttons, is always the same. It looks like this:

    //Add the buttons    CommandBar_AddButtons(hwndCB, 4, &tbCmdButtons);

The parameters, in the order shown, are the handle to the command bar, the number of buttons to add, and the address of an array of TBBUTTON structures. As you may see, the TBBUTTON structure is the key to the process. Here's the declaration of the TBBUTTON structure.

typedef struct _TBBUTTON {          int iBitmap;   //Resource ID of button's bitmap image         int idCommand; //command Id for this button         BYTE fsState;  //Initial state of the button         BYTE fsStyle;  //Style flags encode button behaviors         DWORD dwData;  //point to optional user data         int iString;   //pointer to optional caption string} TBBUTTON

Managing the insertion of buttons into a command bar is mostly a matter of loading the images that are displayed on the buttons and correctly initializing an array of these structures.

Loading The Button Images

There are two basic kinds of images you can use on buttons: the ones you create and the ones you borrow. Let's look at the way to use images of your own first.

// Bitmap//IDB_ONE_TWO             BITMAP  DISCARDABLE     "one_two.bmp"

This fragment of the menubar.rc file associates the constant IDB_ONE_TWO with a bitmap file named one_two.bmp. IDB_ONE_TWO contains two contiguous bitmap images, each 16x16. Here are two important things to know about bitmap button images. They must be exactly 16x16 pixels or they won't load properly. Unlike in other versions of Windows, you don't get any breaks when you try to load images that aren't correctly sized. Also, you should be very conservative about the use of color in these bitmaps. There are many black and white CE devices in the hands of users. Loading unsupported color bitmaps into a menu bar may well be catastrophic. Choosing bitmap colors from a 2 bit grayscale palette will always work, everywhere.

We make these images available to the command bar like this:

   //add bitmaps for buttons   CommandBar_AddBitmap(hwndCB, hInst, IDB_ONE_TWO, 2, 0, 0);

The parameters, in the order shown, are the handle to the command bar, the instance handle, the resource ID of the bitmap, the number of bitmap images aggregated in the bitmap file, and two reserved parameters which must be set to 0.

Adding bitmaps to a command bar makes each individual bitmap part of a list. You reference individual bitmaps by their zero based index in the list. If you call CommandBar_AddBitmap() more than once, these indexes are cumulative. By this I mean that if we followed the function call above with one like this:

   //add 2 more bitmaps for buttons   CommandBar_AddBitmap(hwndCB, hInst, IDB_THREE_FOUR, 2, 0, 0);

After the second call returned, we'd have four bitmaps in the command bar's list . The index of the 1st bitmap in IDB_ONE_TWO would be 0, and the index of the first bitmap in IDB_THREE_FOUR would be 2.

Its very important to make sure you add a number of bitmaps at least equal the number of buttons specified in CommandBar_AddButtons().

Using Standard Button Images

You can also get bitmap images by "borrowing" them from the Windows CE small color bit map lists. This strategy has a couple of big advantages. First the bitmaps are already there, so you don't have to use any memory to get them. Also, you are assured of their color safety and proper size. Here's how you make the standard bitmap list available.

    // Add standard bitmaps for buttons.    CommandBar_AddBitmap (hwndCB, HINST_COMMCTRL,                          IDB_STD_SMALL_COLOR,                          0, 0, 0);

The parameters, in the order shown, are the handle to the command bar, a constant representing the instance handle to the common control dll, the resource ID of a system bit map list, with the remaining three parameters are always set to zero. The resource identifier IDB_STD_SMALL_COLOR references a bitmap list that contains the "standard" bitmaps. If you use the resource identifier IDB_VIEW_SMALL_COLOR, you get access to the "view" bitmap list.

Table 4 - Individual Bitmap Resource Identifiers for Standard Bitmaps

STD_COPY
STD_CUT
STD_DELETE
STD_FIND
STD_FILENEW
STD_FILEOPEN
STD_FILESAVE
STD_HELP
STD_PROPERTIES
STD_REDOW
STD_REPLACE
STD_PASTE
STD_PRINT
STD_PRINTPRE
STD_UNDO

Table 5 - Individual Bitmap Resource Identifiers for View Bitmaps

VIEW_DETAILS
VIEW_LARGEICONS
VIEW_LIST
VIEW_SMALLICONS
VIEW_SORTDATE
VIEW_SORTNAME
VIEW_SORTSIZE
VIEW_SORTTYPE

Like your own bitmaps, adding the system's bitmaps to the command bar list is cumulative. When you want to reference a specific bitmap you add its individual constant to the zero based offset of the last bitmap loaded before it.

Putting It All Together

Here are the three steps for adding buttons to a command bar:

Initialize an array of TBBUTTON structures. There must be on for each buttonAdd button bitmap images to the command bar.

Add the buttons

This code adds buttons with custom bitmap images

// Command band button initialization structure, custom bitmapsTBBUTTON tbCmdButtons[] = {//  BitmapIndex   Command  State     Style    UserData  String    {0,           IDM_ONE,  TBSTATE_ENABLED,                                                 TBSTYLE_BUTTON, 0,    0},    {1,           IDM_TWO,  TBSTATE_ENABLED,                                      TBSTYLE_BUTTON, 0,    0},};HWND    AddCustomButtons( HWND hWnd ){     //we have to destroy the old one before expanding     CommandBar_Destroy( hwndCB );      hwndCB = CommandBar_Create(hInst, hWnd, 800);         // Insert the menu band     CommandBar_InsertMenubar(hwndCB, hInst, IDM_MENU, 0);     //add bitmaps for buttons     CommandBar_AddBitmap(hwndCB, hInst, IDB_ONE_TWO, 2, 0, 0);     //Add the buttons     CommandBar_AddButtons(hwndCB, 2, tbCustomButtons);     // Add exit button to command band control.     CommandBar_AddAdornments (hwndCB, 0, 0);     CommandBar_Show(hwndCB, TRUE);     return hwndCB;}

This code adds buttons using system bitmaps

// Command band button initialization structure, std bitmapsTBBUTTON tbCustomButtons[] = {    {STD_UNDO,  IDM_UNDO, TBSTATE_ENABLED,      TBSTYLE_BUTTON, 0,    0},    {STD_REDOW, IDM_REDO, TBSTATE_ENABLED,      TBSTYLE_BUTTON, 0,    0},};HWND    AddStdCmdButtons( HWND hWnd ){    //we have to destroy the old one before expanding    CommandBar_Destroy( hwndCB );     hwndCB = CommandBar_Create(hInst, hWnd, 1);    // Insert the menu band    CommandBar_InsertMenubar(hwndCB, hInst, IDM_MENU, 0);    // Insert buttons.    CommandBar_AddBitmap (hwndCB, HINST_COMMCTRL,                             IDB_STD_SMALL_COLOR, 0, 0, 0);    //Add the buttons    CommandBar_AddButtons(hwndCB, 2, tbStdCmdButtons);    // Add exit button to command band control.    CommandBar_AddAdornments (hwndCB, 0, 0);    CommandBar_Show(hwndCB, TRUE);    return hwndCB;}

This code adds buttons with automatic "radio button" behavior

// Command band button initialization structureTBBUTTON tbRadioButtons[] = {//  BitmapIndex Command  State     Style    UserData  String    {0,         IDM_ONE, TBSTATE_ENABLED,                          TBSTYLE_GROUP | TBSTYLE_CHECKGROUP,                                                  0,    0},    {1,         IDM_TWO, TBSTATE_ENABLED,                                    TBSTYLE_CHECKGROUP,                                                    0,    0},};HWND    AddRadioButtons(HWND hWnd){    //we have to destroy the old one before expanding    CommandBar_Destroy( hwndCB );     hwndCB = CommandBar_Create(hInst, hWnd, 1);         // Insert the menu band    CommandBar_InsertMenubar(hwndCB, hInst, IDM_MENU, 0);    //add bitmaps for buttons    CommandBar_AddBitmap(hwndCB, hInst, IDB_ONE_TWO, 2, 0, 0);    //Add the buttons  the button structs define the button    //radio behaviors    CommandBar_AddButtons(hwndCB, 2, tbRadioButtons);    // Add exit button to command band control.    CommandBar_AddAdornments (hwndCB, 0, 0);    CommandBar_Show(hwndCB, TRUE);    return hwndCB;}

This code adds buttons with "check box" behavior

// Command band button initialization structureTBBUTTON tbCheckButton[] = {//  BitmapIndex Command    State     Style    UserData  String    {0,         IDM_ONE,    TBSTATE_ENABLED,                                      TBSTYLE_CHECK, 0,    0},};//Command band button initialization structure- check box behaviorTBBUTTON tbDropButton[] = {//  BitmapIndex Command  State     Style    UserData  String    {0,         IDM_ONE, TBSTATE_ENABLED,                                     TBSTYLE_DROPDOWN,                                              0,    0},};HWND    AddCheckButton(HWND hWnd){    //we have to destroy the old one before expanding    CommandBar_Destroy( hwndCB );     hwndCB = CommandBar_Create(hInst, hWnd, 1);         // Insert the menu band    CommandBar_InsertMenubar(hwndCB, hInst, IDM_MENU, 0);    //add bitmaps for buttons    CommandBar_AddBitmap(hwndCB, hInst, IDB_ONE_TWO, 2, 0, 0);    // Insert buttons.    CommandBar_AddBitmap (hwndCB, HINST_COMMCTRL,                           IDB_STD_SMALL_COLOR,                          0, 0, 0);    //Add the buttons the flags in the button structs     //specify the check box behaviors    CommandBar_AddButtons(hwndCB, 1, tbCheckButton);    // Add exit button to command band control.    CommandBar_AddAdornments (hwndCB, 0, 0);    CommandBar_Show(hwndCB, TRUE);    return hwndCB;}

Using a little creativity and the basic toolbar buttons demonstrated above, you'll be able to duplicate most of your Win32 application's menu functionality. In the next installment, we'll complete the picture by showing you how to add Combo Buttons and DropDown buttons to the CE command bar.

Evaluating Menu Strategies

With a little foresight, porting menu functionality can be smooth sailing. When examining your Win32 code with an eye to porting to CE, use these tips to spot potential problem areas:

Possible Porting ObstacleWorkarounds
Nested menus and floating menus
  1. Drop some of the menu functionality
  2. Use a dynamically constructed menu bar in place of the nested or floating menu
Long menu captions
  1. Replace caption with a bitmap
  2. Abbreviate
Too many menu choices for screen
  1. Eliminate redundant or unnecessary items
  2. Redesign menu items as dialog
  3. Use resizable command bands

SOURCE CODE: CE02.txt

About the Author

Nancy Nicolaisen is a software engineer who has designed and implemented highly modular Windows CE products that include features such as full remote diagnostics, CE-side data compression, dynamically constructed user interface, automatic screen size detection, entry time data validation.

In addition to writing for Developer.com, she has written several books including Making Win 32 Applications Mobile. She has also written numerous articles on programming technology for national publications including Dr. Dobbs, BYTE Magazine, Microsoft Systems Journal, PC Magazine; Computer Shopper, Windows Sources and Databased Advisor.

# # #

Share:
Home
Mobile Site | Full Site
Copyright 2017 © QuinStreet Inc. All Rights Reserved