Radek ChalupaČlánkyŠkolení a konzultaceVývoj softwareFreewareKontakt

Hlavní nabídka okna.  9.1.2002

V minulých dílech jsme se naučili použít vlastní ikonu a kurzor a také již umíme ikonu (zatím tím nejjednodušším způsobem) zobrazit v klientské oblasti okna. V tomto pokračování se naučíme používat hlavní nabídku okna a zachytávat výběr některé položky nabídky. Přidáme si tedy do projektu nabídku jako resource typu menu. V editoru zdrojů si v menu vytvoříme položku "Soubor" s jediným příkazem "Konec" (identifikátor třeba ID_END) a položku nápověda s oblíbeným "O programu" (identifikátor ID_ABOUT). Jak nyní zobrazit nabídku v okně. Máme v zásadě 2 možnosti:

  • Nastavit menu jako vlastnost třídy - toto menu pak budou mít implicitně všechna okna této třídy (pokud jim neurčíme jinak)
  • Nastavit menu pouze konkrétnímu oknu při jeho vytvoření funkcí CreateWindowEx

Ukažme si nejprve 1. způsob. Jedimé, co musíme do našeho kódu přidat, je řádka (tučně zvýrazněná) ve funkci InitApp, kde nastavujeme prvky struktury WNDCLASSEX:

wc.lpszClassName = _MainClassName;
wc.lpszMenuName = MAKEINTRESOURCE(IDR_MAINMENU);
wc.style = CS_HREDRAW | CS_VREDRAW;

V tomto případě můžeme nechat ve funkci CreateWindowEx parametr hMenu NULL a menu bude zobrazeno. Nyní 2. způsob. Pokud při registraci třídy nebude menu zadáno, nebo pokud chceme u konkrétního okna toto "třídní menu" změnit, můžeme tak učinit ve funkci CreateWindowEx, která bude vypadat třeba následovně:

g_hwndMain = CreateWindowEx(0, _MainClassName,
  _AppName,
  WS_OVERLAPPEDWINDOW | WS_VISIBLE,
  100, 100, 350, 250,
  NULL, 
LoadMenu(g_hInstance, MAKEINTRESOURCE(IDR_MAINMENU)),
  g_hInstance, NULL);

Nyní tedy máme jedním ze dvou způsobů menu zobrazené, a musíme se naučit zachytávat výběr některé položky uživatelem. Zde se dostáváme k jedné nejběžnějších zpráv Windows, kterou je WM_COMMAND. Tato zpráva je poslána oknu, pokud například uživatel vybere položku menu, klikne na tlačítko umístěné v okně, použije nějakou klávesovou zkratku nebo pokud některý prvek posílá oznámení o změně stavu (například změna textu v edit-boxu). O těchto záležitostech si samozřejmě řekneme později. Nyní k výběru z nabídky. V tomto případě dolní WORD parametru wParam obsahuje identifikátor položky nabídky, v našem případě tedy například ID_END nebo ID_ABOUT. Nám nyní zbývá pouze v proceduře okna vytvřit handler zprávy WM_COMMAND takto:

 switch ( message )
  {
    case WM_COMMAND:
      switch ( LOWORD(wParam) )
      {
        case ID_ABOUT:
          MessageBox(hwnd, "Win API demo díl 9.nnRadek Chalupa, 2002",
            "O programu WinApi demo", MB_ICONINFORMATION);
          break;
        case ID_END:
          SendMessage(hwnd, WM_CLOSE, 0, 0);
          break;
      }
      break;
// atd....

Podívejme se nyní jak lze programově zavřít okno a ukončit aplikaci. Každé okno lze zrušit funkcí DestroyWindow:

BOOL DestroyWindow(
  HWND hWnd   // handle okna
);

Tato funkce se kromě zrušení daného okna postará o zrušení všech podřízených oken (např.edit-boxů a buttonů na dialogu), nabídky a dalších věcí (o nichž ztím nic nevíme) patřících oknu tak, že nezůstane neuvolněná paměť. Pokud takto zrušíme hlavní okno aplikace, dojde k ukončení celé aplikace. Tuto funkci bychom mohli použít v reakci na výběr položky ID_END z nabídky, avšak pokud místo toho pošleme oknu zprávu WM_CLOSE (jak je uvedeno ve výpisu) máme ještě možnost v handleru zprávy WM_CLOSE zobrazit například výzvu k uložení souboru nebo k potvrzení ukončení aplikace. Řeknete si možná že, totéž můžeme udělat přímo v handleru příkazu ID_END. To je samozřejmě pravda, ale v handleru WM_CLOSE zachytíme i požadavek na zavření okna pomocí kliknutí na systémovou ikonu okna nebo klávesovou zkratkou <alt>+F4. Je to tedy ideální "centalizované" místo pro "ošetřovací kód" před zavřením okna. Jak jsem jž naznačil, při použití funkce DestroyWindow zpráva WM_CLOSE poslána není a tak bychom obešli náš ukončovací kód, což by nemuselo být žádoucí. V této souvislosti si ukažme, jak se dotázat uživatele, zda si opravdu přeje zavřít okno a podle toho korektně reagovat. Pro dotaz použijeme již zmíněnou funkci MessageBox, která kromě prostého zobrazení zprávy může sloužit jako dotazovací dialog. Když se podíváte do dokumentace, zjistíte že 4. parametr funkce MessageBox může nabývat mnoha různých hodnot a jejich kombinací. Jedna sada příznaků určuje například, jaká ze systémových ikon má být v dialogu zobrazena. Pro náš dotaz použijeme MB_ICONQUESTION, což znamená že bude zobrazena ikona "otázka" na levé straně dialogu. Kromě tohoto kosmetického vylepšení je samozřejmě důležité zobrazit 2 tlačítka "Ano" a "Ne". V tom případě můžeme použít hodnotu MB_YESNO, nebo MB_YESNOCANCEL, která nám přidá ještě třetí tlačítko "Storno". Nyní už zbývá jen podle návratové hodnoty funkce MessageBox zjistit, které tlačítko uživatel stiskl, případně zda ukončil dialog klávesou <Escape>. V případě stisknutí tlačítka "Ano" funkce vrací hodnotu IDYES. Popis všech možných návratových hodnot je samozřejmě také v dokumentaci. Po tomto výkladu se již můžeme podívat jak bude vypadat handler zprávy WM_CLOSE v proceduře okna:

switch ( message )
{
  case WM_CLOSE:
    if ( MessageBox(hwnd, "Opravdu končit?", _AppName,
      MB_YESNO | MB_ICONQUESTION) != IDYES )
      return 0;
    break;

Když se podíváte na kód, vidíte že v případě že nebylo stisknuto tlačítko "Ano" vrátíme v proeduře okna hodnotu 0 místo abychom zprávu předali dále k výchozímu zpracování funkcí DefWindowProc. Takto můžeme jakoukoli zprávu zadržet, tedy zrušit její "účinek". Doprovodný projekt (Visual C++ 6) je ke stažení zde: win_api_9.zip


Komentář k článku

Jméno:


Email:


Zde můžeš napsat komentář k tomuto článku: (max. 1000 znaků)



Copyright © 2019 - Radek Chalupa