145
C++ pod Windows 1. cast: Úvod Vítam vás pri novom seriáli o programovaní. Ako z názvu môžete usúdit, pôjde o programovanie pod operacným systémom Microsoft Windows využitím jazyka C++. Ak ste si teraz pomysleli, že programovanie s „Céckom“ pod Windows musí byt nehorázne tažké, rád vám poviem, že nemáte pravdu. Iste, ak ste doteraz programovali pod operacným systémom DOS, musí sa vám zdat tažké vytvorit a ovládat všetky tie okná, dialógy, ikonky a iné, pre Windows také typické veci. No a práve tento seriál vám ukáže, ako programovanie pod Windows vyzerá a ako sa vlastne pod Windows programuje. Ak ste citatelmi PC REVUE už dlhší cas a zaujímate sa o jazyk C++, urcite vám ne unikol seriál p. Klimovského „C++ pod lupou”. Ak ste ho sledovali a poctivo sa ucili, nebude vám robit problém zvládnutie informácií uvedených v tomto seriáli. Na tomto mieste by som ešte rád podotkol, že seriál C++ pod Windows nemá nic spolocné so seriálom C++ pod lupou a v žiadnom prípade nejde o jeho pokracovanie. Všetko, co sa o jazyku C++ dá povedat, bolo opísané v tomto seriáli. My sa v tomto seriáli naucíme, ako využit jazyk C++ na programovanie aplikácií pre Windows. Na zaciatku seriálu si vysvetlíme základy programovania pod Windows a neskôr sa zameriame na knižnicu MFC (Microsoft Fundation Class Library), ktorá tvorí štandard pre vývoj aplikácií pre tento operacný systém. Výklad som zvolil tak, aby ste po skoncení seriálu boli schopní programovat pod Windows aplikácie typu textové editory, kresliace programy, aplikácie pre siet , internet a pod. s tým, že budete pripravení na získavanie dalších vedomostí. Na úspešné odladenie všetkých príkladov budete potrebovat prekladac, ktorý podporuje knižnicu MFC. Osobne by som vám odporúcal prekladace Microsoft Visual C++ verzie 5.0 a vyššej. Samo zrejme, je možné použit aj prekladace od firmy Borland, a to pocínajúc verziou Borland C++ Builder 3.0. S urcitými ob medzeniami je možne použit aj prekladac Visual C++ verzie 1.0, ktorý sa dá za darmo získat s knihou „ Programujeme v jazyce Visual C++.” Pri výklade predpokladám, že viete využívat prostredie prekladacov, a nebudem sa zaoberat ich opisom. Použitie prekladacov je dost podrobne opísané v helpe. Tolko teda na úvod. Ak budete mat pripomienky alebo nebudete dacomu rozumiet, stací mi napísat na môj e-mail (uvedený na konci tejto casti seriálu) a spolocne sa budeme snažit váš problém vyriešit. No a teraz môžeme zacat. WINDOWS API. Windows API, co je skratka pre Windows Application Programming Interface, je základné programové rozhranie pre aplikácie Windows. Preto sa niekedy aplikácie pre Windows nazývajú aj aplikáciami vo Windows API. V minulosti sa aplikácie pre Windows programovali len pomocou tohto rozhrania. Lenže ako sa vyvíjali coraz zložitejšie aplikácie, zacal sa prejavovat velký nedostatok rozhrania Windows API, a to fakt, že celé rozhranie je napísané v jazyku C. Programátori preto nemohli používat prvky, ako sú dedicnost (mimochodom, jeden z najdôležitejších prvkov programovania pod Windows) alebo polymorfizmus. Pri vývoji novej aplikácie sa muselo zacínat úplne odznova. Preto sa zacali objavovat knižnice, ktoré pomocou tried a objektov „zapuzdrovali“ najdôležitejšie funkcie Windows API. Sú to knižnice Object Windows Library (OWL) od firmy Borland a Microsoft Fundation Class Library (MFC) od firmy Microsoft, ale o nich si povieme neskôr. DOS verzus Windows Nie, nepôjde o porovnávanie týchto dvoch operacných systémov. Na tomto mieste by som vám chcel len ukázat základný rozdiel medzi programami pod DOS a Windows. Ako príklad si zoberme klasický program pre DOS: #include <stdio.h> int main() { printf(“Hello, world!\ n); return 0; } Ak si ešte dobre pamätáte, funkciu main musí obsahovat každý program pre DOS a nikdy v jednom programe nemôže byt viac ako jedna táto funkcia. Je to prvá funkcia, ktorá je volaná, ked sa spustí program. Jej hlavným cielom je volat ostatné funkcie v programe. V našom príklade volá funkciu printf. Ak sa ukoncí funkcia main, program sa skoncí. Všetky programy napísané pre Windows porušujú toto základné pravidlo programov pre DOS.

C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Embed Size (px)

Citation preview

Page 1: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

C++ pod Windows 1. cast: Úvod Vítam vás pri novom seriáli o programovaní. Ako z názvu môžete usúdit, pôjde o programovanie pod operacným systémom Microsoft Windows využitím jazyka C++. Ak ste si teraz pomysleli, že programovanie s „Céckom“ pod Windows musí byt nehorázne tažké, rád vám poviem, že nemáte pravdu. Iste, ak ste doteraz programovali pod operacným systémom DOS, musí sa vám zdat tažké vytvorit a ovládat všetky tie okná, dialógy, ikonky a iné, pre Windows také typické veci. No a práve tento seriál vám ukáže, ako programovanie pod Windows vyzerá a ako sa vlastne pod Windows programuje. Ak ste citatelmi PC REVUE už dlhší cas a zaujímate sa o jazyk C++, urcite vám ne unikol seriál p. Klimovského „C++ pod lupou”. Ak ste ho sledovali a poctivo sa ucili, nebude vám robit problém zvládnutie informácií uvedených v tomto seriáli. Na tomto mieste by som ešte rád podotkol, že seriál C++ pod Windows nemá nic spolocné so seriálom C++ pod lupou a v žiadnom prípade nejde o jeho pokracovanie. Všetko, co sa o jazyku C++ dá povedat, bolo opísané v tomto seriáli. My sa v tomto seriáli naucíme, ako využit jazyk C++ na programovanie aplikácií pre Windows. Na zaciatku seriálu si vysvetlíme základy programovania pod Windows a neskôr sa zameriame na knižnicu MFC (Microsoft Fundation Class Library), ktorá tvorí štandard pre vývoj aplikácií pre tento operacný systém. Výklad som zvolil tak, aby ste po skoncení seriálu boli schopní programovat pod Windows aplikácie typu textové editory, kresliace programy, aplikácie pre siet , internet a pod. s tým, že budete pripravení na získavanie dalších vedomostí. Na úspešné odladenie všetkých príkladov budete potrebovat prekladac, ktorý podporuje knižnicu MFC. Osobne by som vám odporúcal prekladace Microsoft Visual C++ verzie 5.0 a vyššej. Samo zrejme, je možné použit aj prekladace od firmy Borland, a to pocínajúc verziou Borland C++ Builder 3.0. S urcitými ob medzeniami je možne použit aj prekladac Visual C++ verzie 1.0, ktorý sa dá za darmo získat s knihou „Programujeme v jazyce Visual C++.” Pri výklade predpokladám, že viete využívat prostredie prekladacov, a nebudem sa zaoberat ich opisom. Použitie prekladacov je dost podrobne opísané v helpe. Tolko teda na úvod. Ak budete mat pripomienky alebo nebudete dacomu rozumiet, stací mi napísat na môj e-mail (uvedený na konci tejto casti seriálu) a spolocne sa budeme snažit váš problém vyriešit. No a teraz môžeme zacat. WINDOWS API. Windows API, co je skratka pre Windows Application Programming Interface, je základné programové rozhranie pre aplikácie Windows. Preto sa niekedy aplikácie pre Windows nazývajú aj aplikáciami vo Windows API. V minulosti sa aplikácie pre Windows programovali len pomocou tohto rozhrania. Lenže ako sa vyvíjali coraz zložitejšie aplikácie, zacal sa prejavovat velký nedostatok rozhrania Windows API, a to fakt, že celé rozhranie je napísané v jazyku C. Programátori preto nemohli používat prvky, ako sú dedicnost (mimochodom, jeden z najdôležitejších prvkov programovania pod Windows) alebo polymorfizmus. Pri vývoji novej aplikácie sa muselo zacínat úplne odznova. Preto sa zacali objavovat knižnice, ktoré pomocou tried a objektov „zapuzdrovali“ najdôležitejšie funkcie Windows API. Sú to knižnice Object Windows Library (OWL) od firmy Borland a Microsoft Fundation Class Library (MFC) od firmy Microsoft, ale o nich si povieme neskôr. DOS verzus Windows Nie, nepôjde o porovnávanie týchto dvoch operacných systémov. Na tomto mieste by som vám chcel len ukázat základný rozdiel medzi programami pod DOS a Windows. Ako príklad si zoberme klasický program pre DOS: #include <stdio.h> int main() { printf(“Hello, world!\ n); return 0; } Ak si ešte dobre pamätáte, funkciu main musí obsahovat každý program pre DOS a nikdy v jednom programe nemôže byt viac ako jedna táto funkcia. Je to prvá funkcia, ktorá je volaná, ked sa spustí program. Jej hlavným cielom je volat ostatné funkcie v programe. V našom príklade volá funkciu printf. Ak sa ukoncí funkcia main, program sa skoncí. Všetky programy napísané pre Windows porušujú toto základné pravidlo programov pre DOS.

Page 2: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Základná funkcia programov pre Win dows je funkcia WinMain. Funkcia Win Main plní rovnakú funkciu ako funkcia main, samozrejme, s odlišnostami, ktoré si vyžaduje operacný systém Windows. Zo svojho tela volá iné funkcie, a ak sa skoncí funkcia WinMain, skoncí sa aj program. Vo funkcii WinMain napríklad zadávame meno aplikácie, titulok a velkost okna a iné dôležité veci. Rovnako WinMain ob sahuje aj hlavný „uzol“ správ (co to je, si vysvetlíme neskôr). Pre Windows je ešte jedna typická funkcia, a to WndProc. Táto funkcia sa nazýva aj funkciou okna, a to preto, lebo môže vykreslovat obsah okna a rovnako môže aj okno zrušit. UDALOSTI A SPRÁVY. Ked pracujete s operacným systémom Windows, neustále robíte nejaké operácie, ktoré sa týkajú okien. Napríklad presúvate okná, meníte ich velkost alebo kliknete na ne myšou. Tým sa spúšta operácia nazývaná udalost. Vždy, ked je spustená udalost, pošle operacný systém správu programu, aby ten mohol udalost spracovat. Správa je jednoduchý blok dát, ktorý vygeneruje operacný systém. Tento blok dát obsahuje in formácie o tom, aký druh správy bol vlastne vygenerovaný, a o tom, ktorého okna sa správa týka. Ako sme si povedali pri funkcii WinMain, tá obsahuje hlavný uzol správ. Úlohou tohto uzla je neustále kontrolovat, ci sa neudiala nejaká udalost. Ked bola udalost nájdená, program na nu od povie, ak nebola nájdená žiadna udalost, uzol pokracuje v hladaní dalej. Uzol správ ukoncí svoju cinnost, až ked sa skoncí program. Napríklad ked zmeníte velkost okna, uzol správ zaznamená mnoho udalostí, ktoré spôsobia vykreslenie okna na obrazovku. Ak už je velkost okna zmenená, uzol správ zistuje, ci nedošlo k dalšej udalosti. TIPY NA DOMA. Nebojte sa, nechcem vám zadávat povinnú domácu úlohu. Kto chce, môže si v helpe svojho prekladaca vyhladat informácie o funkciách WinMain a WndProc. NABUDÚCE. Tolko teda na úvod. Nabudúce si podrob nejšie opíšeme funkcie WinMain a Wnd Proc na príklade programu Hello, world!, situovaného do prostredia Windows. Bližšie si opíšeme udalosti a správy systému Windows. Dovtedy dovidenia. 2. cast: Úvod do programovania pod Windows Mesiac uplynul a ja vás opät vítam pri druhej casti seriálu C++ pod Windows. V predošlej casti sme si spomenuli základné pojmy z programovania pod Windows a uviedli sme si rozdiely medzi programami pod DOS a Windows. Teraz vaše vedomosti rozšírime a uvedieme a rozoberieme si aj nejaký program pod Windows. RADY, UZLY A RUTINY SPRÁV. Už viete, že ked operacný systém zistí používatelom spôsobenú udalost, vygeneruje na nu správu. Túto správu potom pošle do radu správ, ktorá prislúcha spustenej aplikácii. Rady správ využíva aplikácia, ked chce zistit, ci bola používatelom vygenerovaná správa a o aký druh správy ide. Zistí to tak, že prehladá všetky správy v rade správ. Uzol správ je cyklus, ktorý neustále prehladáva rad správ a zistuje, ci nedošlo k nejakej udalosti. Prvá inštrukcia tohto uzla je volanie API funkcie s názvom GetMessage. Táto funkcia vracia informácie o každej správe, ktorá caká v rade správ. Po zavolaní tejto funkcie obycajne uzol správ volá niekolko dalších funkcií, ktoré obsluhujú prvky, ako sú vstupy a výstupy z klávesnice. Nakoniec volá uzol správ dalšiu API funkciu, a to funkciu DispatchMessage. Táto funkcia volá vám už známu funkciu okna WndProc. WndProc oznámi, aký druh udalosti nastal, a odpovie na nu volaním dalších funkcií, ktoré zaistuje váš program. Tieto funkcie sa volajú rutiny správ. Rutiny správ urcujú, akým spôsobom bude správa spracovaná. Aplikácie Windows sú vybavené rôznymi druhmi správ, ktoré sú urcené na spracovanie jednotlivých druhov správ. Napríklad ak chcete, aby poklepanie myšou na okno spustilo zvukový signál, musíte na to vytvorit inú rutinu správy, ako ked chcete, aby toto poklepanie spôsobilo vypísanie nejakého textu. Na obr. 1 môžete vidiet dve rutiny správ. Jedna má názov WM_PAINT a druhá WM_DESTROY. Ich funkciu si vysvetlíme nabudúce. Uzol správ Volanie API funkcií

Udalost spôsobená používatelom

Správa

Správa

Správa GetMessage

DispatchMessage

Page 3: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Obr. 1 K vysvetleniu spolupráce radu a uzla správ

PRÍKLAD. Po dlhom teoretickom úvode je nacase, aby sme si uviedli príklad, na ktorom si objasníme preberané pojmy. Príkladom je jednoduchá aplikácia Hello, ktorej jedinou úlohou je vypísanie textu Hello, world! do okna s názvom Program Hello. // Hello.c #include <windows.h> long FAR PASCAL _export WndProc(HWND hwnd, UINT message, UINT wParam, LONG lParam) { HDC hdc; HPEN hpen, hpenOld; PAINTSTRUCT ps; RECT rect; switch (message) { case WM_PAINT: hdc = BeginPaint(hwnd, &ps); GetClientRect(hwnd, &rect); hpen = CreatePen(PS_SOLID, 6, RGB(0, 0, 255)); hpenOld = SelectObject(hdc, hpen); DrawText(hdc, "Hello, world!", -1, &rect, DT_SINGLELINE | DT_LEFT | DT_TOP); SelectObject(hdc, hpenOld); DeleteObject(hpen); EndPaint(hwnd, &ps); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); } int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow) { static char szMeno [] = "Hello"; HWND hwnd; MSG msg; WNDCLASS wndclass; if (!hPrevInstance) { wndclass.style = CS_HREDRAW | CS_VREDRAW; // typ okna wndclass.lpfnWndProc = WndProc; // meno funkcie, ktorá je spojená s triedou okna wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); // natiahnutie ikony aplikácie (ak ste...

// nedefinovali vlastnú, natiahne sa default

wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); // natiahnutie kurzora (ak ste...

WndProc

WM_DESTROY

WM_PAINT

Page 4: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

// nedefinovali vlastný, natiahne sa default

wndclass.hbrBackground = GetStockObject(WHITE_BRUSH); // pozadie okna wndclass.lpszMenuName = NULL; // informácie o menu, ak ste nejaké vytvorili wndclass.lpszClassName = szMeno; // meno, pod akým sa trieda okna zaregistruje RegisterClass(&wndclass); } hwnd = CreateWindow(szMeno, // meno triedy okna "Program Hello", // názov okna WS_OVERLAPPEDWINDOW, // štýl okna CW_USEDEFAULT, // pozícia okna na osi x CW_USEDEFAULT, // pozícia okna na osi y CW_USEDEFAULT, // zaciatocná velkost na osi x CW_USEDEFAULT, // zaciatocná velkost na osi y NULL, // handle na rodica ( v našom prípade žiadne nie je, preto NULL) NULL, // handle na menu okna ( v našom prípade žiadne nie je, preto NULL) hInstance, // handle na inštanciu programu NULL); ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } VYSVETLENIE PROGRAMU HELLO. Aby ste dokonale pochopili, ako program Hello pracuje, musíme si spomenút ešte nieco o handle. Handle je ukazovatel na ukazovatel a ukazuje na adresu uloženú v tabulke. Handle sa využíva na prístup k objektom systému Windows (pozor, nie k takým objektom, aké poznáme z C++). Vela API funkcií vracia handle a dalšie používajú handle ako parameter. Ked sa pozrieme na funkciu WinMain, tá obsahuje hned dva handle, a to hInstance, co je handle spustenej aplikácie, a hPrevInstance, co je handle predchádzajúcej aplikácie. Dalším parametrom funkcie WinMain je lpszCmdParam, co je ukazovatel na retazec príkazového riadka, ktorý bol použitý pri spúštaní aplikácie (nieco podobné ako parameter char* argv[ ] funkcie main v programoch pod DOS). Posledným parametrom je konštanta nCmdShow (môže ich byt aj viac, v takom prípade tieto konštanty oddelujeme pomocou bitového operátora OR {|}), ktorá môže byt použitá na urcenie velkosti okna, jeho súradníc a podobne. Po spustení programu Hello funkcia WinMain zistí, ci nie je spustená iná kópia programu. Ako iste viete, vo Windows môžu bežat naraz aj dve a viac rovnakých aplikácií. WinMain to zistí práve podla obsahu handle s názvom hPrevInstance. Ak sa nezistila prítomnost dalšej aplikácie Hello, WinMain zaregistruje novú triedu okna (opät pozor, nejde o triedu, ako ju poznáme z C++, v terminológii Windows je to urcitý typ okna, ktorý je registrovaný na použitie našou aplikáciou). Registrácia novej triedy okna sa vykonáva pomocou funkcie RegisterClass, ktorá je volaná zvnútra bloku if(!hPrevInstance) a štruktúry WNDCLASS. Práve príkaz if(!hPrevInstance) zarucuje, že registrácia triedy okna sa nevykoná, ak je už rovnaká aplikácia spustená (a tá už triedu okna zaregistrovala). V tele cyklu „naplname“ štruktúru WNDCLASS dátami. Robíme tak pomocou nami deklarovanej štruktúry wndclass, ktorá je založená na štruktúre WNDCLASS (príkaz: WNDCLASS wndclass). Na konci tohto cyklu je už spomínaná funkcia RegisterClass, ktorá na základe získaných informácií štruktúry wndclass zaregistruje novú triedu okna a dá jej meno (v našom prípade szMeno). Ak už máme okno zaregistrované, môžeme ho zobrazit. Najprv však musíme okno vytvorit. Na to slúži funkcia CreateWindow. Opis jej parametrov tu nebudem uvádzat (môže ich mat velmi vela), nájdete ich v príklade Hello a rovnako aj v helpe. Tak teda po tom, ako sme vytvorili okno (CreateWindow), stací ho už len zobrazit. Na to slúži funkcia ShowWindow, ktorá ako parametre má okno, ktoré bolo vytvorené pomocou CreateWindow (v našom prípade hwnd), a konštantu nCmdShow (jej opis sme už uviedli). Posledná funkcia, ktorú použijeme, je UpdateWindow. Túto funkciu použijeme vždy, ked chcem nejakým spôsobom zmenit obsah okna (pripísat don text, zmenit farbu pozadia a pod.). Funkcie CreateWindow, ShowWindow a UpdateWindow sú API funkcie, takže nikde ich nemusíme deklarovat ani nic podobné.

Page 5: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Po tom, ako WinMain zaregistruje novú triedu pre naše okno, spustí hlavný uzol správ pomocou kódu:

while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg);

DispatchMessage(&msg); } V našom programe uzol správ spracúva operácie opakovaným volaním funkcií GetMessage, TranslateMessage, DispatchMessage. Uzol a zároven aj program sa koncí po prevzatí správy WM_QUIT, ktorú zašle funkcia PostQuitMessage. Všetky tieto funkcie obsahujú ako parameter štruktúru msg. Je to opät ako v prípade štruktúry WNDCLASS nami deklarovaná štruktúra (príkaz: MSG msg). Funkcia GetMessage používa túto štruktúru na odovzdávanie informácií o správach. Štruktúra MSG je definovaná v súbore windows.h, a preto ju tu nebudem uvádzat. Uvediem len opis jej clenských premenných. V premennej hwnd sú uložené informácie o okne, ktorému bola správa poslaná. V premennej message je uložené císlo správy (aká je to správa). V premenných wParam a lParam sú uložené informácie o type udalosti, o ktorej jej správa, a o zdroji, ktorý udalost vyvolal. Obidve tieto premenné sú závislé od premennej message. Klasický prípad je, ked udalost bola spôsobená stlacením tlacidla myši, potom štruktúra MSG obsahuje v premennej wParam informáciu o tom, ktoré tlacidlo myši bolo stlacené, a v premennej lParam môže byt informácia o tom, ktorý riadiaci kláves bol stlacený zároven s tlacidlom myši. V premennej time je uložený cas, ked bola správa zaznamenaná, a v premennej pt je uložená pozícia (súradnice) kurzora, ked bola správa zaznamenaná. TIPY NA DOMA. Skúste si doma pozriet v súbore windows.h definíciu štruktúr MSG a WNDCLASS. Rovnako si v helpe pozrite presný opis funkcií GetMessage, TranslateMessage, DispatchMessage, CreateWindow, ShowWindow, UpdateWindow a RegisterClass. Zaexperimentujte s funkciou CreateWindow (zmente pociatocné nastavenia okna, zväcšite alebo zmenšite okno a pod., skúste aj nieco so štýlom okna). NABUDÚCE. Nabudúce si dokoncíme rozbor príkladu Hello a uvedieme si, ako sa vlastne dá do okna kreslit, resp. písat. Opíšeme si kontext zariadení a GDI objekty. Ak nám zostane priestor, zacneme si opisovat aj knižnicu MFC. Teším sa na stretnutie opät o mesiac. 3. cast: Objekty Windows V predošlej casti seriálu som vám slúbil, že v tejto casti si dokoncíme rozbor príkladu Hello a uvedieme si aj nieco o kontexte zariadení a GDI objektoch. Svoj slub hodlám dodržat. Možno sa vám zdá, že sa privelmi zaoberám opisom „klasického“ programovania vo Windows využitím jazyka C a na jazyk C++ akosi zabúdam. Nebojte sa, dostaneme sa aj k C++, ale základná znalost Windows API je pre programátora, ktorý vyvíja aplikácie pre Windows, nevyhnutná. Možno si myslíte, že ked sa naucíte používat niektorú knižnicu (napr. MFC), funkcie a rovnako aj znalosti Windows API vám budú zbytocné. Nie je to pravda. Velakrát sa ocitnete v situácii, ked budete musiet zavolat niektorú API funkciu, lebo vami používaná knižnica neobsahuje vlastné prostriedky na vykonanie vašich požiadaviek. Pri tejto príležitosti mi nedá nespomenút knihu Programování ve Windows z vydavatelstva Computer Press, ktorej autorom je Charles Petzold. Táto kniha sa podrobne zaoberá programovaním vo Windows využitím len Windows API. Mal som možnost si túto knihu prezriet a môžem vám povedat, že sa dotýka takmer všetkých (ak nie úplne všetkých) oblastí programovania vo Windows. No ale na úvod by hádam už aj stacilo, urcite ste netrpezliví a chcete sa rýchlo dozvediet viac o programovaní vo Windows ?. Tak teda podme na to. PROGRAM HELLO. V predošlej casti sme opísali funkciu WinMain programu Hello. Už viete, co sa deje pri spustení programu, ako sa nám ukáže hlavné okno programu a ako program vlastne funguje. Zostáva nám už iba povedat si nieco o funkcii WndProc. O tejto funkcii už viete, že sa nazýva aj procedúrou okna a reaguje na udalosti spôsobené programom. Ale ako to vlastne robí? Jednoducho. Jeden jej parameter – UINT message – obsahuje informácie o správe prijatej od funkcie DispatchMessage (tá správu získala od funkcie GetMessage). Tento parameter sa využije neskôr v príkaze switch, aby „nasmeroval“ túto správu na správnu rutinu správy. V našom príklade sú to rutiny WM_PAINT a WM_DESTROY. WM_PAINT je zavolaná vždy, ked okno potrebuje prekreslit. U nás spracuje WM_PAINT správu tak, že „vykreslí“ pomocou funkcie DrawText do okna text Hello, world!. Bližšie si o tejto funkcii povieme, ked si budeme opisovat kontext zariadení. Rutina WM_DESTROY je zavolaná vždy, ked sa má okno zrušit, resp. ked má byt aplikácia ukoncená. WM_DESTROY zavolá

Page 6: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

API funkciu PostQuitMessage, ktorá vyšle správu WM_QUIT, ktorá oznamuje Windows, že aplikácia môže byt ukoncená. Windows už vykoná všetko potrebné na to, aby sa aplikácia riadne skoncila. KONTEXT ZARIADENÍ. Ak chceme do okna kreslit obrázok, písat text, ak chceme tento text ci obrázok neskôr vytlacit, urcite sa nevyhneme použitiu tzv. kontextu zariadení. Kontext zariadení, oznacuje sa ako DC, je objekt Windows, ktorý prijíma kresliace príkazy z programu a odovzdáva ich výstupným zariadeniam (v skutocnosti to ide ešte cez nízkoúrovnové API, ktoré obsluhuje výstupné zariadenia, a až potom k samotným zariadeniam – pozri obr. 1). V programoch nie je dôležité, aké zariadenie je použité ako výstupné. Program jednoducho získa handle na kontext zariadení a kreslí don. O viac sa program nestará. Jeho prácu preberá práve kontext zariadení, ktorý prevedie kresliace príkazy na nízkoúrovnové príkazy, ktorým rozumie nízkoúrovnové API a to tieto príkazy odovzdá výstupným zariadeniam. Je viac spôsobov, ako vytvorit kontext zariadení. Dostaneme sa k nim v dalších castiach seriálu. GDI OBJEKTY. GDI objekty sa rovnako ako kontext zariadení používajú na kreslenie. Ale zatial co kontext zariadení je objekt, na ktorý sa kreslí, GDI objekty sú objekty, pomocou ktorých sa kreslí (teda napríklad pero, štetec, font atd.). To znamená, že ak chcete v systéme Windows kreslit, musíte používat oba tieto „kresliace“ objekty. Aby sme však boli presní, nie všetky funkcie, ktoré kreslia do kontextu zariadení, musia využívat GDI objekty. Existuje zoznam GDI funkcií (je ich velmi vela), ktoré GDI využívajú. Naopak, existujú funkcie, ktoré GDI „obchádzajú“. Použitie GDI, ako aj klasických „kontextových“ funkcií si opät ukážeme na príklade programu Hello. Obr. 1 Vysvetlenie väzby GDI objektov a kontextu zariadení s aplikáciou POUŽITIE „KRESLIACICH“ OBJEKTOV. V našom príklade Hello sa celá „kresliaca“ cinnost odohráva v procedúre správy WM_PAINT. Aby aplikácia dokázala nakreslit nieco na obrazovku, nemusí toho až tak vela urobit. Ako vidíme v príklade, najprv sa volá funkcia BeginPaint. Táto funkcia, jednoducho povedané, pripraví okno na kreslenie a vráti nám aj handle na kontext zariadení. Ako parametre požaduje handle okna, kde sa bude kreslit (hwnd) a adresu štruktúry PAINTSTRUCT, do ktorej bude odovzdávat informácie o kreslení (&ps). Handle na kontext zariadení sa uloží do premennej hdc. Na co je tento handle potrebný, to sme si už opisovali. Dalej musí aplikácia zistit súradnice okna (tzv. klientskeho okna, co je okno vnútri rámcekov ohranicujúcich aplikáciu a pod menu), do ktorého sa bude kreslit. Potom musí aplikácia získat handle na GDI objekt použitý pri kreslení (v našom prípade je GDI objektom objekt pera). Tento handle sa uloží do premennej hpen a je to vlastne návratová hodnota funkcie CreatePen. Funkcia CreatePen má tri parametre, ktoré urcujú, akým perom sa bude kreslit. Prvé dva parametre sú, myslím, jasné, urcujú typ pera (v našom prípade plný) a jeho šírku (6 pixelov). Tretí parameter je zaujímavý tým, že parameter RGB(0, 0, 255) je makro. Používa sa na urcenie farby GDI objektu a jeho tromi parametrami sú intenzita cervenej, zelenej a modrej farby. To znamená, že príkaz RGB(0, 0, 255) vytvorí modré pero. Další príkaz hpenOld=SelectObject(hdc, hpen) priradí vytvorený objekt pera do kontextu zariadení (objekt hpen sa priradí k objektu hdc). Funkcia SelectObject vracia handle na ten GDI objekt, ktorý bol v kontexte zariadení pred tým, ako sme tam uložili objekt hpen. Ako neskôr uvidíte, handle na predchádzajúci GDI objekt je dôležitý pri uvolnovaní pamäte. Teraz náš program vykoná kreslenie textu na obrazovku pomocou funkcie DrawText. Táto funkcia nevyžaduje nijaký GDI objekt (takže to nie je GDI funkcia), ale pät parametrov. Nebudem ich tu opisovat, ich opis nájdete v helpe, azda len spomeniem, že sú to parametre, ktoré nám udávajú, kde sa text vykreslí a aký kontext zariadení sa na to použije. Teraz sa urcite pýtate, naco sme vytvárali GDI objekty a teraz ich nepoužijeme? Je pravda, že súcasná verzia programu Hello tieto GDI objekty vytvára, ruší, ale na kreslenie ich prakticky nijako nevyužívame. Preto dopíšte nasledujúci riadok do procedúry WM_PAINT pod funkciu DrawText:

Ellipse(hdc, rect.left+10, rect.top+10, rect.right-10, rect.bottom-10); Tento príkaz nakreslí do okna elipsu tak, aby sa neprekrývala s textom. Funkcia Ellipse (to už je GDI funkcia) použije GDI objekt hpen na kreslenie do okna. Skúste sa teraz zamysliet, preco je kružnica modrá a text cierny. Riešenie je tu prakticky uvedené. Ked sme už skoncili s využívaním GDI objektov, je potrebné uviest všetko do pôvodného stavu, aby sa zbytocne nezaplnovala pamät. Na to slúži príkaz SelectObject(hdc, hpenOld), ktorý priradí do kontextu zariadení pôvodný GDI objekt, ktorý tam bol pred objektom hpen. Až teraz zmažeme nami vytvorený objekt pera hpen príkazom DeleteObject(hpen), cím vlastne uvolníme pamät, ktorú objekt zaberal. No a nakoniec zavoláme funkciu EndPaint, ktorá oznámi Windows, že všetko kreslenie je úspešne ukoncené a práca s kontextom zariadení sa rovnako skoncila. Ak by sme túto funkciu nezavolali, Windows by si myslel,

Aplikácia GDI, kontext zariadení

Nízkoúrovnové API

Výstupné zariadenie

Page 7: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

že kreslenie je neúspešné, resp. ešte sa neskoncilo, a stále by posielal správu WM_PAINT, aby prekreslil svoje okno. DÁTOVÉ TYPY WINDOWS. Aby sme mohli uzavriet rozprávanie o základoch programovania vo Windows, treba ešte uviest niektoré dátové typy, ktoré systém Windows zavádza do jazykov C/C++. Nových dátových typov je nesmierne vela, preto sa obmedzím na opis tých najzákladnejších a najcastejšie používaných. Ich charakteristiku nájdete v tabulke. Väcšina ukazovatelov sa zacína písmenami P alebo LP. Podobne, ak ide o handle na nejaký objekt, obycajne sa dátový typ zacína písmenom H.

Dátový typ Strucná charakteristika BYTE 8 bitov bez znamienka

COLORREF RGB farebná hodnota – 32 bitov DWORD 32 bitov bez znamienka HBRUSH Handle na objekt typu Brush

HDC Handle na kontext zariadení HDESK Handle na desktop (plochu) HKEY Handle na registracný klúc HPEN Handle na objekt Pen

LPARAM 32 bitov so znamienkom (prenáša správu) LPBYTE Ukazovatel na BYTE LPCSTR Ukazovatel na konštantný retazec

LPINT Ukazovatel na INT LPLONG Ukazovatel na LONG

UINT Unsigned int WPARAM 32 bitov so znamienkom (prenáša správu)

TIPY NA DOMA. Mohli by ste si pozriet zoznam GDI funkcií, niektoré rutiny správ a pokúsit sa aj dopísat niektoré nové rutiny správ do nášho programu Hello. Napríklad sa pokúste napísat takú rutinu, aby stlacenie lavého tlacidla myši vyvolalo zvukový signál. Na vyslanie zvukového signálu môžete použit funkciu MessageBeep. Jej opis je v helpe. Rovnako si pozrite aj opisy funkcií DrawText, SelectObject, CreatePen, DeleteObject. Nezabudnite si aspon zbežne pozriet nové dátové typy Windows. NABUDÚCE. Ukoncili sme opis klasického programovania pod Windows. Bol to letmý pohlad na to, ako sa kedysi vo Windows programovalo. Tým však nechcem povedat, že informácie tu uvedené vám budú nanic (pozri úvod). V dalšej casti už zacneme s opisom knižnice MFC a vytvoríme nejaké „skutocné“ aplikácie pre Windows. 4. cast: Knižnica Microsoft Fundation Class Pred mesiacom sme si dokoncili opis „klasického“ programovania vo Windows, ktorému sme venovali prvé tri casti seriálu. V nasledujúcich castiach sa budeme venovat knižnici Microsoft Fundation Class (MFC). Niektorí ste mi písali, že máte problémy s prekladacom. Chápem, nie všetci môžete obetovat zopár desiatok tisíc korún na nákup najnovšej verzie Visual C++ ci C++ Buildera. Ale inak to skutocne nejde.

Ked som sa dávnejšie zamýšlal nad tým, o com by som vlastne chcel písat, došiel som práve k C++. Jednak preto, že C++ je skutocným jazykom profesionálov, je v nom písaná drvivá väcšina aplikácií a myslím si, že ešte dlhý cas bude C++ hrat v tejto oblasti prvé husle. Lenže na programovanie „profesionálnych“ aplikácií potrebujete aj „profesionálne“ vývojové prostredie, ktoré sa nedá zohnat za pár korún a urcite sa nedá zadarmo stiahnut z internetu. Mohol som písat o programovaní vo Windows „starým“ spôsobom (API programovaním), ako to bolo v prvých troch castiach seriálu. Mohli by ste si síce preložit všetky ukážkové aplikácie uvedené v seriáli, dokonca by ste sa takto naucili aj slušne programovat a bezpochyby by ste vytvorili aj nejaký ten „programík“. Keby ste však zacali písat „skutocné“ aplikácie, rýchlo by ste pochopili, že programovanie pomocou API vám neposkytuje velký komfort, museli by ste ovládat obrovské množstvo funkcií, nehovoriac o ešte väcšom množstve rôznych parametrov a podobne. Zase by ste teda skoncili pri štúdiu nejakej knižnice, ako je MFC. Dospel som k takémuto záveru: ked sa už ucit programovat vo Windows v C++,

Page 8: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

tak takým spôsobom, aby ste svoje vedomosti mohli zužitkovat pri tvorbe aplikácií, ktoré by ste mohli aj nejako využívat, a nie aby slúžili len vám na vlastné potešenie, že ste nieco naprogramovali. Aby som to uzavrel, na ucenie sa programovat v C++ s knižnicou MFC je podla môjho názoru najvhodnejší produkt Microsoft Visual C++ (je jedno, v akej verzii). Preto sa náš seriál bude venovat práve tomuto prostrediu. Samozrejme, príklady by mali byt funkcné aj v C++ Builderi, ale z priestorových prícin nemôžem dopodrobna opisovat obidve prostredia. Ak budete mat otázky ohladom C++ Buildera, rád vám odpoviem (ak mi to vedomosti dovolia) prostredníctvom e-mailu.

Rád by som sa vrátil k predošlým castiam seriálu. Velmi ma zamrzelo, ked som si cítal prvú cast. Ako ste si urcite všetci všimli, v príklade programu „Hello world“ je chyba, a síce chýbajú ukoncovacie úvodzovky vo volaní funkcie printf. Urobit chybu v programe „Hello world“, navyše v jazyku C je priam hriech, za co sa vám citatelom i celému programátorskému svetu ospravedlnujem. Dúfam, že príklad Hello z druhej casti sa vám podarilo úspešne preložit. Ak ste ho prekladali pod novšími Visual C++ prekladacmi, kompilátor vám vypísal jedno alebo viac varovných hlásení. Bolo to preto, lebo príklad Hello je 16-bitová aplikácia a používa aj nejaké staršie dátové typy, preto tie varovania. Ale program sa dal úspešne spustit. Chcel som zacat úplne od zaciatku, co predstavuje zacat 16-bitovým programovaním. Všetky dalšie príklady budú už 32-bitové a v neskorších castiach sa dostaneme k rozdielom 16- a 32-bitových aplikácií. K druhej casti ešte jedna poznámka. Pri konci, ked opisujem registráciu triedy okna, som sa zle vyjadril ohladom štruktúry WNDCLASS. Píšem tam, že príkazom WNDCLASS wndclass deklarujeme štruktúru. To, samozrejme, nie je celkom pravda, pretože tento príkaz predstavuje len deklaráciu premennej wndclass typu štruktúra. Rovnako to bolo aj v prípade štruktúry MSG. Takéto chybicky by sa nemali vyskytovat a slubujem vám, že urobím všetko preto, aby sa už neobjavili. Teraz sa však vrátme ku knižnici MFC. MICROSOFT FUNDATION CLASS LIBRARY. História knižnice MFC je dost dlhá a vskutku aj zaujímavá. Ale o tom na inom mieste. Preco vznikla, to sme si už povedali v prvej casti seriálu. A akú funkciu táto knižnica plní? Na zjednodušenie si predstavte akýsi obal (niekedy sa knižnici MFC hovorí aj objektový wrapper od ang. slova wrap – balit), ktorý obaluje API funkcie systému Windows. Napríklad ak pracujete s kontextom zariadení, netreba vám už prácne zistovat handle kontextu zariadení, jednoducho vytvoríte objekt (teraz už skutocný objekt jazyka C++, nie objekt Windows) kontextu zariadení a pomocou tohto objektu zavoláte príslušnú clenskú funkciu triedy (opät ide o skutocnú triedu jazyka C++) kontextu zariadení a je po probléme. Ked programujete s knižnicou MFC, nebudete musiet v takej miere (ak vôbec budete musiet) využívat handle. Väcšinou zadefinujete len ukazovatel na nejaký objekt a ten sa už postará o všetko potrebné. Knižnica MFC má vlastné triedy na prácu s oknami, dialógmi, kontextom zariadení, rôznymi GDI objektmi a inými prvkami systému Windows. Ale samotná knižnica a jej aplikacný systém majú ovela viac výhod ako len zjednodušovat programovanie vo Windows. Napríklad vdaka dynamicky linkovaným knižniciam (DLL) sú aplikácie aplikacného systému rýchle a zaberajú málo priestoru na pevnom disku, ako aj v pamäti (dynamicky linkovaným knižniciam bude venovaná niektorá dalšia cast seriálu). Dalšou výhodou môže byt napríklad ucelená štruktúra kódu. Každý programátor má vlastnú formu písania kódu. Používa iné mená pre clenské premenné, pre statické premenné, inak zapisuje príkazy typu if...then...else a podobne. Ak potom príde nový clen do programátorského tímu, ten nemusí rozumiet kódu, ktorý napísal niektorý z jeho kolegov. A práve aplikacný systém dodáva akúsi formu vášmu kódu a tým sú tieto problémy z minulosti zažehnané. Mohol by som vám ešte dlho písat o rôznych výhodách používania MFC, ale nemalo by to velký význam a som presvedcený o tom, že ak budete do tajov MFC prenikat coraz hlbšie, tieto výhody sami spoznáte. APLIKACNÝ SYSTÉM. Už som spomínal aplikacný systém (application framework). Ale co to vlastne je? Aplikacnému systému sa inak hovorí aj kostra aplikácie. Aby ste nepovedali, tu máte jednu definíciu: „Aplikacný systém je integrovaný súhrn objektovo orientovaných softvérových komponentov, zahrnujúci všetko na vytvorenie všeobecnej aplikácie.“ ? Predpokladám, že rovnako ako mne vám táto definícia velmi nepomohla. Rozdiel medzi samotnou knižnicou MFC a aplikacným systémom je malý. Ak by sme to chceli jednoducho vysvetlit, povedali by sme, že všetky triedy knižnice MFC tvoria aplikacný systém, na ktorom „staviate“ vašu aplikáciu. Aplikacný systém je vlastne kostrou aplikácie s podporou pre komponenty používatelského rozhrania, ktoré môžete pridávat na vytvorenú kostru aplikácie. Tieto komponenty používatelského rozhrania sú napríklad stavový riadok, panel nástrojov, podpora databáz a ActiveX komponentov. HISTÓRIA MFC (Z POHLADU VISUAL C++). Teraz vás caká história MFC. Je to trochu nudné cítanie, ale získate aspon aký-taký prehlad o tejto knižnici. Pravdepodobne vám väcšina informácií bude cudzia, ale slubujem, že sa pokúsim do skoncenia seriálu väcšinu z nich vysvetlit tak, aby ste ich mohli aktívne využívat vo vašich aplikáciách. O MFC sa zacalo hovorit na zaciatku 90. rokov. Prvá verzia MFC uzrela svetlo sveta spolu s prostredím Microsoft C/C++ verzie 7.0. Už vtedy obsahovala všeobecné triedy, napríklad triedu kolekcií (collections), zoznamov (lists), máp (maps) a polí (arrays). Obsahovala triedy pre prácu s retazcami, casovými údajmi, prístup k súborom, triedy pre podporu

Page 9: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

„viacdokumentových“ MDI (Multiple Document Interface) aplikácií a ciastocne podporovala aj OLE verzie 1.0. Prvý Visual C++ mal integrovanú MFC verzie 2.0. Táto verzia MFC pridávala do predchádzajúcej vela nových crt, ako sú podpora DLL, kontextovej pomoci, prístup k ovládacím prvkom Visual Basicu (VBX controls) a mnohé iné. MFC 2.5 dodávaná s Visual C++ 1.5 mala podporu pre databázy ODBC (Open Database Connectivity), takže programátori mohli jednoducho pristupovat k databázam napríklad Microsoft Access, Microsoft SQL Server a iným. Rovnako sa zlepšila podpora OLE 2.01. Techniky drag and drop a OLE Automation už neboli len snom vývojárov. Prvá 32-bitová verzia Visual C++ bola verzia 2.0. S nou prišla aj MFC verzie 3.0. Podporovala napríklad tabulku vlastností (property sheet), dokovatelné panely nástrojov a takzvaný Control Development Kit (CDK) na vytváranie ovládacích prvkov OLE. Visual C++ 2.1 s MFC verzie 3.1 rozšírilo podporu databáz – ODBC level 2 –, podporu spolocných ovládacích prvkov Windows 95 a obsahovala aj triedu Winsock na komunikáciu TCP/IP. Keby ste hladali Microsoft Visual C++ 3.0, hladali by ste márne. Microsoft sa totiž rozhodol, že verzie knižnice MFC a produktov Visual C++ sa musia zhodovat, a preto sa pokracovalo až Visual C++ verziou 4.0 a MFC 4.0. Novými prvkami boli triedy OLE založené na Data Access Objects (DAO), podpora kontajnerov ovládacích prvkov OLE, triedy na synchronizáciu vlákien, úplná podpora spolocných ovládacích prvkov Windows 95, nové triedy tree view a rich-edit view. Dalšia verzia bola MFC 4.2, dodávaná s Visual C++ 4.2. Obsahovala triedu WinInet, triedy serverov ActiveX, synchrónne a asynchrónne monikerové triedy ActiveX a bola zlepšená podpora ODBC. Visual C++ 5.0 obsahoval MFC verzie 4.21, ktorá opravovala chyby verzie 4.2 a pridávala podporu jazyka C++ pre klientske programy COM (Component Object Model) spolu s novým príkazom #import. Visual C++ 6.0 obsahuje MFC 6.0. Táto verzia MFC obsahuje nové triedy na prácu so sietou internet, nové triedy na spracovanie dátumu a rôzne triedy na rozšírené spracovanie niektorých ovládacích prvkov. PRÍKLAD. Tento príklad je ukážkou použitia aplikacného systému. Všimnite si, že triedy knižnice MFC a od nej odvodené sa zacínajú podla konvencií písmenom C. Výpis súboru first.h // trieda aplikácie class CFirstApp : public CWinApp { public: virtual BOOL InitInstance(); }; // trieda hlavného rámcového okna aplikácie class CMyFrame : public CFrameWnd { public: CMyFrame(); protected: // afx_msg oznacuje, že funkcia OnPaint je súcastou spracúvania správ MFC

afx_msg void OnPaint(); DECLARE_MESSAGE_MAP() }; Výpis súboru first.cpp #include <afxwin.h> // hlavickový súbor knižnice MFC deklaruje základné triedy #include "prvy.h" // vloženie našich tried CFirstApp first; // globálny objekt aplikácie BOOL CFirstApp::InitInstance() // volá funkcie na vytvorenie a zobrazenie rámcového okna aplikácie { m_pMainWnd=new CMyFrame; // volá konštruktor CMyFrame::CMyFrame m_pMainWnd->ShowWindow(m_nCmdShow); m_pMainWnd->UpdateWindow(); return TRUE; } BEGIN_MESSAGE_MAP(CMyFrame, CFrameWnd) // mapa správ ON_WM_PAINT()

Page 10: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

END_MESSAGE_MAP() CMyFrame::CMyFrame() { Create(NULL, "Program Hello"); // vytvorí hlavné rámcové okno aplikácie + titulok okna } void CMyFrame::OnPaint() // „kresliaca funkcia“ spracúva správu WM_PAINT { CPaintDC dc(this); // vytvára kontext zariadení na kreslenie CRect rectMax; // premenná ukladá velkost klientskeho okna CPen pen(PS_SOLID, 6, RGB(0, 0, 255)); // vytvára objekt pera – ekvivalent API funkcie CreatePen GetClientRect(rectMax); // do premennej rectMax sa uloží rozmer klientskeho okna dc.SelectObject(&pen); // vyberie uložený GDI objekt a nastaví objekt pera ako aktuálny GDI objekt dc.TextOut(0, 0, "Hello, world!"); // vypíše text na obrazovku dc.Ellipse(rectMax); // na obrazovku nakreslí kružnicu tak, aby vyplnala celé klientske okno dc.SelectStockObject(WHITE_BRUSH); // vyberie objekt pera pen a nastaví systémové biele pero } VYSVETLENIE PROGRAMU FIRST. Pozrite sa teraz na príklad Hello z druhej casti seriálu. Program je napísaný úplne inak, ale v skutocnosti robí to isté. V prvej casti seriálu som písal, že každý program pre Windows musí obsahovat funkciu WinMain. Ale program First na prvý pohlad túto funkciu nemá. Funkcie ako WinMain, WndProc, GetMessage a podobne sú zaclenené do aplikacného systému, teda práve do tej kostry aplikácie, a preto ich už nemusíte písat a sú „pripojené“ ku každému vášmu programu (v nasledujúcej casti si podrobne opíšeme proces zostavovania aplikácie). Trieda CFirstApp reprezentuje aplikáciu. Je odvodená od triedy MFC CWinApp, ktorá je základnou triedou klasickej aplikácie založenej na knižnici MFC. Objekt first je globálnym objektom aplikácie a je dôležitý pri spúštaní aplikácie. Pri spustení aplikácie hladá vstavaná funkcia WinMain globálne vytvorený objekt aplikácie triedy odvodenej od triedy CWinApp (v našom prípade objekt first). Uvedomte si, že globálne tvorené objekty sú vytvárané pred spustením hlavného programu. Ak funkcia WinMain tento objekt nájde, volá virtuálnu clenskú funkciu InitInstance, ktorá sa postará o vytvorenie a zobrazenie hlavného rámcového okna aplikácie. Túto funkciu musíte vo vašej odvodenej triede prepísat, lebo základná funkcia InitInstance, ktorá je clenskou funkciou triedy CwinApp, nevie, aký typ rámcového okna chcete. Po tom, ako WinMain zavolala funkciu InitInstance, volá funkciu Run, co je clenská funkcia triedy CWinApp. Túto funkciu nevidíte, opät je skrytá v aplikacnom systéme. Je to však velmi dôležitá funkcia a jej úlohou je dorucovat správy aplikácie oknám aplikácie. V jej tele sú funkcie TranslateMessage, DispatchMessage, PreTranslateMessage. Správa sa teda podobne ako uzol správ klasickej aplikácie založenej na Windows API. Ak dostane správu WM_QUIT, ukoncí aplikáciu. Ak naša aplikácia dostane správu WM_PAINT, volá obslužnú funkciu OnPaint. Funkcia OnPaint je volaná vždy, ked je potrebné prekreslit okno. To znamená vtedy, ked používatel zmenil velkost okna, ked sa aplikácia štartuje, ked je urcitá cast okna zakrytá a znovu odokrytá. Príkaz CPaintDC dc(this) má podobnú funkciu ako vám už známy príkaz BeginPaint (GDI objektom knižnice MFC bude venovaná niektorá dalšia cast seriálu). Objekt typu CRect je podobný API štruktúre RECT a je to pravouholník, ktorého súradnice zadávame v poradí left, top, right, bottom (pricom left a top sú súradnice lavého horného rohu pravouholníka a right a bottom sú súradnice pravého dolného rohu pravouholníka). Objekt pen nerušíme funkciou DeleteObject, ale využijeme menší fígel. Systém Windows obsahuje množstvo tzv. GDI objektov systémového zásobníka. Tieto GDI objekty dokáže aj sám zrušit. Funkcia SelectStockObject vyberie z kontextu zariadení objekt pera pen, zároven vráti ukazovatel na vynímaný objekt a nastaví GDI objekt systémového zásobníka WHITE_BRUSH ako aktuálny GDI objekt. My sa už teda nemusíme starat o rušenie tohto objektu WHITE_BRUSH. TIPY NA DOMA. V helpe si pozrite triedu CWinApp a jej clenské funkcie a premenné. Zistite nieco o dátovom clene m_pMainWnd a triede CFrameWnd a o jej clenských funkciách.

Page 11: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

NABUDÚCE. O mesiac si opíšeme proces zostavovania aplikácie pre Windows (proces kompilovania, linkovania), opíšeme si nové typy súborov, ktoré sa vyskytujú v programoch pre Windows. Dokoncíme si rozbor príkladu First, zároven aj príklad First zdokonalíme a povieme si nieco o mapách správ. 5. cast: Zostavovanie aplikácie Vítam vás pri poslednej casti seriálu v tomto roku a možno aj storocí (ci nebodaj tisícrocí?). Je to zaujímavé, ale my, co teraz žijeme, sme zažili a ešte aj zažijeme to, co sa možno nepodarilo nikomu. Ved pocítajte so mnou. Zatmenie Slnka a Mesiaca, viditelné z okna nášho domu, prelom tisícrocia, rôzne kométy, divoké postavenia planét a dalšie udalosti, ktoré sa normálne dejú jedna po druhej s odstupom dobrých 100 až 200 rokov. A my sme ich všetky zažili v priebehu 5 rokov. K tomu by sa dal prirovnat azda len otrepaný cirkusový výrok: „Uvidíte, co ešte nikto nikdy nevidel!“ A v tomto prípade je aj pravdivý. No ale vrátme sa k našej téme. Chápem, že sa vám asi tažko chce v tomto case rozmýšlat o takých veciach, ako sú C++ a programovanie, ale co už... Treba zatat zuby a pustit sa do ucenia. Na tomto mieste by som chcel podakovat všetkým, ktorí píšu svoje postrehy o tomto seriáli. Vdaka tomu, že si môžem precítat, co od neho cakáte a co by ste v nom uvítali, môžem vám jeho štruktúru stále viac prispôsobovat. Napríklad už v tejto casti nájdete velmi dobré linky na rôzne stránky zaoberajúce sa programovaním v C++. V každej novej casti nájdete aj nové linky. Zároven chcem vyzvat vás všetkých: ak máte nejaké nápady ci požiadavky, co by ste v seriáli chceli/nechceli vidiet, napíšte mi. Ak to budú dobré nápady, urcite sa zrealizujú.

V predchádzajúcej casti sme zacali s opisom knižnice MFC. Uviedli sme jeden ukážkový príklad: First, ktorý sme si aj strucne opísali. Ak máte pocit, že ste nieco nepochopili, nemajte strach, ku všetkému sa dostaneme. Zacneme tým najzákladnejším, teda procesom zostavovania aplikácie. BUILD PROCESS. Aby sme zacali úplne od základov, treba objasnit pojem projekt. Projekt predstavuje súhrn navzájom súvisiacich súborov, ktoré sú kompilované a linkované, aby vytvorili spustitelný súbor alebo knižnicu DLL. Toto je zaužívaná definícia, ktorá sa používa pri projektoch vo Windows. Aby ste si to vedeli lepšie predstavit, projektom môžeme oznacit všetky súbory, ktoré vytvorí prekladac v adresári projektu, plus nejaké dalšie, ktoré sú uložené v adresároch typických pre prekladac (toto sú napr. hlavickové súbory, knižnice). Prípony, ako aj opis súborov typických pre Visual C++ nájdete v tabulke 1. Mnohí z vás urcite poznajú súbory nazývané makefile. Pravda je taká, že ak pracujete s prostredím Visual C++ vyšších verzií, s týmito súbormi sa nestretnete, len ak by ste požiadali prekladac, aby ich vygeneroval. Tieto súbory v minulosti slúžili ako základné súbory celého projektu, v nich sa definovali všetky vztahy medzi zdrojovými súbormi. Momentálne sa velmi nepoužívajú, a ak predsa, slúžia len pre skutocných odborníkov a my sa nimi nebudeme zapodievat (ak aj, tak len vo „vyšších“ pokracovaniach seriálu). V terajšej dobe nahradil ich funkciu tzv. súbor projektu (project file). Je to súbor s príponou DSP a plní rovnakú funkciu ako súbory makefile (uvažujeme prostredie Visual C++). A ako vlastne prebieha kompletný proces zostavovania celej aplikácie? Pozrite si obrázok 1. Myslím, že nepotrebuje komentár.

Prípona súboru

Opis Anglický ekvivalent

APS Podporuje prehliadac zdrojov Resource view file BSC Súbor informácií prehliadaca kódu Source browser information

file CLW „Sprievodca“ triedami Class Wizard file DSP Súbor projektu Project file DSW Súbor pracovného priestoru Workspace file MAK Makefile NCB Súbor prehliadaca tried Class view file OPT Obsahuje konfiguráciu pracovného

priestoru

PLG Vytvára súbor log Tab. 1 Zoznam súborov, ktoré generuje prostredie Visual C++ TYPY SÚBOROV A SÚCASTI VISUAL C++. Aby ste lepšie pochopili, aké typy súborov sa vo Visual C++ generujú, uvedieme si ich strucný opis a vysvetlíme si aj to, aké súcasti Visual C++ ich vytvárajú.

Page 12: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Editor a prehliadac zdrojov: „Zdrojom“ sa vo Windows aplikáciách oznacuje tá cast vašej aplikácie – projektu, ktorú nemusíte programovat len zadávaním nekonecného množstva kódu, ale zvycajne ju navrhujete vizuálne, práve v editori zdrojov. Príkladom zdroja sú napríklad ikony, bitmapy, menu, akcelerátory (klávesové skratky), dialógy. Každý projekt obycajne obsahuje aspon jeden textový súbor skriptov zdrojov (tu sú zadefinované zdroje vašej aplikácie). Tento súbor má príponu RC, co je skratka z resource script file. K vytváraniu súborov zdrojov sa dostaneme neskôr.

Kompilátor zdrojov: Precíta textový súbor skriptov zdrojov (RC) a zapisuje binárny RES súbor pre linker.

Kompilátor C/C++: Kompiluje zdrojový kód (medzi jazykmi C a C++ rozlišuje pomocou prípony súboru. Ak *.c, použije sa kompilátor pre jazyk C, ak *.cpp, resp *.cxx, použije sa kompilátor jazyka C++) a vytvára súbory s príponou OBJ.

Linker: Precíta súbory s príponou OBJ a RES, ktoré vytvoril kompilátor C/C++ a kompilátor zdrojov a s pomocou LIB súborov knižnice MFC, knižnice runtime a Windows kódu vytvára spustitelný EXE súbor. Samozrejmostou je využitie tzv. inkrementálneho linkovania, co minimalizuje cas potrebný na opakované linkovanie kódu, v ktorom sme urobili malé zmeny.

App Wizard: Je to generátor kódu, ktorý vytvára funkcnú kostru aplikácie. Takto vygenerovaná aplikácia má minimálnu funkcnost a slúži len ako základ na pridávanie dalších prvkov. Ako bude vyzerat táto „minimálna funkcnost“, to si volíme v dialógoch pri generovaní aplikácie pomocou App Wizardu. Jeho konkrétne použitie si vysvetlíme nabudúce.

Obr. 1 Proces zostavovania aplikácie vo Visual C++

Class Wizard: Tento program nám ulahcuje udržiavanie poriadku v kóde a triedach. Ak potrebujeme novú triedu ci obslúžit nejakú správu, robíme tak vždy pomocou Class Wizardu. Vyhneme sa tak mnohým problémom s integritou celej aplikácie. Jeho konkrétne použitie si rovnako vysvetlíme nabudúce.

Source Browser: Tento nástroj nám ulahcuje vôbec vyznat sa v zdrojovom kóde. Umožnuje nám editovat aplikáciu nie z pohladu súborov, ale z pohladu tried a funkcií. Pracuje vo viacerých režimoch, opis nájdete v helpe. Ja vám odporúcam vypnút ho, jednak ako zaciatocníci sa v nom asi tažko vyznáte, jednak znacne spomaluje proces zostavovania aplikácie, kedže generuje dost velký súbor (*.bsc). Vypnút ho môžete takto: Z menu Project vyberte Settings..., kliknite na záložku Browse

Page 13: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Info a odškrtnite položku: Build browse info file. Rovnako na karte C/C++ odškrtnite položku Generate browse info. (platí pre verziu Visual C++ 6.0). Ked budete písat také zložité aplikácie, že vám bude robit problém vyznat sa vo svojom zdrojovom kóde, alebo ked preberiete nejaký projekt od svojho kolegu, aby ste sa s ním oboznámili, má použitie Source browsera svoje opodstatnenie, inak nie.

Debugger: Predpokladám, že každému z vás je pojem debugger jasný. Tento program môže casto predstavovat poslednú záchranu z beznádejnej situácie. A samozrejme, že nesmie chýbat v takom prostredí, ako je Visual C++. Len v strucnosti: debugger Visual C++ obsahuje všetky štandardne používané prvky – krokovanie programu, nastavenie bodov prerušenia a podobne. Odporúcam vám dobre sa oboznámit s prostredím debuggera, lebo pri vývoji aplikácií ho budete casto potrebovat. Napíšte si nejaký jednoduchý „programík“ (nemusí to byt program pod Windows) a potom si ho vyskúšajte odkrokovat, sledujte hodnoty premenných... MAPOVANIE SPRÁV V KNIŽNICI MFC. Už viete, ako sa aplikácia zostavuje, aké sú hlavné súcasti prostredia Visual C++, teraz sa vrátme k príkladu First z predchádzajúcej casti. Ako vlastne pracuje systém spracúvania správ v aplikácii, ktorá využíva knižnicu MFC? Na zaciatku musíme ešte doplnit vaše vedomosti o správach. Už sme sa o nich zmienili, ale ešte sme si ich nerozdelovali. Rozlišujeme tri základné druhy správ v systéme Windows. Prvým typom, s ktorým sme sa už stretli, sú tzv. štandardné správy. Zacínajú sa prefixom WM. Príkladom sú napr. správy WM_PAINT, WM_LBUTTONDOWN, WM_TIMER a podobne. Týchto správ je drvivá väcšina. Druhým typom sú príkazové správy. Tie reagujú na používatelov príkaz, ktorý vydal bud kliknutím na položku v menu, na panel nástrojov alebo na nejaký iný ovládací prvok, alebo ked používatel stlacil skratkový kláves. Tieto správy sa zacínajú rovnako prefixom WM, ale za ním musí nasledovat ešte COMMAND, takže príkazová správa vyzerá: WM_COMMAND. Tretím typom správ sú notifikacné alebo aj informacné správy. Tieto správy využívajú pre seba hlavne ovládacie prvky a dcérske okná (co to je, si povieme neskôr), aby dali svojmu rodicovskému oknu vediet, že sa zmenil ich stav. Napríklad ked zmeníte text v textovom poli, ono pošle správu EN_CHANGE, aby oznámilo, že sa nieco udialo, a je na programe, ako túto správu obslúži. Dost však bolo teórie, podme si to aj prakticky vyskúšat. Pozrite sa teraz na výpis príkladu First, najprv na výpis súboru first.h. Nájdite si deklaráciu chránených (protected) funkcií v triede CFirstApp. Nájdete tam deklaráciu jednej funkcie OnPaint. Znaky afx_msg oznacujú, že táto funkcia slúži na spracovanie správ systému Windows funkciami knižnice MFC (v skutocnosti je afx_msg len oznacenie pre nás, že daná funkcia je clenom mapy správ, prekladac toto oznacenie ignoruje). Meno tejto funkcie si nemôžeme volit, ako sa nám zachce. Pre každú správu, ktorú vie knižnica MFC obslúžit, má už MFC vyhradené nejaké meno. Takže ak my chceme obslúžit stlacenie lavého tlacidla myši, musíme použit clenskú funkciu knižnice MFC OnLButtonDown a nijakú inú. Opis správ a priradených funkcií nájdete v tabulke 2. Pod deklaráciou tejto funkcie sa nachádza makro DECLARE_MESSAGE_MAP, ktoré je vždy deklarované v definícii tej triedy, ktorá má na starosti spracovanie konkrétnej správy. Takto sa deklaruje mapa správ. Implementáciu mapy správ si pozrite v zdrojovom súbore príkladu First.cpp. Zacína sa makrom BEGIN_MESSAGE_MAP a koncí makrom END_MESSAGE_MAP. V makre BIGIN_MESSAGE_MAP môžete nájst názov triedy, pre ktorú je urcená táto mapa správ (CMyFrame), ako aj meno triedy, od ktorej je táto trieda odvodená (CFrameWnd). Medzi týmito dvoma makrami sa nachádza zoznam správ, ktoré sú obsluhované funkciami MFC. V našom prípade je to správa ON_WM_PAINT, ktorá je obsluhovaná funkciou OnPaint. Ak teda aplikácia vyšle správu WM_PAINT, aplikacný systém ju zachytí a zavolá príslušnú funkciu, aby správu obslúžila. Pri vývoji aplikácie sa obycajne nemusíte starat o písanie správ a písanie príslušných deklarácií ich obslužných funkcií. Túto prácu za nás robí Class Wizard. Jeho použitie na konkrétnom príklade si ukážeme nabudúce. Aby ste však aspon trochu pochopili, ako daný systém pracuje, mierne si zdokonalíme príklad First. Chceme, aby sa po stlacení lavého tlacidla myši vypísal text „Tu som“ presne na tom mieste, kde sme klikli myšou do okna aplikácie. Ako to urobit? V prvom rade musíme pridat funkciu, ktorá by obsluhovala správu stlacenia lavého tlacidla myši. Nebudeme ešte na to používat Class Wizard, urobíme to klasicky rucne. Ako vidíte v tabulke 2, správa na stlacenie lavého tlacidla myši sa volá WM_LBUTTONDOWN a jej obslužná funkcia OnLButtonDown. Preto dopíšte deklaráciu obslužnej funkcie OnLButtonDown do súboru First.h pod funkciu OnPaint, takže chránená cast triedy CFirstApp bude vyzerat takto: protected: // afx_msg oznacuje ,že funkcia OnPaint je súcastou spracúvania správ MFC

afx_msg void OnPaint(); afx_msg void OnLButtonDown(UINT nFlags, CPoint point);

Page 14: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

DECLARE_MESSAGE_MAP() Prvý parameter funkcie OnLButtonDown nás teraz nemusí zaujímat. O druhom nám stací vediet to, že v nom sú uložené súradnice kurzora pri stlacení lavého tlacidla myši. X-ová súradnica je prístupná ako point.x a Y-ová ako point.y (preco to tak je, to si vysvetlíme v dalších castiach, majte strpenie). Takže funkcia OnLButtonDown nám poskytuje presne to, co potrebujeme. Aby náš program fungoval, nestací pripísat funkciu na stlacenie lavého tlacidla len do deklarácie mapy správ. Musíme pozmenit aj jej implementáciu (súbor: First.cpp), a to takto: BEGIN_MESSAGE_MAP(CMyFrame, CFrameWnd) // mapa správ ON_WM_LBUTTONDOWN() ON_WM_PAINT() END_MESSAGE_MAP() Teraz máme funkciu už kompletne pridanú do systému mapy správ. Ale po spustení programu zistíte, že stlacenie lavého tlacidla myši nic neurobí. Na co sme asi zabudli? Samozrejme, zabudli sme napísat samotné telo funkcie OnLButtonDown. Pripíšte preto nasledujúci kód do súboru First.cpp nad funkciu OnPaint (alebo aj pod nu, je to jedno): void CMyFrame::OnLButtonDown(UINT nFlags, CPoint point) { CClientDC dc(this); dc.TextOut(point.x, point.y, "Tu som"); }

Správa Windows Zodpovedajúca clenská funkcia knižnice MFC

Opis

WM_CHAR OnChar Reaguje na udalost, spôsobenú na klávesnici

WM_CLOSE OnClose Vymaže okno a uzavrie ho WM_CREATE OnCreate Vytvorenie okna WM_DESTROY OnDestroy Zrušenie okna WM_ERASEBKGND OnEraseBkgnd Obnovenie pozadia okna WM_HSCROLL OnHScroll Kliknutie na horizontálny posuvník WM_VSCROLL OnVScroll Kliknutie na vertikálny posuvník WM_KEYDOWN OnKeyDown Stlacenie klávesu WM_KEYUP OnKeyUp Uvolnenie klávesu WM_LBUTTONDBLCLK OnLButtonDblClk „Dvojklik“ na lavom tlacidle myši WM_RBUTTONDBLCLK OnRButtonDblClk „Dvojklik“ na pravom tlacidle myši WM_LBUTTONDOWN OnLButtonDown Stlacenie lavého tlacidla myši WM_RBUTTONDOWN OnRButtonDown Stlacenie pravého tlacidla myši WM_LBUTTONUP OnLButtonUp Uvolnenie lavého tlacidla myši WM_RBUTTONUP OnRButtonUp Uvolnenie pravého tlacidla myši WM_MOVE OnMove Presun okna WM_SETFOCUS OnSetFocus Okno sa stalo aktívnym (dostalo focus) WM_SHOWWINDOW OnShowWindow Zobrazenie/ukrytie okna WM_SIZE OnSize Zmena velkosti okna WM_TIMER OnTimer Vypršanie casu nastaveného v casovaci Tab. 2 Zoznam najcastejšie používaných funkcií na obsluhu konkrétnych správ systému Windows Ak teraz spustíte program First, po stlacení lavého tlacidla myši sa na obrazovke presne na pozícií kurzora objaví text: „Tu som“. A je to hotové. Ani to nebolo také tažké, no nie? Ak vás zaujíma, co to znamená CClientDC dc(this), prezradím vám len to, že týmto vytvárame kontext zariadení, ktorý má také rozmery ako používatelská oblast okna programu First. Na zopakovanie: používatelská oblast okna nezahrna titulkový pruh, menu a okraj okna. Podrobnejšie si túto tému rozoberieme v casti venovanej triedam kontextu zariadení. TIPY NA WWW. Ako som písal v úvode, od tejto casti vás budem pravidelne informovat o zaujímavých adresách, ktoré sa týkajú problematiky programovania v jazykoch C/C++ a Visual C++.

Page 15: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Ak aj vy poznáte nejaké dobré adresy, prosím, napíšte mi ich, cím ich bude viac, tým lepšie. Tu je prvých pät: http://www.programmingtutorials.com/main.asp – nieco pre zaciatocníkov, rôzne tutorialy http://www.visualc.com/ – stránka venovaná Visual C++ http://www.planet-source-code.com/– tu nájdete skutocne nepreberné množstvo zdrojových kódov http://www.programmersheaven.com/– vynikajúca stránka, nájdete tu vela odkazov na iné stránky http://www.cuj.com/ – stránka C/C++ Journalu, casopis venovaný jazykom C/C++ Tolko na zaciatok, nabudúce dalšie. TIPY NA DOMA. Skúste pridat do programu First dalšie funkcie obsluhujúce správy. Napríklad vymyslite nejakú cinnost pre pravé tlacidlo myši, pre „dvojklik“ lavého tlacidla a podobne. Inšpiráciu na funkcie, ktoré môžete použit, nájdete v tabulke 2. NABUDÚCE. Nabudúce si opíšeme prácu s prostredím Visual C++ (vytvoríme aplikáciu s App Wizardom, použijeme Class Wizard, editor zdrojov). Takže stretneme sa až v budúcom roku. Želám vám krásne prežitie vianocných sviatkov a vela úspechov v novom roku. Majte sa pekne. 6. cast: AppWizard a tvorenie aplikácie Silvestra a Vianoce máme za sebou, dúfam, že ste plní sily a energie do nového roku a že ste si dali vela predsavzatí, ktoré hodláte aj splnit. Nech vám to teda vyjde a dúfam, že vám pri tom pomôže aj PC REVUE. A ako to vyzerá so seriálom C++ pod Windows? Dobre ?. No ale teraz vážne. V predošlom pokracovaní sme si vysvetlili základné casti prostredia Visual C++ a spôsob zostavovania aplikácie v nom. Všetko od editácie kódu až po výstupný EXE súbor. Teraz si podrobnejšie opíšeme, ako sa s jednotlivými súcastami prostredia Visual C++ pracuje a vytvoríme našu prvú „skutocnú“ aplikáciu s využitím App Wizardu. Samozrejmostou sú aj dalšie linky na zaujímavé stránky, ktorých obsah sa týka práve jazykov C/C++ a na ktorých nájdete obrovské množstvo rôznych tipov, trikov a rád, ako môžete vyvíjat svoj vlastný softvér. A ako z poradového císla tejto casti môžete usúdit, ide už o šieste pokracovanie seriálu, co znamená, že sa už stretávame pol roka. Ako sa tak pozerám na to, co sme doposial prebrali, asi ešte neviete písat velmi schopné aplikácie, ale tie teoretické veci, co sme si vysvetlovali, sú naozaj velmi dôležité a bez nich by ste asi velmi tažko pochopili problematiku programovania v jazyku C++ pod operacným systémom Windows. Rád vám teraz oznamujem, že od tejto casti bude tých „suchých“ teoretických informácií coraz menej a budeme sa venovat najmä praktickej stránke tvorenia aplikácie a vaše schopnosti písat užitocné aplikácie budú s každým pokracovaním seriálu narastat. Tak sa do toho pustme! Document/View architecture Na zaciatok predsa len trocha teórie. Architektúra nazývaná Document/View tvorí jadro aplikacného systému. Pomocou nej sú v aplikácii oddelené údaje a používatelov pohlad na tieto údaje. Pre jednoduchost si predstavte údaje zapísané v tabulke Excelu. Na jeden list môžete umiestnit tieto údaje zapísané klasicky vo forme císel a tie isté údaje vo forme grafu. Vznikla vám situácia, ked na rovnaký údaj existujú dva rôzne pohlady. Práve v tom je velká výhoda tejto architektúry. Stací vám totiž zadefinovat jeden dokument a potom vytvárat rôzne pohlady na údaje v nom zapísané, bez ich nevyhnutnej editácie. Rovnako nad dokumentom robíme operácie, ako sú otvorit súbor, zapísat súbor a pod. Knižnica MFC nám poskytuje velmi komfortné prostriedky na spravovanie dokumentov a pohladov v našich aplikáciách. Zatial budeme pracovat len s pohladom, k zložitejším dokumentom sa dostaneme neskôr. Prvá aplikácia Konecne sme sa dostali až k tvorbe našej prvej aplikácie v prostredí Visual C++. Nazvime ju jednoducho Hi. Nasledujúci step-by-step opis sa bude týkat Visual C++ verzie 6.0, ale dá sa aplikovat aj na nižšie verzie. Bude sa skladat z ôsmich bodov, pricom samotné kroky AppWizardu tvoria kroky 2-7. Tak teda vycistite myšky, pripravte klávesnicu, ideme na to!

Krok 1: Predpokladám že už máte spustené prostredie Visual C++. Kliknite na File a vyberte New. Zvolte záložku Projects a vyplnte údaje, tak ako vidíte na obr. 1 Ked ste vyplnili tieto údaje, kliknite na OK. Visual C++ automaticky vytvorí adresár zadaný v polícku Location s podadresárom, ktorý má názov vašej aplikácie. V našom prípade vytvorí: C:\Seriál\Hi. V tomto adresári budú uložené všetky súbory, ktoré váš projekt potrebuje (*.cpp, *.h, *.rc...).

Krok 2: Po kliknutí na OK sa objaví dialóg (prvý dialóg AppWizardu), aký môžete vidiet na obr. 2 (pravdepodobne však bude implicitne prednastavená volba Multiple documents).

Page 16: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Zvolte teda Single document. Je to v súvislosti práve s Document/View architektúrou, takže vás to teraz nemusí trápit, dostaneme sa k tomu neskôr. Pokracovat môžete, ak kliknete na Next.

Krok 3: Objaví sa další dialóg, ako vidíte na obr. 3, v ktorom si volíme, ci a akú podporu pre databázy bude mat naša aplikácia. V tomto dialógu nechajte vš etky pôvodné nastavenia a opät kliknite na Next.

Krok 4: Dialóg, aký vidíte na obr. 4, ponúka nastavenia týkajúce sa architektúry klient/server, automatizácie OLE a ActiveX komponentov. Nechajte všetko na pôvodnom nastavení a kliknite na Next.

Krok 5: Na tomto dialógu (obr. 5) môžete menit niektoré z vizuálnych vlastností aplikácie, ako nastavenie toolbarov, niektorých položiek menu. V dolnej casti dialógu vidíte tlacidlo Advanced. Ked nan kliknete, budete môct zmenit napríklad príponu súborov, štýly okien a pod. Nechajte všetko tak, ako je, a opät kliknite na Next.

Krok 6: V tomto dialógu (obr. 6) nastavujete štýl projektu a vygenerovanie pomocných komentárov (to sú tie známe //TODO, pozri dalej v tejto casti). Nechajte MFC Standard a nechajte vygenerovat komentáre. Kliknite na Next.

Krok 7: Objaví sa posledný dialóg AppWizardu (obr. 7), na ktorom máte podrobný prehlad o vytváraných triedach, o ich základných triedach a názvoch súborov. Názvy tried a súborov našej aplikácie sa implicitne generujú podla nami zadaného mena aplikácie. Tieto informácie môžete pri niektorých triedach aj menit, ale pre naše potreby stacia aj prednastavené hodnoty. Kliknite na Finish.

Obr. 1 Zaciatok tvorenia MFC aplikácie

Page 17: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Obr. 2 Prvý krok tvorenia aplikácie pomocou AppWizardu

Obr. 3 Druhý krok tvorenia aplikácie pomocou AppWizardu

Page 18: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Obr. 4 Tretí krok tvorenia aplikácie pomocou AppWizardu

Obr. 5 Štvrtý krok tvorenia aplikácie pomocou AppWizardu

Page 19: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Obr. 6 Piaty krok tvorenia aplikácie pomocou AppWizardu

Obr. 7 Šiesty a posledný krok tvorenia aplikácie pomocou AppWizardu

Page 20: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Obr. 8 Informacný dialóg AppWizardu o novom projekte

Page 21: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Obr. 9 Výstup aplikácie Hi

Krok 8: Tu vám Visual C++ ponúka ešte posledný pohlad na to, co vlastne vytvárate (obr. 8). Po kliknutí na OK a krátkej chvíli, ktorá je potrebná na generovanie súborov, máte vygenerovanú základnú a úplne funkcnú kostru aplikácie. Skúste len tak pre zaujímavost skompilovat a spustit túto aplikáciu. Ako môžete vidiet, ide skutocne o úplne funkcnú aplikáciu, so všetkým, co k takejto aplikácii patrí: menu, panel nástrojov (toolbar), stavový riadok (status bar). Trošku ju preskúmajte, skúšajte rôzne položky menu, aby ste prišli na to, ako pracuje. Všetko toto ste zvládli bez jediného riadka kódu! Vygenerované súbory Teraz sa pozrite do adresára C:\Seriál\Hi. Je tam niekolko súborov, ktoré nám AppWizard vygeneroval. AppWizard vytvoril štyri triedy pre našu aplikáciu, ktoré odvodil od základných tried knižnice MFC.

1. Triedu aplikácie – je odvodená od triedy CWinApp knižnice MFC. Pri generovaní aplikácie sa vytvorí na základe tejto triedy objekt odvodený od triedy CWinApp. Je to základný objekt celej aplikácie (pozri program First).

2. Triedu hlavného okna – jej názov je vždy CMainFrame (nezáleží teda na mene vašej aplikácie). Táto trieda býva odvodená od triedy MFC CFrameWnd (pri aplikáciách Single document) alebo od triedy CMDIFrameWnd (pri aplikáciách Multiple documents).

3. Triedu dokumentu – je odvodená od triedy MFC CDocument. Táto trieda obsahuje clenské funkcie pre prácu s dokumentmi, ako je nacítanie a ukladanie dát.

4. Triedu pohladu – je odvodená od triedy MFC CView. Takto vytvorený pohlad je pripojený k objektu dokumentu, ktorý bol vytvorený od triedy CDocument a pomáha zobrazit údaje z dokumentu. V nasledujúcich castiach sa s nou podrobnejšie oboznámime.

Je dôležité aspon informacne vediet, co sa v ktorom súbore nachádza. Ak totiž budete nechávat mená súborov vždy implicitné (co vám hlavne zo zaciatku velmi odporúcam), tak ako vám ponúka AppWizard, ich mená sa budú menit len v závislosti od mena aplikácie, ale napríklad názov súboru obsahujúci slovo View bude vždy súborom pohladu a názov obsahujúci Doc bude vždy súborom

Page 22: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

dokumentu. Napríklad predstavte si aplikáciu Pokus . Mená súborov obsahujúcich pohlad by boli PokusView.cpp, resp. PokusView.h a mená súborov obsahujúcich dokument by boli PokusDoc.cpp, resp. PokusDoc.h. Pri našej aplikácii Hi sa tieto súbory volajú pre pohlad HiView.cpp, resp. HiView.h a pre súbor dokumentu HiDoc.cpp, resp. HiDoc.h. Ako vidíte, závisí len od mena aplikácie, ostatné ostáva rovnaké. V tabulke 1 máte opis najdôležitejších súborov aplikácie Hi a ich obsahu.

Súbor Opis Hi.cpp Zdrojový súbor triedy CHiApp aplikácie Hi Hi.h Hlavickový súbor triedy CHiApp aplikácie Hi HiView.cpp Zdrojový súbor triedy CHiView aplikácie Hi HiView.h Hlavickový súbor triedy CHiView aplikácie Hi HiDoc.cpp Zdrojový súbor triedy CHiDoc aplikácie Hi HiDoc.h Hlavickový súbor triedy CHiDoc aplikácie Hi Mainfrm.cpp

Zdrojový súbor triedy CMainFrame aplikácie Hi

Mainfrm.h Hlavickový súbor triedy CMainFrame aplikácie Hi Hi.rc Definuje všetky zdroje (menu, dialógy...) Hi.res Skompilovaná verzia súboru Hi.rc Resource.h Textový súbor obsahujúci identifikátory priradené zdrojom aplikácie Hi Stdafx.h Hlavickový súbor, do ktorého sa umiestnujú hlavne direktívy #include

a takto sa vkladajú hlavickové súbory knižnice MFC Stdafx.cpp Zdrojový súbor doplnujúci hlavickový súbor Stdafx.h. Pri kompilácii sa z

týchto dvoch súborov vytvorí predkompilovaný súbor Hi.pch, ktorý znacne urýchluje neskorší preklad (nekompilujú sa nezmenené casti kódu, ale len tie zmenené)

Tab. 1 Opis dôležitých súborov programu Hi Pohlad – View Ako som už spomenul v úvode, najprv zacneme pracovat s pohladom. Pohlad (View) sa javí z pohladu používatela ako okno. Toto okno sa správa ako hociktoré iné, môžeme menit jeho velkost, zatvorit ho, minimalizovat... Z nášho pohladu (teda z pohladu knižnice MFC) je pohlad objekt triedy odvodenej od triedy pre pohlady CView. Vlastnosti tohto objektu nastavujeme ako v každej aplikácii využívajúcej objekty pomocou clenských funkcií tejto triedy. Samozrejme, platia všetky pravidlá OOP programovania. Dedicnost je úplne samozrejmá a bez nej by nebola nijaká MFC. Rovnako aj polymorfizmus sa využíva pomerne casto.

Trieda pohladu: AppWizard nazval našu triedu pohladu CHiView. Ako si môžete v zdrojovom kóde tejto triedy pozriet, je odvodená od spomínanej triedy CView. Ak chcete vidiet kompletnú hierarchiu tried MFC, pozrite si help, ale upozornujem vás, že je to obrovské množstvo informácií, a ak ste si ešte nie istí v tom, co robíte, mohlo by vás to mierne popliest. Existujú akési „základné“ triedy MFC, ale o tom nabudúce. Kreslenie do pohladu Aby sme mohli do okna aj nieco kreslit, musíme si osviežit a rozšírit naše vedomosti o kontexte zariadení. Ako iste s prechádzajúcich castí viete, je to akýsi „medzistupen“ pre komunikáciu našej aplikácie s hardvérom pocítaca. V knižnici MFC predstavuje kontext zariadení objekt triedy CDC. Obycajne deklarujeme ukazovatel na tento objekt a pomocou neho pristupujeme k jednotlivým clenským funkciám triedy CDC. Opät existuje vela tried, ktoré sú odvodené od základnej triedy CDC. Spomeniem napríklad CPaintDC, ktorej použitie ste mohli vidiet v tretej casti seriálu, dalej napríklad CClientDC, co predstavuje klientske okno, a dalšie. Samozrejme, že všetky tieto odvodené triedy zdedia funkcie triedy CDC (samozrejme, zachovávajú sa pravidlá dedicnosti a platia špecifikátory prístupu: public, private a protected). Najdôležitejšou funkciou, ktorá sa zúcastnuje na kreslení do okna, je OnDraw. Je to virtuálna clenská funkcia triedy CView a je volaná vždy, ked je potrebné prekreslit okno (už sme uviedli, kedy je to potrebné). Nachádza sa aj v našom súbore triedy pohladu: HiView.h, kedže túto funkciu je potrebné „pretažit“ (override) na nakreslenie nášho pohladu. Aby naša aplikácia aj nieco robila, musíme dopísat nejaké veci do súboru HiView.cpp. Otvorte teda tento súbor a nájdite v nom funkciu OnDraw. Jej kód bude vyzerat takto: void CHiView::OnDraw(CDC* pDC)

Page 23: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

{ CHiDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: add draw code for native data here }

Ako vidíte, sama aplikácia nás upozornuje, že tu môžeme pridávat naše vlastné veci ( //TODO: add draw code for native data here). Hned v hlavicke funkcie vidíme spomínaný ukazovatel na objekt triedy CDC, ktorý nám hned aj príde vhod, lebo pomocou neho budeme pristupovat k clenským funkciám tejto triedy. Pridajte nasledujúci kód do funkcie OnDraw: CRect rectMax; // získa rozmery klientskeho okna GetClientRect(rectMax); // vpíše do pohladu text pDC->TextOut(0, 0, "Hello, world!"); // nastaví objekt systémového zásobníka ako aktuálny GDI objekt pDC->SelectStockObject(GRAY_BRUSH); // nakreslí do pohladu elipsu a použije pritom objekt systémového zásobníka pDC->Ellipse(rectMax);

Teraz aplikáciu skompilujte a spustte. Ak ste neurobili chybu, výstup aplikácie by mal byt rovnaký, ako vidíte na obr. 9.

Funkcie TextOut, SelectStockObject a Ellipse sú clenskými funkciami triedy CDC, preto k nim pristupujeme pomocou ukazovatela na objekt triedy CDC a ním je pDC. Tento ukazovatel je parametrom funkcie OnDraw. No povedzte, nebolo to pohodlnejšie ako písanie niekolko desiatok riadkov v programe First z tretej casti? A pritom program First nemal menu, toolbar ani iné veci, ktoré naša aplikácia obsahuje. A to všetko napísaním len ôsmich riadkov kódu (vrátane komentárov). Je síce pravda, že aplikácia Hi nepoužíva objekt pera, ale pre jednoduchost iba GDI objekty systémového zásobníka, no nejakú zmenu treba. Ak chcete, aby vykonávala takú istú cinnost ako program First, modifikujte funkciu OnDraw. Ako? To vám zostáva na domácu úlohu ?. Nabudúce sa k tomu vrátime, ale je to skutocne velmi jednoduché a mali by ste to zvládnut aj sami. Tipy na WWW Tu máte dalšie dobré linky na stránky, ktoré sa zaoberajú programovaním v jazykoch C/C++. http://www.progsharehouse.com/index.pl – vynikajúca stránka. Doslova tony informácií. http://www.redrival.com/bigt/ – opisuje základy programovania a milovníci grafiky si takisto prídu na svoje. http://www.kbcafe.com/ – ak ste trochu pokrocilí, je táto stránka práve pre vás. Nájdete tu rôzne tutorialy a on line knihy. http://www.microsoft.com/– pýtate sa, co tu robí táto stránka? Firma Microsoft je predsa výrobcom prostredia Visual C++, tak kde hladat lepšie informácie o tomto prostredí? Informácie získate v casti Developer Tools. Rovnako v Download Centre, ak zadáte produkt Visual C++ 6.0, môžete získat nejaké zdrojové texty, prípadne nejaké update. Tipy na doma Doma skúste generovat pomocou AppWizardu vlastné aplikácie, zmente niektoré nastavenia, aby ste prišli na to, co ktoré nastavenie znamená (nemente však nastavenia databáz a nenastavujte generovanie Multiple documents aplikácie, zbytocne by vás to doplietlo). Rovnako skúste zmenit aj aplikáciu Hi, aby robila to isté, co aplikácia First z tretej casti. Nabudúce Dúfam, že som touto castou uspokojil tých, ktorí sa dožadovali zväcšenia rozsahu jednotlivých castí. Nabudúce si opíšeme prácu s ClassWizardom a pomocou neho pridáme do aplikácie Hi spracúvanie základných správ. Opíšeme si aj „základné“ triedy knižnice MFC. Teším sa na stretnutie opät o mesiac. 7. cast: Class Wizard a správa tried

Page 24: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Mesiac ubehol a my sa opät stretávame pri seriáli C++ pod Windows, ktorý dospel už k svojej siedmej casti. Takže co je nové? Už asi všetci viete o novom webe PC REVUE. Ak vás zaujíma programovanie pod Windows a tento seriál, chcel by som vás upozornit, že na spomenutej stránke v sekcii Programujeme môžete nájst dalšie ukážkové príklady a rôzne iné zdroje k tomuto seriálu, ktoré vám, dúfam, pomôžu lepšie sa vyznat vo svete C++. Tieto ukážkové príklady budú zamerané na práve preberanú tému, napríklad tam budú riešené domáce úlohy a iné okrajové problémy, na ktoré v casopise nezvýši miesto. V predošlej casti sme dost podrobne opísali App Wizard a jednotlivé kroky tvorenia aplikácie. Teraz si podrobnejšie rozoberieme další, nemenej dôležitý prvok prostredia Visual C++, a síce Class Wizard. Opät nájdete aj nejaké dalšie linky na stránky zaoberajúce sa programovaním. Najprv sa však ešte vrátime k predchádzajúcej casti a slúbenému „problémiku“, ako upravit aplikáciu Hi, aby jej výstup bol zhodný s programom First zo štvrtej (nie z tretej!) casti. Týmto sa ospravedlnujem za zámenu castí, ale dúfam, že ste na to prišli sami. Pustme sa teda do toho. DOMÁCA ÚLOHA. Pravdepodobne ste tento problém všetci úspešne vyriešili, ak nie, tu je jeho riešenie. Stacilo len zamenit kód vo funkcii OnDraw aplikácie Hi za kód funkcie OnPaint programu First a trošku ho upravit. A to tak, že namiesto priameho prístupu pomocou objektu triedy CPaintDC (týmto objektom je dc) k jej clenským funkciám budeme využívat ukazovatel pDC, odovzdávaný ako parameter funkcie OnDraw. Tento ukazovatel však nebude ukazovat na objekt triedy CPaintDC, ale na objekt triedy CDC. Obidve triedy (CDC a CPaintDC) sú síce triedami kontextu zariadení, ale objekt odvodený od triedy CDC má všeobecnejší charakter, kedže v hierarchii tried kontextu zariadení stojí najvyššie trieda CDC a trieda CPaintDC je len potomkom triedy CDC. Trieda CPaintDC sa ani velmi v aplikáciách pre Windows nepoužíva, a ak, tak len v prípade, ked potrebujete prepísat funkciu OnPaint. Zmenený kód (teraz už bez zbytocných komentárov) potom vyzerá takto: void CHiView::OnDraw(CDC* pDC) {

CHiDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: add draw code for native data here CRect rectMax; CPen pen(PS_SOLID, 6, RGB(0, 0, 255)); GetClientRect(rectMax); pDC->SelectObject(&pen); pDC->TextOut(0, 0, "Hello, world!"); pDC->Ellipse(rectMax); pDC->SelectStockObject(WHITE_BRUSH); } CLASS WIZARD. Domácu úlohu sme teda vybavili, môžeme zacat preberat nové veci. Class Wizard sme si už zhruba opisovali v piatej casti, takže už asi približne viete, na co táto súcast prostredia Visual C++ slúži. Ešte sme si však neukazovali prácu s týmto nesmierne dôležitým prvkom, co hned aj napravíme. Zmeníme aplikáciu Hi (doplníme jej funkcnost) tak, aby reagovala na niektoré udalosti. Mapu správ sme si už opisovali (bolo to v piatej casti) a dokonca už aj viete pridávat a uberat jednotlivé obslužné funkcie správ. Ale prácnym dopisovaním správ medzi makrá BEGIN_MESSAGE_MAP a END_MESSAGE_MAP by sme sa daleko nedostali. A tu nám Class Wizard výrazne ulahcí prácu. Toto však nie je jediná práca tohto nástroja. Okrem toho, že nám ulahcuje už spomínané obsluhovanie správ, používa sa na pridávanie clenských premenných jednotlivých dialógov a obsluhuje mechanizmy DDV a DDX (dostaneme sa k nim v casti venovanej dialógom), ulahcuje programovanie ActiveX komponentov a pridávanie nových tried. Bez prehánania môžeme povedat, že práve tento nástroj „drží“ aplikáciu pohromade. Jeho základný „look“ (teda vzhlad po prvom spustení) si môžete pozriet na obr. 1. Spustíte ho bud klávesovou skratkou CTRL+W, alebo z menu View. Celý Class Wizard sa skladá z jedného okna (je to vlastne dialóg), na ktorom je pät záložiek. Prvá, ktorá má názov Message Maps, slúži na spravovanie mapy správ v aplikáciách. Ak sa rozhodnete pridat nejakú správu, najprv vyberte zo zoznamu Project meno projektu, pre ktorý správu pridávate (je to z toho dôvodu, že v jednom Workspace – pracovnom prostredí – môže byt uložených viacero projektov). Potom zo zoznamu Class name vyberte triedu, pre ktorú chcete správu pridat (toto je velmi dôležité). Niektoré správy sú pre jednotlivé triedy spolocné (tú istú správu môže obsahovat napríklad trieda dokumentu aj trieda pohladu) alebo líšia sa len málo, a tak si treba dávat pozor, aby ste napríklad správu, ktorá je urcená pre pohlad, nepridávali dokumentu. Môžete tak rozbit celú aplikáciu a potom sa budete cudovat, preco program nepracuje

Page 25: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

tak, ako chcete. Platí to hlavne vtedy, ak pridávate obslužné správy napríklad položkám menu. Ak máte už vybratú triedu, potom v dalšom zozname Object IDs volíte, pre aký objekt chcete pridat obsluhu danej správy. Hned prvá položka v tomto zozname je názov triedy pohladu. Ak ho zvolíte, potom sa v dalšom zozname Messages zobrazia správy, aké môže táto trieda obsluhovat. V zozname Object IDs nájdete napríklad aj názvy jednotlivých položiek menu: ID_APP_ABOUT charakterizuje vybratie položky About... z menu aplikácie. Ak vyberiete nejakú takúto položku, potom sa v zozname Messages zobrazia len dve položky: COMMAND a UPDATE_COMMAND_UI. Nebudeme sa tým však teraz zdržiavat, k obsluhe ovládacích prvkov a menu sa ešte dostaneme. Ak ste si už vybrali ID objektu, pre ktorý chcete pridat obsluhu správy, potom v zozname Messages vyberte názov správy. Tým sa sprístupní tlacidlo Add Function. Kliknite nan. Ak pridávate funkciu na správu nejakého ovládacieho prvku alebo menu, objaví sa dialóg, v ktorom môžete menit názov obslužnej funkcie tejto správy (odporúcam ponechat implicitný). Ak volíte funkciu na obsluhu správy, ktorá je dostupná v danej triede (napr. v triede CHiView funkcia OnLButtonDown), túto možnost nemáte a Class Wizard priradí danej funkcii štandardný názov (tieto názvy funkcií ste mohli vidiet v tabulke 2 v piatej casti seriálu). Potom sa v zozname Member functions zobrazí názov tejto obslužnej funkcie, ako aj názov správy, ktorú obsluhuje. Teraz už stací len kliknút na tlacidlo Edit Code a pridat potrebný kód do obslužnej funkcie. Ak vás zaujíma, co sú to za funkcie, ktoré sú nad správami v zozname Messages (správu chápeme, ked sa zacína prefixom WM), tak to sú virtuálne funkcie, ktoré je niekedy potrebné pretažit (override). (Niekedy, ako napr. pri funkcii OnDraw, je to nevyhnutné. Preto už pri prvom spustení Class Wizardu môžete vidiet v zozname Member functions nejaké funkcie, aj ked ste vy ešte žiadne nepridali, urobil to za vás aplikacný systém.)

Obr. 1 Class Wizard PRÍKLAD. Ak ste tomu neporozumeli, nasledujúci príklad vám v tom pomôže. Zmeníme aplikáciu Hi tak, aby robila to isté, co aplikácia First z piatej casti, ale jednotlivé správy a ich obslužné funkcie budeme pridávat pomocou Class Wizardu. Budeme teda používat tú aplikáciu Hi, ktorá je riešením domácej úlohy z predošlej casti (pozri predošlý text). Zatial máme na obrazovke modrý kruh a v lavom hornom rohu nápis: Hello, world! Teraz chceme, aby program obsluhoval stlacenie lavého tlacidla myši, a síce tak, že po jeho stlacení sa do klientskeho okna aplikácie zobrazí správa „Tu som“ presne na tom istom mieste, kde sa nachádza kurzor. Postup si opíšeme v krokoch.

Page 26: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

1. Ak už máte otvorený projekt Hi, spustte Class Wizard, ak ho nemáte otvorený, otvorte ho cez File-Open a spustte Class Wizard.

2. Pridajte obslužnú funkciu správe WM_LBUTTONDOWN triede CHiView. V zozname Class Name zvolte CHiView, v zozname Object IDs vyberte CHiView a v zozname Messages vyberte WM_LBUTTONDOWN. Kliknite na Add Function. Po týchto operáciách by malo okno Class Wizardu vyzerat, ako ho vidíte na obr. 2. Pod zoznamom Member functions môžete vidiet nápis Description:, kde sa vám zobrazí strucný opis pridávanej funkcie na obsluhu danej správy (vlastne je to opis tej správy).

3. Teraz kliknite na tlacidlo Edit Code a Class Wizard otvorí okno editora a nastaví vás na clenskú funkciu OnLButtonDown triedy CHiView. Kód tejto funkcie teraz vyzerá takto:

void CHiView::OnLButtonDown(UINT nFlags, CPoint point) {

// TODO: Add your message handler code here and/or call default

CView::OnLButtonDown(nFlags, point); } Ako vidíte, opät nás aplikacný systém upozornuje, že tu môžeme pridávat náš vlastný kód, notoricky známym komentárom //TODO. Takto vygenerovaná funkcia OnLButtonDown však ešte nic nerobí, preto ju zmente takto: void CHiView::OnLButtonDown(UINT nFlags, CPoint point) { CClientDC dc(this); dc.TextOut(point.x, point.y, "Tu som"); } Príklad skompilujte, zlinkujte a spustte. Robí presne to, co program First z piatej casti seriálu.

CO VLASTNE UROBIL CLASS WIZARD? Asi sa teraz pýtate, co také prevratné vlastne ten Class Wizard urobil? Bolo by dobré spomenút si, co ste museli robit, aby ste dosiahli takúto funkcnost s programom First. Museli ste editovat hlavickový súbor first.h a manuálne tam dopísat prototyp obslužnej funkcie. Museli ste editovat aj zdrojový súbor first.cpp, kde ste správu pridali medzi makrá BEGIN_MESSAGE_MAP a END_MESSAGE_MAP. Ked používate Class Wizard, toto nepríjemné kódovanie už nemusíte robit, vykoná tak za vás práve Class Wizard (to je prekvapko, nie?) Aby ste videli, co urobil, otvorte hlavickový súbor triedy CHiView, uvidíte tam tento kód:

Page 27: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Obr. 2 Okno Class Wizardu po pridaní obsluhy správy WM_LBUTTONDOWN // Generated message map functions protected: //{{AFX_MSG//{{AFX_MSG(CHiView) afx_msg void OnLButtonDown(UINT nFlags, CPoint point); //}}AFX_MSG DECLARE_MESSAGE_MAP() Class Wizard pridal prototyp funkcie na obsluhu správy WM_LBUTTONDOWN, medzi pre neho charakteristické komentáre //{{AFX_MSG(CHiView) a //{{AFX_MSG. Rovnako aj v zdrojovom súbore triedy CHiView môžete vidiet kód pridaný Class Wizardom: BEGIN_MESSAGE_MAP(CHiView, CView) //{{AFX_MSG_MAP(CHiView) ON_WM_LBUTTONDOWN() //}}AFX_MSG_MAP // Standard printing commands ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview) END_MESSAGE_MAP() Tu je rozpísaná celá mapa správ aplikácie Hi. A opät text umiestnený medzi komentáre //{{AFX_MSG_MAP(CHiView) a //}}AFX_MSG_MAP je pridaný Class Wizardom. Tak teraz už viete, co tajomné urobil Class Wizard. Ešte jedna poznámka. Dávajte si velký pozor a dobre si rozmyslite, ci budete niekedy editovat kód, ktorý vytvoril Class Wizard. Takmer s istotou totiž nieco pokazíte a tým celú aplikáciu rozbijete. Na editáciu kódu vytvoreného Class Wizardom je vždy dobrý len on sám. Ak teda niekedy chcete vymazat obsluhu nejakej správy a následne aj funkciu, ktorú ste pridali pomocou Class Wizardu, použite napríklad tlacidlo Delete Function, ktoré je dostupné na jeho okne pod záložkou Message Maps, a nepokúšajte sa manuálne odstránit túto funkciu z hlavickového súboru a následne aj správu prislúchajúcu tejto funkcii z mapy správ!!! Comu sa však nevyhnete a na co vás upozorní aj sám Class Wizard pri mazaní niektorej správy a jej obslužnej funkcie, je odstránenie tela

Page 28: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

tejto funkcie. Keby ste teraz napríklad chceli kompletne odstránit reakciu na stlacenie lavého tlacidla myši z programu Hi, spustili by ste Class Wizard, v zozname Member functions by ste vybrali funkciu OnLButtonDown a klikli na tlacidlo Delete Function, zobrazil by sa dialóg ako na obr. 3, ktorý vás upozornuje, že treba manuálne vymazat „implementacný“ kód funkcie CHiView::OnLButtonDown (teda ten, ktorý je umiestený v zdrojovom *.cpp súbore triedy CHiView, jednoducho povedané, jej telo). Ak zvolíte pokracovat, Class Wizard vymaže z hlavickového a zdrojového súboru kód, ktorý tam pridal (pozri vyššie), ale ponechá tam celú funkciu OnLButtonDown. Túto funkciu musíte sami vymazat a až teraz je obsluha správy WM_LBUTTONDOWN odstránená z programu Hi.

Obr 3. Použitie makra TRACE na sledovanie súradníc kurzora MAKRÁ TRACE. Aby sme však trochu rozšírili aj vaše vedomosti o ladení aplikácie, ukážeme si aj použitie makra TRACE. Ak ho chcete používat, musíte mat zapnuté sledovanie (tracing). Je zapnuté implicitne už pri inštalácii Visual C++. Ak ho však máte vypnuté, pomocou utility TRACER ho jednoducho zapnete (Enable Tracing). Na co je to dobré? Ak spustíte aplikáciu cez debugger (ciže nie klávesovou skratkou CTRL+F5, ale len klávesom F5), pomocou makra TRACE budete môct sledovat urcité výstupy programu. Ako príklad si znova uvedieme zlepšenie aplikácie Hi.

Poznámka: Celý projekt musí byt v tzv. Debug móde. Ak je nieco ešte stále vo vý voji (to znamená, že ešte meníte kód), je dobré, ak sa celý projekt nachádza v móde Debug. Okrem iného môžete používat práve tieto „sledovacie“ makrá TRACE a ladenie zdrojového kódu. Tento „režim“ zmeníte cez menu: Build -> Set Active Configuration... V budúcnosti sa ešte k problému nastavenia projektu vrátime. POUŽITIE MAKRA TRACE. V terajšej verzii aplikácie Hi, ak klikneme lavým tlacidlom myši do jej okna, zobrazí sa na súradniciach kurzora text: Tu som. My sa teraz pokúsime zistit hodnoty týchto súradníc a tieto hodnoty nebudeme vypisovat do okna aplikácie, ale do výstupného okna (output window), ktoré je prístupné v záložke Debug vo Visual C++. Makro TRACE má rovnakú syntax ako volanie funkcie printf v klasickom programovaní pod DOS. Odporúcam zopakovat . Ak si ešte pamätáte, súradnice kurzora sú prístupné ako point.x a point.y. Dopíšte teda nasledujúci riadok do clenskej funkcie OnLButtonDown, triedy CHiView: TRACE("Poloha kurzora je: %d %d\n", point.x, point.y);

Teraz spustte aplikáciu Hi z prostredia Visual C++ klávesom F5 a párkrát kliknite do jej okna (klientskeho), aby sa zobrazil text Tu som. Ak kliknete na záložku Debug (na spodnej casti obrazovky, pozri obr. 4), mali by ste dostat podobný výstup. TIPY NA WWW. V tipoch na WWW nájdete linky na dva freewarové produkty, pomocou ktorých je možné vyvíjat aplikácie s jazykmi C/C++. A nezabudol som ani na tvorcov hier. http://www.maxcode.com/ – zdrojové kódy a linky na jazyky C/C++/VisualC++, Pascal, QuickBasic... http://www.gamedev.net/gamedev.asp – absolútne vynikajúca stránka, ak vás zaujíma tvorenie hier. Nájdete tam tutorialy na tvorenie pod DirectX, OpenGL, programovanie „fyziky“ v hrách... http://www.bloodshed.net/ – stránka, na ktorej nájdete vynikajúci freeware na tvorenie Windows aplikácií. Má vlastné IDE a z tejto stránky sa k nemu dajú stiahnut rôzne prídavné knižnice, helpy... http://www.cs.virginia.edu/~lcc-win32 – stránka, na ktorú ma upozornil jeden citatel. Obsahuje ANSI-C kompilátor (vraj zatial nepodporuje C++). http://www.vcworld.f2s.com/ – stránka venovaná Visual C++. Zdrojaky a tutorialy. TIPY NA DOMA.

1. Preskúmajte Class Wizard a pomocou neho pridajte do aplikácie Hi obsluhu pravého tlacidla myši, aby sa po jeho stlacení na mieste, kde je kurzor, zobrazil text: Tu nie som.

2. Vrátte aplikáciu Hi do pôvodného stavu (t. j. odstránte obsluhu správ WM_RBUTTONDOWN a WM_LBUTTONDOWN).

3. Vymyslite si vlastné zadania, a nech ich je co najviac! ? V helpe si vyhladajte informácie o makre TRACE a vymyslite si iné zadanie/aplikáciu, ktorej výstupy by ste mohli sledovat pomocou tohto makra.

Page 29: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

NABUDÚCE. V dalšej casti na chvílu opustíme „praktický“ výklad a zájdeme viac do teórie. Opíšeme si totiž dost zložitú, ale dôležitú tému: mapovacie režimy. Do nášho programu Hi pridáme napríklad rolovanie (áno, to sú tie rolovacie „pásky“ na pravom a dolnom konci okna skoro každej aplikácie). Riešenie domácich úloh z tejto casti by ste už teraz mali pravdepodobne nájst na stránke PC REVUE. Stretneme sa teda v marci! Dovidenia. 8. cast: Mapovacie režimy a rolovanie Zacal sa nám marec – mesiac kníh. Ako sa tak pozerám na uponáhlaný vývoj, rôzne „future“ vízie a predpovede, dúfam, že marec ostane mesiacom kníh aj nadalej a že sa nezmení na mesiac digitálnej fotografie alebo nieco podobné. Teraz si pravdepodobne myslíte, že som clovek, ktorý nemá rád nové veci a najradšej by sa zavrel v pivnici so starožitnostami a spomínal na staré dobré casy. Naopak. Nesmierne sa teším novým objavom (nemusia byt len z oblasti IT) a velmi by som sa chcel pozriet tak o sto až dvesto rokov do budúcnosti, ako bude vyzerat náš svet. To, žial, nie je v mojej moci. Preco potom chcem byt taký staromódny a zachovávat veci, ako sú knihy? Asi preto, že knihy boli, sú a budú súcastou nášho života, niecím, co po dlhé roky tvorilo obraz o svete, v ktorom žijeme. Dnes síce ich význam ustupuje pod tlakom väcších a silnejších informacných médií, ale ako by sme dopadli, keby sme len neustále inovovali, nahrádzali staré veci novými? Hoci je to logicky („pocítacovo“) správne, nemali by sme to robit a vo svojom vnútri by sme mali stále ostat ludmi a nemenit sa na pocítace. Potom radšej menme pocítace na svoj obraz a nedovolme pocítacom menit nás ludí na ich obraz!

Po krátkom zamyslení sa podme pozriet, co nás caká v štúdiu programovania pod Windows. V predošlej casti som vám slúbil, že sa bližšie pozrieme na techniku zobrazovania údajov v okne, inak povedané, na mapovacie režimy. Svoj slub hodlám aj dodržat s tou výnimkou, že si opíšeme len základnú prácu s nimi a nebudeme zbytocne zachádzat do podrobností. Vzhladom na vaše doterajšie vedomosti by to nemalo velký význam, a ako som písal v jednej z predchádzajúcich castí, v seriáli sa chcem venovat viac praktickým problémom. K mapovacím režimom sa podrobnejšie vrátime, až ked budeme preberat tlac.

Samozrejme, ako sa stalo už pravidlom, na konci tejto casti nájdete zoznam niekolkých stránok, ktoré sa venujú programovaniu vo Windows. Pridal som aj zoznam diskusných skupín zaoberajúcich sa touto problematikou. Dúfam, že sa vám do nich podarí zapojit. MAPOVACIE REŽIMY (MAPPING MODES). V našich doterajších aplikáciách sme používali na kreslenie na obrazovku pixely zobrazovacieho zariadenia. Presnú definíciu pixela ponechám na nejaký seriál o grafike, nám bude stacit vediet, že pixel je jeden bod na obrazovke. Teda napríklad taký priemerný štvorec sa môže skladat povedzme zo 100 × 100 pixelov (bodiek). Tieto pixely zobrazovacieho zariadenia sa inak nazývajú aj súradnice zariadenia (device coordinates). Kresliacimi jednotkami všetkých doterajších aplikácií uvedených v tomto seriáli boli práve tieto pixely, pretože kontextu zariadenia bol priradený implicitný (kedže sme my nepriradili iný) mapovací režim MM_TEXT, ktorý urcuje práve použitie pixelov ako jednotiek na kreslenie. Keby sme napríklad chceli nakreslit štvorec s rozmermi 100 × 100 pixelov, s horným lavým rohom v lavom hornom rohu používatelskej oblasti okna, použili by sme nasledujúci príkaz: pDC->Rectangle(CRect(0, 0, 100, 100)); Nieco podobné môžete nájst v aplikácii Hi, pozrite si siedmu cast a riešenie domácej úlohy. Tam na kreslenie používame príkaz: pDC->Ellipse(rectMax); Ten nakreslí elipsu s rozmermi klientskeho okna, pretože objekt rectMax sme predtým naplnili rozmermi tejto oblasti okna: GetClientRect(rectMax);

Nakreslíme teda kružnicu vo velkosti klientskeho okna, ale kresliacimi jednotkami budú stále pixely. Asi teraz dobre nechápete, naco vlastne sú tie mapovacie režimy. Pixely majú totiž jednu dost podstatnú nevýhodu. Keby sme si zobrali napríklad vytvorený štvorec: pDC->Rectangle(CRect(0, 0, 100, 100)), rozmery tohto štvorca by celkom logicky boli rozdielne pri rôznych rozlíšeniach. Pri rozlíšení 1024 × 768 by tento štvorec bol menší ako pri rozlíšení 800 × 600 pixelov a ten menší ako pri

Page 30: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

rozlíšení 640 × 480 pixelov. Štvorec by mal stále rozmery 100 × 100 pixelov, ale zdal by sa pri rôznych rozlíšeniach menší alebo väcší. No fajn, ale co ked potrebujeme, aby sa rozmery zachovávali pri všetkých rozlíšeniach? Tu si už, žial, nevystacíme len s použitím pixelov ako súradníc zariadenia, budeme musiet použit tzv. logické súradnice (logical coordinates). Tie priradíme do kontextu zariadenia práve formou mapovacieho režimu (ciže my namapujeme súradnice na dané kresliace jednotky). V tab. 1 môžete vidiet jednotlivé mapovacie režimy a ich kresliace jednotky. Ak teda budeme chciet nakreslit štvorec s rozmermi 5 × 5 cm, použijeme napríklad mapovací režim MM_LOMETRIC a štvorec nakreslíme príkazom: pDC->Rectangle(CRect(0, 0, 500, -500));

Teraz bude mat náš štvorec rozmery 5 × 5 cm bez ohladu na práve nastavené rozlíšenie. Tu však prichádzame k menšiemu problému. Logické súradnice totiž nemôžete používat stále. Nieco pracuje lepšie len s logickými súradnicami, naopak, nieco pracuje lepšie (alebo dokonca len) so súradnicami zariadenia. Vy programátori to musíte zistit a vhodne prepnút medzi logickými súradnicami a súradnicami zariadenia. Opíšme si teraz bližšie, aké mapovacie režimy Windows ponúka.

Mapovací režim MM_TEXT: Nemyslite si, že ked mapovací režim MM_TEXT používa ako svoje kresliace jednotky pixely, je jeho použitie rovnaké, ako keby sme používali súradnice zariadenia (i ked, pravdu povediac, nie ste daleko od pravdy). Pri jeho použití sa hodnota x zväcšuje smerom doprava a hodnota y sa zväcšuje smerom dole. Tento mapovací režim vám dovoluje ešte aj menit súradnice zaciatku. Pomocou funkcií SetViewportOrg a SetWindowOrg môžete napríklad posunút zaciatok súradníc z bodu 0, 0 do bodu 100, 100. Patrí to k vyššej matematike použitia mapovacích režimov, preto vás v tomto bode odkazujem na help k Visual C++.

Mapovacie režimy s pevnou mierkou: Ich anglický ekvivalent je fixed-scale mapping modes. Sú to tie, ktoré môžete vidiet v tab. 1. Pre všetky platí, že hodnota x sa zväcšuje smerom doprava a hodnota y sa zmenšuje smerom dole. Ktorý z nich použijete, záleží len na vás. Líšia sa len v použití iných logických (kresliacich) jednotiek (majú iné mierky). V našich zemepisných šírkach sa budú asi najviac využívat tie, ktoré majú logickú jednotku milimeter. Alebo sa vám nebodaj chce prepocítavat na palce?

Mapovacie režimy s premenlivou mierkou: Inak variable-scale mapping modes. Sú to také mapovacie režimy, ktoré okrem toho, že dovolujú nastavit mierku inú ako pixel (tým sa líšia od MM_TEXT), umožnujú aj posun zaciatku súradníc. Okrem toho umožnujú prispôsobovat obrázok velkosti okna alebo preklopit obrázok okolo jednej z osí. Poznáme dva: MM_ISOTROPIC a MM_ANISOTROPIC. Ako sme už uviedli, ich použitie je nateraz zbytocne zložité, zatial si vystacíme s mapovacími režimami, ktoré používajú pevnú mierku. Prípadným záujemcom odporúcam help.

Mapovací režim Logická jednotka MM_LOMETRIC 0,1 mm MM_HIMETRIC 0,01 mm MM_LOENGLISH 0,01 palca MM_HIENGLISH 0,001 palca MM_TWIPS 1/1440 palca Tab. 1 Mapovacie režimy s pevnou mierkou a ich logické jednotky PREVOD SÚRADNÍC. Teraz prichádza na rad ten problém, ktorý sme už spomínali. Ak totiž raz nastavíte mapovací režim, môžete použit jeho logické súradnice takmer pre všetky funkcie triedy CDC. No napríklad funkcie triedy CRect pracujú dobre iba so súradnicami zariadenia. Vy teda musíte vediet, akú clenskú funkciu ktorej triedy používate, a podla toho prepínat medzi logickými súradnicami a súradnicami zariadenia. Pri tom by vám mohli pomôct nasledujúce všeobecne platné pravidlá:

1. Pre clenské funkcie triedy CDC používajte logické súradnice. 2. Pre clenské funkcie triedy CWnd používajte súradnice zariadenia. 3. Ak chcete zistit, aká je pozícia myši pri pohybe v okne alebo pri kliknutí, používajte súradnice

zariadenia. 4. Ak chcete zachovat nejakú súradnicu v premennej na neskoršie použitie, používajte logické

súradnice. 5. Ak chcete zistit, ci sa kurzor nachádza v nejakej oblasti, používajte súradnice zariadenia.

Page 31: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Prevod súradníc z logických na súradnice zariadenia zabezpecuje funkcia LPtoDP a naopak (zo súradníc zariadenia na logické) funkcia DPtoLP. Obidve sú clenskými funkciami triedy CDC. Ich použitie si ukážeme v príklade 2. PRÍKLAD 1 – POUŽITIE MAPOVACÍCH REŽIMOV. Na príklade si teraz ukážeme použitie mapovacích režimov. Pomocou App Wizardu vytvorte novú aplikáciu s názvom Mapper. Postup nájdete v šiestej casti. Všetky nastavenia okrem tých, ktoré vidíte na obrázkoch, nechajte default. 1. krok: V prostredí Visual C++ vyberte z menu File -> New. Dialóg, ktorý sa vám zobrazí, vyplnte podla obr. 1 (kliknite na MFC AppWizard (exe) a v kolónke Project name zadajte Mapper). 2. krok: Po potvrdení dialógu New sa zobrazí už známy prvý krok tvorenia aplikácie pomocou App Wizardu. Zvolte Single document, tak ako vidíte na obr. 2. 3. krok: Ostatné nastavenia nechajte na prednastavených hodnotách. To znamená, že klikajte na Next, až sa vám zobrazí posledný dialóg App Wizardu, potom kliknite na Finish alebo rovno v predchádzajúcom kroku, kde ste volili typ aplikácie, kliknite na Finish.

Obr 1 Zadávanie mena nového projektu

Obr 2 Vyberanie typu aplikácie

Máme teda vytvorenú základnú, ale funkcnú kostru aplikácie Mapper. Najprv na demonštráciu toho, coho sú schopné mapovacie režimy, pridajte nasledujúci kód. Zmente telo funkcie OnDraw, ako vidíte na nasledujúcom výpise: void CMapperView::OnDraw(CDC* pDC) { pDC->SelectStockObject(GRAY_BRUSH); pDC->Ellipse(0, 0, 200, 200); }

Page 32: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Aplikáciu skompilujte a spustte. Výstupom je sivá kružnica. Ako sme si už vysvetlovali, implicitný mapovací režim je nastavený na MM_TEXT, takže na obrazovke máme kružnicu s rozmermi (priemermi) 200 × 200 pixelov (pDC->Ellipse(0, 0, 200, 200);). Skúste teraz len tak pre zaujímavost vybrat z menu File tejto aplikácie Print Preview. Uvidíte, že keby ste túto kružnicu, tak ako je, vytlacili, na papieri by vyzerala o dost menšia ako na obrazovke. Je to práve použitím pixelov ako kresliacich jednotiek. Na tlac sa totiž používa iné rozlíšenie ako pri zobrazovaní na obrazovku. Ak chceme, aby velkost kružnice bola stála pre všetky rozlíšenia, musíme použit iný mapovací režim. Co tak napríklad MM_LOMETRIC? Mapovací režim je najlepšie nastavovat vo virtuálnej funkcii triedy Cview, a síce OnPrepareDC. Dá sa to aj inde, ale táto funkcie je na to priamo urcená. Pomocou Class Wizardu pridajte do triedy CMapperView virtuálnu funkciu OnPrepareDC a do jej tela pripíšte tento kód: void CMapperView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo) { pDC->SetMapMode(MM_LOMETRIC); }

Aby sa vám vôbec nieco zobrazilo, musíte pozmenit kód na vytváranie kružnice vo funkcii OnDraw. Súradnice y sa totiž pri použití tohto mapovacieho režimu zmenšujú smerom nadol, takže kód na nakreslenie kružnice musí vyzerat takto: pDC->Ellipse(0, 0, 200, -200);

Teraz po spustení programu sa zobrazí kružnica, ktorá bude pri všetkých rozlíšeniach rovnako velká (vždy bude mat priemer 2 cm). Skúste si aj iné rozmery kružnice, prípadne iné mapovacie režimy a posúdte sami. PRÍKLAD 2 – PREVOD SÚRADNÍC. Aby ste lepšie pochopili, na co je dobrý prevod súradníc, trošku zdokonalíme príklad 1. Skúsme zistovat polohu myši pri stlacení jej lavého tlacidla a na základe toho urcit, ci sa nachádza vnútri nakreslenej elipsy. Ako na to? V prvom rade treba pomocou Class Wizardu „namapovat“ obsluhu správy WM_LBUTTONDOWN pre triedu CMapperView. Potom pridajte nasledujúci kód do tela tejto funkcie: void CMapperView::OnLButtonDown(UINT nFlags, CPoint point) { CClientDC dc(this); OnPrepareDC(&dc); CRect rect(0, 0, 200, -200); dc.LPtoDP(rect); if (rect.PtInRect(point)) { TRACE("Poloha kurzora je:%d %d\n", point.x, point.y); } }

Princíp tohto kódu je taký, že ak sa kurzor myši pri stlacení lavého tlacidla myši nachádza vnútri elipsy (podmienka if (rect.PtInRect(point)) ), zobrazíme pomocou makra TRACE jeho súradnice. Ak klikneme vedla, nic sa nestane. Už sme spomenuli, že súradnice polohy kurzora, ako aj súradnice nejakej oblasti (v tomto prípade je to oblast CRect rect(0, 0, 200, -200) ) by mali byt v súradniciach zariadenia, a nie v logických súradniciach. Ak teraz okomentujete riadok dc.LPtoDP (ciže logické súradnice oblasti, do ktorej klikáme, sa neprevedú na súradnice zariadenia), makro TRACE už nevypíše polohu kurzora, aj ked kliknete do vnútra elipsy. ROLOVANIE. Myslím si, že by bolo zbytocné vysvetlovat, co vlastne rolovanie je. Pozrime sa skôr, ako sa dá používat v našich programoch. Základná trieda pre všetky pohlady je trieda CView. Táto trieda však sama osebe rolovanie nepodporuje. Aby sme ho mohli využívat, musíme na to použit triedu CScrollView, ktorá je potomkom triedy CView a rolovanie už podporuje, hoci len ciastocne. To ciastocne znamená, že vie obsluhovat kliknutie myšou na posúvaciu pásku (posuvník, scroll bar), ale nevie obslúžit rolovanie pomocou klávesnice. Naštastie tento „nedostatok“ sa dá lahko doprogramovat (ukážeme si to v príklade 3). AKO TO PRACUJE? Rolovanie pracuje skutocne na jednoduchom princípe a väcšinu práce za nás urobí aplikacný systém. Clenské funkcie triedy CScrollView spracúvajú správy WM_HSCROLL

Page 33: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

a WM_VSCROLL a na základe prijatia jednej z týchto správ vykonajú bud horizontálny, alebo vertikálny posun informácií v okne. PRÍKLAD 3 – ROLOVANIE. Opät sa pustíme do tvorenia novej aplikácie pomocou App Wizarda. Takže z menu File vyberte New, kliknite na záložku Projects, potom na MFC AppWizard (exe) a do názvu projektu zadajte Roller. Kliknite na OK. V dialógu, ktorý sa vám zobrazí, vyberte Single document a kliknite na Next. Na všetkých dalších dialógoch App Wizardu nechajte prednastavené hodnoty, až sa dostanete na posledný (šiesty) dialóg App Wizardu. Tam vyberte triedu CRollerView a v zozname Base Class vyberte triedu CScrollView (presne ako na obr. 3). Po vybratí kliknite na Finish a App Wizard vám vygeneruje novú aplikáciu.

Obr. 3 Vybratie podpory rolovania pre aplikáciu Aby sme videli, že vôbec nieco roluje, použijeme opät kružnicu ako v príklade 1. Upravte preto funkciu OnDraw presne tak, ako ste ju upravovali v príklade 1. Na nastavenie posuvníkov sa používa funkcia SetScrollSizes (clenská funkcia triedy CScrollView). Otázne je, kde toto nastavenie uskutocnit. Bolo by dobré použit takú funkciu, ktorá sa volá ako prvá po úplnom vytvorení okna pohladu, teda ešte pred prvým zavolaním funkcie OnDraw. A takouto funkciou nie je OnPaint ani OnPrepareDC, ale funkcia OnInitialUpdate. Táto funkcia je už implicitne do triedy pohladu pridaná aplikacným systémom, preto len pozmente jej telo takto: void CRollerView::OnInitialUpdate() { CScrollView::OnInitialUpdate(); // nastavi logicke okno 20 × 30 cm CSize sizeTotal(2000, 3000); // urcuje, o kolko sa odroluje okno, ak kliknete do vnutra posuvacej pasky CSize sizePage(sizeTotal.cx/2, sizeTotal.cy/2); // urcuje, o kolko sa odroluje okno, ak kliknete na rolovaciu sipku CSize sizeLine(sizeTotal.cx/50, sizeTotal.cy/50); // podla ziskanych informacii nastavi rolovacie pasky SetScrollSizes(MM_LOMETRIC, sizeTotal, sizePage, sizeLine); }

Všimnite si, že na urcovanie rozmerov sme použili objekt triedy CSize (podrobne pozri help). A ešte jedna malá poznámocka. V príklade Roller neprepisujeme funkciu OnPrepareDC, aj ked používame mapovací režim MM_LOMETRIC. Preco? Pretože ak používame triedu CScrollView, tá nastavuje mapovací režim podla prvého parametra funkcie SetScrollSizes , a preto by ešte jedno nastavovanie vo funkcii OnPreprareDC nemalo zmysel. Skúste teraz príklad preložit a spustit a vyskúšajte jeho funkcnost. Že sa vôbec posuvníky zobrazia, je dané tým, že sme ako rozmer logického okna uviedli 20 × 30 cm, co je viac, ako je možné zobrazit na klasickom monitore (skúste zvolit tento rozmer menší a uvidíte sami). No ak v tejto verzii príkladu

Page 34: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Roller chcete rolovat aj pomocou klávesnice, nebude to fungovat. Aby sme to mohli opravit, treba ešte povedat, ako aplikácia založená na knižnici MFC dokáže obslúžit vstup z klávesnice. Ak stlacíte nejaký kláves, Windows pošle tomu oknu správy WM_KEYDOWN a WM_KEYUP aj s kódmi virtuálnych klávesov (napríklad pre kláves HOME je to VK_HOME, pre šípku doprava VK_RIGHT atd., VK – virtual key; kompletný zoznam nájdete v helpe). Ešte sa skontroluje prípadná prítomnost klávesu SHIFT (ak je zistená, potom sa napr. písmeno transformuje na velké) a pošle sa správa WM_CHAR s kódom pre velké alebo malé písmeno. Class Wizard vám dovoluje mapovat všetky tieto správy (t. j. WM_CHAR, WM_KEYUP, WM_KEYDOWN). Na vás zostáva, akú si zvolíte. Ak ocakávate stlacenie niektorého klávesu s písmenom, použite WM_CHAR, ak to môžu byt aj iné klávesy, tak vám odporúcam WM_KEYDOWN. Tým je náš problém vyriešený. Pomocou Class Wizardu pridajte obsluhu správy WM_KEYDOWN a zmente jej telo takto: void CRollerView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { switch (nChar) { case VK_HOME: OnVScroll(SB_TOP, 0, NULL); OnHScroll(SB_LEFT, 0, NULL); break; case VK_END: OnVScroll(SB_BOTTOM, 0, NULL); OnHScroll(SB_RIGHT, 0, NULL); break; case VK_UP: OnVScroll(SB_LINEUP, 0, NULL); break; case VK_DOWN: OnVScroll(SB_LINEDOWN, 0, NULL); break; case VK_LEFT: OnHScroll(SB_LINELEFT, 0, NULL); break; case VK_RIGHT: OnHScroll(SB_LINERIGHT, 0, NULL); break; default: break; } }

Teraz už môžete rolovat pomocou klávesov HOME, END, šípky nalavo, napravo, hore a dole. Myslím si, že tento kód nepotrebuje bližší komentár, azda si len všimnite, ako sa po stlacení klávesov HOME a END realizuje posun na zaciatok/koniec okna. TIPY NA WWW. www.games-developer.com – zaujímavá stránka pre tvorcov hier. Nájdete tu napríklad linky na free enginy na tvorbu 3D hier. ftp.spies.com – ak si myslíte, že programovanie nemôže byt zábavné, skúste túto stránku. Chodte do adresára LIBRARY/HUMOR/CODE Ak sa chcete zapojit do diskusných skupín zaoberajúcich sa programovaním vo Windows, skúste tieto: comp.os.ms-windows.programmer comp.os.ms-windows.programmer.controls comp.os.ms-windows.programmer.graphics comp.os.ms-windows.programmer.multimedia comp.os.ms-windows.programmer.drivers comp.os.ms-windows.programmer.networks comp.os.ms-windows.programmer.memory comp.os.ms-windows.programmer.misc comp.os.ms-windows.programmer.ole comp.os.ms-windows.programmer.tools comp.os.ms-windows.programmer.win32

Page 35: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Slovo za poslednou bodkou urcuje, akej témy sa bude diskusia týkat. Myslím, že každý si príde na svoje. TIPY NA DOMA. Naprogramujte jednoduchú hru (nie, to nie je vtip). Do okna nakreslite povedzme 10 kružníc, ktoré budú plnit funkciu tercov. Pri každom stlacení lavého tlacidla myši vyhodnotte pozíciu kurzora, a ak sa nachádza vnútri nejakej kružnice, pripocítajte jeden bod. Informáciu o bodoch môžete zobrazovat napríklad pomocou funkcie TextOut. Dávate si pozor na prepínanie medzi logickými súradnicami a súradnicami zariadenia. Tip: Ak chcete, aby po kliknutí na nejakú kružnicu tá aj zmizla, nevyhnete sa použitiu funkcie InvalidateRect, ktorú budete volat z funkcie OnLButtonDown (pozri. help). Táto funkcia spôsobí odoslanie správy WM_PAINT, cím sa zavolá funkcia OnDraw, kde môžete realizovat prekreslenie. Parametrom tejto funkcie je oblast, ktorú chceme prekreslit. Keby ste chceli v príklade 2 prekreslit kružnicu zakaždým, ked na nu kliknete, zmenili by ste funkciu OnLButtonDown takto: void CMapperView::OnLButtonDown(UINT nFlags, CPoint point) { CClientDC dc(this); OnPrepareDC(&dc); CRect rect(0, 0, 200, -200); dc.LPtoDP(rect); if (rect.PtInRect(point)) { TRACE("Poloha kurzora je:%d %d\n", point.x, point.y); InvalidateRect(rect); } } NABUDÚCE. Nabudúce sa bližšie pozrieme na GDI objekty knižnice MFC a možno zacneme preberat aj velmi rozsiahlu, ale zato zaujímavú tému: dialógy, ktorým budeme venovat minimálne tri casti. Dovidenia v apríli. 9. cast: Dialógy I Zacal sa nám mesiac apríl, alebo ako ho mnohí nazývajú, mesiac bláznov. A práve titulom „blázni“ mnohí oznacujú nás – pocítacových nadšencov. Ved preco nie? Každý je svojím spôsobom blázon do niecoho iného. Niekto si kupuje každý mesiac nový mobil, iný zas nové auto (teda ten lepšie zarábajúci ?), niekto sa „vytešuje“ z novej kroniky ludstva, iný z krásnej novej gitary, niekto pozerá Star Trek 325-krát za sebou a takto by sme mohli íst donekonecna. Otázkou však je, preco niektorí ludia uvažujú takto: „Tebe sa páci vážna hudba, mne nie, a preto sa s tebou už nikdy viac nebudem bavit.“ Alebo: „Ako môžeš vidiet krásu v tom, celý den rozoberat rádiá a potom ich znova skladat? Zbohom!“ A co je pritom zaujímavé, ten istý clovek môže vyslovit aj takúto vetu: „Máš pekný oblek, my si urcite budeme rozumiet!“ Nezdá sa vám to trochu „strelené“? Co keby sme tak skúsili posudzovat ludí nie podla toho, ako hovoria a ako sa obliekajú, ale podla toho, co hovoria a ako rozmýšlajú. Ak sa vám tieto slová zdajú povedomé, máte pravdu. Naozaj sú to trošku upravené slová z manifestu istej komunity, ktorá sa zaoberá vecami okolo pocítacov. Ci sú pravdivé a správne, nechám na posúdenie každému z vás.

No vrátme sa radšej k Visual C++. Ako vidiet z nadpisu, zacíname preberat rozsiahlu tému, ktorá je nepochybne jednou z najzaujímavejších pre zaciatocníkov v programovaní vo Visual C++, ale aj v iných prostrediach. Je to totiž jedna z prvých príležitostí, ked môžete vidiet výsledky svojej práce, co vás urcite minimálne motivuje na dalšiu prácu. Na zaciatok tejto casti si však ešte nieco povieme o objektoch GDI a triedach kontextu zariadenia knižnice MFC.

Ako ste si urcite všimli, zmizla cast Tipy na WWW, kedže som vycerpal takmer celú zásobu dobrých linkov. Tipy na stránky sa, samozrejme, budú nadalej vyskytovat, ale budú zaclenené bud do úvodu, alebo záveru. Nová vec, ktorú som pridal, je FAQ. Tento výraz oznacuje (ako iste všetci viete) po anglicky Frequently Asked Questions ciže casto kladené otázky. V tejto casti budeme uverejnovat odpovede na vaše otázky, ktoré došli e-mailom (a je ich už neúrekom) a sú všeobecnejšieho charakteru, takže môžu pomôct aj iným. Je to taká menšia obdoba Programátorskej poradne pre Visual C++. Vaše otázky sa môžu, ale nemusia týkat práve preberanej témy v tej-ktorej casti seriálu (musia sa, samozrejme, týkat programovania v C++). Takže píšte mi a pýtajte sa na všetko, co vám

Page 36: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

nebude jasné, prípadne co neviete vo Visual C++ urobit. Nezabudnite, kto sa nic nepýta, nic sa nedozvie! KONTEXT ZARIADENIA A OBJEKTY GDI V MFC. V seriáli sme už pracovali s kontextom zariadenia a dokonca aj s objektmi GDI. Vo funkcii OnDraw sme odovzdávali ukazovatel na kontext zariadení (pDC), vo funkcii OnLButtonDown sme vytvárali objekt kontextu zariadení (CClientDC dc(this);) a používali sme objekty GDI systémového zásobníka (SelectStockObject(GRAY_BRUSH);).

Kontext zariadenia: Základná trieda pre kontext zariadenia je trieda CDC. Od nej sú potom odvodené dalšie: CClientDC, CWindowDC, CPaintDC... (podrobne pozri help). Rozdiely medzi týmito triedami sú nepatrné, napríklad objekt triedy CClientDC môže pristupovat len ku klientskej casti okna, ale objekt triedy CWindowDC môže pristupovat aj ku klientskej, aj k neklientskej casti okna. Takže je len na vás, ako programátoroch, akú triedu si vyberiete pre vašu aplikáciu. Ked ešte presne neviete, co sa bude od vašej aplikácie požadovat, odporúcam vám najprv používat objekt triedy CDC (ciže tej základnej triedy pre kontext zariadenia) a neskôr, ked si už budete istí, rozhodnite sa pre niektorého potomka triedy CDC. Azda len pripomeniem, že ukazovatel na objekt, ktorý sa odovzdáva ako parameter funkcie OnDraw, je ukazovatelom na objekt triedy CDC.

Objekty GDI: Základná trieda pre všetky objekty GDI (Graphic Device Interface) systému Windows je abstraktná trieda CGdiObject. Každá dalšia trieda nejakého objektu GDI je odvodená od tejto triedy. Opis týchto odvodených tried je v helpe, takže na tomto mieste si ich len vymenujeme: CBitmap, CBrush, CFont, CPallete, CPen, CRgn. S niektorými sme dokonca už aj pracovali (napr. CPen). PRÁCA S OBJEKTMI TRIEDY CDC. Je dôležité vediet pracovat s objektmi kontextu zariadenia. Ked budeme potrebovat napríklad nieco napísat do okna aplikácie (a to sa stáva velmi casto) a potrebujeme tak urobit z funkcie, ktorá nemá ako parameter ukazovatel na objekt triedy kontextu zariadenia, musíme vediet, ako vytvorit a zrušit takýto objekt. Naopak, ak používame kontext zariadení v nejakej funkcii, kde sa ukazovatel na objekt kontextu zariadení odovzdáva ako parameter (napr. funkcia OnDraw), nemusíme sa vôbec starat o konštrukciu a deštrukciu (rušit takýto objekt dokonca ani nesmieme!) tohto objektu, aplikacný systém s MFC urobia túto prácu za nás. Ale ak napríklad vytvárame náš vlastný kontext zariadenia vo funkcii OnLButtonDown (robili sme tak v programoch First a Hi), musíme si tento objekt sami skonštruovat a v istých prípadoch ho musíme aj sami zrušit. Rušenie objektov kontextu zariadenia je nesmierne dôležité, pretože Windows má obmedzený pocet kontextov zariadení, ktoré môže vytvorit a pri ich nedôslednom používaní sa môže lahko stat, že váš program spadne, lebo nemohol vytvorit další kontext zariadenia (a verte mi, takéto chyby sa odhalujú pomerne tažko). Existujú dva spôsoby vytvorenia nového objektu kontextu zariadenia. Prvým je vytvorenie na stacku (tento spôsob bol použitý aj v programoch First a Hi). Má tú výhodu, že sa nemusíme starat o zrušenie takto vytvoreného objektu. Aplikacný systém to urobí za nás pri ukoncení funkcie, v ktorej sme objekt vytvárali (teda napr. OnLButtonDown). Ukážku tohto spôsobu vidíte tu:: void CKontextView::OnLButtonDown(UINT nFlags, CPoint point) { // vytvoríme objekt dc na stacku CClientDC dc(this); dc.TextOut(0, 0, "Tu som"); } // objekt dc je zrušený Parametrom konštruktora objektu dc je ukazovatel na okno (aby bolo systému jasné, ktorého okna sa týka vytvorenie nového objektu kontextu zariadenia). No ak z rôznych prícin nechceme vytvárat objekt na stacku, môžeme ho vytvorit aj pomocou funkcie GetDC, ktorá je clenskou funkciou triedy CWnd (toto je ten druhý spôsob). V tomto prípade získame ukazovatel na kontext zariadenia. Tu je príklad: void CKontextView::OnLButtonDown(UINT nFlags, CPoint point) { // vytvoríme ukazovatel na kontext zariadenia CDC* pDC=GetDC(); pDC->TextOut(0, 0, "Tu som");

Page 37: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

// Dôležité! Musíme uvolnit kontext zariadenia ReleaseDC(pDC); }

Ako sme už uviedli, nesmieme zabudnút takto vytvorený ukazovatel aj sami zrušit pomocou funkcie ReleaseDC.

Takže teraz už viete, ako postupovat pri vytváraní a rušení objektov kontextu zariadenia, a nemal by byt pre vás problém z hocijakej funkcie realizovat výstup na obrazovku. PRÁCA S OBJEKTMI GDI. Každý objekt GDI má iný konštruktor, a preto sa nebudeme bližšie zaoberat opisom každého z nich (tu vás opät odporucím na help). Spomenieme si len, ako sa rušia tieto objekty. Ak cakáte nejakú funkciu podobnú ReleaseDC, ste na omyle (táto funkcia v urcitých prípadoch dokáže odstránit aj objekty GDI – pozri dalej). Pri vytváraní, ale i rušení objektov GDI sa využíva funkcia SelectObject. Pri vytváraní nám táto funkcia vracia ten objekt GDI, ktorý je v nej aktuálne vložený, a zároven vkladá novo vytvorený objekt, no a pri rušení využijeme tento vrátený predchádzajúci objekt, ktorým jednoducho prepíšeme náš nový vložený objekt (ak máte v tom mierny chaos, pozrite si tento príklad): void CKontextView::OnLButtonDown(UINT nFlags, CPoint point) { // vytvoríme nový kontext zariadenia CDC *pDC=GetDC(); // vytvoríme objekt GDI pera // použijeme ho na vykreslenie elipsy CPen newPen(PS_SOLID, 6, RGB(0, 0, 255)); // získame aktuálny objekt GDI (oldPen), ktorý neskôr použijeme na // odstránenie objektu GDI newPen // zároven tento aktuálny objekt GDI (oldPen) nahradíme // novým objektom GDI newPen CPen *oldPen=pDC->SelectObject(&newPen); // vykreslíme elipsu pomocou objektu GDI newPen pDC->Ellipse(0, 0, 400, 200);

// z kontextu zariadenia vyberieme nový objekt GDI newPen // a vložíme tam predchádzajúci objekt GDI oldPen pDC->SelectObject(oldPen); // uvolníme kontext zariadenia ReleaseDC(pDC); }

Ešte pár slov o funkcii ReleaseDC. Táto funkcia, ako už viete, odstráni kontext zariadenia, ktorý jej odovzdáme ako parameter (presnejšie povedané, odovzdávame ukazovatel na tento kontext zariadenia, v tomto prípade je ním pDC). No ak máme k tomuto kontextu zariadenia priradené nejaké objekty GDI, odstránia sa aj tie? Odpoved znie: áno. Ak teda viete a ste si istí, že kontext zariadenia bude zrušený skôr ako objekty GDI k nemu priradené, nemusíte tieto objekty GDI už rušit, stací zrušit kontext zariadenia.

Existuje ešte jeden spôsob, ako zrušit objekty GDI, a to použitím objektov GDI systémového zásobníka. Tento postup sme si už opisovali vo štvrtej casti, takže sa k nemu nebudem vracat (skúste si však na domácu úlohu prerobit funkciu CKontextView::OnLButtonDown tak, aby ste využívali objekt GDI systémového zásobníka). ZACÍNAME DIALÓGMI. Na zaciatok si hádam ujasnime, co to vlastne dialóg je. Dialóg je okno ako každé iné, môže spracúvat správy, môžeme s ním robit operácie ako s každým iným oknom (zavriet, premiestnit, zmenit velkost). V systéme Windows existujú dva druhy dialógov: modálne a nemodálne . Modálne dialógy sú bežnejšie. Tento typ dialógov neumožnuje používatelovi vykonávat dalšiu cinnost v danej aplikácii, pokým ho neuzavrie (príkladom takýchto dialógov sú dialógy Open, Save as, ale aj tzv. informacné dialógy, napr. This program has performed an illegal operation...). Nemodálne dialógy, ako už istotne tušíte, sa líšia od modálnych v tom, že umožnujú dalšiu prácu s aplikáciou (napr. dialóg Find vo Worde). Z hladiska knižnice MFC sa však tieto dva typy dialógov

Page 38: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

nijako zvlášt nelíšia. Obidva majú základnú triedu CDialog, rozdiel je len v ich vytváraní a rušení. Teraz zacneme s tým najjednoduchším, co nám knižnica MFC ponúka, a síce budú to informacné dialógy, inak známe ako Messageboxy. VYTVÁRAME MESSAGEBOX. Použitie tohto typu modálnych dialógov si najlepšie ukážeme na príklade. Chceme, aby sa po stlacení lavého tlacidla myši zobrazil informacný dialóg s nejakým textom a, samozrejme, s ikonkou. Pomocou AppWizardu si vytvorte novú aplikáciu s názvom Information. Typ aplikácie bude rovnaký, aký sme používali doteraz, ciže zvolte Single Document Interface a ostatné parametre nechajte na default. Ak už máte vygenerovanú základnú kostru aplikácie, potom pomocou Class Wizardu pridajte obsluhu správy WM_LBUTTONDOWN do triedy CInformationView. Teraz zmente kód funkcie OnDraw v tejto triede takto: void CInformationView::OnDraw(CDC* pDC) { pDC->TextOut(0, 0, "Stlac lave tlacidlo mysi na zobrazenie informacneho dialogu."); }

Teraz už používatel bude vediet, co s aplikáciou robit. A ako sa vlastne vyvolá zobrazenie informacného dialógu? Velmi jednoducho: stací zavolat funkciu MessageBox, pricom jej parametre sú: MessageBox(„Text v informacnom okne“, „Titulok informacného okna“, Jeho typ);

Tretí parameter Jeho typ môže nadobúdat hodnoty, ako vidíte v tab. 1. Ttreba si uvedomit, že tieto parametre môžeme kombinovat. Oddelujeme ich operátorom |. Všetky nasledujúce volania funkcie MessageBox sú správne: MessageBox("Môj prvý dialóg v prostredí Visual C++", "Hurá!",MB_ABORTRETRYIGNORE |

MB_DEFBUTTON2 | MB_ICONINFORMATION); MessageBox("Môj prvý dialóg v prostredí Visual C++", "Hurá!",MB_ABORTRETRYIGNORE |

MB_SYSTEMMODAL | MB_DEFBUTTON3 | MB_ICONINFORMATION); MessageBox("Môj prvý dialóg v prostredí Visual C++", "Hurá!",MB_YESNO | MB_DEFBUTTON2 |

MB_ICONWARNING);

Jeho typ Ikona Opis MB_OK - zobrazí sa len tlacidlo OK MB_OKCANCEL - zobrazia sa tlacidlá OK a Cancel MB_RETRYCANCEL - zobrazia sa tlacidlá Retry a Cancel MB_YESNO - zobrazia sa tlacidlá Yes a No MB_YESNOCANCEL - zobrazia sa tlacidlá Yes, No a Cancel MB_ABORTRETRYIGNORE - zobrazia sa tlacidlá Abort, Retry a Ignore MB_APPLMODAL - nebudete môct pracovat s aplikáciou, pokým informacné

okno nezavriete; môžete však pracovat s inými aplikáciami

MB_SYSTEMMODAL - nebudete môct pracovat so systémom ani s dalšou aplikáciou, pokým nezavriete informacné okno (informacné okno zostáva stále na obrazovke aj pri práci s inou aplikáciou)

MB_TASKMODAL - podobné ako APPLMODAL, neúcinný v aplikácii, ktorá využíva MFC

Page 39: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

MB_DEFBUTTON1 - urcuje „Default Button“ v dialógu (teda ten, ktorý sa aktivuje po stlacení enter); používa sa pri viactlacidlových informacných oknách (napr. MB_ABORTRETRYIGNORE); nastavuje prvé tlacidlo ako default

MB_DEFBUTTON2 - Nastavuje druhé tlacidlo ako default MB_DEFBUTTON3 - Nastavuje tretie tlacidlo ako default MB_ICONASTERISK alebo MB_ICONINFORMATION

Zobrazí ikonu

MB_ICONEXCLAMATION alebo MB_ICONWARNING

Zobrazí ikonu

MB_ICONQUESTION

Zobrazí ikonu

MB_ICONHAND alebo MB_ICONSTOP alebo MB_ICONERROR

Zobrazí ikonu

Tab. 1 Typy informacných dialógov

Aby sme videli použitie funkcie MessageBox v praxi, dopíšte tento kód do funkcie OnLButtonDown v aplikácii Information. void CInformationView::OnLButtonDown(UINT nFlags, CPoint point) {

MessageBox("Môj prvý dialóg v prostredí Visual C++", "Hurá!", MB_OK | MB_ICONINFORMATION);

}

Teraz sa po stlacení lavého tlacidla myši v okne aplikácie zobrazí informacný dialóg, ako vidíte na obr. 1. Pohrajte sa trochu s týmto jedným riadkom, zmente parametre funkcie podla tabulky, vyskúšajte rôzne kombinácie. Tolko o dialógoch v tejto casti, nabudúce sa pozrieme na klasické modálne dialógy so všetkým, co k nim patrí, vrátane mechanizmu výmeny údajov medzi ovládacími prvkami a premennými v programe a práce s editorom zdrojov.

Obr. 1 Informacný dialóg programu Information FAQ. V tejto „podcasti“ seriálu C++ pod Windows dávam priestor vám citatelom, aby ste kládli otázky o programovaní vo Visual C++, na ktoré sa pokúsim odpovedat. V tejto casti uverejnujem dve otázky, ktoré mi prišli ešte v zaciatkoch seriálu: Q: Nejde mi skompilovat/zlinkovat program Hello z druhej casti seriálu. A: Tu je návod, ako postupovat pri zostavovaní projektu Hello world z druhej casti seriálu: Ak máte spustené Visual C++, z menu File vyberte New. Kliknite na záložku Projects a vyberte Win32 Application. Do polícka Project Name zadajte Hello. Kliknite na OK. Objaví sa vám další dialóg, na ktorom zvolte: An Empty Project a kliknite na Finish. Potom sa vám zobrazia informácie o novom projekte. Opät kliknite na OK. Potom z menu Project vyberte Add Tto Project a potom Files. Nájdite súbor, ktorý obsahuje zdrojový text programu a vyberte Add. Ak ešte nemáte napísaný zdrojový text, z menu Add To Project vyberte New a potom pod záložkou Files vyberte: C++ Source File. Na tom

Page 40: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

istom dialógu do polícka File name vpíšte: hello.c. Je dôležité , aby prípona súboru bola *.c, a nie *.cpp. Potom napíšte zdrojový kód do tohto súboru. Projekt zostavte. Q: Nejde mi skompilovat/zlinkovat program First zo štvrtej casti seriálu, pricom mi hlási nejaký problém s knižnicou nafxcwd.lib. A: Postup pri vytváraní nového projektu je podobný ako pri programe Hello (pozri predchádzajúcu odpoved). Tu však ešte treba zvolit podporu pre knižnicu MFC v danom projekte. Z menu Project vyberte Settings. V zozname Microsoft Fundation Classes vyberte namiesto Not Using MFC: Use MFC in a Shared DLL alebo Use MFC in a Static Library. Projekt nanovo skompilujte a zlinkujte (Rebuild All). TIPY NA DOMA. Väcšinu tipov na doma nájdete v predchádzajúcom texte. Na tomto mieste vám len pripomeniem, aby ste nezabudli experimentovat (napr. s funkciou MessageBox), lebo len samotným cítaním bez programovania sa daleko nedostanete. NABUDÚCE. O mesiac zacneme naplno pracovat s dialógmi, vytvoríme velký dialóg, ktorý bude obsahovat väcšinu štandardných ovládacích prvkov, ktoré sa dajú použit v dialógoch. Vysvetlíme si mechanizmy DDX a DDV, a ak nám ostane priestor, zacneme sa venovat aj spolocným ovládacím prvkom Windows (Windows Common Controls). Dovidenia o mesiac. 10. cast: Dialógy II Po mesiaci sa opät stretávame. Kedže máme dnes toho prebrat neúrekom, podme sa radšej hned venovat naším dialógom. MECHANIZMY DDX A DDV. Ak ste doteraz pracovali s Visual Basicom (VB) alebo C++ Builderom, predávanie dát medzi ovládacími prvkami dialógov a premennými v programe nebol žiaden väcší problém. Jednoducho ste si zadefinovali nejakú premennú a túto premennú ste naplnili dátami pomocou vlastnosti (Property) daného ovládacieho prvku. (napr. vo VB: text=Text1.Text). Vo Visual C++ to takto jednoducho nejde. Visual C++ má zadefinované presné postupy a mechanizmy, ako sa môžu prenášat dáta z/do dialógov. Tieto mechanizmy sa nazývajú DDX (Dialog Data Exchange) a DDV (Dialog Data Validation). Ako iste už tušíte, DDX umožnuje výmenu dát medzi dialógom a premennou (a samozrejme naopak) a DDV overuje, ci sa tieto dáta rovnajú zadaným požiadavkám (t.j. napr. ci nemajú väcší pocet znakov, ci sú z urcitého rozsahu a pod.) a tiež nastavuje tieto rozsahy. Obidva tieto mechanizmy sú úzko spojené s Class Wizardom (v nom napríklad priradujeme ovládacím prvkom premenné) a funkciami UpdateData a DoDataExchange. Ich použite si teraz ukážeme na príklade. NÁŠ PRVÝ MODÁLNY DIALÓG. Vytvorte novú aplikáciu pomocou MFC AppWizardu. (Tak ako doteraz, zvolte SDI a ostatné nechajte na default). Nazvime si ju jednoducho Vymena. (Pre istotu si ešte pozrite informáciu o novom projekte, ktorá je na obr. 1 a porovnajte si ju so svojou)

Page 41: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Obr 1. Informácie o projekte Vymena

Tak, teraz ked už máme vygenerovanú kostru novej aplikácie, podme vytvorit nový zdroj dialógu. Ako nato? Kliknite na záložku Resource View v paneli Workspace. Zobrazia sa vám všetky zdroje (Resources), ktoré projekt obsahuje. Trošku sa pohrajte a preskúmajte co všetko tu je. (obr. 2)

Page 42: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Obr. 2 Zdroje projektu Vymena

Nás však teraz najviac zaujímajú dialógy. Na pridanie nového dialógu, kliknite pravým tlacidlom na kartu Dialog a vyberte Insert Dialog. Všimnite si, že ak vyberiete len Insert..., máte na výber o mnoho viac možností. Ked vložíte nový dialóg, mal by vyzerat asi ako na obr 3.

Page 43: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Obr 3 Nový zdroj dialógu

Novému dialógu je automaticky priradené ID: IDD_DIALOG1. Dobre, dialóg už máme, podme s ním zacat pracovat! Najprv pre pohodlnost, kto chce môže si nastavit mriežku pre lepšie a presnejšie umiestnenie jednotlivých ovládacích prvkov. V dolnej casti obrazovky kliknite na mriežku (Toggle Grid), ako vidíte na obr. 4.

Obr. 4

Teraz kliknite prvým tlacidlom na dialóg a z menu vyberte Properties. Vyplnte hlavicku dialógu (Caption) tak, ako vidíte na obrázku 5. (záložka General).

Page 44: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Obr. 5 Hlavicka dialógu

Základný dialóg máme. Do dialógu teraz pridajte dva popisy (Static Text), dve textové polia (Edit Box) a jedno tlacidlo (Button), usporiadajte a pomenujte ich tak, ako vi díte na obr. 6. Ak neviete, ktorý ovládací prvok reprezentuje tá-ktorá ikona, pomôže vám obr. 7. Názvy (ID) jednotlivých ovládacích prvkov zatial nechajte tak ako sú, zmente len hlavicku Caption (postupujte podobne ako pri zmene hlavicky dialógu, ciže kliknite pravým tlacidlom na ovládacom prvku, vyberte Properties a v záložke General dopíšte Caption).

Stlacte teraz kombináciu kláves CTRL+T aby ste videli ako bude dialóg v aplikácií vyzerat. Spokojní? Teraz ešte zmeníme názvy (ID) niektorých prvkov. Textové pole za popisom s hlavickou: Zadajte Text nazvite IDC_GETNAME (predtým IDC_EDIT1). Tlacidlo s hlavickou Skopírovat nazvite IDC_COPY (predtým IDC_BUTTON1) a posledné textové pole nazvite IDC_SHOWNAME (predtým IDC_EDIT2). V prípade tlacidla IDC_COPY kliknite ešte na záložku Styles a zaškrtnite Default button (obr. 8). To znamená, že toto tlacidlo bude „prednastavené“ hned po vyvolaní dialógu a po stlacení klávesy Enter v dialógu sa aktivuje.

Tak, vizuálnu cast navrhovania dialógu máme za sebou. Podme teraz „zakomponovat“ tento dialóg do nášho programu. Chceme, aby sa po kliknutí lavým tlacidlom do klientského okna aplikácie Vymena zobrazil dialóg IDD_DIALOG1. Potom ak zadáme text do textového pola IDC_GETNAME a klikneme na tlacidlo IDC_COPY (alebo stlacíme klávesu ENTER – vid. Default button), text z pola IDC_GETNAME sa objaví v poli IDC_SHOWNAME). Z prostredia Dialog Editoru spustte Class Wizard (CTRL+W). Ten sa vás opýta, ci chcete vytvorit novú triedu pre tento dialóg (obr. 9a). Nechajte zaškrtnuté Create a new class a kliknite na OK. Potom sa zobrazí další dialóg, ktorý vyplnte podla obrázka 9b. V jednej z úvodných castí sme si povedali, že všetky triedy odvodené od niektorej z tried MFC sa podla konvencie zacínajú písmenom C, preto sa aj naša trieda nazýva CVymenaDialog. (Jej základná trieda je trieda MFC CDialog). V tomto dialógu si ešte všimnite, že musíme presne urcit, ktorému dialógu bude patrit novo vytváraná trieda (položka Dialog ID).

Ked už máte spustený Class Wizard uistite sa, že v zozname Class name máte triedu CVymenaDialog. Kliknite na záložku Member Variables. Zobrazia sa vám všetky ovládacie prvky, ktoré sú na dialógu IDD_DIALOG1. (obr. 10a). Teraz postupne pridajte všetkým ovládacím prvkom premenné, podla tab. 1. Premennú pridáte tak, že sa nastavíte na ten ovládací prvok, ktorému danú premennú pridávate a kliknete na tlacidlo Add Variable. Potom sa zobrazí dialóg ako na obr. 10b.

ID ovládacieho prvku Clenská premenná Typ Category IDC_COPY m_btnCopy CButton Control IDC_GETNAME m_editGetName CString Value IDC_SHOWNAME m_editShowName CString Value Tab. 1 Pomenovanie premenných v dialógu IDD_DIALOG1

Všimnite si, že v prípade premenných m_editGetName a m_editShowName môžete volit aj maximálnu dlžku retazca (dole v Class Wizarde). Pre naše potreby zadajte max. dlžku retazca na 25 znakov. Aby sa nieco vôbec stalo, po kliknutí na tlacidlo IDC_COPY, musíme ešte pridat funkciu, ktorá sa zavolá po kliknutí na toto tlacidlo. V Class Wizarde sa teda vrátte na záložku Message Maps a v triede CVymenaDialog vyhladajte v zozname Object Ids objekt IDC_COPY. Potom mu pridajte správu BN_CLICKED, ktorá sa objaví vpravo v zozname Messages. Jej meno nechajte „prednastavené“, ciže OnCopy (obr. 11).

Zavrite Class Wizard a zmente funkciu OnDraw triedy CVymenaView takto: void CVymenaView::OnDraw(CDC* pDC) { pDC->TextOut(0, 0, "Kliknite tu lavým tlacidlom na vyvolanie dialógu Vymena"); } Teraz už užívatel bude vediet, co má s aplikáciou robit. Aby sa ale dialóg naozaj zobrazil, upravte ešte funkciu OnLButtonDown triedy CVymenaView (to, že ju musíte najprv pridat pomocou Class Wizardu už nepíšem, malo by vám to byt jasné). void CVymenaView::OnLButtonDown(UINT nFlags, CPoint point) { CVymenaDialog VymenaDlg;

Page 45: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

VymenaDlg.DoModal(); }

Funkcia DoModal (v našom prípade je to clenská funkcia triedy CDialog) spôsobí nahranie zdroju dialógu a jeho následné zobrazenie. Ked táto funkcia zobrazí modálny dialóg, užívatel nemôže pracovat s aplikáciou až pokým nie je okno tohto dialógu zavreté (presne sme si toto opisovali v predchádzajúcej casti). Aby prekladac spoznal triedu CVymenaDialog, pridajte ešte na zaciatok súboru VymenaView.cpp tento riadok: #include "VymenaDialog.h"

Obr. 6 Dialóg Výmena

Skúste teraz aplikáciu preložit a spustit. Po kliknutí lavým tlacidlom myši do okna sa zobrazí dialóg. Do oboch textových polí síce môžete vpisovat text, ale po stlacení tlacidla s nápisom Skopírovat sa nic nestane (co je ale celkom logické, kedže sme zatial nepridali žiaden kód do funkcie OnCopy). Teraz to napravíme. V zdrojovom súbore triedy CVymenaDialog vyhladajte funkciu OnCopy a zmente jej telo takto: void CVymenaDialog::OnCopy() { //skopírujeme dáta z ovládacích prvkov do premenných

UpdateData(TRUE); m_editShowName=m_editGetName; //skopírujeme dáta z premenných do ovládacích prvkov UpdateData(FALSE); }

Tajomná funkcia UpdateData urobí vlastne celú prácu za nás. Môže vykonávat tri operácie. Môže kopírovat dáta z ovládacieho prvku do premennej, môže kopírovat dáta z premennej do ovládacieho prvku a rovnako môže aj overovat dáta, ktoré užívatel zadal v ovládacom prvku. Ak má parameter TRUE, tak vtedy sa kopírujú dáta z ovládacieho prvku do premennej a ak má parameter FALSE tak naopak. V knihe Programujeme v jazyce Visual C++, (Mark Andrews, Computer Press, 1997) je použitý zaujímavý trik na zapamätanie, v ktorom smere sa presúvajú dáta. V zdrojovom súbore triedy dialógu autor použil direktívy #define nasledovne: #define VAR2CTRL FALSE #define CTRL2VAR TRUE (VAR2CTRL – Variable To Control) (CTRL2VAR – Control To Variable) Naša funkcia OnCopy by s týmito direktívami vyzerala takto: (nezabudnite ich ale pridat na zaciatok súboru VymenaDialog.cpp) void CVymenaDialog::OnCopy()

Page 46: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

{ //skopírujeme dáta z ovládacích prvkov do premenných UpdateData(CTRL2VAR); m_editShowName=m_editGetName; //skopírujeme dáta z premenných do ovládacích prvkov UpdateData(VAR2CTRL); }

Obr. 7 Popis ikôn pre ovládacie prvky dialógu

Obr. 8 Nastavenie Default button u tlacidla

Page 47: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Obr. 9a Vytvorenie novej triedy pre dialóg

Obr. 9b Vytvorenie novej triedy pre dialóg

Page 48: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Obr. 10a Pridávanie premenných k ovládacím prvkom

Obr. 10b Pridávanie premenných k ovládacím prvkom

Teraz, ked spustíte aplikáciu a vyvoláte dialóg, po napísaní textu do horného textového pola a stlacení klávesy ENTER alebo kliknutí na tlacidlo s textom Skopírovat (co je vlastne jedno a to isté), text z horného pola sa zobrazí v dolnom textovom poli (obr. 12.) Ak sa pokúsite napísat text dlhší ako 25 znakov, nebude vám to umožnené. Spôsobí to práve mechanizmus DDV, ktorý kontroluje dlžku oboch retazcov v textových poliach.

Page 49: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

FUNKCIA DoDataExchange V PROGRAMOCH. Na zaciatku sme písali, že na výmene dát sa zúcastnujú dve funkcie: UpdateData a DoDataExchange. Prvú sme si už opísali, teraz sa pozrime na druhú. Je to clenská funkcia triedy CWnd a je volaná funkciou UpdateData. Túto funkciu by sme nikdy nemali volat priamo, vždy by sme ju mali najprv zavolat funkciu UpdateData a nechat na nej volanie funkcie DoDataExchange. Tu je kód tejto funkcie ako vyzerá v aplikácií Vymena: void CVymenaDialog::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CVymenaDialog) DDX_Control(pDX, IDC_COPY, m_btnCopy); DDX_Text(pDX, IDC_SHOWNAME, m_editShowName); DDV_MaxChars(pDX, m_editShowName, 25); DDX_Text(pDX, IDC_GETNAME, m_editGetName); DDV_MaxChars(pDX, m_editGetName, 25); //}}AFX_DATA_MAP }

Okrem volania výmeny dát (prefix DDX), môžete v tomto výpise nájst aj volanie s prefixom DDV. Toto volanie nespôsobuje žiaden prenos dát, len nastavuje maximálne, resp. minimálne hodnoty, aké môže premenná priradená ovládaciemu prvku nadobúdat. Ked si pozornejšie prezriete tento kód zistíte, že je tu vlastne to, co ste pridávali pomocou Class Wizardu. A opät, ako tomu bolo v prípade pridávania obsluhy správ, kód pridaný Class Wizardom je umiestnený medzi charakteristické komentáre. Vo funkcii DoDataExchange sú to: //{{AFX_DATA_MAP(CVymenaDialog) – zaciatok bloku //}}AFX_DATA_MAP – koniec bloku

Obr. 11 Pridanie funkcie na obsluhu stlacenia tlacidla

Obr. 12 Koncový dialóg aplikácie Vymena Na mechanizmoch DDX a DDV sa ešte zúcastnuje aj kód medzi blokmi AFX_DATA_INIT. V našej aplikácií Vymena nájdete tieto komentáre v tele konštruktora dialógu: //{{AFX_DATA_INIT(CVymenaDialog) m_editShowName = _T(""); m_editGetName = _T("");

Page 50: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

//}}AFX_DATA_INIT

Kód medzi týmito blokmi pripojuje premenné k ovládacím prvkom. Tieto bloky AFX_DATA_INIT a AFX_DATA_MAP nájdete vždy v zdrojovom súbore triedy dialógu. Tretí a posledný blok týkajúci sa prenosu dát, ktorý pridá do vášho programu Class Wizard je umiestnený v hlavickovom súbore triedy dialógu. V našom súbore DialogVymena.h vyzerá takto: //{{AFX_DATA(CVymenaDialog) enum { IDD = IDD_DIALOG1 }; CButton m_btnCopy; CString m_editShowName; CString m_editGetName; //}}AFX_DATA Tento blok deklaruje premenné pripojené k ovládacím prvkom. FAQ Q: V ktorých súboroch nájdem telá funkcií ako napr. WinMain, Run, OnIdle? A: Funkcia WinMain je v súbore winmain.cpp, ktorý nájdete pod adresárom VC98\MFC\SRC. Funkcie Run a OnIdle sú v súbore appcore.cpp. Dalšie funkcie, ktoré tvoria jadro aplikacného systému tiež nájdete v tomto adresári. TIPY NA DOMA. Pohrajte sa trochu s dialógom Vymena. Skúste pridat dalšie prvky, na ktorých odskúšate funkciu DDX a DDV mechanizmov. Nezadajte nic do pola max. pocet znakov pri textovom poli IDC_GETNAME. Skúste priamo vo funkcii OnCopy previest vlastnú kontrolu, ci je zadaný retazec kratší alebo rovný ako 15 znakov. Ak nie je, vypíšte o tom užívatelovi správu pomocou funkcie MessageBox. NABUDÚCE. Ešte zdaleka problematiku dialógov neuzatvárame. Nabudúce vytvoríme velký modálny dialóg, ktorý bude obsahovat takmer všetky prvky, ktoré sa na dialóg môžu umiestnovat (okrem spolocných ovládacích prvkov Windows, ich použitie s i ukážeme až o dva mesiace). Teším sa na stretnutie o mesiac. 11. cast: Dialógy III DOMÁCA ÚLOHA. Pozrime sa bližšie, ako sa mala riešit domáca úloha z predchádzajúcej casti. Išlo o „manuálne“ vykonanie kontroly, ci text v poli IDC_GETNAME nie je dlhší ako 15 znakov. Ak je dlhší, potom zobrazit používatelovi správu pomocou informacného dialógu, že text je dlhší, než aký môžeme kopírovat. Jediný problém, s ktorým ste sa mohli stretnút, je, ako zistit dlžku retazca v premennej m_editShowName. Nebolo to nic tažké. Stacilo si uvedomit, že táto premenná je premennou typu Cstring, a v helpe si pozriet clenské funkcie tejto triedy. Hladanou funkciou na zistenie dlžky retazca je potom funkcia GetLength. Teraz to už bolo jednoduché. Ponúkam jednu z možných úprav funkcie OnCopy v zdrojovom súbore triedy dialógu Vymena. void CVymenaDialog::OnCopy() { //skopírujeme dáta z ovládacích prvkov do premenných UpdateData(CTRL2VAR); // ak splna podmienku... if ((m_editShowName.GetLength()) < 15) { //...skopírujeme dáta z premenných // do ovládacích prvkov m_editShowName=m_editGetName; UpdateData(VAR2CTRL); } else MessageBox("Zadaný text je dlhší ako 15 znakov", "Pozor!", MB_ICONWARNING);

Page 51: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

} VYTVÁRAME BIGDLG. Ako som slúbil v predchádzajúcej casti, teraz si vytvoríme dialóg, do ktorého „napcháme“ takmer všetky štandardné ovládacie prvky, ktoré sa dajú. Spustte AppWizard a vytvorte kostru aplikácie s názvom BigDlg. Postupujte ako v prípade aplikácie Vymena. Pre istotu si ešte pozrite výpis o novom projekte na obr. 1 a porovnajte ho s vaším. Teraz vytvorte nový zdroj dialógu (postup nájdete v predchádzajúcej casti). Jeho ID nemente, nechajte IDD_DIALOG1. Kedže plánujeme pridávat vela ovládacích prvkov, zväcšite tento zdroj dialógu, aby mal rozmery približne 20 × 16 cm. Pravým tlacidlom vyvolajte jeho vlastnosti a do položky Caption na záložke General vpíšte: Štandardné ovládacie prvky, Jún 2001. Kliknite na záložku More Styles a nastavte vlastnost Visible. Ak ste všetko urobili správne, mali by ste na obrazovke vidiet nieco také ako na obr. 2. PRIDÁVAME OVLÁDACIE PRVKY. Teraz pridajte ovládacie prvky tak, ako ste ich pridávali v aplikácii Vymena. Umiestnenie a velkost každého ovládacieho prvku vidíte na obr. 3, co je už kompletne hotový dialóg v aplikácii. Popis jednotlivých ovládacích prvkov je tu:

Obr. 1 Informácie o projekte BigDlg

Page 52: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Obr. 2 Situácia po vytvorení a pomenovaní nového dialógu

Page 53: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Obr. 3 Dialóg aplikácie BigDlg v „nasadení“

Opisy (Static Box) Meno, Dátum nar. a dalšie: Tento ovládací prvok slúži len ako informácia, napr. aké informácie má zadat. Za behu aplikácie neprebieha žiadna interakcia medzi ovládacím prvkom popis a používatelom. Používatel nemôže priamo menit text napísaný v takomto ovládacom prvku. Postupne pridajte všetky tieto popisy do dialógu. Nemusíte každý prácne pridávat jeho vybraním z toolbaru Controls. Pochopitelne, fungujú známe techniky Windows, ked stací držat CTRL a ovládací prvok jednoducho skopírujete na nové miesto. Teraz stací len pomenit hlavicky (Caption) jednotlivých popisov a vybavené. Do hlavicky sa zvykne pred prvé písmeno vkladat znak & (ampersand), co sa prejaví ako podciarknutie prvého znaku v popise (nemusí to byt, samozrejme, pravidlom, môže sa vložit pred ktorékolvek písmeno, to bude pri behu podciarknuté). Potom je možné pristupovat k ovládaciemu prvku hned za popisom pomocou klávesu ALT plus príslušné písmeno. Napríklad na textové pole IDC_MENO sa môžeme jednoducho prepnút stlacením klávesov ALT+M.

Textové pole (Edit Box) IDC_MENO: Textové pole je základným ovládacím prvkom na získavanie údajov od používatela. Pole IDC_MENO umiestnite hned vedla popisu s hlavickou Meno. Jeho ID zmente z IDC_EDIT1 na IDC_MENO. Ostatné nastavenia nechajte na default.

Textové pole (Edit Box) IDC_RODC1: Je umiestnené za popisom Rodné císlo. Bude obsahovat len císla (co aj s pomocou Class Wizardu nastavíme) a bude to „dátumová“ cast rodného císla. Zmente jeho ID na IDC_RODC1.

Textové pole (Edit Box) IDC_RODC2: Je umiestnené za popisom / a obsahuje posledné štvorcíslie rodného císla. Jeho ID zmente na IDC_RODC2. Popisu s hlavickou / nastavte vo vlastnostiach na záložke Styles vlastnost Align Text na Center.

Textové pole (Edit Box) IDC_POZN: Je umiestnené za popisom s hlavickou Poznámka a obsahuje krátky text. Je to viacriadkové textové pole, preto vo vlastnostiach tohto textového pola oznacte v záložke Styles položku Multiline a odznacte položku Auto HScroll . Samozrejme, nezabudnite zmenit jeho ID na IDC_POZN.

Rámcek (Group Box) s hlavickou Kategória: Nemá nijaký hlbší význam. Slúži len na vizuálne spojenie prepínacov. Do hlavicky (Caption) napíšte Kategória. ID nechajte default.

Page 54: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Prepínace (Radio Button) IDC_KATEGORY, IDC_RADIO2, IDC_RADIO3: Všetky tieto prepínace sú umiestnené vnútri rámceka Kategória. Vrchnému nastavte vlastnosti, ako vidíte na obr. 4. Dalším len zaškrtnite vlastnost Tab Stop. Nechajte im implicitné ID a hlavicku (Caption) vypíšte podla obr. 3. Nenastavujte už vlastnost Group. Teraz Windows zabezpecí, že môže byt oznacený len jeden z týchto troch prepínacov. Rámcek Kategória nemá na ich správanie nijaký úcinok. (Na korektné správanie sa prepínacov je dôležitá ešte vlastnost Auto na karte Styles, ktorá je aj implicitne nastavená.)

Obr. 4 Vlastnosti prvého (vrchného) prepínaca zrucnosti

Obr. 5 Doplnenie položiek v Combo boxe

Obr. 6 Nastavenie vlastností bitmapy pre owner draw tlacidlo

Rámcek (Group Box) Vlastník: Do hlavicky vpíšte text Vlastník a nastavte vlastnost Group, cím ukoncíte predchádzajúcu skupinu.

Zaškrtávacie polícka (Check Box) IDC_SPC, IDC_NOTEBOOK, IDC_APPLE: Tieto ovládacie prvky umiestnite do rámceka Vlastník. Všetky vlastnosti nechajte implicitné, ibaže zmente ID a hlavicky, ako vidíte v tab. 1.

ID Caption IDC_SPC Štandardné PC IDC_NOTEBOOK Notebook

Page 55: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

IDC_APPLE Apple Tab. 1 ID a hlavicky ovládacích prvkov v rámceku Vlastník

Pole so zoznamom (Combo Box) IDC_ZAMEST: Prvé pole so zoznamom bude tzv. simple. Používatel môže do horného textového pola napísat, co chce, a potom vybrat položku z pripojeného zoznamu. Zmente ID tohto Combo boxu na IDC_ZAMEST a vo vlastnostiach na karte Styles položku Type nastavte na Simple. Pocas návrhu aplikácie (tzv. Design time), môžeme do tohto pola so zoznamom pridávat položky. Niekde, ako napr. v prípade zoznamu (List Box), to nejde a my môžeme pridávat položky až pocas behu aplikácie (Run time). Ukážeme si to aj v aplikácii BigDlg. Kedže do pola so zoznamom môžeme pridávat položky aj v Design time, vo vlastnostiach na záložke Data vpíšte hodnoty, ako vidíte na obr. 5. Na další riadok sa dostanete stlacením CTRL + ENTER. Nezlaknite sa, ked po dopísaní a odklepnutí ENTER nic v poli so zoznamom neuvidíte. To, že položky boli naozaj do zoznamu pridané, si môžete overit, ak spustíte dialóg v tzv. testovom móde, ciže stlacte CTRL + T alebo vyberte z menu Layout -> Test.

Pole so zoznamom (Combo Box) IDC_VZDELANIE: V tomto poli so zoznamom (tzv. Dropdown) nechajte všetky hodnoty tak, ako boli implicitne nastavené, len zmente ID na IDC_VZDELANIE. Na

záložke Data pridajte úrovne vzdelania: Základné, Stredoškolské bez maturity, Stredoškolské + maturita, Vysokoškolské. V Combo boxe typu Dropdown môžete cokolvek napísat do horného

textového pola alebo kliknutím na šípku vybrat nejakú položku zo zoznamu.

Pole so zoznamom (Combo Box) IDC_JAZYK: Posledné pole so zoznamom bude typu Drop List. V tomto type môže používatel vyberat položky iba z pripojeného zoznamu. Nemôže písat vlastné hodnoty ako v typoch Drodown a Simple. Tento typ nastavte na záložke Styles podobne ako pri Combo Boxe IDC_ZAMEST. V záložke Data pridajte jazyky anglictinu, francúzštinu, španielcinu, nemcinu. ID zmente na IDC_JAZYK.

Zoznam (List Box) IDC_ZAMERANIE : V zozname môžete vybrat len jednu položku. Položky do zoznamu nemôžete pridávat pocas Design time. Ako sa pridávajú, to si ukážeme dalej v tejto casti. Zmente ID tohto zoznamu na IDC_ZAMERANIE, ostatné vlastnosti nechajte na default.

Posuvníky (Horizontal Scroll Bar) IDC_PROSPECH, IDC_ROCNIK: Pridajte dva horizontálne posuvníky a zmente ich ID. Posuvník nalavo bude mat ID: IDC_PROSPECH a posuvník napravo ID: IDC_ROCNIK. V Design time nemôžeme s posuvníkmi vela robit, všetko sa nastavuje až pri behu (Run time). Popis (Static text) IDC_STATIC_ROCNIK : Umiestnite tento popis vedla posuvníka IDC_ROCNIK. Nemente jeho hlavicku, zmente len jeho ID na IDC_STATIC_ROCNIK. Bude slúžit na zobrazenie císla rocníka podla pozície posuvníka IDC_ROCNIK.

Tlacidlo (Button) IDC_FOTO a bitmapy “FOTOU“ a “FOTOD“: Funkciou tlacidla IDC_FOTO je zobrazit obrázok (bitmapu). Vo vlastnostiach tohto tlacidla nastavte v záložke General jeho ID na IDC_FOTO a Caption zmente na Foto. Na záložke Styles zaškrtnite položku Owner draw. Táto vlastnost nám umožnuje riadit zobrazovanie tohto ovládacieho prvku. Aby sme mohli nejakú bitmapu v našom dialógu zobrazit, musíme ju pridat ako zdroj. Pravým tlacidlom myši kliknite v zozname zdrojov na BigDlg Resources (pozri obr. 2). Vyberte položku Import... a nájdite na disku nejakú vhodnú bitmapu. Po pridaní bitmapy v zdrojoch pribudne jedna položka Bitmap, v ktorej (po otvorení) nájdete obrázok (zdroj) s názvom IDB_BITMAP1. Pravým kliknutím na IDB_BITMAP1 vyberte jej vlastnosti (properties). Do polícka ID napíšte “FOTOU“. Je dôležité, aby ID bolo v úvodzovkách (pozri obr. 6). Táto bitmapa sa zobrazí, ak bude tlacidlo v hornom stave (U – Up). Aby sme nejako odlíšili aj kliknutie na tlacidlo (dolný stav D – Down), pridajte do zdrojov ešte raz tú istú bitmapu, ale do ID napíšte “FOTOD“ a z menu Image vyberte Invert Colors (najbežnejší spôsob, ako rozlíšit stavy: horný a dolný – „stlacený“). Dalšie stavy, ktoré môže tlacidlo nadobúdat a ktoré môžeme „ukázat“ pomocou bitmapy na tomto tlacidle, sú stav s fokusom (focused), vtedy by sme ID bitmapy nazvali “FOTOF“ a stav neprístupné (disabled), ked by ID bitmapy bolo “FOTOX“. Skúste si doma vytvorit dalšie bitmapy a vyskúšajte aj tieto stavy.

Tlacidlo (Button) IDC_BTNINF: Je to len pomocné tlacidlo, ktoré nemá inú funkciu ako informovat používatela, že sa niekde v jeho okolí nachádza fotografia. Jeho ID zmente na IDC_BTNINF a do pola Caption vo vlastnostiach na záložke General napíšte: Fotka. To je všetko.

Ked už máte všetky ovládacie prvky pridané, skontrolujte ešte ich poradie. Kombináciou CTRL + D alebo z menu Layout -> Tab Order zobrazíte ich poradie. Poradie meníte poklepaním na jednotlivé ovládacie prvky v tom poradí, aké chcete. Poradie pre náš príklad vidíte na obr. 7. Ak sa pomýlite v poklepkávaní, stlacte CTRL a kliknite na ten ovládací prvok, ktorý má ako posledný správne císlo. Dalšie kliknutia (už bez držania CTRL) budú císlovat ovládacie prvky od císla posledného „dobrého“ ovládacieho prvku.

Page 56: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Obr. 7 Poradie ovládacích prvkov v dialógu aplikácie BigDlg

Page 57: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Obr. 8 Vytváranie novej triedy pre dialóg „PROGRAMOVANIE“ DIALÓGU. V prvom rade je potrebné vytvorit triedu pre náš nový dialóg. Postupujte ako v prípade dialógu Vymena. Spustte Class Wizard a na prvom dialógu Adding a Class, ktorý by sa vám mal zobrazit po spustení Class Wizardu (ten rozpoznal, že zdroj dialógu IDD_DIALOG1 nie je spojený so žiadnou existujúcou triedou) zvolte Create a New Class a potvrdte. Objaví sa další s názvom New Class, ktorý vyplnte podla obr. 8.

Po vytvorení novej triedy sa vám zobrazí štandardné okno Class Wizardu. Teraz pridajte podla tab. 2 clenské premenné dialógu jednotlivým ovládacím prvkom (postup – pozri dialóg Vymena).

ID ovládacieho prvku Clenská premenná Typ IDC_MENO m_strMeno CString IDC_RODC1 m_nRodc1 Int IDC_RODC2 m_nRodc2 Int IDC_POZN m_strPozn CString IDC_KATEGORY m_nCat Int IDC_SPC m_bSpc BOOL IDC_NOTEBOOK m_bNot BOOL IDC_APPLE m_bApl BOOL IDC_ZAMEST m_strZamest CString IDC_ZAMERANIE m_strZam CString IDC_VZDELANIE m_strVzd CString IDC_JAZYK m_nJaz Int IDC_PROSPECH m_nProsp Int IDC_ROCNIK m_nRoc Int Tab. 2 Clenské premenné dialógu

Page 58: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Posledné dve premenné (m_nProsp a m_nRoc) súvisia s posuvníkmi, s nimi budeme pracovat až v nasledujúcej casti. Pri premenných m_nRodc1 a m_nRodc2 nastavte minimálnu hodnotu na 0 a maximálnu v prípade m_nRodc1 na 999999 a v prípade m_nRodc2 na 9999.

Ak ste už všetky premenné pridali, „namapujte“ pomocou Class Wizardu do triedy dialógu správu WM_INITDIALOG. Kliknite na Edit Code a pridajte kód na obsluhu tejto správy vo funkcii OnInitDialog. BOOL CBigDialog::OnInitDialog() { // získame ukazovatel na ovládací prvok // IDC_ZAMERANIE CListBox* pLB=(CListBox*) GetDlgItem(IDC_ZAMERANIE); // pomocou ukazovatela pLB pridáme do zoznamu // IDC_ZAMERANIE položky pLB->InsertString(-1, "Všeobecné"); pLB->InsertString(-1, "Jazykové"); pLB->InsertString(-1, "Humanitné"); pLB->InsertString(-1, "Technické"); pLB->InsertString(-1, "Prírodovedecké"); pLB->InsertString(-1, "Elektrotechnické"); // nahráme bitmapu na tlacidlo IDC_FOTO VERIFY(m_foto.AutoLoad(IDC_BUT, this)); return CDialog::OnInitDialog(); }

Ako iste tušíte, tento kód pridá do zoznamu IDC_ZAMERANIE položky, co bolo neuskutocnitelné v Design time. Rovnako pomocou funkcie AutoLoad nahráme na tlacidlo IDC_FOTO bitmapy “FOTOU“ a “FOTOD“. Na správnu funkciu je ešte potrebné zadeklarovat premennú m_foto. Pridajte do deklarácie triedy dialógu CBigDialog (BigDialog.h) túto súkromnú clenskú premennú: CBitmapButton m_foto;

Teraz musíme pripojit dialóg IDD_DIALOG1 k pohladu. Otvorte zdrojový súbor triedy pohladu (BigDlgView.cpp). Na zaciatok pridajte jednu include direktívu na vloženie deklarácie triedy CBigDialog (bez nej by sme nemohli z pohladu tento dialóg vyvolat). #include "BigDialog.h"

Zmente funkciu OnDraw v tomto súbore, aby bolo používatelovi jasné, co má s aplikáciou robit: void CBigDlgView::OnDraw(CDC* pDC) { pDC->TextOut(0, 0, "Kliknite tu lavým tlacidlom na vyvolanie dialógu. "); }

Predpokladám, že teraz je vám už jasné, co bude nasledovat. Áno, bude to pridanie obsluhy správy WM_LBUTTONDOWN. Pridajte ju a zmente kód jej obslužnej funkcie, ako vidíte dalej: void CBigDlgView::OnLButtonDown(UINT nFlags, CPoint point) { CBigDialog BigDlg; // naplníme premenné dialógu // "prvotnými" dátami BigDlg.m_strMeno="Janka Mrkvicková"; BigDlg.m_nRodc1=722810; BigDlg.m_nRodc2=5437;

Page 59: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

BigDlg.m_strPozn="Študent dosahoval dobré výsledky v prírodných vedách."; // 0 - zaciatocník; 1 - mierne pokrocilí; 2 - Pokrocilí BigDlg.m_nCat=1; BigDlg.m_bSpc=TRUE; BigDlg.m_bNot=TRUE; BigDlg.m_bApl=FALSE; BigDlg.m_strZam="Všeobecné"; BigDlg.m_strZamest="Programátor"; BigDlg.m_strVzd="Vysokoškolské"; // 0 - Anglictina; 1 - Francúzština; 3 - Španielcina // 4 - Nemcina BigDlg.m_nJaz=3; // vyvoláme dialóg int ret=BigDlg.DoModal(); TRACE("DoModal vratil: %d\n", ret); } FAQ Q: Ako spustit z mojej aplikácie inú, ktorá by potom bežala paralelne s mojou na pozadí? A: Tento problém sa dá vyriešit pomocou použitia procesov. Bez dalšieho vysvetlovania (v seriáli sa k vláknam a procesom ešte dostaneme) si pozrite ilustracný kód: // pomocou procesov spustíme prehliadac DWORD xxx; STARTUPINFO si; PROCESS_INFORMATION pi; char* cmd; BOOL navrat; cmd="IEXPLORE.EXE c:\\Hry\\Strelec\\default.htm"; // spustí server memset(&si,0,sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.wShowWindow = SW_SHOW; ---CUT--- if (!CreateProcess(NULL, cmd ,NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi)) { if (xxx = GetLastError()==2) { MessageBox("Chyba, nenájdený súbor", NULL, MB_OK); } } // zrušenie procesu a tým ukoncenie IE navrat = TerminateProcess(pi.hProcess,1); // proces zrušený CloseHandle(pi.hThread); CloseHandle(pi.hProcess); Tento kód vyvolá proces (spustí Internet Explorer a natiahne v nom stránku default.htm). Nakoniec proces ukoncí a tým zavrie Internet Explorer. Nosná funkcia pri procesoch je CreateProcess, jej opisu sa budeme venovat neskôr.

Page 60: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

TIPY NA DOMA. Pohrajte sa s rôznymi nastaveniami vlastností jednotlivých ovládacích prvkov. Nemusíte presne vediet, ktorá na co slúži, ale aby ste mali aspon akú-takú predstavu. Skúste zmenit hlavicku owner draw tlacidla na inú ako Foto a program pri nahrávaní bitmapy na toto tlacidlo spadne. Preco? (Návod: Pozri Help – funkciu AutoLoad a triedu CbitmapButton.) A ked zmeníte ID tlacidla owner draw na iné (pricom hlavicku necháte Foto), preco program nespadne? (Samozrejme, musíte upravit aj volanie funkcie AutoLoad, kde treba prepísat IDC_FOTO na vami zvolené ID tlacidla.) V helpe si pozrite, na co slúži makro VERIFY. Pridajte funkcnost pre tlacidlo IDC_BTNINF. Nech sa po kliknutí na toto tlacidlo zobrazí informacný dialóg s informáciou, že nad ním možno nájst fotku (alebo s nejakou inou, to už je jedno, ide o to, ako to naprogramovat). A nakoniec pridajte do funkcie OnLButtonDown makrá TRACE tak, aby vždy po uzavretí dialógu vypísali do okna Debug hodnoty všetkých clenských premenných dialógu. NABUDÚCE. Zdá sa mi, že dialógy sa nám natiahnu ešte aspon na dve casti. Nabudúce si dokoncíme zacaté rozprávanie (zapojíme posuvníky, odstránime zavretie dialógu po stlacení ENTER) a povieme si o spolocných ovládacích prvkoch systému Windows (Windows Common Controls). Po dialógoch nás caká vytváranie menu, serializácia a iné dobrôtky spojené s dokumentmi. Teším sa na stretnutie o mesiac. 12. cast: Dialógy IV A je to tu! Seriál C++ pod Windows oslavuje prvé výrocie. Verte alebo nie, stretávame sa už po celý jeden rok, co pri seriáloch, ktorých maximálna dlžka života sú dva roky, urcite nie je na zahodenie. Niet sa comu cudovat, programovanie v jazyku C++ pod operacným systémom Windows je rozsiahla téma, ktorá sa nedá zvládnut za krátke casové obdobie. Zacali sme od úplných základov, postupne sme sa prepracovali ku knižnici MFC. Už viete, ako urobit „oknovú“ aplikáciu, viete, ako pracuje, viete, co s cím súvisí a co je najdôležitejšie, viete ju urobit v „profesionálnom“ vývojovom prostredí využitím „profesionálneho“ programovacieho jazyka (teda aspon dúfam). Takéto vedomosti urcite nie sú na zahodenie. Ak ich chcete mat viac, chcete byt „niekým“ v prostredí profesionálneho programovania, vzdelávajte sa, získavajte dalšie vedomosti. Úprimne dúfam, že vám v tom pomôže aj tento seriál. DOMÁCA ÚLOHA. Na zaciatku sa ešte vrátme k predchádzajúcej casti. Za úlohu ste mali doplnit funkciu OnLButtonDown v triede pohladu o makrá TRACE, ktoré by po zavretí dialógu vypísali hodnoty clenských premenných dialógu BigDlg. Inak povedané, zobrazia to, co bolo vidiet v dialógu. Akúsi pomoc ste už v tejto funkcii mali, po zavretí dialógu vypísalo makro TRACE návratovú hodnotu funkcie DoModal. Kód uvedený dalej je riešením domácej úlohy. Dopíšte ho do existujúcej funkcie OnLButtonDown. Všetky volania makier TRACE je v kóde potrebné umiestnit až za volanie funkcie DoModal. // ... funkcia OnLButtonDown ... // vyvoláme dialóg int ret=BigDlg.DoModal(); TRACE("DoModal vratil: %d\n", ret); TRACE("Meno: %s, Rodné císlo: %d/%d\n", BigDlg.m_strMeno, BigDlg.m_nRodc1, BigDlg.m_nRodc2); TRACE("Poznámka: %s\n", BigDlg.m_strPozn); TRACE("Kategória: %d\n", BigDlg.m_nCat); TRACE("Vlastník: Štandardné PC: %d, Notebook: %d, Apple: %d\n", BigDlg.m_bSpc, BigDlg.m_bNot, BigDlg.m_bApl); TRACE("Budúce zamestanie: %s\n", BigDlg.m_strZamest); TRACE("Zameranie: %s\n", BigDlg.m_strZam); TRACE("Vydelanie: %s\n", BigDlg.m_strVzd); TRACE("Pref. jazyk: %d\n", BigDlg.m_nJaz);

Dalšou úlohou bolo, aby sa po kliknutí na tlacidlo IDC_BTNINF zobrazil informacný dialóg s nejakou správou. Pomocou Class Wizardu bolo treba pridat obsluhu správy BN_CLICKED pre toto tlacidlo v triede CBigDialog. Tu je výpis funkcie na obsluhu tejto správy : void CBigDialog::OnBtninf()

Page 61: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

{ MessageBox("Nad týmto tlacidlom môžete nájst fotku.", "Informácia", MB_ICONINFORMATION); }

Tolko domáce úlohy, ideme dalej. ZAPOJENIE POSUVNÍKOV. Ak v terajšej verzii dialógu BigDlg kliknete na posúvacie pásky, nestane sa nic. Kód pre obsluhu posuvníkov musíme pridat sami, nikto to za nás neurobí (tým nikým myslím vizuálne nástroje Visual C++). Pri posuvníkoch sledujeme ich rozsah a pozíciu. Ak nastavíme rozsah od 0 po 100, hodnota 50 bude presne v strede. Ked kliknete na jednu zo šípok posuvníkov alebo ked taháte tlacidlo posuvníka, prípadne ked kliknete do vnútra posuvníka (priamo na rolovaciu pásku), ten posiela správy WM_HSCROLL alebo WM_VSCROLL (podla toho, ci ide o horizontálny alebo vertikálny posuvník). A to sú práve správy, ktorých obslužné funkcie využijeme na napísanie kódu, ktorý obsluhuje posuvníky. V našom prípade ešte chceme, aby sa po kliknutí na posuvník IDC_ROCNIK aj vypísalo na popise IDC_STATIC_ROCNIK císlo rocníka. Musíme teda rozlišovat, na ktorý posuvník sme klikli. Najprv si však vysvetlíme, ako kód pridat, a až potom sa budeme zaoberat tým, ako pracuje. PRIDANIE KÓDU. Do hlavickového súboru triedy CBigDialog pridajte dve verejné clenské premenné, ktoré urcujú rozsah posuvníkov: public:

enum { Min=0 }; enum { Max=4 };

Pretože máme dva rôzne posuvníky, musíme použit aj dva rôzne ukazovatele na ne. Pre lepšiu prehladnost pridajte aj tieto dve deklarácie do verejnej casti toho istého hlavickového súboru: CScrollBar* pSBP; CScrollBar* pSBR;

Spustte Class Wizard a do triedy CBigDialog pridajte obsluhu správy WM_HSCROLL (kedže máme horizontálne posuvníky). V zdrojovom súbore tejto triedy pridajte tento kód do funkcie OnInitDialog, ktorý má na starosti nastavenie rozsahov posuvníkov podla premenných Min a Max a zobrazenie nultého rocníka v popise IDC_STATIC_ROCNIK. Všimnite si, že tu musíme používat dva rôzne ukazovatele! // Nastavíme rozsah posuvníkov pSBP = (CScrollBar*) GetDlgItem(IDC_PROSPECH); pSBP->SetScrollRange(Min, Max); pSBR = (CScrollBar*) GetDlgItem(IDC_ROCNIK); pSBR->SetScrollRange(Min, Max); // Pociatocný rocník (nultý rocník) SetDlgItemText(IDC_STATIC_ROCNIK, "0."); Nakoniec pridajte kód do obslužnej funkcie správy WM_HSCROLL, ako vidíte na listingu. void CBigDialog::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { int pom1, pom2; CString strText; pom1 = pScrollBar->GetScrollPos(); // identifikujeme, o ktorý posuvník sa jedná if (pScrollBar==pSBP) { switch(nSBCode) { // klikneme na lavú šípku posuvníka case SB_LINELEFT:

Page 62: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

pom2 = (Max - Min) / 4; if ((pom1 - pom2) > Min) pom1 -= pom2; else pom1 = Min; pScrollBar->SetScrollPos(pom1); break; // klikneme na pravú šípku posuvníka case SB_LINERIGHT: pom2 = (Max - Min) / 4; if ((pom1 + pom2) < Max) pom1 += pom2; else pom1 = Max; pScrollBar->SetScrollPos(pom1); break; } } // ak sa jedná o posuvník rocníka, meníme // aj príslušný popis if (pScrollBar==pSBR) { switch(nSBCode) { // klikneme na lavú šípku posuvníka case SB_LINELEFT: pom2 = (Max - Min) / 4; if ((pom1 - pom2) > Min) { pom1 -= pom2; // integerový pom1 prevedieme // na stringový strText // vhodný pre SetDlgItemText strText.Format("%d.", pom1); SetDlgItemText(IDC_STATIC_ROCNIK, strText); } else { pom1 = Min; strText.Format("%d.", pom1); SetDlgItemText(IDC_STATIC_ROCNIK, strText); } pScrollBar->SetScrollPos(pom1); break; // klikneme na prvú šípku posuvníka case SB_LINERIGHT: pom2 = (Max - Min) / 4; if ((pom1 + pom2) < Max) { pom1 += pom2; // integerový pom1 prevedieme // na stringový strText // vhodný pre SetDlgItemText strText.Format("%d.", pom1); SetDlgItemText(IDC_STATIC_ROCNIK, strText); } else { pom1 = Max; strText.Format("%d.", pom1); SetDlgItemText(IDC_STATIC_ROCNIK, strText);

Page 63: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

} pScrollBar->SetScrollPos(pom1); break; } } } AKO TO PRACUJE? Tak, kód už máme, už ho len pochopit, však? Nemalo by vám to robit velký problém. Ako z kódu vidno, pomocou tretieho parametra funkcie OnHScroll identifikujeme, o ktorý posuvník ide (teda ktorý „vysiela“ správu WM_HSCROLL). Za povšimnutie hádam ešte stojí clenská funkcia triedy CString, funkcia Format. Ak máme nejaký císelný (alebo iný) údaj a chceme ho previest na retazec, nemusíme zbytocne volat konverzné funkcie. Všetko zabezpecí funkcia Format, ktorá má syntax podobnú s funkciou printf (dúfam, že máte zopakované).

Ako sa používa, vidíte vo výpise dalej. Táto funkcia má, samozrejme, takisto svoje „muchy“, takže si ju radšej podrobnejšie pozrite v helpe. POSLEDNÁ ÚPRAVA V BIGDIALOGU. Pri praktickom používaní tohto dialógu sa stretneme ešte s jedným problémom. Pri vypisovaní položky (napríklad textového pola IDC_MENO) a následnom stlacení Enter sa dialóg zavrie. Je to spôsobené tým, že tlacidlo IDOK má vo svojich vlastnostiach nastavenú vlastnost Default button [záložka (karta) Styles] a Windows po stlacení Enteru hladajú najprv aktívne tlacidlo (to, na ktorom „stojíme“ – má bodkovaný okraj), a ked ho nenájdu, potom hladajú defaultné tlacidlo (má hrubší okraj), co je v našom prípade tlacidlo OK. Kláves Enter pre nich znamená, že vykonajú presne to, ako keby sme na tlacidlo OK klikli myšou. Dobre, a co keby sme nenastavili žiadne defaultné tlacidlo? Skúste si to a uvidíte sami. Dialóg sa aj tak zavrie. A to preto, lebo ked Windows nenájdu žiadne aktívne ani žiadne defaultné tlacidlo, aj tak zavolajú funkciu OnOK, ktorá vymení dáta medzi dialógom a aplikáciou a dialóg uzavrie (a to dokonca aj vtedy, ked dialóg neobsahuje nijaké tlacidlo OK). Klasickým spôsobom, ako obíst tento nedostatok (ciže zavretie dialógu, aj ked nemáme žiadne tlacidlo defaultné), je napísat prázdnu funkciu OnOK štandardnému tlacidlu OK a cez Class Wizard mu pridat obsluhu správy BN_CLICKED. Potom v programe namiesto štandardného ID tlacidla OK (IDOK) zvolit iné a tomu takisto pridat obsluhu správy BN_CLICKED. A až z tejto funkcie (samozrejme, že sa musí volat inak ako štandardná funkcia OnOK.) budeme volat funkciu OnOK. Teraz budeme mat zarucené, že len pri kliknutí na tlacidlo OK sa aktivuje „uzavieracia sekvencia“.

Page 64: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Spustte Class Wizard a v triede CBigDialog v zozname Object IDs vyhladajte IDOK. V zozname Messages vyberte BN_CLICKED a potom Add Function. (Meno nechajte implicitné ciže OnOK. Tým my „prepíšeme“ štandardnú funkciu OnOK.) V zdrojoch, konkrétne v dialog editore, zmente ID tlacidla IDOK povedzme na IDC_KLIKOK a opät tomuto tlacidlu pridajte cez Class Wizard obsluhu správy BN_CLICKED. Pri kliknutí na Add Function je, samozrejme, potrebné zadat iné meno než OnOK, zmente ponúkané na OnKlikOK a dokoncite pridávanie funkcie. Upravte kód týchto funkcií, ako vidíte dalej. Ak náhodou máte nastavené tlacidlo teraz už IDC_OK ako default button, odznacte mu túto vlastnost (inak by toto pridávanie kódu a funkcii bolo nanic). Skúste aplikáciu spustit, malo by všetko fungovat. void CBigDialog::OnOK() { // Prázdna funkcia } void CBigDialog::OnKlikOK() { // až z tejto funkcie zavoláme OnOK, ktorá // dialóg uzavrie CDialog::OnOK(); }

Samozrejme, existujú aj iné spôsoby, ako Windows „oklamat“. Napríklad tento: Vytvorte v dialog editore hocijaké tlacidlo a umiestnite ho na dialóg. V jeho vlastnostiach na záložke General odznacte vlastnost Visible a na záložke(karte) Styles zaškrtnite vlastnost Default button. Program spustte a teraz by sa dialóg tiež nemal zavriet pri stlacení klávesu Enter, ale len pri kliknutí na tlacidlo OK. Líšia sa v niecom tieto dva spôsoby? Ak áno, v com? Ktorý je podla vás lepší? Skúste nad tým porozmýšlat. FAQ Q: Co tvorí základ knižnice MFC? A: Lahká otázka, na ktorú sa však tažko dá strucne odpovedat. Je tažké urcit, ktoré triedy knižnice MFC sú tie „najzákladnejšie“. Vo všeobecnosti sa triedy knižnice MFC delia do kategórií, pricom jednotlivé kategórie môžu obsahovat viac ci menej tried. Tieto kategórie vidíte na obr. 1. Rozhodol som sa uvádzat ich v „pôvodnom znení“ ciže v anglictine, lebo prekladat ich by aj tak nemalo zmysel a slovenský preklad by nebol velmi výstižný. Pod obrázkom 1 nájdete strucný opis týchto kategórií. CObject – základná trieda takmer celej knižnice MFC Application Architecture – do tejto skupiny patria triedy, ktoré tvoria základ každej aplikácie MFC. Napríklad triedy CWinApp, CDocument, CDocTemplate a i. Všetky sú odvodené, samozrejme, od CObject a hned potom od CCmdTarget (pozri dalej) Exceptions – patria sem triedy, ktoré obsluhujú výnimky, ktoré môžu v aplikácii nastat (napríklad pri zápise do súboru, pri procesoch a pod.). Základnou triedou tejto skupiny je CException File Services – ako možno s názvu vydedukovat, pôjde o triedy, ktoré sa zaoberajú cítaním/zápisom dát z disku/na disk. Využívajú sa pri serializácii. Základnou triedou je CFile Graphical Drawing – v tejto skupine sú triedy kontextov zariadení. Hlavnou je CDC, dalšie sú napr. CClientDC, CPaintDC a i. Control Support – trieda obsluhujúca stavy líšt (nástrojovej, stavovej) – CDocState a trieda obsluhujúca zoznamy obrázkov – CImageList Graphical Drawing Objects – triedy objektov GDI (CPen, CBitmap, CBrush, CFont…) Menus – nepotrebuje komentár. Základnou triedou je CMenu Command Line – obsahuje jedinú triedu CCommandLineInfo, ktorá má na starosti spracovanie príkazového riadka pri štarte aplikácie ODBC Database Support – obsahuje triedy, ktoré umožnujú spojenie k zdroju dát, cez ktorý môžete potom tieto dáta obsluhovat (napríklad trieda CDatabase) DAO Database Support – triedy umožnujúce pripojenie k databázam cez Data Access Objects Synchronization – triedy na prácu s procesmi, semaformi atd. Windows Sockets – obsluhuje Windows Socket API Arrays, Lists, Maps – súhrnne sa tieto triedy nazývajú triedy kolekcií; obsahujú prvky na prácu a riadenie dátových abstrakcií (polia, zoznamy, kolekcie mapovaných objektov) Internet Services – ide o triedy zabezpecujúce pripojenie a správu internetových pripojení CCmdTarget – základná trieda pre mapu správ a pre správy a udalosti

Page 65: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Window Support - patria sem triedy pre správu okien. Základnou triedou je CWnd, od ktorej sú postupne odvodzované napríklad CFrameWnd, CDialog(aj dialóg je okno) Frame Windows – triedy pre správu okien. Patria sem napríklad CFrameWnd, CMDIChildWnd a i. Control Bars – triedy na vytváranie nástrojových, stavových a iných líšt (CStatusBar, CToolBar...) Property Sheets – obsahuje dve triedy, ktoré slúžia na vytváranie a správu dialógov s kartami (záložkami). Takéto dialógy sa nazývajú property sheets alebo aj tab dialog boxes. Triedy sú CPropertySheet a CPropertySheetEx DialogBoxes – v tejto kategórii sú triedy, ktoré potrebujeme pri práci s dialógmi. Hlavnou je, samozrejme, CDialog, od ktorej sú odvodené dalšie. Patria sem aj tzv. spolocné dialógy Windows, ako napr. dialóg tlace (trieda CPrintDialog), dialóg výberu farby (trieda CColorDialog) a dalšie. Views – skupina zahrnujúca všetky pohlady. Sú tu triedy Cview (základná pre všetky pohlady), CscrollView (tie už poznáme), potom napr. CEditView, CRichEditView (slúžiace na programovanie textových editorov) a dalšie. Controls – tu sú triedy ovládacích prvkov, ktoré sme pridávali (a ešte aj budeme pridávat) do dialógov. Každý ovládací prvok musí mat, samozrejme, aj svoju triedu, napr. pre editacné okno to je trieda CEdit, pre tlacidlo CButton a pod. Spomente si, ako sme v predchádzajúcej casti deklarovali premenné pre tlacidlo s bitmapou (Fotka). Robili sme tak pomocou triedy CBitmapButton, ktorá sem takisto patrí. Rovnako aj v tejto casti rozlišujeme posuvníky pomocou premenných odvodených od základnej triedy pre posuvníky: CScrollBar. TIPY NA DOMA. Doma skúste pridat do obsluhy posuvníkov reakciu na kliknutie dovnútra posuvníka (ciže priamo na rolovaciu pásku). Takisto pridajte popis k posuvníku IDC_PROSPECH, kde podla polohy tohto posuvníka budete zobrazovat prospech slovami, teda výborný až nedostatocný. Rozšírte rozsah posuvníka IDC_ROCNIK tak, aby bolo možné zobrazit rocníky 0. až 6. Skúste pridat kód, ktorý bude obchádzat zavretie dialógu po stlacení klávesu ESC. Podobne, ako to bolo v prípade klávesu ENTER, kde sa volala funkcia OnOK, ak stlacíme ESC, volá sa funkcia OnCancel. Postupujte teda podobne ako v prípade funkcie OnOK. NABUDÚCE. Táto cast bola síce oproti predchádzajúcej skôr monotematická, na druhej strane sme však uzavreli tému modálne dialógy a ich štandardné ovládacie prvky. V budúcej casti preberieme nové, spolocné ovládacie prvky systému Windows (tie, ktoré prišli až s Windows 95). Potom si v strucnosti opíšeme nemodálne dialógy a tým s dialógmi definitívne skoncíme. 13. cast: Dialógy V.

Page 66: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

V úvode by som sa ešte rád vrátil k jedenástej casti. Nejako som sa vtedy nechal pri písaní „uniest“, a preto bola táto cast pridlhá na to, aby sa celá zmestila do jedného císla. Z toho dôvodu bol zdrojový kód k FAQ umiestnený na webe a z rovnakého dôvodu nebola v tejto casti uverejnená domáca úloha (i ked jej riešenie sa možno objavilo v dvanástej casti – neviem to urcite, kedže v case písania tejto casti ešte nevyšlo júlové císlo). OK, nabudúce sa budem „krotit“, aby k podobným problémom už nedošlo. A ešte jedna poznámocka k jedenástej casti. Vo výpise funkcie OnInitDialog sa vyskytla chybicka. Pri volaní funkcie AutoLoad mal byt jej prvým parametrom názov ovládacieho prvku nie IDC_BUT, ale IDC_FOTO (ako bolo uvedené v poznámke). Tento problém tiež súvisel s domácou úlohou (kde bol jeho presný opis), každopádne sa za túto chybu ospravedlnujem.

Obr. 1 Nová trieda pre dialóg WINDOWS COMMON CONTROLS. Ovládacie prvky, ktoré sme doteraz preberali, by sa dali nazvat aj štandardné ovládacie prvky. Štandardné preto, lebo svojím spôsobom existovali už aj v starších verziách Windows (starších ako W95, resp. WNT 3.51). Ale ked prišli na trh „úžasné“ Windows 95 (a WNT 3.51), do rodiny ovládacích prvkov pribudli další clenovia. Väcšina z nich je len akýmsi rozšírením a doplnením tých štandardných, ci už o grafiku, alebo o nové metódy. Netreba ich však považovat za nepotrebné, velakrát môžu predstavovat to pravé, co vašu aplikáciu – presnejšie dialógy v nej – sprehladní a oživí. Všetky „spolocné“ ovládacie prvky Windows sú v tab. 1 spolu s prislúchajúcou triedou knižnice MFC a krátkym opisom. Ak ste sa s niektorými doteraz nestretli, pre lepšie pochopenie väcšinu ovládacích prvkov uvedených v tab. 1 si môžete pozriet v helpe aj na obrázkoch, co aspon trochu zo zaciatku pomôže. Názov (slov.) Názov

(angl.) Trieda MFC Opis

Animácia Animate, Animation

CanimateCtrl Zobrazí animáciu (AVI súbor). Môže íst max. O dvojprúdový AVI súbor, neskomprimovaný, resp. Skomprimovaný cez kompresiu RLE8. Ak obsahuje zvuky, sú ignorované.

Zobrazenie dátumu

Time picker, Date and Time picker

CdateTimeCtrl Umožní používatelovi zmenit aktuálny dátum a cas v pocítaci.

Page 67: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Rozšírené pole so zoznamom

Extended Combo Box

CcomboEx Ako klasické pole so zoznamom, ale môže zobrazit aj obrázky.

Hlavicka Header CheaderCtrl Tlacidlo, ktoré slúži ako hlavicka, napríklad pre stlpec. (Excel: oznacenie stlpcov A1, A2)

Klávesová skratka

Hotkey ChotKeyCtrl Klávesová skratka.

Zoznam obrázkov

ImageList CimageList Zoznam obrázkov (s rovnakými rozmermi), ktoré môžu byt použité napr. v prehliadaci stromových štruktúr (strome) alebo v prehliadaci zoznamov.

Prehliadac zoznamov

List, List Control

ClistCtrl Umožnuje v jednom okne zobrazit obrázok a k nemu aj text. (Rozšírenie klasického zoznamu)

Kalendár Month Calendar

CmonthCalCtrl Zobrazuje informácie o dátume.

Indikátor priebehu

Progress CprogressCtrl Zobrazuje priebeh nejakej operácie (napr. kopírovanie dát, priebeh inštalácie).

Viacúcelová lišta

ReBar CrebarCtrl Lišta, na ktorej môžeme zobrazit viacero ovládacích prvkov (v skutocnosti sú to „detské okná“ – child windows, v ktorých môžeme tieto ovládacie prvky zobrazit) napr. v IE horná lišta, kde môže byt textové pole s adresou, tlacidlá dopredu, dozadu a pod.

Rozšírené textové pole

RichEdit CrichEditCtrl Textové pole, podporujúce napr. formátovanie textu a vkladanie objektov OLE.

Bežec Slider CsliderCtrl Nieco ako posuvník bez posúvacích šípok s možnostou zobrazenia polohy.

Císelník Spin, Spin button

CspinButtonCtrl Dvojica šípok, obycajne slúžiaca na pridávanie/uberanie z hodnoty.

Stavový riadok

Status Bar CstatusBarCtrl Okno zobrazujúce stavové „informácie“.

Panel nástrojov

ToolBar CtoolBarCtrl Okno zobrazujúce nástroje.

List, Karta Tab CtabCtrl Používa sa v dialógoch so záložkami (listami, kartami), napr. nastavovanie vlastností pri ovládacích prvkoch (karta Styles, General atd.).

Krátka nápoved

ToolTip CtoolTipCtrl Nápoved napr. k niektorým tlacidlám z panela nástrojov. Obycajne text vo farebnom okienku, ktorý sa zobrazí, ak kurzor dlhšie stojí na jednom mieste.

Strom Tree, Tree Control

CtreeCtrl Umožnuje zobrazovat text a obrázky v stromovom poradí (napr. rodokmen a pod.).

Tab. 1 Spolocné ovládacie prvky Windows (od W95) PRÍKLAD. Ked už viete, ktoré sú spolocné ovládacie prvky a ako vyzerajú, uvedieme si krátky príklad, na ktorom si ukážeme „programovanie“ tých najrozšírenejších. Budú to: Animácia, Bežec, Zoznam obrázkov, Prehliadac zoznamov a Strom. Podme na to! Vytvorte nový projekt s vlastnostami, ako mal BigDlg (SDI a Finish). Nazvite ho jednoducho Controls. Vytvorte dialóg, jeho ID nechajte IDD_DIALOG1, hlavicku zmente na: Spolocné ovládacie prvky. Spustte Class Wizard a pridajte novú triedu pre dialóg: CSpolDlg (obr. 2). Ešte urobte „štandardné“ operácie, do triedy pohladu pridajte obsluhu správy WM_LBUTTONDOWN, v jej obslužnej funkcii vyvolajte dialóg, zmente funkciu OnDraw a „includujte“ hlavickový súbor dialógu (všetko ako v prípade BigDlg). Ciže na zaciatok zdrojového súboru triedy pohladu pridajte jednu direktívu include: #include "SpolDlg.h" Zmente kód vo funkcii OnDraw: void CControlsView::OnDraw(CDC* pDC) { pDC->TextOut(0, 0, "Kliknite tu lavým tlacidlom na vyvolanie dialógu SpolDlg"); }

Page 68: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Pomocou Class Wizardu namapujte správu WM_LBUTTONDOWN a zmente telo jej obslužnej funkcie: void CControlsView::OnLButtonDown(UINT nFlags, CPoint point) { CSpolDlg spolDlg; spolDlg.DoModal(); } Nebolo by zlé v tejto fáze program skompilovat a zlinkovat a zachytit tak prvé chybicky. PRIDÁVANIE OVLÁDACÍCH PRVKOV. Po týchto „prípravných“ prácach môžeme zacat pridávat jednotlivé ovládacie prvky. Ešte azda uvediem, že každý z pridávaných ovládacích prvkov má ovela viac funkcií a vlastností, ktorými ho môžeme ovládat, ako tie, ktoré si tu spomenieme. My tu však uvedieme len tie najzákladnejšie, dôležité pri „klasickom“ používaní daného prvku. Dalšie informácie získate lahko v helpe. Umiestnenie jednotlivých ovládacích prvkov vidíte na obr. 3. Už nebudem podrobne rozpisovat pridávanie vysvetlujúcich popisov k ovládacím prvkom, pri nich stací vždy zmenit len hlavicku (Animácia, Bežec atd.).

Obr. 2 Dialóg v „nasadení“

Animácia: Aj ked sa to nezdá, animácia sa v programoch vyskytuje dost casto (napr. animované logo firmy, zobrazenie kopírovania súborov). Preto si prácu s nou opíšeme. Podla obr. 7 z desiatej casti vyberte ovládací prvok animácia (ID nechajte default) a pridajte ho na dialóg, pricom vytvorte štvorec s rozmermi približne 50 × 40 (podla rozmeru v pravej dolnej casti stavového riadka VC++). Pod to umiestnite tlacidlo, ktoré bude slúžit na spustenie animácie. ID tlacidla zvolte IDC_PLAY a hlavicku: Spusti. Aby sme nejako mohli pristupovat k tomuto ovládaciemu prvku (animácii), musíme ešte vytvorit premennú, ktorá mu bude priradená (bude jeho clenskou premennou). Spustte Class Wizard, v poli so zoznamom Class Name vyberte CSpolDlg, kliknite na kartu Member Variables, v zozname vyberte IDC_ANIMATE a kliknite na Add Variable. Dialóg vyplnte podla obr. 4.

Page 69: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Obr. 3 Clenská premenná dialógu priradená ovládaciemu prvku IDC_ANIMATE

Ked už ste v Class Wizarde, kliknite spät na kartu Message Maps a v rovnakej triede pridajte tlacidlu IDC_PLAY obsluhu správy BN_CLICKED a kliknite na Edit Code. Pridajte kód do tejto funkcie, ako vidíte tu: void CSpolDlg::OnPlay() { // otvoríme súbor s animáciou m_ctrlAnimate.Open("dillo.avi"); // prehráme animáciu m_ctrlAnimate.Play(0,0xFFFF,1); }

Toto riešenie predpokladá, že v adresári projektu máte súbor dillo.avi. (je dostupný na MSDN Library CD 1 ako súbor v projekte Cmnctrl1). Ak nemáte tento súbor, použite iný, musí však splnat podmienky uvedené v tab. 1 (podrobne vid. help). Skúste si na domácu úlohu pridat tento súbor do zdrojov (nový zdroj nazvite, ako chcete) a potom zmente volanie funkcie Open. Je to jednoduché.

Bežec: Zabudnite na posuvníky! Ak budete chciet urobit nieco také, co robili v BigDlg posuvníky IDC_ROCNIK a IDC_PROSPECH, knižnica MFC vám ponúka lepšie riešenie. Je ním bežec (slider), ktorý je lahšie „programovatelný“ ako posuvníky. Pre jeho „spojazdnenie“ dokonca nemusíte mapovat správy WM_HSCROLL a WM_VSCROLL (ak teda nechcete zobrazovat pozíciu bežca). My si naprogramujeme bežec, ktorý sa bude pohybovat po „skokoch“, ktoré mu my urcíme, a svoju pozíciu nám bude ukazovat rovnako ako pri posuvníkoch, plus jeho relatívnu pozíciu budeme vidiet aj na nom samotnom (tzv. Ticks). Pridajte pod tlacidlo IDC_PLAY jeden posuvník a hned vedla (napravo) jeden popis (na ukázanie hodnoty, ktorej zodpovedá aktuálna pozícia bežca). Do ID bežca napíšte IDC_BEZEC, na karte Styles zaškrtnite vlastnosti Tick marks a Auto ticks. ID popisu zmente na IDC_STATIC_BEZEC. Pomocou Class Wizardu pridajte clenskú premennú pre ovládací prvok Bezec, ktorá je typu integer, a nazvite ju m_nBezec(podobne ako pri animácii). Bude predstavovat pozíciu bežca. Kedže chceme, aby sa bežec pohyboval po nami urcených skokoch (dajme tomu, že ich bude 10), pridajte (už nie pomocou Class Wizardu) ešte do tohto súboru súkromné statické (to je dôležité) pole nSkok premenných typu integer (samotné „skoky“ nastavíme až v zdrojovom súbore). private: static int nSkok[10];

Tým by boli úpravy hlavickového súboru dokoncené. Ideme do zdrojového súboru dialógu. Na zaciatok (za direktívy preprocesora) naplnte pole urcujúce bežcove „skoky“. Napríklad takto: int nSkok[10]={10, 20, 30, 40, 50, 60, 70, 80, 90, 100};

Page 70: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Podobne ako pri posuvníkoch aj tu musíme zadeklarovat ukazovatel na tento ovládací prvok

a nastavit rozsah. Opät využijeme funkciu OnInitDialog, ktorá je na to najvhodnejšia. Zmente jej telo takto: BOOL CSpolDlg::OnInitDialog() { CString strText; CSliderCtrl* pBezec=(CSliderCtrl*) GetDlgItem(IDC_BEZEC); pBezec->SetRange(0, 9); pBezec->SetPos(m_nBezec); strText.Format("%d", nSkok[pBezec->GetPos()]); SetDlgItemText(IDC_STATIC_BEZEC, strText); return CDialog::OnInitDialog(); }

Aby sme v popise vedla bežca videli jeho presnú pozíciu, musíme namapovat obsluhu správy WM_HSCROLL a zmenit jej obslužnú funkciu takto: void CSpolDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { // aby sa aktualizoval // popis pozície // inak sa OnHScroll nemusí volat CString strText; CSliderCtrl* pBezec=(CSliderCtrl*) GetDlgItem(IDC_BEZEC); strText.Format("%d", nSkok[pBezec->GetPos()]); SetDlgItemText(IDC_STATIC_BEZEC, strText); }

Zoznam obrázkov: Zoznam obrázkov sám osebe velkú funkciu neplní, jeho „sila“ a „užitocnost“ sa prejaví až v spojení s prehliadacom zoznamov a stromom. Do prehliadaca obrázkov vkladáme ikony. Tieto ikony „taháme“ bud zo zdrojov (podla ID), alebo priamo z disku (pozri vyššie dillo.avi) (podla mena). My využijeme prvý spôsob. Vložte do zdrojov tri nové ikony (v Resources pravým tlacidlom na Icon a Insert Icon). Ak máte nejaké vhodné na disku, môžete ich pokojne importovat. Ak nepatríte medzi graficky zdatných alebo sa vám nechce kreslit nejaké perfektné ikony, vytvorte tri rozdielne ikony jednoducho tak, že každú vyplníte inou farbou. Pre jednoduchost zvolte „klasické“ výplne Red, Green, Blue a podla toho nazvite aj ID týchto ikon: IDI_RED, IDI_GREEN, IDI_BLUE. Teraz máme tri ikony – cervenú, zelenú a modrú – v tvare štvorca. Ked máme ikony, musíme ich ešte nejako „naprogramovat“. Pridajte jednu súkromnú premennú typu Zoznam obrázkov do triedy dialógu: private: CImageList m_ZoznamObr;

Teraz dopíšte tento kód do funkcie OnInitDialog (pod volanie funkcie SetDlgItemText (alebo pred, ak chcete), podmienkou je, aby ste kód pridali pred volanie funkcie CDialog::OnInitDialog()). BOOL CSpolDlg::OnInitDialog() {

// ... HICON hIkony[3];

m_ZoznamObr.Create(32, 32, ILC_COLOR4, 3, 3); hIkony[0]=AfxGetApp()->LoadIcon(IDI_RED); hIkony[1]=AfxGetApp()->LoadIcon(IDI_GREEN); hIkony[2]=AfxGetApp()->LoadIcon(IDI_BLUE);

Page 71: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

for (int i=0; i<3; i++) { m_ZoznamObr.Add(hIkony[i]); } return CDialog::OnInitDialog(); } Tým je zoznam obrázkov dokoncený.

Prehliadac zoznamov: Ako sme povedali, zoznam obrázkov využijeme až v spojení s prehliadacom zoznamov, prípadne stromom. Pridajte do dialógu podla obr. 3 jeden prehliadac zoznamov (ListControl). Jeho vlastnosti nastavte, ako vidíte na obr. 5. ID nechajte na default: IDC_LIST1. Aby sme mohli sledovat, akú položku máme oznacenú, pridajte pod prehliadac aj jeden popis. Do jeho ID napíšte: IDC_STATIC_LIST a vymažte text z jeho Caption.

Obr. 4 Vlastnosti prehliadaca zoznamov Pre prepojenie prehliadaca zoznamov so zoznamom obrázkov pridajte ešte tento kód do funkcie OnInitDialog: CListCtrl* pList=(CListCtrl*) GetDlgItem(IDC_LIST1); pList->SetImageList(&m_ZoznamObr, LVSIL_SMALL); pList->InsertItem(0, "cervená", 0); pList->InsertItem(1, "zelená", 1); pList->InsertItem(2, "modrá", 2);

Pre obmenu textu v popise podla vybranej položky zo zoznamu je potrebné pomocou Class Wizardu namapovat správu prehliadaca zoznamov: LVN_ITEMCHANGED. Jej kód upravte takto: void CSpolDlg::OnItemchangedList1(NMHDR* pNMHDR, LRESULT* pResult) { CString strPolozka; int nVyber; NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR; CListCtrl* pList = (CListCtrl*) GetDlgItem(IDC_LIST1); // iItem je clenská premenná // štruktúry NM_LISTVIEW nVyber = pNMListView->iItem; strPolozka = pList->GetItemText(nVyber, 0); SetDlgItemText(IDC_STATIC_LIST, strPolozka); *pResult = 0; }

Strom: Juj, ako som písal v úvode, musím sa krotit ?. CTreeCtrl* pTree = (CTreeCtrl*) GetDlgItem(IDC_TREE1);

Page 72: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

TV_INSERTSTRUCT tvinsert; tvinsert.hParent = NULL; tvinsert.hInsertAfter = TVI_LAST; tvinsert.item.mask = TVIF_TEXT; tvinsert.item.hItem = NULL; tvinsert.item.state = 0; tvinsert.item.stateMask = 0; tvinsert.item.cChildren = 0; tvinsert.item.lParam = 0; tvinsert.item.pszText = "Otec"; HTREEITEM hOtec = pTree ->InsertItem(&tvinsert); tvinsert.item.pszText = "Mama"; HTREEITEM hMama = pTree->InsertItem(&tvinsert); tvinsert.hParent = hOtec; tvinsert.item.pszText = "Dcéra_1"; pTree->InsertItem(&tvinsert); tvinsert.item.pszText = "Syn_1"; pTree->InsertItem(&tvinsert); tvinsert.hParent = hMama; tvinsert.item.pszText = "Dcéra_2"; pTree->InsertItem(&tvinsert); ---CUT--- void CSpolDlg::OnSelchangedTree1(NMHDR* pNMHDR, LRESULT* pResult) { NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR; CTreeCtrl* pTree = (CTreeCtrl*) GetDlgItem(IDC_TREE1); HTREEITEM hSelected = pNMTreeView->itemNew.hItem; if (hSelected != NULL) { char text[15]; TV_ITEM item; item.mask = TVIF_HANDLE | TVIF_TEXT; item.hItem = hSelected; item.pszText = text; item.cchTextMax = 30; VERIFY(pTree->GetItem(&item)); SetDlgItemText(IDC_STATIC_TREE, text); } *pResult = 0; } DOMÁCA ÚLOHA. Doma si skúste naprogramovat aj iné spolocné ovládacie prvky (císelník, kalendár) a vyskúšajte si ich rôzne nastavenia a vlastnosti. Skúste si sami (bez informácií z našej webovej stránky) naprogramovat strom len s pomocou helpu. Aj ked budete možno neúspešní, vela sa naucíte! Vytvorte si lepší zoznam obrázkov, ktorý použite namiesto tu vytvoreného v prehliadaci zoznamov a pripojte ho aj k stromu. NABUDÚCE. O mesiac nemodálnymi dialógmi definitívne skoncíme túto tému (ved už bolo nacase) a povieme si nieco o tvorbe menu. 14. cast: Dialógy – dokoncenie

Page 73: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Leto sa nám pomaly koncí, pre školopovinných sa zacína škola, pre „prácopovinných“ sa skoncil cas dovoleniek a zase je tu ten známy kolotoc. Ráno do školy/práce, poobede zo školy a vecer/v noci ? z práce domov. V sobotu ešte stihnút, na co nebol cas cez pracovný týžden, v nedelu poobede sa pripravit na pondelok a vecer s frflaním, že opät sme si nenašli cas „na seba“, zaspávat. Dúfam, že aj napriek tejto casovej „kompresii“ si predsa len nájdete chvílku pre seba a v pokoji si precítate oblúbený casopis (PC REVUE) a nezabudnete ani na svoj oblúbený seriál ?. NEMODÁLNE DIALÓGY. Ak si ešte spomeniete, v deviatej casti sme si vysvetlili základný rozdiel medzi modálnymi a nemodálnymi dialógmi. Nemodálne dialógy na rozdiel od modálnych umožnujú používatelovi dalej pracovat v aplikácii a „nezastavia“ tak jej beh. Toto je pohlad používatela. Co sa týka pohladu programátora, ten bude zrejme o nieco kritickejší. Nemodálne dialógy sa totiž trochu tažšie programujú. Môžu, samozrejme, obsahovat všetky tie ovládacie prvky ako modálne dialógy, ale nemodálne dialógy sa tažšie vytvárajú, rušia a celá komunikácia medzi nimi a aplikáciou je trochu odlišná. Prvý rozdiel, ktorý nám bije do ocí hned na zaciatku, je použitie funkcie Create namiesto funkcie DoModal. Medzi týmito dvoma funkciami je jeden podstatný rozdiel. Kým funkcia DoModal sa skoncí zavretím dialógu (ked ukoncíme dialóg, je jedno ci OK, Cancel alebo ALT + F4), funkcia Create sa skoncí hned, pricom dialóg zostane stále na obrazovke a je nám ponechaný takpovediac napospas. My sa musíme postarat o jeho zrušenie. A teraz si pravdepodobne položíte takéto otázky: Ved za ten cas, co bol dialóg aktívny, sa mohli „podmienky“ v aplikácii zmenit, a komu a ako my pošleme správu, že rušíme dialóg? Ako sa vôbec aplikácia dozvie, že sme klikli na nejaké tlacidlo dialógu? Kto v skutocnosti vlastní dialóg? Je to pohlad? Odpovede na ne sú v ukážkovom príklade. PRÍKLAD 1. Namiesto dalšej teórie si ukážeme „programovanie“ nejakého nemodálneho dialógu. Spustte AppWizard, vytvorte nový projekt s menom Nemodalny. Všetky jeho nastavenia nechajte default, ale zvolte aplikáciu SDI. Tento náš nemodálny dialóg bude robit presne to isté, co dialóg z projektu Vymena z desiatej casti (prenášat dáta medzi dvoma EditBoxami). Ak máte vytvorenú novú kostru aplikácie, pridajte nový dialóg do zdrojov (ID nechajte default: IDD_DIALOG1). Podla desiatej casti pridajte aj všetky ovládacie prvky (t. j. dva popisy, dva EditBoxy, jedno „kopírovacie“ tlacidlo). Ich ID tiež vyplnte podla desiatej casti. Okrem týchto ovládacích prvkov nechajte na dialógu aj tlacidlá OK a Cancel, aby sme si na nich ukázali další rozdiel medzi modálnymi a nemodálnymi dialógmi. (Poznámka: Dúfam, že ste si uvedomili: ak nastavujete tlacidlo IDC_COPY ako default button, musíte túto vlastnost odznacit na tlacidle IDOK). Kompletný „screen“ dialógu aplikácie Nemodalny vidíte na obr. 1.

Obr. 1 Nemodálny dialóg Vymena Teraz vytvorte novú triedu pre dialóg. Nazvite ju CNemodDialog. Pomocou Class Wizardu pridajte ovládacím prvkom premenné (ako v 10. casti – IDC_GETNAME>m_editGetName>CString, IDC_SHOWNAME>m_editShowName>CString a IDC_COPY>m_btnCopy>CButton). Tlacidlu m_btnCopy pridajte obsluhu správy BN_CLICKED (do triedy dialógu). Pridajte do nej tento kód: void CNemodDialog::OnCopy() { //skopírujeme dáta z ovládacích prvkov do premenných UpdateData(TRUE); m_editShowName=m_editGetName; //skopírujeme dáta z premenných do ovládacích prvkov UpdateData(FALSE); }

Do triedy pohladu tejto aplikácie pridajte obsluhu správy WM_LBUTTONDOWN a zmente kód funkcie OnDraw (hádam ho už ani nemusím písat, prípadne pozrite projekt Subor dalej). Tak

Page 74: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

a odteraz to zacne byt zaujímavé. Zacneme pridávat prvky potrebné na zobrazenie dialógu Vymena ako nemodálneho dialógu. Najprv musíme napísat konštruktor takéhoto dialógu. Pridajte preto nasledujúci kód do zdrojového súboru triedy dialógu (pôvodný konštruktor nemusíte mazat): CNemodDialog::CNemodDialog(CView* pView) { m_editGetName = _T(""); m_editShowName = _T(""); m_pView=pView; }

Práve parameter konštruktora prekladacu hovorí, ci ide o modálny alebo o nemodálny konštruktor (presnejšie, ci konštruktor „konštruuje“ modálny alebo nemodálny dialóg). Ak je jeho parametrom ukazovatel na pohlad (CView a od neho odvodené), pôjde o nemodálny konštruktor, ak je jeho parametrom ukazovatel na „okno“ (CWnd a od neho odvodené), pôjde o modálny konštruktor. Do hlavickového súboru tejto triedy pridajte tieto deklarácie: private: CView* m_pView; public: CNemodDialog(CView* pView);

Pre nemodálne dialógy musíte prepísat štandardné funkcie CDialog::OnOK a CDialog::OnCancel. Keby ste tak neurobili, po zavretí dialógu (kliknutie na OK, Cancel, stlacenie ESC, Enter alebo ALT+F4 v dialógu) by sa vyvolali tieto štandardné funkcie triedy CDialog, ktoré dalej volajú funkciu EndDialog. Táto funkcia je však urcená len pre modálne dialógy a pre nemodálne sa nemôže použit. Pre ne je urcená funkcia DestroyWindow (ved si skúste neprepísat tieto funkcie a uvidíte sami, co sa stane). Namapujte preto do triedy dialógu na tlacidlá OK a Cancel správu BN_CLICKED a kód obslužných funkcií zmente takto: void CNemodDialog::OnOK() { DestroyWindow(); // NESMIE sa volat //CDialog::OnOK(); } void CNemodDialog::OnCancel() { DestroyWindow(); // NESMIE sa volat //CDialog::OnCancel(); }

Tieto funkcie umožnia korektné zavretie dialógu. Presunme sa teraz do súborov triedy pohladu, z ktorých budeme dialóg vyvolávat. Pridajte jednu deklaráciu do hlavickového súboru triedy pohladu, aby sme mohli k dialógu pristupovat: private: CNemodDialog* m_pNdlg;

Aby ste nemuseli zbytocne includovat hlavickový súbor triedy dialógu, pridajte nasledujúci riadok na zaciatok hlavickového súboru triedy pohladu: class CNemodDialog;

V zdrojovom súbore triedy pohladu sa však už includovaniu tohto súboru nevyhneme:

Page 75: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

#include "NemodDialog.h"

Zmente konštruktor a deštruktor pohladu: CNemodalnyView::CNemodalnyView() { // vytvoríme objekt dialógu m_pNdlg=new CNemodDialog(this); } CNemodalnyView::~CNemodalnyView() { // zruší okno dialógu delete m_pNdlg; } A nakoniec pridajte nasledujúci kód do funkcie OnLButtonDown: void CNemodalnyView::OnLButtonDown(UINT nFlags, CPoint point) { // zistíme, ci už náhodou // dialóg nebol vytvorený if (m_pNdlg->GetSafeHwnd()==0) { // ak nie, zobrazí okno dialógu m_pNdlg->Create(IDD_DIALOG1); } }

To je všetko. Vyskúšajte si túto aplikáciu. Ak ste nastavili dialógu vlastnost Visible, potom po stlacení lavého tlacidla myši sa zobrazí nemodálny dialóg Vymena. O tom, že skutocne ide o nemodálny dialóg, sa môžete presvedcit tak, že po vyvolaní dialógu vám bude umožnené dalej pracovat s aplikáciou. SPOLOCNÉ DIALÓGY WINDOWS. Spolocné dialógy Windows sú založené na podobnom princípe ako spolocné ovládacie prvky Windows. Ich kód je uložený v súbore commdlg.dll (pre spolocné ovládacie prvky to bol súbor commctl.dll (pre Windows ME, pre NT comctl32.dll pre ovládacie prvky a comdlg32.dll pre dialógy)). Všetky spolocné dialógy Windows nájdete v tab. 1 spolu s krátkym opisom a miestom „výskytu“ v niektorej aplikácii (obsahuje cestu v menu).

Trieda MFC Opis Miesto výskytu CfileDialog Otvorit/uložit súbor Word -> Súbor -> Otvorit CFontDialog Umožnuje zmenu fontu Word -> Formát -> Písmo

(len prvá karta: Písmo) CColorDialog Výber/vytvorenie farby Skicár (Malovanie) -> Farby

-> Upravi t farby CPageSetupDialog

Nastavenie stránky Notepad -> Súbor -> Nastavenie strany

CPrintDialog Dialóg tlace (nastavenie tlaciarne a tlac) WordPad -> Súbor -> Tlaèi• CFindReplaceDialog

Vyhladá a príp. zamení hladaný retazec VisualC++ -> Edit -> Replace

COleDialog Obsahuje OLE dialógy , napr. Vložit inak (Excel) alebo Vložit objekt (Word, Excel) a dalšie

Tab. 1 Spolocné dialógy Windows Je dobré si uvedomit, že tieto dialógy len zhromaždujú informácie od používatela, ale nic s nimi dalej nerobia. Napríklad dialóg otvorenia súboru síce zistí, aký súbor sa má otvorit, ale neotvorí ho. Rovnako dialóg tlace zistí informácie o tlaciarni, ale samotný dokument nevytlací. Prácu s asi najrozšírenejším FileDialogom si ukážeme na príklade, použitie ostatných spolocných dialógov je podobné. PRÍKLAD 2. Cielom tohto ukážkového príkladu nebude nic zložité: po stlacení lavého tlacidla myši v okne aplikácie vyvolat dialóg Otvorit súbor a potom pomocou MessageBoxu vypísat meno

Page 76: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

vybraného súboru. Spustte AppWizard a vytvorte nový projekt s názvom Subor. Všetky hodnoty nechajte štandardne nastavené, iba zvolte aplikáciu SDI. V súbore SuborView.cpp potom zmente kód funkcie OnDraw: void CSuborView::OnDraw(CDC* pDC) { pDC->TextOut(0, 0, "Kliknite sem na vyvolanie dialógu: Otvorit súbor"); }

Do tejto triedy (CSuborView) pridajte aj obsluhu správy WM_LBUTTONDOWN. Jej obslužnú funkciu zmente takto: void CSuborView::OnLButtonDown(UINT nFlags, CPoint point) { // konštrukcia objektu dialógu CFileDialog Fdlg(TRUE, NULL, "*.cpp"); if (Fdlg.DoModal()==IDOK) { MessageBox(Fdlg.GetPathName(), "Vybrali ste súbor:", MB_OK); } }

To je všetko. Teraz po stlacení lavého tlacidla myši v okne aplikácie sa zobrazí dialóg Otvorit súbor (prvý parameter konštruktora – TRUE: Otvorit súbor, FALSE: Uložit ako). Ak vyberiete nejaký súbor, dialóg sa zavrie a zobrazí sa informacný dialóg podobný tomu na obr. 2.

Obr. 2 Meno vybraného súboru TIPY NA DOMA. Skúste rozšírit aplikáciu Nemodalny tak, aby sa po kliknutí lavým tlacidlom do okna aplikácie vyvolala nemodálna verzia dialógu Vymena a po kliknutí pravým tlacidlom zase modálna verzia (nápoved: ak ide o modálny dialóg, tak m_pView=NULL). Po zavretí dialógu vypíšte kopírovaný text do okna aplikácie. Pokúste sa zistit, ci po zavretí okna nemodálneho dialógu sa tiež zruší jeho objekt (ale skúste sa, prosím, nepozriet najprv do helpu ?) . Rozšírte aplikáciu tak, aby sa zakaždým, ked kliknete lavým tlacidlom do okna, vyvolala dalšia kópia nemodálneho dialógu Vymena. Naprogramujte si aj iné spolocné dialógy Windows. Napríklad pri dialógu výber farby sa pokúste zistit, akú farbu si používatel v dialógu vybral. FAQ Q: Ako sa dajú preniest dáta medzi dvoma dialógmi? A: Dáta medzi dvoma dialógmi sa dajú vymenit napríklad využitím nejakej pomocnej premennej, ktorú zadefinujeme v triede, z ktorej vyvolávame tieto dialógy [štandardne je to trieda pohladu, skutocným vlastníkom dialógov je však hlavné rámcové okno aplikácie (obsahuje titulkový pruh a pruh menu, toolbar atd., podrobnejšie v dalšej casti seriálu)]. Príklad: Máme dva dialógy, kde jeden sa aktivuje po stlacení lavého tlacidla a druhý po stlacení pravého tlacidla myši. My chceme, aby si vymenili pomocou premennej globalna dáta zo svojich textových polí (edit boxov). Zadeklarujeme preto premennú globalna ako verejnú clenskú premennú triedy pohladu typu CString. Obslužné funkcie správ WM_RBUTTONDOWN a WM_LBUTTONDOWN potom zmeníme takto: void CViacDokView::OnLButtonDown(UINT nFlags, CPoint point) { CDialog1 dlg1; dlg1.DoModal();

Page 77: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

globalna=dlg1.m_strDlg; } void CViacDokView::OnRButtonDown(UINT nFlags, CPoint point) { CDialog1 dlg1; CDialog2 dlg2; dlg2.m_strDlg2=globalna; dlg2.DoModal(); } NABUDÚCE. O mesiac zacneme pracovat aj s triedou dokumentu a vytvoríme si menu. Prajem pekné prežitie septembra. 15. cast: Menu, ToolBar, StatusBar Kým v predchádzajúcej casti som písal, že leto sa nám pomaly koncí, teraz už môžem bez pochýb povedat, že leto je nadobro v tahu. Ale ved ani jesen nie je zlá. Pre optimistov znamená krásnu vônu pestro sfarbeného popadaného lístia. Pre pesimistov zase neporiadok na chodníkoch, zaprícinený týmto popadaným a nepríjemne šuštiacim lístím a vela upršaných dní. Dúfam, že väcšina z vás patrí do tej prvej skupiny, programovanie a vývoj aplikácií si totiž casto vyžaduje trocha zidealizovaný pohlad na vec, aby nás z toho nacisto neporazilo. ? MENU, TOOLBAR, STATUSBAR. Náš prehlad tvorby GUI (grafického používatelského rozhrania) zakoncíme, ked si povieme o týchto troch neodmyslitelných súcastiach každej aplikácie Win. Co je Menu, to je azda každému jasné; z pohladu MFC je to objekt odvodený od triedy CMenu. Táto trieda má v sebe aj podporu viacúrovnových, pop-up a plávajúcich pop-up menu. ToolBar ciže panel nástrojov je z pohladu MFC objekt odvodený od triedy CToolbar, ktorá je potomkom triedy CControlBar (spolocnej triedy pre všetky objekty ovládacích prvkov líšt – bars) a tá je potomkom triedy CWnd (z toho vyplýva, že ToolBar a StatusBar sú okná). Presnejšie ToolBar je okno, ktoré umožnuje zobrazit na sebe tlacidlá, kliknutím na ktoré vyvoláme nejakú operáciu. Kliknutie na položku v ToolBare má rovnaký úcinok ako výber nejakej položky v menu (generujú sa príkazové správy – podrobnejšie neskôr). StatusBar alebo stavový riadok slúži na zobrazovanie stavových informácií. Môže to byt lubovolný text, napr. help k položkám v menu, súradnice kurzora myši a pod. Je to opät okno, ale na rozdiel od ToolBaru nepodporuje vstup od používatela ani negeneruje nijaké správy alebo operácie. Prevažne slúži len na zobrazenie textu, prípadne ikon. Je to objekt odvodený od triedy CStatusBar, ktorá má rovnakých predkov ako trieda CToolBar. V prípade Menu a ToolBaru je ich vytváranie v réžii editora zdrojov. Jediné, co musíme doprogramovat, je obsluha správ, ktoré môžu vzniknút pri práci s nimi. SPRACOVANIE PRÍKAZOVÝCH SPRÁV. Príkazové správy sme už v strucnosti opisovali a už viete, že vznikajú ako reakcia na vybratie položky v menu, stlacenie tlacidla v paneli nástrojov a pri stlacení klávesovej skratky (použití akcelerátora). Väcšina takýchto správ pochádza z rámcového okna, ktorému koniec koncov patrí menu aj panel nástrojov. Co však v takom prípade, ked chceme príkazovú správu obslúžit v triede pohladu alebo dokumentu? Aplikacný systém so svojou architektúrou smerovania príkazov (command routing architecture) nám to, samozrejme, umožnuje. Ked zachytí príkazovú správu, jej obslužnú funkciu hladá v tomto poradí v triedach: Pohladu -> Dokumentu -> Hlavného rámcového okna SDI -> Aplikácie (platí pre aplikácie SDI, o MDI neskôr). Použitie príkazových správ nám opät výrazne ulahcuje ClassWizard, ktorý tento proces úplne automatizuje. Jediné, co musíme urobit, je napísat kód obslužnej funkcie. PRÍKLAD. Teraz si na príklade ukážeme vytváranie a programovanie opísaných prvkov. Vytvoríme aplikáciu, ktorá po výbere položky v menu alebo po stlacení príslušného tlacidla panela nástrojov zobrazí dialóg bud na zadanie mena, alebo priezviska. Zadaný text sa potom zobrazí v stavovom riadku. Pridáme funkcnost aj pre plávajúce pop-up menu. Pomocou AppWizardu vytvorte nový projekt s názvom GUI. Jeho nastavenia vidíte na obr. 1. (všetko default, ibaže SDI).

Page 78: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Obr. 1 Informácie o novom projekte

Pomocou editora zdrojov vytvorte dva zdroje dialógov (ID nechajte default: IDD_DIALOG1 – zadanie mena a IDD_DIALOG2 – zadanie priezviska). Ovládacie prvky pridajte podla obr. 2 a 3, na ktorých sú oba dialógy. Všetky ID a vlastnosti okrem Edit Boxov nechajte default. V IDD_DIALOG1 zmente ID tohto textového pola na IDC_MENO a v druhom dialógu na IDC_PRIEZVISKO.

Obr. 2 Dialóg na zadanie mena

Obr. 3 Dialóg na zadanie priezviska

Page 79: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Obr. 4 Vytvorenie novej triedy pre zdroj dialógu

Obr. 5 Zmenenie mien súborov pre triedy CGUIDialogM a CGUIDialogP

Teraz spustte ClassWizard a vytvorte nové triedy postupne pre oba dialógy. Pre IDD_DIALOG1 bude mat trieda názov CGUIDialogM a pre IDD_DIALOG2 to bude trieda s názvom CGUIDialogP. Kedže ide o jednoduché dialógy, do ktorých nejdeme pridávat nejakú super funkcnost, môžu mat spolocný hlavickový aj implementacný súbor. Preto v dialógu vytvárania novej triedy pre oba zdroje v textovom poli File Name zvolte Change... a zadajte: GUIDialogs.h pre hlavickový a GUIDialogs.cpp pre zdrojový súbor. (obr. 4 a 5). Ak sa vám po pridaní náhodou tieto dve triedy nezobrazia v pohlade ClassView, potom z menu Project vyberte Add to Project, potom Files a nájdite a pridajte súbory GUIDialogs.h a GUIDialogs.cpp.

Ked už máme pridané dialógy, už nám ostáva len pridat nieco, co by ich vyvolalo. Zacneme s Menu.

Krok 1 – úprava Menu: V editori zdrojov v „zložke“ Menu dvakrát kliknite na zdroj menu IDR_MAINFRAME. Kliknite na položku menu Help a stlacte kláves Insert. Tým pridáte dalšiu položku do menu. Dvakrát na nu kliknite (resp. pravé tlacidlo a Properties) a do pola Caption napíšte text: &GUI (ampersand má v tomto prípade rovnaký úcinok ako pri ovládacích prvkoch dialógov – pozri 11. cast seriálu). Všetky ostatné vlastnosti nechajte default. Pod položku GUI pridajte dalšiu s Caption: &Dialógy. Zaškrtnite jej vlastnost Pop-up. Ako pop-up menu k tejto položke vložte dve položky, ktorých Caption je: &Meno a &Priezvisko. Ich vlastnosti nechajte na default, ibaže vo vlastnostiach na záložke General vpíšte do pola Prompt pre Meno: Zobrazí dialóg so zadaním mena a pre Priezvisko: Zobrazí dialóg so zadaním priezviska (obr. 6). Tento text sa bude zobrazovat v StatusBare ako nápoved k týmto položkám menu.

Page 80: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Obr. 6 Vlastnosti položky v menu

Na obr. 7 vidíte celú štruktúru položiek menu GUI v editori zdrojov.

Obr. 7 Štruktúra položiek v menu

Nech vás netrápi, že v ID položiek Meno a Priezvisko je namiesto DIALÓGY len DIALGY. V ID totiž nemôžu byt takéto špeciálne znaky (akým je v tomto prípade dlžen). Je to síce len kozmetická chybicka, ale pri vývoji softvéru v našich podmienkach si na to treba zvyknút.

Ked už máme vizuálne veci za sebou, musíme ešte pridat nejaký kód na „spojazdnenie“ nových položiek menu. Pravým tlacidlom kliknite na Meno a z plávajúceho pop-up menu (áno, to je to plávajúce menu spomenuté na zaciatku, ktoré si aj my neskôr naprogramujeme) vyberte ClassWizard. Pridajte tejto položke Menu obsluhu príkazovej správy, ktorá je reakciou na kliknutie alebo „odenterovanie“ tejto položky (na COMMAND a Add Function). Túto funkcnost pridajte do triedy CMainFrame (skúste si doma pridat obsluhu týchto príkazových správ aj do triedy pohladu, odskúšajte funkcnost a potom ich pridajte aj do triedy Dokumentu. Funguje to rovnako? Je v tom nejaký rozdiel? Skúste nad tým porozmýšlat, viac informácií v casti spracovanie príkazových správ). Názvy obslužných funkcií nechajte ponúkané. Ich kód zmente, ako vidíte tu: void CMainFrame::OnGuiDialgyMeno() { CGUIDialogM dlgM; dlgM.DoModal(); } void CMainFrame::OnGuiDialgyPriezvisko() { CGUIDialogP dlgP; dlgP.DoModal(); }

Samozrejme, ešte nezabudnite pridat na zaciatok súboru MainFrm.cpp túto include direktívu: #include "GUIDialogs.h"

Projekt preložte a spustite. Vyskúšajte funkcnost nových položiek menu (vrátane spomínanej nápovede v stavovom riadku).

Krok 2 – úprava ToolBaru: Znova sa vrátte do editora zdrojov a v „zložke“ Toolbar dvakrát kliknite na zdroj IDR_MAINFRAME. Tým spustíte editor. V nom na konci bitmapy panela nástrojov vidíte prázdne polícko. Trochu ho myšou posunte doprava, aby bolo opticky oddelené od predchádzajúcich. Pomocou editora don vpíšte M (po kliknutí na toto tlacidlo toolbaru sa zobrazí dialóg na zadanie Mena). Vedla tohto tlacidla sa objavilo dalšie prázdne a don vpíšte P.

Page 81: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Obr. 8 ToolTip pri tlacidlách panela nástrojov Ak dvakrát kliknete na niektoré tlacidlo panela nástrojov (ešte stále sa, samozrejme, nachádzame v editori), zobrazia sa jeho vlastnosti. Do polícka ID vpíšte rovnaké ID ako v prípade položiek menu (pre tlacidlo M bude ID: ID_GUI_DIALGY_MENO a pre tlacidlo P bude ID: ID_GUI_DIALGY_PRIEZVISKO). Ako som v úvode spomínal, aplikacný systém smeruje príkazové správy z menu a panela nástrojov rovnakým spôsobom (smerom), a preto môžeme použit rovnaké obslužné funkcie, co v tomto prípade zabezpecíme rovnakým ID. Tým, že sme zvolili rovnaké ID, aj nápoved v stavovom riadku bude rovnaká a už ju nemusíme zbytocne prepisovat. Pre panel nástrojov existuje ešte takzvaná ToolTip nápoved (ak sa myšou zastavíte nad niektorým tlacidlom ToolBaru, zobrazí sa zvycajne žltý pravouholník s textom, ktorý objasnuje funkciu tlacidla). Túto „funkcnost“ pridáte jednoducho tak, že vo vlastnostiach tlacidla do pola Prompt pridáte za nápoved, ktorá sa zobrazuje v stavovom riadku, znak nového riadka \n a za ním text ToolTipu (obr. 8). V našom príklade pre tlacidlo M pridajte ToolTip: Dialóg meno a pre tlacidlo P zase Dialóg priezvisko. Kontrolná otázka: Myslíte si, že sa zmenil Prompt aj vo vlastnostiach položiek Menu? Skúste najprv odpovedat a až potom sa pozriet.

Krok 3 – úprava StatusBaru: Dostali sme sa do predposlednej fázy úpravy projektu GUI. V nej spojíme dialógy pre zadanie mena a priezviska so stavovým riadkom, na ktorom zobrazíme text zadaný v týchto dialógoch. Stavový riadok je definovaný v súbore MainFrm.cpp v statickom poli indicators. V našom prípade definíciu predstavuje kód: static UINT indicators[] = { ID_SEPARATOR, // status line indicator ID_INDICATOR_CAPS, ID_INDICATOR_NUM, ID_INDICATOR_SCRL, };

Indikátory CAPS, NUM a SCRL sa aktivujú, ked stlacíme príslušný kláves (ak ste sa s tým ešte nestretli, spustte aplikáciu GUI a vyskúšajte si to). Pre naše potreby nám budú stacit len dve „prázdne“ miesta v stavovom riadku, pricom v prvom (má index 0 a je vlavo) sa bude zobrazovat text z dialógu Meno a v druhom text z dialógu Priezvisko. Zmente preto definíciu stavového riadka takto: static UINT indicators[] = { ID_SEPARATOR, ID_SEPARATOR, };

Pomocou ClassWizardu pridajte textovým poliam v dialógoch premenné, ako vidíte v tab. 1.

ID ovládacieho prvku Clenská premenná Typ Max. dlžka IDC_MENO m_strMeno CString 10 IDC_PRIEZVISKO m_strPriezvisko CString 15 Tab. 1 Clenské premenné dialógov Teraz už len pridajte tento riadok do funkcie OnGuiDialgyMeno: m_wndStatusBar.SetPaneText(0, dlgM.m_strMeno);

Page 82: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

a do funkcie OnGuiDialgyPriezvisko: m_wndStatusBar.SetPaneText(1, dlgP.m_strPriezvisko);

Samozrejme, že tento kód musíte pridat až za volanie funkcie DoModal. Premenná m_wndStatusBar je deklarovaná v hlavickovom súbore triedy CMainFrame ako premenná typu stavový riadok (CStatusBar m_wndStatusBar). To je všetko, po spustení aplikácie sa bude zobrazovat v lavej casti StatusBaru meno a v pravej priezvisko. Ako však uvidíte, tieto dva panely StatusBaru nie sú velmi esteticky usporiadané. Lavý je akýsi vypuklý a väcší ako pravý. To hned napravíme. Pridajte tieto dva riadky do súboru MainFrm.cpp niekde ku koncu funkcie OnCreate: m_wndStatusBar.SetPaneInfo(0, 0, 0, 300); m_wndStatusBar.SetPaneInfo(1, 0, 0, 300);

Tým sme nastavili rovnaký vzhlad a rozmery pre oba panely stavového riadka (viac o funkciách stavového riadka sa docítate v helpe). Krok 4 – pridanie plávajúceho pop-up menu: Ako som písal na zaciatku, trieda CMenu má v sebe obsiahnutú podporu aj pre takýto typ menu (štandardne sa toto menu používa, ak kliknete pravým tlacidlom myši do klientskej oblasti okna). Pomocou editora zdrojov vložte nový zdroj menu do projektu (postupujte rovnako ako pri vkladaní nového dialógu). Nový zdroj nazvite IDR_MYMENU. Jeho položky vyplnte podla obr. 9. Kedže aj tu platí rovnaké smerovanie príkazových správ ako v klasickom menu a ToolBare, ID položiek Meno a Priezvisko zvolte rovnaké ako v predchádzajúcom prípade. „Oddelovac“ (Separator, horizontálna ciara) vložíte do menu tak, že pri vkladaní novej položky menu zaškrtnete vlastnost Separator na karte General (do pola Caption nemusíte nic vpisovat). Pre položku About... napíšte jej ID: ID_APP_ABOUT, co je opät rovnaké ako v prípade klasického menu pre položku Help -> About GUI... Teraz pomocou ClassWizardu pridajte zdroj IDR_MYMENU do už existujúcej triedy CMainFrame a pridajte obsluhu správy WM_CONTEXMENU takisto do triedy CMainFrame. Kód jej obslužnej funkcie zmente takto: void CMainFrame::OnContextMenu(CWnd* pWnd, CPoint point) { CMenu MyMenu; MyMenu.LoadMenu(IDR_MYMENU); MyMenu.GetSubMenu(0)->TrackPopupMenu(TPM_CENTERALIGN | TPM_RIGHTBUTTON, point.x, point.y, this); }

Opis parametrov nosnej funkcie TrackPopupMenu nájdete v helpe. Výsledné menu vidíte na obr. 10.

Obr. 9 Položky plávajúceho menu

Obr. 10 Plávajúce pop-up menu

Page 83: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

TIPY NA DOMA. Prepíšte aplikáciu GUI tak, aby dialógy na zadanie mena aj priezviska boli nemodálne. Ked ich takto zmeníte, doprogramujte aj zaškrtnutie tej položky menu, ktorej dialóg bol vyvolaný. Zaškrtnutie sa robí namapovaním správy UPDATE_COMMAND_UI pri položkách menu. V tele tejto funkcie sa potom zaškrtnutie robí pomocou funkcie CCmdUI::SetCheck (podrobnejšie v helpe). Existujú aj dalšie funkcie, napríklad CCmdUI::Enable, ktorá dokáže sprístupnit, resp. zneprístupnit položku v menu. Pozrite sa na tieto funkcie, v praxi sa dost casto využívajú. Už nezostal priestor na opísanie akcelerátorov (klávesových skratiek). Pozrite sa na prácu s nimi, je jednoduché ich používat a aj programovat (vo väcšine prípadov si vystacíte s editorom zdrojov). Naprogramujte akcelerátory do našej aplikácie pre nami pridané položky menu. Opät platí použitie rovnakých ID. Upravte StatusBar tak, aby mal tri položky. V prvej (index 0) sa bude zobrazovat help k položkám menu a tlacidlám ToolBaru, v druhej text z dialógu meno a v tretej text z dialógu priezvisko. Tým opravíte prepisovanie textu dialógu meno nápovedou a inými informáciami, ktoré aplikácia zobrazuje štandardne v prvej položke. NABUDÚCE. Slubovanú prácu s triedou dokumentu si odložíme do budúcej casti. Dovidenia v novembri. 16. cast: Document/View a ladenie Opät sa stretávame pri dalšom, už šestnástom pokracovaní seriálu. Tentoraz si podrobnejšie preberieme interakciu dokument/pohlad a povieme si nieco o pokrocilej ladiacej technike, tzv. dumpingu. DOCUMENT/VIEW. Ked sme sa zacínali zaoberat knižnicou MFC, uviedli sme si aj základné informácie o dokumentoch a pohladoch. Už vieme, že dokument tvorí obsah toho, co sa zobrazí v pohlade . Z toho vyplýva, že pohlad závisí od dokumentu (naopak to neplatí). Môže tiež existovat viacero pohladov na jeden dokument [otázka na zamyslenie: Platí to aj naopak? Môže existovat viacero dokumentov, ktoré majú jeden pohlad? Odpoved nájdete v helpe (zadajte v indexe document/view), ale skúste najprv docítat túto cast do konca a odpovedat si sami]. V programoch MFC predstavuje dokument trieda odvodená od triedy MFC Cdocument (alebo jej potomkov) a pohlad predstavuje trieda odvodená od triedy Cview (alebo jej potomkov). Ked programujete aplikáciu MFC, casto potrebujete vymenit dáta medzi dokumentom a pohladom alebo zmenit pohlad, ked sa zmenili dáta v dokumente (co je logické). Na tieto operácie nám slúžia viaceré funkcie, ktoré si teraz opíšeme.

CView::GetDocument – táto funkcia vracia ukazovatel na dokument, a tým nám pomocou neho umožnuje pristupovat z pohladu k clenským funkciám alebo premenným dokumentu. Nevracia však ukazovatel na CDocument, ale konkrétne na náš objekt dokumentu, ktorý je odvodený od Cdocument (majme napríklad triedu class CMojDokument : public CDocument{…}, potom funkcia GetDocument vráti ukazovatel na CMojDokument, podrobnejšie pozri funkciu GetDocument v zdrojovom súbore triedy pohladu v príklade Študenti).

CDocument::UpdateAllViews – funkcia UpdateAllViews informuje pohlady (ci všetky, to závisí od jej parametra, podrobne v helpe), že dáta v dokumente sa zmenili a bolo by dobré, keby aktualizovali pohlad na tieto dáta (napríklad ak pohladom je graf, bude prekreslený podla aktuálnych dát). Zo svojho tela volá funkciu CView::OnUpdate.

CView::OnUpdate – funkcia zmení pohlad podla dát z dokumentu (ak zvolíme predchádzajúci príklad, realizovala by prekreslenie grafu). Môže, samozrejme, slúžit aj na iné veci (napríklad zruší platnost pohladu, cím vyvolá funkciu OnDraw). OnUpdate je na rozdiel od predchádzajúcich dvoch funkcií virtuálnou funkciou, takže si ju môžeme prispôsobit našim potrebám.

Triedy CDocument a CView obsahujú aj funkcie, ktoré aplikacný systém volá ako prvé po nejakej akcii, ako je napríklad spustenie aplikácie, vytvorenie nového dokumentu alebo pohladu. Tieto funkcie sú dobré napríklad na úvodné naplnenie objektu dokumentu nejakými prvotnými dátami.

CView::OnInitialUpdate – je virtuálna clenská funkcia triedy CView a je volaná pri spustení aplikácie (presnejšie pri prvom pripojení pohladu k dokumentu, ale ešte pred zobrazením pohladu), alebo po vybratí File-New, File-Open z menu. Základnou úlohou tejto funkcie je volat funkciu CView::OnUpdate (co, ak ju neprepíšeme, je jediné, co robí). Pri jej prepisovaní musíme zabezpecit volanie funkcie OnUpdate alebo aspon základnej funkcie OnInitialUpdate (a tá zavolá OnUpdate).

CDocument::OnNewDocument – podobne ako pri predchádzajúcej funkcii je volaná, ak používatel vyberie z menu File-New. Rovnako je volaná pri prvom vytváraní objektu dokumentu. Jej základná implementácia volá funkciu CDocument::DeleteContents, ktorá zmaže obsah dokumentu, ale neznicí ho. Pri aplikáciách SDI sa totiž namiesto vytvorenia nového dokumentu a zrušenia starého

Page 84: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

(napr. pri výbere File-New z menu) radšej reinicializuje starý dokument (vymaže sa jeho obsah a je oznacený ako cistý).

Celý proces spustenia a prevádzku štandardnej aplikácie MFC založenej na architektúre Document/View s jedným pohladom môžete vidiet na obr. 1 a s viacerými pohladmi na obr. 2. Ak sa v obrázkoch nachádza formulácia vytvorenie/zrušenie objektu CDocument/Cview, vždy sa myslí objektu odvodeného od objektu CDocument/CView. Rovnako informácie v obrázkoch a vyššie v texte platia len pre aplikácie SDI!

Dlho som uvažoval a vymýšlal, aký ukážkový príklad použit na ozrejmenie opísanej funkcnosti. Nakoniec som sa rozhodol použit upravený, casom už akýsi „štandardizovaný“ príklad z knihy Inside Visual C++, ktorej autorom je David J. Kruglinski. Podobný príklad nájdete aj na MSDN Library k Visual C++ (tuším sa volá DAOENROL a je tutorialom k programovaniu databáz pomocou DAO) a aj v iných knihách a na internete. Tento príklad sa mi zdá najvhodnejší a na jeho základoch sa dá vysvetlit viacero programovacích techník vrátane serializácie, dumpingu, kolekcií a už spomenutých databáz, ku ktorým sa dostaneme v dalších castiach. Príklad uvedený v tejto casti ukazuje, ako spolu komunikujú dokument s pohladom a akým spôsobom si vymienajú dáta. Používa dve triedy, s ktorými ste ešte priamo neprišli do kontaktu – CFormView a CObject – a používa aj pokrocilú ladiacu techniku (tzv. diagnostic dumping). Všetky tieto relatívne nové prvky si teraz opíšeme.

Obr. 1 Spúštanie štandardnej aplikácie MFC (jeden pohlad)

Obr. 2 Spúštanie štandardnej aplikácie MFC (viacero pohladov)

Obr. 3 Konzolová aplikácia s podporou MFC Trieda CFormView TRIEDA CFORMVIEW. Je triedou pohladu, ale skôr sa podobá na dialóg ako na pohlad. Táto trieda je dokonca aj priamo spojená s konkrétnym zdrojom dialógu, podporuje mechanizmy DDX a DDV a zobrazovanie rovnakých ovládacích prvkov, aké boli uvedené v castiach o dialógoch. Od dialógov odlišuje túto triedu viacero faktorov. V prvom rade nie je odvodená od spolocnej „materskej“ triedy všetkých dialógov CDialog, ale od triedy CScrollView, co, ako vieme, je trieda pre pohlady. Dalej má

Page 85: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

schopnost spracúvat príkazové správy z hlavného menu, resp. z toolbaru, co pri dialógoch nebolo priamo možné. Na druhej strane sa nám však pri používaní tejto triedy bude hodit vela vecí, co sme sa naucili pri dialógoch. Okrem už, dúfam, rutinného používania ovládacích prvkov ? môžeme uplatnit vedomosti z prenosu dát medzi ovládacími prvkami a premennými v programe (funkcia UpdateData, DDX, DDV), aj ked tieto mechanizmy sa v triede CFormView implementujú mierne odlišne. TRIEDA COBJECT. Ako ste mali možnost vidiet v dvanástej casti, táto trieda tvorí základ takmer celej knižnice MFC. Priamo v sebe má zabudovanú podporu serializácie, dumpingu, kolekcií a run-time informácie o triede. Tým, že drvivá väcšina tried MFC je odvodená od tejto triedy, takisto získavajú túto funkcnost. DIAGNOSTIC DUMPING. Ako som už uviedol, ide o metódu ladenia aplikácie. Kedže ide o ladenie, je prístupná podobne ako makro TRACE len v ladiacom (Debug) režime. Môže sa používat podobne ako makro TRACE (kým syntax TRACE bola podobná funkcii printf z jazyka C, syntax dumpingu je zase podobná výpisu pomocou cout) na výpis nejakých ladiacich informácií, ale sféra jeho využitia je podstatne širšia. Používa sa napríklad na zistovanie tzv. memory leaks, co sú diery v pamäti, ktoré vznikajú, ak zabudneme zrušit nejaký objekt (pomocou new vytvoríme, ale na delete zabudneme), pomocou neho môžeme zisit adresu objektu a hodnoty jeho clenských premenných. PRÍKLAD 1 – DUMPING. Po dlhom case si teraz vytvoríme aplikáciu bez AppWizardu, ktorá podporuje MFC. Z menu File vyberte New a na karte Projects vyberte typ projektu: Win32 Console Application. Do mena projektu vpíšte Dumpovanie a kliknite na OK. V prvom kroku, ktorý je zároven posledným, vyberte možnost An application that supports MFC (obr. 3) a kliknite na Finish. Odklepnite aj informácie o novom projekte.

Pozrite sa na kód tejto aplikácie, nemalo by vám robit problém pochopit ho. Projekt preložte a spustite. Z pohladu používatela je jediné, co tento program teraz robí, vypísanie textu na obrazovku a v tomto smere mu ani my nepridáme nijakú funkcnost. Do súboru dumpovanie.h pripíšte túto deklaráciu triedy: class CDumpEx : public CObject { public: int m_nPom; };

Vo funkcii _tmain v súbore dumpovanie.cpp vytvorte objekt typu CDumpEx príkazom:

CDumpEx dumpEx;

A vo vetve else podmienky pridajte nasledujúce riadky: dumpEx.m_nPom=10; afxDump << dumpEx; afxDump << "Hodnota premennej m_nPom=" << dumpEx.m_nPom << "\n";

Teraz nastavte Breakpoint na riadok, kde premennej m_nPom priradujete hodnotu a debuggujte program (F5). Ked dosiahnete Breakpoint, krokujte aplikáciu (Step Into, Step Over). Ked sa dostanete za riadok afxDump << „Hodnota premennej...“, skoncite debuggovanie (SHIFT+F5) a pozrite sa na kartu Debug v okne Output. Okrem iných tam budú riadky: a CObject at $12FF58 Hodnota premennej m_nPom=10

Pomocou preddefinovaného objektu kontextu dumpu (podrobne pozri help) sme vypísali potrebné informácie. Samozrejme, nemusíte pridávat breakpointy a krokovat, ak nechcete; stací, ak spustíte aplikáciu pod Debuggerom (F5). Ale nie je na škodu obcas si zopakovat aspon tie klávesové skratky na ladenie ?. MAKRÁ DECLARE_DYNAMIC A IMPLEMENT_DYNAMIC. Ked vo svojom programe odvodzujete nejakú triedu od Cobject (priamo ci cez jej potomka), tieto makrá vám sprístupnia run-time informácie o odvádzanej triede. Skúste si v našom príklade Dumpovanie do súboru dumpovanie.h do triedy CDumpEx pridat makro: DECLARE_DYNAMIC(CDumpEx)

Page 86: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

A do súboru dumpovanie.cpp niekde pred funkciu _tmain makro:

IMPLEMENT_DYNAMIC(CDumpEx, CObject)

Zmenil sa nejako debug výstup v okne Output? Dalšie informácie o dumpingu a týchto makrách nájdete v helpe. PRÍKLAD 2 – ŠTUDENTI. Teraz sa konecne môžeme pustit do programovania slúbeného príkladu na ozrejmenie spolupráce dokumentu s pohladom. Vytvorte nový projekt s názvom StudentList typu AppWizard EXE. V prvom kroku zvolte SDI aplikáciu a pokracujte až do kroku 6, pricom nechajte všetky nastavenia default. V 6. kroku zmente základnú triedu pohladu z CView na CFormView. Kliknite na Finish a dokoncite tvorenie aplikácie.

Zobrazí sa vám zdroj dialógu, na ktorý budete pridávat ovládacie prvky. Preložte a spustite aplikáciu v takejto forme a chvílu ju preskúmajte, aby ste jasne videli rozdiel medzi triedami CFormView a CView. Rovnako sa pozrite na kód triedy CStudentListView. Teraz pridáme triedu, ktorá bude uchovávat informácie o študentovi (jeho meno a priezvisko, rocník a aktuálny pocet kreditov za daný rocník). Z menu Project vyberte Add To Project a New... Na karte Files vyberte položku C/C++ Header File a do textového pola File name vpíšte student.h (obr. 4).

Obr. 4 Pridanie hlavickového súboru

Po jeho pridaní napíšte don tento kód: class CStudent : public CObject { DECLARE_DYNAMIC(CStudent) public: CString m_strMeno; CString m_strPriezvisko; int m_nRocnik; int m_nKredity; // prepísaná Dump funkcia pre naše potreby void Dump(CDumpContext& dc) const; };

Zopakujte postup na pridanie nového súboru do projektu, ibaže teraz pridajte C++ Source File s názvom student.cpp. Vpíšte donho nasledujúci kód: #include "stdafx.h" #include "student.h" IMPLEMENT_DYNAMIC(CStudent, CObject) void CStudent::Dump(CDumpContext& dc) const {

Page 87: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

// zavoláme funkciu základnej triedy CObject::Dump(dc); dc <<"Meno študenta: " << m_strMeno << "\n"; dc <<"Priezvisko študenta: " << m_strPriezvisko << "\n"; dc <<"Rocník štúdia: " << m_nRocnik << "\n"; dc <<"Pocet kreditov: " << m_nKredity; }

Teraz pridajte do pohladu (presnejšie do zdroja dialógu IDD_STUDENTLIST_FORM) ovládacie prvky podla obr. 5. ID a premenné jednotlivých ovládacích prvkov (vrátane triedy, do ktorej ich pomocou ClassWizardu pridajte) máte v tabulke 1. Obr. 5 „Pohlad“ programu StudentList v editore dialógov

ID ovládacieho prvku Jeho clenská premenná

Trieda Umiestnenie

IDC_MENO m_strMeno CStudentListView Vedla popisu Meno IDC_PRIEZVISKO m_strPriezvisko CStudentListView Vedla pop.

Priezvisko IDC_ROCNIK m_nRocnik CStudentListView Vedla pop. Rocník

štú. IDC_KREDITY m_nKredity CStudentListView Vedla pop. Pocet

kred. IDC_WRITEDATA - CStudentListView Tlacidlo s hlavickou

Zapíš údaje(default button)

IDC_GETDATA - CStudentListView Tlacidlo s hlavickou Získaj údaje

IDC_CLEARVIEW CStudentListView Tlacidlo s hlavickou Vycisti pohlad

Tab. 1 Ovládacie prvky pohladu aplikácie StudentList Do hlavickového súboru triedy CStudentListDoc vložte túto include direktívu: #include “student.h” a jednu verejnú clenskú premennú typu CStudent: public: CStudent m_student;

Táto clenská premenná nám umožnuje (ako už urcite všetci tušíte) pristupovat z dokumentu k clenským premenným triedy CStudent. Objekt triedy CStudent je vytvorený pri konštrukcii dokumentu a zrušený pri rušení dokumentu.

Pomocou ClassWizardu pridajte obsluhu správy BN_CLICKED tlacidlu IDC_WRITEDATA do triedy CStudentListView. Táto funkcia nám bude slúžit na zapísanie dát z pohladu (pomocou premenných pohladu m_strMeno...) cez dokument až do clenských premenných triedy CStudent. K dokumentu pristupujeme pomocou ukazovatela nan, ktorý získame pomocou funkcie GetDocument. Do tela obslužnej funkcie kliknutia na tlacidlo IDC_WRITEDATA napíšte tento kód: void CStudentListView::OnWritedata() { // získame ukazovatel na dokument CStudentListDoc* pDoc=GetDocument(); // rovnako ako v dialógoch, volá DDX UpdateData(TRUE); // vpíšeme dáta do premenných triedy CStudent pDoc->m_student.m_strMeno=m_strMeno; pDoc->m_student.m_strPriezvisko=m_strPriezvisko;

Page 88: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

pDoc->m_student.m_nRocnik=m_nRocnik; pDoc->m_student.m_nKredity=m_nKredity; }

Ked už máme funkciu, ktorá zapíše dáta z pohladu do dokumentu, bolo by asi vhodné napísat funkciu, ktorá robí opacnú operáciu. Tlacidlu IDC_GETDATA priradte do triedy pohladu obsluhu správy BN_CLICKED a kód obslužnej funkcie zmente takto: void CStudentListView::OnGetdata() { CStudentListDoc* pDoc=GetDocument(); m_strMeno=pDoc->m_student.m_strMeno; m_strPriezvisko=pDoc->m_student.m_strPriezvisko; m_nRocnik=pDoc->m_student.m_nRocnik; m_nKredity=pDoc->m_student.m_nKredity; UpdateData(FALSE); }

Funkcia tlacidla IDC_CLEARVIEW je jasná. Po jej stlacení sa vymažú dáta z pohladu (nie však z objektu CStudent!). Napísanie jej kódu vám nemôže robit problém, preto ho ani neuvádzam (považujte to za drobnú domácu úlohu ?). AKO TO TESTOVAT? Ked chcete odskúšat funkcnost programu CStudentList, napíšte do textových polí nejaké informácie, potom zapíšte tieto údaje do premenných objektu CStudent (tlacidlo IDC_WRITEDATA), vymažte text z textových polí (tlacidlo IDC_CLEARVIEW) a tlacidlom IDC_GETDATA vyvolajte z objektu CStudent dáta spät do pohladu. Je však aj iná metóda – využitím dumpingu (predsa sme neprepisovali funkciu Dump v triede CStudent zbytocne!). Pridajte nasledujúci riadok do deštruktora triedy dokumentu: Dump(afxDump); co spôsobí zavolanie funkcie CStudentListDoc::Dump, ktorej kód zmente: void CStudentListDoc::Dump(CDumpContext& dc) const { CDocument::Dump(dc); dc << "\n" << m_student << "\n"; }

Teraz môžete funcnost programu „odsledovat“ aj na výstupe v karte Debug. Vrelo vám odporúcam odkrokovat tento kód do podrobností, aby ste pochopili, co a kedy sa volá. FAQ Q: Chcem z môjho programu lockovat pocítac. Dá sa to? A: Áno, samozrejme, ale len vo Windows 2000 (a vyššom). Sú dve možnosti, ako to urobit. Bud použitím funkcie LockWorkStation, ktorú z programu zavoláte (bližšie informácie o potrebných knižniciach a hlavickových súboroch v helpe), alebo využitím locknutia z príkazového riadka. V druhom prípade by lockovanie v programe vyzeralo takto: system("rundll32 user32.dll,LockWorkStation"); TIPY NA DOMA. Zlepšite program StudentList tak, aby hned po spustení, ked ešte nie sú prístupné žiadne dáta v triede CStudent ani v pohlade, zneprístupnil tlacidlo IDC_WRITEDATA (kedže nie je co zapisovat) a sprístnupnite ho až po zadaní prvého znaku do textových polí. V helpe si pozrite použitie podmieneného prekladu dumpingových funkcií pomocou #ifdef _DEBUG a #endif a pridajte ich do tohto programu. Využitím funkcie CView::OnInitialUpdate „naplnte“ pohlad základnými dátami už pri jeho vytvorení (napr. menu a priezvisku priradte text: Default a pod.). NABUDÚCE. V dalšej casti vyriešime „nedostatok“ aplikácie StudentList, ktorým je jej schopnost spracúvat a uchovávat informácie len o jednom študentovi. Pridáme jej podporu spracovania viacerých študentov (využitím kolekcií) a možno prejdeme aj k problému, ako tieto dáta uložit na disk (serializácii). Nabudúce si tiež povieme, co nás ešte v seriáli caká a približne kedy budeme koncit.

Page 89: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

17. cast: FAQ Ani sme sa nenazdali a rok 2001 sa koncí. Pre niekoho bol úspešný, pre niekoho menej. Pre niekoho bol radostný, pre niekoho tragický. Som presvedcený, že rok 2001 sa napevno vpíše do dejín ludstva ako nie príliš „úspešný“ – a nebude to uvedením Windows XP...

Ale podme na inú tému. Ked som už spomenul Windows XP, ich uvedenie bude mat na nás vývojárov nemalý dosah. Okrem ocakávaného súboju NET a Java, ktorého vítaza asi tak skoro nespoznáme, prináša tento operacný systém vela nových vecí, ktoré sa my, „úbohí“ programátori musíme naucit, ak chceme zostat „in“.

Co sa týka náplne tejto casti seriálu, rozhodol som sa dopriat vám oddych od výkladu zložitých vecí Visual C++ a MFC. Celá cast bude venovaná vašim otázkam, ktoré ste mi poslali a ktoré majú všeobecnejší charakter, takže poslúžia, predpokladám, väcšine z vás. V odpovediach si ukážeme aj nejaké praktické veci, ktoré som vo výklade nespomenul, ale ich urcite každý, kto tvorí praktické aplikácie, využije. V závere si povieme, co nás ešte caká a asi kedy budeme so seriálom koncit.

Ešte k vašim otázkam. Niektorí ste mi posielali vaše kompletné aplikácie s otázkami typu, ako urobit to ci ono, co treba zmenit, aby to nepadalo, a podobne. Žial, nie je v mojej moci na všetky takto stavané otázky odpovedat. Cítat a pochopit niekoho kód, akokolvek dobre okomentovaný, je vždy casovo velmi nárocné. A ešte k tomu dat odpoved na otázky, ktoré nevie zodpovedat ani sám autor tohto programu, nie je možné. Preto sa ospravedlnujem tým, ktorí sa takejto odpovede nedockali. Q: Dá sa zadarmo zohnat/stiahnut Visual C++? A: Co sa týka stiahnutia, nemám informácie, že by Microsoft uvolnil Visual C++ takouto cestou (teda ak nie ste clenmi niektorého z programov MSDN Subscriptions). Visual C++ sa však dá zohnat s niektorými knihami, ktoré vysvetlujú programovanie s týmto vývojovým nástrojom. Napríklad Visual C++ Introductory Edition (co je vlastne Visual C++ Standard Edition, len má nejaké obmedzenia, ktoré sa týkajú tvorby komercných aplikácií) sa dá zohnat s knihami Programujeme v Microsoft Visual C++ od tímu autorov (Kruglinski, Shepherd, Wingo) alebo Programujeme v COM a COM+ od Dalibora Kacmáre. Všetky vyšli vo vydavatelstve Computer Press. Sú však aj dalšie knihy (bližšie informácie na http://www.cpress.cz/knihy). Ak máte o ne záujem, pozrite sa v najbližšom kníhkupectve alebo virtuálnom obchode z knihami. (Pocítajte však s tým, že ich cena sa pohybuje od 600 Sk vyššie.) Q: Existujú na internete volne dostupné ucebnice o jazyku C/C++? A: Áno, samozrejme. Za najlepšiu takto dostupnú ucebnicu považujem publikáciu od Bruce Eckela Thinking in C++ 2nd. Edition; Volume 1 a Volume 2, kde nájdete skutocne vycerpávajúci výklad všetkých crt C++ vrátane tých najmodernejších. Na konci každej kapitoly sú cvicenia, ktoré sa týkajú preberanej látky v konkrétnej kapitole a sú jasne formulované, jednoduché. Ich úspešné vyriešenie má urcite motivujúci charakter. Všetky zdrojové kódy z knihy sa dajú zadarmo stiahnut z webu (okrem riešení cvicení, tie sú dostupné za poplatok). Jedinou nevýhodou pre našinca môže byt, že stiahnutá kniha je v anglickom jazyku, ale v terajšej dobe to nepovažujem za velký problém. Tento autor uspokojí aj záujemcov o Javu – dostupná je kniha Thinking in Java 2nd. edition – takisto velmi kvalitná kniha. A konecne web, z ktorého sa dajú knihy stiahnut a kde získate dalšie informácie: www.bruceeckel.com. Q: Aké knihy sú najvhodnejšie na ucenie sa programovania s Visual C++? A: Samozrejme, nemôžem hodnotit, ktorá kniha je tá naj..., ale za velmi kvalitné považujem Mistrovství ve Visual C++ od tragicky zosnulého Davida J. Kruglinského, jej pokracovanie: Programujeme v Microsoft Visual C++ (pozri vyššie), ktoré sú skutocné ucebnicami, a nie referencnými príruckami. Q: Na inštalacných médiách Visual Studia 6.0 sa nenachádza help. Odkial ho mám doinštalovat? A: Inštalacky samotného Visual Studia 6.0 skutocne neobsahujú help k jednotlivým produktom (Visual Basic 6.0, Visual C++ 6.0, Visual FoxPro 6.0 a dalšie). Help sa nachádza na samostatných médiách, ktoré sa oznacujú ako Microsoft Developer Network for Visual Studio 6.0 (známe MSDN) a mali by sa dodávat spolu s Visual Studiom (je možné ich zohnat aj samostatne). Sú to dve, prípadne viac CD (záleží od verzie). Najnovšie MSDN nájdete vždy na stránke www.msdn.microsoft.com, co je on line verzia cédeckových MSDN. Q: Existuje na Visual C++ nejaký Service Pack a nechystajú sa jeho nové verzie?

Page 90: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

A: Na stránke http://msdn.microsoft.com/vstudio/downloads/updates.asp sa dá stiahnut, prípadne objednat na CD najaktuálnejší Service Pack 5 (26.2.2001). Update sa týka všetkých aplikácií Visual Studia 6.0, ktorého je Visual C++ 6.0 súcastou. Nová verzia Visual C++ bude zahrnutá v novom Visual Studiu NET (Visual Studio 7.0), ktoré sa momentálne (25. 10. 2001) nachádza vo verzii Release Candidate. V terajšej dobe sa dá zadarmo objednat Beta 2 Visual Studia NET(na CD alebo DVD), a ked cítate tieto riadky, pravdepodobne už aj spomínaný Release Candidate: (http://www.microsoft.cz/akce/vsbeta2/). A pozrite sa aj na stránku: http://www.msdn.microsoft.com/vstudio/nextgen/beta.asp.

To boli všeobecné otázky, ktoré sa priamo netýkali programovania vo Visual C++. Nasledujú otázky priamo súvisiace s vývojom aplikácií. Q: Ako sa dajú cítat/zapisovat údaje z/do INI súborov? A: Aj ked Microsoft tvrdí, že v 32-bitových Windows by sa mali všetky informácie z INI súborov nachádzat v registroch, v praxi sa toto pravidlo nie vždy dodržiava a ukazuje sa, že INI súbory sa budú používat ešte dost dlho. Casto napríklad potrebujeme takto cítat údaje, ktoré ovplyvnujú správanie celého programu, a hrabat sa u zákazníka v registroch Windows nie je velmi praktické (je „používatelsky“ jednoduchšie zmenit údaje v INI súbore ako ich menit v registroch).

Na cítanie císelných dát z INI súboru slúži funkcia: GetPrivateProfileInt( LPCTSTR menoSekcie, LPCTSTR menoKluca, INT nVratDefault, LPCTSTR menoINISuboru); Na cítanie retazcov slúži funkcia: GetPrivateProfileString( LPCTSTR menoSekcie, LPCTSTR menoKluca, LPCTSTR strDefault, LPTSTR vratString, DWORD velkostRet, LPCTSTR menoINISuboru); Presný opis parametrov nájdete v helpe, rovnako aj štruktúru INI súboru. Ak máme INI súbor s názvom mojini.ini s obsahom: [NASTAVENIA] SpustP=1 CestaEXE=C:\MojProjekt\EXE\mojprojekt.exe potom cítanie hodnoty SpustP by zabezpecovala funkcia: int PrecitajIntINI(LPCTSTR section, LPCTSTR name, LPCTSTR file) { return GetPrivateProfileInt(section, name, 0, file); } Volali by sme ju napr. takto: SpustP=PrecitajIntINI("NASTAVENIA ", "SpustP ", "mojini.ini "); Cítanie hodnoty CestaEXE, co je retazec, bude zabezpecovat funkcia: LPTSTR PrecitajStringINI(LPCTSTR section, LPCTSTR name, LPCTSTR file) { LPTSTR docas=NULL; docas=(LPTSTR)GlobalAllocPtr(GHND, 50 * sizeof(TCHAR)); GetPrivateProfileString(section, name, "", docas, 4096, file); return docas; } Volat ju môžeme takto:

Page 91: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

char cesta[_MAX_PATH]; strcpy(cesta, PrecitajStringINI("NASTAVENIA", "CestaEXE", "mojini.ini"));

V premennej cesta budeme mat uložený string z INI súboru. Na úspešne skompilovanie tohto kódu musíte okrem štandardných hlavickových súborov includovat aj string.h (pre strcpy), stdlib.h (pre _MAX_PATH ) a windowsx.h (na správne alokovanie pamäte pre retazec vo funkcii PrecitajStringINI).

Tým sme vybavili cítanie, ostáva ešte zápis. Na zápis retazcových aj císelných hodnôt bez rozdielu slúži funkcia: BOOL WritePrivateProfileString(LPCTSTR menoSekcie, LPCTSTR menoKluca, LPCTSTR zapisRet, LPCTSTR menoINISuboru); Pokial ide o bližší opis parameterov, opät vás odkážem na help. Funkcia zapisujúca hodnotu 0 do parametra SpustP vyzerala takto: BOOL ZapisStringINI(LPCTSTR section, LPCTSTR name, LPCTSTR str_zapisat, LPCTSTR file) { return WritePrivateProfileString(section, name, str_zapisat, file); } Volali by sme ju takto: BOOL bNavrat; bNavrat=ZapisStringINI(“NASTAVENIA”, “SpustP”, “0”, “mojini.ini”);

Ak zadaný súbor INI v zadanej ceste neexistuje, vytvorí sa v systémovom adresári Windows. Takisto ak neexistuje sekcia/klúc, kam chceme zapisovat, vytvoria sa. Ak je meno súboru INI bez akejkolvek cesty (ako v našom príklade), defaultný adresár, kde ho program hladá, je aj adresár Windows. Existujú aj dalšie funkcie na prácu so súbormi INI [napr. WritePrivateProfileSection na zápis celej sekcie (Get... na cítanie), WritePrivateProfileStruct na zápis štruktúry dát (Get... na cítanie)], ktoré sme si tu nespomenuli, ich použitie je podobné ako v prípade zápisu/cítania jednoduchých dát. Pre záujemcov o detaily je k dispozícii help. Q: Ako získam zoznam všetkých procesov, ktoré práve bežia vo Windows? A: V prvom rade je potrebné zohnat si súbory psapi.h, psapi.lib a psapi.dll (dostupné napríklad na www.msdn.microsoft.com – dajte si ich tam vyhladat alebo mi napíšte, pošlem vám ich). Psapi.h a psapi.lib pridajte do svojho projektu (v menu Project->Add to Project->Files) a psapi.dll nahrajte do System32 adresára v adresári, kde máte nainštalované Windows. Sú to súbory obsahujúce deklarácie a definície funkcií, ktoré sú nevyhnutné pre náš ukážkový príklad a vo všeobecnosti zabezpecujú prácu s procesmi. Ukážeme si tvorbu celej aplikácie po krokoch (príklad je urcený len pre operacné systémy NT/2000, mal by fungovat aj pod XP). 1. Vytvorte nový projekt s názvom Procesy (Win32 Console Application->An empty project a Finish). 2. Pridajte do projektu súbory psapi.h, psapi.lib a jeden nový zdrojový súbor – procesy.c (Nie cpp). 3. Tento kód pridajte do súboru procesy.c: #include <windows.h> #include <stdio.h> #include "psapi.h" // struktura vsetkych procesov typedef struct { char* ProcesName; DWORD ProcessID; } PROCESY; // 200 ako max. pocet procesov

Page 92: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

PROCESY proc[200]; void ZiskajProcesy(DWORD processID, int Poradie) { HANDLE hProcess; char szProcessName[_MAX_PATH] = "unknown"; // alokacia pamate pre pole mien procesov proc[Poradie].ProcesName=malloc(sizeof(char)); // ziskame HANDLE procesu hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID); // ziskame meno procesu if (hProcess) { HMODULE hMod; DWORD cbNeeded; if (EnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded)) { GetModuleBaseName(hProcess, hMod, szProcessName, sizeof(szProcessName)); } } // zapiseme informacie o procese do struktury strcpy(proc[Poradie].ProcesName, szProcessName); proc[Poradie].ProcessID=processID; CloseHandle(hProcess); } void main() { DWORD aProcesses[1024], cbNeeded, cProcesses; unsigned int i; if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded)) return; // Kolko je procesov? cProcesses = cbNeeded / sizeof(DWORD); // ziskame mena a ID procesov for (i = 0; i<cProcesses; i++) ZiskajProcesy(aProcesses[i], i); // vypiseme vsetky procesy for (i = 0; i<cProcesses; i++) printf("%d %s\n", proc[i].ProcessID, proc[i].ProcesName); } Q: Ako pridám aplikáciu do SystemTray (TaskBaru)? A: Pozrite si nasledujúcu funkciu: void PridajNaTaskbar(HWND hWnd, UINT uID, HICON hIcon, LPSTR lpszTip) { NOTIFYICONDATA tnid; tnid.cbSize = sizeof(NOTIFYICONDATA);

Page 93: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

tnid.hWnd = hWnd; tnid.uID = uID; tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; tnid.uCallbackMessage = WM_NOTIFYICON; tnid.hIcon = hIcon; if (lpszTip) lstrcpyn(tnid.szTip, lpszTip, sizeof(tnid.szTip)); else tnid.szTip[0] = '\0'; res = Shell_NotifyIcon(NIM_ADD, &tnid); if (hIcon) DestroyIcon(hIcon); } Má tieto parametre: Parameter HWND hWnd získate pri tvorení okna aplikácie (návratová hodnota funkcie CreateWindow – pozri druhú cast seriálu). Parameter UINT uID obsahuje ID zdroja ikony (zdroj ikony pridáte do zdrojov cez menu Visual C++ Insert -> Resource a vyberiete Icon alebo Import, ak chcete použit existujúcu ikonu). Pre náš príklad bude meno zdroja ikony IDI_PRIKLAD. Potom parameter uID bude obsahovat práve toto meno. Parameter HICON hIcon je handle zdroja ikony. Najprv musíte zadeklarovat tento handle ako: HICON hIcon; a jeho hodnotu získate ako návratovú hodnotu funkcie LoadIcon (parameter funkcie LoadIcon hInstance je tiež opísaný v druhej casti seriálu): hIcon=LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PRIKLAD)); Parameter LPSTR lpszTip je akýkolvek textový retazec, ktorý sa zobrazí, ak myšou „zájdete“ nad ikonu v TaskBare.

Volanie funkcie PridajNaTaskbar by v našom prípade vyzeralo takto: PridajNaTaskbar(hWnd, IDI_PRIKLAD, hIcon, "Pokus s TaskBarom");

Ak sme však ikonu na TaskBar pridali, bolo by dobré ju po skoncení aplikácie vymazat. Pridajte preto nasledujúci kód na miesto, kde ukoncujete svoju aplikáciu (v druhej casti seriálu by to bolo vo vetve WM_DESTROY funkcie WndProc pred volanie funkcie PostQuitMessage(0)), napríklad: case WM_DESTROY: // zmazeme ikonu z toolbaru tnid.cbSize = sizeof(NOTIFYICONDATA); tnid.hWnd = hWnd; tnid.uID = IDI_PRIKLAD; res=Shell_NotifyIcon(NIM_DELETE, &tnid); // potom ukocime aplikaciu PostQuitMessage(0); return 0;

Premenná res je deklarovaná ako BOOL. Otázkou zostáva, ako zachytávat správy pre aplikáciu umiestnenú v TaskBare (napr. kliknutie myšou na ikonu aplikácie). Vo funkcii PridajNaTaskbar sa nachádza riadok: tnid.uCallbackMessage = WM_NOTIFYICON;

WM_NOTIFYICON je používatelom definovaná správa, ktorá bude vyslaná vždy, ked myšou „zájdete“ nad ikonu aplikácie v TaskBare. Túto správu obslúžite ako každú inú vo funkcii okna

Page 94: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

WndProc (stále uvažujeme príklad z druhej casti seriálu), kde bude predstavovat dalšiu vetvu case. Napríklad stlacenie pravého tlacidla myši na ikone aplikácie v TaskBare môžete obslúžit takto: switch (message) { // ak zajdeme mysou nad ikonu... case WM_NOTIFYICON: // ...a stlacime prave tlacidlo mysi... if (lParam == WM_RBUTTONDOWN) { MessageBox(hWnd, "Stlacili ste prave tlacidlo mysi?", "Informacia", MB_OK) } break;

Správa WM_NOTIFYICON predstavuje používatelom definovanú správu, preto ju musíte na zaciatku programu aj zadefinovat: // pouzivatelom definovana sprava #define WM_NOTIFYICON (WM_USER+100)

Ak by sa náhodou vyskytli problémy pri zachytení tejto správy (záleží na verzii Windows), zmente hlavicku funkcie WndProc z druhej casti seriálu takto: LRESULT WINAPI WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) co predstavuje modernejší zápis (používaný vo WIN32API) oproti tomu, ktorý je uvedený v druhej casti seriálu. CO NÁS EŠTE CAKÁ? V dalších castiach si ešte preberieme kolekcie, serializáciu, rozdiely aplikácií SDI a MDI, povieme si nieco o registroch, písaní knižníc DLL, tlaci a seriál ukoncíme „cerešnickami“ v podobe využívania komponentov ActiveX, bitmáp a viacvláknového programovania. Tým ukoncíme základ, spomínaný v prvej casti, po ktorého zvládnutí budete pripravení na získavanie dalších vedomostí. Po dlhšom zvažovaní som sa rozhodol neopisovat prácu s databázami [pomocou DAO, o „jednoduchšom“ ADO (ActiveX Data Objects) si povieme aspon základy], co je velmi rozsiahla téma, takmer na další seriál. Takisto si nepovieme nic o komponentoch COM, COM+, DCOM, OLE a podobných, co sú rovnako velmi rozsiahle a zložité „technológie“, ktorých vývoj ešte stále nie je ukoncený a v novom Visual Studiu bude práca s nimi znacne zjednodušená, takže možno až potom... Vidím to tak, že seriál by mohol koncit 24. castou, v ktorej uvedieme podobné FAQ, ako bolo v tejto casti, cím celú tému uzavrieme. NABUDÚCE. V dalšej casti sa vrátime opät ku klasickému výkladu a budeme pokracovat v zdokonalovaní aplikácie StudentList. Stretneme sa až v roku 2002, takže vám prajem krásne prežitie Vianoc, veselého Silvestra a vela, vela úspechov v roku 2002! 18. cast: Kolekcie Rok 2002 sa už stal realitou. Zase nám bude pár týždnov trvat, než prestaneme na dokumnety písat „01“ ci „2001“ a „zosynchronizujeme sa s našou casovou osou“, ako by povedal nejeden fanúšik sci-fi. Dúfam, že ste Silvestra prežili bez ujmy na zdraví a zacali s velkým odhodlaním plnit novorocné predsavzatia (moment, kolkého že dnes máme? Okolo 10. januára? Tak to hej, to sa predsavzatia ešte plnia ?).

Medzi najvýznamnejšie udalosti roku 2002 bude pre WIN programátorský svet predstavovat uvolnenie novej verzie Microsoft Visual Studia – Visual Studio NET(ohlásené na 13. februára) a následný boom „NET aplikácií“, ktoré po jeho uvedení zaplavia softvérový trh. Som velmi zvedavý, ako sa mu bude darit. Ide totiž skôr o úplne nový produkt ako o pokracovanie. Microsoft sa rozhodol pre použitie minima kódu zo starého VStudia a takmer všetko sa písalo nanovo (predsa len je VS 6.0 už tamer štyri roky starý a jeho jadro nebolo príliš vhodné na zakomponovanie všetkých nových „features“). Uvidíme teda, ako si NET poradí s existujúcou konkurenciou, každopádne treba mu držat palce.

Seriál až do svojho konca zostane pri Visual Studiu 6.0, azda niekedy v poslednej casti si opíšeme najdôležitejšie crty jeho novej verzie.

Page 95: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Touto castou sa opät vraciame ku klasickému výkladu, ktorý sme na mesiac prerušili. Preberieme si velmi dôležitú a rozsiahlu problematiku kolekcií (Collections). COLLECTIONS. Oznacenie „kolekcia“ sa nepoužíva len v súvislosti s Vianocami, ale predstavuje aj dôležitú súcast knižnice MFC. Aby ste si pod týmto pojmom nepredstavovali nieco zložité a ktovieako abstraktné, stací si uvedomit, že kolekciu tvorí skupina objektov (dalo by sa povedat aj pole objektov, ale pre kolekcie má pole osobitný význam, preto ostaneme pri oznacení skupina objektov). Kolekcia je sama osebe takisto objektom, ktorý môže uchovávat nejaké dáta, resp. ako sme už povedali, aj iné objekty. Kolekcie sa vo všeobecnosti delia podla dvoch kritéríí. Podla svojej vnútornej štruktúry (angl. shape), ktorá urcuje, ako sú dáta/objekty v kolekcii usporiadané. Druhé kritérium rozdeluje kolekcie podla tried kolekcií MFC.

Podla vnútornej štuktúry rozlišujeme kolekcie: List, Array a Map (zoznam, pole a mapa). List – predstavuje usporiadaný, neindexovaný zoznam prvkov. Má svoj zaciatok (oznacovaný head) a koniec (tail). Vyznacuje sa hlavne svojou rýchlostou pri vkladaní/odstranovaní prvkov. Array – predstavuje usporiadané, indexované pole objektov (indexy môžu byt len celocíselné hodnoty). Podobá sa klasickému polu elementárnych prkov. Map – na prvý pohlad najkomplikovanejší, ale nenahraditelný typ kolekcie, ktorý nejakému klúcovému objektu priraduje objekt s hodnotou. Tento typ kolekcie sa zvykne oznacovat aj ako slovník.

Porovnanie vlastností typov kolekcií vidíte v tab. 1. Z pohladu MFC existujú tri typy tried kolekcií, pricom v každej skupine existujú triedy, ktoré pracujú s kolekciami typu List, Array a Map (pozri tab. 2 a tab. 3). Rozlišujeme teda:

1. triedy kolekcií založené na šablónach jazyka C++, založené na kolekciách objektov rôznych typov (tab. 2),

2. triedy kolekcií založené na šablónach jazyka C++, založené na kolekciách typovo bezpecných ukazovatelov na objekty rôznych typov (tab. 2),

3. triedy kolekcií, ktoré nie sú založené na šablónach jazyka C++ (tab. 3).

Tieto triedy sa ešte dalej môžu delit na triedy, ktoré sú serializovatelné, triedy, ktoré podporujú dumpovanie, a triedy, ktoré sú typovo bezpecné. V súcasnosti sa odporúca využívat triedy založené na šablónach C++, jednak sú novšie a viacobjektové, jednak sú typovo bezpecné (type-safe). Samozrejme, nic vám nebráni používat nešablónové triedy.

Typ kolekcie

Usporiadané Indexované Vkladanie prvku

H¾adanie konkrétneho

prvku

Duplicitné prvky

List Áno Nie Rýchle Pomalé Podporuje Array Áno Áno (pod¾a

celoèíselného indexu)

Pomalé Pomalé Podporuje

Map Nie Áno (pod¾a k¾úèa)

Rýchle Rýchle K¾úèe nie Hodnoty áno

Tab. 1 Vlastnosti kolekcií podla vnútornej štruktúry prvkov

Obsah kolekcie Triedy pre typ kolekcie Array

Triedy pre typ kolekcie List

Triedy pre typ kolekcie Map

Kolekcia objektov rôznych typov

CArray CList CMap

Kolekcia ukazovatelov na objekty rôznych typov

CTypedPtrArray CTypedPtrList CTypedPtrMap

Tab. 2 Triedy kolekcií MFC založené na šablónach C++

Triedy pre typ kolekcie Array

Triedy pre typ kolekcie List

Triedy pre typ kolekcie Map

CObArray CObList CMapPtrToWord CByteArray CPtrList CMapPtrToPtr CDWordArray CStringList CMapStringToOb CPtrArray CMapStringToPtr CStringArray CMapStringToString

Page 96: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

CWordArray CMapWordToOb CUIntArray CMapWordToPtr Tab. 3 Nešablónové triedy kolekcií MFC

Dalšie informácie o týchto triedach nájdete v helpe. PREDPRÍPRAVA NA PRÍKLADY. Aby sme si ukázali opät nieco nové v prostredí Visual C++, nasledujúci príklad + všetky úlohy, ktoré si budete doma skúšat, si uložte do jedného Workspace (pracovného prostredia). V praxi sa podobný postup využíva velmi casto, preto bude dobré, ak si ho osvojíte a zacnete aktívne používat (navrhujem vám napr. všetky príklady z jednej casti seriálu udržovat v samostatných pracovných prostrediach). Z menu File vyberte New, kliknite na kartu Workspaces a vyberte Blank Workspace. Do textového pola Workspace name napíšte Collections a potvrdte (OK). Teraz máme vytvorené cisté pracovné prostredie a don budeme postupne pridávat jednotlivé projekty. PRÍKLAD – POUŽITIE CTypedPtrList. V príklade si ukážeme použitie kolekcie typu List s pomocou na šablóne založenej triedy MFC CTypedPtrList, ktorá „uchováva“ objekty ukazovatelov na nejakú triedu (resp. objekt). Za predpokladu, že máte otvorené Workspace Collections, vytvorte nový projekt typu Win32 Console Application s podporou MFC (pozri predchádzajúcu cast). Vloženie tohto projektu do Workspace Collections zabezpecíte tak, že na karte výberu typu nového projektu oznacíte Add to current Workspace (obr. 1). Meno projektu je: SimpleList.

Ak všetko prebehlo bez problémov, vo Workspace Collections sa teraz nachádza jeden projekt SimpleList. Pomocou menu Insert-New Class vložte do tohto projektu novú triedu CSimpleListClass (potrebné údaje vidíte na obr. 2).

Kód hlavickového súboru triedy CSimpleListClass upravte takto: class CSimpleListClass : public CObject { // pre dumping DECLARE_DYNAMIC(CSimpleListClass) public: CSimpleListClass(int nPoradie); virtual ~CSimpleListClass(); void Dump(CDumpContext &dc) const; void PrintPoradie(); private: int m_nPoradieObjektu; };

V implementacnom súbore triedy CSimpleListClass zmente konštruktor a pridajte kód funkcie PrintPoradie a Dump: CSimpleListClass::CSimpleListClass(int nPoradie) { m_nPoradieObjektu=nPoradie; } void CSimpleListClass::PrintPoradie() {

printf("Cislo objektu: %d\n", m_nPoradieObjektu); } void CSimpleListClass::Dump(CDumpContext &dc) const { CObject::Dump(dc); dc << "Poradie objektu: " << m_nPoradieObjektu << "\n"; }

Pre správnu funkciu dumpingu pridajte ešte makro IMPLEMENT_DYNAMIC na zaciatok tohto súboru:

Page 97: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

IMPLEMENT_DYNAMIC(CSimpleListClass, CObject)

Teraz prepíšte vetvu else vo funkcii _tmain (kde sa nachádza komentár: // TODO: code your application's behavior here). int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) { int nRetCode = 0; // initialize MFC and print and error on failure if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) { // TODO: change error code to suit your needs cerr << _T("Fatal Error: MFC initialization failed") << endl; nRetCode = 1; } else { // Vetva else – tento kód pridajte CSimpleListClass* simpleListObject; // objekt ukazovatelov na CSimpleListClass CTypedPtrList <CObList, CSimpleListClass*> m_SimpleList; // vytvarame a vkladame ukazovatele na objekty typu CSimpleListClass // do zoznamu simpleList for (int i=0; i<5; i++) { simpleListObject=new CSimpleListClass(i); m_SimpleList.AddTail(simpleListObject); } // posuvame sa po objektoch v kolekcii POSITION pos=m_SimpleList.GetHeadPosition(); while (pos!=NULL) { // GetNext vracia ukazovatel na CSimpleListClass // preto je priradenie spravne! simpleListObject=m_SimpleList.GetNext(pos); simpleListObject->PrintPoradie(); } // nastavime hlbku Dumpovania afxDump.SetDepth(1); // Dump m_SimpleList afxDump << m_SimpleList; // vyberieme ukazovatele na objekty CSimpleListClass // a zrusime ich while (!m_SimpleList.IsEmpty()) { simpleListObject=m_SimpleList.RemoveHead(); afxDump <<"Upraveny m_SimpleList: \n"; afxDump << m_SimpleList; delete simpleListObject; } } return nRetCode;

Page 98: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

} Opíšeme si niektoré nové konštrukcie, ktoré by vám mohli robit problémy. Riadok CTypedPtrList <CObList, CSimpleListClass*> m_SimpleList;

Obr. 1 Pridávanie projektu do Workspace deklaruje objekt ukazovatelov na triedu CSimpleListClass. Prvý parameter je základnou triedou kolekcie. Môže to byt len trieda CPtrList alebo nami použitá CObList. CObList sa odporúca používat, ak ukladáte objekt odvodený od CObject (co v našom prípade aj robíme – pozri triedu CSimpleListClass), v opacnom prípade sa používa CPtrList (skúste si príklad aj s touto triedou a sledujte, ako sa zmení Dump výstup). Na riadku POSITION pos=m_SimpleList.GetHeadPosition(); deklarujeme premennú pos typu POSITION a priradujeme jej ukazovatel na zaciatok zoznamu m_SimpleList (podrobnejšie v helpe). Táto premenná reprezentuje polohu prvku v zozname. Dalej v kóde používame túto premennú v spojení s funkciou GetNext, ktorá vracia ukazovatel na prvok, ktorého pozícia je práve pos, a zároven inkrementuje túto premennú, ciže po zavolaní funkcie bude obsahovat polohu prvku pos+1(ak sme na poslednom prvku, tak pos=NULL). Preto pri posúvaní sa po prvkoch v zozname vykonávame test: while (pos!=NULL) ci sme už náhodou nedosiahli jeho koniec. Za povšimnutie ešte stojí proces rušenia objektov simpleListObject. Pri odstranovaní objektov z kolekcie vracia funkcia RemoveHead ukazovatel na práve rušený objekt, ktorý použijeme, aby sme zrušili aj samotný objekt (vynatím ukazovatela zo zoznamu sa objekt nezruší!). Do súboru stdafx.h vložte ešte túto include direktívu #include <afxtempl.h>

Tá je potrebná, ak chcete používat kolekcie založené na šablónových triedach (pre nešablónové triedy by ste includovali súbor afxcoll.h).

To bolo použitie triedy CtypedPtrList; skúste si aj použitie ostatných tried, pricom dalšie projekty pridávajte do Workspace Collections. Odporúcam vám všetky triedy kolekcií MFC dobre naštudovat, niekedy sa budete musiet rozhodnút, ktorú z nich použijete. A správne sa rozhodnete len v takom prípade, že budete dokonale poznat každú z týchto tried. ZLEPŠENIE PROJEKTU ŠTUDENTI. Teraz s využitím kolekcií zlepšíme projekt StudentList zo 16. casti. Nová verzia bude schopná uchovávat informácie o viacerých študentoch, pricom bude možné posúvat sa po jednotlivých záznamoch a vkladat/mazat údaje o konkrétnom študentovi. Nevytvárajte zbytocne nový projekt, všetky zmeny vykonajte do už existujúceho projektu StudentList.

Krok 1. Pridajte menu Student, ktoré bude obsahovat položky: First, Last, Next, Prev, |Separator|, Insert, Remove. Takisto pridajte tlacidlá na toolbar, ktoré budú mat rovnaký význam (a hlavne rovnaké ID), ich grafickú interpretáciu nechám na vás. Pomocou ClassWizardu namapujte

Page 99: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

obslužné správy týchto tlacidiel (v tomto prípade je potrebné namapovat pre každé tlacidlo okrem tlacidla Insert aj správu UPDATE_COMMAND_UI – podrobnosti sú v tab. 4).

Obr. 2 Vloženie novej triedy do projektu

Obr. 3 Okno zlepšenej aplikácie StudentList Táto správa je potrebná na to, aby sme mohli podla aktuálnej pozície v zozname zneprístupnit niektoré tlacidlo (napr. ak sa nachádzame na prvom zázname, zneprístupníme tlacidlá First a Prev). Podrobnejší význam tlacidiel je v tab. 5. Nakoniec ešte správne odstránte tlacidlo IDC_GETDATA („správne“ znamená zo zdrojov + nezabudnút odstránit ho pomocou ClassWizardu + vymazat telo obslužnej funkcie). Ako by malo vyzerat okno aplikácie, to vidíte na obr. 3.

Krok 2. Teraz to už stací celé iba naprogramovat. Zmeny student.h ---CUT--- #include <afxtempl.h> ---CUT--- typedef CTypedPtrList<CObList, CStudent*> CStudentList;

Page 100: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Zmeny StudentListDoc.h public: CStudentList* GetList() { return &m_studentList; } private: CStudentList m_studentList; Zmeny StudentListDoc.cpp ---CUT--- CStudentListDoc::CStudentListDoc() { afxDump.SetDepth(1); } ---CUT--- void CStudentListDoc::Dump(CDumpContext& dc) const { CDocument::Dump(dc); dc << "\n" << m_studentList << "\n"; } ---CUT--- void CStudentListDoc::DeleteContents() { Dump(afxDump); while (m_studentList.GetHeadPosition()) { delete m_studentList.RemoveHead(); } } Zmeny StudentListView.h protected: POSITION m_position; // uchovava aktualnu poziciu v zozname CStudentList* m_pList; virtual void InsertEntry(POSITION position); virtual void GetEntry(POSITION position); Zmeny StudentListView.cpp ---CUT--- CStudentListView::CStudentListView() : CFormView(CStudentListView::IDD) { //{{AFX_DATA_INIT(CStudentListView) m_nKredity = 0; m_strMeno = _T(""); m_strPriezvisko = _T(""); m_nRocnik = 0; //}}AFX_DATA_INIT // TODO: add construction code here m_position=NULL; }

Page 101: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

---CUT--- void CStudentListView::OnInitialUpdate() { m_pList = GetDocument()->GetList(); CFormView::OnInitialUpdate(); } ---CUT--- void CStudentListView::OnWritedata() { InsertEntry(m_position); GetDocument()->SetModifiedFlag(); GetDocument()->UpdateAllViews(this); } ---CUT--- void CStudentListView::OnStudentInsert() { InsertEntry(m_position); GetDocument()->SetModifiedFlag(); GetDocument()->UpdateAllViews(this); } ---CUT--- void CStudentListView::OnStudentFirst() { if (!m_pList->IsEmpty()) { m_position = m_pList->GetHeadPosition(); GetEntry(m_position); } } ---CUT--- void CStudentListView::OnUpdateStudentFirst(CCmdUI* pCmdUI) { POSITION pos; pos = m_pList->GetHeadPosition(); pCmdUI->Enable((m_position != NULL) && (pos != m_position)); } ---CUT--- void CStudentListView::OnStudentLast() { TRACE("Entering CStudentView::OnCommandEnd\n"); if (!m_pList->IsEmpty()) { m_position = m_pList->GetTailPosition(); GetEntry(m_position); } } ---CUT--- void CStudentListView::OnUpdateStudentLast(CCmdUI* pCmdUI)

Page 102: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

{ POSITION pos; pos = m_pList->GetTailPosition(); pCmdUI->Enable((m_position != NULL) && (pos != m_position)); } ---CUT--- void CStudentListView::OnStudentNext() { POSITION pos; TRACE("Entering CStudentView::OnCommandNext\n"); if ((pos = m_position) != NULL) { m_pList->GetNext(pos); if (pos) { GetEntry(pos); m_position = pos; } } } ---CUT--- void CStudentListView::OnUpdateStudentNext(CCmdUI* pCmdUI) { POSITION pos; pos = m_pList->GetTailPosition(); pCmdUI->Enable((m_position != NULL) && (pos != m_position)); } ---CUT--- void CStudentListView::OnStudentPrev() { POSITION pos; TRACE("Entering CStudentView::OnCommandPrev\n"); if ((pos = m_position) != NULL) { m_pList->GetPrev(pos); if (pos) { GetEntry(pos); m_position = pos; } } } ---CUT--- void CStudentListView::OnUpdateStudentPrev(CCmdUI* pCmdUI) { POSITION pos; pos = m_pList->GetHeadPosition(); pCmdUI->Enable((m_position != NULL) && (pos != m_position)); } ---CUT---

Page 103: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

void CStudentListView::OnStudentRemove() { POSITION pos; if ((pos = m_position) != NULL) { m_pList->GetNext(pos); if (pos == NULL) { pos = m_pList->GetHeadPosition(); TRACE("GetHeadPos = %ld\n", pos); if (pos == m_position) { pos = NULL; } } GetEntry(pos); CStudent* ps = m_pList->GetAt(m_position); m_pList->RemoveAt(m_position); delete ps; m_position = pos; GetDocument()->SetModifiedFlag(); GetDocument()->UpdateAllViews(this); } } ---CUT--- void CStudentListView::OnUpdateStudentRemove(CCmdUI* pCmdUI) { pCmdUI->Enable(m_position != NULL); } ---CUT--- void CStudentListView::InsertEntry(POSITION position) { if (UpdateData(TRUE)) { // UpdateData returns FALSE if it detects a user error CStudent* pStudent = new CStudent; pStudent->m_strMeno = m_strMeno; pStudent->m_strPriezvisko = m_strPriezvisko; pStudent->m_nRocnik = m_nRocnik; pStudent->m_nKredity = m_nKredity; m_position = m_pList->InsertAfter(m_position, pStudent); } } ---CUT--- void CStudentListView::GetEntry(POSITION position) { if (position) { CStudent* pStudent = m_pList->GetAt(position); m_strMeno= pStudent->m_strMeno; m_strPriezvisko= pStudent->m_strPriezvisko; m_nRocnik= pStudent->m_nRocnik; m_nKredity=pStudent->m_nKredity; } else { OnClearview(); } UpdateData(FALSE); }

Page 104: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

---CUT--- void CStudentListView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) { m_position = m_pList->GetHeadPosition(); GetEntry(m_position); // natiahne prvotne data pre pohlad } ID ovládacieho prvku Správa Obslužná funkcia Trieda

ID_STUDENT_FIRST COMMAND OnStudentFirst CStudentListView

ID_STUDENT_FIRST UPDATE_COMMAND_UI

OnUpdateStudentFirst CStudentListView

ID_STUDENT_LAST COMMAND OnStudentLast CStudentListView

ID_STUDENT_LAST UPDATE_COMMAND_UI

OnUpdateStudentLast CStudentListView

ID_STUDENT_NEXT COMMAND OnStudentNext CStudentListView

ID_STUDENT_NEXT UPDATE_COMMAND_UI

OnUpdateStudentNext CStudentListView

ID_STUDENT_PREV COMMAND OnStudentPrev CStudentListView

ID_STUDENT_PREV UPDATE_COMMAND_UI

OnUpdateStudentPrev CStudentListView

ID_STUDENT_INSERT COMMAND OnStudentInsert CStudentListView

ID_STUDENT_REMOVE

COMMAND OnStudentRemove CStudentListView

ID_STUDENT_REMOVE

UPDATE_COMMAND_UI

OnUpdateStudentRemove

CStudentListView

Tab. 4 Ovládacie prvky projektu StudentList

ID ovládacieho prvku Opis ID_STUDENT_FIRST Posun na zaciatok zoznamu (na prvý záznam) ID_STUDENT_LAST Posun na koniec zoznamu (na posledný

záznam) ID_STUDENT_NEXT Posun na další prvok zoznamu ID_STUDENT_PREV Posun na predchádzajúci prvok zoznamu ID_STUDENT_INSERT Vloženie nového záznamu ID_STUDENT_REMOVE Odstránenie aktívneho záznamu Tab. 5 Význam ovládacích prvkov projektu StudentList TIPY NA DOMA. V helpe si pozrite informácie o kolekciách, vhodne doplnia výklad z tejto casti. Takisto sa snažte aspon scasti pochopit príklad Collect, uvedený v helpe. Skutocne, aj ked sa to nezdá, kolekcie sú velmi dôležitou súcastou aplikácného systému. Ich dokonalým zvládnutím sa zaradíte medzi „vyšších“ MFC programátorov. A co je najdôležitejšie, kolekcie urcite využijete pri mnohých projektoch a pomôžu vám sprehladnit váš kód. NABUDÚCE. V nasledujúcej casti sa naplno vrhneme na serializáciu, opíšeme si, ako ju spojazdnit v našich projektoch, samozrejme, nezabudneme na nejaký ukážkový príklad. Takže „docítania“ o mesiac... 19. cast: Súbory a serializácia

Page 105: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Ked cítate tieto riadky, nové Visual Studio je už realitou. Uvidíme, ako sa uchytí, každopádne každý, kto chce zostat „in“, by s ním mal zacat pracovat co najskôr. Niektorí ste sa ma v súvislosti s Microsoft .NET (ktorý získava na dôležitosti práve s uvedením nového Visual Studia) pýtali, ci vôbec ešte má zmysel ucit sa veci ako MFC, ATL (Active Template Library), COM (Component Object Model) a pod. Moja odpoved je áno. .NET síce mení tradicný spôsob vývoja aplikácií (a aplikácie samotné), ale s týmito vedomostami pochopíte .NET rýchlejšie a rýchlejšie zacnete aj vyvíjat .NET aplikácie. Samozrejme, netvrdím, že klasickému programovaniu Win32 „odzvonilo“, práve naopak, myslím si, že ešte dlho bude hrat dôležitú úlohu. Preto sa nebojte, urcite ešte budete mat možnost vedomosti o programovaní Win32 nejako zhodnotit :-).

V tejto a nasledujúcej casti si preberieme prácu so súbormi v prostredí Windows (samozrejme využitím MFC). Po jej precítaní by ste mali byt schopní uložit/cítat dáta (objekty, kolekcie atd.) na/z disk(u). Ako príklad si vytvoríme jednoduchý textový editor, ktorý si budete môct dalej rozšírit a vytvorit tak pre prax užitocnú aplikáciu. MFC a I/O OPERÁCIE. Ked sa rozpamätáte na casy klasického C-cka a operacného sytému DOS, práca so súbormi nepredstavovala velký problém. Pomocou funkcie fopen sa súbor otvoril, fgets alebo fscanf precítali dáta a fputs alebo fprintf dáta zapísali. Nakoniec sa ešte súbor pomocou fclose zatvoril a bolo to vybavené. V prostredí Windows to nie je inak, len sa zmenili mená funkcií a vdaka výnimkám sa zlepšila robustnost výsledných aplikácií. Knižnica MFC nám ponúka dva prostriedky na prácu so súbormi. Prvým je trieda CFile a druhým na nej založená serializácia. ZÁKLADY I/O OPERÁCIÍ V MFC – TRIEDA CFile. Trieda CFile predstavuje najjednoduchší spôsob, ako pracovat so súbormi v prostredí Windows. V helpe sa docítate, že táto trieda sprostredkúva nebufferované I/O operácie na binárnej úrovni. Cez jej potomkov podporuje napr. prácu s textovými súbormi, súbormi mapovanými do pamäte, „socketovými“ súbormi (také, ktoré zabezpecujú komunikáciu v sieti) a inými typmi súborov. My si ukážeme prácu s najvyššou vrstvou – triedou CFile. Ak ju zvládnete, práca s jej potomkami vám nebude robit problémy. PRÍKLAD 1 – TRIEDA CFile. Na príklade si ukážeme cítanie a zapisovanie do súboru. Vytvorte cisté Workspace s názvom Files a pridajte don nový projekt (Win32 Console Application s podporou MFC) s názvom ClassFile. Vetvu else vo funkcii _tmain zmente takto: char* pszFileName = "c:\\pokus.txt"; char* zapisat="Ahoj svet!"; char citat[11]; try { // skonstruujeme objekt CFile typu myFile CFile myFile(pszFileName, CFile::modeCreate | CFile::modeReadWrite); // zapisujeme do suboru myFile.Write(zapisat, 11); // ideme na zaciatok myFile.Seek(0, CFile::begin); // citame zo suboru

int pocet=myFile.Read(citat, 11); // vypiseme precitane data cout << citat; // zavrieme subor

myFile.Close(); } catch(CFileException *e) { // zistime podrobnosti o pricine vynimky ::AfxThrowFileException(e->m_cause, e->m_lOsError, pszFileName); }

Ako prvé skonštruujeme objekt typu CFile (myFile). Tento typ konštruktora nám súbor odovzdaný ako prvý parameter (pszFileName) aj otvorí, preto volaním funkcie Write môžeme priamo zapisovat dáta. Druhým parametrom konštruktora môžeme bližšie špecifikovat prístupové práva na súbor (Read, Write, Read/Write...). Viacero špecifikátorov potom oddelujeme pomocou bitového OR (|) (zoznam najpoužívanejších nájdete v tab. 1).

Page 106: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Špecifikátor Opis CFile::modeCreate Vytvorí nový súbor. Ak súbor už existuje, jeho

obsah sa zmaže CFile::modeNoTruncate Vytvára nový súbor. Ak súbor už existuje, jeho

obsah sa nezmaže CFile::modeRead Otvorí súbor na cítanie CFile::modeReadWrite Otvorí súbor na cítanie a zápis CFile::modeWrite Otvorí súbor na zápis CFile::modeNoInherit Zabranuje, aby súbor bol zdedený „detskými“

procesmi CFile::shareDenyNone Dovoluje ostatným procesom cítat/zapisovat do

súboru CFile::shareDenyRead Zakáže ostatným procesom cítat zo súboru CFile::shareDenyWrite Zakáže ostatným procesom zapisovat do súboru CFile::shareExclusive Nedovoluje ostatným procesom manipulovat so

súborom (Read/Write) Tab. 1 Zoznam najpoužívanejších špecifikátorov triedy CFile

Vo výpise kódu si všimnite presun na zaciatok súboru pomocou funkcie Seek . Tento presun je nevyhnutný, inak by sme cítali neexistujúce dáta, co by nám asi velmi nepomohlo. Ak by akákolvek operácia v bloku try zlyhala, zachytí sa výnimka typu CFileException, ktorú obslúžime v bloku catch. Funkcia AfxThrowFileException nám podá vysvetlenie, preco došlo k zlyhaniu (v helpe si môžete pozriet bližší opis všetkých chybových stavov).

V príklade sme si neukázali použitie clenských funkcií triedy CFile na zistovanie stavu súboru (jeho názov, cestu, cas a dátum vytvorenia atd). Tieto funkcie sú dobre opísané v helpe, preto im nebudeme zbytocne venovat miesto. SERIALIZÁCIA. Azda každá „lepšia“ aplikácia ponúka uloženie, resp. nacítanie dát do svojho programu z disku, prípadne iného úložného média (storage medium). Casto potrebujeme zapísat/cítat stav objektu (stav objektu je obycajne daný hodnotou jeho clenských prermenných) na/z disk(u). Tento proces zapisovania a cítania „stavu“ objektu na/z disk(u) sa nazýva serializácia. Ako som už uviedol, serializácia je založená na triede CFile. Nespomenul som však triedu CArchive, ktorá predstavuje medzivrstvu medzi serializáciou a triedou CFile (obr. 1) a je na fungovanie serializácie nevyhnutná.

Obr. 1 Vrstvy serializácie

Bližší opis triedy CArchive je v helpe, praktická ukážka jej použitia je v príklade 2. Všetky tieto vrstvy sa obycajne stretávajú vo funkcii Serialize (virtuálna clenská funkcia triedy CObject ), ktorú si prepíšete v triede, ktorej objekt chcete serializovat (ak budete využívat len objekt CArchive, funkciu Serialize síce prepisovat nemusíte, ale to vám velmi neodporúcam – pozri príklad 2). Dalšou podmienkou funkcnosti serializácie je, že trieda, ktorej objekt chcete serializovat, je odvodená od CObject a musí obsahovat makro DECLARE_SERIAL v hlavickovom súbore a makro IMPLEMENT_SERIAL v implementacnom súbore. Ak máte štandardnú SDI (resp. MDI) aplikáciu, implementáciu serializácie do tejto aplikácie vám aplikacný systém výrazne zjednoduší. Ale ak máte iný typ aplikácie, musíte sa o všetko postarat sami. Ukážeme si obidva prípady. Príklad 2 predstavuje „manuálnu“ serializáciu, príklad 3 zase serializáciu aplikácie SDI s využitím všetkých výhod aplikacného systému (ten si ukážeme až v dalšej casti). MAKRÁ DECLARE_SERIAL A IMPLEMENT_SERIAL. V 16. casti sme sa stretli s makrami DECLARE_DYNAMIC a IMPLEMENT_DYNAMIC, ktoré sprístupnovali run-time informácie o triede. Makrá DECLARE_SERIAL a IMPLEMENT_SERIAL majú podobnú funkciu, a síce sprístupnujú serializáciu pre triedu, v ktorej sa nachádzajú (robia triedu serializovatelnou). Zaujímavá je syntax makra IMPLEMENT_SERIAL, ktoré ako svoj tretí parameter dostáva verziu objektu konkrétnej triedy. Toto císlo (väcšie alebo rovné 0) sa nazýva císlo schémy objektu a zabranuje pri nacítavaní stavu objektu do pamäte nacítat stav objektu s inou schémou, ako je schéma jeho triedy v pamäti. Tým sa zabezpecí nacítanie správnej verzie objektu (podrobne pozri help).

Page 107: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

PRÍKLAD 2 – MANUÁLNA SERIALIZÁCIA JEDNODUCHÉHO OBJEKTU. Do Workspace Files pridajte nový projekt – Win 32 Console Application s podporou MFC. Názov projektu: BasicSerial. Pridajte novú triedu (generic class) CStudentSerial. Aby sme túto triedu urobili serializovatelnou, odvodte ju od CObject: class CStudentSerial : public CObject

Pridajte makro DECLARE_SERIAL a prepíšte funkciu Serialize DECLARE_SERIAL(CStudentSerial) void Serialize(CArchive& archive);

To sú zmeny v hlavickovom súbore. Do zdrojového (implementacného) súboru pridajte makro IMPLEMENT_SERIAL: IMPLEMENT_SERIAL(CStudentSerial, CObject, 1)

A telo funkcie Serialize: void CStudentSerial::Serialize(CArchive& archive) { // najprv zavolame povodnu funkciu CObject::Serialize(archive); // chceme citat alebo zapisovat? if(archive.IsStoring()) { archive << m_strMeno << m_strPriezvisko << m_nRocnik << m_nKredity; } else { archive >> m_strMeno >> m_strPriezvisko >> m_nRocnik >> m_nKredity; } }

Funkcia IsStoring (clenská funkcia triedy CArchive) urcuje, ci chceme stav objektu zapisovat alebo cítat. Operátory << a >> triedy CArchive vždy vracajú referenciu na objekt CArchive, preto je zápis v archive >> m_strMeno >> m_strPriezvisko >>... správny.

Teraz už môžeme pomocou objektu CArchive serializovat objekty tejto triedy. Kedže serializovat znamená ukladat stav objektu, ktorý je urcený hodnotami jeho clenských premenných, pridajte tieto verejné clenské premenné do triedy CStudentSerial: CString m_strMeno; CString m_strPriezvisko; int m_nRocnik; int m_nKredity;

Pridajte aj dve verejné funkcie (jedna bude slúžit na zápis a druhá na cítanie dát). void Read(); void Write();

Do zdrojového súboru pridajte kód funkcie Read: void CStudentSerial::Read() {

Page 108: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

char* pszFileName = "c:\\pokus.dat"; // skonstruujeme objekt CFile CFile theFile(pszFileName, CFile::modeRead); // skonstruujeme objekt CArchive a // spojime ho s objektom CFile CArchive archive(&theFile, CArchive::load); // citame data Serialize(archive); // zavrieme subor aj objekt CArchive archive.Close(); theFile.Close(); }

A kód funkcie Write: void CStudentSerial::Write() { char* pszFileName = "c:\\pokus.dat"; CFile theFile(pszFileName, CFile::modeCreate | CFile::modeWrite); CArchive archive(&theFile, CArchive::store); Serialize(archive); archive.Close(); theFile.Close(); }

Kód v týchto funkciách som z dôvodu šetrenia miesta neošetril na prípadné výnimky ako v príklade 1, ale nic vám nebráni tak urobit. Pri konštrukcii objektu CArchive urcuje druhý parameter konštruktora, ci chceme dáta zapisovat alebo cítat. Nesmieme zabudnút ani na uzavretie objektov CFile a CArchive. Keby ste nevolali funkciu Serialize, mohli by ste zapisovat/cítat dáta priamo vo funkcii Write/Read pomocou pretažených operátorov << a >> triedy CArchive.

Presunte sa teraz do súboru BasicSerial.cpp, kde pridajte jednu include direktívu: #include "StudentSerial.h" Teraz pridajte tento kód do vetvy else funkcie _tmain: // vytvorime objekt CStudentSerial studentObject; // naplnime ho datami studentObject.m_strMeno="Janko"; studentObject.m_strPriezvisko="Mrkvicka"; studentObject.m_nRocnik=2; studentObject.m_nKredity=66; // zapiseme data studentObject.Write(); // vyprazdnime premenne studentObject.m_strMeno=""; studentObject.m_strPriezvisko=""; studentObject.m_nRocnik=0; studentObject.m_nKredity=0; // precitame data studentObject.Read(); // vypiseme data cout << (LPCTSTR)studentObject.m_strMeno << "\n"; cout << (LPCTSTR)studentObject.m_strPriezvisko << "\n" ;

Page 109: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

cout << (UINT)studentObject.m_nRocnik << "\n"; cout << (UINT)studentObject.m_nKredity << "\n";

A to je všetko. Príklad si vyskúšajte (odporúcam odkrokovat) a skúste zapisovat dáta priamo vo funkciách Read/Write bez použitia funkcie Serialize (ale ako som už spomínal, takýto postup neodporúcam). POZNÁMKA K OPERÁTOROM << A >> TRIEDY CArchive. Trieda CArchive má pretažené tieto operátory len pre niektoré dátové typy jazyka C++ a triedy MFC (pozri tab. 2). Pri použití nejakého iného dátového typu je nevyhnutné explicitné pretypovanie na typ, ktorý CArchive pozná. Tu treba dat pozor na to, že operátor zapisovania (<<) je pretažený pre hodnoty a operátor cítania (>>) je pretažený pre referencie. Ak máme napríklad premennú m_nVar typu enum, správny kód, ktorý realizuje zápis/cítanie pomocou CArchive, by vyzeral takto: // zapis archive << (int) m_nVar; // citanie archive >> (int&) m_nVar; CObject* (ukazovatel na CObject)

SIZE and CSize Float

WORD CString POINT a Cpoint DWORD BYTE RECT a Crect Double LONG CTime a CtimeSpan Int COleCurrency ColeVariant COleDateTime COleDateTimeSpan Tab. 2 Dátové typy a triedy, ktoré operátory << a >> CArchive priamo poznajú PROBLÉMY S VLOŽENÝMI OBJEKTMI A UKAZOVATELMI. Co keby mala trieda CStudentSerial v sebe vložené nejaké iné objekty? Ako by potom vyzerala serializácia? Treba rozlišovat dva prípady: Trieda obsahuje vložený další objekt alebo trieda obsahuje len ukazovatel na další objekt.

Ak chceme tento další objekt (teraz je jedno, ci je priamo vložený alebo máme k dispozícii len ukazovatel) serializovat, musí byt odvodený od CObject, obsahovat funkciu Serialize a serializacné makrá. Ak je objekt priamo vložený do triedy, potom ho serializujeme tak, že vo funkcii Serialize triedy CStudentSerial zavoláme funkciu Serialize tohto objektu. Ak máme k dispozícii len ukazovatel na tento objekt, môžeme ho serializovat priamo s dátovými clenmi triedy CStudentSerial, pretože operátory CArchive dokážu pracovat s ukazovatelmi na CObject (co v prvom prípade, ked máme len objekt typu Cobject, nebolo možné). Pre lepšie pochopenie si pozrite nasledujúce výpisy:

Prípad 1: V triede CStudentSerial máme vložený další objekt: public: CRozvrh m_rozvrh;

Trieda CRozvrh obsahuje jeden dátový clen m_strDen typu CString. Má svoju funkciu Serialize. Funkcia Serialize triedy CStudentSerial potom vyzerá takto: void CStudentSerial::Serialize(CArchive& archive) {

// najprv zavolame povodnu funkciu CObject::Serialize(archive); // serializujeme vlozeny objekt m_rozvrh.Serialize(archive); ... }

Prípad 2: V triede CStudentSerial máme len ukazovatel na další objekt: public:

CRozvrh* m_rozvrh;

Page 110: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Potom funkcia Serialize vyzerá takto: void CStudentSerial::Serialize(CArchive& archive) { // najprv zavolame povodnu funkciu CObject::Serialize(archive); // chceme citat alebo zapisovat if(archive.IsStoring()) { // m_rozvrh mozeme serializovat priamo

archive << m_strMeno << m_strPriezvisko << m_nRocnik << m_nKredity << m_rozvrh;

} else { archive >> m_strMeno >> m_strPriezvisko >> m_nRocnik >> m_nKredity >> m_rozvrh; } } TIPY NA DOMA. Vyskúšajte si prácu aj s triedami odvodenými od CFile (odporúcam vyskúšat triedu CStdioFile). V helpe si pozrite podrobnejšie informácie o triede CArchive a jej clenoch. Ošetrite kód funkcií Write a Read v príklade 2, aby zachytával výnimky, ktoré môžu vzniknút pri práci s CFile a CArchive, a pokúste sa nasimulovat stavy, aby tieto výnimky aj nastali. Overte si na príklade, ako sa serializujú vložené objekty a ukazovatele na objekty. NABUDÚCE. V dalšej casti si preberieme serializáciu v aplikáciách SDI a pri tej príležitosti si povieme nieco o triedach CEditView a CRichEditView. Dovidenia v marci. 20. cast: Serializácia SDI aplikácií I V tejto casti budeme pokracovat v zacatom rozprávaní o serializácii a preberieme si serializáciu aplikácií typu SDI (Single Document Interface). Uvedieme si dva ukážkové príklady. Prvým bude už známa aplikácia StudentList, ktorú si rozšírime o možnost ukladania/cítania dát na/z disku. Druhým príkladom bude jednoduchý textový editor, na ktorom si okrem serializácie ukážeme prácu s triedami CEditView a CRichEditView. SERIALIZÁCIA A APLIKACNÝ SYSTÉM. V predchádzajúcej casti sme si ukázali, ako implementovat serializáciu do našich aplikácií „manuálne“ ciže bez použitia výhod, ktoré nám poskytuje aplikacný systém. Museli sme urobit vela vecí (vytvorit objekt CArchive, spojit ho s objektom CFile, ošetrit výnimky, ktoré by mohli nastat atd.). Mali sme síce priamu kontrolu nad každým serializovaným bajtom, ale pri väcších aplikáciách je takéto programovanie velmi prácne. Teraz si ukážeme, ako serializovat s využitím aplikacného systému. Kedže sa budeme venovat serializácii SDI aplikácií, musíme si povedat nieco aj o tom, ako tieto aplikácie pracujú. POHLAD DOVNÚTRA APLIKÁCIE SDI. V šestnástej casti sme si zhruba ukázali, ako aplikácia SDI pracuje. Ale co sa deje v jej vnútri? Aby sme nehovorili velmi abstraktne, všetko si vysvetlíme priamo na príklade. Vytvorte nový projekt (ci ho pridáte do Workspace Files, alebo nie, nechám na vás) s názvom SDIApp. Všetky nastavenia nechajte default. Po vygenerovaní kostry aplikácie pridajte na zaciatok funkcií (tab. 1) debug výpis pomocou objektu afxDump v tvare:

afxDump << "TRIEDA::FUNKCIA()\n"; Napríklad do funkcie CSDIAppApp::InitInstance by ste pridali: afxDump << "CSDIAppApp::InitInstance()\n";

Funkcie, do ktorých ste pridali tento debug výpis, sú „nosnými“ funkciami každej aplikácie SDI (nerátajte konštruktory jednotlivých objektov – tam sme debug výpis pridávali z iného dôvodu – pozri dalej). Niektoré sme si už opisovali v 16. casti, zvyšné si opíšeme teraz.

Page 111: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

CMainFrame::CMainFrame CSDIAppDoc::OnNewDocument CMainFrame::~CMainFrame CSDIAppDoc::Serialize CMainFrame::OnCreate CSDIAppView::CSDIAppView CMainFrame::PreCreateWindow CSDIAppView::~CSDIAppView CSDIAppApp::CSDIAppApp CSDIAppView::PreCreateWindow CSDIAppApp::InitInstance CSDIAppView::OnDraw CSDIAppDoc::CSDIAppDoc CSDIAppView::GetDocument CSDIAppDoc::~CSDIAppDoc Tab. 1 Zoznam funkcií na pridanie debug výpisu

int CMainFrame::OnCreate: Táto funkcia dostáva ako svoj parameter ukazovatel na štruktúru

CREATESTRUCT, ktorá obsahuje parametre vytváraného okna. Štandardne slúži na vytvorenie hlavného rámcového okna aplikácie (CFrameWnd::OnCreate(lpCreateStruct)), toolbaru (m_wndToolBar.CreateEx(...)) a statusbaru (m_wndStatusBar.Create(this)). Samozrejme, nic nám nebráni rozšírit ju podla našich potrieb.

BOOL CMainFrame::PreCreateWindow: Funkcia, ktorá je volaná ešte pred samotným zavolaním funkcie CMainFrame::OnCreate. Je tým správnym miestom na zmenenie vzhladu vytváraného okna (pomocou clenov štruktúry CREATESTRUCT).

BOOL CSDIAppApp::InitInstance: Je volaná zakaždým, ked vytvárame novú inštanciu našej aplikácie. V nej sa napríklad vytvára šablóna dokumentov (podrobnejšie dalej), zapisujú sa základné údaje do registrov (funkcia SetRegistryKey – špecifikuje, kam sa budú v registroch ukladat nastavenia aplikácie – podrobnejšie nabudúce), nahrá sa zoznam naposledy otvorených súborov (funkcia LoadStdProfileSettings), volá sa dôležitá funkcia CWinApp::OnFileNew (volá sa nepriamo cez funkciu CWinApp::ProcessShellCommand – odporúcam odkrokovat – Step Into), spracuje sa príkazový riadok aplikácie (objekt CCommandLineInfo – pozri help), pomocou funkcie (ShowWindow) sa zobrazí hlavné rámcové okno aplikácie. Mali by sme si ešte vysvetlit, co je inštancia aplikácie. Kedže Windows umožnuje beh viacerých kópií jedného programu súcasne, vzniká potreba nejakým spôsobom rozdelit inicializáciu aplikácie do niekolkých castí, aby sa pri spúštaní jej dalšej kópie nemusela zavádzat ešte raz celá aplikácia, ale len jej cast. Spúštanie (inicializácia) aplikácie je preto rozdelené do dvoch castí. Prvú predstavuje tzv. one-time initialization, co je inicializácia, ked sa spúšta aplikácia po prvýkrát (jej prvá kópia). Zakaždým, ked sa spúšta dalšia kópia tejto aplikácie, prebieha tzv. instance initialization („inicializácia inštancie“), ale už nepriebieha one-time initialization. Instance initialization prebieha aj pri prvom štarte aplikácie spolu s one-time initialization. Akýmsi opakom funkcie InitInstance je funkcia ExitInstance. Je volaná aplikacným systémom pri ukoncovaní inštancie aplikácie po zatvorení všetkých okien (podrobnejšie v helpe).

Šablóna dokumentu: Vo funkcii InitInstance si všimnite tento blok kódu: CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(CSDIAppDoc), RUNTIME_CLASS(CMainFrame), // main SDI frame window RUNTIME_CLASS(CSDIAppView)); AddDocTemplate(pDocTemplate);

Tu sa vytvára objekt šablóny dokumentu aplikácie typu SDI. Funkcia AddDocTemplate potom spája triedy aplikácie, dokumentu, pohladu a hlavného rámca našej aplikácie (pozor – nepliest so spájaním objektov, ktoré sa deje vo funkcii OnFileNew – pozri dalej). Zaujímavé na tom je, že ked sa takto spájajú tieto triedy, žiadne ich objekty okrem objektu aplikácie neexistujú, aplikacný systém ich vytvára dynamicky. Celý tento mechanizmus súvisí s dynamickou identifikáciou tried pocas run-time a dynamickým vytváraním objektov. Je založený na makrách (DECLARE_DYNAMIC, DECLARE_DYNCREATE, RUNTIME_CLASS) a vo svojej podstate je podobný RTTI (runtime type information), ktoré bolo predstavené vo Visual C++ 4.0. Mechanizmus dynamickej identifikácie tried pocas behu programu bol v MFC implementovaný ešte pred zavedením RTTI do C++ a stále sa aj používa, kedže poskytuje viac možností ako spomínané RTTI. Nebudeme sa tu teraz tejto problematike podrobne venovat, ak nám niekedy pri konci seriálu zostane cas, povieme si o nej viac. Nedockavcov zatial odkážem na help. O šablónach dokumentov sa ešte zmienime pri aplikáciách MDI.

CWinApp::OnFileNew: Túto funkciu v kóde nevidno, ale vykoná pre nás velký kus roboty, preto si o nej povieme viac. Ako sme už uviedli, je volaná prostredníctvom funkcie ProcessShellCommand. Vytvára pre nás objekt dokumentu, objekt hlavného rámca, hlavné rámcové

Page 112: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

okno aplikácie (nezobrazuje ho). Dalej vytvára objekt pohladu, okno pohladu (ale takisto ho nezobrazuje) a spájs objekt dokumentu, objekt hlavného rámca a objekt pohladu. Potom volá funkcie týchto objektov CDocument::OnNewDocument pre objekt dokumentu, CFrameWnd::ActivateFrame pre objekt hlavného rámca a CView::OnInitialUpdate pre objekt pohladu. Funkciu CFrameWnd::ActivateFrame sme si ešte neopisovali, takže len strucne: táto funkcia zobrazuje hlavné rámcové okno aplikácie (to je to najpodstatnejšie, viac nájdete v helpe). Funkcia OnFileNew tiež reaguje na výber položky z menu File-New (prípadne stlacením príslušného tlacidla na toolbare, resp. skratkového klávesu). V tomto prípade sa však správa trochu inak. V aplikácii SDI sú objekty dokumentu, hlavného rámca a pohladu vytvorené iba raz (pri štarte aplikácie). Pri výbere File-New z menu už logicky vytvorené musia byt (inak by ani žiadne menu nebolo), preto v takomto prípade funkcia OnFileNew preskocí ich vytváranie a následné spojenie a svoju cinnost zacína až volaním CDocument::OnNewDocument.

Teraz už asi tušíte, preco sme pridávali debug výpisy do jednotlivých funkcií. Takto sa totiž dá najlahšie zistit, ako aplikácia SDI pracuje (v akom poradí sú uvedené funkcie volané, nám prezradí, co sa deje pri spustení aplikácie, ked s aplikáciou pracujeme, zistíme, aká funkcia sa pri akej cinnosti volá atd.). Spustte teda aplikáciu SDIApp v debug móde (F5) a hned ju zase ukoncite. Pozrite sa na kartu Debug. Malo by vám vyjst takéto poradie: CSDIAppApp::CSDIAppApp() CSDIAppApp::InitInstance() CSDIAppDoc::CSDIAppDoc() CMainFrame::CMainFrame() CMainFrame::PreCreateWindow() CMainFrame::PreCreateWindow() CMainFrame::OnCreate() CSDIAppView::CSDIAppView() CSDIAppView::PreCreateWindow() CSDIAppDoc::OnNewDocument() CSDIAppView::OnDraw() CSDIAppView::GetDocument() CSDIAppView::~CSDIAppView() CMainFrame::~CMainFrame() CSDIAppDoc::~SDIAppDoc()

My sme si už poradie funkcií všeobecnejšie opísali (pozri obr. 1 v 16. casti seriálu), teraz sme si ho prebrali podrobnejšie. Asi sa pýtate, preco sa CMainFrame::PreCreateWindow() volá dvakrát za sebou. Prvýkrát sa táto funkcia zavolá pri vytváraní objektu hlavného rámca a o druhé zavolanie sa stará kód vnútri funkcie CFrameWnd::LoadFrame (odporúcam odkrokovat). Skúste chvílu s aplikáciou pracovat a sledujte, aká funkcia sa zavolá pri tej-ktorej cinnosti (pomocou Class Wizardu si pridajte aj dalšie funkcie – CView::OnInitialUpdate, CFrameWnd::ActivateFrame a pod. – a zistite, pri akej cinnosti sú volané). IMPLEMENTÁCIA SERIALIZÁCIE. Teraz, ked už vieme ako aplikácia SDI pracuje, môžeme spojazdnit serializáciu. So serializáciou súvisia operácie File-> Open a File->Save, resp. File-> Save as.

Otvorenie súboru je obslúžené funkciou CWinApp::OnFileOpen. Táto funkcia je namapovaná na položku menu ID_FILE_OPEN. Funkcia zo svojho tela volá dalšie funkcie, ktoré najprv zobrazia štandardný File-Open dialóg, potom volajú funkciu CDocument::OnOpenDocument (táto funkcia otvorí súbor, zavolá funkciu DeleteContents, aby bolo isté, že dokument je prázdny, potom volá funkciu CObject::Serialize, ktorá nacíta dáta. Aplikacný systém volá túto funkciu na reinicializáciu existujúceho objektu dokumentu (teda pri otváraní súboru sa nevytvára nový objekt dokumentu, ale využije sa už existujúci – overte si to pomocou výpisov afxDump).

Uloženie súboru sa zacína volaním funkcie OnFileSave (štandardne mapovaná na položku menu ID_FILE_SAVE). Volá funkciu CDocument::OnSaveDocument, ktorá sa postará o všetko potrebné (volá funkciu CObject::Serialize a tá zapíše dáta z dokumentu na disk). V prípade Save as je situácia rovnaká, ibaže obslužná funkcia položky ID_FILE_SAVE_AS sa volá OnFileSaveAs. ÚPRAVA PROJEKTU STUDENTLIST. Teraz pridáme serializáciu do nášho projektu StudentList (verzia z 18. casti). Co tipujete, kolko bude potrebné pridat kódu? Ked vám poviem, že stací pridat jednu funkciu a navyše ešte tri riadky kódu, uveríte mi? Ak nie, hned vás o tom presvedcím. V hlavickovom a implementacnom súbore triedy CStudent zmente makrá DECLARE_DYNAMIC a IMPLEMENT_DYNAMIC na DECLARE_SERIAL(CStudent) a IMPLEMENT_SERIAL(CStudent, CObject, 0). Týmito makrami sme sprístupnili serializáciu, ale „odstavili“ sme prístup k run-time

Page 113: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

informáciám o tejto triede, co nám však teraz velmi neprekáža. V hlavickovom súbore pridajte deklaráciu virtuálnej funkcie Serialize: virtual void Serialize(CArchive& ar); a v implementacnom súbore zapíšte túto funkciu takto: void CStudent::Serialize(CArchive& ar) { if (ar.IsStoring()) { ar << m_strMeno << m_strPriezvisko << m_nRocnik << m_nKredity; } else { ar >> m_strMeno >> m_strPriezvisko >> m_nRocnik >> m_nKredity; } }

Teraz už len stací pridat jeden riadok kódu na koniec funkcie CDocument::Serialize: void CStudentListDoc::Serialize(CArchive& ar) { if (ar.IsStoring()) { // TODO: add storing code here } else { // TODO: add loading code here } m_studentList.Serialize(ar); } a je koniec. Aplikáciu odskúšajte – vytvorte záznamy aspon o troch študentoch a uložte ich, potom vytvorte nový dokument a údaje nacítajte. Odporúcam vám aj do tejto aplikácie pridat informatívne výpisy do dôležitých funkcií, prípadne celú aplikáciu odkrokovat. Tak najlepšie pochopíte, co sa kedy deje. PRÍPONY SÚBOROV A REGISTRÁCIA PROGRAMU. Terajšia verzia aplikácie StudentList vám síce umožnuje uložit dáta do súboru, ale tento súbor je bez prípony a bežnému používatelovi nie je jasné, s akou aplikáciou je spojený. Aby sme mohli vytvárat súbor s príponou a ten asociovat s našou aplikáciou, musíme si ešte vysvetlit, co je to zdroj šablóny dokumentu. Zdroj šablóny dokumentu používame pri volaní funkcie AddDocTemplate. Je to prvý parameter konštruktora objektu typu CSingleDocTemplate (obycajne sa nazýva IDR_MAINFARME). Ked sa pozriete do zdrojov na položku String Table a tam rozkliknete IDR_MAINFRAME, uvidíte jeden retazec, ktorého podretazce sú oddelené pomocou znaku \n. Upravte retazec takto: StudentList\n\nStudent\nStudentList Files (*.Slt)\n.Slt\n StudentList.Document\nStudent Document

Pre lepšie pochopenie uvádzam ešte vysvetlenie každého z podretazcov (tab. 2). Podretazec Opis StudentList\n titulok okna aplikácie \n implicitné meno dokumentu (ak necháme

nezadané (náš prípad), bude Untitled Student\n meno typu dokumentu StudentList Files (*.Slt)\n opis typu dokumentu a filter .Slt\n prípona dokumentu StudentList.Document\n ID typu dokumentu v registroch

Page 114: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Student Document opis ID v registroch Tab. 2 Vysvetlenie podretazcov v IDR_MAINFRAME

Ked teraz spustíte StudentList, pri otváraní a ukladaní súborov už budete mat k dispozícii príponu, filter a opis typu dokumentu.

Obr. 1 Informácie o type prípony v registroch Stále nám však ešte chýba asociácia súborov s príponou *.Slt s naším programom. Pridajte preto volanie funkcií EnableShellOpen a RegisterShellFileTypes do funkcie CStudentListApp::InitInstance: EnableShellOpen(); RegisterShellFileTypes(TRUE);

Prvá funkcia umožní spúštanie našej aplikácie priamo zo súborového prehliadaca a druhá zaregistruje prípony súborov v registroch (podrobne pozri help). Teraz už môžeme spúštat súbory programu priamo (pozrite sa v registroch do HKEY_CLASSES_ROOT, kde vyhladajte .Slt – obr. 1). Samozrejmostou je aj priradenie ikony súborom s príponou *.Slt. Zdroj tejto ikony predstavuje IDR_SDIAPPTYPE (nájdete v zložke ICON v zdrojoch).

K dokonalosti už ostáva len sfunkcnit tzv. Drag and Drop ciže otvorenie súboru jeho priamym pretiahnutím na okno programu (jednoducho napr. z Explorera zoberiete súbor a taháte ho až nad okno programu, kde ho pustíte). Opät do funkcie CStudentListApp::InitInstance pridajte tento riadok: m_pMainWnd->DragAcceptFiles();

Opis tejto funkcie je v helpe. Drag and Drop teraz bude fungovat. (Poznámka: Nastavenie týchto vlastností sa dá komfortnejšie urobit už pri tvorení aplikácie – vo 4. kroku tvorenia aplikácie pomocou AppWizardu – kliknite na Advanced a vyplnte požadované polia,

Page 115: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

hlavne pole File extension). AppWizard teraz pridá všetky tu opisované funkcie automaticky a už žiadne dalšie úpravy nebudú potrebné). NABUDÚCE. Ako sa tak dívam na velkost súboru, opis slúbených tried CEditView a CRichEditView a ukážkovú aplikáciu si necháme do budúcej casti. O mesiac dovidenia. 21. cast: Serializácia SDI aplikácií II. Vítam vás pri dalšom pokracovaní nášho rozprávania o programovaní pod Windows. V tejto casti uvedením ukážkového príkladu dokoncíme opis serializácie aplikácií SDI. Vytvoríme jednoduchý textový editor r la Poznámkový blok (využitím triedy MFC CEditView). Slubovaný druhý príklad (zložitejší textový editor s využitím triedy CRichEditView) zatial neuvedieme. Používa niektoré zložitejšie prvky, ktoré by ste teraz mali problém pochopit. Tento príklad si uvedieme až na záver seriálu, ked si preberieme veci ako tlac, vytváranie helpu, prípadne vytváranie inštalácií. Bude to komplexný príklad na záver, ktorý zhrnie väcšinu preberanej problematiky. Teraz si namiesto toho preberieme prácu s registrami Windows. TRIEDA CEditView. Pri dialógoch sme sa stretli s ovládacím prvkom textové pole (Edit Box – trieda CEdit). Je to ovládací prvok Windows, do ktorého je možné zadávat text. Trieda CEditView sa správa podobne ako textové pole, ibaže jej pole pôsobnosti je iné. Zjednodušene by sa dalo povedat, že umožnuje aplikovat ovládací prvok textové pole na celý pohlad. V skutocnosti je to, samozrejme, zložitejšie. Dôležité je zbytocne nehladat súvislosti medzi týmito triedami. CEditView je trieda pohladu (ako napríklad už známe CScrollView, CFormView) odvodená od CView. CEdit je trieda ovládacieho prvku (odvodená priamo od CWnd). CEditView dokáže bez akéhokolvek nášho programovania obsluhovat (okrem iných) príkazové správy, ako vidíte v tab. 1. Ide o štandardný balík funkcií každého textového editora. O tom, ako túto funkcnost sprístupnit a využit, si povieme v príklade 1. Podrobnejšie informácie o triede CEditView nájdete v helpe. Odporúcam pozriet si vlastnosti tejto triedy. ID_EDIT_SELECT_ALL ID_EDIT_FIND ID_EDIT_PASTE ID_EDIT_COPY ID_EDIT_REPLACE ID_EDIT_REPEAT Tab 1 Implicitná funkcnost v CEditView PREDPRÍPRAVA NA PRÍKLADY. Vytvorte nové Workspace s názvom Editors. Don pridajte ukážkový príklad 1 z tejto casti. Neskôr nám bude slúžit aj na druhý príklad, vytváraný na základe triedy CRichEditView. Obr. 1 Výber základnej triedy pre pohlad

Page 116: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Obr. 1 Výber základnej triedy pre pohlad PRÍKLAD 1 – MyNotepad. Do Workspace Editors pridajte pomocou App Wizardu nový projekt s názvom MyNotepad. Pri tvorení kostry projektu vyberte na zaciatku možnost SDI Application, v kroku 4/6 kliknite na tlacidlo Advanced. V dialógu Advanced Options vpíšte do pola File extension: mxt, ostatné nastavenia nechajte na default. Pre názornost som pre náš program zvolil odlišnú príponu súborov namiesto klasickej txt. Program dokáže pracovat aj s klasickými textovými súbormi (prípona *.txt – pozri dalej – Nastavenie filtra). Dialóg zavrite a pokracujte na posledný krok 6/6 tvorenia kostry aplikácie pomocou App Wizardu (všetky ostatné vlastnosti projektu nechajte na default). V kroku 6/6 vyberte zo zoznamu ako základnú triedu pre pohlad triedu CEditView (obr. 1). Kliknite na Finish. Ak projekt MyNotepad teraz skompilujete a zlinkujete, máte funkcný program, ktorý vám umožní písat do jeho pohladu text, ten ukladat, resp. cítat do/zo súboru (áno, máme fungujúcu serializáciu a nemuseli sme nic kódovat!). Skúste si MyNotepad spustit, napíšte do jeho okna nejaký text, ten uložte, potom otvorte (vyskúšajte si ho otvorit vš etkými tromi spôsobmi, ako sme si opisovali v predchádzajúcej casti, t. j. cez menu File – Open, otvorením priamo z Explorera a pomocou drag and drop). Co na to poviete? Dostali sme relatívne vela funkcnosti, a to bez napísania jediného riadka kódu. POZRIMA SA NA KÓD. Podme sa teda pozriet, co nám to App Wizard všetko vygeneroval. Súbory Mainfrm.h a Mainfrm.cpp neobsahujú pre nás v tejto chvíli nic zaujímavé. Pozrite sa do súboru MyNotepad.cpp do funkcie InitInstance. Všetky funkcie, ktoré sme prácne pridávali v predchádzajúcej casti, za nás pridal App Wizard. Pridal ich na základe našej volby prípony v kroku 4/6. Teraz otvorte súbor MyNotepadDoc.cpp. Tu sa nachádza celá „logika“ našej aplikácie. Zacneme vo funkcii OnNewDocument, kde môžete vidiet tento zaujímavý riadok kódu: ((CEditView*)m_viewList.GetHead())->SetWindowText(NULL);

m_viewList je deklarovaná hlboko v útrobách MFC (súbor afxwin.h) ako premenná typu CPtrList. V 18. casti sme si CPtrList opisovali, takže už by ste mali vediet, že premenná tohto typu bude predstavovat nešablónovú kolekciu typu zoznam (uchováva „prázdne“ – void ukazovatele – podrobne v helpe) . V našom prípade je m_viewList zoznamom ukazovatelov na pohlady. Riadok vo funkcii OnNewDocument získa „prvý“ pohlad v tomto zozname, pretypuje ho na CeditView (co musí,

Page 117: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

kedže ide o „prázdny“ ukazovatel) a pomocou funkcie SetWindowText nastaví text. Ako už viete zo 16. casti, OnNewDocument sa volá pri reinicializácii dokumentu, preto SetWindowText(NULL) vymaže akýkolvek text, ktorý mohol byt v okne, a vytvorí tak „cistý“ dokument (rozumej: neobsahujúci text). Skúste si namiesto parametra NULL napísat nejaký text, napr.: ((CEditView*)m_viewList.GetHead())->SetWindowText("Mama ma Emu"); a potom spustit program. Zmení sa nieco? Teraz sa v súbore presunte do funkcie Serialize. Celá serializácia je „vybavená“ jediným riadkom: ((CEditView*)m_viewList.GetHead())->SerializeRaw(ar);

SerializeRaw je clenskou funkciou triedy CeditView. Je to špeciálna funkcia urcená na ukladanie textu vo formáte raw a neumožnuje serializovat iné informácie (napr. informácie o objektoch, ako to robí štandardná funkcia Serialize). Túto funkciu nájdete v súbore viewedit.cpp (ak máte nainštalované Visual C++ vrátane zdrojových textov k MFC). Odporúcam si ju pozriet [dostanete sa k nej pomocou Step Into (F11) pocas debuggovania]. V tomto súbore preskúmajte aj funkcie WriteToArchive a ReadFromArchive. Nie je nevyhnutné ich dokonale pochopit, ale obsahujú niektoré finty, ktoré možno niekedy vo svojich programoch využijete. SÚBOR MyNotepad.reg. Ked sa pozriete na kartu Files v okne Workspace, nájdete tam súbor s príponou reg. Tento súbor bol vygenerovaný App Wizardom a ukazuje nám, aké zmeny vykoná v registroch funkcia RegisterShellFileTypes. Už by ste mali vediet, že táto funkcia zápisom v registroch asociuje súbory s príponou mxt s našou aplikáciou (pozri dalej – Práca s registrami). ROZŠÍRENIE APLIKÁCIE MyNotepad. Teraz pridáme funkcnost opísanú v tab. 1. Aj ked „pridat“ nie je v tomto prípade správne oznacenie, kedže mi túto funkcnost len sprístupníme. Celé je to velmi jednoduché. V editore zdrojov chodte do zložky Menu a rozkliknite IDR_MAINFRAME. Do menu Edit pridajte položky, ako vidíte na obr. 2. Ich hlavicku a ID vypíšte podla tab. 2. Všimnite si, že automaticky po zadaní ID dokáže aplikacný systém identifikovat tento ovládací prvok a priradí mu opis (Prompt na záložke General). Do hlavicky ste vpisovali aj skratkový kláves (\tXXX). Na jeho sfunkcnenie je ešte potrebné zeditovat takzvaný Accelerator.

Obr. 2 Menu Edit v aplikácii MyNotepad Otvorte zložku Accelerator a v nej rozkliknite opät zdroj IDR_MAINFRAME. Mali by ste vidiet to, co je na obr. 3. Tento zoznam obsahuje všetky skratkové klávesy, ktoré používa váš program. Dvakrát kliknite na prázdnu položku (na konci zoznamu), resp. z menu Insert vyberte New Accelerator. Objaví sa dialóg ako na obr. 4. Do položky ID vpíšte postupne ID pridaných položiek do menu Edit (tab. 2) a do položky Key zase jednotlivé skratkové klávesy (už bez CTRL, kedže CTRL je už štandardne zaškrtnuté – check Modifiers). Tým sme s úpravami menu Edit skoncili, aplikáciu preložte, spustte a vyskúšajte pridané veci. Opät si uvedomte, že všetko sme dosiahli bez napísania jediného riadka kódu! &Find...\tCtrl+F ID_EDIT_FIND Replace…\tCtrl+H ID_EDIT_REPLACE Repeat\tCtrl+R ID_EDIT_REPEAT

Page 118: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Select all\tCtrl+A ID_EDIT_SELECT_ALL Tab. 2 ID položiek menu NASTAVENIE FILTRA. Pri práci s našou aplikáciou iste narazíte na jednu nepríjemnost. Ak chcete otvorit nejaký súbor, máte na výber len príponu mxt alebo všetky súbory (*.*). MyNotepad je však príkladom jednoduchého textového editora, preto by mal takto priamo dokázat pracovat aj s inými príponami, predovšetkým s txt. Nastavenie podpory pre rôzne prípony sa robí pomocou tzv. filtra. S týmto pojmom ste sa už stretli, ked sme si vysvetlovali spolocné dialógy Windows (File Dialog). Tam na nastavenie filtra stacilo upravit konštruktor tohto dialógu.

Obr. 3 Zdroj skratkových klávesov V aplikácii MFC, založenej na interakcii dokument – pohlad, musíme urobit trochu viac. V prvom rade namapujte do triedy CMyNotepadDoc príkazovú správu na ID_FILE_OPEN (cím vlastne prepíšete štandardné volanie MFC). Jej telo zmente takto: void CMyNotepadDoc::OnFileOpen() { // konštrukcia objektu dialógu //(všetko jeden riadok) CFileDialog Fdlg(TRUE, NULL, NULL, NULL, "MyNotepad Files (*.mxt)|*.mxt

|Text Files (*.txt)|*.txt |All Files (*.*)|*.*||");

// otvoríme súbor if (Fdlg.DoModal()==IDOK) { AfxGetApp()->OpenDocumentFile(Fdlg.GetPathName()); } } Keby ste OnFileOpen neprepísali, štandardná implementácia tejto funkcie volá funkciu CWinApp::DoPromptFileName, ktorá zobrazí klasický File-Open dialóg s údajmi, ktoré sa nachádzajú v zdroji IDR_MAINFRAME (String Table – pozri predchádzajúcu cast). Potom DoPromptFileName volá funkciu CWinApp::OpenDocumentFile, ktorá má ako parameter meno súboru, ktorý chceme otvorit. Tá sa aj postará o jeho otvorenie. My preskocíme volanie funkcie DoPromptFileName a dáta, ktoré potrebuje OpenDocumentFile, získame z nami vytvoreného File dialógu (do ktorého sme pridali podporu viacnásobného filtra). Na jej volanie používame funkciu AfxGetApp, ktorá vracia ukazovatel na objekt aplikácie (CWinApp). PRÁCA S REGISTRAMI. S príchodom 32-bitových Windows došlo k viacerým podstatným zmenám. Jednou z nich bolo zavedenie registrov namiesto dovtedy používaných súborov INI. Presný opis

Page 119: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

registrov a ich filozofiu si tu nebudeme vysvetlovat, podrobné informácie nájdete na internete, v helpe k VC++ alebo nejakej knihe. Nám bude stacit predstavit si registre ako nejakú hierarchickú databázu, ktorá slúži Windows na ukladanie systémových informácií o nich samotných a aplikáciám na ukladanie rôznych nastavení a záznamov. K jednotlivým informáciám v registroch sa pristupuje pomocou klúcov (keys), v ktorých sú uložené nejako pomenované hodnoty (value name), ktoré môžu obsahovat dalšie dáta (value data). Pre nás ako programátorov je najdôležitejšie vediet, ako do registrov zapisovat a cítat z nich údaje. Ukážeme si to na príklade aplikácie MyNotepad. PRÍKLAD 2 – MYNOTEPAD A REGISTRE. Ako som už spomenul, registre slúžia ako náhrada súborov INI z cias 16-bitových Windows. Ale aj terajšie aplikácie casto používajú súbory INI (prácu s nimi sme si ukázali v 17. casti).

Obr. 4 Pridanie klávesovej skratky položke menu (resp. toolbaru) Preto aj aplikácie vygenerované App Wizardom umožnujú presmerovat zápis do registrov (prednastavený) na zápis do súboru INI. Otvorte súbor MyNotepad.cpp a vo funkcii InitInstance nájdite riadok: SetRegistryKey(_T("Local AppWizard-Generated Applications")); Tento riadok vytvára v registroch klúc: Local AppWizard-Generated Applications, v ktorom vytvorí další klúc s názvom aplikácie a don ukladá informácie (pozrite sa do HKEY_CURRENT_USER\Software, co tam všetko je). Ak si v helpe precítate podrobnejšie informácie o funkcii SetRegistryKey, zistíte, že jej vynechaním sa namiesto do registrov budú tieto informácie zapisovat do súboru INI. To stojí za vyskúšanie. Okomentujte volanie tejto funkcie, aplikáciu spustte, otvorte nejaký súbor a potom ju uzavrite. Chodte do adresára Windows a nájdite tam súbor MyNotepad.ini. Malo by v nom byt zaznamenané meno otvoreného súboru (v položke Recent File List). Ak volanie SetRegistryKey znova sprístupníte a zopakujete otvorenie nejakého súboru v programe MyNotepad, informácia o tom sa už zapíše do registrov. My však nechceme, aby naša „premakaná“ aplikácia MyNotepad bola v registroch pod nic nehovoriacim klúcom Local AppWizard... Zmente preto volanie funkcie SetRegistryKey takto: SetRegistryKey(_T("PC REVUE"));

Teraz sa budú všetky informácie ukladat do klúca s názvom PC REVUE (jeho pozícia je opät HKEY_CURRENT_USER\Software).

Na prácu s registrami slúžia funkcie GetProfileInt, WriteProfileInt, GetProfileString, WriteProfileString a i. (napr. WriteProfileBinary), všetko metódy CWinApp – nájdete v helpe). V 17. casti sme opisovali podobné funkcie, ibaže mali vo svojom názve ešte slovícko Private. Tie môžete, samozrejme, takisto použit na zápis údajov do registrov. Použitie clenských funkcií CWinApp je však trochu pohodlnejšie, kedže nemusíte vypisovat celú cestu ku klúcu. Napríklad zápis retazca môj textový editor ? do klúca (key) Settings do hodnoty menom (value name) AboutString prislúchajúceho našej aplikácii by ste vykonali volaním funkcie WriteProfileString napr. z InitInstance takto: AfxGetApp()->WriteProfileString("Settings", "AboutString", "môj textový editor :-)");

Cítanie a zápis iných druhov informácií sú velmi jednoduché a nevyžadujú, aby sme ich opisovali. Urcite však všetko vyskúšajte. TIPY NA DOMA. Všetky úpravy prevedte do aplikácie MyNotepad. Pridajte na toolbar tie položky, co ste pridávali do menu. Pohrajte sa s ikonou dokumentu, skúste vytvorit vlastnú. Pridajte podporu

Page 120: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

viacnásobného filtra aj na ukladanie súboru (položka menu File-Save, resp. File-Save as... (Návod: pozrite si v helpe, ako sa prepisujú štandardné príkazy MFC – Technical Note 22 – Standard Commands Implementation). V helpe si pozrite informácie o funkciách DoPromptFileName a OpenDocumentFile (všetky metódy CWinApp). Toto cvicenie odporúcam spravit: Do registrov do klúca Settings pridajte dve položky: AboutString a AboutInt. AboutString bude predstavovat retazec, ktorý natiahnete do dialógu O aplikácii... a AboutInt bude císlo verzie (takisto zobrazené v About dialógu) – ciže ked používatel vyberie z menu Help-About MyNotepad..., všetky informácie okrem ikony natiahnete z registrov. Ak budete mat chut, môžete sa pohrat aj so zápisom a cítaním iných ako celocíselných hodnôt (napr. skúste nejako z registrov precítat císlo verzie 1.13 – reálne císlo). NABUDÚCE. O mesiac sa pozrieme na zúbky aplikáciám MDI. Dovidenia v máji. 22. cast: Serializácia aplikácií MDI Nastal máj, lásky cas. Dúfam, že ked cítate tieto riadky, pocasie sa už umúdrilo a je pekne teplo a slnecno, tak ako sa na máj patrí. A preco som zacal práve o pocasí? Práve ked píšem tieto riadky, vonku už asi piaty den za sebou na striedacku prší alebo sneží, Dunaj má blízko k 9 metrom a sila vetra je takisto pozoruhodná. Ideálny cas sediet doma s nejakou dobrou knihou/manuálom ?, prípadne pocítacovou hrou. Týchto vecí sa teraz zriekam, aby som sa s vami podelil o dalšie vedomosti z oblasti programovania. Opíšeme si aplikácie MDI a na príklade si ukážeme ich reálne použitie. MDI. MDI je skratka pre Multiple Document Interface aplikácie, ciže také, ktoré môžu obsahovat viacero objektov dokumentu (iná, menej presná definícia hovorí, že sú to také aplikácie, ktoré môžu mat vo svojom rámcovom okne ešte dalšie „okienka“). S príchodom 16-bitových Windows 3.0 (s ktorými sa uviedol aj štandard MDI) sa hovorilo, že aplikácie MDI sú tým pravým a predstavujú štandard, ktorý by mala dodržiavat každá aplikácia Windows. Táto predstava spôsobila menšiu „davovú“ psychózu medzi programátormi a každý sa za každú cenu snažil co najviac využívat „štandard MDI“ vo svojich aplikáciách. Ked prišli nové Windows s ponukou Štart, cím sa zjednodušilo prepínanie medzi aplikáciami, prišla s nimi aj nová teória: naco je niekomu vela okienok v jednej aplikácii (kde sme obmedzení velkostou zobrazitelnej plochy na monitore), ked sa môže jednoducho prepínat medzi tou istou aplikáciou a pracovat tak s jej X okienkami naraz? (Túto filozofiu v praxi dobre vidiet napr. vo Windows CE, ktoré aplikácie MDI ani nepodporujú). Výsledkom bola „psychóza 2“, ked zase všetci prepisovali svoje aplikácie MDI na SDI. V súcasnosti opät upadá SDI a coraz viac sa zacína presadzovat štandard MDI. A aké ponaucenie z toho plynie? Neriadte sa zbytocne „módou“, tá je v programovaní velmi nestála a premenlivá a nesnažte sa do bodky dodržiavat všetky najnovšie štandardy. Lepšie urobíte, ked na prvom mieste budete mat funkcnost vašej aplikácie. Ušetríte si tak mnoho starostí. Ale to som odbocil. Vo Windows môžete nájst mnoho príkladov aplikácií MDI [napr. súcasti Visual Studia (VC++, VB...), súcasti Office (Word, Excel, Access...)]. Najlepšie bude, ak si teraz bez dalšej teórie jednu aplikáciu MDI vytvoríme, aby ste chápali, o com tu píšem. PRÍKLAD 1 – MDI MYNOTEPAD. Ked si spomeniete na predchádzajúcu cast, tam sme vytvárali jednoduchý SDI textový editor (ktorý možno teraz používate namiesto klasického notepadu ?). Jeho základnú funkcnost sme dosiahli bez napísania jediného riadka kódu, preto si teraz vytvoríme jeho verziu MDI. Do Workspace Editors pridajte nový projekt [typ – MFC AppWizard (Exe)] s názvom MDIMyNotepad. V prvom kroku nechajte zaškrtnuté defaultné nastavenie (Multiple Documents). Pokracujte do kroku 4/6 (nemente nijaké nastavenia), kde kliknite na Advanced... a do pola File Extension vpíšte text: mxt. Dalej v kroku 6/6 vyberte pre triedu pohladu CMDIMyNotepadView zo zoznamu Base class: základnú triedu CEditView. Ked dokoncíte tvorenie aplikácie, preložte a spustite ju. Pri prvom spustení sa vám zobrazí okno, aké vidíte na obr. 1.

Preskúmajte jej možnosti (File – New, File – Open, menu Window). Skúste zavriet všetky okná vnútri rámcového okna a sledujte, ako sa zmení menu. Vyskúšajte funkcnost Drag & Drop otvorenia súboru a otvorenie súboru priamo cez Explorer. Poznámka: Pri spustení cez Explorer pravdepodobne natrafíte na problém, že sa bude spúštat predchádzajúca SDI verzia MyNotepadu. Môžete ho vyriešit napr. tak, že v kroku 4/6 tvorenia aplikácie napíšete inú príponu ako mxt. Takýmto prístupom by ste však mali vo Windows za chvílu zaregistrovaných zbytocne velké množstvo prípon a takisto to nepredstavuje najoptimálnejšie riešenie problému (co ak naozaj potrebujete už používanú príponu?). Lepším riešením je „premapovat“ príponu mxt na náš nový program. Tu je postup pre Windows 2000 (pre iné OS si pozrite help): z menu Explorera vyberte Tools -> Folder Options..., kliknite na kartu File types (obr. 2 – tu máte zoznam všetkých registrovaných prípon) tam v zozname nájdite príponu mxt a kliknite na Delete. Odklepnite varovné hlásenie. Teraz, ked ukoncíte a opätovne

Page 121: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

spustíte program MDIMyNotepad, bude už všetko fungovat, tak ako má (program si pri spustení zápisom do registrov zaistí asociáciu teraz už „neobsadenej“ prípony).

Obr. 1 Základné okno verzie MDI aplikácie MyNotepad Jednotlivé okná, ktoré sa nachádzajú vnútri rámcového okna aplikácie MDI, sa nazývajú detské okná (z angl. child windows). Detských okien môžete mat v jednom rámcovom okne lubovolne vela, ale len jedno z nich môže byt aktívne. Do tohto aktívneho okna smeruje aplikacný systém všetky správy/príkazy. MDI DETAILNEJŠIE. Už vieme, ako aplikácie MDI navonok vyzerajú, teraz sa na ne podme pozriet „zvnútra“. Ak kliknete na kartu ClassView vo Visual Studiu a porovnáte obsah projektov MyNotepad a MDIMyNotepad, zistíte, že sa líšia len v triede CChildFrame, ktorá je navyše v projekte MDI. CChildFrame, ktorá je odvodená od triedy CMDIChildWnd, predstavuje triedu pre každé detské okno (jej objekt je tvorený zakaždým, ked otvárame nové detské okno). Detské okná zdielajú menu s hlavným rámcovým oknom (trieda CMainFrame). V aplikácii MDI je trieda hlavného rámcového okna odvodená od triedy CMDIFrameWnd. Obsahuje už menu a vždy je vytvorený len jeden objekt rámcového okna (vo funkcii InitInstance pri spustení aplikácie):

// create main MDI Frame window CMainFrame* pMainFrame = new CMainFrame; if (!pMainFrame->LoadFrame(IDR_MAINFRAME)) return FALSE; m_pMainWnd = pMainFrame;

Teda ak budete chciet modifikovat vzhlad a správanie sa detských okien, budete editovat triedu CChildFrame. Pri zmenách hlavného rámca budete zase editovat triedu CMainFrame. Dalšie triedy v oboch projektoch sú zhodné – je tu trieda dokumentu, pohladu a aplikácie. Ked sa pozriete do funkcie InitInstance v implementacnom súbore triedy aplikácie, narazíte na odlišné vytváranie objektu šablóny dokumentu oproti aplikácii SDI:

CMultiDocTemplate* pDocTemplate; pDocTemplate = new CMultiDocTemplate( IDR_MDIMYNTYPE, RUNTIME_CLASS(CMDIMyNotepadDoc), RUNTIME_CLASS(CChildFrame), // custom MDI child frame RUNTIME_CLASS(CMDIMyNotepadView)); AddDocTemplate(pDocTemplate);

Tento kód umožnuje našej aplikácii používat viacero detských okien, ktoré sú napojené na objekt dokumentu a pohladu. Je dôležité uvedomit si, že MDI aplikácie dovolujú existenciu viac ako jedného objektu dokumentu. (t. j. všetky detské okná a ich pohlady môžu byt napojené na jeden a ten istý objekt dokumentu, ale tiež môžu mat každé svoj vlastný objekt dokumentu. Toto sú už dost nárocné témy MFC [napr. ak chcete mat aplikáciu MDI, ktorá v jednom detskom okne zobrazí císelné údaje, v druhom ich reprezentáciu v stlpcovom grafe, v dalšom zase bude spojnicový graf atd., stací

Page 122: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

vám jeden dokument a tri rôzne pohlady, môžete použit delené okná (Splitter Windows – trieda CSplitWnd – funguje aj v SDI, aj v MDI a pod.) a rôzne dalšie „machinácie“]. Odporúcam vám pozriet si dalšie informácie o triede CMultiDocTemplate v helpe. Takisto si pozrite informácie o funkciách GetFirstDocPosition a GetNextDoc, ktoré sú clenskými funkciami triedy CDocTemplate a umožnujú pohyb po objektoch jednotlivých dokumentov. AKO TO PRACUJE? Postup vykonávania aplikácie je zhruba rovnaký, aký sme opisovali v 20. casti. [Na dokonalé pochopenie odporúcam pridat debug výpisy do tých istých funkcií ako v 20. casti a porovnat poradie, v akom sa tieto funkcie volajú (+ pridajte debug výpisy aj do funkcií triedy CChildFrame); nakoniec skúste aplikáciu ešte odkrokovat.] Isté rozdiely sú vo funkcii CWinApp::OnFileNew. Táto funkcia v aplikáciách MDI najprv vytvára objekt dokumentu, potom objekt detského okna (nevytvára už objekt hlavného rámca, lebo ten je už vytvorený – pozri vyššie: // create main MDI Frame window).

Obr. 2 Asociácia prípony mxt s novou verziou MyNotepadu Ked vytvára tento objekt, menu IDR_MAINFRAME je nahradené IDR_MDIMYNTYPE (podrobnejšie dalej). Vytvára sa aj objekt pohladu a prepojujú sa objekty dokumentu, hlavného rámca a pohladu. Volajú sa funkcie CDocument::OnNewDocument pre objekt dokumentu, CView::OnInitialUpdate pre objekt pohladu a CFrameWnd::ActivateFrame pre objekt detského rámca, ktorá toto okno zobrazí, aby sme s ním mohli pracovat. Situáciu, ktorá nastane po vybratí položky New Window z menu Window, sme ešte neopisovali (a ani sme nemohli, v SDI žiadne Menu Window nie je). Túto cinnost obsluhuje funkcia CMDIFrameWnd::OnWindowNew (pozri Technical note 22), ktorá zabezpecí otvorenie nového detského okna, cím vlastne vytvorí nový pohlad na už existujúci dokument (práve aktívny). MDI A PRÁCA S DOKUMENTOM. Princíp práce s dokumentom (ako objektom) je v aplikáciách MDI okrem jednej výnimky rovnaký ako v prípade SDI. Výnimkou je vytváranie nového dokumentu zakaždým, ked nahrávame súbor z disku (File -> Open), resp. ked vytvárame nový súbor (File -> New). Dokument sa teda už nereinicializuje ako v aplikáciách SDI. Druhou odlišnostou, ktorá však v SDI nemohla nastat, je, že objekt dokumentu je automaticky zrušený pri zavretí detského okna. MDI A ZDROJE. Ak sa teraz pozriete na zdroje aplikácie MDIMyNotepad a porovnáte ich s verziou SDI, na prvý pohlad budete vidiet, že pribudol jeden nový zdroj do menu (okrem IDR_MAINFRAME aj IDR_MDIMYNTYPE). Prvé menu sa zobrazí, ak neexistuje žiadne detské okno. Len co otvoríme nejaké detské okno, je toto menu nahradené IDR_MDIMYNTYPE. Druhou, menej viditelnou zmenou, ktorá však tiež súvisí s aktuálnym obsahom hlavného rámcového okna, je odlišný obsah zdroja String table. Ten v tomto prípade obsahuje záznam IDR_MAINFRAME (použitý, ked sa v hlavnom rámcovom okne nic nenachádza – obsahuje titulok okna, ku ktorému sa v prípade otvorenia nejakého súboru pripíše jeho meno) plus záznam IDR_MDIMYNTYPE (ten je použitý, ak sme už otvorili nejaké detské okná). MDI ŠTUDENTI. Dúfam, že si ešte spomínate na projekt StudentList, ktorý predstavoval malický „informacný systém“. Zbieral dáta o študentoch, ktoré ste mohli ukladat na disk a neskôr cítat. No povedzte, nebolo by pekné využit pri tomto projekte teraz nadobudnuté skúsenosti s aplikáciami MDI a umožnit tak používatelovi mat naraz zobrazené informácie o viacerých študentoch a tie simultánne editovat? A ako to spravit? Jednoducho... Štatistiky ukazujú, že najbežnejšia programovacia technika

Page 123: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

je technika Copy – Paste (co tam po efektivite, hlavne že to beží). My túto štatistiku teraz potvrdíme. Vytvorte nový projekt s názvom MDIStudents, v kroku 4/6 kliknite na tlacidlo Advanced... a zadajte príponu slt. V kroku 6/6 zvolte pre pohlad ako základnú triedu CFormView. Tým v AppWizarde skoncíme. Zeditujte zdroje rovnako, ako je uvedené v 16. casti, do projektu pridajte súbory student.h a student.cpp a skopírujte obsah súborov StudentListDoc.h, StudentListDoc.cpp, StudentListView.h, StudentListView.cpp. Vykonajte triviálne zmeny, na ktoré prídete sami alebo vás na ne upozorní kompilátor/linker a to je všetko. Máte funkcnú verziu MDI aplikácie StudentList. (Na zaciatku budúcej casti sa k tejto „domácej úlohe“ ešte vrátime a vysvetlíme si možné úskalia pri prepisovaní aplikácií SDI na MDI. Ale odporúcam vám pokúsit sa o tento prepis už teraz, znalosti totiž nezískate len cítaním, ale musíte aj trochu popremýšlat. Na tomto príklade sa dá krásne pochopit princíp fungovania MDI, takže smelo do toho!) TIPY NA DOMA. Okrem spomenutej aplikácie vám dalej odporúcam v helpe si pozriet informácie o triedach CMDIFrameWnd a CMDIChildWnd. Pridajte do MDIMyNotepadu rozšírený filter, umožnujúci v dialógu FileOpen/FileSave priamo pracovat aj so súbormi, ktoré majú príponu txt. NABUDÚCE. Opisom aplikácií MDI sme dokoncili dlhú pút k pochopeniu architektúry Document/View knižnice MFC. Nepovedali sme si však všetko. Už spomenuté viacnásobné pohlady, dokumenty, delené pohlady a dalšie nám zatial ostávajú cudzie. Ale ako som už uviedol, tieto témy sa radia už k „vyššej matematike“ MFC a urcite nepatria do základného balíka informácií, ktorý by vám mal poskytnút tento seriál. Z mailov a otázok, ktoré mi prišli, vyplýva váš záujem o túto problematiku. Možno z nich vyberiem zopár otázok, ktoré umiestnim do FAQ, takže nezúfajte ?. Od budúcej casti až do konca seriálu (asi 4 – 5 castí) sa už budeme zaoberat len spomenutými „cerešnickami“ v programovaní Windows – bitmapami, knižnicami DLL, komponentmi ActiveX, tlacou. Pritom si zvolíme cisto praktický prístup => menej teórie a viac praktických informácií (how to...). Tešte sa teda spolu so mnou na dalšie pokracovanie. Prajem krásny máj. 23. cast: How to... I Vítam vás pri dalšom pokracovaní seriálu. V predchádzajúcej casti sme dobrali všetko, co som na zaciatku oznacil ako rozumný základ, po ktorého zvládnutí budete pripravení na získavanie dalších vedomostí a budete takisto schopní samostatne vytvárat štandardné aplikácie pre Windows. V tejto casti si môžete overit, co v praxi znamená „získavanie dalších vedomostí“. Len výnimocne totiž budete mat dostatok casu na preštudovanie nejakej knihy alebo rozsiahlejšej dokumentácie, dopodrobna rozoberajúcej konkrétny problém, ktorý sa snažíte vyriešit. Ovela castejšie budete vyhladávat už jeho funkcné riešenie, co je velakrát rýchlejšie a jednoduchšie ako pokúšanie sa o samostatné riešenie (treba však povedat, že nie vždy je takéto hladanie úspešné ?). A takýto prístup vám tu teraz ponúkam. Sú to riešenia štandardných „zákysov“, s ktorými sa môžete stretnút (niektoré som vybral z vašich mailov). Nebudú to vôbec zložité veci, na všetky by ste istotne po urcitom case prišli aj sami, ale naco vymýšlat nieco, co už bolo vymyslené? HOW TO ZOBRAZIT BITMAPU. Bitmapy sa v programoch používajú relatívne casto. Najjednoduchším spôsobom, ako zobrazit bitmapu, je pridat ju do zdrojov v design-time a odtial ju pomocou štandardných funkcií pocas behu programu natiahnut a zobrazit. Vytvorte si štandardnú SDI aplikáciu s Document/View podporou. Základnú triedu pohladu odvodte od CView. V editore zdrojov vyberte z menu Insert -> New Resource, kliknite na Bitmap (obr. 1) a potom na Import (za predpokladu, že bitmapu už máte vytvorenú).

Obr. 1 Pridávanie bitmapy do zdrojov Nalistuje a naimportujte želanú bitmapu (napr. c:\Windows\zapotec.bmp). Tá sa teraz zobrazí v zdrojoch pod názvom IDB_BITMAP1 (ten môžete zmenit). V triede pohladu vo funkcii OnDraw pridajte tento kód: void CBitmapView::OnDraw(CDC* pDC)

Page 124: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

{ CSize m_sizeZdroj; CBitmap* pbmpMyBitmap; CDC* pdcMemory; BITMAP bm; pdcMemory=new CDC; pbmpMyBitmap=new CBitmap; pbmpMyBitmap->LoadBitmap(IDB_BITMAP1); pdcMemory->CreateCompatibleDC(pDC); pdcMemory->SelectObject(pbmpMyBitmap); pbmpMyBitmap->GetObject(sizeof(bm), &bm); // Ziskame rozmery bitmapy m_sizeZdroj.cx=bm.bmWidth; m_sizeZdroj.cy=bm.bmHeight; // Zachova velkost bitmapy (+100 nema ziadny vyznam) pDC->BitBlt(0, 0, m_sizeZdroj.cx+100, m_sizeZdroj.cy+100, pdcMemory, 0, 0, SRCCOPY); // Umozni zmenit velkost bitmapy pDC->SetStretchBltMode(2); pDC->StretchBlt(100, 0, m_sizeZdroj.cx+100, m_sizeZdroj.cy+100, pdcMemory, 0, 0,

m_sizeZdroj.cx, m_sizeZdroj.cy, SRCCOPY); delete pdcMemory; delete pbmpMyBitmap; }

Obr. 2 Výstup aplikácie na zobrazovanie bitmáp

Uvedený kód predstavuje len umelý (ale funkcný) príklad, ako zobrazit v programe bitmapu (výstup vidíte na obr. 2). V praxi by sa žiadalo deklarácie premenných dat do hlavickového súboru, alokovat (new) a uvolnit (delete) pamät do konštruktora/deštruktora a obslúžit aj další mapovací režim (uvedený príklad funguje správne len v mapovacom režime MM_TEXT). Skúste si to. HOW TO PREHRAT WAV ZVUK. Ked už sme pri zdrojoch, ukážeme si, ako prehrat jednoduchý zvuk (wav – súbor). Najprv použijeme funkciu PlaySound (budeme prehrávat priamo súbor nahrávaný z disku) a potom si ukážeme použitie funkcie sndPlaySound na prehrávanie zvuku zo zdrojov. Vytvorte novú Dialog-Based aplikáciu a na dialóg pridajte dve tlacidlá: PlayF a PlayR (to sú hlavicky, ID nechajte default). Namapujte na ne správu BN_CLICKED a do obslužnej funkcie pre tlacidlo PlayF (IDC_BUTTON1) pridajte tento kód: void CSoundDlg::OnButton1() { // prehravame priamo zo suboru CString sndSound;

Page 125: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

sndSound="c:\\Windows Logon Sound.wav"; PlaySound(sndSound, NULL, SND_FILENAME); }

Obr. 3 Wav súbor v zdrojoch

Aby sme vôbec mohli prehrávat nejaký súbor zo zdrojov, je potrebné ho tam najprv pridat. V editore zdrojov vyberte z menu Insert -> Resource a potom Import. Nalistujte hocijaký wav súbor. Po pridaní sa v editore zdrojov vytvorí nová zložka s názvom "WAVE" a v nej zdroj IDR_WAVE1 (obr. 3). Teraz už môžete pridat kód do funkcie OnButton2: void CSoundDlg::OnButton2() { // prehravame zo zdrojov HRSRC hRes; HGLOBAL hData; BOOL bOk = FALSE; if ((hRes = ::FindResource(AfxGetResourceHandle(), MAKEINTRESOURCE(IDR_WAVE1),

"WAVE")) != NULL && (hData = ::LoadResource(AfxGetResourceHandle(), hRes)) != NULL) { // prehrame zdroj bOk = sndPlaySound((LPCSTR)::LockResource(hData), SND_MEMORY |

SND_SYNC | SND_NODEFAULT); // uvolnime FreeResource(hData); } if (!bOk) if (!bOk) MessageBox("Chyba pri prehravani suboru.", "Upozornenie",

MB_ICONWARNING | MB_OK); }

Funkcie PlaySound a sndPlaySound sú deklarované v súbore mmsystem.h a implementované v súbore winmm.lib. Pridajte preto túto include direktívu: #include <mmsystem.h> a v nastaveniach projektu [menu Project -> Settings (ALT + F7)] na karte Link pridajte knižnicu winmm.lib (obr. 4). HOW TO PO KLIKNUTÍ NA TLACIDLO ODOBRAT ZO ZOZNAMU POLOŽKU A PRIDAT JU DO DRUHÉHO ZOZNAMU. Velmi castý problém. Vytvorte si nový projekt (MFC AppWizard exe) typu Dialog based application. Všetky ostatné nastavenia nechajte default. V editore zdrojov pridajte na dialóg ovládacie prvky, ako vidíte na obr. 5. Ich ID nechajte default.

Page 126: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Obr. 4 Pridanie knižnice do projektu Potom vo funkcii OnInitDialog naplnte IDC_LIST1: CListBox* pLB=(CListBox*) GetDlgItem(IDC_LIST1); pLB->InsertString(-1, "1. Polozka"); pLB->InsertString(-1, "2. Polozka"); pLB->InsertString(-1, "3. Polozka"); pLB->InsertString(-1, "4. Polozka"); pLB->InsertString(-1, "5. Polozka");

Pomocou Class Wizardu namapujte na tlacidlo IDC_BUTTON1 správu BN_CLICKED. Do tela obslužnej funkcie OnButton1 pridajte takýto kód: void CListCtrlDlg::OnButton1() { CString strSelection; CListBox* pLB1=(CListBox*) GetDlgItem(IDC_LIST1); CListBox* pLB2=(CListBox*) GetDlgItem(IDC_LIST2); // index polozky, ktora je oznacena int index=pLB1->GetCurSel(); // ak sme nieco oznacili if (index!=-1) { // ziskame text pLB1->GetText(index, strSelection); // ak uz v List2 existuje pridavana // polozka, tak nepridat if ((pLB2->FindString(-1, strSelection))==LB_ERR) pLB2->AddString(strSelection); else

MessageBox("Tento zaznam uz bol pridany!", "Upozornenie", MB_ICONINFORMATION | MB_OK);

} } HOW TO ZMAZAT OZNACENÚ POLOŽKU ZO ZOZNAMU. Využijeme predchádzajúci príklad. V editore zdrojov pridajte ešte jedno tlacidlo IDC_BUTTON2 s hlavickou X. Namapujte nan správu BN_CLICKED a do obslužnej funkcie napíšte kód: void CListCtrlDlg::OnButton2() { CListBox* pLB1=(CListBox*) GetDlgItem(IDC_LIST1);

Page 127: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

// index polozky, ktora je oznacena int index=pLB1->GetCurSel(); if (index!=-1) { // zmazeme polozku pLB1->DeleteString(index); } } HOW TO NAKRESLIT DO DIALÓGU NEJAKÚ GRAFIKU. Ak ste programovali vo Visual Basicu, kreslit do dialógu sa dalo jednoducho využitím ovládacích prvkov Shape a Line už v design time. Vo Visual C++ však tieto prvky nenájdete. Kreslit je možné len pocas behu programu (run-time). Najvhodnejšia funkcia na kreslenie je funkcia OnPaint. Najlepšie je kreslit do nejakého ovládacieho prvku (napr. Static Text). Vychádzame z toho, že ovládací prvok je vlastne okno (potomok triedy CWnd), a preto môžeme don kreslit rovnako, ako keby sme kreslili do okna.

Obr. 5 Dialóg na kopírovanie položiek medzi dvoma zoznamami Opät využijeme zdroj dialógu z predchádzajúceho príkladu. Pridajte na dialóg ovládací prvok Static Text (ID nechajte default, hlavicku zmažte, rozmer: 158 × 38). Potom do funkcie OnPaint do vetvy else pridajte nasledujúci kód: else { // device context na kreslenie CPaintDC dc(this); // ziskame ukazovatel na ovladaci prvok, do ktoreho chceme kreslit CWnd* pWnd=GetDlgItem(IDC_STATIC); // ziskame kontext zariadenia CDC* pControlDC=pWnd->GetDC(); // povieme mu, ze chceme don kreslit pWnd->Invalidate(); pWnd->UpdateWindow(); // kreslime pControlDC->SelectStockObject(BLACK_BRUSH); pControlDC->LineTo(150, 50); pControlDC->Ellipse(30, 30, 50, 50); // nezabudnut uvolnit kontext zariadenia pWnd->ReleaseDC(pControlDC); CDialog::OnPaint(); }

Page 128: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Obr. 6 Kreslenie do dialógu

Výsledok vi díte na obr. 6. Samozrejme, nemusíte kreslit len do ovládacieho prvku Static Text, ale napríklad aj do tlacidla, zoznamu atd. (jednoducho do všetkého, co je odvodené od CWnd). HOW TO ZMENIT CAPTION TLACIDLA ALEBO JEHO DALŠIE VLASTNOSTI (ROZMER, UMIESTNENIE) ZA BEHU PROGRAMU. Riešenie je podobné ako v predchádzajúcom prípade. Stací pristupovat k ovládaciemu prvku ako k oknu a pomocou štandardných funkcií zmenit niektoré vlastnosti. Ukážeme si, ako zmenit hlavicku a umiestnenie. Vytvorte nový zdroj dialógu a pridajte don ovládací prvok tlacidlo. ID nechajte default a hlavicku takisto. Dalej pridajte ešte jedno tlacidlo (to bude iniciovat zmenu). Jeho ID tiež nechajte default a hlavicku zmente na Change. Namapujte nan správu BN_CLICKED a do tela obslužnej funkcie pridajte kód: void CDrawDlg::OnButton2() { //ziskame ukazovatel na ovladaci prvok tlacidla CWnd* pWnd=GetDlgItem(IDC_BUTTON1); // nastavime text na tlacidle pWnd->SetWindowText("Moj button"); // zmenime polohu tlacidla - zachovame jeho velkost pWnd->SetWindowPos(&wndTop, 50, 10, 0, 0, SWP_NOSIZE); } HOW TO POUŽÍVAT ŠTANDARDNÉ FUNKCIE NA PRÁCU S RETAZCAMI, AKO MÁ VB (MID, COMPARE ATD.). Visual Basic má velmi silnú podporu pre prácu s retazcami. Oproti nemu sa môže zdat používanie céckovských funkcií strcmp, strcpy... trochu zdlhavé a zložité. Ale aj VC++ (presnejšie MFC) má zabudovanú takúto podporu. Tá však nie je prístupná pre „klasické“ retazce (char*). Aby sme ju mohli využívat, je potrebné deklarovat retazce ako objekty odvodené od triedy MFC CString. Príklad jej použitia je tu: CString strFirst; CString strSecond; strFirst="Mama ma Emu"; // skopirujeme od piatej pozicie strSecond=strFirst.Mid(5); // porovname (no case sensitive) int jeRovne=strSecond.CompareNoCase(strFirst); // hladame podretazec int nasiel=strSecond.Find("ma"); cout << (LPCTSTR)strSecond << "\n"; cout << jeRovne << "\n"; cout << nasiel << "\n"; // konvertujeme na velke strSecond.MakeUpper(); cout << (LPCTSTR)strSecond << "\n";

Page 129: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

// otocime retazec strSecond.MakeReverse(); cout << (LPCTSTR)strSecond << "\n"; // kopitujeme obsah retazca strSecond=strFirst; cout << (LPCTSTR)strSecond;

Samozrejme, že funkcií na prácu s retazcami poskytuje trieda CString ovela viac, ich zoznam nájdete v helpe. NABUDÚCE. Prvú cast How to máme úspešne za sebou. O mesiac sa rovnakým štýlom pozrieme na ActiveX komponenty, knižnice DLL, tlac a možno aj databázy. Kedže sa blížime k záveru seriálu, pýtajte sa na veci, co vám nie sú jasné (z jednotlivých castí, prípadne iné problémy, na aké ste narazili). Môžem vám odpovedat prostredníctvom casopisu a vaše otázky tak môžu pomôct viacerým citatelom. Dovidenia v júli. 24. cast: How to... II Co sme nacali v predchádzajúcej casti, tým budeme teraz pokracovat. Pozrieme sa na tlac a komponenty ActiveX. Opät uplatníme cisto praktický (how-to) prístup, aj ked pri tlaci a ActiveX sa istej dávke teórie nedá vyhnút. TLAC A WINDOWS. Tlac v prostredí Windows (aj ked sa to nemusí na prvý pohlad zdat – hlavne tým, co programovali tlac ešte pod MS DOS) nie je jednoduchá. Ak chceme mat celý tento proces úplne pod kontrolou, musíme dokonale poznat viacero Windows štruktúr a ich obsah a musíme takisto vediet, ako ich naplnit správnymi údajmi. Naštastie knižnica MFC berie na seba väcšinu týchto cinností, cím nám podstatne zjednoduší prácu. V tab. 1 si pozrite funkcie, ktoré MFC využíva na prípravu tlace a na samotnú tlac. Ich podrobný opis nájdete v helpe, najdôležitejšie vlastnosti si uvedieme.

CView::OnPreparePrinting – Ako sa dá pochopit z jej názvu, táto funkcia len pripravuje tlac a náhlad pred tlacou. Je teda volaná ešte pred týmito operáciami. Jej využitie vidíte v tabulke 1. Jej dalšou dôležitou cinnostou je, že zavolá funkciu CView::DoPreparePrinting, ktorá vyvolá štandardný dialóg tlace a vytvorí kontext zariadenia pre tlaciaren (platí pre tlac), resp. vytvorí len samotný kontext zariadenia (platí pre náhlad na tlac).

CView::OnBeginPrinting – Po funkcii OnPreparePrinting nasleduje práve táto funkcia. Volá sa na zaciatku tlace (resp. náhladu na tlac). Ako jeden z parametrov má ukazovatel na kontext zariadenia tlaciarne (vytvorený v predchádzajúcej funkcii). Druhým parametrom je ukazovatel na objekt typu CPrintInfo (podrobne v helpe). CPrintInfo je trieda obsahujúca informácie o práve prebiehajúcom „jobe“ na tlaciarni.

CView::OnPrepareDC – Funkcia, v ktorej môžete upravit kontext zariadenia tlaciarne podla svojich potrieb. Dôležité je, že táto funkcia sa volá pre každú stranu tlace. Pomocou nej môžete teda jednoducho menit nastavenia tlace pre jednotlivé strany.

CView::OnPrint – Štandardná implementácia zavolá funkciu OnDraw, ktorej odovzdá ukazovatel na kontext zariadenia tlaciarne. Rovnako ako predchádzajúca aj táto funkcia sa volá pre každú stránku zvlášt , co nám umožnuje nastavit rôzne parametre tlace pre konkrétne strany (císlo strany, ktorá sa práve tlací, sa dá zistit pomocou m_nCurPage – clenská premenná triedy CPrintInfo. Jeden parameter funkcie OnPrint je ukazovatel na túto triedu).

CView::OnEndPrinting – Volaná na konci tlace (resp. na konci náhladu na tlac). Ak sme vytvárali nejaké GDI objekty vo funkcii OnBeginPrinting, je to najlepšie miesto na ich uvolnenie.

CView::OnDraw – Niekedy okolo ôsmej casti sme si opisovali kontext zariadení systému Windows. Tam sme si len nacrtli jeho možnosti a až teraz spoznáme jeho pravú silu. Výstup na tlaciaren urobíme jednoducho tak, že funkciu OnDraw zavoláme s ukazovatelom na kontext zariadenia tlaciarne (napr. z funkcie OnPrint). Všetko, co doteraz vieme o OnDraw, ostáva v platnosti. To znamená, že ak si v OnDraw nakreslíme nejaký obrázok, ten môžeme poslat na tlaciaren, ak zavoláme OnDraw s ukazovatelom na kontext tlaciarne alebo na obrazovku, ak OnDraw zavoláme s ukazovatelom na kontext obrazovky. Z toho vyplýva aj používanie funkcie OnDraw na WYSIWYG tlac – stací nám len „presmerovat“ ukazovatel na príslušný kontext zariadenia (presný postup si ukážeme v príkladoch, pozri WYSIWYG tlac). Funkcia Najcastejšie využitie CView::OnPreparePrinting Nastavenie císla prvej a poslednej strany

Page 130: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

CView::OnBeginPrinting Vytvorenie GDI objektov potrebných na tlac (fonty, štetce...) CView::OnPrepareDC Nastavenie špecifických parametrov kontextu zariadenia (napr. mapovací

režim) a testovanie konca tlace CView::OnPrint Pripravenie dát na tlac a volanie OnDraw (pre WYSIWYG) alebo ich

priamy zápis na tlaciaren CView::OnEndPrinting Zrušenie GDI objektov Tab. 1 – Najdôležitejšie funkcie MFC zabezpecujúce tlac, ktoré môžete prepísat (v poradí podla volania) HOW TO WYSIWYG TLAC. Predpokladám, že skratka WYSIWYG vám nieco hovorí. Ak nie, tak vedzte, že WYSIWYG je z anglického What You See Is What You Get. Po preložení do slovenciny dostanete relatívne presný opis, co táto skratka vyjadruje. Jednoducho to, co práve vidíte na monitore, uvidíte aj na výstupnom zariadení (nemusí to byt len tlaciaren). Objekty si zachovajú svoju velkost, umiestnenie, farbu a dalšie atribúty. Ak chceme z nášho programu realizovat WYSIWYG tlac, môžeme tak urobit jednoducho využitím funkcie OnDraw, ako sme už opísali. V tomto príklade si ukážeme výhody mapovacích režimov s pevnou mierkou (konkrétne MM_TWIPS). Vytvorte nový MFC AppWizard Project s názvom WSWTlac (najlepšie bude, ak si ešte predtým vytvoríte cisté Workspace s názvom napr. TFour a don budete pridávat všetky tu uvedené príklady). Aplikácia bude typu MDI, triedu pohladu odvodte od CScrollView a všetko ostatné nechajte default. Najprv si ukážeme použitie štandardného mapovacieho režimu MM_TEXT. Do triedy CWSWTlacView pridajte súkromnú clenskú premennú: private: CRect m_rectArea; Upravte konštruktor tejto triedy: // okno 1024x768 pixelov CWSWTlacView::CWSWTlacView() : m_rectArea(0, 0, 1024, 768) {} Teraz nastavte rolovacie pásky vo funkcii OnInitialUpdate: void CWSWTlacView::OnInitialUpdate() { CScrollView::OnInitialUpdate(); CSize sizeTotal(m_rectArea.Width(), m_rectArea.Height()); // rolovanie strany CSize sizePage(sizeTotal.cx / 2, sizeTotal.cy / 2); // rolovanie riadkov CSize sizeLine(sizeTotal.cx / 100, sizeTotal.cy / 100); SetScrollSizes(MM_TEXT, sizeTotal, sizePage, sizeLine); } Do funkcie OnDraw dopíšte tento kód: // 10x napiseme do okna text Ahoj! // vertikalne posunuty o 20 pixelov int k=0; for (int i=0; i<10; i++) { pDC->TextOut(0, k, "Ahoj!"); k+=20; }

Pomocou ClassWizardu pridajte do tejto triedy ešte funkcie OnPrepareDC a OnPrint. Do týchto funkcií plus do OnDraw, OnPreparePrinting, OnBeginPrinting, OnEndPrinting pridajte debug výpisy, aby ste si overili poradie volania. Program spustte, porovnajte vzhlad textu v okne a v náhlade na tlac (Print Preview). Skúste text aj vytlacit. Preco je výstup rozdielny, to sme si podrobne vysvetlovali v ôsmej casti, takže len strucne. Mapovací režim MM_TEXT používa ako svoje jednotky

Page 131: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

zobrazenia pixely. Pixely sú tzv. súradnice zariadenia, ciže vzhlad výsledného obrázka (resp. strany) závisí od „rozlíšenia“, aké konkrétne zariadenie používa. Jednoduchý príklad je klasické rozlíšenie Windows, ked text bude vyzerat odlišne pri rozlíšení 1024 × 768 a inak pri 800 × 600. V našom prípade sa takto správa tlaciaren. Používa iné rozlíšenie ako Windows, preto je výsledný text zdeformovaný. Po skoncení tlace (resp. náhladu na tlac) by ste mali dostat takéto poradie volaní v debug výpise: // spustenie aplikácie CWSWTlacView::OnPrepareDC CWSWTlacView::OnDraw // print, resp. print preview CWSWTlacView::OnPreparePrinting CWSWTlacView::OnBeginPrinting CWSWTlacView::OnPrepareDC CWSWTlacView::OnPrint CWSWTlacView::OnDraw

Problém zdeformovaného textu pri tlaci vyriešime použitím mapovacieho režimu s pevnou mierkou. PRECO MM_TWIPS. V ôsmej casti sme si o tomto režime povedali len to, že logická jednotka, ktorú MM_TWIPS používa, je 1/1440 palca. Asi teraz nevidíte jeho velké praktické využitie (ved komu by sa chcelo pocítat 1/1440 a ešte k tomu v palcoch!?). Opak je však pravdou. Hovorí vám nieco pojem bod (point)? Väcšina aplikácií používa na vyjadrenie velkosti textu body (napr. teraz píšem vo Worde s 10-bodovým písmom, nadpis má velkost 16 bodov). Výhoda je v tom, že rovnako reprezentuje text aj tlaciaren (t. j. 10-bodové písmo bude 10-bodové na obrazovke aj na tlaciarni. Z toho vyplýva použitie písmen TrueType, ktoré môžu byt zobrazené v lubovolnej velkosti). A práve MM_TWIPS je mapovacím režimom, kde sa lahko používajú body. Jeden twip je 1/20 bodu (pre úplnost: jeden bod je 1/72 palca). To znamená, že ak chceme písmo velkosti 10 bodov, bude to presne 10 × 20, co je 200 twipov. Twipy si vyskúšame na našej WSWTlac aplikácii. V prvom rade musíme zmenit konštruktor: // papier velkosti A4 (21cm x 29.7cm) vyjadreny v twipoch // 1 palec=2.54 cm, 1 palec=1440 twipov CWSWTlacView::CWSWTlacView() : m_rectArea(0, 0, 11909, 16804) {} Zmente funkciu SetScrollSizes vo funkcii OnInitialUpdate: SetScrollSizes(MM_TWIPS, sizeTotal, sizePage, sizeLine); A nakoniec ešte upravte funkciu OnDraw. Aby sme si prakticky ukázali výhodu MM_TWIPS, použijeme font Times New Roman s velkostou 12 bodov. Podrobnejšie informácie o funkcii CreateFont nájdete v helpe. CFont font; // pouzijeme Times New Roman // -240 znamena velkost 12 bodov font.CreateFont(-240, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_ROMAN, "Times New Roman"); // priradime kontextu zariadenia CFont* pOldFont=(CFont*) pDC->SelectObject(&font); // 10× napiseme do okna text Ahoj! // vytvorenym fontom int k=0; for (int i=0; i<10; i++) { pDC->TextOut(0, k, "Ahoj!");

Page 132: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

// vertikalny rozostup 16 bodov k-=320; } pDC->SelectObject(pOldFont);

Teraz sa výstupy na obrazovke a na tlaciarni (resp. v náhlade na tlac) už nebudú líšit. Použitie fontu Times New Roman si môžete vyskúšat aj pri použití mapovacieho režimu MM_TEXT. Uvidíte, že ani vytvorenie fontu s pevnou velkostou pomocou funkcie CreateFont problémy režimu MM_TEXT nevyrieši. HOW TO TLAC BEZ WYSIWYG. Teraz si predstavme, že nebudeme chciet realizovat WYSIWYG tlac (t. j. na tlaciarni bude iný výstup, ako máme na obrazovke). Využijeme predchádzajúci príklad. Funkcnost ostane rovnaká (použitie MM_TWIPS na výpis 10× textu Ahoj! využitím fontu Times New Roman). Teraz však budeme chciet, aby výstup na tlaciarni nebol tento text, ale ASCII hodnota jednotlivých znakov textu Ahoj!. Ako som uviedol, v tejto situácii sa už nebudeme môct spolahnút na štandardnú funkciu OnPrint, lebo tá volá funkciu OnDraw a to my nechceme. Budeme teda musiet prepísat túto funkciu a z nej realizovat tlac. Môžete modifikovat predchádzajúci príklad alebo si vytvorte nový projekt. Pre prehladnost odporúcam vytvorit nový projekt. Nazvite ho NWSWTlac a pridajte do Workspace TFour. Vykonajte presne tie isté úpravy ako pri projekte WSWTlac (pre mapovací režim MM_TWIPS). Jediný rozdiel bude, že upravíme ešte aj funkciu OnPrint: void CNWSWTlacView::OnPrint(CDC* pDC, CPrintInfo* pInfo) {

CFont font; // opat pouzijeme Times New Roman // ale velkosti 16 bodov // ... ako v OnDraw, prave namiesto // hodnoty –240 hodnota –320 // priradime kontextu zariadenia tlaciarne CFont* pOldFont=(CFont*) pDC->SelectObject(&font); // vypiseme ASCII hodnoty CString strAhoj="Ahoj!"; int k=0; for (int i=0; i<10; i++) { CString ascValue; int r=0; for (int j=0; j<5; j++) { ascValue.Format("%d", strAhoj[j]); pDC->TextOut(r, k, ascValue); // rozstupy medzi cislami // 30 bodov r+=600; } // rozstupy medzi riadkami // 16 bodov k-=320; } pDC->SelectObject(pOldFont); }

Na obrázkoch 1 a 2 vidíte text na monitore a text v náhlade pred tlacou (ciže aký dostanete, ak stranu vytlacíte). Ked sa nad tým zamyslíte, je to velmi jednoduché. V OnPrint robíme presne to isté, co v OnDraw, ibaže ukazovatel na kontext zariadenia neukazuje na kontext obrazovky, ale tlaciarne.

Page 133: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Obr. 1 Vzhlad strany na obrazovke

Obr. 2 Vzhlad stránky v náhlade pred tlacou

Už teda viete tlacit z klasickej Document/View aplikácie MFC. Azda sa vám teraz tlac z prostredia Windows zdá celkom jednoduchá. Nuž, možno zdá, ale nie je. Predstavte si aplikáciu nezaloženú na Document/View architektúre (napr. dialógovú aplikáciu). Bum! – všetko, co ste sa v tejto casti naucili, nemôžete použit. Na funkcie OnDraw, OnPrint... môžete hned zabudnút. Teraz budete musiet sami inicializovat tlaciaren, poslat na nu dáta, správne ju „uzavriet“, dat si pozor, aby

Page 134: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

ste nezmenili defaultnú tlaciaren atd. A verte, nie je to nic jednoduché (pozrite si v helpe topic Printing and Print Spooler Functions a pochopíte). KOMPONENTY ActiveX. Mnohí ste už asi termín ActiveX poculi, urcite ste aj nejaké komponenty ActiveX využívali. Možno však dobre nerozumiete, co sa pod týmto pojmom skrýva. Tu je jedna z mnohých definícií: ActiveX je skupina technológií, ktorá umožnuje navzájom komunikovat softvérovým komponentom v sietovom prostredí bez ohladu na jazyk, v ktorom boli naprogramované. ActiveX je založené na technológii COM (Component Object Model). Programátori, ktorí zažili Windows 3.1 a boom technológie OLE, budú teraz isto namietat, že táto definícia nehovorí o ActiveX, ale o OLE. Scasti majú aj pravdu. V každom prípade je dobré si uvedomit rozdiel medzi ActiveX a OLE. Nie je pravda, že ActiveX je nasledovník OLE, ani to, že OLE je už zastarané. ActiveX aj OLE sú založené na tej istej technológii – COM (len na okraj: COM je základný objektový model, ktorý umožnuje objektu preniest svoju funkcionalitu na iný objekt, resp. hostitelskú aplikáciu). OLE je skratka pre Object Linking and Embedding [ciže spájanie a vkladanie objektov – napr. vo Worde môžete mat tabulku z Excelu – to je OLE, nie ActiveX (pozor, nepliest s Active Document Containment technológiou!)]. Všetko ostatné, co sa netýka spájania a vkladania objektov, sa teraz nazýva tzv. Active technology (ActiveX). V princípe by sa dalo povedat, že ActiveX je vlastne „sietové OLE“, to však nie je stopercentne presné. Pre lepší prehlad odporúcam v MSDN helpe pozriet topic: OLE defined, OLE Background , Active Technology Topics , Active document containment, ATL Article Overview -> Introduction to COM and ATL. Pochopit všetky tieto technológie nie je jednoduché a urcite to je záležitost na viac ako pár týždnov. To vás tento seriál nenaucí. Naucí vás však používat komponenty ActiveX vo vašich aplikáciách. ActiveX sú primárne písané tak, aby ten, kto ich chce použit, nemusel ovládat všetky detaily COM-u, dokonca aby len vedel, co to COM je. Ako na to? To si povieme až nabudúce. NABUDÚCE. V dalšej casti si dokoncíme a na príklade ukážeme použitie komponentov ActiveX a opíšeme si prácu s knižnicami DLL. Celé leto je pred nami, prajem vám teda vela pokoja, oddychu a pekné dovolenky. 25. cast: ActiveX & DLL Vítam vás pri predposlednej casti seriálu C++ pod Windows. Ci už ju cítate na Slovensku v prehriatej kancelárii, alebo niekde v Karibiku s nohami namocenými v mori, obletovaní miestnymi krásavicami a s havanou v ústach – som rád, že ste si našli cas. Opät budeme pokracovat v nacatom rozprávaní a dokoncíme komponenty ActiveX a povieme si nieco o písaní a používaní knižníc DLL. POUŽITIE KOMPONENTOV ACTIVEX V PROGRAMOCH. V predošlej casti sme si zhruba povedali, co sú komponenty ActiveX, a ak ste si pozreli odporúcané témy v helpe, mali by ste mat dostatok informácií aspon na základnú orientáciu sa v pojmoch. Mali by ste teda vediet, že komponenty ActiveX majú príponu DLL alebo OCX, že na ich využívanie je potrebné ich najprv zaregistrovat (program regsvr32.exe) a že okno, ktoré obsahuje komponent ActiveX, sa nazýva kontajner. Ak ste niekedy programovali vo Visual Basicu, potom sa vám bude zdat používanie ActiveX velmi povedomé. Každý komponent ActiveX má svoje vlastnosti (properties), metódy (methods) a udalosti (events) (v helpe nájdete, co na co slúži). Ich zoznam a opis sa obycajne dodáva spolu s komponentom ako súbor pomocníka (*.hlp). Prístup k nim máte cez objekt komponent, ktorý si v programe musíte vytvorit.

Page 135: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Obr. 1 Pridanie komponentu do projektu PRÍKLAD 1 – POUŽITIE KOMPONENTU CALENDAR. Komponent Calendar je v štandardnej dodávke Windows (súbor mscal.ocx v systémovom adresári Windows) a jeho novšia verzia sa dodáva aj v balíku Office. Použijeme ho na objasnenie použitia komponentov ActiveX v programoch. Naprogramujeme jednoduchú aplikáciu, ktorá umožní nastavit rok v kalendári podla nášho zadania v textovom poli. Vytvorte si cisté Workspace TFive a pridajte don nový projekt typu MFC AppWizard exe s názvom XCalendar. Vyberte dialógovú aplikáciu. Uistite sa, že v kroku 2/4 je zaškrtnutá podpora pre komponenty ActiveX (ActiveX Controls). Ak už máte vytvorenú kostru, vložte do projektu ActiveX komponent mscal.ocx. Z menu Project –> Add to Project –> Components and Controls (tzv. galéria komponentov – pozri help). V zložke Registered ActiveX Controls nájdite Calendar Control X.X (X.X – verzia, závisí od verzie Windows, prípadne Office) – obr. 1.

V poli path to control vidíte fyzické umiestnenie komponentu na vašom disku. Ak kliknete na tlacidlo More Info, zobrazí sa vám help, kde nájdete spomínaný zoznam a opis všetkých vlastností, metód a udalostí, ktoré daný komponent podporuje. Kliknite na Insert. Po potvrdení, že naozaj chcete tento komponent do projektu vložit, sa vám zobrazí dialóg, aký vidíte na obr. 2. Tento dialóg vás informuje, aké triedy budú pridané do vášho projektu na podporu vkladaného komponentu. Vyberte obidve triedy.

Kliknite na OK a zavrite dialóg Components and Controls Gallery. V Toolboxe Controls vám pribudla jedna ikona, reprezentujúca pridaný ActiveX ovládací prvok Calendar. Pridajte ho do zdroja dialógu ako bežný ovládací prvok a pridajte ešte jedno tlacidlo a jedno textové pole (tlacidlá OK a Cancel môžete odstránit). Výsledný zdroj dialógu vidíte na obr. 3.

ID tlacidla zmente na IDC_BTN_NASTAV_ROK a nastavte mu vlastnost Default Button (Caption vidíte na obr.). ID textového pola nastavte na IDC_EDIT_ROK. Tlacidlu pridajte obsluhu správy BN_CLICKED – funkcia OnBtnNastavRok . Komponentu Calendar (IDC_CALENDAR1) pridajte clenskú premennú m_calendar kategórie Control. Ovládaciemu prvku IDC_EDIT_ROK pridajte premennú m_sYear typu short (rozsah 1900-2100). Teraz už stací dopísat kód do funkcie OnBtnNastavRok : void CXCalendarDlg::OnBtnNastavRok() { UpdateData(TRUE);

Page 136: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

// include potrebnych hlavickovych // suborov na deklarovanie // objektu m_calendar vykona automaticky // Class Wizard m_calendar.SetYear(m_sYear); }

Tým sa naša práca skoncila. Projekt skompilujte, zlinkujte a vyskúšajte jeho funkcnost. Ak chcete vediet, co všetko môžete robit s komponentom Calendar, pozrite sa do zdrojového súboru triedy CCalendar na zoznam všetkých clenských funkcií. Niektoré si vyskúšajte. Všetky tieto funkcie majú nieco spolocné. Bez ohladu na to, akú zložitú operáciu vykonávajú, každá volá len jednu „všemocnú“ funkciu InvokeHelper, ktorá všetko zariadi. Táto funkcia „vyvoláva“ (invoke) vlastnosti a metódy komponentu ActiveX. Akú, to urcuje jej prvý parameter (tzv. dispatch ID). Dalšie parametre nás teraz nemusia zaujímat (pre záujemcov je opät k dispozícii help). KNIŽNICE DLL. Urcite ste sa už pri svojej práci so systémom Windows stretli s knižnicami DLL (ciže s dynamicky linkovanými knižnicami). Teraz si ukážeme, ako môžeme takéto knižnice využit pri písaní nášho softvéru. Najskôr však trocha teórie. Knižnica DLL je osobitný súbor (programový modul) uložený mimo programu (procesu), ktorý ho využíva. Obsahuje skompilované a zlinkované funkcie a iné dáta. Je nahrávaná pocas behu (runtime) programom, ktorý ju využíva (môže to byt bud klasický program – EXE –, alebo aj dalšia knižnica – DLL). Tento program sa nazýva klientsky program (klient).

Obr. 2 Triedy pridávané do projektu pri vkladaní komponentu Calendar Po nahraní do pamäte je namapovaná do pamätového priestoru klienta. Ako sme uviedli, obsahuje funkcie a „iné dáta“, pod ktorými sa myslia napríklad rôzne premenné, zdroje, triedy C++ a pod. Funkcie môžu byt dvojakého typu. Sú to bud tzv. interné funkcie, ktoré môže používat knižnica DLL ako svoje pomocné funkcie na vykonávanie „interných“ operácií. Môžu byt volané len vnútri knižnice DLL a žiadny iný modul k nim zvonku nemá prístup. A potom sú tu tzv. exportované funkcie, ktoré, ako z ich názvu vyplýva, sú urcené na „export“, teda ich môžeme volat z iného programového modulu. Z hladiska ich využitia môžeme zjednodušene prirovnat interné funkcie k súkromným a externé funkcie k verejným metódam v klasických triedach C++.

Page 137: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Obr. 3 Zdroj dialógu aplikácie XCalendar Aby to však nebolo také jednoduché, existujú dva spôsoby, ako exportovat funkcie z knižnice DLL. Prvý a jednoduchší z nich je exportovat ich pomocou mena . Znamená to deklarovat každú funkciu v knižnici pomocou klúcového slova __declspec a rozšíreného atribútu dllexport. Napríklad deklaráciou: __declspec(dllexport) char* CopyBText(char* strInput) sme zaviedli funkciu CopyBText vracajúcu retazec s jedným vstupným parametrom – takisto retazcom. Aby sme ju mohli použit v inom module, musíme ju tam zase naimportovat: __declspec(dllimport) char* CopyBText(char* strInput)

Takýto zápis pri použití C++ kompilátora však môže predstavovat problém. Ten totiž vygeneruje pre funkciu tzv. dekorované meno (co je meno pozostávajúce z mena triedy, funkcie a zoznamu parametrov). S týmto menom nevedia pracovat iné jazyky (dokonca ani klasické cécko). Preto sa odporúca zadefinovat pre exportované (a importované) funkcie externé C linkovanie: extern "C" __declspec(dllexport) char* CopyBText(char* strInput) extern "C" __declspec(dllimport) char* CopyBText(char* strInput)

To však ešte nie je všetko. Takto zadeklarovanú funkciu v knižnici by sme nemohli použit napríklad v jazyku Visual Basic. Preco? Kompilátor implicitne používa na odovzdávanie parametrov konvenciu __cdecl (parametre funkcie vyberá zo stacku volajúci program). Visual Basic (a iné) vyžaduje konvenciu __stdcall (parameter zo stacku vyberá volaná funkcia). Takže pre tento prípad musíme použit takúto deklaráciu exportovanej funkcie: extern "C" __declspec(dllexport) char* __stdcall CopyBText(char* strInput)

Ak chcete, aby všetky funkcie dodržiavali konvenciu __cdecl (resp. __stdcall), môžete do zoznamu parametrov kompilátora v nastavení projektu knižnice DLL pridat prepínac /Gd (resp. /Gz).

To je všetko, co sa týka exportovania funkcií pomocou mena. Druhým, zložitejším spôsobom je exportovat funkcie pomocou ich ordinálneho císla. Používa sa na to súbor definícií (.DEF). V praxi je momentálne tento spôsob pre svoju relatívnu zložitost málo používaný (boli casy – vo Win16 to bol preferovaný spôsob, vo Win32 už Microsoft odporúca exportovanie pomocou mena), a preto sa mu ani nebudeme venovat. Pre zvedavcov je tu help. Takisto sa nebudeme venovat práci s knižnicami pomocou funkcie LoadLibrary, ktorá zabezpecuje explicitné nahranie knižnice do pamäte z modulu, ktorý ju využíva. Túto prácu prenecháme linkeru (použijeme implicitné linkovanie) – viac na príklade (záujemcovia o explicitné linkovanie nájdu informácie v helpe). PRECO DLL? Ešte vám nie je jasné, ako by ste mohli knižnice DLL využit vo svojich projektoch? Môžem vám z vlastnej skúsenosti povedat, že DLL-ky sa v programátorskej praxi používajú velmi

Page 138: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

casto. Ved naco vytvárat niekolkomegové súbory exe, ktoré vznikajú pri použití štandardných knižníc LIB, ked máme možnost rozdelit funkcnost na niekolko samostatných modulov a tie v runtime spojit? Takisto sa výrazne zjednodušuje ladenie takto delenej aplikácie. Stací si samostatne odladit jednotlivé DLL-ky a v prípade problémov efektívne zasahovat len v module, kde sa vyskytla chyba (dokonca môžete priamo z vývojového prostredia „spustit“ a odladit knižnicu DLL, stací len nastavit cestu ku klientovi). Silu DLL knižníc dokumentuje aj samotný operacný systém Windows, ktorého základná funkcnost je tiež podelená do rôznych knižníc DLL (napr. kernel32.dll, security.dll). MFC je tiež dobrým príkladom (knižnica mfc42.dll). KNIŽNICE DLL A MFC. Ak chcete vytvorit knižnicu DLL bez podpory MFC, môžete na to využit volbu Win32 Dynamic-Link Library pri vytváraní nového projektu. Ak chcete vo vašej knižnici využívat aj MFC, bude lepšie, ak využijete možnosti poskytované aplikacným systémom a vytvoríte ju pomocou App Wizardu pre knižnice DLL. Ak si zvolíte túto cestu, zistíte, že existujú dva typy MFC knižníc – regulárne (regular), dokonca dva druhy, a rozširujúce (extension DLLs). Aby ste si vedeli správne vybrat, ktorý typ využijete, musíte poznat základný rozdiel medzi nimi. A ten je v tomto prípade velmi jednoduchý a jednoznacný. Regulárne knižnice MFC podporujú štandardné céckovské rozhranie, ciže nedokážu exportovat prvky, ktoré podporuje jazyk C++ (t. j. nedokážu exportovat triedy, pretažené funkcie a pod.). Samozrejme, v týchto knižniciach môžete využívat prvky jazyka C++, ibaže tie casti kódu, ktoré ich využívajú, nemôžete exportovat. Naproti tomu rozširujúce knižnice MFC plne podporujú C++ rozhranie, a preto dokážu exportovat všetky prvky jazyka C++. Možno sa vám teraz zdá existencia regulárnych knižníc MFC zbytocná, no z hladiska kompatibility s inými jazykmi má svoje opodstatnenie. Ak chcete, aby vaša knižnica MFC fungovala bez problémov s inými jazykmi (napr. Visual Basic), mali by ste ju vytvorit ako regulárnu. Takisto ak neplánujete exportovat žiadne C++ prvky, mali by ste použit regulárnu knižnicu (ak neplánujete využívat MFC, môžete, samozrejme, použit aj klasickú Win32 knižnicu). STATICKÉ A „ZDIELANÉ“ LINKOVANIE S MFC. V prípade, že vytvárate regulárnu knižnicu MFC, máte na výber, ci ju chcete s knižnicou MFC linkovat staticky alebo dynamicky (synonymum pre zdielané linkovanie). Táto téma je velmi podrobne opísaná v helpe (pozrite si topic: regular DLLs). Takže len strucne. Ak si zvolíte statické linkovanie, do vašej knižnice sa automaticky nakopíruje aj kód knižnice MFC (konkrétne je to knižnica mfc42.dll – platí pre Release mód). Zväcší to síce vašu výslednú DLL-ku a zníži sa rýchlost prekladu, ale máte istotu, že na PC u zákazníka váš program nebude padat pre chýbajúcu alebo nekompatibilnú knižnicu MFC (velmi casté problémy, hlavne ak vyvíjate na systéme NT a zákazník má 9.x). Ak zvolíte dynamické linkovanie (use MFC in a shared DLL), žiadny kód sa do vašej knižnice nekopíruje a všetko sa zostavuje až pocas behu. Je teda úplne na vás, ktorú možnost využijete – záleží na situácii. Statické linkovanie generuje naozaj odpudivo velké výstupné moduly, ale je bezpecné.

Page 139: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Obr. 4 Vytváranie regulárnej knižnice MFC Naproti tomu na obhajobu dynamického linkovania treba povedat, že väcšina problémov sa dá eliminovat kvalitným inštalacným programom (v súcasnosti napríklad vo Windows Installeri 1.1 je to otázka dvoch kliknutí). Statické a dynamické linkovanie môžete, samozrejme, použit aj na klasické EXE programy, nielen na knižnice DLL (pozri Project Settings, karta General – poznámka: Statické linkovanie podporuje len Visual C++ verzie Professional a Enterprise). Varovanie: Velký pozor si treba dat pri písaní rozširujúcich knižníc MFC. Rozširujúca knižnica totiž môže byt linkovaná len dynamicky. Túto podmienku však musí splnat aj klientsky program, ktorý túto knižnicu využíva. To znamená, že nemôžete používat rozširujúcu knižnicu MFC s klientom, ktorý používa statické linkovanie s knižnicou MFC. Táto chyba je o to zákernejšia, že všetko sa bez problémov skompiluje a zlinkuje, problémy nastanú až po spustení programu (podrobne pozri help: DLLs –> Extension).

Všetky príklady umiestnujte do Workspace TFive.

Obr. 5 Dialóg klienta knižnice RegularDLL PRÍKLAD 2 – REGULARDLL. Prvý príklad bude velmi jednoduchý. Naprogramujeme si knižnicu, ktorá vyexportuje nášmu klientskemu programu jedinú funkciu, ktorej úlohou bude obrátit retazec získaný z dialógu. Vytvorte nový projekt MFC AppWizard DLL s názvom RegularDLL. V kroku 1/1 vyberte Regular DLL using shared DLL (obr. 4).

Page 140: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Všetky ostatné nastavenia nechajte štandardné. V kóde do súboru RegularDLL.cpp pridajte túto („otácaciu“) funkciu: extern "C" __declspec(dllexport) char* CopyBText(char* strInput) { // pouzivame dynamicke linkovanie // toto makro je nutne AFX_MANAGE_STATE(AfxGetStaticModuleState()); // obratime text return strrev(strInput); }

Co sa týka tajomného MFC makra AFX_MANAGE_STATE, to si vysvetlím jednoducho. Ak píšete regulárnu knižnicu MFC a využívate dynamické linkovanie (as shared DLL), toto makro musíte pridat do všetkých exportovaných funkcií – podrobnosti pozri v helpe). Ak projekt teraz skompilujete a zlinkujete, dostanete ako výstupný súbor knižnicu RegularDLL.dll. Pre nás bude zaujímavý ešte jeden súbor: RegularDLL.lib. Je to tzv. knižnica importov – (pozri help, topic DLLs –>Link Implicitly), ktorá obsahuje zoznam všetkých vyexportovaných symbolických mien (ale žiadny kód) a meno knižnice DLL. Tento súbor je „zástupcom“ knižnice DLL a pridáva sa do klientskeho programu, aby bolo možné v procese linkovania klienta vytvorit väzby medzi importovanými (v kóde klienta) a exportovanými (tie sú v knižnici LIB) symbolmi. Tento proces sa nazýva implicitné linkovanie, ktoré sme už spomínali. Tým máme vytvorenú knižnicu DLL. Ešte potrebujeme nejaký klientsky program, aby sme mohli vyskúšat jej funkcnost. Vytvorte teda nový projekt s názvom RegularDLLClient, ktorý bude typu Dialog based application (všetky ostatné nastavenia nechajte default). Zdroj dialógu upravte podla obr. 5. Vlastnosti textových polí nechajte default, zmente len ID tlacidla na IDC_BTN_UPRAV, jeho hlavicku vidíte na obrázku. Tomuto tlacidlu tiež nastavte vlastnost Default Button.

Spustite Class Wizard a pridajte ovládacím prvkom clenské premenné (tab. 1) a tlacidlu IDC_BTN_UPRAV namapujte správu BN_CLICKED. Tu je telo jej obslužnej funkcie: void CRegularDLLClientDlg::OnBtnUprav() { UpdateData(TRUE); char strTemp[20]; strcpy(strTemp, m_strInput); // volame funkciu z kniznice DLL m_strOutput=CopyBText(strTemp); UpdateData(FALSE); } ID ovl. prvku Clenská premenná Typ Max. znakov IDC_EDIT1 m_strInput CString 20 IDC_EDIT2 m_strOutput CString 20 Tab. 1 – Clenské premenné dialógu RegularDLLClient

Do klienta naimportujte funkciu CopyBText (tento riadok pridajte na zaciatok súboru RegularDLLClientDlg.cpp): extern "C" __declspec(dllimport) char* CopyBText(char* strInput);

Teraz sa pokúste takýto kód skompilovat a zlinkovat. Kompilácia by mala prebehnút bez problémov, ale linker vyhlási chybu 2001 - unresolved external. Táto chyba znamená, že nebol schopný nájst telo importovanej funkcie CopyBText. Preto ešte nahrajte súbor RegularDLL.dll do adresára \Debug projektu klienta. Súbor RegularDLL.lib nahrajte k zdrojovým textom klienta a pridajte ho do projektu (v nastaveniach projektu – karta Link, pole Object/Library modules – pozri 23. cast a pridanie knižnice winmm.lib). Knižnicu DLL však nemusíte nahrat len do adresára, kde sa nachádza EXE súbor klienta (aj ked to považujem za najvhodnejšie na vývoj). Môžete ju nahrat napr. do

Page 141: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

systémového adresára Windows, do adresára Windows alebo do adresára, ktorý je uvedený v premennej PATH (túto možnost však neodporúcam). Jednoduchý spôsob, ako zistit, kam všade môžete umiestnit knižnicu, je spustit klienta a ten, ak si nebude môct knižnicu nájst, vypíše chybové hlásenie so zoznamom všetkých adresárov, kde knižnicu neúspešne hladal. PRÍKLAD 3 – EXTENSIONDLL. Ako sa tak pozerám na rozsah clánku, na tento príklad už v tejto casti nezostane priestor. Takže nabudúce... NABUDÚCE. O mesiac sa pri seriáli C++ pod Windows stretneme naposledy. Dokoncíme si rozširujúce knižnice DLL, povieme si nieco o budúcnosti programovania s Visual C++ a o programovaní pod Windows vôbec, pozrieme sa na Visual Studio .NET a uvedieme si aj rozlúckové FAQ. Takže naposledy dovidenia v septembri. 26. cast: Záver V tejto casti sa pri seriáli C++ pod Windows stretávame naposledy. Predchádzajúcich 25 castí bolo venovaných cisto programovaniu a riešeniu rôznych programátorských úloh. Teraz síce dokoncíme rozprávanie o rozširujúcich knižniciach DLL, ale hlavne sa pokúsim zhrnút všetko, co sme sa spolu naucili (áno, aj mne priniesol tento seriál vela nových poznatkov, aj ked asi trochu iných ako vám). Takisto sa vám pokúsim nacrtnút, co so získanými vedomostami môžete dalej podniknút a ako ich dalej zveladovat. Ale na zaciatok si dáme predsa len nieco z programovania. PRÍKLAD 3 – MFC ExtensionDLL. Ak chcete exportovat zo svojich knižníc prvky jazyka C++ a využívat pri tom knižnicu MFC (napr. ak exportujete triedu odvodenú od nejakej triedy MFC) , predstavujú rozširujúce knižnice MFC tú správnu volbu. (Na exportovanie C++ prvkov môžete, samozrejme, použit aj štandardnú Win32 DLL knižnicu.) Teóriu nájdete v predchádzajúcej casti, takže si uvedieme slúbený príklad. Opät použijeme funkciu na otácanie textu ako pri regulárnych knižniciach, ale tentoraz bude táto funkcia (CopyBText) súcastou triedy. Do Workspace TFive pridajte pomocou MFC App Wizardu (DLL) nový projekt s názvom ExtensionDLL. V kroku 1/1 vyberte MFC Extension DLL a kliknite na finish. Do súboru ExtensionDLL.cpp pridajte tento kód: class AFX_EXT_CLASS Exported { public: char* CopyBText(char* strInput) { return strrev(strInput); } };

Makro AFX_EXT_CLASS slúži na exportovanie/importovanie tried z rozširujúcich knižníc MFC. Keby ste používali klasickú WIN32 DLL knižnicu, použili by ste klúcové slovo __declspec: class __declspec(dllexport) Exported...

Projekt preložte, mali by ste ako výstup dostat (okrem iného) jednu DLL-ku (ExtensionDLL.DLL) a jednu LIB-ku (ExtensionDLL.LIB). Teraz ešte vytvorit klienta. Mohli by ste použit aj RegularDLL klienta, ale pre zachovanie urcitej integrity radšej vytvorte novú aplikáciu s názvom ExtensionDLLClient. Bude to rovnaká aplikácia ako RegularDLLClient (MFC AppWizard exe, Dialog based, shared MFC..). Zdroj dialógu zeditujte rovnako ako pri RegularDLLClient aplikácii (resp. Copy & Paste), pridajte aj rovnaké clenské premenné. Kód obslužnej funkcie správy BN_CLICKED je takýto: void CExtensionDLLClientDlg::OnBtnUprav() { Exported exObject; UpdateData(TRUE); char strTemp[20]; strcpy(strTemp, m_strInput); m_strOutput=exObject.CopyBText(strTemp); UpdateData(FALSE); }

Page 142: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

Aby ste mohli triedu Exported využívat, musíte ju naimportovat, preto na zaciatok súboru ExtensionDLLClientDlg.cpp vložte tento kód: class AFX_EXT_CLASS Exported { public: char* CopyBText(char* strInput); };

Všimnite si, že musíte exportovat kompletnú deklaráciu triedy (malo by to byt samozrejmé, ale pre istotu pripomínam, že v klientovi sa nikde v zdrojových textoch nenachádza kód funkcie CopyBText, ten sa dotahuje z DLL knižnice v runtime). Zaujímavé je tiež použitie makra AFX_EXT_CLASS, ktoré sme v nezmenenej podobe použili na export aj import dát (pre klasické WIN32 DLL knižnice bez podpory MFC by ste museli použit class __declspec(dllimport) Exported...). Finta je v tom, že makro AFX_EXT_CLASS je raz definované ako __declspec(dllexport), ak exportujeme, a __declspec(dllimport), ak importujeme. Ako aplikacný systém zistí, ktorú verziu má použit, to sa docítate v helpe, v topicu DLLs, exporting using AFX_EXT_CLASS. Má to hlavne tú výhodu, že môžete použit rovnaké hlavickové súbory tak pre knižnicu DLL, ako aj pre klienta. Vrátme sa však k nášmu klientovi. Teraz už stací len nakopírovat súbor ExtensionDLL.DLL k „exácu“ ExtensionDLLClienta (\Debug adresár) a súbor ExtensionDLL.LIB k zdrojovým textom klienta a pridat do projektu (ALT + F7, karta Link, pole Object/Library modules). Preložte a vyskúšajte klienta, všetko by malo fungovat. (Poznámka: Pri exportovaní tried sa exportujú len verejné dátové cleny a funkcie.) AKO STE NA TOM? V prvej casti som napísal: Ak ste si teraz pomysleli, že programovanie s „Céckom“ pod Windows musí byt nehorázne tažké, rád vám poviem, že nemáte celkom pravdu. Ak teraz – po preštudovaní 25 castí – zacínate mat dojem, že na tejto vete je trochu pravdy, tak seriál vo vašom prípade splnil svoj úcel. Ak nie, nic to, skúsim vám pomôct. Vždy, ked sa pustíte do študovania novej problematiky, zacínate takpovediac na zelenej lúke. Všetko je zahalené tajomstvom (co je to Class Wizard a MFC, ako sa dajú využívat pri písaní aplikácií...). Tento „základný“ level je dobrým odrazovým mostíkom pre dalšie štúdium. Snažíte sa nájst odpovede na svoje otázky (a v tomto hladaní ste relatívne úspešní) a ešte neviete, co všetko neviete, takže dravo postupujete vpred. Táto fáza by sa dala nazvat aj optimistickou fázou. Po istom case sa však zastavíte na jednom mieste a stojíte, neviete, ako dalej. Zrazu ste si zacali uvedomovat, co všetko neviete a v akom vysokom nepomere sú vaše terajšie vedomosti k celej problematike. Práve teraz nastáva kríza, ked sa rozhoduje o budúcnosti. Ak sa totiž zlaknete a stratíte záujem, je vysoko pravdepodobné, že sa k tejto problematike tak skoro nevrátite. V tejto pesimistickej fáze je najdôležitejší retrospektívny pohlad. Musíte si uvedomit, co ste sa už všetko naucili, že vaše prvotné otázky vám pripadajú smiešne a vo všetkých tých tajomných pojmoch sa už ako-tak viete orientovat. Ak to dokážete, potom fázu pesimizmu prekonáte a nastáva fáza realizmu (už si pripadám ako na nejakej prednáške z dejín literatúry ?). Teraz už danú problematiku zvládate viac-menej rutinne, už nie je vela vecí, co vás dokáže prekvapit, a väcšinu štandardných problémov ste schopní vyriešit v krátkom case. Predpokladám, že väcšina z vás sa nachádza v kritickej druhej fáze, preto ak nebudete vediet, ako dalej, skúste použit opisovaný prístup. CO STE SA VŠETKO NAUCILI? Zacnime pekne po poriadku. Naucili ste sa pracovat s prostredím Microsoft Visual C++. Kedže viete využívat možnosti tohto prostredia, máte velkú výhodu. Visual C++ patrí totiž k tým zložitejším vývojovým prostrediam (napr. oproti Borland C++ Builderu). Ale ak ho viete využívat, prechod na iný vývojový nástroj vám nespôsobí velké problémy, co však, naopak, nemusí platit (poznám niekolko „C++builderistov“, ktorí sa pokúšali prejst na VC++ a skoncili ešte vo fáze optimizmu ?). Dalej by ste mali na základnej úrovni rozumiet, ako sa správa štandardný program pod Windows (smerovanie správ, procedúra okna atd.). Tieto vedomosti (prostredie VC++ a základy Windows) tvoria spolu pevné základy, na ktorých ste mohli stavat štúdium knižnice MFC. A práve táto knižnica vám mala pomôct pri lepšom pochopení objektového Windows programovania, ked ste neboli zbytocne zatažení starým programovaním API. AKO DALEJ? No dobre, poviete si, ovládam VC++, knižnicu MFC a co dalej? Túto otázku by si mali urcite položit všetci, ktorí sa ešte programovaním neživia (tí, ktorí sú už profesionálnymi programátormi, mi dajú za pravdu, že na túto otázku im velmi rýchlo dá odpoved šéf, resp. zákazník ?). Samozrejme, existuje nespocetne vela ciest, akými sa vydat, ale ked chcete zostat pri VC++ a MFC, máte zhruba tieto možnosti (pozri obr. 1):

1. Vyvíjanie aplikácií pre OS Windows 2. Vyvíjanie aplikácií pre mobilné zariadenia triedy PocketPC, PocketPC 2002, CE.NET 3. Programovanie pre „web“ 4. Programovanie pre enterprise

Page 143: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

5. Tvorenie hier s DirectX (Prosím, neberte tieto možnosti ako nejakú dogmu, sú to len moje návrhy, ktoré však,

samozrejme, nemajú všeobecnú platnost.) Ktorú z nich si vyberiete, je len na vás, a ak ešte neviete, skúsim vám pomôct vymenovaním vecí, co si budete musiet doštudovat (predpokladám, že vaše vedomosti zodpovedajú približne rozsahu tohto seriálu). Takže prvá možnost – programovanie klasických desktopových aplikácií pre OS Windows. Musíte úplne zvládnut knižnicu MFC a vedomosti z oblasti OLE, COM a COM+ sú v tejto oblasti velmi užitocné (ak uvažujete aj o iných OS ako Windows pre svoju cielovú platformu, pozrite sa napr. na technológiu CORBA). Momentálne nie sú pre vás vedomosti architektúry .NET životne dôležité, každopádne bude dobré orientovat sa v tejto oblasti, aby ste nezaspali dobu. Urcite by ste však mali zvládat niektorú z technológií na prístup k databázam. Je v princípe jedno, ktorú – záleží na type aplikácií, aké píšete, ale myslím, že by ste mohli zacat s ADO (ktoré vám bude väcšinou úplne stacit), a ak sa budete orientovat len na výkon, tak potom OLE DB.

Vaša volba pri výbere vývojového prostredia by mala padnút na Visual Studio .NET, ale ak nemáte dost financií, vystacíte aj s Visual Studiom 6.0. Druhá možnost, vyvíjanie pre mobilné zariadenia, je v súcasnosti atraktívna a jej vývoj ide velmi rýchlo dopredu. Cím skôr teda naskocíte, tým lepšie. Pripravte sa však na to, že vývojárske technológie pre mobilné zariadenia sa približne jednu generáciu omeškávajú za desktopovými. Ak si však vyberiete platformu PocketPC a vyššiu, tento rozdiel až tak velmi nepocítite (horšie je to napr. s platformou PalmOS). PocketPC má pre vás ešte jednu výhodu. Má velmi dobrú podporu knižnice MFC, takže drvivú väcšinu vedomostí z tohto seriálu môžete použit aj tam. Programovanie pre zariadenia PocketPC vám ulahcí aj vývojové prostredie Microsoft eMbedded Visual Tools (obsahuje eMbedded Visual C++ a eMbedded Visual Basic), ktoré je na 90 % zhodné s Visual Studiom 6.0 a dá sa od Microsoftu stiahnut zadarmo zo stránky http://www.microsoft.com/mobile/downloads/emvt30.asp (napr. stretnete sa tu s rovnakým Class Wizardom, podobným App Wizardom a Resource editorom ako pri klasickom Visual C++). Ak si vyberiete túto možnost, pravdepodobne sa nevyhnete ani databázam a databázovému programovaniu. Máte na výber relatívne dost databáz, ja mám velmi dobré skúsenosti s Microsoft SQL Serverom for Windows CE, ktorý si tiež môžete zadarmo stiahnut a používat (http://www.microsoft.com/sql/CE/default.asp). Musíte však vlastnit vývojársku licenciu na desktopový SQL Server. V tomto prípade vás neminú technológie ako ADO CE alebo OLE DB CE. Ale tu neplatí ako pri desktopových aplikáciách, že vystacíte s ADO CE (ktoré, mimochodom, ani nie je oficiálne v eVC++ podporované). Takže radšej siahnite po OLE DB CE. Ak potrebujete písat naozaj velmi jednoduché aplikácie, potom vystacíte aj s eVB a ADO CE. Budúcnost na tejto platforme je tiež spojená s .NET architektúrou a už je na svete aj beta 1 .NET Compact Frameworku, co je akýsi plug-in do Visual Studia .NET umožnujúci vyvíjat mobilné aplikácie. Tretia možnost – programovanie pre „web“. Týmto nemyslím nejaké programovanie webových stránok, skôr to programovanie v pozadí, ktoré nevidno, ale bez ktorého sú webové stránky len nefunkcné statické obrázky. Spadá sem všetko od programovania webových serverov (ako Microsoft IIS, Apache) cez vytváranie/programovanie aplikacných serverov (tvoria medzivrstvu medzi webovou stránkou a databázou)

Page 144: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

až po budovanie e-business riešení (napr. CRM – Customer Relationship Management)). Posledne menované spadajú už skôr do štvrtej oblasti – programovanie pre enterprise (pozri dalej). Ak sa chcete presadit v tejto oblasti, znalosti VC++ a MFC vám prídu vhod. Doštudovat si budete musiet základy práce sietí (protokoly, aplikácie klient/server), zídu sa vedomosti napríklad architektúry .NET alebo Sun ONE (ak sa zameriate na vytváranie webových služieb). Štvrtá možnost (úzko prepojená s tretou) – programovanie pre enterprise. Ak zamierite sem, dostanete sa do „vyššej“ spolocnosti, ked velakrát ani nebudete vela programovat, ale stanú sa z vás konzultanti. Neodporúcam vám orientovat sa na túto možnost, pokým nie ste už zamestnaní niekde, kde sú ochotní investovat vysoké sumy do kúpy nejakého ERP (Enterprise Resource Planning) systému, inak nemáte šancu dostat sa k takémuto systému (legálne) a hlavne nebudete mat kde svoje vedomosti uplatnit. Pravdepodobne sa nevyhnete databázam (hlavne ORACLE a MS SQL Server). Myslím, že dalšie odporúcania sú zbytocné, a ak sa orientujete na túto oblast, máte už v týchto veciach jasno. No a posledná možnost – tvorenie hier. Takisto velmi zaujímavá oblast. V súcasnosti sa na programovanie hier využíva predovšetkým DirectX a OpenGL sa presúva do segmentu profesionálneho nasadenia grafiky, na co bolo aj primárne urcené (o mrtvom Glide už nemá zmysel nic bližšie písat). Ak budete chciet tvorit hry, musíte okrem pozitívneho vztahu k nim disponovat aj základnými znalostami grafiky (2D aj 3D) a zvuku (znalosti zo spracovania videa sa tiež môžu zíst). Samozrejme, ak budete napríklad programovat len cistú „fyziku“ (deformácie, nárazy, vplyv vetra...), tak napr. grafiku velmi nevyužijete, ale zo zaciatku sa nemôžete nejako špecializovat. Na MFC môžete, samozrejme, zabudnút, ale ako vývojové prostredie je VC++ 6.0 úplne ideálne. Tak, a už sme vycerpali všetky možnosti. Ako som však už písal, existuje ich ovela viac, je len na vás, aké dalšie si vymyslíte a vyberiete. Môžete aj lubovolne kombinovat (napr. ak vám to pomôže, ja sa momentálne zameriavam na oblasti 1, 2, 4). Obr. 1 Ako dalej?

Page 145: C++ pod Windows 1. cast: Úvodmedia1.jex.cz/files/media1:49e6b91344bb5.pdf.upl/05. C++ pod... · na môj e-mail (uvedený na konci tejto casti seriálu) ... WINDOWS API. Windows API,

POZNÁMKY K SERIÁLU... V niektorej z predchádzajúcich castí som ešte sluboval uviest rozsiahlejší príklad, ktorý by demonštroval väcšinu znalostí, ktoré ste mali získat štúdiom tohto seriálu. Tento program, ako aj všetky dalšie ukážkové príklady, riešenia domácich úloh a vašich otázok nájdete na niektorom CD REVUE (reálne v decembrovom, ale možno až v budúcom roku), kde bude uvedená elektronická (PDF) verzia tohto seriálu. Všetky zdrojové texty budú umiestnené v projektovom tvare (co cast, to jedno workspace), takže to nebudú len „suché“ výpisy kódu. Ešte by som rád vymenoval zdroje, z ktorých som pri písaní jednotlivých castí cerpal informácie. V prvom rade to bol velmi dobre spracovaný help k Visual Studiu: MSDN Library, potom vynikajúca kniha Mistrovství ve Visual C++ (už som sa o nej zmienoval v predchádzajúcich castiach) a kniha Marka Andrewsa Programujeme v jazyce Visual C++ (Computer Press, 1997). Rôzne „on-line“ zdroje som postupne zverejnoval v Tipoch na WWW. ZÁVER. Tak, vážení, koncíme. Musím povedat, že pre mna bolo písanie do casopisu tiež velmi zaujímavou skúsenostou a vela som sa naucil. Bol by som velmi rád, keby ste mi do mailu napísali pár vašich postrehov o tomto seriáli, co sa vám pácilo/nepácilo, v com vám seriál pomohol a co vám priniesol. Samozrejme, som vám stále k dispozícií, co sa týka riešenia programátorských „zákysov“. Marek Šamaj