WinCE Menüs

Alle Jahre wieder…
tauchen die gleichen UI Probleme erneut auf.

Ich hatte doch schon mal Menüs in Windows CE angefangen … und wieder abgeändert und wieder noch etwas geändert … und dann lief es nicht mehr.
Oder … hat es überhaupt je richtig funktioniert ?

Es wird Zeit für eine Detailanalyse.


Windows CE ist nicht Windows CE, sondern es ist Windows CE oder (das alte) Windows Mobile. Und im Falle von Windows Mobile ist es ein “Smartphone” von vor 2007 mit Bildschirmauflösungen von 320x240, anders gesagt: Briefmarken-klein.

Jetzt kann man zwar mit CommandBar Controls “echte” Menüzeilen basteln, die dann aber auf diesen Kleingeräten unbenutzbare UIs bedeuten. Und das fällt einem nicht auf bei einem einzigen Menüeintrag, aber bei 3+ wird das schon sehr deutlich.

Die Pseudo-Shell-Menu-Bar

Die AGYShell von Windows Mobile/CE bringt allerdings ein ähnliches Feature mit, nämlich die beiden “Software-Buttons” am unteren Rand.
Gedacht waren diese “Tasten” für ein schnelles OK oder Cancel, bzw. Zurück und Weiter.
An die beiden schmalen Schaltflächen schmiegt sich in der Mitte auch noch der Keyboard-Button, mit dem man die virtuelle Tastatur ausfahren kann.

Idee: Popup-Menü durch Software-Button

Ich dachte mir also, dass das Programm-Menü (Datei, Bearbeiten, usw.) unter Windows Mobile nicht ständig angezeigt werden muss, sondern nur ein Menü Software-Button angezeigt wird, und erst beim Klick darauf, wird das eigentliche Menü als Popup-Menü ausgefahren.
Damit wird kein Platz unnötig belegt und die Programmlogik kann dann genau so aussehen, wie auf einer vollen Desktop App.

Resourcen-Zwang

Die Funktion SHCreateMenuBar erzeugt zwar diese Software-Buttons, doch sie verlangt eine Menü-Ressource in der Programmdatei, und die sieht z.B.: so aus:

1// IDs from shared header
2#define MY_MENU_ID            102
3#define MY_MENU_BUTTON_1_TEXT 103
4#define MY_MENU_BUTTON_2_TEXT 104
5#define MY_MENU_BUTTON_1_MSG  40000
6#define MY_MENU_BUTTON_2_MSG  40001
 1// .rc resource file:
 2STRINGTABLE  
 3BEGIN
 4  MY_MENU_BUTTON_1_TEXT        "OK"
 5  MY_MENU_BUTTON_2_TEXT        "HELP"
 6END
 7
 8MY_MENU_ID SHMENUBAR DISCARDABLE
 9BEGIN
10  MY_MENU_ID, 
11  2,
12
13  I_IMAGENONE, MY_MENU_BUTTON_1_MSG, 
14  TBSTATE_ENABLED, TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE,
15  MY_MENU_BUTTON_1_TEXT, 0, NOMENU,
16    
17  I_IMAGENONE, MY_MENU_BUTTON_2_MSG, 
18  TBSTATE_ENABLED, TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE,
19  MY_MENU_BUTTON_2_TEXT, 0, 0,
20END

Im C Code kann man nun auf das MY_MENU_ID Makro verweisen:

 1HWND hwnd_of_windows;
 2SHMENUBARINFO mbi;
 3RECT rc_window, rc_menu;
 4
 5// create menu bar
 6mbi.cbSize = sizeof(mbi);
 7mbi.hwndParent = hwnd_of_window;
 8mbi.dwFlags = 0;
 9mbi.hInstRes = (HINSTANCE)GetModuleHandle(NULL);
10mbi.nToolBarId = MY_MENU_ID;
11SHCreateMenuBar(&mbi);
12
13// resize main window to exclude menu-bar
14GetWindowRect(hwnd_of_window, &rc_window);
15GetWindowRect(mbi.hwndMB, &rc_menu);
16rc_window.bottom -= (rc_menu.bottom - rc_menu.top);
17MoveWindow(hwnd_of_windows, rc_window.left, rc_window.top, 
18    rc_window.right - rc_window.left, rc_window.bottom - rc.top, 
19    FALSE);
20DrawMenuBar(hwnd_of_window);

Anschließend lässt sich in der WM_COMMAND Nachricht die MY_MENU_BUTTON_1_MSG und MY_MENU_BUTTON_2_MSG ID wieder abfangen. An dieser Stelle kann man dann ein übliches Win32 Popup-Menü mit den eigentlichen Menüeinträgen anzeigen lassen.

Fazit

Dieser Workaround funktioniert tatsächlich, zumindest konnte ich im Device-Emulator nun meinen Texteditor laden, der dort sein Menü nur beim Klick auf den Software-Button anzeigt.
Und die gleiche Anwendungslogik erzeugt am herkömmlichen Desktop weiter ein Fenster-Menü.

Mein Anliegen ist damit gelöst … doch man lernt daraus wieder, wie verdammt schwierig es ist, mit “einer API” zwei unterschiedliche Plattformen auf den gleichen Nenner zu bringen.

Das erklärt auch, warum der Trend zu Webanwendungen geht, denn dort ist “Responsive Design” mit ein paar Zeilen CSS erreichbar, während man auf der nativen Seite lange hin und her probieren muss, bis man eine akzeptable Lösung gefunden hat.

Nachtrag: RC in CMAKE

Und wie bindet man jetzt Resource-Dateien .rc in CMake in ein Projekt ein?

Einfach per target_sources:

1if(WIN32)
2  file(GLOB RC_RESOURCE_FILES
3    "*.rc"
4  )
5  target_sources(${PROJECT_NAME} PRIVATE ${RC_RESOURCE_FILES})
6endif(WIN32)

Denn ohne die Resource in der EXE schlägt der Aufruf von SHCreateMenuBar fehl.