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

Použití časovače. Vzhled prvků v novém stylu Windows.

11.1.2002

V minulém článku jsme se naučili vytvářet ty nejjednodušší dětská okna - prvky Windows, kterým byl editbox a tlačítko a také již umíme zachytávat zprávy od těchto prvků. V tomto díle si vytvoříme stavový řádek okna do kterého budeme vypisovat aktuální čas, abychom se seznámili a časovači (timer). Vzhledem k narůstajícímu počtu uživatelů Windows XP si také, zatím bez podrobnějšího výkladu, ukážeme, jak jednoduše zařídit, aby naše prvky v kklientské oblasti měly vzhled podle nastavení Windows XP. Pokud chceme, aby prvky klientské oblasti používaly nový vzhled odvozený od nastavení Windows XP, musíme to systému říci tzv. "manifestem", což může být externí soubor s přesně definovaným názvem odvozeným od názvu aplikace: JmenoProgramu.exe.manifest, název tedy vytvoříme přidáním "přípony" manifest za název programu (včetně exe). Druhou možností je tento soubor vložit do resources programu zdroj typu RT_MANIFEST nazvaný CREATEPROCESS_MANIFEST_RESOURCE_ID. Tento manifest je textový soubor v XML formátu obsahující příslušné informace. Vzor takového manifestu najdete mezi soubory doprovodného projektu ("WinApi.exe.manifest"). Kromě tohoto manifestu musíme dále v kódu registrovat a inicializovat třídy pro běžné prvky (common controls). To lze udělat funkcí InitCommonControls, ale lépe novější a doporučovanou funkcí InitCommonControlsEx.

BOOL InitCommonControlsEx(
    LPINITCOMMONCONTROLSEX lpInitCtrls
);

Tato funkce umožňuje registrovat jednotlivě různé "speciální" prvky, jako například progress-bar, tool-tip a pod, přičemž účinek je kumulativní. Podrobnosti jsou samozřejmě vysvětleny v MSDN. Základní použití tak jak je i v našem doprovodném programu (umístěné na začátek funkce InitApp) vypadá takto:

INITCOMMONCONTROLSEX icc;
icc.dwSize = sizeof(INITCOMMONCONTROLSEX);
icc.dwICC = ICC_WIN95_CLASSES;
if ( !InitCommonControlsEx(&icc) )
  return FALSE;

Pro použití této funkce je ještě potřeba mít ve zdrojovém kódu vložen příslušný hlavičkový soubor:

#include <commctrl.h>

do projektu přidat knihovnu comctl32.lib, pokud tedy ji tam již nepřidá "wizard" při vytváření projektu v určitém vývojovém prostředí. Nyní již by ve Windows XP měly i prvky klientské oblasti mít požadovaný vzhled.

Nyní k slíbenému stavovému řádku a výpisu času do něj. Stavový řádek můžeme vytvořit jako okno systémové třídy (kterou tedy nemusíme registrovat) STATUSCLASSNAME, kterou použijeme jako jméno třídy ve funkci CreateWindowEx. Existuje ještě funkce CreateStatusWindow, která je však již zastaralá a je doporučeno používat CreateWindowEx. Takto tedy vytvoříme v našem programu stavový řádek:

g_hwndStatusBar = CreateWindowEx(0,
  STATUSCLASSNAME,
  TEXT("Stavový řádek"),
  WS_CHILD | WS_VISIBLE,
  0, 0, 0, 0,
  g_hwndMain,
  (HMENU)NULL,
  g_hInstance,
  NULL);
if ( g_hwndStatusBar == NULL )
  return FALSE;

Hodnota STATUSCLASSNAME je definována jako "msctls_statusbar32", což je skutečné jméno třídy, které získáte třeba pomocí programu Spy++, když si necháte zobrazit údaje o okně stavového řádku. Možná se podivujete nad zadanými rozměry okna, kde jsou "samé nuly". Stavový řádek má totiž jednu šikovnou vlastnost: při změně velikosti jeho rodičovského okna nemusíme přepočítávat a nastavovat jeho rozměry, ale stačí když při zachycení zprávy WM_SIZE v proceduře hlavního okna tuto zprávu přepošleme oknu stavového řádku s tím, že parametry wParam a lParam předáme beze změny. Stavový řádek se pak automaticky drží spodního okraje okna tak jak od něj očekáváme. A vzhledem k tomu že po vytvoření stavového řádku, když dojde k zobrazení hlavního okna, zpráva WM_SIZE je poslána, tak ani počáteční velikost nemusíme počítat a nastavovat, proto ty nuly v rozměrech. Tedy jediné co ještě uděláme, je následující handler v proceduře hlavního okna:

case WM_SIZE:
   SendMessage(g_hwndStatusBar, WM_SIZE, wParam, lParam);
   break;

Stavový řádek máme tedy vytvořen, nyní něco o časovačích. Každé okno může mít jeden nebo více časovačů (timer), které tomuto oknu posílají v nastaveném intervalu zprávu WM_TIMER. Její parametr wParam obsahuje identifikátor časovače, abychom mohli jednotlivé časovače rozlišit, pokud jich jednomu oknu nastavíme více. Jak časovač vytvořit a zastavit. K vytvoření a aktivaci časovače slouží funkce SetTimer:

UINT_PTR SetTimer(
  HWND hWnd,              // handle okna
  UINT_PTR nIDEvent,      // identifikátor časovače
  UINT uElapse,           // interval časovače
  TIMERPROC lpTimerFunc   // procedura časovače
);

Procedury časovače se používají pouze ve zvláštních případech a nemusíme se jí zatím zabývat. Důležité je vědět že interval, ve kterém budou posílány zprávy WM_TIMER se zadává v milisekundách. Pokud chceme časovač zastavit, použijeme funkci KillTimer:

BOOL KillTimer(
  HWND hWnd,          // handle okna
  UINT_PTR uIDEvent   // identifikátor časovače
);

Nyní si tedy ukažme, jak ve stavovém řádku zobrazíme hodiny s aktualizací času každou sekundu. Nejprve musíme vytvořit časovač:

SetTimer(g_hwndMain, _TimerClock, 1000, NULL);

Toto volání funkce umístíme opět do funkce InitApp, hodnota _TimerClock je definovaná "někde na začátku" jako:

#define _TimerClock 1

Poté již naše okno dostávaá každou sekundu zprávu WM_TIMER, na kterou budeme reagovat tak, že získáme systémový čas, převedeme je to textového řetězce a ten nastavíme jako text okna stavového řádku:

// část kódu procedury okna
SYSTEMTIME sysTime
// ...
case WM_TIMER:
  GetLocalTime(&sysTime);
  _stprintf(chText, TEXT(" Dnes je: %d.%d.%d, %d:%.2d:%.2d hod."),
    sysTime.wDay, sysTime.wMonth, sysTime.wYear,
    sysTime.wHour, sysTime.wMinute, sysTime.wSecond);
  SetWindowText(g_hwndStatusBar, chText);
  break;

Pro získání systémového času je zde použita funkce GetLocalTime, která nám naplní strukturu SYSTEMTIME

typedef struct _SYSTEMTIME { 
    WORD wYear; 
    WORD wMonth; 
    WORD wDayOfWeek; 
    WORD wDay; 
    WORD wHour; 
    WORD wMinute; 
    WORD wSecond; 
    WORD wMilliseconds; 
} SYSTEMTIME, *PSYSTEMTIME;

Hodnotami určujícími aktuální datum a čas. Zde vidíte výsledek i s použitím manifestu pro nový vzhled prvků ve Windows XP:

win-api-11

Ještě technická poznámka k doprovodnému projektu. Počínaje tímto dílem je projekt ve verzi Visual C++ NET. I když většina čtenářů asi ještě nemá tuto poslední verzi Visual C++ (součást Visual Studia .NET) k dispozici, není problém si vytvořit v příslušné verzi Visual C++ projekt a vždy pouze kopírovat zdrojové soubory .cpp, .h a skript zdrojů .rc do stávající verze projektu a překompilovat.

Doprovodný projekt (Visual C++) je ke stažení zde: win_api_11.zip