10. Standard vezérlők (Standard Controls: Button, Edit, List Box)
2014.06.08. 14:55
Példa: ctl_one
Ugyan már használtunk gombokat, de azért itt is megemlítjük őket, hogy minden egy helyen legyen. Már említettük, de frissítsük fel, hogy minden vezérlő tulajdonképpen egy-egy ablak, tehát mindent amit az ablakokkal lehet, azt a vezérlőkkel is lehet. Mint azt már korábbról tudjuk, van egy üzenet hurok. Az ablakunk ezen keresztül üzenetekkel kommunikál. Elküldük a vezérlőnek hogy csináljon valamit, és ha esemény következik be, akkor a vezérlő visszaküld egy másik üzenetet. A hagyományos vezérlőknél ez az üzenet a WM_COMMAND lesz, amint azt a gomboknál és a menüknél már láttuk. Van még olyan hogy WM_NOTIFY, erről később lesz szó.
Sokféle üzenetet küldözgetünk, és minden vezérlőnek megvan a saját üzenet készlete. Általában egy üzenetet csak az fogad, akinek szánjuk, de létezik olyan eset is, amikor egy üzetetet több vezérlő is fogad.
Üzenetet küldhetünk a SendMessage() API segítségével, és használhatjuk a GetDlgItem()-et hogy megkapjuk a vezérlő handlerét. Vagy használhatjuk a SendDlgItemMessage()-t, ami mindkettőt elvégzi egyszerre. A két módszer eredménye azonos.
A Windows környezetben az egyik leggyakoribb vezérlő elem a szerkesztő (edit). Ezt alkalmazzuk arra, hogy a felhasználó adatokat adhasson meg, módosíthassa, másolhassa azokat. Lényegében a notepad.exe is csak egy rendes ablak, benne egy nagy szerkesztő vezérlővel.
Ebben a példában a szerkesztés vezérlőt ezzel a kóddal hozzuk elő:
SetDlgItemText(hwnd, IDC_TEXT, "This is a string");
És ennyi! Ez máris működik!
Hogy szöveget fogadjunk a vezérlőtől az se sokkal bonyolultabb, bár kicsit több munka van vele, mint a beállítással:
int len = GetWindowTextLength(GetDlgItem(hwnd, IDC_TEXT)); if(len > 0) { int i; char* buf; buf = (char*)GlobalAlloc(GPTR, len + 1); GetDlgItemText(hwnd, IDC_TEXT, buf, len + 1); //... do stuff with text ... GlobalFree((HANDLE)buf); }
Először is le kell foglalnunk a sztring számára a memóriát. Ehhez tudnunk kell, hogy mennyi memórára van szükségünk. Nincs olyan hogy GetDlgItemTextLength(), de van helyette GetWindowTextLength(). Csak arra van szükség, hogy átadjuk a GetDlgItem() handlerét neki.
Most hogy már megvan a hossza, le tudjuk foglalni a szükséges memóriát. Ellenőrízzük, hogy van-e valami szöveg, és csak ha a hossz nagyobb mint nulla, akkor folytatjuk. A GlobalAlloc() foglalja le a memóriát, és nullákkal tölti fel. Ez ugyanaz mint a DOS/UNIX C-ben a calloc(). Kapunk egy memória mutatót. A kért memória mennyiség egyel nagyobb, mint a szöveg hossza volt! Miért? Mert a GetWindowTextLength() a szöveg hosszába nem számolja bele a lezáró nullát.
Figyelem! Néhány API kéri a lezáró nullát, mások pedig nem! Ennek minden esetben utána kell nézni, ellenkező esetben memória túlcsordulás, és más durva dolgok történhetnek!!!
Most már hívhatjuk a GetDlgItemText()-et, ami a buf pufferbe beleírja a tárolt szöveget. Figyeljük meg, hogy itt is +1 a szöveg hossza! Fogunk még ezzel szívni..
Miután ezzel megvagyunk, fel kell szabadítani a lefoglalt memórát. Ezt oly módon kell megtenni, hogy ne szivárogjon, mert akkor lecsöpög, és és processzor áramköreit rövidre zárja, amitől tönkremegy a számítógép :D
After we're all done using the text (which we'll get to in a moment), we need to free up the memory that we allocated so that it doesn't leak out and drip down onto the CPU and short circuit your computer. To accomplish this, we simply call GlobalFree() and pass in our pointer.
Szóval csak hívjuk meg a GlobalFree()-t, és megmenekül a világ.
Ha már van sok szép szövegünk, előbb utóbb szembejön a probléma, hogy mi van amikor a felhasználó számokat akar bevinni? Szerencsére van erre API, ami lefoglalja a szükséges memóriát, és elvégzi a szöveg->szám átalakítást.
BOOL bSuccess; int nTimes = GetDlgItemInt(hwnd, IDC_NUMBER, &bSuccess, FALSE);
Hasonlóan működik mint a GetDlgItemText(), de nem pufferbe másolja a sztringet, hanem a szám értékét adja vissza. A harmadik paraméter opcionális. Ha hiba van az átalakításkor, a függvény nulla értékkel tér vissza. Ez nem szerencsés, mert a felhasználó is írhat nullát. Nem tudjuk megkülönböztetni. Ha nem baj hogy nullát ad vissza hiba esetén, akkor nem kell megadni. TODO: Ennek azért még utána kell nézni, nem biztos hogy jól értettem.
A szerkesztés vezérlőnek megadhatunk egy ES_NUMBER stílust. Ez kizárólag a 0-9 számjegyeket engedi begépelni. Hasznos, ha csak pozitív egész értéket kell megadni.
Másik kedvenc vezérlőnk a ListBox(). Az első amit tenni akarunk vele, hogy elemet adunk hozzá:
int index = SendDlgItemMessage(hwnd, IDC_LIST, LB_ADDSTRING, 0, (LPARAM)"Hi there!");
Ez nagyon egyszerű volt. Ha a listadoboz rendelkezik LBS_SORT stílussal, akkor abc sorrendben fogja megjeleníteni, egyébként a sor végére biggyeszti az új elemet.
SendDlgItemMessage(hwnd, IDC_LIST, LB_SETITEMDATA, (WPARAM)index, (LPARAM)nTimes);
Ha jól értem, akkor az indexhez hozzárendeli az értéket, tehát a "Hi there!" szöveghez az nTimes értékét. TODO!
Van, mikor nem érdekel minket, hogy mikor változik meg a listbox értéke, egyszerűen a kiválasztás ténye érdekel (ez a gyakoribb). Máskor viszont már az is számít, ha a felhasználó éppen kiválasztja. Például a kiválasztás eredményétől függ a többi választások lehetősége. Ennek figyelésére szolgál az LBN_SELCHANGE üzenet, ami a WM_COMMAND-on keresztül érkezik. Ellentétben a gombok, vagy a menü WM_COMMAND kezelőjével ahol csak a kattintás tényét kapjuk meg, itt több más paramétert is kapunk. Emiatt egy második ellenőrzésre is szükség van, ahol megvizsgáljuk a wParam értékét.
case WM_COMMAND: switch(LOWORD(wParam)) { case IDC_LIST: // It's our listbox, check the notification code switch(HIWORD(wParam)) { case LBN_SELCHANGE: // Selection changed, do stuff here. break; } break; // ... other controls } break;
Most hogy tudjuk, megváltozott a kiválasztás, olvassuk is ki hogy mit kért a user! Ebben a példában többszörös kiválasztásra adtunk lehetőséget, ami kicsit komplikálja a helyzetet. Ha egyszerű lista lenne, akkor elég csak az LB_GETCURSEL használata, és megkapjuk a kiválasztott tétel indexét.
Először is szükséges tudnunk, hogy hány kiválasztott elem van, mert eszerint foglaljuk a memóriát az indexek tárolására.
HWND hList = GetDlgItem(hwnd, IDC_LIST); int count = SendMessage(hList, LB_GETSELCOUNT, 0, 0);
int *buf = GlobalAlloc(GPTR, sizeof(int) * count); SendMessage(hList, LB_GETSELITEMS, (WPARAM)count, (LPARAM)buf); // ... Do stuff with indexes GlobalFree(buf);
Lefoglalunk annyi memóriát amennyit kell, és az LB_GETSELITEMS feltölti nekünk a kiválasztott indexekkel. Tehát buf[0] az első index, és buf[count-t] az utolsó.
Nyilván ez arra kell nekünk, hogy lekérdezzük a kiválasztáshoz tartozó adatokat. Ez sem bonyolult, csak egy üzenetet kell küldenünk:
int data = SendMessage(hList, LB_GETITEMDATA, (WPARAM)index, 0);
Nem csak int, hanem bármilyen egyszerű típusba is lehet kérni például HBITMAP az int helyett:
HBITMAP hData = (HBITMAP)SendMessage(hList, LB_GETITEMDATA, (WPARAM)index, 0);
Ugyan már volt szó róla, de emlékezzünk meg az ún statikus vezérlőkről is! Azért statikus, mert nem csinál semmit, nem változik. Tipikusan ilyen lehet egy szöveg megjelenítése. Ekkor az IDC_STATIC azonosítót kapja (-1), ami azt jelenti, hogy nincs azonosítója.
Példánkban megjelenítjük az adatot a kiválasztásban. Feltételezzük, hogy csak egy elem lehet kiválasztva. TODO!
SetDlgItemInt(hwnd, IDC_SHOWCOUNT, data, FALSE);
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.