Radek Chalupa   konzultace a školení programování, vývoj software na zakázku

Ovládací prvky na dialogovém okně - úvod.

13.1.2002

V předchozím článku jsme si ukázali, jak vytvořit ten nejjednodušší dialog, založený na standardní třídě dialogových oken. Umíme už zjistit, jakým způsobem uživatel dialog ukončil, tedy v typickém případě rozlišit stisk "OK" a "Storno". V tomto pokračování se začneme zabývat prací s běžnými dialogovými prvky. Jedním ze specifických rysů dialogového okna je, že prvky na něm umístěné definujeme ve skriptu zdrojů a nemusíme je vytvářet "ručně" pomocí CreateWindowEx. Výhodou je kromě zjednodušení kódu snadná práce s rozmístěním prvků na dialogu (pokud samozřejmě používáme nějaký vizuální editor zdrojů). Začneme tím, že si na dialog umístíme jedno editační pole (IDC_EDIT1), tlačítko (IDC_ZOBRAZ_TEXT), prvek Static text (IDC_KOPIE) a check-box (IDC_SLEDOVAT). Identifikátory prvků uvedené v závorkách si samozřejmě můžete zvolit podle svého, pak budete muset upravit i zdrojový kód. Cílem v této fázi bude naučit se jak získat text obsahu edit-boxu, jak zjistit okamžitý stav zaškrtnutí check-boxu a jak zachytit událost, kdy dojde ke změně obsahu edit-boxu, tedy když uživatel zapíše či smaže znak. Řekněme, že budeme chtít po stisknutí tlačítka přenést aktuální obsah edit-boxu do titulku hlavního okna aplikace. Tím se také přesvědčíme o tom, že i když je otevřen modální dialog, další okna aplikace mohou běžně přijímat zprávy a reagovat na ně, i když pro uživatele jsou v té době nedostupná. V proceduře dialogu již máme handler zprávy WM_COMMAND, kde zachytáváme stisk tlačítek "OK" a "Storno". Přidejme tedy do handleru WM_COMMAND následující case:
case IDC_ZOBRAZ_TEXT:
  GetDlgItemText(hwndDlg, IDC_EDIT1, chText, 200);
  SendMessage(g_hwndMain, WM_SETTEXT, 0, (LPARAM)chText);
  break;
To je vše co musíme udělat pro přenesení textu edit-boxu do titulku hlavního okna. Jak je vidět, pro získání textu prvku na dialogu můžeme použít funkci GetDlgItemText:
UINT GetDlgItemText(
  HWND hDlg,       // handle okna dialogu
  int nIDDlgItem,  // identifikátor prvku
  LPTSTR lpString, // buffer pro text
  int nMaxCount    // maximální počet zkopírovaných znaků
);
Samozřejmě bychom mohli použít již dříve uvedenou funkci GetWindowText. Pak bychom ale museli získat handle (HWND) prvku (edit-boxu). K získání handle kteréhokoli prvku na dialogu slouží funkce GetDlgItem:
HWND GetDlgItem(
  HWND hDlg,       // handle okna dialogu
  int nIDDlgItem   // identifikátro prvku
);
Řekněme si ještě o funkci, která nám vrátí číselnou hodnotu (pokud ji lze převést) obsahu prvku dialogu. U edit-boxu je možné a výhodné pro tento případ nastavit v editoru zdrojů vlastnost "Number" odpovídající stylu okna ES_NUMBER. Příslušnou funkcí je pak GetDlgItemInt:
UINT GetDlgItemInt(
  HWND hDlg,           // handle okna dialogu
  int nIDDlgItem,      // identifikátor prvku
  BOOL *lpTranslated,  // indikátor úspešného převodu
  BOOL bSigned         // zda jde o hodnotu se znaménkem
);
Tato funkce nám vrátí číselnou hodnotu obsahu prvku, pokud má obsah číselný smysl. Úspěšnost převodu je uložena do parametru lpTranslated. Dále si řekneme, jak zjistíme v požadovaném okamžiku stav zaškrtnutí check-boxu. Zde zjistíme, že jednou z nejčastěji používaných funkcí je SendMessage, tedy funkce která pošle zvolenému oknu zprávu, která může sloužit k "ovládání" okna, tedy například nastavení textu, přidání řádku do list-boxu a stovky podobných. Kromě toho tato funkce vrací výsledek zpracování zprávy v příslušném okně. Mnoho zpráv je totiž dotazovacích, kterými požadujeme od cílového okna nějakou informaci, například právě stav zaškrtnutí check-boxu. V dialogových oknem můžeme místo SendMessage použít funkci SendDlgItemMessage:
LRESULT SendDlgItemMessage(
  HWND hDlg,      // handle okna dialogu
  int nIDDlgItem, // identifikátor prvku
  UINT Msg,       // kód zprávy
  WPARAM wParam,  // 1. parametr zprávy
  LPARAM lParam   // 2. parametr
);
V této funkci nemusíme uvádět handle okna, ale místi toho identifikátor prvku dialogu a systém si handle zjistí sám. Je to obdobná jako u vztahu funkcí GetWindowText a GetDlgItemText. V případě zjištení stavu check-boxu použijeme zprávu BM_GETCHECK. Identifikátor zprávy začíná na "BM", což je ze slov "button message". Znamená to, že check-box je prvek třídy "BUTTON". Návratová hodnota zprávy (tedy to co nám vrátí funkce SendMessage nebo SendDlgItemMessage) pak může být jednou ze 3 hodnot:
  • BST_CHECKED - check-box je zaškrtnut
  • BST_UNCHECKED - check-box není zaškrtnutý
  • BST_INDETERMINATE - check-box je "zašedlý", tedy v neurčitém stavu
V této souvislosti si řekněme o "opačné" zprávě, tedy nastavení stavu check-boxu programově. Tou zprávou je BM_SETCHECK, a v parametru wParam zprávy SendDlgItemMessage uvedeme jeden z výše uvedených stavů, do kterého chceme check-box nastavit. Toto počáteční nastavení se obvykle realizuje v handleru zprávy WM_INITDIALOG, kterou obdržíme pouze jednou "na začátku" po vytvoření dialogového okna, těsně před tím, než je dialog zobrazen. Nakonec si ukážeme, jak sledovat jakékoli změny v obsahu editačního pole. V tomto případě je posílána tzv. oznamovací (notifikační) zpráva EN_CHANGE. Tato a další oznamovací zprávy nejsou ve skutečnosti samostatnými zprávami, ale jsou posílány jako horní WORD parametru wParam zprávy WM_COMMAND. Vše si ukážeme na příkladě, ve kterém budeme změny v editačním poli "kopírovat" do prvku "static text", tedy při každé zachycení změny obsahu editu (IDC_EDIT1), zjistíme jeho obsah a ten nastavíme jako text prvku static text (IDC_KOPIE). Toto budeme provádět pouze pokud je v daném okamžiku zaškrtnut check-box (IDC_SLEDOVAT). Do handleru WM_COMMAND si přidejme násjedující kód, který vše zrealizuje:
case IDC_EDIT1:
  if ( HIWORD(wParam) == EN_CHANGE )
  {
    if ( SendDlgItemMessage(hwndDlg, IDC_SLEDOVAT, BM_GETCHECK, 0,0) == BST_CHECKED )
    {
      GetDlgItemText(hwndDlg, IDC_EDIT1, chText, 200);
      SetDlgItemText(hwndDlg, IDC_KOPIE, chText);
    }
  }
  break;
Výsledek "v akci" vidíte na následujícím obrázku:

win-api-13

Ještě si můžeme nastavit výchozí stav check-boxu na zaškrtnutý v handleru zmíněné zprávy WM_INITDIALOG:
case WM_INITDIALOG:
  SendDlgItemMessage(hwndDlg, IDC_SLEDOVAT, BM_SETCHECK, BST_CHECKED, 0);
  break;
Tolik pro toto pokračování a příště se samozřejmě budeme prvkům na dialogu  dále věnovat, neboť jde o základní část většiny programů, tedy uživatelské rozhraní. Doprovodný projekt (Visual C++ 6) je ke stažení zde: win_api_13.zip