Týmto článkom by som chcel nadviazať na seriál o Win32 API. Dnes sa dozvieme, ako vytvoriť tlačidlá a EditBox.
Riešenie, ktoré v tomto článku vysvetlím, je podľa mňa najjednoduchšie. Neskôr si ukážeme, ako sa dá podobný problém riešiť pomocou dialógových okien.
Najprv treba pochopiť ako vlastne Windows chápe tlačidlá, edit boxy a ostatné ovládacie prvky. Všetky tieto prvky vystupujú ako dcérske prvky okien a zasielajú im správy. Pre nás je zatiaľ dôležitá jediná správa: WM_COMMAND, ktorú zasiela tlačidlo svojmu rodičovskému oknu, keď dôjde k jeho stlačeniu.
Teraz ako sa vlastne také dcérske okno vytvára. Tieto okna budeme vytvárať pomocou funkcie CreateWindowsEx. Nemusíme však definovať našu vlastnú triedu okna, pretože nám Windows poskytuje už preddefinované triedy. Medzi najvýznamnejšie patria BUTTON, COMBOBOX, EDIT, LISTBOX... Podľa ich názvu určite viete, aký typ ovládacieho prvku sa s nimi vytvára. Definícia funkcie CreateWindowEx vyzerá nasledovne:
HWND CreateWindowEx(
DWORD dwExStyle,
LPCTSTR lpClassName,
LPCTSTR lpWindowName,
DWORD dwStyle,
int x,
int y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu,
HINSTANCE hInstance,
LPVOID lpParam
);
Jednotlivé parametre sú:
- DWORD dwExStyle – rozširujúce štýly okna. V našej aplikácii budeme používať NULL,
- LPCTSTR lpClassName – ako triedu použijeme jednu z už spomenutých preddefinovaných tried,
- LPCTSTR lpWindowName – reťazec v tomto parametri sa zobrazí ako popis v tlačidle alebo ako text v edit boxe bezprostredne po jeho vytvorení. Ak chcete vytvoriť edit box bez textu vložte prázdne úvodzovky („“),
- DWORD dwStyle – štýly okna. Keďže sú ovládacie prvky dcérske okná, musia mať štýl WS_CHILD, ak chceme, aby sa nám hneď zobrazili (inak by sme ich museli zobraziť neskôr pomocou ShowWindow()), pridáme aj štýl WS_VISIBLE. Každý z ovládacích prvkov má aj svoje dosť podstatné štýly, o ktorých si povieme neskôr,
- int x – x-ová poloha v rodičovskom okne,
- int y – y-ová poloha v rodičovskom okne,
- int nWidth – šírka nášho okna,
- int nHeigh – výška nášho okna,
- HWND hWndParent – dôležitý manipulátor rodičovského okna,
- HMENU hMenu - manipulátor menu, avšak v tomto prípade ID ovládacieho prvku,
- zvyšné dva parametre môžeme kľudne nechať NULL.
Pri vytváraní tlačidla je dôležité vedieť, že po stlačení zasiela rodičovskému oknu správu WM_COMMAND, ktorej hodnota wParam je ID nášho prvku. Toto ID priradíme nášmu oknu tak, že funkcii CreateWindowEx predáme miesto manipulátora menu práve toto ID. To potom v správe WM_COMMAND testujeme, aby sme zistili, o ktoré tlačidlo ide. Zíde sa to, keď máme v našej aplikácii viac tlačidiel a potrebujeme rozoznať, ktoré z nich bolo stlačené. Taktiež by som ešte spomenul niektoré zo štýlov, ktoré môžeme predať funkcii CreateWindowEx pri vytváraní ovládacieho prvku. Sú to:
- BS_PUSHBUTTON – vytvorí klasické tlačidlo,
- BS_DEFPUSHBUTTON – vytvorí klasické tlačidlo, ktoré je označené po vytvorení aplikácií (po stlačení enter sa vyberie pravé toto tlačidlo),
- BS_CHECKBOX – vytvorí zaškrtávacie políčko,
- BS_3STATE – trojpolohové zaškrtávacie políčko,
- BS_RADIOBUTTON – okrúhle výberové políčko.
Pri vytváraní edit boxu môžeme použiť:
- ES_MULTILINE – vytvorí viacriadkové textové pole,
- ES_NUMBER - do textového poľa je možné vkladať len čísla,
- ES_PASSWORD – zobrazí hviezdičku alebo čierny krúžok miesto znaku. Dá sa použiť iba pre jednoriadkové polia,
- ES_UPPERCASE – všetky písmená v poli sú veľké,
- ES_LOWERCASE - všetky písmená v poli sú malé,
- ES_CENTER - vycentruje text,
- ES_AUTOHSCROLL – automaticky posunie text, ak sa používateľ dostane na posledný znak zobrazený v poli,
- ES_AUTOVSCROLL – vertikálny posun.
Ešte by som spomenul, že text budeme do nášho poľa vkladať pomocou funkcie SetWindowText a vyberať ho odtiaľ pomocou GetWindowText. Obe tieto funkcie preberajú ako prvý parameter manipulátor ovládacieho prvku, ako druhý ukazovateľ na reťazec, ktorý majú zobraziť v poli, do ktorého má text z poľa skopírovať. Funkcia GetWindowText má ešte ako tretí parameter maximálnu dĺžku reťazca, ktorý má kopírovať.
Všetko si to ukážeme na ukážkovom programe a potom si vysvetlíme jednotlivé časti. Program je kompilovaný pomocou Dev C++. Základný kód je klasický kód, ktorý Dev C++ pridá, keď sa vytvorí Window Project (preto tie anglické commenty).
#include
#define IDB_MENO 1001
#define IDB_KONIEC 1002
/* Declare Windows procedure */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
/* Make the class name into a global variable */
char szClassName[ ] = "WindowsApp";
HWND hEdit,ButtonMeno,ButtonKoniec;
int WINAPI WinMain (HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR lpszArgument,
int nFunsterStil)
{
HWND hwnd; /* This is the handle for our window */
MSG messages; /* Here messages to the application are saved */
WNDCLASSEX wincl; /* Data structure for the windowclass */
/* The Window structure */
wincl.hInstance = hThisInstance;
wincl.lpszClassName = szClassName;
wincl.lpfnWndProc = WindowProcedure; /* This function is called by windows */
wincl.style = CS_DBLCLKS; /* Catch double-clicks */
wincl.cbSize = sizeof (WNDCLASSEX);
/* Use default icon and mouse-pointer */
wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
wincl.lpszMenuName = NULL; /* No menu */
wincl.cbClsExtra = 0; /* No extra bytes after the window class */
wincl.cbWndExtra = 0; /* structure or the window instance */
/* Use Windows's default color as the background of the window */
wincl.hbrBackground = (HBRUSH) COLOR_WINDOWFRAME;
/* Register the window class, and if it fails quit the program */
if (!RegisterClassEx (&wincl))
return 0;
/* The class is registered, let's create the program*/
hwnd = CreateWindowEx (
0, /* Extended possibilites for variation */
szClassName, /* Classname */
"Ukažkovy program", /* Title Text */
WS_OVERLAPPEDWINDOW, /* default window */
CW_USEDEFAULT, /* Windows decides the position */
CW_USEDEFAULT, /* where the window ends up on the screen */
250, /* The programs width */
120, /* and height in pixels */
HWND_DESKTOP, /* The window is a child-window to desktop */
NULL, /* No menu */
hThisInstance, /* Program Instance handler */
NULL /* No Window Creation data */
);
/* Make the window visible on the screen */
ShowWindow (hwnd, nFunsterStil);
/* Run the message loop. It will run until GetMessage() returns 0 */
while (GetMessage (&messages, NULL, 0, 0))
{
/* Translate virtual-key messages into character messages */
TranslateMessage(&messages);
/* Send message to WindowProcedure */
DispatchMessage(&messages);
}
/* The program return-value is 0 - The value that PostQuitMessage() gave */
return messages.wParam;
}
/* This function is called by the Windows function DispatchMessage() */
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) /* handle the messages */
{ case WM_COMMAND:
switch(LOWORD(wParam)) //kontroluje hodnotu wParam - obsahuje id prvku
{case IDB_MENO:
char retazec[256];
GetWindowText(hEdit,retazec,255);
MessageBox(hwnd,retazec,"Voláš sa:",MB_OK);
break;
case IDB_KONIEC:
if(MessageBox(hwnd,"Praješ si skutočne vypnúť tento program?","Vypnúť?", MB_ICONQUESTION | MB_YESNO)==IDYES)
PostQuitMessage (0);
break;}
break;
case WM_CREATE:
hEdit = CreateWindowEx(NULL, "EDIT", "", WS_CHILD | WS_VISIBLE | WS_BORDER, 120,10,100,18,hwnd,NULL, NULL,NULL);
ButtonMeno = CreateWindowEx( NULL,"BUTTON", "Ako sa volám?", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON , 20,35,95,20,hwnd,(HMENU)IDB_MENO, NULL,NULL); //ako hodnota manipulatora menu sa da dcerskym oknam predat ich id
ButtonKoniec = CreateWindowEx(NULL, "BUTTON", "KONIEC", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON , 125,35,95,20,hwnd,(HMENU)IDB_KONIEC, NULL,NULL);
break;
case WM_DESTROY:
PostQuitMessage (0); /* send a WM_QUIT to the message queue */
break;
case WM_PAINT:
HDC hDC;
PAINTSTRUCT ps; //Nepotrebujeme nejak vynimocne riesit, zaobideme sa bez formatovania
hDC = BeginPaint(hwnd,&ps);
TextOut(hDC,20,10,"Zadaj meno:",12);
EndPaint(hwnd,&ps);
break;
default: /* for messages that we don't deal with */
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0;
}
Teraz sa podrobnejšie pozrieme na vybrané časti kódu:
#define IDB_MENO 1001
#define IDB_KONIEC 1002
Hneď v úvode je definícia ID tlačidiel. ID by mali mať všetky prvky (edit boxu som nepridával, lebo v tomto prípade ho nepotrebujem), a väčšinou sú všetky konštanty definované v hlavičkovom súbore.
case WM_CREATE:
hEdit = CreateWindowEx(NULL, "EDIT", "", WS_CHILD | WS_VISIBLE | WS_BORDER, 120,10,100,18,hwnd,NULL, NULL,NULL);
ButtonMeno = CreateWindowEx( NULL,"BUTTON", "Ako sa volám?", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,20,35,95,20,hwnd, (HMENU)IDB_MENO, NULL, NULL); //ako hodnota manipulatora menu sa da dcerskym oknam predať ich id
ButtonKoniec = CreateWindowEx(NULL, "BUTTON", "KONIEC", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON , 125,35,95,20,hwnd,(HMENU)IDB_KONIEC, NULL,NULL);
break;
Keď rodičovské okno obdrží správu WM_CREATE, vytvoríme tri dcérske okná - ovládacie prvky (ako hodnotu manipulátora okna im predáme ich id).
case WM_PAINT:
HDC hDC;
PAINTSTRUCT ps; //Nepotrebujeme nejak vynimocne riesit, zaobideme sa bez formatovania
hDC = BeginPaint(hwnd,&ps);
TextOut(hDC,20,10,"Zadaj meno:",12);
EndPaint(hwnd,&ps);
break;
Keď už máme vytvorené ovládacie prvky, napíšeme si ešte popisky.
case WM_COMMAND:
switch(LOWORD(wParam)) //kontroluje hodnotu wParam - obsahuje id prvku
{case IDB_MENO:
char retazec[256];
GetWindowText(hEdit,retazec,255);
MessageBox(hwnd,retazec,"Voláš sa:",MB_OK);
break;
case IDB_KONIEC:
if(MessageBox(hwnd,"Praješ si skutočne vypnúť tento program?","Vypnúť?", MB_ICONQUESTION | MB_YESNO)==IDYES)
PostQuitMessage (0);
break;}
break;
Teraz to najdôležitejšie - správa WM_COMMAND. Najprv pomocou príkazu switch zistíme hodnotu wParam. wParam správy WM_COMMAND obsahuje id ovládacieho prvku. V prípade, že má hodnotu IDB_MENO (bolo stlačené tlačidlo s textom "Ako sa volám?") získame pomocou GetWindowText() text vpísaný v poli s manipulátorom hEdit. Tento text skopíruje do reťazca s názvom retazec (max. v dĺžke 255 znakov). Potom tento text zobrazíme pomocou MessageBox.
Ak má wParam hodnotu IDB_KONIEC, spýtame sa, či si používateľ praje skutočne ukončiť našu aplikáciu a ak áno, tak ju skončíme (funkcia PostQuitMessage(0)).
To by bolo v krátkosti všetko o ovládacích prvkoch. V prípade, že sa budete nudiť do vydania ďalšieho pokračovania článku, môžte si skúsiť spracovať aj texty z edit boxov dlhšie ako 255-znakové.