7. Menük, és ikonok (Menus and icons)
2014.06.05. 20:44
Példa: menu_one
Általában a menüket a csillivilli szerkesztővel hozzuk létre, ami elkészít egy .rc fájlt, amit végül belefordít a .exe-be. Ez ugye elkészítésileg egyszerű, de a belseje meg bonyolult. Most kézzel fogjuk ezt megtenni. Ebben a példában a simple_window projektet vesszük alapul, ezt fogjuk tovább bővíteni.
Először a resource.h fájl:
#define IDR_MYMENU 101 #define IDI_MYICON 201 #define ID_FILE_EXIT 9001 #define ID_STUFF_GO 9002
Nem sok minden van benne, mert most egy egyszerű menüt készítünk. A neveket természetesen tetszőlegesen választhatjuk. Jöhet az .rc fájl.
#include "resource.h" IDR_MYMENU MENU BEGIN POPUP "&File" BEGIN MENUITEM "E&xit", ID_FILE_EXIT END POPUP "&Stuff" BEGIN MENUITEM "&Go", ID_STUFF_GO MENUITEM "G&o somewhere else", 0, GRAYED END END IDI_MYICON ICON "menu_one.ico"
Az .rc fájlt hozzáadjuk a projekthez (vagy a makefile-hoz ha azt használunk). Bele include-oljuk a resource.h állományt a forrásfájlba, így most már abból elérhetők lesznek a parancsok azonosítói, valamint a menü erőforrás. A menü, és az ikon erőforrások ablakhoz rendelése legegyszerűbben úgy lehetséges, ha regisztráljuk az ablak osztályt így:
wc.lpszMenuName = MAKEINTRESOURCE(IDR_MYMENU);wc.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_MYICON));
wc.hIconSm = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_MYICON), IMAGE_ICON, 16, 16, 0);
Próbáljuk ki, mit alkottunk! Az ablakunknak most már van egy menüje, ami tartalmaz egy File, és egy Stuff menüpontot. Feltételezzük, hogy az .rc fájl megfelelően össze lett állítva, és becsatoltuk az .exe programunkba (lásd. fordítóval kapcsolatos jegyzet).
Az ablak bal felső sarkában, valamint a taskbar-on most már látnunk kell a kicsi ikont, amit megadtunk. Ha Alt+Tab-ot nyomunk, akkor ugyanez megjelenik nagyban is a program listán. Az egyszerűség kedvéért a LoadIcon()-t használtuk, ami jó is, ha az alapértelmezett 32x32 méretű ikont akarjuk tölteni. Ha kissebb kép áll rendelkezésre, akkor használhatjuk a LoadImage()-t. Ne feledjük, hogy minden egyes fájl amit felveszünk erőforrásnak, növeli a végleges programunk méretét!
Második példa: menu_two
A menü erőforrás alternatívája, amikor menet közben építjük fel. Ez több munkával jár, de rugalmasabb, és néha csak így lehet. Megtehetjük, hogy az ikonokat nem erőforrásként építjük be, hanem külön fájlként futásidőben töltjük be. Így akár a felhasználó is kiválaszthatja, hogy melyik legyen.
Kezdjük újra a simple-window példát, de most anélkül, hogy hozzáadnánk a .h, és .rc fájlokat! Most a WM_CREATE üzenetet fogjuk használni, hogy a menüt az ablakhoz adjuk.
#define ID_FILE_EXIT 9001
#define ID_STUFF_GO 9002
Ezt a két azonosítót hozzáadjuk a .c fájlhoz, majd jöhet a WM_CREATE:
case WM_CREATE: { HMENU hMenu, hSubMenu; HICON hIcon, hIconSm; hMenu = CreateMenu(); hSubMenu = CreatePopupMenu(); AppendMenu(hSubMenu, MF_STRING, ID_FILE_EXIT, "E&xit"); AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&File"); hSubMenu = CreatePopupMenu(); AppendMenu(hSubMenu, MF_STRING, ID_STUFF_GO, "&Go"); AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&Stuff"); SetMenu(hwnd, hMenu); hIcon = LoadImage(NULL, "menu_two.ico", IMAGE_ICON, 32, 32, LR_LOADFROMFILE); if(hIcon) SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon); else MessageBox(hwnd, "Could not load large icon!", "Error", MB_OK | MB_ICONERROR); hIconSm = LoadImage(NULL, "menu_two.ico", IMAGE_ICON, 16, 16, LR_LOADFROMFILE); if(hIconSm) SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIconSm); else MessageBox(hwnd, "Could not load small icon!", "Error", MB_OK | MB_ICONERROR); } break;
Ez létrehozza a menüt, és hozzácsatolja az ablakhoz, pont mintha erőforrásból csináltuk volna. A program befejezésével a menü automatikusan megszűnik, így nekünk nincs több gondunk vele. Viszont kipróbáltuk a GetMenu(), és a DestroyMenu()-t.
Az ikonokat gyorsan lerendezzük. Meghívjuk kétszer a LoadImage()-t, egyszer a 16x16-os, egyszer pedig a 32x32-es ikonhoz. Itt nem használhatjuk a LoadIcon()-t, mert az csak erőforráshoz alkalmas. NULL-t adunk meg a példány handlernek, mert nem töltünk erőforrást a modulból, hanem az azonosítót adjuk meg, amelyik ikont be akarjuk tölteni. Végül az LR_LOADFROMFILE flag fogja jelezni, hogy amit megadtunk az a fájl neve, és nem erőforrásból lesz.
Ha minden sikeres volt, akkor hozzárendeljük az ikon handlerjét az ablakhoz a WM_SETICON üzenettel. Ellenkező esetben dialógus ablak figyelmeztet arra, hogy hiba történt.
Megjegyzés: A LoadIcon() hibával tér vissza, ha az ikon nem a programmal megegyező könyvtárban van. Ha VC++ alatt dolgozunk, és a fejlesztő környezetből futtatjuk a programot, akkor a munkakönyvtár az lesz, ahol a projektfájl is található. Ha viszont Debug, vagy Release módban futtatod például explorerrel, vagy parancssorból, akkor át kell másolni az ikon fájlt, hogy a program megtalálja. Esetleg használhatunk teljes elérési utat is (ez nyilván nem túl praktikus).
Rendben hogy van menünk, de csináljon is valamit! Ehhez csak az kell, hogy kezelje a WM_COMMAND üzenetet. Tehát ellenőríznünk kell az üzenetet amit kaptunk. A WndProc() most így néz ki:
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) { switch(Message) { case WM_CREATE: { HMENU hMenu, hSubMenu; hMenu = CreateMenu(); hSubMenu = CreatePopupMenu(); AppendMenu(hSubMenu, MF_STRING, ID_FILE_EXIT, "E&xit"); AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&File"); hSubMenu = CreatePopupMenu(); AppendMenu(hSubMenu, MF_STRING, ID_STUFF_GO, "&Go"); AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&Stuff"); SetMenu(hwnd, hMenu); hIcon = LoadImage(NULL, "menu_two.ico", IMAGE_ICON, 32, 32, LR_LOADFROMFILE); if(hIcon) SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon); else MessageBox(hwnd, "Could not load large icon!", "Error", MB_OK | MB_ICONERROR); hIconSm = LoadImage(NULL, "menu_two.ico", IMAGE_ICON, 16, 16, LR_LOADFROMFILE); if(hIconSm) SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIconSm); else MessageBox(hwnd, "Could not load small icon!", "Error", MB_OK | MB_ICONERROR); } break; case WM_COMMAND: switch(LOWORD(wParam)) { case ID_FILE_EXIT: break; case ID_STUFF_GO: break; } break; case WM_CLOSE: DestroyWindow(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, Message, wParam, lParam); } return 0; }
Alakul. Kezd már jó kövér lenni :)
Láthatjuk, hogy felvettük a WM_COMMAND üzenetet is, de van belül mégegy switch(). Ez a wParam alapján dönti el, hogy mit kell csinálnia. Természetesen az Exit-re jó lenne kilépni, ezt így tudjuk megtenni:
PostMessage(hwnd, WM_CLOSE, 0, 0);
A WM_COMMAND kezelőnk most már ilyen lett:
case WM_COMMAND: switch(LOWORD(wParam)) { case ID_FILE_EXIT: PostMessage(hwnd, WM_CLOSE, 0, 0); break; case ID_STUFF_GO: break; } break;
Az ID_STUFF_GO már a fantáziánkra van bízva. Megfigyelhetjük, hogy a menu_one.exe a saját ikonjával látszik, míg a menu_two.exe nem. Előbbi erőforrást használt, utóbbi pedig menet közben töltötte be az ikont. Amikor az explorer megjeleníti a programokat, akkor megkeresi az erőforrások között az első ikont, és ezt fogja felhasználni program ikonként. Ha azt szeretnénk, hogy mindenkor a saját ikonunkat lássuk, csak annyi a dolgunk, hogy felvesszük erőforrásként az ikont, és ID-nek valami nagyon alacsony számot adunk neki (például 1). Még használnunk sem kell, a windows mégis ezt fogja a program ikonjának mutatni.
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.