3. Csináljunk ablakot, és örüljünk (simple_window)!
2014.06.01. 22:27
Példa: simple_window
Az ablak kirakása nem túl nehéz, csak előbb sok mindent meg kell csinálni. Szóval nem nehéz, csak macerás. Ha jól látom, akkor a win32-re ez végig igaz lesz! Gépelünk, gépelünk..
Jöjjön előbb egy példa, amin keresztül boncolhatjuk azt a bizonyos orvosi lovat!
#include <windows.h> const char g_szClassName[] = "myWindowClass"; // Step 4: the Window Procedure LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_CLOSE: DestroyWindow(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, msg, wParam, lParam); } return 0; } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX wc; HWND hwnd; MSG Msg; //Step 1: Registering the Window Class wc.cbSize = sizeof(WNDCLASSEX); wc.style = 0; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wc.lpszMenuName = NULL; wc.lpszClassName = g_szClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if(!RegisterClassEx(&wc)) { MessageBox(NULL, "Window Registration Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK); return 0; } // Step 2: Creating the Window hwnd = CreateWindowEx( WS_EX_CLIENTEDGE, g_szClassName, "The title of my window", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 240, 120, NULL, NULL, hInstance, NULL); if(hwnd == NULL) { MessageBox(NULL, "Window Creation Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK); return 0; } ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); // Step 3: The Message Loop while(GetMessage(&Msg, NULL, 0, 0) > 0) { TranslateMessage(&Msg); DispatchMessage(&Msg); } return Msg.wParam; }
Akár kis is próbálhatjuk hogy mit alkottunk! Töltsük be a példaprogramok közül, és futtassuk le!
Megjelent egy hagyományos ablak, amiben ugyan nincs semmi, de átméretezhető, és a sarkában ott vannak a vezérlő gombok. És még fejléce is van neki! Nahát! És mindez alig nyolcvan sorba került! ;o)
Nézzük meg, hogy mi a retek ez a sok programsor!
Első lépés:
Elöljáróban annyit, hogy a Window osztálynak semmi köze nincs a C++ osztályokhoz! Ez tárolja az ablakhoz felhasznált ikont, a vezérlőket, és a háttérszínt. Ezeket a tulajdonságokat nem kell minden ablakhoz külön külön beállítani, elég csak az ablakokat létrehozni. Persze ablakonként is állíthatók ha kell.
const char g_szClassName[] = "myWindowClass";
Ez a változó tárolja az ablak osztályának a nevét, ezzel fogja a rendszer regisztrálni az ablakunkat.
A következőkben létrehozunk egy WNDCLASSEX típusú változót (aminek van pár beállítható értéke), majd a RegisterClassEx() függvénnyel regisztráljuk.
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX); wc.style = 0; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wc.lpszMenuName = NULL; wc.lpszClassName = g_szClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if(!RegisterClassEx(&wc)) { MessageBox(NULL, "Window Registration Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK); return 0; }
Akkor tételesen az egyes mezők jelentéséről:
cbSize: Jelzi a struktúra méretét
style:Egyszerűen legyen 0
lpfnWndProc:Mutató a window eljárásra
cbClsExtra: Lefoglalt memória mennyisége az extra adatok számára. Legyen 0!
cbWndExtra: Ugyanaz, csak ablakonként. Szintén 0.
hInstance: Az alkalmazás példány kezelője (ez a WinMain() első paramétere).
hIcon: Nagy (32x32) ikon, amit az Alt+Tab nyomásakor látunk
hCursor: Az ablak felett látható kurzor
hbrBackground: Háttér ecset, ami megadja az ablak színét
lpszMenuName: A menü erőforrás neve
lpszClassName: Azonosító név
hIconSm: Kicsi (16x16) ikon, amit a taskbar-on és az ablak bal felső sarkában látunk
Ezután meghívjuk a RegisterClassEx() eljárást, ami sikertelenség esetén dob egy üzenetet.
Második lépés:
Ha már regisztráltunk, akkor létrehozhatjuk az ablak példányunkat.
HWND hwnd;hwnd = CreateWindowEx( WS_EX_CLIENTEDGE, g_szClassName, "The title of my window", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 240, 120, NULL, NULL, hInstance, NULL);
Az első paraméter egy szebb dizájnt ad az ablak keretének (3D). Ha látni szeretnéd a különbséget, akkor állítsd próbából 0-ra az értékét!
Következőként az osztály nevét adjuk meg.
A WS_OVERLAPPEDWINDOW egy ablak stílus paraméter. Sok ilyen van, ki kell próbálni, hogy melyik mit csinál.
A CW_USEDEFAULT, CW_USEDEFAULT, 240, 120 az ablak elhelyezését határozza meg. X,Y,Szélesség,Magasság formátumban. Az értékek pixelben értendőek, a képernyő bal felső sarka a 0,0. Amikor CW_USEDEFAULT érték szerepel, akkor a rendszer automatikusan helyezi el az ablakot a képernyőn.
Most jön a ParentWindowHandle, a MenuHandle, ApplicationInstanceHandle, WindowCreationData négyes.
ParentWindowHandle: Ha egy gombot teszünk az ablakra, akkor a gombot gyermeknek, az ablakot pedig szülőnek nevezzük. Mivel ez az ablak egy legfelső objektum, nincs szülője, ezért ez NULL.
MenuHandle: A menü is NULL, mert nincs olyanunk.
ApplicationInstanceHandler: A WinMain() első paraméterét átadjuk
WindowCreationData: Nem használjuk, NULL
Ténykedésünk eredményét mindig ellenőrízzük le, ezt végzi el az alábbi pár sor:
if(hwnd == NULL) { MessageBox(NULL, "Window Creation Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK); return 0; }
Ha minden jó volt, akkor a WinMain() utolsó paraméterének segítségével megjeleníthetjük az ablakunkat
ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd);
Az nCmdShow paraméter opcionális, egyszerűen írhatnánk azt is, hogy SW_SHOWNORMAL. Mégis, ezzel be lehet állítani pár dolgot az ablakon. Ez nem túl tiszta még, majd kiderül.
Azt hiszed végeztünk? Hát nem!
Harmadik lépés:
while(GetMessage(&Msg, NULL, 0, 0) > 0) { TranslateMessage(&Msg); DispatchMessage(&Msg); } return Msg.wParam;
Ez a program motorja!
A GetMessage() üzenetet kap az alkalmazások üzenet sorából. Minden alkalommal amikor a felhasználó mozgatja az egeret, gépel a billentyűzeten, kattint a menün, stb, a rendszer generál egy üzenetet a program üzenetsorába. A GetMessage() hívásával kiolvassuk a következő rendelkezésre álló üzenetet, és töröljük a sorból.
A TranslateMessage() további dolgokat tesz a billentyűeseményekkel.
A DispatchMessage() tovább küldi az üzenetet, hátha nem nekünk szólt. A rendszer tudni fogja kinek kell továbbítania.
Negyedik lépés:
Eldöntjük, hogy a kapott üzenetekkel mit kezdjünk.
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_CLOSE: DestroyWindow(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, msg, wParam, lParam); } return 0; }
A HWND paraméter címzi az aktuális ablakot. Például a bezárás gombot megnyomva innen tudjuk, hogy melyik ablakot is kell bezárni. A WM_CLOSE üzenet a rendszer általános parancsa, ha Alt+F4-et nyomunk, vagy az ablak sarkában az 'x'-et. A rendszer automatikusan bezárná az ablakot, de célszerű ezt inkább manuálisan megtenni, mert így a kilépés előtt még megtehetünk dolgokat. Például mentés kilépés előtt, kilépési megerősítés kérése, stb.
Ha meghívjuk a DestroyWindow() eljárást, akkor a rendszer küld egy WM_DESTROY üzenetet, amit még utoljára elcsíphetünk. Ekkor a PostQuitMessage() segítségével küldhetünk egy végeredmény kódot az esetleges hívó program számára.
Egyenlőre ennyi, most örülünk :)
A bejegyzés trackback címe:
Kommentek:
A hozzászólások a vonatkozó jogszabályok értelmében felhasználói tartalomnak minősülnek, értük a szolgáltatás technikai üzemeltetője semmilyen felelősséget nem vállal, azokat nem ellenőrzi. Kifogás esetén forduljon a blog szerkesztőjéhez. Részletek a Felhasználási feltételekben és az adatvédelmi tájékoztatóban.