Programování pro PocketPC - Aplikace založené na dialogu 5/6

Aplikace založené na dialogu jsou tím naprosto nejjednodušším druhem aplikací pro počítače s operačním systémem založeným na Microsoft Windows, včetně zařízení PocketPC. Ukážeme vám, jak je naprogramovat a k čemu všemu mohou sloužit.
Aplikace založené na dialogu jsou tím naprosto nejjednodušším druhem aplikací pro počítače s operačním systémem založeným na Microsoft Windows, včetně zařízení PocketPC. V této minisérii budeme mluvit o aplikacích založených na různých typech dialogů a ukážeme si, že je velmi jednoduché je naprogramovat a jdou používat nejen k vytváření jednoduchých aplikací, ale například i k "simulaci" uživatelského rozhraní PalmOS.

Přepínání mezi dialogy ve stylu Palm Pilotů

Uživatelské rozhraní počítačů Palm Pilot nemá z úsporných důvodů koncepci "hlavního okna aplikace". Místo toho je složeno z řady formulářů, z nichž vždy je alespoň jeden zobrazen na displeji.

Pokud bychom chtěli teoreticky vytvářet aplikaci se společným uživatelským rozhraním na počítači s PalmOS i na PocketPC, pak je přirozené použít právě aplikaci založenou na dialogu. Každý formulář PalmOS budeme simulovat jedním dialogem PocketPC.

V naší aplikaci musíme vyřešit, jak mezi sebou jednoduchým způsobem přepínat dialogy na displeji. Nabízím vám jedno z možných řešení, na kterém si ukážeme několik dalších vlastností dialogových oken.

Bude se nám hodit i (doteď nepoužité) hlavní okno aplikace, které bude v pozadí a které bude zajišťovat zprávy společné pro naši aplikaci. Současně zabrání nepříjemnému blikání při přepínání mezi dialogy. Připomenu úplně standardní funkci WinMain:

#define WM_PREPNIDIALOG (WM_USER + 16) // Konstantni jmeno okna a trida okna TCHAR* g_mainWinName = TEXT("Aplikace s dialogy"); TCHAR* g_mainWinClass = TEXT("dialogyV1"); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { g_hInst = hInstance; HWND other = FindWindow(g_mainWinClass, g_mainWinName); if(other) { SetForegroundWindow(other); return 0; } WNDCLASS wc = { CS_VREDRAW|CS_HREDRAW, MainWindowProc, 0, 0, g_hInst, NULL, NULL, (HBRUSH)(COLOR_WINDOW + 1), NULL, g_mainWinClass }; RegisterClass(&wc); HWND hwndMain = CreateWindowEx( 0, g_mainWinClass, g_mainWinName, WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); ShowWindow(hwndMain, SW_MAXIMIZE); // Zobrazime prvni vybrany dialog PostMessage(hwndMain, WM_PREPNIDIALOG, (WPARAM)IDD_TABLE, (LPARAM)TableDlgProc); MSG msg; // Smycka zpracovani udalosti while(GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; }


Základem je implementace zpracování zprávy WM_PREPNIDIALOG, při kterém obsahuje wParam resource identifikator dialogu a lParam adresu funkce dialogu. Zpracovaní této události je vyřešeno nasledujícím způsobem:

case WM_PREPNIDIALOG: { int newid = DialogBox(g_hInst, (LPCTSTR)wParam, hwnd, (DLGPROC)lParam); switch(newid) { case IDD_TABLE: PostMessage(hwnd, WM_PREPNIDIALOG, (WPARAM)IDD_TABLE, (LPARAM)TableDlgProc); break; case IDD_OPTIONS: PostMessage(hwnd, WM_PREPNIDIALOG, (WPARAM)IDD_OPTIONS, (LPARAM)OptionsDlgProc); break; ... default: PostQuitMessage(0); break; } } return 0L;


Jak vidíme, tak na začátku zavoláme dialog, jehož resource identifikátor a adresa funkce byly ve zprávě předány. Poté vezmeme hodnotu, vrácenou funkcí DialogBox (je to druhý parametr funkce EndDialog) a podle této hodnoty zařadíme do fronty zpráv další zprávu WM_PREPNIDIALOG (nebo ukončíme aplikaci).

Pokud si ve funkci dialogu přejeme přepnout se do jiného dialogu, zavoláme funkci EndDialog s vhodným parametrem, například:

EndDialog(hDlg, IDD_OPTIONS);


Příklad si můžete stáhnout (ZIP) zde.

Povšimněte si prosím, že takto napsanou aplikaci není možné hladce ukončit z ovládacích panelů PocketPC.

Důvod je jednoduchý: pokud zavoláme modální (=blokující) dialog, je jeho rodič zablokován. Významem tohoto zablokování je zabránit uživateli interagovat s rodičovským oknem, je-li "nad ním" zobrazen dialog. Bohužel, je to právě ono rodičovské okno (v našem případě hlavní okno aplikace), kterému by byla zaslána zpráva WM_CLOSE, požadující ukončení aplikace.

Řešení je několik, ale nejjednodušší je přidat do každé funkce dialogu, v naší aplikaci zpracování zprávy WM_ERASEBKGND, při kterém opět "odblokujeme" hlavní okno aplikace:

case WM_ERASEBKGND: { HWND hOwner = GetWindow(hDlg, GW_OWNER); if(IsWindow(hOwner)) { EnableWindow(hOwner, TRUE); return FALSE; } } break;


Implementování společného mechanismu pro přepínání formulářů by byl vhodný základ pro vytváření multiplatformních aplikací; třeba tento příklad inspiruje některého čtenáře...


Příště ukončíme minisérii příkladem programování vícestránkových dialogů typu PropertySheet.