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 Flags | Controls 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 Flag | What It Adds | Command Message |
CMDBAR_HELP | Help button | WM_HELP |
CMDBAR_OK | OK button | WM_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 16×16. Here are two important things to know about bitmap button images. They must be exactly 16×16 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 Obstacle | Workarounds |
Nested menus and floating menus |
|
Long menu captions |
|
Too many menu choices for screen |
|
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.
# # #