Adapting Application Menus to the CE User Interface
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.
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|
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.