Programare VISUAL C++

78
www.cartiaz.ro – Carti si articole online gratuite de la A la Z Programare VISUAL C++ Sisteme de operare Scurtă descriere VC ++ poate fi utilizat pentru a dezvolta programe pentru trei platforme Win32: Windows NT (pe procesoare multiple), Windows 95/98 si Win32s. Windows NT este un SO multifir (multithreaded ) pe 32 biti cu mediu grafic integrat si posibilitati de server avansate. A fost dezvoltat pentru a maximiza portabilitatea, stabilitatea si securitatea. Subsistemul Win32s este un alt nivel pentru Windows 3.1; acesta implementeaza un subset al sistemului Win32 care permite executia aplicatiilor pe 32 biti. Windows 95 mosteneste o parte semnificativa de cod de la Windows 3.1. Asigura o compatibilitate pentru aplicatiile scrise pentru Windows 3.1. Windows şi Mesajele Windows este referit adesea ca un sistem de operare bazat pe mesaje. Fiecare eveniment (apasarea unei taste, clic de mouse, etc.) este transformat într-un mesaj. In mod obişnuit aplicaţiile sunt construite în jurul unei bucle de mesaje care regăseşte aceste mesaje şi apelează funcţia potrivită pentru a trata mesajul. Mesajele, deşi sunt trimise aplicaţiilor, nu se adresează acestora, ci unei alte componente fundamentale a SO, fereastra (windows). O fereastra este mai mult decât o zonă dreptunghiulară afişată pe ecran; aceasta reprezintă o entitate abstractă cu ajutorul căreia utilizatorul şi aplicaţia interacţionează reciproc.

description

Programare VISUAL C++

Transcript of Programare VISUAL C++

Page 1: Programare VISUAL C++

www.cartiaz.ro – Carti si articole online gratuite de la A la Z

Programare VISUAL C++

Sisteme de operareScurtă descriere

VC ++ poate fi utilizat pentru a dezvolta programe pentru trei platforme Win32: Windows NT (pe procesoare multiple), Windows 95/98 si Win32s.

Windows NT este un SO multifir (multithreaded ) pe 32 biti cu mediu grafic integrat si posibilitati de server avansate. A fost dezvoltat pentru a maximiza portabilitatea, stabilitatea si securitatea.

Subsistemul Win32s este un alt nivel pentru Windows 3.1; acesta implementeaza un subset al sistemului Win32 care permite executia aplicatiilor pe 32 biti.

Windows 95 mosteneste o parte semnificativa de cod de la Windows 3.1. Asigura o compatibilitate pentru aplicatiile scrise pentru Windows 3.1.

Windows şi Mesajele

Windows este referit adesea ca un sistem de operare bazat pe mesaje. Fiecare eveniment (apasarea unei taste, clic de mouse, etc.) este transformat într-un mesaj. In mod obişnuit aplicaţiile sunt construite în jurul unei bucle de mesaje care regăseşte aceste mesaje şi apelează funcţia potrivită pentru a trata mesajul.Mesajele, deşi sunt trimise aplicaţiilor, nu se adresează acestora, ci unei alte componente fundamentale a SO, fereastra (windows). O fereastra este mai mult decât o zonă dreptunghiulară afişată pe ecran; aceasta reprezintă o entitate abstractă cu ajutorul căreia utilizatorul şi aplicaţia interacţionează reciproc.

Page 2: Programare VISUAL C++

www.cartiaz.ro – Carti si articole online gratuite de la A la Z

Aplicaţii, Fire şi Ferestre

O aplicaţie Win32 constă din unul sau mai multe fire (threads), care sunt căi paralele de execuţie. Gândim firele ca fiind multatsking-ul din cadrul unei aplicaţii.

Observaţie: Sub Win32s, poate rula o aplicaţie cu un singur fir de execuţie.

O fereastră este totdeauna “gestionată de” un fir; un fir poate fi proprietarul uneia sau mai multor ferestre sau pentru nici una. In final, ferestrele sunt într-o relaţie ierarhică; unele sunt la nivelul cel mai de sus, altele sunt subordonate părinţilor lor, sunt ferestre descendente.

Page 3: Programare VISUAL C++

www.cartiaz.ro – Carti si articole online gratuite de la A la Z

Procese, fire şi ferestre

Exista mai multe tipuri de ferestre in Windows; cele mai obişnuite sunt asociate cu o aplicaţie. Boxele de dialog din cadrul unei ferestre sunt de asemenea ferestre. Acelaşi lucru pentru butoane, controale de editatre, listbox-uri, icoane, etc.

Clase Window

Comportarea unei ferestre este definita de clasa fereastră (window class). Clasa fereastră menţine informaţii despre modul de afişare iniţial, icoana implicită, cursor, resursele meniu şi cel mai important lucru adresa funcţiei ataşată ferestrei – procedura fereastră – window procedure. Când o aplicaţie procesează mesaje, aceasta se face în mod obişnuit prin apelul funcţiei Windows DispatchMessage pentru fiecare mesaj primit; DispatchMessage la rândul ei apelează procedura fereastră corespunzătoare, identificând iniţial cărei ferestre îi este trimis mesajul. În continuare procedura fereastră va trata mesajul.

Există mai multe clase fereastră standard furnizate de Windows. Aceste clase sistem globale implementează în general funcţionalitatea controalelor comune. Orice aplicaţie poate folosi aceste controale, de exemplu orice aplicaţie poate implementa controale de editare, utilizând clasa fereastra Edit.Aplicaţiile pot de asemeni să-şi definească propriile clase fereastră cu ajutorul funcţiei RegisterClass. Acest lucru se întâmplă în mod obişnuit pentru fereastra principală a aplicaţiei (icoana, resurse, etc.).

Windows permite de asemeni subclasarea sau superclasarea unei ferestre existente. Subclasarea substituie procedura fereastră pentru o clasă ferestră cu o altă procedură. Subclasarea se

realizează prin schimbarea adresei procedurii fereastră cu ajutorul funcţiei SetWindowLong (instance subclassing) sau SetClassLong (subclasare globală).Instance subclassing – înseamnă că se schimbă numai comportarea ferestrei specificate.Global subclassing – înseamnă că se schimbă comportarea tuturor ferestrelor de tipul specificat.

Page 4: Programare VISUAL C++

www.cartiaz.ro – Carti si articole online gratuite de la A la Z

Observaţie: Global subclassing se comporăţ diferit in Win32 şi în Windows pe 16 biţi (Win32s). In cazul Win32, aceasta afectează numai fereastra care este sub controlul aplicaţiei ce face subclasarea; în windows pe 16 biţi, efectul este global, se afectează ferestrele fiecărei aplicaţii.

Superclasarea crează o nouă clasă bazată pe o clasă existentă, reţinând numai procedura fereastră. Pentru a superclasa o clasă fereastră, o aplicaţie regăseşte informaţiile despre clasa fereastră utilizând funcţia GetClassInfo, modifică structura WNDCLASS astfel recepţionată şi foloseşte structura modificată într-un apel al funcţiei RegisterClass. GetClassInfo întoarce de asemenea şi adresa procedurii fereastră. Mesajele pe care noua fereastră nu le tratează trebuie trecute acestei proceduri.

Tipuri de mesaje

Mesajele reprezintă în fapt evenimente la diferite nivele ale aplicaţiei. Există o clasificare a acestor mesaje (din păcate nu prea exactă): mesaje fereastră, mesaje de notificare şi mesaje de comandă, dar deocamdată nu ne interesează acest lucru.

Mesajele windows constau din mai multe părţi, descrise de structura MSG.

typedef struct tagMSG { HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; DWORD time; POINT pt;} MSG;

Descriere:

hwnd identifică în mod unic fereastra la care a fost transmis acest mesaj. Fiecare fereastră în Windows are un asemenea identificator.message reprezintă identificatorul mesajului. Identificatorii mesajului sunt referiţi în mod obişnuit cu ajutorul constantelor simbolice decât prin valoarea lor numerică care o au în sistem. Această descriere se găseşte în windows.h.Următoarele elemente pot fi privite ca parametrii ai mesajului, care au valori specifice funcţie de fiecare mesaj în parte. Marea majoritate a mesajelor încep cu WM_. De exemplu WM_LBUTTONDOWN, WM_MOUSEMOVE, WM_LBUTTONUP, etc.

Aplicaţiile pot să-şi definească propriile mesaje. Cerinţa majoră este ca identificatorul mesajului să fie unic. Pentru a defini un mesaj în sistem folosim funcţia RegisterWindowMessage.

Mesaje şi multitasking

In Windows 3.1, bucla de mesaje are rol important în interacţiunea dintre aplicaţii şi SO. Funcţionarea corectă a unui SO Windows 3.1 depinde de cooperarea dintre aplicaţii. Aplicaţiile sunt cele care permit SO să preia controlul. Acest neajuns este înlăturat în Windows 95/98, NT, 2000. SO este cel care realizează programarea aplicaţiilor pentru

Page 5: Programare VISUAL C++

www.cartiaz.ro – Carti si articole online gratuite de la A la Z

cuantele de timp necesare pe procesor. Deşi această planificare a execuţiei este diferită pe Windows 95/98 faţă de NT, rolul primordial revine SO.

Cozi de mesaje

În Windows pe 16 biţi, SO menţine o singură coadă de mesaje. Când o aplicaţie încearcă să găsească următorul mesaj din coada de mesaje prin funcţiile GetMessage sau PeekMessage, SO poate efectua un context switch şi poate activa o altă aplicaţie pentru care mesajele aşteaptă în coadă. Mesajul din vârful cozii este extras şi returnat aplicaţiei via structura MSG. Dacă aplicaţia eşuează în apelul lui GetMessage, PeekMessage sau Yield, aceasta blochează sistemul. Coada de mesaje poate deveni plină...

În Win32 (95/98, NT, 2000) mecanismul cozii de mesaje este mult mai complicat. O singură coadă de mesaje nu mai rezolvă problema. Aici sistemul de operare dă controlul unei aplicaţii şi tot SO permite multitasking-ul. Două sau mai multe fire pot accesa coada de mesaje în acelaşi timp şi nu există nici o garanţie că mesajele extrase sunt ale lor.Acesta este unul motivele pentru care coada de mesaje a fost separată în cozi de mesaje individuale pentru fiecare fir din sistem.

Procese şi Fire

Într-un SO non-multifir, de exemplu UNIX, unitatea cea mai mică de execuţie este task-ul sau procesul. Mecanismul de planificare (aparţine SO) al task-urilor va comuta între acestea; multitasking-ul se realizează între două sau mai multe procese (task-uri). Dacă o aplicaţie are nevoie să execute mai multe funcţii simultan, atunci aceasta se va divide în mai multe task-uri. Din această tehnică decurg anumite neajunsuri, consum de resurse, timp de lansare a unui nou task, spaţii de adrese diferite, probleme de sincronizare, etc.

În contrast, într-un sistem multifir (multifilar, multithreaded) unitatea cea mai mică de execuţie este firul, nu procesul. Un proces sau task poate conţine mai multe fire, dintre care unul singur este firul principal, firul primar. Lansarea în execuţie a unui nou fir cere mai puţine resurse din partea SO, firul rulează în cadrul aceluiaşi proces, problemele de sincronizare sunt şi nu sunt complicate. Oricum apar două sincronizări: sincronizare între procese şi sincronizare între fire.

Fire şi Mesaje

După cum am mai spus proprietarul ferestrei este firul de execuţie. Fiecare fir are coada proprie, privată, de mesaje în care SO depozitează mesajele adresate ferestrei. Aceasta nu înseamnă că un fir trebuie neapărat să aibă o fereastră proprie şi o coadă proprie de mesaje. Pot exista fire şi fără fereastră şi fără buclă de mesaje.În MFC, aceste fire se numesc worker threads (nu au ataşată o fereastră, nu prezintă interfaţă către utilizator), iar celelalte se numesc user-interface threads.

Apeluri de funcţii Windows

Windows oferă un mare număr de funcţii pentru a executa o mare varietate de task-uri, controlul proceselor, gestionarea ferestrelor, fişierelor, memoriei, servicii grafice, comunicaţii, etc.Apelurile sistem pot fi organizate în trei categorii: 1. servicii nucleu (apeluri sistem pentru controlul proceselor, firelor, gestiunea memoriei, etc.);2. servicii utilizator (gestiunea elementelor de interfaţă ale utilizatorului cum ar fi ferestre, controale, dialoguri,

etc.);3. servicii GDI (Graphics Device Interface) (ieşirea grafică independentă de dispozitiv).

Page 6: Programare VISUAL C++

www.cartiaz.ro – Carti si articole online gratuite de la A la Z

Sistemul Windows include de asemenea funcţii API pentru alte funcţionalităţi – MAPI (Messaging API), TAPI (Telephony API) sau ODBC (Open Database Connectivity).

Servicii Nucleu

Serviciile nucleu cuprind de obicei: getionarea fişierelor, memoriei, proceselor, firelor, resurselor. Gestionarea fişierelor nu ar trebui să se mai facă cu funcţii din bibliotecile C sau prin iostream-urile din

C++. Aplicaţiile ar trebui să utilizeze conceptul Win32 de obiect fisier – file object – şi funcţiile asociate cu acesta. De exemplu există fişiere mapate în memorie care asigura comunicarea între task-uri.

Referitor la gestionarea memoriei pe lângă funcţiile cunoscute, SO Windows oferă funcţii care pot manipula spaţii de adrese de sute de MB alocându-le dar nefăcând commiting.

Cea mai importantă faţetă a proceselor şi firelor este gestiunea sincronizării. Problema este complet nouă şi nu a fost întâlnită în Windows 3.1. În Win32, sincronizarea se face cu ajutorul unor obiecte de sincronizare, pe care firele le pot utiliza pentru a informa alte fire despre starea lor, de a proteja zone senzitive de cod sau de a obtine informatii despre alte fire sau starea altor obiecte.

In Win32 multe resurse nucleu sunt reprezentate ca obiecte – obiecte nucleu: fişiere, fire, procese, obiecte de sincronizare, etc. Obiectele sunt referite prin manipulatori, identificatori (handlers); există funcţii pentru manipularea generică a obiectelor, pentru manipularea obiectelor de un anumit tip. Sub NT, obiectele au ataşate proprietăţi de securitate. De exemplu, un fir nu poate accesa un obiect fişier dacă nu are drepturile necesare care să coincidă cu proprietăţile de securitate.

Modulul nucleu furnizezză de asemeni funcţii pentru gestionarea resurselor interfaţă-utilizator. Aceste resurse includ icoane, cursoare, şabloane de dialog, resurse string, tabele de acceleratori, bitmap-uri, etc.Nucleul NT furnizează printre altele: atribute de securitate pentru obiectele nucleu, backup, funcţionalitatea aplicaţiilor de tip consolă care pot utiliza funcţii pentru memoria virtuală sau pot utiliza mai multe fire de execuţie.

Servicii utilizator

Modulul utilizator furnizează apeluri sistem care gestionează aspecte şi elemente ale interfeţei utilizatorului; sunt incluse funcţii care manipulează ferestre, dialoguri, meniuri, controale, clipboard, etc. Se exemplifică tipurile de operaţii pentru fiecare resursă în parte (în general creare, modificare, ştergere, mutare, redimensionare, etc.).

Modulul utilizator furnizează funcţii pentru managementul mesajelor şi cozilor de mesaje. Aplicaţiile pot utiliza aceste apeluri pentru a controla conţinutul cozii de mesaje proprii, a regăsi şi a procesa mesajele, a crea noi mesaje. Noile mesaje pot fi trimise (sent) sau plasate (posted) la orice fereastră. Un mesaj plasat pentru o fereastră – funcţia PostMessage - înseamnă pur şi simplu intrarea acestuia în coada de mesaje nu şi procesarea imediată a acestuia. Trimiterea unui mesaj (sent) implică tratarea lui imediată sau mai corect spus funcţia SendMessage nu-şi termină execuţia pînă când mesajul nu a fost tratat.

Servicii GDI

Funcţiile din GDI sunt utilizate în mod obişnuit pentru a executa operaţii grafice primitive independente de dispozitiv pe contexte de dispozitiv. Un context de dispozitiv este o interfaţă la un periferic grafic specific (în fapt este o structură de date păstrată în memorie). Contextul de dispozitiv poate fi utilizat pentru a obţine informaţii despre periferic şi pentru a executa ieşirile grafice pe acest periferic. Informaţiile care pot fi obţinute printr-un context de dispozitiv, descriu în detaliu acest periferic.

Page 7: Programare VISUAL C++

www.cartiaz.ro – Carti si articole online gratuite de la A la Z

Ieşirea grafică este executată printr-un context de dispozitiv prin pasarea (trecerea) unui manipulator (identificator) al contextului de dispozitiv funcţiilor grafice din GDI.

Contextele de dispozitiv pot descrie o varietate mare de periferice. Contextele de dispozitiv obişnuite includ: contexte de dispozitiv display, contexte de dispozitiv memorie (pentru ieşirea unui bitmap memorat în memorie) sau contexte de dispozitiv printer.Un context de dispozitiv foarte special este contextul de dispozitiv metafile care permite aplicaţiilor de a înregistra permanent apelurile din GDI (fişierul păstrează o serie de primitive grafice) care sunt independente de dispozitiv.Metafişierele joacă un rol crucial în reperzentarea independentă de dispozitiv a obiectelor OLE înglobate.

Desenarea într-un context de dispozitiv se face cu ajutorul coordonatelor logice. Coordonatele logice descriu obiectele utilizând măsurători reale independente de dispozitiv, de exemplu, un dreptunghi poate fi descris ca fiind lat de 2 inch şi înalt de 1 inch. GDI furnizează funcţionalitatea necesară pentru maparea coordonatelor logice în coordonate fizice.

Diferenţe semnificative există în modul cum această mapare are loc în Win32s, Windows 95 şi Windows NT.

Win32s şi Windows 95 folosesc reprezentarea coordonatelor pe 16 biti.Windows NT poate manipula coordonate pe 32 biţi.

Toatre cele trei sisteme suportă mapări (transformări) din coordonate logice în coordonate fizice. Aceste transformări sunt determinate (influenţate) de valorile ce specifică originea coordonatelor şi (signed extent) extensia cu semn a spaţiului logic şi al celui fizic.Originea coordonatelor specifică deplasarea pe orizontală şi verticală, iar extensia (extent) determina orientarea şi scara obiectelor după mapare (transformare).În plus, Windows NT oferă ceea ce se numeşte world transformation functions. Prin aceste funcţii, orice transformare liniară poate fi folosită pentru transformarea spaţiului de coordonate logice în spaţiul de coordonate fizice; în plus pentru translaţii şi scalare ieşirile pot fi rotite sau sheared.Exemple de funcţii grafice: Rectangle, Ellipse, Polygon, TextOut, etc.Alte funcţii de interes deosebit (bit blit functions: PatBlt, BitBlt, StechBlt) sunt cele legate de desenarea şi copierea bitmap-urilor.

Contextele de dispozitiv pot fi create şi distruse, starea lor poate fi salvată şi reîncărcată.

Un alt grup de funcţii gestionează transformările de coordonate. Funcţii comune tuturor platformelor pot fi utilizate pentru a seta sau regăsi originea şi extent-ul unei ferestre (spaţiul de coordonate logic) şi viewport-ului (spaţiul de coordonate al perifericului destinaţie).NT posedă funcţii specifice pentru transformări matriceale.

Funcţiile GDI pot fi folosite de asemenea pentru gestionarea paletelor, aceasta înseamnă că prin gestionarea paletei de culori, aplicaţiile pot selecta o mulţime de culori care se potrivesc cel mai bine cu culorile din imaginea (gif, pcx.) care trebuie afişată. Gestionarea paletei poate fi utilizată şi în tehnici de animaţie.

O altă trăsătură a GDI-ului este crearea şi gestionarea obiectelor GDI (pensoane, peniţe, fonturi, bitmap-uri, palete) precum şi a regiunilor şi a clipping-ului.

Alte API-uri

• Funcţii pentru controale comune; • Funcţii pentru dialoguri comune;• MAPI, (Messaging Applications Programming Interface);• MCI (Multimedia Control Interface);• OLE API; • TAPI (Telephony API).

Page 8: Programare VISUAL C++

www.cartiaz.ro – Carti si articole online gratuite de la A la Z

Raportarea erorilor

Majoritatea funcţiilor Windows folosesc un mecanism pentru evidenţierea erorilor. Când apare o eroare, aceste funcţii setează o valoare a erorii pentru firul respectiv, valoare care poate fi regăsită cu funcţia GetLastError.

Valoarile pe 32 biţi, returnate de această funcţie sunt definite in winerror.h sau în fişierul header al bibliotecii specifice.Valoarea erorii poate fi setată şi din cadrul aplicaţiei cu ajutorul funcţiei SetLastError. Codurile de eroare trebuie să aibă setat bitul 29.

Folosirea funcţiilor din biblioteca C/C++

Aplicaţiile Win32 pot folosi setul standard al funcţiilor din biblioteca C/C++ cu anumite restricţii. Aplicaţiile Windows nu au acces în mod normal la stream-urile stdin, stdout, stderr sau obiectele iostream din C++. Numai aplicaţiile consolă pot utiliza aceste stream-uri.Funcţiile relative la fişiere pot fi folosite, dar acestea nu suportă toate facilităţile oferite de SO Windows – securitate, drepturi de acces.

În locul funcţiilor din familia exec se va folosi CreateProcess.

În ceeea ce priveşte gestionarea memoriei se folosesc cu succes funcţiile din C sau C++. Funcţiile din biblioteca matematică, pentru gestionarea stringurilor, a bufferelor, a caracterelor, a conversiilor de date pot fi de asemenea folosite.Aplicaţiile Win32 nu trebuie să folosească întreruperea 21 sau funcţii IBM PC BIOS.

Page 9: Programare VISUAL C++

Crearea unei aplicaţii WindowsVC++ nu compilează numai cod ci şi generează cod. Pentru generarea unei aplicaţii se foloseşte AppWizard.În primul rând aici se lucrează cu proiecte. Un proiect poate conţine mai multe aplicaţii.Pentru a crea un proiect vom folosi comenzile File->New->Projects. Din lista prezentată în pagina (tab-ul) Projects vom selecta MFC AppWizard (EXE), selectăm locul unde va fi memorat pe HDD şi apoi completăm numele proiectului. După acest lucru va trebui să completăm o serie de informaţii grupate pe etape.

Etapa 1. Tip aplicaţie

În etapa 1 (pas 1) vom selecta tipul aplicaţiei (se alege interfaţa cu utilizatorul). Avem următoarele posibilităţi:

• O aplicaţie single document interface (SDI), are numai un document deschis la un moment dat. Când selectăm File->Open, fişierul existent şi care este deschis va fi închis înainte ca să se deschidă noul document.

• O aplicaţie multiple document interface (MDI), cum ar fi Excel sau Word, poate deschide mai multe documente odată. Dacă dorim vizualizări multiple pentru un document va trebui să construim o aplicaţie MDI.

• O aplicaţie dialog-based, cum ar fi utilitarul Character Map. Aplicaţiile nu au meniu.

OBSERVAŢIE:: Aplicaţiile bazate pe dialog sunt diferite de cele de tip SDI sau MDI. Vor fi tratate în mod separat.

Mai există un checkbox care ne dă posibilitatea de a indica dacă dorim suport pentru arhitectura Document/View. Opţiunea se foloseşte în special pentru portarea aplicaţiilor dintr-un alt sistem de dezvoltare. Nu o vom folosi.

Etapa 2. Baze de dateÎn această etapă vom alege nivelul pentru suportul bazelor de date.Există patru posibilităţi:

• Pentru aplicaţii fără baze de date vom selecta None. • Dacă dorim să avem acces la baze de date dar nu dorim să derivăm vizualizarea din CFormView sau să nu

avem meniu Record vom selecta Header Files Only. • Dacă dorim să derivăm vizualizarea din CFormView şi să avem meniul Record dar nu dorim să

serializăm documentul, vom selecta Database View Without File Support. • Dacă dorim suport pentru baze de date şi în plus dorim şi salvarea documentului vom selecta Database

View With File Support.

Dacă selectăm ultima opţiune, va trebui să indicăm şî sursa de date – butonul Data Source.

Etapa 3. Suport pentru documente compuseTehnologia ActiveX şi OLE este referită ca fiind tehnologia documentului compus (compound document technology).În această etapă există cinci posibilităţi:

• Dacă nu scriem o aplicaţie ActiveX, alegem None.

9

Page 10: Programare VISUAL C++

• Dacă dorim o aplicaţie care să conţină obiecte ActiveX înglobate sau legate, cum ar fi aplicaţia Word, alegem Container.

• Dacă dorim ca aplicaţia noastră să furnizeze obiecte, care pot fi înglobate, pentru alte aplicaţii, dar aplicaţia să nu poată fi executată separat (stand alone), vom alege Mini Server.

• Dacă dorim ca aplicaţia noastră să furnizeze obiecte, care pot fi înglobate, pentru alte aplicaţii, şi aplicaţia să poată fi executată separat (stand alone), vom alege Full Server.

• Dacă dorim ca aplicaţia să încorporeze opţiunile 3 şi 4 vom selecta Both Container and Server.

Dacă alegem suport pentru documentele compuse vom avea şi suport pentru fişiere compuse (compound files). Fişierele compuse conţin unul sau mai multe obiecte ActiveX şi sunt salvate într-un mod special astfel încât un obiect poate fi modificat fără a rescrie întregul fişier. Pentru acest lucru facem o selecţie pe unul din butoanele radio Yes, Please, sau No, Thank You.Tot în această pagină ne hotărâm dacă aplicaţia suportă automatizare sau va folosi controale ActiveX.

OBSERVAŢIE: Dacă dorim ca aplicaţia să fie un control ActiveX, nu trebuie să creăm o aplicaţie .exe obişnuită. Crearea controlului ActiveX se face selectând o altă opţiune din Projects.

Etapa 4. Opţiuni pentru interfaţă. Alte Opţiuni

Următoarele opţiuni afectează modul de afişare al interfeţei:• Docking Toolbar. AppWizard pregăteşte un toolbar. Acesta poate fi editat (adăugare, modificare, ştergere). • Initial Status Bar. AppWizard crează o bară de stare care afişează mesajele ataşate comenzilor din meniu sau alte mesaje specifice aplicaţiei.• Printing and Print Preview. Aplicaţia va avea opţiunile Print şi Print Preview din meniul File, şi o parte din codul necesar va fi generat de AppWizard.• Context-Sensitive Help. Meniul Help va avea opţiunile Index şi Using Help, şi o parte din codul necesar pentru a fi implementat Help va fi generat de AppWizard. Această decizie este dificil de luat mai târziu pentru că părţi din cod sunt generate în diverse locuri din cadrul aplicaţiei.• 3D Controls. Aplicaţia va arăta ca o aplicaţie obişnuită Windows 95. Dacă nu selectăm această opţiune, boxele de dialog vor avea background alb şi nu vor fi umbre în jurul boxelor de editare, checkbox-urilor şi alte controale.• MAPI(Messaging API). Aplicaţia va avea posibilitatea de trimite fax-uri, email-uri şi alte mesaje.• Windows Sockets. Aplicaţia va putea accesa Internet-ul în mod direct, folosind protocoale ca FTP şi HTTP (protocolul World Wide Web).

Putem seta de asemenea numărul fişierelor din lista MRU. Implicit acest număr este 4.Butonul Advanced activează două tab-uri (Document Template Strings, Window Styles) unde putem schimba numele aplicaţiei, titlul ferestrei cadru, extensia fişierelor folosite în File->Open, etc.

Prporpietăţile care pot fi setate pentru ferestrele cadru:• Thick Frame. Dacă nu o selectăm se previne redimensionarea.• Minimize Box. • Maximize Box. • System Menu. • Minimized. Cadrul este minimizat când aplicaţia este lansată în execuţie. Pentru aplicaţiile SDI, această opţiune va fi ignorată când aplicaţia rulează sub Windows 95.

10

Page 11: Programare VISUAL C++

• Maximized. The frame is maximized when the application starts. For SDI applications, this option will be ignored when the application is running under Windows 95.

Alte OpţiuniDorim biblioteca MFC să fie “legată” ca un DLL partajabil sau în mod static? Un DLL este o colecţie de funcţii utilizate de diferite aplicaţii. Folosirea DLL-urilor face ca programul să fie mai mic dar mai greu de instalat. Dacă legătura este statică creşte mărimea programului dar nu mai sunt probleme deosebite cu instalarea.

Etapa 6. Numele fişierelor şi al claselorUltima etapă stabileşte numele claselor şi fişierelor create de AppWizard. Putem schimba aceste nume. Dacă aplicaţia include o clasă pentru vizualizare, care în mod obişnuit este derivată din CView, putem schimba clasa de bază, de exemplu CScollView sau CEditView care oferă o funcţionalitate sporită vizualizării.Vom apăsa butonul Finish pentru terminarea generării aplicaţiei.

Urmează exemple de creare aplicaţii pentru fiecare tip în parte.

Crearea DLL-urilor, Aplicaţiilor de tip Consolă Alte opţiuni ce se regăsesc în tab-ul Projects:• ATL COM AppWizard • Custom AppWizard • Database Project • DevStudio Add-In Wizard • Extended Stored Procedure AppWizard • ISAPI Extension Wizard • Makefile • MFC ActiveX ControlWizard • MFC AppWizard (dll) • Utility Project • Win32 Application • Win32 Console Application • Win32 Dynamic Link Library • Win32 Static Library

ATL COM AppWizardATL este Active Template Library, şi este utilizată pentru a scrie un mic control AciteX.

Custom AppWizardSe foloseşte în general pentru a crea noi proiecte din altele existente (se copie cod, help, etc.).

Database ProjectDacă avem instalat Enterprise Edition of Visual C++, putem crea un proiect de baze de date.

11

Page 12: Programare VISUAL C++

DevStudio Add-In WizardAdd-ins lucrează exact ca macro-urile care automatizează Developer Studio, dar acestea sunt scrise în C++ sau alt limbaj de programare; macro-urile sunt scrise în VBScript. Acestea folosesc automatizarea pentru a manipula Developer Studio.

ISAPI Extension WizardISAPI este pentru Internet Server API şi se referă la funcţii pe care le putem apela pentru a interacţiona cu un server Microsoft Internet Information Server.

MakefileDacă dorim să creăm un proiect care este utilizat cu un alt utilitar make diferit de Developer Studio, vom selecta această opţiune. Nu se generează cod.

MFC ActiveX ControlWizardActiveX controls sunt controale pe care lescriem pentru a fi utilizate într-un dialog din VC++, o formă din VB sau chiar o pagină Web. Aceste controale înlocuiesc controalele VBX.

MFC AppWizard (DLL)Dacă dorim să colectăm un număr de funcţii într-un DLL, şi aceste funcţii folosesc clase din MFC, vom selecta această opţiune. Dacă funcţiile nu folosesc MFC alegem Win32 Dynamic Link Library.

Win32 ApplicationSe crează o aplicaţie obişnuită în C++.

Win32 Console ApplicationO aplicaţie consolă arată ca o aplicaţie obişnuită DOS.

Win32 Dynamic Link LibraryDacă dorim să colectăm un număr de funcţii într-un DLL, şi aceste funcţii folosesc clase din MFC, vom selecta această opţiune. Dacă funcţiile nu folosesc MFC alegem Win32 Dynamic Link Library.

Win32 Static LibraryAplicaţia va încorpora funcţiile pe care foloseşte dintr-un DLL.

12

Page 13: Programare VISUAL C++

Înţelegerea codului generat de AppWizardAplicaţie SDIO aplicaţie SDI are meniu pe care utilizatorul poate să-l folosească pentru a deschide documente şi să lucreze cu ele.

AppWizard generează cinci clase. Numele proiectului este FirstSDI.• CAboutDlg, o clasă de dialog pentru About• CFirstSDIApp, o clasă CWinApp pentru întraga aplicaţie (obiectul aplicaţie) application • CFirstSDIDoc, o clasa document• CFirstSDIView, o clasă vizualizare • CMainFrame, o clasă cadru

Fişierul FirstSDI.h – pentru aplicaţie// FirstSDI.h : main header file for the FIRSTSDI application//#if !defined(AFX_FIRSTSDI_H__CDF38D8A_8718_11D0_B02C_0080C81A3AA2__INCLUDED_)#define AFX_FIRSTSDI_H__CDF38D8A_8718_11D0_B02C_0080C81A3AA2__INCLUDED_#if _MSC_VER >= 1000#pragma once#endif // _MSC_VER >= 1000#ifndef __AFXWIN_H__ #error include `stdafx.h' before including this file for PCH#endif#include "resource.h" // main symbols/////////////////////////////////////////////////////////////////////////////// CFirstSDIApp:// See FirstSDI.cpp for the implementation of this class//class CFirstSDIApp : public CWinApp{public: CFirstSDIApp();// Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CFirstSDIApp) public: virtual BOOL InitInstance(); //}}AFX_VIRTUAL// Implementation //{{AFX_MSG(CFirstSDIApp) afx_msg void OnAppAbout(); // NOTE - The ClassWizard will add and remove member functions here. // DO NOT EDIT what you see in these blocks of generated code! //}}AFX_MSG DECLARE_MESSAGE_MAP()};///////////////////////////////////////////////////////////////////////////////{{AFX_INSERT_LOCATION}}// Microsoft Developer Studio will insert additional declarations// immediately before the previous line.

13

Page 14: Programare VISUAL C++

#endif //!defined(AFX_FIRSTSDI_H__CDF38D8A_8718_11D0_B02C_0080C81A3AA2__INCLUDED_)

#ifndef acţionează ca în codul care urmează,şi are ca efect includerea fişierului ce urmează o singură dată. #ifndef test_h#include "test.h"#define test_h#endif

#pragma once este de asemenea pentru a preveni definiţiile multiple dacă acest fişier este inclus de două ori.

Clasa CFirstSDIApp derivată din CWinApp, furnizează cea mai mare funcţionalitate a aplicaţiei. Instanţa acestei clase constituie obiectul aplicaţie. AppWizard a generat anumite funcţii pentru această clasă care reacoperă funcţiile moştenite din clasa de bază. Această secţiune de cod începe cu //Overrides. De asemenea sunt generate annumite comentarii care ajută la înţelegerea codului. În această secţiune vom găsi declaraţia pentru funcţia InitInstance(). Următoarea secţiune de cod este pentru harta de mesaje. AppWizard generează cod pentru constructorul CFirstSDIApp, şi funcţiile InitInstance() şi OnAppAbout() în fişierul firstsdi.cpp. Codul generat pentru constructor arată astfel:

CFirstSDIApp::CFirstSDIApp(){ // TODO: add construction code here, // Place all significant initialization in InitInstance}

Ca regulă generală trebuie reţinuţ că Microsoft construieşte obiectele în doi paşi. Se crează obiectul unde se scrie cod care sigur se execută corect, iar iniţializările se fac cu ajutorul unei funcţii membru, care poate indica dacă acestea au fost efectuate cu succes sau nu. Constructorul nu întoarce nici o valore, construieşte obiectul.

În continuare prezentăm listingul pentru CFirstSDIApp::InitInstance()BOOL CFirstSDIApp::InitInstance(){ AfxEnableControlContainer(); // Standard initialization // If you are not using these features and want to reduce the size // of your final executable, you should remove from the following // the specific initialization routines you don't need.#ifdef _AFXDLL Enable3dControls(); // Call this when using MFC in a shared DLL#else Enable3dControlsStatic(); // Call this when linking to MFC statically#endif // Change the registry key under which our settings are stored. // You should modify this string to be something appropriate, // such as the name of your company or organization.

SetRegistryKey(_T("Local AppWizard-Generated Applications")); LoadStdProfileSettings(); // Load standard INI file options (including

// MRU) // Register the application's document templates. Document templates // serve as the connection between documents, frame windows, and views.

14

Page 15: Programare VISUAL C++

CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(CFirstSDIDoc), RUNTIME_CLASS(CMainFrame), // main SDI frame window RUNTIME_CLASS(CFirstSDIView)); AddDocTemplate(pDocTemplate);

// Parse command line for standard shell commands, DDE, file open

CCommandLineInfo cmdInfo; ParseCommandLine(cmdInfo);

// Dispatch commands specified on the command line

if (!ProcessShellCommand(cmdInfo)) return FALSE;

// The one and only window has been initialized, so show and update it.

m_pMainWnd->ShowWindow(SW_SHOW); m_pMainWnd->UpdateWindow(); return TRUE;}

Comentariile sunt incluse de AppWizard. InitInstance este apelată prima la lansarea apliacţiei.

AfxEnableControlContainer() permite aplicaţiei de a conţine controale ActiveX. Se permite afişarea controalelor cu aspect 3D.Se completează regiştrii sub care va fi înregistrată această aplicaţie.InitInstance() înregistrează în continuare şablonul de document care este SDI în acest caz.InitInstance() crează un obiect vid (neiniţializat) CCommandLineInfo pentru a menţine parametrii pasaţi aplicaţiei prin linia de comandă când aceasta a fost lansată în execuţie şi apoi apelează ParseCommandLine() pentru a completa acest obiect. În final se procesează linia de comandă printr-un apel la ProcessShellCommand(). De exemplu dacă lansăm aplicaţia din linia de comandă astfel: FirstSDI fooble, aplicaţia va deschide fişierul fooble.

Parametrii din linia de comandă pe care ProcessShellCommand() îi suportă sunt următorii:

Parameter ActionNone Start app and open new file.Filename Start app and open file./p filename Start app and print file to default printer./pt filename printer driver port Start app and print file to the specified printer./dde Start app and await DDE command./Automation Start app as an OLE automation server./Embedding Start app to edit an embedded OLE item.

Dacă dorim să implementăm o altă comportare, vom construi o clasă derivată din CCommandLineInfo pentru a păstra linia de comandă, şi apoi rescriem funcţiile CWinApp:: ParseCommandLine() şi CWinApp::ProcessShellCommand().

15

Page 16: Programare VISUAL C++

Se completează variabila m_pMainWnd cu adresa obiectului aplicaţiei, variabilă ce este defintăîn CWinThread, ce este clasă de bază pentru CWinApp.La sfârşit funcţia întoarce TRUE pentru a indica că restul aplicaţiei poate rula.

Harta de mesaje indică că funcţia OnAppAbout() va trata un mesaj. Care este? Va trata comanda de meniu care are ID-ul ID_APP_ABOUT. Acest ID corespunde comenzii de meniu Help->About.Descrierea hărţii de mesaje este:

BEGIN_MESSAGE_MAP(CFirstSDIApp, CWinApp) //{{AFX_MSG_MAP(CFirstSDIApp) ON_COMMAND(ID_APP_ABOUT, OnAppAbout) // NOTE - The ClassWizard will add and remove mapping macros here. // DO NOT EDIT what you see in these blocks of generated code! //}}AFX_MSG_MAP // Standard file-based document commands ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew) ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen) // Standard print setup command ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)END_MESSAGE_MAP()

OnAppAbout() arată aşa:

void CFirstSDIApp::OnAppAbout(){ CAboutDlg aboutDlg; aboutDlg.DoModal();}

Se construieşte obiectul aboutDlg, care este o boxă de dialog, şi apoi se execută acest dialog.

Aplicaţia Multiple Document Interface O asemenea aplicaţie are meniu şi permite utilizatorului de a deschide mai multe documente odată. AppWizard a generat cinci clase. Numele proiectului este FirstMDI. Clasele generate sunt:

• CAboutDlg, o clasă de dialog pentru About• CFirstMDIApp, o clasă CWinApp pentru aplicaţie• CFirstMDIDoc, o clasă document• CFirstMDIView, o clasă pentru vizualizare• CMainFrame, o clasă cadru

Listing pentru FirstMDI.h// FirstMDI.h : main header file for the FIRSTMDI application//#if !defined(AFX_FIRSTMDI_H__CDF38D9E_8718_11D0_B02C_0080C81A3AA2__INCLUDED_)#define AFX_FIRSTMDI_H__CDF38D9E_8718_11D0_B02C_0080C81A3AA2__INCLUDED_#if _MSC_VER >= 1000#pragma once#endif // _MSC_VER >= 1000#ifndef __AFXWIN_H__ #error include `stdafx.h' before including this file for PCH#endif#include "resource.h" // main symbols/////////////////////////////////////////////////////////////////////////////// CFirstMDIApp:

16

Page 17: Programare VISUAL C++

// See FirstMDI.cpp for the implementation of this class//class CFirstMDIApp : public CWinApp{public: CFirstMDIApp();// Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CFirstMDIApp) public: virtual BOOL InitInstance(); //}}AFX_VIRTUAL// Implementation //{{AFX_MSG(CFirstMDIApp) afx_msg void OnAppAbout(); // NOTE - The ClassWizard will add and remove member functions here. // DO NOT EDIT what you see in these blocks of generated code ! //}}AFX_MSG DECLARE_MESSAGE_MAP()};///////////////////////////////////////////////////////////////////////////////{{AFX_INSERT_LOCATION}}// Microsoft Developer Studio will insert additional declarations immediately// before the previous line.#endif //!defined(AFX_FIRSTMDI_H__CDF38D9E_8718_11D0_B02C_0080C81A3AA2__INCLUDED_)

Nici o diferenţă faţă de clasa de la aplicaţia SDI.

Listing pentru CFirstMDIApp::InitInstance()BOOL CFirstMDIApp::InitInstance(){ AfxEnableControlContainer(); // Standard initialization // If you are not using these features and want to reduce the size // of your final executable, you should remove from the following // the specific initialization routines you don't need.#ifdef _AFXDLL Enable3dControls(); // Call this when using MFC in a shared DLL#else Enable3dControlsStatic(); // Call this when linking to MFC statically#endif // Change the registry key under which your settings are stored. // You should modify this string to be something appropriate, // such as the name of your company or organization. SetRegistryKey(_T("Local AppWizard-Generated Applications")); LoadStdProfileSettings(); // Load standard INI file options (including

// MRU) // Register the application's document templates. Document templates // serve as the connection between documents, frame windows, and views. CMultiDocTemplate* pDocTemplate; pDocTemplate = new CMultiDocTemplate( IDR_FIRSTMTYPE, RUNTIME_CLASS(CFirstMDIDoc),

17

Page 18: Programare VISUAL C++

RUNTIME_CLASS(CChildFrame), // custom MDI child frame RUNTIME_CLASS(CFirstMDIView)); AddDocTemplate(pDocTemplate); // create main MDI Frame window CMainFrame* pMainFrame = new CMainFrame; if (!pMainFrame->LoadFrame(IDR_MAINFRAME)) return FALSE; m_pMainWnd = pMainFrame; // Parse command line for standard shell commands, DDE, file open CCommandLineInfo cmdInfo; ParseCommandLine(cmdInfo); // Dispatch commands specified on the command line if (!ProcessShellCommand(cmdInfo)) return FALSE; // The main window has been initialized, so show and update it. pMainFrame->ShowWindow(m_nCmdShow); pMainFrame->UpdateWindow(); return TRUE;}

Care sunt deferenţele? Folosind WinDiff le putem vedea mai uşor.

• Aplicaţia MDI foloseşte CMultiDocTemplate • Aplicaţia MDI setează fereastra cadru şi apoi o arată, ceea ce nu face o aplicaţie SDI.

Componentele unei aplicaţii bazate pe dialogAppWizard generează trei clase:

• CAboutDlg, o clasă dialog pentru About• CFirstDialogApp, o clasă CWinApp pentru întreaga aplicaţie• CFirstDialogDlg, o clasă de dialog pentru întreaga aplicaţie.

// FirstDialog.h : main header file for the FIRSTDIALOG application//#if !defined(AFX_FIRSTDIALOG_H__CDF38DB4_8718_11D0_B02C_0080C81A3AA2__INCLUDED_)#define AFX_FIRSTDIALOG_H__CDF38DB4_8718_11D0_B02C_0080C81A3AA2__INCLUDED_#if _MSC_VER >= 1000#pragma once#endif // _MSC_VER >= 1000#ifndef __AFXWIN_H__ #error include `stdafx.h' before including this file for PCH#endif#include "resource.h" // main symbols/////////////////////////////////////////////////////////////////////////////// CFirstDialogApp:// See FirstDialog.cpp for the implementation of this class//class CFirstDialogApp : public CWinApp{public: CFirstDialogApp();// Overrides // ClassWizard generated virtual function overrides

18

Page 19: Programare VISUAL C++

//{{AFX_VIRTUAL(CFirstDialogApp) public: virtual BOOL InitInstance(); //}}AFX_VIRTUAL// Implementation //{{AFX_MSG(CFirstDialogApp) // NOTE - The ClassWizard will add and remove member functions here. // DO NOT EDIT what you see in these blocks of generated code ! //}}AFX_MSG DECLARE_MESSAGE_MAP()};///////////////////////////////////////////////////////////////////////////////{{AFX_INSERT_LOCATION}}// Microsoft Developer Studio will insert additional declarations immediately// before the previous line.#endif // !defined(AFX_FIRSTDIALOG_H__CDF38DB4_8718_11D0_B02C_0080C81A3AA2¬__INCLUDED_)

Listing pentru CDialog16App::InitInstance()BOOL CFirstDialogApp::InitInstance(){ AfxEnableControlContainer(); // Standard initialization // If you are not using these features and want to reduce the size // of your final executable, you should remove from the following // the specific initialization routines you don't need.#ifdef _AFXDLL Enable3dControls(); // Call this when using MFC in a shared DLL#else Enable3dControlsStatic(); // Call this when linking to MFC statically#endif CFirstDialogDlg dlg; m_pMainWnd = &dlg; int nResponse = dlg.DoModal(); if (nResponse == IDOK) { // TODO: Place code here to handle when the dialog is // dismissed with OK } else if (nResponse == IDCANCEL) { // TODO: Place code here to handle when the dialog is // dismissed with Cancel } // Because the dialog has been closed, return FALSE so that you exit the // application, rather than start the application's message pump. return FALSE;}

19

Page 20: Programare VISUAL C++

Bucla de mesaje “ascunsă”

#include <windows.h>int WINAPI WinMain(HINSTANCE d1, HINSTANCE d2, LPSTR d3, int d4){ MessageBox(NULL, "Hello, World!", "", MB_OK);}

Bucla de mesaje şi procedura fereastră sunt ascunse. MessageBox afişează o boxă de dialog care conţine procedura fereastră şi deoarece boxa de dialog este modală (nu poate fi părăsită fără a se da clic pe ...) practic se ciclează pe bucla de mesaje.

Bucla de mesaje există

Un program windows obişnuit, în timpul iniţializării, înregistrează mai întâi clasa fereastră apoi crează fereastra principală utilizând noua clasă înregistrată. În exemplul ce urmează folosim deja clasa înregistrată, BUTTON.

#include <windows.h>int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE d2, LPSTR d3, int d4){ MSG msg; HWND hwnd; hwnd = CreateWindow("BUTTON", "Hello, World!", WS_VISIBLE | BS_CENTER, 100, 100, 100, 80, NULL, NULL, hInstance, NULL);

while (GetMessage(&msg, NULL, 0, 0)) { if (msg.message == WM_LBUTTONUP) { DestroyWindow(hwnd); PostQuitMessage(0); } DispatchMessage(&msg); } return msg.wParam;}

Explicaţii: După ce se creează fereastra, programul intră în bucla while, unde se apelează GetMessage. Când aplicaţia primeşte un mesaj, GetMessage întoarce acel mesaj; valoarea întoarsă este FALSE numai dacă mesajul primit a fost WM_QUIT.La tratarea mesajului WM_LBUTTONDOWN se distruge fereastra aplicaţiei şi apoi se pune în coda de mesaje, mesajul WM_QUIT, pentru a se realiza terminarea buclei while. Orice alt mesaj diferit de WM_LBUTTONDOWN nu este tratat de aplicaţie, este preluat de DispatchMessage care va apela procedura fereastră a clasei BUTTON. În marea majoritate a cazurilor procedura nu execută nimic special, unul din rolurile ei fiind acela de a goli coada de mesaje a aplicaţiei şi de a respecta principiul “în Windows nici un mesaj nu se pierde”.

În afară de GetMessage, mai existăşi funcţia PeekMessage care se utilizează de obicei când aplicaţia doreşte să execute anumite acţiuni şi nu are nici un mesaj de procesat.

Page 21: Programare VISUAL C++

Proceduri fereastră

#include <windows.h>// ---------------- Apelata pe mesajul WM_PAINTvoid DrawHello(HWND hwnd){ HDC hDC; PAINTSTRUCT paintStruct; RECT clientRect; hDC = BeginPaint(hwnd, &paintStruct); if (hDC != NULL) { GetClientRect(hwnd, &clientRect); DPtoLP(hDC, (LPPOINT)&clientRect, 2); DrawText(hDC, "Hello, World!", -1, &clientRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); EndPaint(hwnd, &paintStruct); }}// --------------------------- Procedura fereastraLRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){ switch(uMsg) { case WM_PAINT: DrawHello(hwnd); break;

case WM_DESTROY: PostQuitMessage(0); break;

default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } return 0; // trebuie sa intoarca totdeauna 0 (zero)}// --------------- Programul principalint WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR d3, int nCmdShow){ MSG msg; HWND hwnd; WNDCLASS wndClass; if (hPrevInstance == NULL) // valabil numai pentru Windows 3.1 { memset(&wndClass, 0, sizeof(wndClass));

// stiluri de fereastra wndClass.style = CS_HREDRAW | CS_VREDRAW; wndClass.lpfnWndProc = WndProc; // procedura fereastra wndClass.hInstance = hInstance; // instanta aplicatiei wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); // resursa cursor wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

Page 22: Programare VISUAL C++

// resursa penson wndClass.lpszClassName = "HELLO"; // nume fereastra

// inregistrare fereastra if (!RegisterClass(&wndClass)) return FALSE; } // terminat if

hwnd = CreateWindow("HELLO", "HELLO", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg);

return msg.wParam;}

Explicaţii: se crează fereastra prin completarea structurii WNDCLASS; se înregistrează fereastra RegisterClass(&wndClass); se crează fereastra CreateWindow; se stabileste modul de afişare al ferestrei ShowWindow(hwnd, nCmdShow); se afişează fereastra propriu zisă UpdateWindow(hwnd); urmează bucla de mesaje.

Codul ce trebuie urmărit este cel din WndProc, procedura fereastră.

Ce mesaje sunt tratate? Care sunt răspunsurile aplicaţiei la aceste mesaje?

WndProc tratează două mesaje: WM_PAINT şi WM_DESTROY. Alte mesaje decât cele indicate sunt tratate de către DefWindowProc.

La mesajul WM_DESTROY se plasează în coada de mesaje, mesajul WM_QUIT care are ca efect terminarea buclei de mesaje, şi deci terminarea aplicaţiei.

La mesajul WM_PAINT se apelează funcţia DrawHello.

Dar când este trimis mesajul WM_PAINT şi de cine? Mesajul WM_PAINT este trimis prima dată de funcţia UpdateWindow, adică atunci când fereastra devine vizibilă prima dată. Încercaţi opţiunile Size şi Move din meniul sistem. Ce se întâmplă? Trebuie reţinut următorul lucru: dacă în coada de mesaje apar mai multe mesaje WM_PAINT, sistemul va trata numai ultimul mesaj. În fapt ultima redesenare a ferestrei rămâne vizibilă, restul afişărilor ar fi consumatoare de timp şi în plus ar crea şi un efect neplăcut datorat rdesenărilor succesive.

Să explicăm codul din DrawHello.

hDC = BeginPaint(hwnd, &paintStruct);

BeginPaint încearcă să completeze variabila paintStruct şi ca răspuns obţine un context de dispozitiv care va trebui folosit de funcţiile din GDI. Ieşirile grafice au nevoie de acest context de dispozitiv.

GetClientRect(hwnd, &clientRect);

Page 23: Programare VISUAL C++

Se obţin dimensiunile zonei client, completate în clientRect. Observaţi parametrii funcţiei: hwnd va indica pentru ce fereastră se doreşte acest lucru. DPtoLP(hDC, (LPPOINT)&clientRect, 2);

Coordonatele fizice sunt transformate în coordonate logice. Primul parametru, hDC, indică pentru ce context de dispozitiv se face acest lucru.

DrawText(hDC, "Hello, World!", -1, &clientRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);

Se afişează în zona client, “Hello, World!”, folosind contextul de dispozitiv obţiunut de BeginPaint.

EndPaint(hwnd, &paintStruct);

Se termină desenarea, şi se distrug informaţiile din paintStruct.

La terminarea funcţiei, hDC se distruge, fiind local. În fapt după EndPaint hDC-ul nu mai este valid.

Procedura fereastră nu este nimic altceva decât o structura mare switch.

Mai multe bucle de mesaje şi proceduri fereastră

Aplicaţiile pot avea câte bucle de mesaje doresc. De exemplu o aplicaţie care are propria buclă de mesaje şi face apel la MessageBox va avea cel puţin două bucle de mesaje.Pentru exemplificare vom considera cazul desenării libere realizat cu o captură a mouse-lui. Aplicaţia trebuie să fie în stare să trateze mesajele WM_LBUTTONDOWN, WM_LBUTTONUP şi WM_MOUSEMOVE pentru a realiza această desenare. Vom avea tot timpul în minte faptul că un eveniment de mouse, în zona client, va genera un mesaj care va fi însoţit de coordonatele punctului unde acesta a avut loc.

Logica aplicaţiei este următoarea: bucla de mesaje va trata mesajul WM_LBUTTONDOWN. În cadrul funcţiei ce tratează acest mesaj se va realiza capturarea mouse-lui, astfel aplicaţia este informată de orice mişcare a mouse-lui prin tratarea mesajelor WM_MOUSEMOVE şi WM_LBUTTONUP. Ieşirea din cea de-a doua buclă de mesaje se face la tratarea mesajului WM_LBUTTONUP, caz în care şi capturarea mouse-lui încetează. De reţinut că în cadrul acestei a doua bucle de mesaje controlăm mereu dacă mouse-ul este capturat pentru zona client. Acest lucru înseamnă că dacă facem clic stânga în afara zonei client şi ţinem butonul stâng al mouse-lui apăsat şi ne mişcam prin zona client nu se va desena nimic. Mouse-ul nu a fost capturat de această fereastră.

Funcţii noi în acest cod.GetMessagePos() = obţine coordonatele punctului unde se află mouse-ul, coordonate relative la ecran. Coordonatele sunt obţinute într-un DWORD, care conţine în primii doi octeti valoarea lui x, iar în ultimii doi octeţi valoarea lui y. (Numărătoarea octeţilor se face de la stânga la dreapta.) Macro-ul MAKEPOINTS transformă valoarea unui DWORD într-o structură de tip POINTS.Cum zona client (fereastra) este plasată în cadrul ecranului, va trebui să translatăm aceste coordonate în zona client.ScreenToClient() = transformă coordonate ecran în zona client.DPtoLP() = transformă coordonatele fizice de dispozitiv în coordonate logice, necesare pentru a desena în zona client.LineTo() = desenează un segment de la origine (sau punctul stabilit cu MoveTo, MoveToEx) până la punctul curent.GetCapture() = testează dacă mouse-ul a fost capturat de fereastra aplicaţiei. SetCapture(HWND ) = realizează capturarea mouse-ului pentru fereastra cu handler-ul specificat

Page 24: Programare VISUAL C++

ReleaseCapture() = eliberează capturarea mouse-ului.GetDC() = obţine un context de dispozitiv pentru a desena în fereastră (zona client).ReleaseDC() = eliberează contextul de dispozitiv obţinut cu GetDC.

#include <windows.h>void AddSegmentAtMessagePos(HDC hDC, HWND hwnd, BOOL bDraw){ DWORD dwPos; POINTS points; POINT point; dwPos = GetMessagePos(); points = MAKEPOINTS(dwPos); point.x = points.x; point.y = points.y; ScreenToClient(hwnd, &point); DPtoLP(hDC, &point, 1); if (bDraw) LineTo(hDC, point.x, point.y); else MoveToEx(hDC, point.x, point.y, NULL);}

void DrawHello(HWND hwnd){ HDC hDC; MSG msg; if (GetCapture() != NULL) return; hDC = GetDC(hwnd); if (hDC != NULL) { SetCapture(hwnd); AddSegmentAtMessagePos(hDC, hwnd, FALSE); while(GetMessage(&msg, NULL, 0, 0)) { if (GetCapture() != hwnd) break; switch (msg.message) { case WM_MOUSEMOVE: AddSegmentAtMessagePos(hDC, hwnd, TRUE); break; case WM_LBUTTONUP: goto ExitLoop; default: DispatchMessage(&msg); } }ExitLoop: ReleaseCapture(); ReleaseDC(hwnd, hDC); }}

LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){ switch(uMsg) { case WM_LBUTTONDOWN: DrawHello(hwnd);

Page 25: Programare VISUAL C++

break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } return 0;}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR d3, int nCmdShow){ MSG msg; HWND hwnd; WNDCLASS wndClass; if (hPrevInstance == NULL) { memset(&wndClass, 0, sizeof(wndClass)); wndClass.style = CS_HREDRAW | CS_VREDRAW; wndClass.lpfnWndProc = WndProc; wndClass.hInstance = hInstance; wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wndClass.lpszClassName = "HELLO"; if (!RegisterClass(&wndClass)) return FALSE; } hwnd = CreateWindow("HELLO", "HELLO", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg); return msg.wParam;}

Observaţie. Mesajul WM_MOUSEMOVE poate fi tratat şi în bucla de mesaje din WinMain, dar pentru a realiza aceeaşi funcţionalitate codul şi logica aplicaţiei trebuiesc schimbate.

Concluzii

Fiecare aplicaţie Windows este construită în jurul unei bucle de mesaje. O buclă de mesaje face apeluri repetate la funcţiile GetMessage sau PeekMessage şi regăseşte mesajele pe care le dispecerează procedurilor fereastră prin funcţia DispatchMessage.

Procedurile fereastră sunt definite pentru clasele fereastră în momemntul când clasa fereastră a fost înregistrată prin RegisterClass.

Mesajele adresate aplicaţiei sunt tratate de procedura fereastră sau sunt trimise procedurii implicite DefWindowProc sau DefDlgProc în situaţia când nu sunt tratate de procedura fereastră.

Orice mesaj windows trebuie tratat, nu trebuie pierdut. Mesajele pot fi plasate sau trimise unei aplicaţii. Mesajele plasate sunt depozitate în coada de unde sunt regăsite

cu GetMessage sau PeekMessage. Faţă de un mesaj plasat, un mesaj trimis (SendMessage) implică

Page 26: Programare VISUAL C++

imediat un apel al procedurii fereastră. Cu alte cuvinte nu se termină execuţia funcţiei SendMessage până când mesajul nu a fost tratat.

O aplicaţie poate avea mai multe bucle de mesaje.

Page 27: Programare VISUAL C++

Citirea hărţii de mesaje

Hărţile de mesaje sunt părţi ale modelului MFC de programare Windows. În loc de a scrie funcţia WinMain() care trimite mesaje la procedura fereastră (funcţia) WindProc() şi apoi să controlăm ce mesaj a fost trimis pentru a activa funcţia corespunzătoare, vom scrie doar funcţia care tratează mesajul şi vom adăuga mesajul la harta de mesaje a clasei. Cadrul de lucru va face operaţiunile necesare pentru a ruta acest mesaj în mod corect.

Construirea hărţii de mesaje

Hărţile de mesaje se construiesc în două etape. Declaraţia hărtii de mesaje (macro DECLARE_MESSAGE_MAP()) se face în fişierul .h al clasei, iar implementarea se face in fişierul .cpp al clasei (BEGIN_MESSAGE_MAP() ... END_MESSAGE_MAP()).Exemplu//{{AFX_MSG(CShowStringApp) afx_msg void OnAppAbout(); // NOTE - the ClassWizard will add and remove member functions here. // DO NOT EDIT what you see in these blocks of generated code ! //}}AFX_MSG DECLARE_MESSAGE_MAP()

Se declară funcţia OnAppAbout() care este prefixată cu afx_msg ce constituie un comentariu pentru compilatorul de C++, dar care indică vizual că această funcţie tratează un mesaj. Această funcţie o vom găsi şi în cadrul macro-ului BEGIN_MESSAGE_MAP(), ca un parametru al macro-ului ON_COMMAND(). Primul parametru al acestui din urmă macro este ID-ul mesajului (comenzii în acest caz), iar al doilea numele funcţiei ce tratează acest mesaj.

Cod in .cpp

BEGIN_MESSAGE_MAP(CShowStringApp, CWinApp) //{{AFX_MSG_MAP(CShowStringApp) ON_COMMAND(ID_APP_ABOUT, OnAppAbout) // NOTE - the ClassWizard will add and remove mapping macros here. // DO NOT EDIT what you see in these blocks of generated code! //}}AFX_MSG_MAP // Standard file based document commands ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew) ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen) // Standard print setup command ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)END_MESSAGE_MAP()

Page 28: Programare VISUAL C++

Macro-ul DECLARE_MESSAGE_MAP() adaugă date mebru şi funcţii la clasa respectivă. Practic se declară o tabelă cu un număr de intrări variabil (sfârşitul tabelei este marcat (completat) de END_MESSAGE_MAP()) şi funcţii care operează cu acest elementele acestui tabel.

Macro-uri pentru harta de mesajeBEGIN_MESSAGE_MAP şi END_MESSAGE_MAP sunt macro-uri care ca şi macro-ul DECLARE_MESSAGE_MAP din fişierul .h, declară anumite variabile membru şi funcţii pe care cadrul de lucru le va utiliza pentru a naviga prin hărţile de mesaje ale tuturor obiectelor din sistem Printre macro-urile folosite în hărţile de mesaje, enumerăm:

• DECLARE_MESSAGE_MAP—folosit în fişierul .h pentru a declara că va exista o hartăesaje in .cpp

• BEGIN_MESSAGE_MAP—Marchează începutul hărţii de mesaje în fişierul sursă. • END_MESSAGE_MAP—Marchează sfârşitul hărţii de mesaje în fişierul sursă. • ON_COMMAND—Folosit pentru a face legătura între comenzi şi funcţiile care tratează

aceste comenzi. • ON_COMMAND_RANGE—Folosit pentru a face legătura între un grup de comenzi şi

funcţia care le tratează.• ON_CONTROL—Folosit pentru a face legătura între un mesaj de notificare al unui

control şi funcţia ce-l tratează.• ON_CONTROL_RANGE—Folosit pentru a face legătura între un grup de mesaje de

notificare al unui control şi funcţia corespunzătoare. • ON_MESSAGE—Folosit pentru a realiza legătura între un mesaj definit de utilizator şi

funcţia care-l tratează. • ON_REGISTERED_MESSAGE—Folosit pentru a realiza legătura între un mesaj defint

de utilizator, dar înregistrat şi funcţia care-l tratează.• ON_UPDATE_COMMAND_UI—Folosit pentru a indica funcţia care va face

actualizarea pentru o comandă specifică.• ON_COMMAND_UPDATE_UI_RANGE—Ca mai sus, dar pentru un grup de comenzi. • ON_NOTIFY—Folosit pentru a indica funcţia ce va adăuga informaţii suplimentare,

pentru un mesaj de notificare al unui control.• ON_NOTIFY_RANGE—Ca mai sus, dar pentru un grup de mesaje de notificare al unui

control. ON_NOTIFY_EX—Ca la ON_NOTIFY, dar funcţia va întoarce TRUE sau FALSE pentru a indica dacă notificarea poate fi trecută altui obiect pentru tratări suplimentare.

• ON_NOTIFY_EX_RANGE—Ca mai sus, dar se referă la un grup de comenzi de notificare.

În plus la ceste macro-uri, există peste 100 de macro-uri, unul pentru fiecare din cele mai comune mesaje. De exemplu macro-ul ON_CREATE pentru mesajul WM_CREATE, etc. În mod obişnuit aceste macro-uri sunt adăugate la clasă de către Class Wizard.

Page 29: Programare VISUAL C++

Cum lucrează harta de mesajeFiecare aplicaţie are un obiect moştenit din clasa CWinApp şi o funcţie membru Run(). Această funcţie apelează funcţia CWinThread::Run(), care apelează GetMessage(), TranslateMessage() şi DispatchMessage().

Funcţia fereastră (în SDK) ştie handler-ul ferestrei pentru care este trimis mesajul. Fiecare obiect fereastră foloseşte acelaşi stil al clasei Windows şi aceeaşi funcţie WindProc, numită AfxWndProc(). MFC menţine ceva asemănător, numit handle map, o tabelă cu handler-ii ferestrelor şi pointeri la obiecte, şi framework-ul foloseşte aceasta pentru a trimite un pointer la obiectul C++, un CWnd*. În continuare el apelează WindowProc(), o funcţie virtuală a acestui obiect. Datorită polimorfismului, indiferent că este vorba de un Button sau o vizualizare se va apela funcţia corectă.WindowProc() apelează OnCmdMsg(), funcţia C++ care efectiv manipulează mesajul. Mai întâi caută dacă acesta este un mesaj, o comandă sau o notificare. Presupunând că este un mesaj. caută în harta de mesage a clasei, folosind funcţiile şi variabilele membru adăugate la clasă de DECLARE_MESSAGE_MAP, BEGIN_MESSAGE_MAP şi END_MESSAGE_MAP. Modul de organizare al acestei tabele permite căutarea mesajului, dacă este nevoie, în toată arborescenţa clasei.

AfxWndProc()->WindowProc()->OnCmdMsg()Se va explica cum se adaugă un mesaj la o clasă şi funcţia corespunzătoare acestuia, care clasă tratează mesajul, cum se scrie cod în funcţie, etc.

Recunoaşterea mesajelor

Există aproximativ 900 mesaje Windows. Prefixele mesajului Windows şi Tipuri fereastrăPrefix Window TypeABM, ABN Appbar ACM, ACN Animation control BM, BN Button CB, CBN Combo box CDM, CDN Common dialog box CPL Control Panel application DBT Any application (device change message) DL Drag list box DM Dialog box EM, EN Edit box FM, FMEVENT File Manager HDM, HDN Header control HKM HotKey control IMC, IMN IME window LB, LBN List box

Page 30: Programare VISUAL C++

LVM, LVN List view NM Any parent window (notification message) PBM Progress bar PBT Any application (battery power broadcast) PSM, PSN Property sheet SB Status bar SBM Scrollbar STM, STN Static control TB, TBN Toolbar TBM Track bar TCM, TCN Tab control TTM, TTN ToolTip TVM, TVN Tree view UDM Up Down control WM Generic window

Care e diferenţa între mesajele care se termină în M şi cele care se termină în N? Primul este un mesaj la control (am apăsat butonul, de exemplu), al doilea este un mesaj de notificare de la control la fereastra proprietară a controlului, care are semnificaţia de “am fost apăsat”, “s-a întâmplat ceva în control...”.Există şi mesaje care nu se termină în M (CB_) dar acţionează la fel.

Înţelegerea comenzilorO comandă este un tip special de mesaj. Windows generează comenzi când utilizatorul

alege un articol de meniu, apasă un buton, sau altfel spune sistemului să facă ceva. Pentru un articol de meniu se primeşte mesajul WM_COMMAND iar pentru notificarea unui control WM_NOTIFY, cum ar fi selectarea dintr-un list box.

Comenzile şi notificările sunt trecute prin SO ca orice alt mesaj, până când acestea ajung la OnWndMsg(). În acest punct pasarea mesajului windows încetează şi se startează rutarea comenzilor în MFC.Mesajele de comandă au ca prim parametru, ID-ul articolului din meniu care a fost selectat sau a butonului care a fost apăsat.Rutarea comenzilor este mecanismul pe care OnWndMsg() îl foloseşte pentru a trimite comanda (sau notificarea) la obiectele care pot trata acest mesaj. Numai obiectele care sunt moştenite din CWnd pot primi mesaje, dar toate obiectele care sunt moştenite din CCmdTarget, incluzând CWnd şi CDocument, pot primi comenzi sau notificări. Aceasta însemană că o clasă moştenită din CDocument poate avea o hartă de mesaje. Pot să nu existe mesaje în această hartă ci numai pentru comenzi şi notificări, dar tot hartă de mesaje se numeşte.

Comenzile şi notificările ajung la clasă prin mecanismul de rutare al comenzilor. OnWndMsg() apelează CWnd::OnCommand() sau CWnd::OnNotify().OnCommand() apelează OnCmdMsg(). OnNotify() apelează de asemenea OnCmdMsg(). Binenţeles că ambele funcţii efectuează anumite controale înainte de a face aceste apeluri.

Page 31: Programare VISUAL C++

OnCmdMsg() este virtuală, ceea ce înseamnă că diferite comenzi au implementări diferite. Implementarea pentru fereastra cadru trimite comanda vizualizărilor şi documentelor pe care le conţine.

Comanda pentru actualizări

Folosit în special pentru actualizarea articolelor din meniu. De exemplu când se selectează text in vizualizare şi opţiunile de Copy, Cut, Paste, Undo sunt implementate aceste articole de menu vor fi afişate în starea enable sau disable (funcţie de logica programului).Există două posibilităţi de a face acest lucru: continuous update approach şi update-on-demand approach. Continuous update approach presupune existenţa unei tabele mari ce conţine câte o intrare pentru fiecare meniu şi un flag care va indica dacă opţiunea este disponibilă sau nu.Cea de-a doua posibilitate presupune controlarea tuturor condiţiilor asupra unui articol de meniu înainte ca meniul să fie afişat. În acest caz obiectul care are meniul va şti mai multe despre acesta, în schimb nu toată aplicaţia va avea acces la aceste informaţii.Tehinca MFC-ului este de a utiliza un obiect numit CCmdUI, o comandă a interfeţei utilizatorului, şi de a da acest obiect când se trimite mesajul CN_UPDATE_COMMAND_UI. În harta de mesaje va apărea macro-ul ON_UPDATE_COMMAND_UI.

Ce se întâmplă in realitate?

SO trimite mesajul WM_INITMENUPOPUP; clasa CFrameWnd va construi un obiect CCmdUI, setează variabilele membru ce corespund primului articol din meniu şi apelează funcţia membru DoUpdate(). DoUpdate() trimite mesajul CN_COMMAND_UPDATE_UI la ea însăşi, cu un pointer la obiectul CCmdUI. Se vor seta variabilele membru ale obiectului CCmdUI cu articolul doi din meniu şi procesul continuă până când este parcurs tot meniul. Obiectul CCmdUI este folosit pentru a valida (enable) sau invalida (disable) [gray(disable) sau ungray(enable) ] articolele din meniu sau butoane.

CCmdUI are următoarele funcţii membru:• Enable() — Are un parametru care poate lua valorile TRUE sau FALSE (implicit TRUE). • SetCheck() — Marchează sau demarchează articolul.

• SetRadio() – Setează sau nu unul din butoanele radio al unui grup.• SetText()—Setează textul unui meniu sau buton.• DoUpdate()—Generează mesajul.

Exemplu:

BEGIN_MESSAGE_MAP(CWhoisView, CFormView) ... ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE, OnUpdateEditPaste) ...END_MESSAGE_MAP()

Page 32: Programare VISUAL C++

void CWhoisView::OnUpdateEditPaste(CCmdUI* pCmdUI){ pCmdUI->Enable(::IsClipboardFormatAvailable(CF_TEXT));}

Page 33: Programare VISUAL C++

Moduri de mapare (mapping mode)

SetMapMOde(hdc, iMapMode);

iMapMode = GetMapMode(hdc);

vezi si GetDeviceCaps(...)

Mod de mapare Unitati logice Axa x Axa yMM_TEXT Pixel spre dreapta in josMM_LOMETRIC 0,1 mm spre dreapta in susMM_HIMETRIC 0,01 mm spre dreapta in susMM_LOENGLISH 0.01 inci spre dreapta in susMM_HIENGLISH 0.001 inci spre dreapta in susMM_TWIPS 1/1440 inci spre dreapta in susMM_ISOTROPIC arbitrar (x = y) Selectabil selectabilMM_ANISOTROPIC

Arbitrar (x!=y) Selectabil Selectabil

twips = twentieth of a point = a douazecea parte dintr-un punct1 punt = 1/72 dintr-un inci

ClientToScreen si ScreenToClient

The ClientToScreen function converts the client coordinates of a specified point to screen coordinates.

BOOL ClientToScreen( HWND hWnd, // window handle for source coordinates LPPOINT lpPoint // pointer to structure containing screen coordinates );

BOOL ScreenToClient( HWND hWnd, // window handle for source coordinates LPPOINT lpPoint // pointer to structure containing screen coordinates );

ParametershWnd Identifies the window whose client area is used for the conversion. lpPoint Points to a POINT structure that contains the client coordinates to be converted. The new screen coordinates are copied into this structure if the function succeeds.

Return Values If the function succeeds, the return value is nonzero. If the function fails, the return value is zero.

Remarks

Page 34: Programare VISUAL C++

The ClientToScreen function replaces the client coordinates in the POINT structure with the screen coordinates. The screen coordinates are relative to the upper-left corner of the screen.

See Also MapWindowPoints, POINT, ScreenToClient

MapWindowPoints

The MapWindowPoints function converts (maps) a set of points from a coordinate space relative to one window to a coordinate space relative to another window.

int MapWindowPoints( HWND hWndFrom, // handle of window to be mapped from HWND hWndTo, // handle of window to be mapped to LPPOINT lpPoints, // address of structure array with points to map UINT cPoints // number of structures in array

);

ParametershWndFrom Identifies the window from which points are converted. If this parameter is NULL or HWND_DESKTOP, the points are presumed to be in screen coordinates. hWndTo Identifies the window to which points are converted. If this parameter is NULL or HWND_DESKTOP, the points are converted to screen coordinates. lpPoints Points to an array of POINT structures that contain the set of points to be converted. This parameter can also point to a RECT structure, in which case the cPoints parameter should be set to 2. cPoints Specifies the number of POINT structures in the array pointed to by the lpPoints parameter.

Return ValuesIf the function succeeds, the low-order word of the return value is the number of pixels added to the horizontal coordinate of each source point in order to compute the horizontal coordinate of each destination point; the high-order word is the number of pixels added to the vertical coordinate of each source point in order to compute the vertical coordinate of each destination point.

See Also ClientToScreen, POINT, RECT, ScreenToClient

Sistemele de coordonate ale dispozitivului

Windows mapeaza coordonate logice specificate in fct GDI la coordonate fizice ale dispozitivului.Modul de mapare defineste maparea coordonatelor de fereastra (window) - coordonate logice - la coordonatele vizorului (viewport) - coordonate de dispozitiv.In toate coordonatele de dispozitiv sint folositi pixelii ca unitate de masura, valorile pe axa x cresc spre dreapta iar pe axa y de sus in jos.1. Coordonate ecran = cind lucram cu tot ecranul. Sint folosite in mesajul

WM_MOVE si in urm. fct. Windows: CreateWindow si MoveWindow (ambele pentru alte ferestre decit fereastra descendent), GetMessagePos,

Page 35: Programare VISUAL C++

GetCursorPos, SetCursorPos, GetWindowRect, WindowFromPoint si SetBrushOrgEx.

2. Coordonate fereastra (GetWindowDC) = se refera la intraga fereastra a ecranului, inclusiv bara de titlu, meniu, barele de derulare si chenarul ferestrei

3. Coordonate zona client (GetDC, ReleaseDC, BeginPaint, EndPaint)

Vizorul si fereastraPt. vizor se folosesc coordonatele de dispozitiv (pixeli).Pt. toate modurile de mapare, W transforma coordonatele ferestrei (coordonate logice) in coordonate ale vizorului (coordonate de dispozitiv) folosind doua formule:

xViewport = (xWindow - xWinOrg) * (xViewExt / xWinExt) + xViewOrgyViewport = (yWindow - yWinOrg) * (yViewExt / yWinExt) + yViewOrg

unde (xWindow, yWindow) este pct in coordonate logice care trebuie translatat, iar (xViewport, yViewport) este pct. in coordonate de dispozitiv. (xWinOrg, yWinOrg) = originea ferestrei in coordonate logice; (xViewOrg, yViewOrg) = originea vizorului in coordonate dispozitiv. Formulele de mai sus implica faptul ca punctul (xWinOrg, yWinOrg) este intotdeauna mapat la punctul (xViewOrg, yViewOrg).(xWinExt, yWinExt) = extensia ferestrei in coordonate logice;(xViewExt, yViewExt) = extensia vizorului in coordonate de dispozitiv;In majoritatea modurilor de mapare aceste extensii sint prestabilite si nu pot fi modificate. Raportul intre extensia vizorului si extensia ferestrei reprezinta un factor de scalare folosit pentru convertirea unitatilor logice in unitati de dispozitiv. Extensiile pot avea valori negative: aceasta inseamna ca nu este obligatoriu ca valorile pe axa x sa creasca spre dreapta si valorile pe axa y sa creasca in jos.

Convertire pct de dispozitiv in pct logice si invers:

DPtoLP(hdc, pPoints, iNumber);LPtoDP(hdc, pPoints, iNumber);

pPoints = matrice de structuri POINTiNumber = r. de puncte care urmeaza sa fie convertite.

Modul MM_TEXTFct SetViewportOrgEx si SetWindowOrgEx modifica originea vizorului si a ferestrei. Aceste functii au ca efect deplasarea axelor astfel incit punctul de coordonate (0,0) nu se mai refera la coltul din stg sus al ecranului. In general se foloseste doar una din cele doua functii.Explicati asupra lucrului cu aceste functii:daca schimbam originea vizorului la (xViewOrg, yViewOrg) atunci pct. logic de coordonate (0,0) va fi mapat la punctul de coordonate de dispozitiv (xViewOrg, yViewOrg).daca schimbam originea ferestrei la (xWinOrg, yWinOrg) atunci acest punct logic va fi mapat la punctul de coordonate de dispozitiv (0,0) care este intotdeauna coltul din stinga sus al zonei client.

Page 36: Programare VISUAL C++

Ex. Sa pp ca zona client are latimea cxClient si inaltimea cyClient. Daca dorim ca punctul de coordonate logice (0,0) sa se afle in centrul zonei client, at.:

SetViewportOrgEx(hdc, cxClient / 2, cyClient / 2, NULL);

Valorile logice ale axei x sint cuprinse in intervalul [-cxClient/2, cxClient/2], iar cele ale axei y in intervalul [-cyClient/2, cyClient/2]. Afisarea de text incepind cu coltul din stg sus, care are coordonatele de dispozitiv (0,0) inseamna folosirea urmatoarelor coordonate logice:

TextOut ( hdc, -cxClient/2, -cyClient /2, “...”,...);

Acelasi rezultat poate fi obtinut si cu fct SetWindowOrgEx in locul fct SetViewportOrgEx:

SetWindowOrgEx (hdc, -cxClient / 2, -cyClient / 2, NULL);

Mouse si tastatura

Parametrii lParam si wParam de la mesajele de mouse

x = LOWORD(lParam)y = HIWORD(lParam)

wParam:MK_LBUTTON Buton stg mouse apasatMK_RBUTTON Buton dr mouse apasatMK_MBUTTONMK_SHIFT Tasta shift apasataMK_CONTROL Tasta Ctrl apsata

Tratare mesaje de la mouse case WM_MOUSEMOVE: if (wParam & MK_LBUTTON )...

if (MK_SHIFT & wParam)if (MK_CONTROL & wParam)

Shift si Ctrl apasateelse

Shift apasataelse if (MK_CONTROL & wParam)

Ctrl apasataelse

Shift si Ctrl nu sint apasate

Functia GetKeyState returneaza starea butoanelor si a tastelor de modificare daca se folosesc codurile virtuale VK_LBUTTON, VK_RBUTTON, VK_SHIFT si VK_CONTROL.

Page 37: Programare VISUAL C++

Tipărire şi Previzualizare

Bazele tipăririi şi previzualizării în MFCCerinţe pentru acest curs:

⇒ Contextul de dispozitiv;⇒ Arhitectura Document/View, aplicaţii tip SDI;⇒ Tratarea mesajelor simple de mouse;⇒ defilarea orizontală şi/sau verticală (opţional).

Obiective: determinarea obiectelor folosite de către MFC pentru tipărire şi previzualizare; determinarea ordinii de apel al funcţiilor implicate în tipărire şi previzualizare; determinarea elementelor ce identifică pagina de tipărit, etc.

Lecţia se va desfăşura în laborator. Drept exerciţiu independent: să se adauge codul necesar pentru a realiza defilarea orizontală şi/sau verticală.

Pentru învăţarea manevrării contextului de dispozitiv se poate propune construirea unei aplicaţii care să deseneze graficul unei funcţii. Se vor avea în vedere toate situaţiile posibile unde poate fi desenat graficul funcţiei astfel încât să fie ocupată cât mai bine zona client (pentru un grafic ce apare numai în cadranul IV se va desena numai acest cadran şi graficul, etc.).

Vom exemplifica tipărirea şi vizualizarea pe baza unui exemplu.

Se crează o aplicaţie cu arhitectura Document/View şi tip SDI (restul setărilor rămânând cele implicite) şi cu suport pentru print/preview.

Numele proiectului este Print1.

Prima modificare.

În funcţia CPrint1View::OnDraw() adăugăm:

pDC->Rectangle(20, 20, 220, 220);

Se va desena un dreptunghi (cu mărimile măsurate în pixeli). (20,20) reprezintă colţul din stânga sus al dreptunghiului, iar (220,220) reprezintă colţul din dreapta jos al dreptunghiului. Deci mărimea laturilor dreptunghiului este 200 pe 200 pixeli.

Colţul din stânga sus al zonei client are coordonatele (0,0), axa Ox este pe orizontală, axa Oy este pe verticală.

Această aplicaţie poate produce vizualizarea şi tipărirea documentului.

ScalareaDocumentul listat şi cel afişat pe ecran nu are aceeaşi dimensiune (nu arată la fel) pentru că imprimanta foloseşte unitatea de măsură, dots, iar pe ecran se foloseşte pixelul, şi acestea au mărimi diferite (200 dots # 200 pixeli).Acest lucru este descris de modul de mapare (implicit MM_TEXT).Dacă dorim să scalăm imaginea tipărită la o anumită dimensiune, trebuie să alegem diferite moduri de mapare.

Page 38: Programare VISUAL C++

Moduri de mapareMode Unit X YMM_HIENGLISH 0.001 inch Increases right Increases upMM_HIMETRIC 0.01 millimeter Increases right Increases upMM_ISOTROPIC User-defined User-defined User-definedMM_LOENGLISH 0.01 inch Increases right Increases upMM_LOMETRIC 0.1 millimeter Increases right Increases upMM_TEXT Device pixel Increases right Increases downMM_TWIPS 1/1440 inch Increases right Increases up

Lucrul cu grafice în modul MM_TEXT devine o problemă când imprimantele şi ecranele au un număr diferit de dots/pixeli pe pagină.Un mod de mapare mai bun pentru lucrul cu grafice este MM_LOENGLISH, care foloseşte ca unitate de măsură a suta parte dintr-un inch. Pentru a folosi acest mod de mapare, folosim funcţia SetMapMode():

pDC->SetMapMode(MM_LOENGLISH);pDC->Rectangle(20, -20, 220, -220);

Atentie la originea axelor şi la sensul acestora (creşterea luix şi a lui y) Când vom tipări documentul de mai sus vom obţine un dreptunghi cu laturile exact de 2 inch.

Tipărirea mai multor paginiMFC tratează tipărirea documentului (mai puţin bitmap-uri). Funcţia OnDraw() din clasa pentru vizualizare

realizează desenarea pe ecran cât şi tipărirea la imprimantă. Lucrurile se complică când documentul are mai multe pagini sau alte tratări speciale (informaţii de început şi de sfârşit de pagină).

Exemplificare:Vom modifica aplicaţia astfel încât să desenăm mai multe dreptunghiuri care să nu încapă pe o pagină.Adăugăm o variabilă membru (int m_numrects) la clasa document care va memora numărul de dreptunghiuri care se vor desena şi pe care o iniţializăm în constructor. Pe mesajul WM_LBUTTONDOWN vom incrementa această variabilă, iar pe mesajul WM_RBUTTONDOWN vom decrementa această variabilă. Codul pentru cele două funcţii este dat mai jos:

print1View.cpp --CPrint1View::OnLButtonDown()void CPrint1View::OnLButtonDown(UINT nFlags, CPoint point){ CPrint1Doc* pDoc = GetDocument(); ASSERT_VALID(pDoc); pDoc->m_numRects++; Invalidate(); CView::OnLButtonDown(nFlags, point);}print1View.cpp --CPrint1View::OnRButtonDown()void CPrint1View::OnRButtonDown(UINT nFlags, CPoint point){ CPrint1Doc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (pDoc->m_numRects > 0) { pDoc->m_numRects--; Invalidate(); } CView::OnRButtonDown(nFlags, point);}

Page 39: Programare VISUAL C++

Rescriem funcţia OnDraw() astfel:

print1View.cpp --CPrint1View::OnDraw()void CPrint1View::OnDraw(CDC* pDC){ CPrint1Doc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: add draw code for native data here pDC->SetMapMode(MM_LOENGLISH); char s[10]; wsprintf(s, "%d", pDoc->m_numRects); pDC->TextOut(300, -100, s); for (int x=0; x<pDoc->m_numRects; ++x) { pDC->Rectangle(20, -(20+x*200), 200, -(200+x*200)); }}

Vedem ce se întâmplă în PrintPreview. Codul din PrintPreview (în acest moment) nu ştie cum să afişeze mai multe pagini.

Determinarea numărului de pagini se face în funcţia OnBeginPrinting().

print1View.cpp --CPrint1View::OnBeginPrinting()void CPrint1View::OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo){ CPrint1Doc* pDoc = GetDocument(); ASSERT_VALID(pDoc); int pageHeight = pDC->GetDeviceCaps(VERTRES); int logPixelsY = pDC->GetDeviceCaps(LOGPIXELSY); int rectHeight = (int)(2.2 * logPixelsY); int numPages = pDoc->m_numRects * rectHeight / pageHeight + 1; pInfo->SetMaxPage(numPages);}

OnBeginPrinting() are doi parametri: un pointer la un context de dispoztiv al imprmantei şi un pointer la un obiect CPrintInfo. Pentru că versiunea implicită nu se referă la aceşti doi pointeri, numele parametrilor sunt comentaţi pentru a preveni mesajele de avertizare la compilare.

void CPrint1View::OnBeginPrinting(CDC* /*pDC*/ , CPrintInfo* /*pInfo*/)

Pentru a seta numărul paginii, trebuie să accesăm ambele obiecte CDC* şi CPrintInfo, deci va trebui să scoatem comentariile de la ceşti doi parametri.

Trebuie să avem următoarele informaţii:1. înălţimea paginii;2. numărul de dots pe inch.

Înălţimea paginii o obţinem printr-un apel al funcţiei GetDviceCaps(), care furnizează informaţii despre posibilităţile contextului de dispozitiv. Avem nevoie de rezoluţia verticală (numărul de dots tipăribili de la începutul paginii până la sfârşitul ei), deci vom furniza ca parametru constanta VERTRES, în funcţia GetDeviceCaps(). Constanta HORZRES în aceeaşi funcţie ne furnizează rezoluţia orizontală.GetDeviceCaps() acceptă un număr de 29 de constante diferite (a se vedea help-ul).

În exemplul nostru, pentru a şti câte dreptunghiuri încap pe o pagină, trebuie să ştim înălţimea dreptunghiului în dots, deci va trebui să împărţim dots pe pagină la dots pe dreptunghi.Ştim că fiecare dreptunghi este înalt de 2 inch cu un spaţiu de 20/100 între fiecare dreptunghi. Distanţa totală de la începutul desenării unui dreptunghi până la următorul este de 2.2 inch.Apelul GetDeviceCaps(LOGPIXELSY) ne dă numărul de dots pe inch pentru imprimantă (care este ataşată la sistem)., care înmulţită cu 2.2 ne dă dots pe dreptunghi.

Page 40: Programare VISUAL C++

Rulând aplicaţia vom observa că deşi în previzualizare avem două pagini, la listare vom obţine pagina 1 de două ori.Trecerea de la o pagină la alta este următorul pas.

Setarea originiiVa trebui să furnizăm informaţia privitoare la începutul unei noi pagini. Pentru acest lucru, vom rescrie funcţia OnPrepareDC().Adăugăm aceaceastă funcţie la clasa de vizualizare cu ClassWizard. Codul este:

print1View.cpp --CPrint1View::OnPrepareDC()void CPrint1View::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo){ if (pDC->IsPrinting()) { int pageHeight = pDC->GetDeviceCaps(VERTRES); int originY = pageHeight * (pInfo->m_nCurPage - 1); pDC->SetViewportOrg(0, -originY); } CView::OnPrepareDC(pDC, pInfo);}

Cadrul de lucru MFC, apelează această funcţie înainte de afişa datele pe ecran sau a le scrie la imprimantă. Acelaşi cod realizează previzualizarea cât şi tipărirea datelor. Dacă aplicaţia previzualizează datele nu este nevoie să schimbăm procesarea efectuată de OnPrepareDC(). Cod necesar este numai pentru tipărire, de aceea folosim funcţia IsPrinting() pentru a determina dacă acţiunea este de tipărire sau nu.Dacă tipărim documentul, trebuie să determinăm care pagină trebuie tipărită, adică să stabilim pagina curentă. Pentru aceasta avem nevoie de înălţimea în dots a paginii de tipărit, deci un nou apel al funcţiei GetDeviceCaps().În continuare trebuie să detrerminăm originea noului viewport (poziţia coordonatelor (0,0)) pentru afişare. Schimbând originea, dăm informaţia necesară cadrului de lucru MFC, pentru a şti de unde să înceapă tipărirea datelor.Pentru prima pagină originea este (0,0); pentru pagina a doua, originea este mutată în jos cu numărul de dots pe pagină. În general, componenta verticală este mărimea paginii înmulţită cu pagina curentă minus 1. Numărul paginii este menţinut într-o variabilă membru a clasei CPrintInfo. După ce calculăm noua origine, vom seta aceasta în cadrul contextului de dispozitiv apelând funcţia SetViewportOrg().Se va rula aplicaţia cu aceste ultime modificări efectuate.

MFC şi TipărireaAlte funcţii importante în procesul de tipărire sunt:

Printing Functions of a View ClassFunction Description

OnBeginPrinting() Override this function to create resources, such as fonts, that you need for printing the document. You also set the maximum page count here.

OnDraw() This function serves triple duty, displaying data in a frame window, a print preview window, or on the printer, depending on the device context sent as the function's parameter.

OnEndPrinting() Override this function to release resources created in OnBeginPrinting().

OnPrepareDC() Override this function to modify the device context used to display or print the document. You can, for example, handle pagination here.

OnPreparePrinting() Override this function to provide a maximum page count for the document. If you don't set the page count here, you should set it in OnBeginPrinting().

OnPrint() Override this function to provide additional printing services, such as printing headers and footers, not provided in OnDraw().

Pentru a tipări documentul se apelează mai întâi OnPreparePrinting() care apelează DoPreparePrinting() care la rândul ei este responsabilă pentru afişarea boxei de dialog Print şi crează contextul de dispozitiv pentru imprimanta selectată.

print1View.cpp --CPrint1View::OnPreparePrinting() as Generated by AppWizardBOOL CPrint1View::OnPreparePrinting(CPrintInfo* pInfo)

Page 41: Programare VISUAL C++

{ // default preparation return DoPreparePrinting(pInfo);}

Folosind pointerul la obiectul CPrintInfo, putem face aici diverse iniţializări. Trebuie cercetată clasa CPrintInfo.

Members of the CPrintInfo ClassMember DescriptionSetMaxPage() Sets the document's maximum page number.SetMinPage() Sets the document's minimum page number.GetFromPage() Gets the number of the first page that users selected for printing.

GetMaxPage() Gets the document's maximum page number, which may be changed in OnBeginPrinting().

GetMinPage() Gets the document's minimum page number, which may be changed in OnBeginPrinting().

GetToPage() Gets the number of the last page users selected for printing.m_bContinuePrinting Controls the printing process. Setting the flag to FALSE ends the print job.m_bDirect Indicates whether the document is being directly printed.m_bPreview Indicates whether the document is in print preview.m_nCurPage Holds the current number of the page being printed.m_nNumPreviewPages Holds the number of pages (1 or 2) being displayed in print preview.m_pPD Holds a pointer to the print job's CPrintDialog object.m_rectDraw Holds a rectangle that defines the usable area for the current page.m_strPageDesc Holds a page-number format string.

Când funcţia DoPreparePrinting() afişează boxa de dialog Print, utilizatorul poate seta o parte din datele membru ale clasei CPrintInfo. În mod obişnuit, ultimul apel trebuie făcut pentru SetMaxPage() înainte ca DoPreparePrinting() să afişeze boxa de dialog Print. Dacă nu putem determina numărul de pagini până când nu avem un DC pentru imprimanta selectată, trebuie să aşteptăm până când obţinem acest context de dispozitiv. În mod normal contextul de dispozitiv al imprimantei se crează la selecţia acesteia în cadrul acestei boxe de dialog.După OnPreparePrinting(CDC*, CPrintInfo*), MFC apelează OnBeginPrinting(), care este un alt loc potrivit pentru a seta numărul maxim de pagini, dar şi locul pentru a crea resurse, cum ar fi fonturi, necesare pentru a completa job-ul de tipărire.În continuare, MFC apelează OnPrepareDC() pentru prima pagină a documentului. Aceasta constituie începutul buclei care se execută o dată pentru fiecare pagină a documentului. In OnPrepareDC() putem controla care parte din întreg documentul se tipăreşte, ca fiind pagina curentă. Aici vom seta originea viewportului pentru document.După OnPrepareDC(), MFC apelează OnPrint() pentru a tipări pagina curentă. În mod normal, OnPrint() apelează OnDraw() cu un parametru pointer la CDC, care automat redirectează ieşirea spre imprimantă. Putem rescrie OnPrint() pentru a controla modul cum documentul este tipărit. Putem tipări headers şi footers în OnPrint() şi apoi să apelăm versiunea de bază a lui OnDraw() pentru a tipări pagina curentă. Pentru a preveni vesriunea din clasa de bază ca să nu rescrie headers şi footers, restricţionăm zona tipăribilă prin setarea variabilei m_rectDraw din obiectul CPrintInfo la un dreptunghi care nu acopră headers şi footers.

Versiune posibilă OnPrint() cu Headers şi Footersvoid CPrint1View::OnPrint(CDC* pDC, CPrintInfo* pInfo){ // TODO: Add your specialized code here and/or call the base class // Call local functions to print a header and footer. PrintHeader(); PrintFooter(); CView::OnPrint(pDC, pInfo);}

Se poate renunţa la apelul lui OnDraw() în OnPrint(), creind cod suplimentar, ca în exemplul de mai jos.

Versiune posibilă OnPrint() fără OnDraw()void CPrint1View::OnPrint(CDC* pDC, CPrintInfo* pInfo)

Page 42: Programare VISUAL C++

{ // TODO: Add your specialized code here and/or call the base class // Call local functions to print a header and footer. PrintHeader(); PrintFooter(); // Call a local function to print the body of the document. PrintDocument();}

După ce MFC a terminat de tipărit ultima pagină, apelează funcţia OnEndPrinting(), unde putem distruge orice resursă de care nu mai avem nevoie.

Page 43: Programare VISUAL C++

MeniuriSe vor descrie mai intai elementele aplicatiei (bara de titlu, meniul, etc.).W furnizeaza suport aplicatiilor care utilizeaza meniuri: afisarea barei de meniu, derularea unui meniu popup cind acesta este selectat, notifica aplicatia cind o comanda de meniu a fost selectata.

Definire termeni:

Bara de meniu care apare in partea cea mai de sus a ferestrei se numeste top-level menu, iar comenzile se numesc top-level menu items. Meniul care apare cind un articol de meniu este selectat se numeste drop down menu, iar articolele din acest meniu se numesc articole meniu (menu items). Articolele de meniu sunt identificate prin valori intregi, numite ID-ul art. de meniu sau ID-ul comenzii. W suporta meniurile popup care sunt asemanatoare cu cele drop down cu deosebirea ca pot fi afisate oriunde pe ecran. Meniurile de context (right click) sunt meniuri popup.Meniul sistem cuprinde czi pt. redimensionare, mutare, minimizare, maximizare, inchidere fereastra.Act. legate de meniuri le gasim in clasa CMenu din MFC. CMenu contine o data membru publica HMENU m_hMenu ce pastreaza un handle la meniu, si mai multe functii ce constituie in fapt apeluri ale fct din API (CMenu::TrackPopupMenu, CMenu::EnableMenu, etc. CMenu contine doua fct virt DrawItem si MeasureItem care pot fi rescrise daca dorim sa cream articole de meniu stilizate ce contin bitmap-uri si alte elemente grafice.

Crearea unui meniu in MFC. Modalitati:1. in mod programabil, CreateMenu, InsertMenu, etc;2. initializind o serie de structuri de date ce descriu continutul unui meniu si apoi apelam

CMenu::LoadMenuIndirect;3. cream o resursa de meniu si incarcam meniul rezultat in aplicatie in timpul executiei.

Crearea unui meniuMetoda cea mai usoara este de a adauga un template de meniu la fis. de resurse al aplicatiei. Un fisier de resurse este un fis. text care are ext rc, si care instructiuni ce definesc meniul. Acest fis. de res este compilat si legat la aplicatie cu un utilitar numit rc.exe (in MS VC++). O resursa este un obiect binar ca de ex. un meniu, o icoana, stringuri, bitmap-uri. Fiecare resursa este identificata printr-un ID intreg sau string (“MyMenu” sau IDR_MYMENU). ID-urile sunt definite cu #define. O data ce o resursa este compilata si legata la o aplicatie ea poate fi incarcata printr-un simplu apel de functie. Un fis de resurse contine: ID resusrsa, numele art de meniu, ID-urile art de meniu, atribute ale meniului.

IDR_MAINFRAME MENU PRELOAD DISCARDABLEBEGIN

POPUP “&File”BEGIN

MENUITEM “&New\tCtrl+N”, ID_FILE_NEW...MENUITEM SEPARATOR

ENDPOPUP “&View”BEGIN...END

END

Indicatii:43

Page 44: Programare VISUAL C++

O elipsa in descrierea meniului inseamna ca sunt necesare inf suplimentare dupa ce art este selectat, ! inseamna ca se executa imediat o comanda (Exit!).Afxres.h defineste valorile ID-urile pt. comenzile uzuale. Valori valide pt. ID-uri in intervalul 1-0xEFFF sau mai exact 0x8000 – 0xF000 cf. Nota Teh. 20 din MFC.Textul ce urmeaza car. TAB \t identifica un accelerator. Un accelerator este o tasta sau o combinatie de taste (Ctrl+C, Ctrl+V, etc.) care cind sunt apasate au acelasi efect ca selectarea art din meniu.Cind definim art de meniu, putem sa-i indicam starea initiala, de ex GRAYED, ceea ce-l face disable, CHECKED.

Incarcarea si afisarea unui meniuIn timpul exec. o res. de meniu tr. incarcata si atasata la fereastra. Cind fer este afisata, meniul va fi afisat de asemenea.Metoda 1: apel la fct CFrameWnd::Create

Create(NULL, _T(“Nume aplicatie”), WS_OVERLAPPEDWINDOW, rectDefault, NULL, MAKEINTRESOURCE(IDR_MAINFRAME));

Param. 6 indica resursa de meniu. MAKEINTRESOURCE transforma un intreg intr-o data de tip LPSTR.

Metoda 2: apel la fct CFrameWnd::LoadFrame, care creaza o fereastra cadru si ataseaza un meniu la ac.

LoadFrame(IDR_MAINFRAME, WS_OVERLAPPEDWINDOW, NULL, NULL);

LoadFrame este asemenatoare cu Create, dar poate incarca si icoane si alte resurse ceea ce nu face Create (in spate este CreateWindow , CreateWindowEx).

Metoda 3: construim un ob de tip meniu, clasa CMenu, si-l incarcam CMenu::LoadMenu si apoi il atasam la fer. CWnd::SetMenu:

CMenu menu;menu.LoadMenu(IDR_MAINFRAME);SetMenu(&menu);menu.Detach();

CMenu::Detach() va detasa meniul din ob CMenu a.i. meniul nu va fi distrus prematur cind on menu va fi distrus.Regula gen. Un meniu incarcat cu LoadMenu ar trebui sa fie distrus cu DestroyMenu inainte ca aplicatia sa se termine.

Metoda 3 este folosita in programe care au mai mult de un meniu. Ex. de aplicatie cu doua meniuri:

Create(NULL, _T(“aplicatie”));m_menuLong.LoadMenu(IDR_LONGMENU);m_menuShort.LoadMenu(IDR_SHORTMENU);SetMenu(m_bShortMenu ? &m_menuShort, &m_menuLong);

Comutarea intre cele doua meniuri se face astfel (ca raspuns la o comanda a utilizatorului) de la meniul lung la cel scurt:

m_bShortMenu = TRUE;44

Page 45: Programare VISUAL C++

SetMenu(&m_menuShort);DrawMenuBar();

de la m.s la m.l

m_bShortMenu = FALSE;SetMenu(&m_menuLong);DrawMenuBar();

Fct. CWnd::DrawMenuBar() redeseneaza bara meniu pt. a reflecta schimbarile facute in meniu. Daca m_menuLong si m_menuShort sunt var. membru ale clasei fereastra cadru (derivata din CWnd), destructorii pt. meniuri se vor apela cind fer. cadru va fi distrusa, deci in acest caz nu mai e nevoie de DestroyMenu.

Raspunsul la comenzile meniuluiCind utilizatorul selecteaza un art. de meniu (bara de meniuri) fer primeste o serie de mesaje: WM_INITMENU ce notifica fer ca un articol al meniului superior (top-level menu item) a fost selectat. Inainte ca meniul sa fie afisat fer primeste mesajul WM_INITMENUPOPUP, locul unde ar putea fi actualizate (enable, disable, checked, etc.) articolele meniului, meniul inca nu este afisat. Cind parcurgem art. unui meniu drop down (popup), fer primeste mesajul WM_MENUSELECT, mesaj ce poate fi tratat, in special in SDK, prin afisarea unui text in bara de stare (scurt help). Dar cel mai important mesaj este WM_COMMAND trimis cind util. selecteaza un art de meniu.Cuv inferior al param. wParam pastreaza ID-ul comenzii (art. din meniul popup) si in SDK se face practic switch pe LOWORD(wParam) pentru a identifica care comanda a fost selectata. In MFC se adauga in harta de mesaje a clasei respective macroul ON_COMMAND care trateaza mesajul WM_COMMAND. ON_COMMAND are doi parametri, primul indica ID_ul comenzii, iar al doilea indica handlerul comenzii (functia ce va fi apelata la selectia acestei comenzi). Ex. ON_COMMAND(ID_FILE_SAVE, OnFileSave);

Functiile ce trateaza comenzile de meniu nu au parametri si nu intorc nimic.

Intervale pentru comenziSunt folosite pentru a trata un grup de art. meniu cu o singura functie. Functia va avea codul necesar pentru a identifica corect care comanda a fost selectata. De exemplu tratam culorile penitei si construim un meniu cu comenzile: Rosu, Verde, Albastru. Pentru a manevra mai usor interfata, comenzile acestui meniu vor fi inserate in harta de mesaje a clasei derivate din CView. Pp. ID-urile sunt: IDM_ROSU, IDM_VERDE, IDM_ALBASTRU iar fct. corespunzatoare OnRosu, OnVerde, OnAlbastru, iar culoarea o pastram intr-un int, m_nCuloare, cu valorile respective 0,1,2.In harta de mesaje vom avea:ON_COMMAND(IDM_ROSU, OnRosu);ON_COMMAND(IDM_VERDE, OnVerde);ON_COMMAND(IDM_ALBASTRU, OnAlbastru);

iar fctiile vor fi:

void CXView::OnRosu(){ m_nCuloare = 0;}

void CXView::OnVerde(){ m_nCuloare = 1;}

void CXView::OnAlbastru(){ m_nCuloare = 2;}

Daca am avea mai multe culori? Cum procedam?45

Page 46: Programare VISUAL C++

Grupam aceste comenzi si vom extrage ID-ul comenzii cu CWnd:;GetCurrentMessage, iar harta de mesaje va fi:

ON_COMMAND(IDM_ROSU, OnCuloare);ON_COMMAND(IDM_VERDE, OnCuloare);ON_COMMAND(IDM_ALBASTRU, OnCuloare);

void CXView::OnCuloare(){UNIT nID = (UINT) LOWORD(GetCurrentMessage()->wParam);m_nCuloare = nID – IDM_ROSU;}

Indecsii meniului incep cu 0 (zero). Dar harta de mesaje este inca prea mare. Vom folosi macroul ON_COMMAND_RANGE care trateaza o serie de comenzi care au ID-uri contigue (secventiale):ON_COMMAND_RANGE(IDM_ROSU, IDM_ALBASTRU, OnCuloare);

Actualizarea art intr-un meniu

Metoda 1: actualizare in momentul cind art este selectat void CXView::OnCuloare(){

CMenu* pMenu = GetMenu();pMenu->CheckMenuItem(m_nCuloare + IDM_ROSU, MF_UNCHECKED);pMenu->CheckMenuItem(nID, MF_CHECKED);m_nCuloare = nID – IDM_ROSU;

}

Metoda 2: mutam codul care actualizeaza meniul in tratarea mesajului WM_INITMENUPOPUP iar fct este OnInitMenuPopup. Aici actualizarea se face inainte ca meniul sa fie afisat. Aceasta functie are trei parametri: un pointer de tip CMenu ce pointeaza la submeniul care va fi afisat, un UINT – valoare ce da indexul art din submeniu si o var. BOOL care este # 0 daca mesajul este al meniului sistem si 0 altfel.

In harta de mesaje avem:ON_WM_INITMENUPOPUP()

iar fct: (COLOR_MENU_INDEX este o variabila ce specifica pozitia meniului Color in bara de meniu a aplicatiei)

void CMainFrame::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu){if (!bSysmenu && (nIndex == COLOR_MENU_INDEX)) {pPopMenu->CheckMenuItem(IDM_ROSU, MF_UNCHECKED);pPopMenu->CheckMenuItem(IDM_VERDE, MF_UNCHECKED);pPopMenu->CheckMenuItem(IDM_ALBASTRU, MF_UNCHECKED);pPopMenu->CheckMenuItem(m_nCuloare + IDM_ROSU, MF_CHECKED);}

46

Page 47: Programare VISUAL C++

Un alt mecanism este tot la mesajul WM_INITMENUPOPUP si consta in a completa harta de mesaje cu fct (handleri) pentru comenzi, handleri ce vor fi apelati inainte ca meniul sa fie vizibil si inaintea fiecarei selectii din meniu. Fiecarui handler de actualizare ii este pasat un pointer la un obiect CCmdUI a carei functii membru pot fi folosite pentru a modifica art de meniu. Si pt. ca aceasta clasa CCmdUI, nu este specifica unui tip particular al unui element al interfetei utilizatorului, acesti handleri pot fi utilizati si pentru actualizarea butoanelor din toolbar si alte obiecte ale interfetei UI.Ex:

in harta de mesaje:ON_COMMAND_UPDATE_UI(IDM_ROSU, OnUpdateRosu)ON_COMMAND_UPDATE_UI(IDM_VERDE, OnUpdateVerde)ON_COMMAND_UPDATE_UI(IDM_ALBASTRU, OnUpdateAlbastru)

void CMainFrame::OnUpdateRosu(CCmdUI* pCmdUI){ pCmdUI->SetCheck(m_nCuloare);}, etc.

Metode din CCmdUI: Enable, SetCheck, SetRadio (nu are echivalent in SDK), SetText

Keyboard Acceleratori – Adaugare la aplicatie

Acceleratori: combinatii de taste pentru a selecta o comanda. Un accelerator produce mesajul WM_COMMAND.

Se creaza o tabela de acceleratori in fis de resurse, o resursa speciala care coreleaza ID-ul art de meniu cu tastele sau combinatiile de taste, si incarca resursa in program cu un apel de functie.Resursa tabela de acceleratori este definita de un bloc ACCELERATORS in fis de resurse. Format general:

ResurseID ACCELERATORSBEGIN...ENDIn MFC tabela de acceleratori are structura:

IDR_MAINFRAME ACCELERATORS PRELOAD MOVEABLEBEGIN“N”, ID_FILE_NEW, VIRTKEY, CONTROL...VK_BACK, ID_EDIT_UNDO, VIRTKEY, ALT

END

VIRTKEY = spune compilatorului de resurse ca prima intrare este un cod de cheie virtuala, iar urmatorul este CONTROL, ALT sau SHIFT

Incarcarea acceleratorilor se face cu fct:LoadAccelTable(MAKEINTRESOURCE(IDR_MAINFRAME));De asemenea se poate face si cu LoadFrame. Daca doua resurse au acelasi nume, LoadFrame le incarca pe amindoua la un singur apel de functie.

47

Page 48: Programare VISUAL C++

Pentru ca acceleratorii sa lucreze, bucla de mesaje trebuie sa includa un apel la functia API ::TranslateAccelerator

Crearea meniului programatic (at run time)

CMenu menuMain;menuMain.CreateMenu();

Cmenu menuPopup;menuPopup.CreatePopupMenu();menuPopup.AppendMenu(MF_STRING, IDM_ROSU, “&Rosu”);menuMain.AppendMenu(MF_POPUP, (UINT) menuPopup.Detach(), “&Culori”);menuPopup.CreatePopupMenu();menuPopup.AppendMenu(MF_STRING, IDM_EXIT, “&Exit”);...menuMain.AppendMenu(MF_POPUP, (UINT)menuPopup.Detach(), “&Sesiune”);

SetMenu(&menuMain);menuMain.Detach();

Modificarea programatica

Functiile necesare pentru modificare sunt:AppendMenu, InsertMenu, ModifyMenu, DeleteMenu, RemoveMenu

Inainte de a modifica un meniu, trebuie sa obtinem un pointer la acel meniu, CWnd::GetMenu. In top-level menu, articolele sunt referite prin indici ce incep cu 0 (zero) – cel mai din stinga.

CMenu* pMenu = GetMenu();pMenu-DeleteMenu(1, MF_BYPOSITION); saupMenu->DeleteMenu(IDM_CULORI, MF_BYCOMMAND);

sau item-uri din meniuri

CMenu* pMenu = GetMenu()-GetSubMenu(1);pMenu->DeleteMenu(IDM_VERDE, MF_BYCOMMAND);

sau echivalent:CMenu* pMenu = GetMenu();pMenu->DeleteMenu(IDM_VERDE, MF_BYCOMMAND); // merge bine

Cititi clasa CMenu.

Meniul system

CMenu* pSysMenu = GetSystemMenu(FALSE);

FALSE = inseamna ca dorim un pointer la o copie a meniului sistem pe care vrem sa-l modificam. TRUE = reset system menu la starea sa implicita.

48

Page 49: Programare VISUAL C++

pSysMenu->AppendMenu(MF_SEPARATOR);pSysmenu->AppendMenu(MF_STRING, IDM_COMANDA_NOUA, _T(“&Comanda adaugata”));

in harta de mesaje:

ON_WM_SYSCOMMAND()

void CMainFrame::OnSysCommand(UINT nID, LPARAM lPram){if ((nID & 0xFFF0 ) == IDM_COMMANDA_NOUA){ // ceva }CFrameWnd::OnSysCommand(nID, lParam);}

Owner Draw Menus = Meniuri grafice

Metoda 1: se creaza bitmap-uri care se folosesc apoi in AppendMenu. Vezi clasa CBitmap. Dzavantaj: bitmap-urile au marime fixa.

Metoda 2: articole meniu desenate de utilizator. Se trateaza mesajul WM_DRAWITEM. Art. de meniu are atributul ca trebuie desenat de proprietar si nu de windows. Inainte ca meniul ce contine un articol owner-draw sa fie afisat pentru prima data, W trimite un mesaj WM_MEASUREITEM pentru a obtine informatii despre dimensiunile art. meniului. Pentru fiecare articol owner-draw al meniului se transmite un asemenea mesaj.

Pas 1. Primul pas in implementarea unui asemenea meniu este de a seta atributul (stilul) meniului cu MF_OWNERDRAW. Editorul de resurse nu suporta acest stil. Se poate apela la ModifyMenu sau la modificarea manuala a resurselor.Pas 2. se adauga fct OnMeasureItem la mesajul WM_MEASUREITEMafx_msg OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpmis);

nIDCtl = ID controluluitypedef struct tagLP{UINT CtlType; // ODT_MENUUINT CtlID;UINT itemID; // ID art meniuUINT itemWidth; // lat. orizontala in pixeliUINT itemHeight; // inalt in pixeliUINT itemData;} MEASUREITEMSTRUCT;

lpmis->itemWidth = ::GetSystemMetrics(SM_CYMENU) * 4;lpmis->itemHeight = ::GetSystemMetrics(SM_CYMENU);

Pas 3. Se scrie fct OnDrawItem pentru mesajul WM_DRAWITEM

afx_msg OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpdis);

Are la baza urmatoarea structura DRAWITEMSTRUCT care contine:

49

Page 50: Programare VISUAL C++

UINT CtlType;UINT CtlID;UINT itemID;UINT itemAction;UINT itemState;HWND hwndItem;HDC hDC;RECT rcItem;DWORD itemData;

Mai multe informatii in Help si J. Prossie

Procesare WM_MENUCHAR : OnMenuChar

O parte neplacuta a meniurilor owner-draw este aceea ca windows nu furnizeaza shortcut-uri pentru acestea. Chiar daca folosim inainte un meniu text, apoi il modificam in owner-draw, shortcut-ul de la text nu mai functioneaza. rezolvarea problemei consta in tratarea mesajului WM_MENUCHAR, care este primit de fer. cind un meniu este afisat si o tasta care nu corespunde unui articol de meniu este apasata. Prin procesarea acestui mesaj, putem adauga shortcut-uri la meniurile owner-draw. Prototipul functiei CWnd::OnMenuChar este urmatorul:afx_msg LRESULT OnMenuChar(UINT nChar, UINT nFlags, CMenu* pMenu)

nChar = contine codul ANSI al tastei;nFlags = contine un flag MF_POPUP daca meniul la care mesajul vine este un submeniu;pMenu = pointer la meniu;

Valoarea returnata:HIWORD(LRESULT) trebuie sa contina una din urm. valori:0 = daca W ar trebui sa ignore tasta;1 = daca W ar trebui sa inchida meniul2 = daca W ar trebui sa selecteze unul din art. afisate din meniuIn acest din urma caz LOWORD(LRESULT) contine ID-ul art. de meniu corespunzator.Exista definit macroul MAKELRESULT care seteaza valoarea returnata:

LRESULT lResult = MAKELRESULT(IDM_ROSU, 2); va seta high word pe 2 si low word cu IDM_ROSU,

Meniuri contextuale: WM_CONTEXTMENUSe activeaza de obicei la clic dreapta mouse. Un meniu contextual nu este nimic altceva decit un submeniu care nu e atasat la un top-level menu. Functia CMenu::TrackPopupMenu afiseaza un asemenea meniu. Prototipul fct. este:BOOL TrackPopupMenu(UINT nFlags, int x, int y, CWnd* pWnd, LPRECT lpRect = NULL)x, y = locatia pe ecran (coord. ecran) unde va apare meniul;nFlags = inf. despre alianiamentul relativ la x (TPM_LEFTALIGN, TPM_CENTERALIGN, TPM_RIGHTALIGN) si care buton este utilizat in continuare pt a fcae o selectie (TPM_LEFTBUTTON, TPM_RIGHTBUTTON).pWnd = identifica fereastra care va primi mesajul dupa selectia unei comenzi din meniu;lpRect = dimensiunea unui dreptunghi (coord. ecran) in care utilizatorul poate face clic fara a anula meniul afisat. Ex.

CMenu menu;

50

Page 51: Programare VISUAL C++

menu.LoadMenu(IDR_CONTEXTMENU);CMenu* pContextMenu = menu.GetSubMenu(0);pContextMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON, point.x, point.y, AfxGetMainWnd());

Daca tratam mesajul WM_CONTEXTMENU in harta de mesaje avem macroul:ON_WM_CONTEXTMENU si fct afx_msg OnContextMenu(CWnd* pWnd, CPoint point);pWnd = identifica fereastra in care s-a facut clic si point coordonatele punctului unde s-a facut clic.

Procesarea comenzilor. Flagul TPM_RETURNCMD = folosit la obtinerea unui raspuns la apelul unui meniu contextual.

int nCmd = (int) pContextMenu->TrackPopupMenu(TPM_RETURNCMD...

switch(nCmd){case IDM_CONTEXT_1:

break;...}Un meniu afisat in acest mod va genera mesaje WM_COMMAND cind un articol este selectat.

51

Page 52: Programare VISUAL C++

Dialoguri şi ControalePentru fiecare boxă de dialog care apare pe ecran există două entităţi pe care trebuie să le dezvoltăm: o resursă boxă de dialog şi o clasă boxă de dialog.Resursa boxă de dialog este folosită pentru a desena boxa de dialog şi controalele pe care le conţine.Clasa menţine valorile din boxa de dialog şi există o funcţie membru a acestei clase care este folosită pentru a activa boxa de dialog. În general se construieşte un meniu ce va conţine o opţiune pentru activarea acestui dialog.Resursa boxă de dialog se construieşte cu editorul de resurse. Pe baza acestei resurse Class Wizard va genera clasa corespunzătoare. În general boxele de dialog sunt derivate din CDialog.

Crearea unei resurse de dialogControalele uzuale din Windows pot fi folosite într-o boxă de dialog. Enumerăm dintre acestea:

• Static text. Folosit de alte controale pentru descriere.• Edit box. Folosit pentru a prelua anumite informaţii (şiruri de caractere, numere). Poate fi definit ca având

o singură linie sau mai multe linii.• Button. Fiecare boxă de dialog are implicit butoanele OK şi Cancel care pot fi păstrate sau şterse. • Check box. Permite selectare sau deselectare. Lucrează independent.• Radio button. Permite selectare sau deselectare. Nu lucrează independent.• List box. Conţin o listă de articole, din care utilizatorul poate selecta un articol la un moment dat. Nu

putem tasta text în cadrul acestui control.• Combo box. Este o combinaţie între Edit box şi List box.

Definirea boxei de dialod şi ID-ul ControluluiFiecare boxă de dialog are un ID unic (un identificator), iar controalele din cadrul boxei de dialog au de asemenea ID-uri. Fiecare control din cadrul unei boxei de dialog este văzut ca o fereastră. Deci o parte din funcţionalitatea unei ferestre se va reflecta şi asupra controlului. Clasa CDialog este derivată din clasa CWnd care descrie funcţioanlitatea unei ferestre. Dacă avem un pointer la fereastră (GetDlgItem() pentru dialoguri) putem apela metode specifice pentru aceasta. De exemplu putem ascunde o fereastră (ShowWindow()), o putem activa sau dezactiva (EnableWindow()) sau îi putem citi/modifica conţinutul (GetWindowText(), SetWindowText()), etc. Pentru a şti ce funcţii se aplică va trebui să consultăm clasa de bază şi clasa derivată. Editorul de resurse defineşte implicit un ID pentru boxa de dialog şi ID-uri pentru controalele pe care le ataşăm acesteia. Putem modifica aceste ID-uri (Properties din meniul contextual disponibil pentru controlul selectat sau boxa de dialog) pentru a le da un sens conform aplicaţiei noastre. De obicei aceste ID-uri încep cu IDD_ urmate de un text pentru boxele de dialod şi cu IDC_ pentru controalele din boxa de dialog. (Exemple: IDD_DIALOG1, IDC_STATIC1, IDC_EDIT1, IDC_RADIO1, etc.).Pentru controalele dintr-o boxă de dialog putem defini date membru (variabile) în cadrul clasei pentru a menţine starea acestora. Cu ajutorul Class Wizard-ului putem defini aceste variabile şi le putem manevra într-o anumită măsură. De asemenea putem defini şi implementa funcţiile necesare tratării mesajelor de notificare pe care acestea le suportă.Observaţie: MFC utilizează tabele pentru a gestiona harta de mesaje precum şi pentru a menţine legătura dintre o variabilă şi un control. A se vedea clasa CDataExchange şi funcţia DoDataExchange().

Exerciţiu. Se va crea o boxă de dialog cu mai multe controale şi se vor explica aceste lucruri pe codul rezultat.

Se vor urmări posibilităţile de aliniere a controalelor, redimensionare, ordinea controalelor, stilurile, etc.

Tipuri de date pentru fiecare control:• Edit box. În mod obişnuit un string, dar poate fi şi un int, long, float.

• Check box. int • Radio button. int • List box. String • Combo box. String • Scrollbar. int

Page 53: Programare VISUAL C++

Pentru a afişa boxa de dialog va trebui să apelăm funcţia membru DoModal() în cazul boxelor de dialog modale. Boxele de dialog amodale vor fi tratate separat, deoarece nu urmează aceeaşi construcţie.

Exemplu de lansare în execuţie a unei boxe de dialog CSdiDialog dlg; // se creaza obiectul pe stiva dlg.m_check = TRUE; // variabila atasata unui control check dlg.m_edit = "hi there"; // variabila atasata unui control Edit CString msg; if (dlg.DoModal() == IDOK) // are loc afisarea dialogului { msg = "You clicked OK. "; } else { msg = "You cancelled. "; } msg += "Edit box is: "; msg += dlg.m_edit; AfxMessageBox (msg);

Tipărirea codului (Autocompletion: Tools->Options->Editor)

Când tastăm codul avem posibilitatea de a vedea numele variabilelor membru şi a funcţiilor din clasă. Dacă tastăm dlg. şi apoi pauză, va apărea o fereastră, ce va lista toate variabilel membru şi funcţiile din clasă, inclusiv cele moştenite din clasa de bază. În continuare vom folosi tastele săgeţi pentru selecţie şi apoi apăsăm tasta Space pentru a trece selecţia făcută în cadrul codului.

Ce se ascunde în spatele unei boxe de dialog?Cel mai important lucru este înţelegerea funcţiei care urmează.

void CSdiDialog::DoDataExchange(CDataExchange* pDX){ CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CSdiDialog) DDX_Control(pDX, IDC_LIST1, m_listbox); DDX_Check(pDX, IDC_CHECK1, m_check); DDX_Text(pDX, IDC_EDIT1, m_edit); DDV_MaxChars(pDX, m_edit, 10); DDX_Radio(pDX, IDC_RADIO1, m_radio); //}}AFX_DATA_MAP}

Trebuie să ştim că între boxa de dialog afişată pe ecran şi variabilele care menţin starea anumitor controale este definit un schimb bidirecţional de date. Un rol important îl joacă aici funcţia UpdateData() care stabileşte practic direcţia de schimb ecran->variabile sau variabile->ecran.Funcţiile care încep cu DDX_ realizează schimbul de date. Există 34 de funcţii care încep cu DDX, una pentru fiecare tip de dată care poate fi schimbată între clasă şi dialog. Primul parametru arată direcţia, al doilea este ID-ul controlului iar al treilea parametru este numele variabilei. Codul este adăgat de Class Wizard.

Page 54: Programare VISUAL C++

OBSERVAŢIE: Anumite funcţii DDX nu sunt generate de Class Wizard. De exemplu, când ne conectăm la o valoare din List Box, singura alegere posibilă pentru tip este CString. Alegând acest lucru, Class Wizard va genera un apel la DDX_LBString() care conectează stringul selectat la variabila membru de tip CString. Există situaţii când un index de tip int este mai bun. Pentru acest lucru există funcţia DDX_LBIndex() care realizeză acest schimb de informaţii. O parte dintre funcţiile din List Box folosesc indecşi pentru a manevra articolele dintr-un list box. Putem adăuga cod în DodataExchange(), dar în afara comentariilor speciale introduse de Class Wizard. În acest caz vom adăuga singuri variabila membru la clasă.

Funcţiile care încep cu DDV realizează validarea datelor. Prametrul al doilea este numele variabilei, iar al treilea specifică limitele.

Utilizarea controlului List Box Un control de acest tip nu poate fi iniţializat decăt atunci când boxa de dialog este afişată, deci este pe ecran. Din acest motiv trebuie să fim atenţi la momentul iniţializării. Nu putem apela funcţii membru din această clasă atâta timp cât dialogul nu este afişat. Acest lucru este valabil pentru orice control pe care îl accesăm ca un control şi nu ca o valoare.O asemenea iniţializare are loc în funcţia OnInitialDialog() care este apelată de cadrul de lucru (frame work) imediat ce dialogul a fost afişat pe ecran. Această funcţie este apelată la trimiterea mesajului WM_INITDIALOG.

Cade în sarcina noastră să includem acest mesaj în tratare (se face cu Class Wizard). Se va explica în mod concret cum se foloseşte Class Wizard pentru a adăuga noi mesaje tratate de clasa respectivă. De asemenea tot cu Class Wizard se ataşează variabile pentru controalele dintr-o boxă de dialog.

Un exemplu pentru OnInitDialog()BOOL CSdiDialog::OnInitDialog(){ CDialog::OnInitDialog(); m_listbox.AddString("First String"); m_listbox.AddString("Second String"); m_listbox.AddString("Yet Another String"); m_listbox.AddString("String Number Four"); m_listbox.SetCurSel(2); return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE}

Această funcţie apelează mai întâi OnInitDialog() din clasa de bază, iar în continuare se execută codul din cadrul funcţiei. Stringurile vor fi afişate în ordinea în care au fost introduse (adăugate) cu funcţia AddString().În final se apelează funcţia SetCurSel(), funcţie care are drept parametru un index ce identifică articolele din List Box. Indexul pentru primul articol are valoarea 0 (zero).

OBSERVAŢIE: În mod obişnuit, articolele în list box sunt adăugate dintr-un vector sau o listă. Pentru acest lucru se folosesc clasele colecţie din MFC (CStringArray, CList, etc.).

Page 55: Programare VISUAL C++

Pentru a menţine (memora) articolul (string-ul) selectat în list box, vom adăuga o variabilă membru de tip CString la această clasă. Această variabilă va fi privată, şi deci vom adăuga funcţii membru publice la clasă pentru a manevra această variabilă pentru a păstra stilul de programare impus de POO.Pentru selecţie se va trata mesajul de notificare BN_CLICKED, care are semnificaţia că utilizatorul a făcut o selecţie în list box. Funcţia care tratează acest mesaj se va adăuga tot cu Class Wizard. În codul care urmează se exemplifică preluarea stringului din list box la apăsarea butonului OK.

void CSdiDialog::OnOK(){ int index = m_listbox.GetCurSel(); if (index != LB_ERR) { m_listbox.GetText(index, m_selected); } else { m_selected = ""; } CDialog::OnOK();}

Explicaţii. Funcţia GetCurSel() întoarce indexul la articolul selectat. Dacă acesta este valid (index != LB_ERR) se obţine valoarea acestui articol în variabila m_selected care este de tip CString folosind funcţia GetText al cărei prim parametru este indexul articolului.. În final se apelează funcţia OnOK() din clasa de bază pentru a executa alte procesări necesare. În cele ce urmează vom observa apeluri la funcţiile din clasa de bază. Aceste apeluri sunt generate de Class Wizard. Există şi excepţii când aceste apeluri trebuiesc invalidae (comentate). Ca un exemplu vom vedea la împărtirea zonei client în mai multe ferestre (splitting window) la apelul funcţiei OnCreateClient().

Folosirea butoanelor radioLa afişarea unei boxe de dialog care conţine butoane radio vom vedea că nici un buton nu este selectat. Pentru a realiza o selecţie implicită adăugăm în OnInitDialog() următoarele linii de cod.

m_radio = 1;UpdateData(FALSE);

Variabila m_radio este ataşată unui grup de butoane radio şi reprezintă indexul butonului în acest grup. Primul buton are indexul 0 (zero). Funcţia UpdateData(FALSE) stabileşte direcţia de actualizare, care în acest caz este de la variabilă la ecran şi în final are loc un refresh al boxei de dialog.

Un grup de butoane radio poate fi accesat după ce boxa de dialog nu mai este pe ecran, deci nu trebuie să tratăm OnOK() sau ONCancel().

Page 56: Programare VISUAL C++

Controale clasiceW pune la dispozitie 6 (sase) controale clasice. Un control nu este altceva decit o fereastra cu stiluri speciale. Trebuie retinut faptul ca a lucra cu un asemea control este ca si cum am lucra cu o fereastra. Tipurile controalelor, structurile WNDCLASS si clasele corespondente MFC sunt date in tabela urmatoare:

Controale clasice

Control Type WNDCLASS MFC ClassButtons "BUTTON" CButtonList boxes "LISTBOX" CListBoxEdit controls "EDIT" CEditCombo boxes "COMBOBOX" CComboBoxScroll bars "SCROLLBAR" CScrollBarStatic controls "STATIC" CStatic

Un control este creat prin instantierea clasei respective din MFC urmat apoi de apelul functiei Create din acea clasa. MFC creaza un obiect in doi pasi. De descris avantajele si dezavantajele acestei metode.Daca m_wndPushButton este un obiect CButton, instructiunea:

m_wndPushButton.Create (_T ("Start"), WS_CHILD ¦ WS_VISIBLE ¦ BS_PUSHBUTTON, rect, this, IDC_BUTTON);

creaza un control push button ce contine textul “Start”. Descriere parametrii pt. fct. Create:primul parametru specifica textul controlului;al doilea param. reprezinta stilul ferestrei, ce reprezinta o combinatie intre stilurile ferestrei si stiluri specifice controlului.; Controlul creat este o fereastra descendent (copil) al ferestrei identificata de al patrulea parametru (in SDK se furnizeaza un HWND la fereastra parinte);al treilea parametru specifica marimea si pozitia (in pixeli) controlului, data printr-un obiect CRect; pozitia este relativa la coltul din stg sus al ferestrei parinte;ultimul parametru este ID-ul butonului (controlului - un intreg), folosit in diverse functii pentru a avea access la el; acest ID trebuie sa aiba o valoare unica in interiorul ferestrei date pentru a putea identifica corect controlul si functiile care trateaza mesajele de notificare.

Unele controale (ListBox, Edit) pentru a se alinia la noile stiluri, au o noua functie membru CreateEx. Stilurile extinse se scriu numai in cadrul acestei functii (vezi CreateWindow si CreateWindowEx).

Daca m_wndListBox este un obiect CListBox, urmatoarea instructiune creaza un control list box cu stilulu extins WS_EX_CLIENTEDGE:

m_wndListBox.CreateEx (WS_EX_CLIENTEDGE, _T ("LISTBOX"), NULL, WS_CHILD | WS_VISIBLE | LBS_STANDARD, rect, this, IDC_LISTBOX);

Ca o alternativa, putem deriva clasa noastra din CListBox, si apoi rescriem functia PreCreateWindow in clasa derivata, si aplicam stilul de fereastra WS_EX_CLIENTEDGE:

BOOL CMyListBox::PreCreateWindow (CREATESTRUCT& cs){ if (!CListBox::PreCreateWindow (cs)) return FALSE;

cs.dwExStyle |= WS_EX_CLIENTEDGE; return TRUE;}

Page 57: Programare VISUAL C++

Un control trimite notificari parintelui sub forma de mesaje WM_COMMAND. Tipurile de notificari depind de tipul controlului, dar in fiecare caz, informatia din parametrii mesajului, wParam si lParam, identifica controlul care trimite mesajul si actiunea ceruta de mesaj.De exemplu, cind un push button este apasat (clic), codul de notificare este BN_CLICKED in HIWORD(wParam) si ID-ul controlului in LOWORD(wParam), iar handler-ul ferestrei controlului in lParam.Cadrul de lucru MFC, insereaza in harta de mesaje acest mesaj de notificare, ascunzind detaliile de implementare pentru tratarea mesajului WM_COMMAND (se face legatura intre ID-ul controlului si functia care trateaza mesajul de notificare):

ON_BN_CLICKED (IDC_BUTTON, OnButtonClicked)

ON_BN_CLICKED este un macrou, si asemenea macro-uri exista pentru fiecare mesaj de notificare de la fiecare control.Exista un macro generic ON_CONTROL, care manipuleaza toate notificarile si toate tipurile de controale, si ON_CONTROL_RANGE, care mapeaza notificari in mod identic de la doua sau mai multe controale la o functie comuna. Comunicarea intre controale si parinti (proprietarii controlului) este in ambele directii. De ex. parintele poate trimite mesajul BM_SETCHECK unui control check box cu parametrul wParam BST_CHECKED.

MFC simplifica interfata controlului bazata pe mesaje prin construirea de functii membru in clasa controlului care wrap BM_SETCHECK si alte mesaje ale controlului. De exemplu:

m_wndCheckBox.SetCheck (BST_CHECKED);

plaseaza un check mark in interiorul check box-ului reprezentat de un obiect CButton, numit m_wndCheckBox.Din cauza ca un control este o fereastra, anumite functii membru pe care controlul le mosteneste din CWnd sunt folositoare pentru controlul programarii.De exemplu aceeasi functie care modifica titlul unei ferestre, SetWindowText, modifica textul (eticheta) unui push button, sau schimba continutul unui control de editare (box edit). Alte functii: GetWindowText, EnableWindow, SetFont. Daca vrem sa facem ceva in control si nu gasim o functie corespunzatoare in clasa controlului va trebui sa cautam o asemenea functie in clasa CWnd din care sunt derivate toate contraolele.

Clasa CButton CButton reprezinta controale de tip button bazate pe clasa WNDCLASS "BUTTON". Controalele button exista in patru variante: push buttons, check boxes, radio buttons, si group boxes. Se vor desena cele patru tipuri.

Cand cream un control buton, vom specifica tipul acestuia prin includerea unuia din urmatoarele flag-uri in stilul ferestrei butonului:

Style DescriptionBS_PUSHBUTTON Creates a standard push button controlBS_DEFPUSHBUTTON Creates a default push button; used in dialog boxes to identify the push

button that's clicked if Enter is pressedBS_CHECKBOX Creates a check box controlBS_AUTOCHECKBOX Creates a check box control that checks and unchecks itself when clickedBS_3STATE Creates a three-state check box controlBS_AUTO3STATE Creates a three-state check box control that cycles through three states—

checked, unchecked, and indeterminate—when clickedBS_RADIOBUTTON Creates a radio button controlBS_AUTORADIOBUTTON Creates a radio button control that, when clicked, checks itself and

unchecks other radio buttons in the groupBS_GROUPBOX Creates a group box control

In plus, putem adauga urmatoarele valori (OR pe biti) la stilul ferestrei controlului privitoare la linierea textului ce insoteste controlul:

Style DescriptionBS_LEFTTEXT Moves the text accompanying a radio button or check box control from the

button's right (the default) to its left

Page 58: Programare VISUAL C++

BS_RIGHTBUTTON Same as BS_LEFTTEXTBS_LEFT Left justifies the button text in the control rectangleBS_CENTER Centers the button text in the control rectangleBS_RIGHT Right justifies the button text in the control rectangleBS_TOP Positions the button text at the top of the control rectangleBS_VCENTER Positions the button text in the center of the control rectangle verticallyBS_BOTTOM Positions the button text at the bottom of the control rectangleBS_MULTILINE Allows text too long to fit on one line to be broken into two or more lines

Exista si alte tipuri de stiluri de butoane, dar care sunt folosite mai putin. De ex., BS_NOTIFY, programeaza un buton sa trimita notificarile BN_DOUBLECLICKED, BN_KILLFOCUS, si BN_SETFOCUS. BS_OWNERDRAW creaza un buton owner-draw (desenat de proprietar – programatorul va scrie cod pentru acest lucru) — infatisarea (aparenta) butonului este gestionata de parintele butonului.

Butoane PushUn push button este un control buton creat cu stilul BS_PUSHBUTTON. Cind este apasat, controlul trimite parintelui notificarea BN_CLICKED printr-un mesaj WM_COMMAND. In absenta stilului BS_NOTIFY, un asemenea control nu trimite nici un alt tip de notificare.Macroul ON_BN_CLICKED din MFC leaga notificarile BN_CLICKED de functia membru din clasa fereastra parinte:ON_BN_CLICKED(IDC_BUTTON, OnButtonClicked)

Functiile pentru BN_CLICKED nu au parametri si nu intorc valori.

Check Boxes

Check boxes sunt butoane create cu stilul BS_CHECKBOX, BS_AUTOCHECKBOX, BS_3STATE, sau BS_AUTO3STATE. Stilurile BS_CHECKBOX si BS_AUTOCHECKBOX pot presupune doua stari: checked si unchecked. Un check box trece in starea checked sau unchecked cu CButton::SetCheck:

m_wndCheckBox.SetCheck (BST_CHECKED); // Checkm_wndCheckBox.SetCheck (BST_UNCHECKED); // Uncheck

Pentru a determina daca un check box este in starea checked, folosim CButton::GetCheck. O valoare de retur egala cu BST_CHECKED inseamna ca chcek box-ul este in starea checked, iar BST_UNCHECKED este pt unchecked.Check boxes trimit notificarile BN_CLICKED parintilor lor cind facem click in zona lor. Stilul BS_AUTOCHECKBOX face ca acest control sa lucreze ca un switch on/off automatizat in raspuns la even. click mouse. Stilul BS_CHECKBOX nu face acelasi lucru. Un exemplu de cod pentru un check box cu stilul BS_CHECKBOX si ce trebuie sa facem la BN_CLICKED:

void CMainWindow::OnCheckBoxClicked (){ m_wndCheckBox.SetCheck (m_wndCheckBox.GetCheck () == BST_CHECKED ? BST_UNCHECKED : BST_CHECKED);}

Stilurile BS_3STATE si BS_AUTO3STATE creaza un check box care presupune o a treia stare numita nedeterminata (indeterminate), si controlul intra in aceasta stare cind facem clic pe un asemenea buton iar starea lui curenta este checked sau cind apelam SetCheck cu parametrul BST_INDETERMINATE:

m_wndCheckBox.SetCheck (BST_INDETERMINATE);

Un check box in starea indeterminate contine a grayed check mark. Aici pare sa lucreze o logica trivalenta “partiala”, in sensul ca starea nu poate fi sigura nici checked nici unchecked. (Ex. selectam text in bold si nebold.)

Page 59: Programare VISUAL C++

Butoane Radio

Un buton radio este un control de tip buton cu stilul BS_RADIOBUTTON sau BS_AUTORADIOBUTTON. In mod normal butoanele radio lucreaza in grup, si reprezinta o lista de optiuni mutual exclusive. Cind selectam un buton radio cu stilul BS_AUTORADIOBUTTON va ramine activ numai butonul selectat, celelalte butoane din grup devenind inactive in mod automat. Daca folosim stilul BS_RADIOBUTTON, va trebui sa scriem noi cod pentru a dezactiva celelalte butoane, folosind functia CButton::SetCheck.Butoanele radio trimit notificarile BN_CLICKED parintilor lor, la fel ca mai sus.Urmatorul cod trateaza BN_CLICKED:

void CMainWindow::OnRadioButton1Clicked (){ m_wndRadioButton1.SetCheck (BST_CHECKED); m_wndRadioButton2.SetCheck (BST_UNCHECKED); m_wndRadioButton3.SetCheck (BST_UNCHECKED); m_wndRadioButton4.SetCheck (BST_UNCHECKED);}

Deselectind (unchecking) celelalte butoane radio mentine exclusivitatea selectiei. Un handler (o fct.) pentru BN_CLICKED nu este necesar pentru butoanele cu stilul BS_AUTORADIOBUTTON. Pentru butoane radio cu stilul BS_AUTORADIOBUTTON pentru a deselecta corect alte butoane din grup, trebuie sa grupam butoanele in momentul crearii, a.i. W sa stie care butoane apartin grupului. Pentru a crea un grup d ebutoane radio cu stilul BS_AUTORADIOBUTTON urmam urmatoarea procedura (tehnica):

1. In codul aplicatiei, cream butoanele unul dupa altul fara a intercala intre acestea alte controale de alt tip.2. Pentru a marca inceputul grupului, atribuim stilul WS_GROUP primului buton radio pe care il cream. 3. Daca cream controale aditionale dupa ultimul buton radio din grup, atribuim stilul WS_GROUP primului

control aditional pe care il cream.

Cream doua grupuri de cite 4 respectiv 3 butoane radio cu stilul BS_AUTORADIOBUTTON cu un control check box intre ele:

m_wndRadioButton1.Create (_T ("COM1"), WS_CHILD ¦ WS_VISIBLE ¦ WS_GROUP ¦ BS_AUTORADIOBUTTON, rect1, this, IDC_COM1);m_wndRadioButton2.Create (_T ("COM2"), WS_CHILD ¦ WS_VISIBLE ¦ BS_AUTORADIOBUTTON, rect2, this, IDC_COM2);m_wndRadioButton3.Create (_T ("COM3"), WS_CHILD ¦ WS_VISIBLE ¦ BS_AUTORADIOBUTTON, rect3, this, IDC_COM3);m_wndRadioButton4.Create (_T ("COM4"), WS_CHILD ¦ WS_VISIBLE ¦ BS_AUTORADIOBUTTON, rect4, this, IDC_COM4);m_wndRadioButton1.SetCheck (BST_CHECKED);

m_wndCheckBox.Create (_T ("Save settings on exit"), WS_CHILD ¦ WS_VISIBLE ¦ WS_GROUP ¦ BS_AUTOCHECKBOX, rectCheckBox, this, IDC_SAVESETTINGS);

m_wndRadioButton5.Create (_T ("9600"), WS_CHILD ¦ WS_VISIBLE ¦ WS_GROUP ¦ BS_AUTORADIOBUTTON, rect5, this, IDC_9600);m_wndRadioButton6.Create (_T ("14400"), WS_CHILD ¦ WS_VISIBLE ¦ BS_AUTORADIOBUTTON, rect6, this, IDC_14400);m_wndRadioButton7.Create (_T ("28800"), WS_CHILD ¦ WS_VISIBLE ¦ BS_AUTORADIOBUTTON, rect7, this, IDC_28800);m_wndRadioButton5.SetCheck (BST_CHECKED);

Butoanele radio nu sunt niciodata checked implicit. Este responsabilitatea programatorului.

Page 60: Programare VISUAL C++

Group Boxes

Un control group box este creat cu stilul BS_GROUPBOX. Acest control nu primeste si nici nu trimite mesaje. Singurul rol al lor este de a grupa anumite controale in interfata destinata utilizatorului.

Clasa CListBox Clasa CListBox din MFC incapsuleaza controalele list box, care afiseaza o lista de stringuri numite articole. Un list box optional poate sorta articolele pe care le contine si are implementata navigarea verticala, optional si cea orizontala.Cind pe un item (articol) facem clic sau dublu clic , list box-urile (care au stilul LBS_NOTIFY) notifica parintilor lor printr-un mesaj WM_COMMAND. MFC simplifica procesarea acestor mesaje furnizind macro-ul ON_LBN in harta de mesaje, care ruteaza notificarile list box-ului la functii din clasa fereastra parinte..Un list box standard afiseaza stringuri intr-o coloana verticala si permite ca un singur articol sa fie selectat la un moment dat. Articolul curent selectat este afisat in video invers cu culoarea sistem COLOR_HIGHLIGHT. Windows suporta un numar de variatii de la standardul initial, variatii ce permit selectii multiple, afisarea pe mai multe coloane, list box-uri desenate de proprietar, afisare de imagini in locul textului.

Crearea unui List Box

Urmatoarea instructiune creaza un list box din obiectul CListBox numit m_wndListBox:

m_wndListBox.Create (WS_CHILD ¦ WS_VISIBLE ¦ LBS_STANDARD, rect, this, IDC_LISTBOX);

LBS_STANDARD combina the stilurile WS_BORDER, WS_VSCROLL, LBS_NOTIFY, si LBS_SORT pentru a crea un list box care are margini, o bara de scroll verticala, care notifica parintilor sai cind selectia s-a schimbat sau s-a facut dublu clic pe un articol, si ca articolele vor fi sortate in ordine alfabetica. Implicit bara de scroll este vizibila numai cind articolele nu pot fi afisate in intregime in fereastra controlului. Pentru a face ca bara de scroll sa fie afisata tot timpul va trebui sa includem stilul LBS_DISABLENOSCROLL. A list box doesn't have a vertical scroll bar unless the style WS_VSCROLL or LBS_STANDARD is included. Putem crea list box-uri care cuprind toata zona client. List box-urile au incapsulata interfata cu tastatura (tastele sageti, page up, down, apasarea unui caracter muta selectia pe articolul care incepe cu acel caracter. Apasarea barei de spatiu face sa avem selectie multipla sau nu (on/off).Putem programa interfata cu tastatura prin includerea stilului LBS_WANTKEYBOARDINPUT si procesarea mesajelor WM_VKEYTOITEM si WM_CHARTOITEM. O aplicatie MFC poate mapa aceste mesaje cu fct. OnVKeyToItem si OnCharToItem folosind macro-urile ON_WM_VKEYTOITEM si ON_WM_CHARTOITEM. O clasa list box derivata poate manipula aceste mesaje singura prin suprascrierea functiilor virtuale CListBox::VKeyToItem si CListBox::CharToItem. O utilizarae a acestei proprietati este de a crea clase list box-uri self-contained list box class , care raspund la Ctrl-D prin stergerea articolului curent selectat.

List Box StylesStyle Description

LBS_STANDARD Creates a "standard" list box that has a border and a vertical scroll bar, notifies its parent window when the selection changes or an item is double-clicked, and sorts items alphabetically.

LBS_SORT Sorts items that are added to the list box.LBS_NOSEL Creates a list box whose items can be viewed but not selected.LBS_NOTIFY Creates a list box that notifies its parent when the selection changes or

an item is double-clicked.LBS_DISABLENOSC ROLL Disables the list box's scroll bar when it isn't needed. Without this style,

an unneeded scroll bar is hidden rather than disabled.LBS_MULTIPLESEL Creates a multiple-selection list box.LBS_EXTENDEDSEL Adds extended selection support to a multiple-selection list box.LBS_MULTICOLUMN Creates a multicolumn list box.LBS_OWNERDRAWVARIABLE Creates an owner-draw list box whose items can vary in height.LBS_OWNERDRAWFIXED Creates an owner-draw list box whose items are the same height.LBS_USETABSTOPS Configures the list box to expand tab characters in item text.LBS_NOREDRAW Creates a list box that doesn't automatically redraw itself when an item

Page 61: Programare VISUAL C++

is added or removed.LBS_HASSTRINGS Creates a list box that "remembers" the strings added to it.

Conventional list boxes have this style by default; owner-draw list boxes don't.

LBS_WANTKEYBOARDINPUT Creates a list box that sends its parent a WM_VKEYTOITEM or WM_CHARTOITEM message when a key is pressed. This style is used to customize the list box's response to keyboard input.

LBS_NOINTEGRALHEIGHT Allows a list box to assume any height. By default, Windows sets a list box's height to a multiple of the item height to prevent items from being partially clipped.

Pentru ca fontul implicit pe care W il foloseste pt list box-uri este proportional spatiat, virtual este imposibil de a alinia coloanele prin spatii. O modalitate de a crea liste ce contin informatii pe mai multe coloane este sa folosim SetFont pt a aplica un font fixed-pitch la un list box. O solutie mai buna este de a asigna list box0urilor stilul LBS_USETABSTOPS si de a separa coloanele de informatii cu tab. Un list box cu stilul LBS_USETABSTOPS trateaza caracterele tab ca un procesor de texte. Implicit tab este de marimea a 8 caractere pentru latime. Putem schimba acest lucru cu fct. CListBox::SetTabStops. SetTabStops masoara distanta in unitati de dialog = o patrime din latimea unui caracter in fontul sistem.Instructiunea: m_wndListBox.SetTabStops (64);

pune spatiul dintre tab-uri la 64 unitati de dialog , and

int nTabStops[] = { 32, 48, 64, 128 };m_wndListBox.SetTabStops (4, nTabStops);

plaseaza stop tab-uri la 32, 48, 64, si 128 unitati de dialog fata de marginea din stinga.Implicit un list box se redeseneaza singur cind este adaugat/sters un articol. Pentru a impiedica acest lucru putem seta stilul LBS_NOREDRAW. O asemenea lista va fi redesenata cind zona ei client va fi invalidata.O alta alternativa este de a inhiba procesul de actualizare cu LBS_NOREDRAW si a-l reactiva dupa ce ultimul articol din list box a fost adaugat. Putem face redesenarea enable/disable prin trimiterea mesajului si nu mai este necesar Invalidate()

m_wndListBox.SendMessage (WM_SETREDRAW, FALSE, 0); // Disable redraws. m_wndListBox.SendMessage (WM_SETREDRAW, TRUE, 0); // Enable redraws.

Stilul LBS_MULTIPLESEL este folosit pentru selectii multiple. Cele mai multe list box-uri sunt create cu stilul LBS_EXTENDEDSEL, care permite selectii extinse. Cu un asemenea stil se fac selectii cu ajutorul mouse-ului si a tastei Ctrl (pe sarite) sau Shift (selectie contigua) (se poate combina Ctrl si Shift).Stilul LBS_MULTICOLUMN creaza un list box cu mai multe coloane (implicit 16 car per art.), care in mod normal au si stilul WS_HSCROLL pentru defilare orizontala. List Box-urile multicoloana nu pot avea bara verticala pentru scroll. Latimea coloanei se ajusteaza cu functia CListBox::SetColumnWidth.

Adaugarea si Stergerea articolelor

Articolele sunt adaugate cu fct. CListBox::AddString si CListBox::InsertString. Instructiunea:

m_wndListBox.AddString (string);

adauga un ob. CString la list box. Daca stilul include LBS_SORT, at. art. e pozitionat corespunzator ordinii de sortare alfabetice, altfel este adaugat la sfirsitul listei. InsertString adauga art. la o pozitie indicata de primul parametru al fct. (zero-based index):

m_wndListBox.InsertString (3, string);

LBS_SORT nu are efect asupra stringurilor adaugate cu InsertString.Ambele functii AddString si InsertString intorc pozitia stringului din list box. In caz de esec se returneaza LB_ERRSPACE pentru a indica ca un list box este plin sau LB_ERR pt. a indica ca s-a intimplat altceva din diverse

Page 62: Programare VISUAL C++

motive. Capacitatea unui list box este limitata numai de memoria disponibila. Functia CListBox::GetCount returneaza numarul art. dintr-un list box. Fct. CListBox::DeleteString elimina un articol dintr-un list box, articol identificat prin indexul sau. Intoarce numarul articolelor ramase in list box. Pentru a sterge toate art. fol. fct. CListBox::ResetContent.

Daca dorim sa asociem un pointer pe 32 biti sau o valoare DWORD cu un articol din list box putem folosi fct. CListBox::SetItemDataPtr sau CListBox::SetItemData. Un pointer sau un DWORD asociat cu un art. poate fi regasit cu fct. CListBox::GetItemDataPtr sau CListBox::GetItemData. O folosire a acestei trasaturi este de ex. de a asocia o structura de date –ce contine nr. de telefon – pentru persoanele dintr-un list box. Din cauza ca GetItemDataPtr intoarce un pointer la void trebuie facuta conversia. O alta tehnica este de a asocia extra date – in particular text – cu art. dintr-un list box , s acream un list box cu stilul LBS_USETABSTOPS, sa setam primul tab stop la o pozitie din afara marginii drepte a list box-ului si din a adauga stringuri ce contin car tab urmate de extra data (text). Textul de la dreapta tab-ului va fi invizibil, dar CListBox::GetText va returna intregul text, deci si cel extra.

Cautarea si regasirea articolelor

CListBox::GetCurSel intoarce indexul (0-based) al articolului care este selectat. Daca valoarea returnata este LB_ERR inseamna ca nu s-a selectat nimic. GetCurSel este adesea apelata ca urmare a unei notificari ce semnifica ca selectia s-a schimbat sau a fost facut dublu clic pe un articol. Un program poate seta selectia curenta cu SetCurSel. Pasind valoarea –1 pt. SetCurSel vom deselecta toate articolele. Pentru a gasi daca un art particular este selectat folosim CListBox::GetSel.SetCurSel identifica un articol prin indexul sau, dar art. pot fi selectate si dupa continut cu fct. CListBox::SelectString care realizeaza o singura selectie pentru un articol ce incepe cu textul specificat si selecteaza art. daca se gaseste unul care satisface conditia. Instructiunea

m_wndListBox.SelectString (-1, _T ("Times"));

incepe cautarea cu primul art din list box si va selecta primul art care incepe cu Times. Cautarea nu este case senzitive. Primul parametru indica indexul de unde incepe cautarea; -1 inseamna de la inceput. Indiferent de unde incepe cautarea aceasta poate sa parcurga circular intreaga lista asociata list box-ului daca este necesar. Pentru a cauta pentru un art. particular fara a schimba selectia vom folosi CListBox::FindString sau CListBox::FindStringExact. FindString face cautare numai pe primele caractere din art. Daca se intoarce LB_ERR inseamna ca nu s-a gasit acel art, altfel se intoarce indexul art. FindStringExact adauga in plus cautarea exacta. Cu indexul obtinut anterior putem aobtine textul art. cu CListBox::GetText. Se copie textul art. in var. string.

CString string;int nIndex = m_wndListBox.GetCurSel ();if (nIndex != LB_ERR) m_wndListBox.GetText (nIndex, string);

Al doilea parametru este un pointer la char. Putem folosi CListBox::GetTextLen pentru a determina marimea zonei necesare pentru a primi textul art. inainte de a apela GetText. Selectiile multiple sunt tratate diferit. GetCurSel, SetCurSel, si SelectString nu pot fi folosite in acest caz. Art. sunt selectate (deselectate) cu fct. SetSel si SelItemRange. In continuare se selecteaza art. cu indecsii 0, 5, 6, 7, 8, si 9 si deselecteaza art. 3:

m_wndListBox.SetSel (0);m_wndListBox.SelItemRange (TRUE, 5, 9);m_wndListBox.SetSel (3, FALSE);

Alte functii: GetSelCount pt. determinarea nr. art. selectate, GetSelItems pentru regasirea indecsilor art. selectate. Intr-un list box cu selectie multipla, dreptunghiul ce reprezinta art. cu focus-ul asupra lui, poate fi mutat fara a schimba selectia curenta. Dreptunghiul care are focusul poate fi mutat sau obtinut cu fct. SetCaretIndex si GetCaretIndex. Multe din fct. ce lucreaza cu o singura selectie sunt disponibile si pentru list box-urile cu sele. multipla: GetText, GetTextLength, FindString, si FindStringExact.

Notificarile List Box

Notificarile sunt trimise via mesajul WM_COMMAND. In app. MFC, notificarile list box-urilor sunt mapate la functiile din clasa cu macro-ul ON_LBN….Vezi tabelul de mai jos.

Page 63: Programare VISUAL C++

Notificările LBN_DBLCLK, LBN_SELCHANGE, si LBN_SELCANCEL sunt trimise numai daca list box-ul a fost creat cu stilul LBS_NOTIFY sau LBS_STANDARD.

List Box NotificationsNotification Sent When Message-Map Macro LBS_NOTIFY

Required?LBN_SETFOCUS The list box gains the input focus. ON_LBN_SETFOCUS NoLBN_KILLFOCUS The list box loses the input focus. ON_LBN_KILLFOCUS NoLBN_ERRSPACE An operation failed because of

insufficient memory.ON_LBN_ERRSPACE No

LBN_DBLCLK An item is double-clicked. ON_LBN_DBLCLK YesLBN_SELCHANGE The selection changes. ON_LBN_SELCHANGE YesLBN_SELCANCEL The selection is canceled. ON_LBN_SELCANCEL Yes

Notificarile cele mai folosite sunt: LBN_DBLCLK si LBN_SELCHANGE. Pentru a determina indexul articolului pe care s-a facut dublu clic intr-un list box cu o singura selectie folosim CListBox::GetCurSel. Vezi ex.

// In CMainWindow's message mapON_LBN_DBLCLK (IDC_LISTBOX, OnItemDoubleClicked) void CMainWindow::OnItemDoubleClicked (){ CString string; int nIndex = m_wndListBox.GetCurSel (); m_wndListBox.GetText (nIndex, string); MessageBox (string);}

Pentru un LB cu selectie multipla folosim GetCaretIndex in locul fct. GetCurSel pentru a determina articolul pe care s-a facut dublu clic. Notificarea LBN_SELCHANGE este trimisa cind utilizatorul schimba selectia, dar nu si in cazul cind selectia este schimbata automat prin program. Un LB cu selectie simpla trimite notificarea LBN_SELCHANGE cind selectia se muta din cauza unui clic sau a apasarii unei taste. Intr-un LB cu sel. multipla notificarea LBN_SELCHANGE este trimisa cind se face clic pe un art., cind starea selectiei art. este modificata (on/off) si cind dreptunghiul care are focus-ul este mutat.

Clasa CStatic CStatic, reprez. un control static creat din "STATIC" WNDCLASS.Exista trei tipuri de CS: text (folosit pentru a eticheta alte controale), dreptunghiuri si imagini.

m_wndStatic.Create (_T ("Name"), WS_CHILD ¦ WS_VISIBLE ¦ SS_LEFT, rect, this, IDC_STATIC);

SS_LEFT = aliniaza text in stinga. Daca textul nu incape se continua pe linia urmatoare. Pt. a preveni trecerea textului pe linia urm. putem folosi stilul SS_LEFTNOWORDWRAP in locul lui SS_LEFT. Alte stiluri: SS_CENTER sau SS_RIGHT. Stilul SS_SIMPLE este asemanator cu SS_LEFT dar creaza un control al carui text poate fi modificat cu CWnd::SetWindowText.Pt. a centra vertical textul facem OR pe flagul SS_CENTERIMAGE. (SS_SUNKEN pentru desenare…)CS pt. desenat dreptughiuri. Stilul poate fi ales din urmatoarele:

Style DescriptionSS_BLACKFRAME Hollow rectangle painted in the system color COLOR_WINDOWFRAME

(default = black)SS_BLACKRECT Solid rectangle painted in the system color COLOR_WINDOWFRAME (default

= black)SS_ETCHEDFRAME Hollow rectangle with etched bordersSS_ETCHEDHORZ Hollow rectangle with etched top and bottom bordersSS_ETCHEDVERT Hollow rectangle with etched left and right bordersSS_GRAYFRAME Hollow rectangle painted in the system color COLOR_BACKGROUND (default

Page 64: Programare VISUAL C++

= gray)SS_GRAYRECT Solid rectangle painted in the system color COLOR_BACKGROUND (default =

gray)SS_WHITEFRAME Hollow rectangle painted in the system color COLOR_WINDOW (default =

white)SS_WHITERECT Solid rectangle painted in the system color COLOR_WINDOW (default = white)

Instructiunea:

m_wndStatic.Create (_T (""), WS_CHILD ¦ WS_VISIBLE ¦ SS_ETCHEDFRAME, rect, this, IDC_STATIC);

creaza un CS asemanator cu grup box.Un CS dreptunghi nu afiseaza text. CS pt. imagini formate din bitmap-uri, icoane, cursoare sau metafisiere GDI. Stilurile folosie in acest caz sunt:

Style DescriptionSS_BITMAP A static control that displays a bitmapSS_ENHMETAFILE A static control that displays a metafileSS_ICON A static control that displays an icon or a cursor

Dupa ce se creaza un control static imagine, asociem bitmap, icoana sau cursor cu fct. SetBitmap, SetEnhMetaFile, SetIcon sau SetCursor. Instructiunile:

m_wndStatic.Create (_T (""), WS_CHILD ¦ WS_VISIBLE ¦ SS_ICON, rect, this, IDC_STATIC);m_wndStatic.SetIcon (hIcon);

creaz un CS care afis. o icoana si ii atasam icoana cu SetIcon. Dreptughiul este marit automat pentru a cuprinde imaginea. Exista o serie de falg-uri care pot fi folosite pentru a controla modul de afisare al imaginii in control (SS_CENTERIMAGE = are ca cerinta majora, drept. de afisare tr. sa fie destul de mare pentru a cuprinde imaginea), Implicit, un CS nu trimite mesaje de notificare. Daca se creaza CS cu stilul SS_NOTIFY, at. CS trimite urm. notificari:

Static Control NotificationsNotification Sent When Message-Map Macro

STN_CLICKED The control is clicked. ON_STN_CLICKEDSTN_DBLCLK The control is double-clicked. ON_STN_DBLCLKSTN_DISABLE The control is disabled. ON_STN_DISABLESTN_ENABLE The control is enabled. ON_STN_ENABLE

Notificarile STN_CLICKED si STN_DBLCLK permit crearea de CS care raspund la clic-uri de mouse, ca in ex.:

// In CMainWindow's message mapON_STN_CLICKED (IDC_STATIC, OnClicked) // In CMainWindow::OnCreatem_wndStatic.Create (_T ("Click me"), WS_CHILD ¦ WS_VISIBLE ¦ SS_CENTER ¦ SS_CENTERIMAGE ¦ SS_NOTIFY ¦ SS_SUNKEN, rect, this, IDC_STATIC); void CMainWindow::OnClicked (){ m_wndStatic.PostMessage (WM_CLOSE, 0, 0);}

Page 65: Programare VISUAL C++

Clasa CEdit (CE)CEdit din MFC incapsuleaza functionalitatea unui control de editare folosit pt. a edita text: pe o singura linie sau pe mai multe linii. Zona client din Notepad este un control de editare multilinie. Un control de editare este limitat la 60 KB text. Daca e nevoie de mai mult text vom folosi un control RICH EDIT (imbogatit).

Crearea unui control Edit

Daca m_wndEdit este un obiect CEdit instructiunea

m_wndEdit.Create (WS_CHILD ¦ WS_VISIBLE ¦ WS_BORDER ¦ ES_AUTOHSCROLL, rect, this, IDC_EDIT);

creaza un control single line care face scroll orizontal automat, daca textul nu incape in zona de afisare. Incluzind stilul ES_MULTILINE vom crea un CE multilinie:

m_wndEdit.Create (WS_CHILD ¦ WS_VISIBLE ¦ WS_BORDER ¦ WS_HSCROLL ¦ WS_VSCROLL ¦ ES_MULTILINE, rect, this, IDC_EDIT);

WS_HSCROLL si WS_VSCROLL adauga barele de scroll vertical si orizontal. Putem folosi CEdit::SetRect sau CEdit::SetRectNP pt. a defini zona editabila a controlului independent de marginile controlului. O utilizare pt. aceste functii este de a defini marimea paginii care ramine constanta chiar daca controlul este redimensionat. Putem folosi de asemenea CEdit::SetMargins pentru a specifica latimea (in pixeli) marginii stg si dreapta. Implicit latimile marginilor sunt 0. Cind este prima data creat, un CE va accepta numai 30,000 caractere. Putem modifica acest lucru cu CEdit::LimitText sau CEdit::SetLimitText. Urmatoarea instructiune seteaza nr. max de car. la 32:

m_wndEdit.SetLimitText (32);

Cind folosim un CE multilinie, SetLimitText limiteaza cantitatea totala de text din control, deci nu lungimea fiecarei linii. In acest caz putem controla lungimea liniei numai manual.O metoda este de a folosi SetFont pt a comuta fontul CE la un font fixed-pitch si CEdit::SetRect pt a specifica dreptunghiul de formatare a carui latime este un pic mai mare decit latimea caracterelor din font inmultita cu nr. de caractere dorit a se afisa pe o linie.

Edit Control StylesStyle Description

ES_LEFT Left-aligns text in the control.ES_CENTER Centers text in the control.ES_RIGHT Right-aligns text in the control.ES_AUTOHSCROLL Permits the edit control to scroll horizontally without a horizontal scroll bar. To

add a horizontal scroll bar, include the style WS_HSCROLL.ES_AUTOVSCROLL Permits the edit control to scroll vertically without a vertical scroll bar. To add a

vertical scroll bar, include the style WS_VSCROLL.ES_MULTILINE Creates a multiline edit control.ES_LOWERCASE Displays all characters in lowercase.ES_UPPERCASE Displays all characters in uppercase.ES_PASSWORD Displays asterisks instead of typed characters.ES_READONLY Creates an edit control whose text can't be edited.ES_NOHIDESEL Prevents the edit control from hiding the selection when the control loses the input

focus.ES_OEMCONVERT Performs an ANSI-to-OEM-to-ANSI conversion on all characters typed into the

control so that the application won't get unexpected results if it performs an ANSI-to-OEM conversion of its own. Obsolete.

ES_WANTRETURN Programs the Enter key to insert line breaks instead of invoking the default push button for multiline edit controls used in dialog boxes.

Page 66: Programare VISUAL C++

O alta functie folositoare pt a initializa un CE este CEdit::SetTabStops, care seteaza spatiile dintre tab_uri. Implicit tab stopul este de 8 caractere. Ca si CListBox::SetTabStops, CEdit::SetTabStops masoara distanta in unitati de dialog.

Inserarea si Regasirea Textului

Textul se insereaza cu SetWindowText si se regaseste cu GetWindowText. CEdit mosteneste ambele functii din clasa de baza CWnd. Instructiunea

m_wndEdit.SetWindowText (_T ("Hello, MFC"));

insereaza textul "Hello, MFC" in controlul m_wndEdit, si

m_wndEdit.GetWindowText (string);

regaseste textul intr-un obiect CString numit string. GetWindowText si SetWindowText lucreaza cu ambele tipuri de controale, single line si multiline. Textul inserat cu SetWindowText inlocuieste textul existent, iar GetWindowText returneaza tot textul din control, chiar daca acesta este pe mai multe linii. Pt. a stegte textul apelam SetWindowText cu un sir nul:

m_wndEdit.SetWindowText (_T (""));

Putem insera text fara a stege cel existent cu CEdit::ReplaceSel. Daca unul sau mai multe caractere sunt selectate cind apelam ReplaceSel, textul care se insereaza inlocuieste textul selectat, in caz contrar textul este inserat la pozitia curenta a cursorului (caret-ului). Un control multiline insereaza line break automat. Daca dorim sa determinam line break-urile dintr-un text folosim CEdit::FmtLines pt a face enable soft line breaks inainte de apelul lui GetWindowText:

m_wndEdit.FmtLines (TRUE);

Cu soft line breaks enabled, fiecare linie este delimitata cu doua CR (0x13) urmat de un LF (0x10). Pt. a invalida soft line break folosim FmtLines( FALSE):

m_wndEdit.FmtLines (FALSE);

CR introdus in text la apasarea tastei <Enter> sunt semnificate de o pereche CR/LF. FmtLines nu afecteaza modul de afisare al textului intr-un CE multilinie, ci afecteaza numai modul cum este memorat intern textul si formateaza textul regasit cu GetWindowText.Pentru a citi exact o linie de text dintr-un control multilinie folosim CEdit::GetLine. GetLine copie continutul unei linii intr-un buffer pe care trebuie sa-l alocam si apoi furnizam functiei adresa acestuia. Linia este identificata de un index 0-based. Instructiunea:

m_wndEdit.GetLine (0, pBuffer, nBufferSize);

copie prima linie din control in zona data de pBuffer, iar par. 3 indica dimensiunea buff. in bytes. GetLine returneaza numarul de octeti copiati in buffer. Putem determina dinaninte marimea necesara a buff. cu fct. CEdit::LineLength, iar numarul de linii din control il det. cu fct. CEdit::GetLineCount. GetLineCount nu returneaza niciodata 0, chiar daca nu exista text valoarea returnata este 1.

Clear, Cut, Copy, Paste, and Undo

CEdit furnizeaza functii pentru operatiile enumerate mai sus. Instructiunea:

m_wndEdit.Clear ();

sterge textul selectat fara a afecta continutul clipboard-ului.Instructiunea:

m_wndEdit.Cut ();

sterge textul selectat si il copie in clipboard.Instructiunea:

Page 67: Programare VISUAL C++

m_wndEdit.Copy ();

copie textul selectat in clipboard fara a-l sterge.Putem interoga CE pt. selectia curenta cu un apel al fct. CEdit::GetSel, care returneza o valoare DWORD ce contine doi intregi pe 16 biti ce specifica indexul de inceput si indexul de sfarsit al selectiei. Daca indecsii sunt egali nu exista text selectat. Exista o forma a fct. GetSel care copie indecsii in doi intregi ale caror adrese sunt pasate ca parametrii prin referinta. Putem adauga urmatoare functie IsTextSelected, la clasa controlului de editare derivat din CEdit pentru a determina daca exista sau nu text selectat in control:

BOOL CMyEdit::IsTextSelected (){ int nStart, nEnd; GetSel (nStart, nEnd); return (nStart != nEnd);}

CEdit::Cut and CEdit::Copy nu fac nimic daca nu este text selectat. Textul poate fi selectat prin program cu CEdit::SetSel. Instructiunea:

m_wndEdit.SetSel (100, 150);

selecteaza 50 de caractere incepind cu al 101-lea caracter si o face vizibila in view daca aceasta nu este vizibila (se face scroll automat). Pt. a preveni defilarea (scrolling), vom folosi si al 3-lea param. al functiei cu valoarea TRUE.Cind facem selectii prin program intr0un control multilinie, este necesar adesea sa convertim un numar de linie si posibil un offset din interiorul acestei linii intr-un index pe care-l vom folosi in SetSel. Functia CEdit::LineIndex accepta un numar de linie 0-based si returneaza indexul primului caracter din acea linie. In ex. care urmeaza se determina index-ul primului caracter din linia 8 (LineIndex), apoi determinam lungimea liniei si selectam tot textul care se gaseste in acea linie (SetSel):

int nStart = m_wndEdit.LineIndex (7);int nLength = m_wndEdit.LineLength (nStart);m_wndEdit.SetSel (nStart, nStart + nLength);

CEdit furnizeaza fct. LineFromChar pt. a calcula numarul liniei plecind de la index-ul unui caracter.CEdit::Paste pastes text intr-un CE.

m_wndEdit.Paste ();

Daca clipboard-ul nu contine text, CEdit::Paste nu are efect. Daca nu exista text selectat cind se apeleaza Paste se insereaza textul din clipboard la pozitia curenta a caret-ului. Daca exista o selectie, atunci textul din clipboard inlocuieste selectia existenta. Putem determina din timp daca exista text in clipboard printr-un apel al fct. ::IsClipboardFormatAvailable. Instructiunea:

BOOL bCanPaste = ::IsClipboardFormatAvailable (CF_TEXT);

seteaza bCanPaste la o val. # 0 daca exista text in clipboard sau 0 in caz contrar. O alta trasatura a unui CE este posibilitatea roll back-ului (undo). to nonzero if text is available from the clipboard, and 0 if it isn't, reface ultima stergere:.Edit controls also feature a built-in undo capability that "rolls back" the previous editing operation. The statement

m_wndEdit.Undo ();

undoes the last operation, provided that the operation can be undone. Se poate determina din timp daca am putea apela You can determine ahead of time whether calling Undo prinapelul fct. will accomplish anything with CEdit::CanUndo. O alta fct.A related function, CEdit::EmptyUndoBuffer, reseteaza manual flag-ul pentru undo, a.i., urmatoarele apeluri la Undo nu vor face nimic. manually resets the undo flag so that subsequent calls to Undo will do nothing (and calls to CanUndo will return FALSE) until another editing operation is performed.

Notificarile Controlului de EditareEdit Control Notifications

Page 68: Programare VISUAL C++

Edit controls send notifications to their parents to report various input events. In app. MFC, notificarile sunt mapate cu macro-uri de forma In MFC applications, these notifications are mapped to handling functions with ON_EN in harta de mesaje a clasei.message map macros. Edit control notifications and the corresponding message map macros are summarized in the table below.A common use for EN_CHANGE notifications is to dynamically update other controls as text is entered into an edit control.In ex. urm. se trateaza notificarea (mesaj) EN_CHANGE a unui CE. Un control de tip push buton (m_wndPushButton) este facut enable/disable dupa cum exista/nu exista text in CE cu ID=IDC_EDIT si dat de ob. The following code updates a push button (m_wndPushButton) as text is entered into an edit control (m_wndEdit), ID=IDC_EDIT) : so that the push button is enabled if the edit control contains at least one character and disabled if it doesn't:

// In CMainWindow's message mapON_EN_CHANGE (IDC_EDIT, OnUpdatePushButton) void CMainWindow::OnUpdatePushButton (){ m_wndPushButton.EnableWindow (m_wndEdit.LineLength ());}

Edit Control NotificationsNotification Sent When Message-Map Macro

EN_UPDATE The control's text is about to change. ON_EN_UPDATEEN_CHANGE The control's text has changed. ON_EN_CHANGEEN_KILLFOCUS The edit control loses the input focus. ON_EN_KILLFOCUSEN_SETFOCUS The edit control receives the input focus. ON_EN_SETFOCUSEN_HSCROLL The edit control is scrolled horizontally using a scroll bar. ON_EN_HSCROLLEN_VSCROLL The edit control is scrolled vertically using a scroll bar. ON_EN_VSCROLLEN_MAXTEXT A character can't be entered because the edit control

already contains the number of characters specified with CEdit::LimitText or CEdit::SetLimitText. This notification is also sent if a character can't be entered because the caret is at the right or the bottom edge of the control's formatting rectangle and the control doesn't support scrolling.

ON_EN_MAXTEXT

EN_ERRSPACE An operation fails because of insufficient memory. ON_EN_ERRSPACE

Providing interactive feedback of this nature is generally considered good user interface design. Most users would rather see a button remain disabled until all of the required information is entered than click a button and receive an error message.

Clasa The CComboBox Class(control combo box)Un CB este format dintr-un CE si un LB. The combo box combines a single-line edit control and a list box into one convenient package. Tipuri de Combo boxes-uri: come in three varieties: simple, drop-down, andsi drop-down list. Figure 7-7 shows a drop-down list combo box with its list displayed.

CB simple (stil CBS_SIMPLE) sunt cele mai putin folosite. Trasatura principala a acestora este ca sunt permanent afisate. Cind un utilizator selecteaza un art din lista, ac. art. este automat copiat in CE. Utilizatorul poate tipari text direct in CE. Daca textul se potriveste cu un art din lista, art este automat pus pe video invers si se executa scroll-ul .

Un CB Simple combo boxes are the least used of the three combo box types. A simple combo box's list box is permanently displayed. When the user selects an item from the list, that item is automatically copied to the edit control. The user can also type text directly into the edit control. If the text the user enters matches an item in the list box, the item is automatically highlighted and scrolled into view.A drop-down (stil CBS_DROPDOWN) combo box differsera de un CB simplu prin aceea ca lista este afisata numai la cererea utilizatorului si nu permite introducerea de text in CE asociat. from a simple combo box in that its list box is displayed only on demand. A drop-down list combo box works the same way but doesn't allow text to be typed into the edit control.

Page 69: Programare VISUAL C++

Un CB drop-down list (stil CBS_DROPDOWNLIST) are in plus fata de CB drop-down bara de navigare verticala.Stilurile se dau in functia This restriction effectively limits the user's selection to items appearing in the list box.The style flags you pass to Create or CreateEx .determine what type of combo box you create. CBS_SIMPLE creates a simple combo box, CBS_DROPDOWN creates a drop-down combo box, and CBS_DROPDOWNLIST creates a drop-down list combo box. Alte stiluri exista pentru cosmetizarea CB. Other styles control additional aspects of the combo box's appearance and behavior, as shown in the table below. Many of these styles will look familiar because they're patterned after list box and edit control styles. CBS_AUTOHSCROLL, for example, does the same thing for the edit control portion of a combo box control that ES_AUTOHSCROLL does for a stand-alone edit control. Cind cream un CB trebuie sa punem stilul When you create a combo box control, don't forget to include the style WS_VSCROLL daca dorim scroll vertical if you want the list box to have a vertical scroll bar and WS_BORDER if you want the control's border to be visible. IfDaca m_wndComboBox iseste un a obiect CComboBox object, the statementinstructiunea:

m_wndComboBox.Create (WS_CHILD ¦ WS_VISIBLE ¦ WS_BORDER ¦ WS_VSCROLL ¦ CBS_DROPDOWNLIST ¦ CBS_SORT, rect, this, IDC_COMBOBOX);

ceara un CB drop-down list care contine bara pt. scroll vertical.creates a drop-down list combo box whose list box contains a vertical scroll bar when the number of items in the list box exceeds the number of items that can be displayed and that automatically sorts the items added to it. Dimensiunea controlului (dreptunghiul) trebuie sa fie destul de mare pt. a afisa tot textul. The control rectangle you specify in the call to CComboBox::Create should be large enough to encompass the list box part of the control as well as the edit box.

Combo Box StylesStyle Description

CBS_AUTOHSCROLL Enables horizontal scrolling in the edit control portion of a combo box.

CBS_DISABLENOSCROLL Disables the combo box list box's scroll bar when it isn't needed. Without this style, an unneeded scroll bar is hidden rather than disabled.

CBS_DROPDOWN Creates a drop-down combo box.CBS_DROPDOWNLIST Creates a drop-down list combo box.CBS_HASSTRINGS Creates a combo box that "remembers" the strings added to it.

Conventional combo boxes have this style by default; owner-draw combo boxes don't.

CBS_LOWERCASE Forces all text in the combo box to lowercase.CBS_NOINTEGRALHEIGHT Prevents the combo box's list box height from having to be an exact

multiple of the item height.CBS_OEMCONVERT A combo box whose edit control performs an ANSI-to-OEM-to-

ANSI conversion on all characters so that the application won't get unexpected results if it performs an ANSI-to-OEM conversion of its own. Obsolete.

CBS_OWNERDRAWFIXED Creates an owner-draw combo box whose items are all the same height.

CBS_OWNERDRAWVARIABLE Creates an owner-draw combo box whose items can vary in height.CBS_SIMPLE Creates a simple combo box.CBS_SORT Automatically sorts items as they are added.CBS_UPPERCASE Forces all text in the combo box to uppercase.

Exista asemanari intre fct din CE si LB.Not surprisingly, the list of CComboBox member functions reads a lot like the list of member functions for CEdit and CListBox.Adaugare art se face cu Items are added to a combo box, for example, with CComboBox::AddString andsi CComboBox::InsertString., and Nr. max. de car. pt. CE al CB este setat cu the maximum character count for a combo box's edit control is set with CComboBox::LimitText. Fct. The GetWindowText andsi SetWindowText functions that lucreaza pt. CE al CB. CComboBox inherits from CWnd get and set the text in the edit control. Functii specifice:Functions unique to combo boxes include GetLBText, care regaseste textul unui art. identificat printr-un index 0-based.

Page 70: Programare VISUAL C++

which retrieves the text of an item identified by a 0-based index; GetLBTextLen, returneaza lung. unui art., in caractere;which returns the length of an item, in characters; ShowDropDown, afiseaza sau ascunde un CB which hides or displays the drop-down list; box; and GetDroppedState, whichreturneaza o valoare ce indica daca CB returns a value indicating whether the drop-down list este afisat. is currently displayed.

Notificari Combo Box Notifications

Combo boxes send notifications to their parents much as edit controls and list boxes do. The following table lists the notifications the parent can expect, the corresponding MFC message-map macros, and the types of combo boxes the notifications apply to.Combo Box Notifications

Notification Message-Macro Map Simple

Drop-Down

Drop-Down List

CBN_DROPDOWNSent when the drop-down list is displayed.

ON_CBN_DROPDOWN √ √

CBN_CLOSEUPSent when the drop-down list is closed.

ON_CBN_CLOSEUP √ √

CBN_DBLCLKSent when an item is double-clicked.

ON_CBN_DBLCLK √

CBN_SELCHANGESent when the selection changes.

ON_CBN_SELCHANGE √ √ √

CBN_SELENDOKSent when a selection is made.

ON_CBN_SELENDOK √ √ √

CBN_SELENDCANCELSent when a selection is canceled.

ON_CBN_SELENDCANCEL √ √

CBN_EDITUPDATESent when the text in the edit control is about to change.

N_CBN_EDITUPDATE √ √

CBN_EDITCHANGESent when the text in the edit control has changed.

ON_CBN_EDITCHANGE √ √

CBN_KILLFOCUSSent when the combo box loses the input focus.

ON_CBN_KILLFOCUS √ √ √

CBN_SETFOCUSSent when the combo box receives the input focus.

ON_CBN_SETFOCUS √ √ √

CBN_ERRSPACESent when an operation fails because of insufficient memory.

ON_CBN_ERRSPACE √ √ √

Nu toate notificarile se aplica la toate tipurile de CB. Not all notifications apply to all combo box types. Notificarile CBN_DROPDOWN andsi CBN_CLOSEUP notifications, for example, nu sunt trimise la un CB simpla aren't sent to (CBS_SIMPLE) pt. ca un asemenea CB este deschis tot timpul. combo boxes because a simple combo box's list box doesn't open and close. By the same token,CB cu stilurile CBS_DROPDOWN andsi CBS_DROPDOWNLIST-style combo boxes don't receive nu primesc notificarea CBN_DBLCLK pt. ca pe art. din lista nu se poate face dublu clic. (LB asociat CB se inchide dupa primul clic).notifications because the items in their lists can't be double-clicked. (Why? Because the list box closes after the first click.) Notificarile CBN_EDITUPDATE andsi CBN_EDITCHANGE notifications are sunt echivalente cu equivalent to EN_UPDATE andsi EN_CHANGE trime de CE, si notifications sent by edit controls, and CBN_SELCHANGE este la fel cu is to combo boxes as LBN_SELCHANGE pt. LB.is to list boxes.One nuance you should be aware of Cind procesam when processing notificarea CBN_SELCHANGE notifications is that when a notification arrives, CE asociat poate sa nu fie actualizat cu selectia din LB asociat. the edit control might not have been updated to match the list box selection. Therefore, you should use Va trebui sa folosim GetLBText pentru a regasi noul text selectat in loc de to retrieve the newly selected text instead of GetWindowText. Indexul art. selectat il gasim cu You can get the index of the selected item with CComboBox::GetCurSel.

Page 71: Programare VISUAL C++

Clasa The CScrollBar ClassMFC's Clasa CScrollBar incapsuleaza controlul scroll bar creat din class encapsulates scroll bar controls created from the "SCROLLBAR" WNDCLASS. Controalele scroll bar sunt identice in cea mai mare parte cu “ferestrele” scroll bar. In timp ce ferestrele Scroll bar controls are identical in most respects to the "window" scroll bars used in Chapter 2's Accel application. But whereas window scroll bars sunt create cu stilurile are created by adding WS_VSCROLL andsi WS_HSCROLL flags to the window style, controalele scroll bar controls sunt create explicit cu are created explicitly with CScrollBar::Create. Barele scroll window sint lipite de fereastra principala, controalele scroll bar pot fi plasate oriunde in fereastra si pot fi setate la orice latime si inaltime. And though a window scroll bar runs the full length of the window's client area and is inherently glued to the window border, scroll bar controls can be placed anywhere in the window and can be set to any height and width.

You create vertical scroll bars by specifying the styleStil SBS_VERT pentru CSB vertical si and horizontal scroll bars by specifying SBS_HORZ pentru orizontal. IfDaca m_wndVScrollBar andsi m_wndHScrollBar sunt obiecte are CScrollBar objects, the statements instructiunile:

m_wndVScrollBar.Create (WS_CHILD ¦ WS_VISIBLE ¦ WS_BORDER ¦ SBS_VERT, rectVert, this, IDC_VSCROLLBAR);m_wndHScrollBar.Create (WS_CHILD ¦ WS_VISIBLE ¦ WS_BORDER ¦ SBS_HORZ, rectHorz, this, IDC_HSCROLLBAR);

creaza doua controale CSB, unul vertical si unul orizontal. create two scroll bar controls, one vertical and the other horizontal.Obtinerea dim. standard folosite de W se obtin printr-un apel al fct.You can query Windows for the standard width of a vertical scroll bar or the standard height of a horizontal scroll bar with the API ::GetSystemMetrics API function. The following code fragment sets nWidth and nHeight to the system's standard scroll bar width and height:

int nWidth = ::GetSystemMetrics (SM_CXVSCROLL);int nHeight = ::GetSystemMetrics (SM_CYHSCROLL);

Ca alternativa la crearea CSB standard se poate specifica An alternative method for creating a scroll bar with a standard height or width is to specify the style SBS_TOPALIGN, SBS_BOTTOMALIGN, SBS_LEFTALIGN, or SBS_RIGHTALIGN when creating it. SBS_LEFTALIGN and SBS_RIGHTALIGN align a vertical scroll bar control along the left or right border of the rectangle specified in the call to Create and assign it a standard width. SBS_TOPALIGN and SBS_BOTTOMALIGN align a horizontal scroll bar control along the top or bottom border of the rectangle and assign it a standard height.Unlike the other classic controls, scroll bar controls don't send WM_COMMAND messages; they send WM_VSCROLL and WM_HSCROLL messages instead. MFC applications process these messages with OnVScroll and OnHScroll handlers, as described in Chapter 2. I didn't mention two scroll bar notification codes in Chapter 2 because they apply only to scroll bar controls. SB_TOP means that the user pressed the Home key while the scroll bar had the input focus, and SB_BOTTOM means the user pressed End.MFC's CScrollBar class includes a handful of functions for manipulating scroll bars, most of which should seem familiar to you because they work just like the similarly named CWnd functions. CScrollBar::GetScrollPos and CScrollBar::SetScrollPos get and set the scroll bar's thumb position. CScrollBar::GetScrollRange and CScrollBar::SetScrollRange get and set the scroll bar range. You use CScrollBar::SetScrollInfo to set the range, position, and thumb size in one step. For details, refer to the discussion of CWnd::SetScrollInfo in Chapter 2.

Page 72: Programare VISUAL C++

Status Bars şi Toolbars(Bara de stare şi de instrumente)

Lucrul cu barele de instrumente (Toolbars)Butoanele de pe toolbar corespund la comenzi, la fel ca articolele dintr-un meniu. În general butoanele din toolbar sunt duplicate pentru comenzi din meniu (shortcut-uri). Bara de instrumente poate fi adăugată cu AppWizard. AppWizard crează bara de instrumente pentru cele mai utilizate comenzi din File, Edit şi Help. Rămâne în sarcina noastră să modificăm bara de instrumente după cum dorim.Fiecare buton de pe bara de instrumente are un ID care-l identifică. Pe baza acestui ID vom ataşa o comandă pentru buton.

Ştergerea butoanelor din bara de instrumenteVom exemplifica cele ce urmează pe baza unei aplicaţii, numită Tool, cu arhitectura Document/View şi de tip MDI.AppWizard furnizează o bară de instrumente docking (bara de instrumente nu este fixă, poate fi mutată oriunde în zona client, arată ca o paletă plutitoare) implicit pentru această aplicaţie. Această funcţionalitate este dată de AppWizard şi MFC.

Pentru a şterge un buton din toolbar procedăm în felul următor: 1. selectăm pagina (tab) Resource View 2. expandăm Tool Resources pentru a vedea resursele aplicaţiei3. selectăm Toolbar4. dublu clic pe IDR_MAINFRAME pentru a-l edita.5. ştergerea unui buton se face prin tragerea butonului respectiv din această listă (clic pe buton, se ţine butonul

stâng al mouse-ului apăsat şi se trage butonul in afara toolbar-ului şi apoi eliberarea butonului).

Adăugarea de butoane la Toolbar

Adăugarea se face în doi paşi:1. se desenează icoana butonului selectând butonul alb (blank) din toolbar; desenarea se face folosind

instrumentele puse la dispoziţie de mediul de programare.2. se ataşează o comandă noului buton adăugat; se selectează Properties pentru acest nou buton şi vom indica Id-ul

acestui buton (ID_CIRCLE în acest caz).

Descriere şi ToolTip

În continuare vom (defini) ataşa descrierea butonului şi a ToolTip-ului. Descrierea apare în bara de stare, iar ToolTip-ul apare când mouse-ul rămâne deasupra butonului pentru o secundă sau două. Aceste două şiruri de caractere le vom introduce în boxa de editare Prompt: mai întâi şirul pentru descrierea butonului şi apoi separat cu newline (\n) şirul pentru ToolTip.

Adăugare comandă pentru buton

Se disting două situaţii:1. butonul din toolbar are un articol de meniu corespunzător, adică selectarea butonului sau a articolului de meniu

are acelaşi efect;

Page 73: Programare VISUAL C++

2. butonul din toolbar nu are un articol de meniu corespunzător.

În primul caz vom alege acelaşi ID pentru buton ca şi pentru comanda din meniu. Se presupune că pentru comenzile din meniu am scris codul.

În cel de-al doilea caz vom proceda în felul următor:

1. Selectăm butonul pentru care dorim să creăm tratarea mesajului şi apoi deschidem ClassWizard.

2. Din foia de proprietăţi care apare, cu ID-ul butonului selectat, selectăm in boxa Class Name, clasa la care vom adăuga funcţia, în mod obişnuit clasa pentru vizualizare (derivată din CView sau alte clase din această ierarhie).

3. Executăm dublu clic pe COMMAND din boxa Messages.

4. Acceptăm numele funcţiei propus de MFC şi apoi edităm codul.

Observaţie: Dacă nu se defineşte nici o comandă pentru un buton din toolbar atunci MFC îl va face disabled în timpul execuţiei. Acelaşi lucru este valabil şi pentru o comandă din meniu.

Se va exersa acest lucru la calculator.

Un răspuns posibil pentru butonul cu ID-ul ID_CIRCLE.

Listing CToolView::OnCircle()void CToolView::OnCircle() { CClientDC clientDC(this); CBrush newBrush(RGB(255,0,0)); CBrush* oldBrush = clientDC.SelectObject(&newBrush); clientDC.Ellipse(20, 20, 200, 200); clientDC.SelectObject(oldBrush);}

Recapitulare: Evidenţierea folosirii pensoanelor (clasa CBrush). Ce înseamnă contextul de dispozitiv? Cum se foloseşte? Rolul funcţiei SelectObject(). Alte întrebări legate de contextul de dispozitiv.

Funcţiile membre ale clasei CToolBar Dacă dorim să schimbăm comportarea toolbar-ului sau forma sa de afişare, vom folosi funcţiile din clasa CToolBar. Toolbar-ul este accesibil din clasa CMainFrame, acolo fiind definită şi variabila m_wndToolBarde tip CToolBar. În general schimbarea comportării toolbar-ului se face în funcţia CMainFrame::OnCreate().

Funcţiile membru ale clasei CToolBarFunction DescriptionCommandToIndex() Obtains the index of a button, given its IDCreate() Creates the toolbarGetButtonInfo() Obtains information about a buttonGetButtonStyle() Obtains a button's styleGetButtonText() Obtains a button's text labelGetItemID() Obtains the ID of a button, given its indexGetItemRect() Obtains an item's display rectangle, given its indexGetToolBarCtrl() Obtains a reference to the CToolBarCtrl object represented by the CToolBar objectLoadBitmap() Loads the toolbar's button imagesLoadToolBar() Loads a toolbar resource

Page 74: Programare VISUAL C++

SetBitmap() Sets a new toolbar button bitmapSetButtonInfo() Sets a button's ID, style, and image numberSetButtons() Sets the IDs for the toolbar buttonsSetButtonStyle() Sets a button's styleSetButtonText() Sets a button's text labelSetHeight() Sets the toolbar's heightSetSizes() Sets the button sizes

Lucrul cu bara de stare (Status Bars)Bara de stare constă în afişarea unui text ce descrie comanda ce urmează a fi selectată. De asemenea bara de stare conţine starea unor taste (CAPS, NUM Lock, etc.). Comportamentul barei de stare este încapsulat în clasa CStatusBar. O bară de stare este asemănătoare cu toolbar-ul. Se crează în CMainFrame::Create().

Metodele clasei CStatusBar Method DescriptionCommandToIndex() Obtains an indicator's index, given its IDCreate() Creates the status barGetItemID() Obtains an indicator's ID, given its indexGetItemRect() Obtains an item's display rectangle, given its indexGetPaneInfo() Obtains information about an indicatorGetPaneStyle() Obtains an indicator's styleGetPaneText() Obtains an indicator's textGetStatusBarCtrl() Obtains a reference to the CStatusBarCtrl object represented by the CStatusBar objectSetIndicators() Sets the indicators' IDsSetPaneInfo() Sets the indicators' IDs, widths, and stylesSetPaneStyle() Sets an indicator's styleSetPaneText() Sets an indicator's text

Bara de stare conţine mai multe părţi, numite panes, care afişează informaţii despre starea aplicaţiei şi a sistemului.

Metoda cea mai folosită pentru a personaliza bara de stare, este de adăuga noi părţi. Pentru a face acest lucru procedăm astfel:

1. Creăm un ID pentru o comandă pentru noul pane.

2. Creăm un string implicit pentru acest pane.

3. Adăugăm comanda ID a pane-ului la tabloul (array) indicatorilor barei de stare.

4. Creăm funcţia corespunzătoare pentru acest ID.Explicăm aceste lucruri în detaliu.

Crearea unui ID pentru o nouă comandă

Pentru a adăuga un ID pentru o comandă (definirea ID-ului) procedăm astfel: View -> Resource Symbols -> New Symbol şi apoi tastăm ID-ul in boxa Name, de ex. ID_MYNEWPANE, apoi OK şi Close.

Crearea stringului implicitPresupunem că am creat ID-ul. Pentru a defini stringul procedăm astfel:

1. selectăm Resource View (din panelul workspace)-> şi apoi dublu clic pe resursa String Table pentru a o deschide;

2. alegem Insert, New String pentru a deschide boxa de dialog String Properties. 3. tastăm ID-ul, ID_MYNEWPANE în boxa ID (sau îl alegem din lista drop-down) şi apoi tastăm textul

(stringul) in boxa Caption .

Page 75: Programare VISUAL C++

Adăugarea Id-ului la tabloul (array) indicatorilorMFC foloseşte un tablou de ID-uri pentru a determina ce paneluri să afişeze. Acest tablou este pasat ca argument în funcţia membru a barei de stare SetIndicators(), care este apelată în funcţia Create() din CMainFrame.

Listing MainFrm.cpp--The Indicator Arraystatic UINT indicators[] ={ ID_SEPARATOR, // status line indicator ID_INDICATOR_CAPS, ID_INDICATOR_NUM, ID_INDICATOR_SCRL,};

Pentru a adăuga noul pane, tastăm ID-ul în acest vector în poziţia în care vrem să apară panelul nou, urmat de virgulă dacă nu este pe ultima poziţie.

Listing MainFrm.cpp—Vectorul indicatorilor după adăugarestatic UINT indicators[] ={ ID_SEPARATOR, // status line indicator ID_MYNEWPANE, // ID adaugat ID_INDICATOR_CAPS, ID_INDICATOR_NUM, ID_INDICATOR_SCRL,};

Crearea funcţiei pentru Update (CCmdUI)MFC nu actualizează (enable) în mod automat noul pane adăgat. Trebuie să creăm o funcţie pentru actualizarea (enable) noului pane. În mod normal se foloseşte ClassWizard pentru a trata mesajele, dar ClassWizard nu face nimic pentru bara de stare. Va trebui să adăgăm manual intrările în harta de mesaje, în fişierul .h şi în în .cpp. Aceste modificări le vom face în afara comentariilor speciale AFX_MSG_MAP folosite de ClassWizard.

Listing MainFrm.h--Message Map// Generated message map functionsprotected: //{{AFX_MSG(CMainFrame) afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); // NOTE - the ClassWizard will add and remove member functions here. // DO NOT EDIT what you see in these blocks of generated code! //}}AFX_MSG afx_msg void OnUpdateMyNewPane(CCmdUI *pCmdUI); DECLARE_MESSAGE_MAP()

Listing MainFrm.cpp--Message MapBEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) //{{AFX_MSG_MAP(CMainFrame) // NOTE - the ClassWizard will add and remove mapping macros here. // DO NOT EDIT what you see in these blocks of generated code ! ON_WM_CREATE() //}}AFX_MSG_MAP ON_UPDATE_COMMAND_UI(ID_MYNEWPANE, OnUpdateMyNewPane)END_MESSAGE_MAP()

Listing CMainFrame::OnUpdateMyNewPane()void CMainFrame::OnUpdateMyNewPane(CCmdUI *pCmdUI) {

Page 76: Programare VISUAL C++

pCmdUI->Enable(); pCmdUI->SetText(m_paneString); }

m_paneString conţine stringul (este de tip CString).

Modificarea stringului pentru m_paneString

Se adaugă data membru la clasa CMainFrame:

private:CString m_paneString;

Prima varianta:Se iniţializează această variabilă în constructorul clasei CMainFrame:

m_paneString = "Default string";

Pentru a afişa bara de stare pentru prima dată, se adaugă următoarele linii de cod în CMainFrame::OnCreate(), înainte instrucţiunea return:

CClientDC dc(this); SIZE size = dc.GetTextExtent(m_paneString); int index = m_wndStatusBar.CommandToIndex(ID_MYNEWPANE); m_wndStatusBar.SetPaneInfo(index,ID_MYNEWPANE, SBPS_POPOUT, size.cx);

Acest cod setează stringul şi mărimea panelului. Setarea mărimii panelului se face printr-un apel la funcţia SetPaneInfo(), care are nevoie de indexul panelului, ID-ul, stilul de afişare şi mărimea acestui panel. CommandToIndex() obţine indexul panelului având ca parametru ID-ul panelului. şi GetTextExtent() obţine mărimea, calculată conform fontului utilizat, mărimii caracterelor şi lungimii şirului m_paneString.

A doua variantă:Adăugarea unei opţiuni în meniu pentru modificarea conţinutului variabilei m_paneString. De obicei se crează o boxă de dialog în care se preia noua valoare. Acest lucru implică adăugarea unei opţiuni de meniu şi tratarea acesteia (scrierea codului pentru funcţie). De asemenea implică crearea unei boxe de dialog. Se vor explica în detaliu aceste lucruri.

Cod posibil:

Listing CMainFrame::OnFileChangestring()void CMainFrame::OnFileChangestring() { CPaneDlg dialog(this); dialog.m_paneString = m_paneString; int result = dialog.DoModal(); if (result == IDOK) { m_paneString = dialog.m_paneString; CClientDC dc(this); SIZE size = dc.GetTextExtent(m_paneString); int index = m_wndStatusBar.CommandToIndex(ID_MYNEWPANE); m_wndStatusBar.SetPaneInfo(index, ID_MYNEWPANE, SBPS_POPOUT, size.cx); }}

De observat asemănarea codului cu cel din prima variantă.

Lucrul cu RebarRebar sunt toolbar-uri care conţin alte controale decât butoanele toolbar.

Page 77: Programare VISUAL C++

Ca exemplificare vom adăuga un check box care este reprezentat de clasa CButton.

Etape:

În CMainFrame (.h) adăugăm:

public:CReBar m_rebar;CButton m_check;

În CMainFrame::Create() adăugăm la sfârşit următoarele linii:

if (!m_rebar.Create(this) ) { TRACE0("Failed to create rebar\n"); return -1; // fail to create }

Controlul check box are nevoie de un ID.Selectăm View->Resource Symbols->New şi tastăm IDC_CHECK. Acceptăm această alegere. Se adaugă astfel o linie în resource.h şi această resursă nu poate fi utilizată şi de alte controale.

Din nou în CMainFrame::OnCreate(), adăugăm următoarele linii pentru a crea check box-ul:

if (!m_check.Create("Check Here", WS_CHILD|WS_VISIBLE|BS_AUTOCHECKBOX, CRect(0,0,20,20), this, IDC_CHECK) ) { TRACE0("Failed to create checkbox\n"); return -1; // fail to create }

În final, se adaugă acest control la rebar:

m_rebar.AddBar(&m_check, "On The Bar", NULL, RBBS_BREAK | RBBS_GRIPPERALWAYS);

AddBar() are patru parametri: un pointer la controlul care va fi adăugat, textul afişat alături de control, un pointer la un bitmap pentru a fi folosit pentru imaginea background-ului pe rebar şi stilul rebar-ului.

Alte stiluri sunt:

• RBBS_BREAK puts the band on a new line, even if there's room for it at the end of an existing line. • RBBS_CHILDEDGE puts the band against a child window of the frame. • RBBS_FIXEDBMP prevents moving the bitmap if the band is resized by the user. • RBBS_FIXEDSIZE prevents the user from resizing the band. • RBBS_GRIPPERALWAYS guarantees sizing wrinkles are present. • RBBS_HIDDEN hides the band. • RBBS_NOGRIPPER suppresses sizing wrinkles. • RBBS_NOVERT hides the band when the rebar is vertical. • RBBS_VARIABLEHEIGHT enables the band to be resized by the rebar.

În acest moment aplicaţia funcţionează dar nu se întâplă nimic la selecţie sau deselecţie check box.Pentru ca aplicaţia să reacţioneze la clic pe check box trebuie să prindem mesajul şi să-l tratăm. Cel mai simplu mod pentru a face acest lucru este să schimbăm ceea ce se deseanează în OnDraw() din vizualizare. Adăugăm la clasa vizualizare tratarea mesajului care apare când se face clic pe acest buton. Adăugarea se face manual ca la bara de stare, în afara codului generat de ClassWizard:Înainte de DECLARE_MESSAGE_MAP, adăugăm:

afx_msg void OnClick();

Page 78: Programare VISUAL C++

Adăugăm funcţia la clasa vizualizare:

void CRebarView::OnClick(){ Invalidate();}

Aceasta va apela OnDraw().

În harta de mesaje adăugăm (tot în afara codului generat de ClassWizard):

ON_BN_CLICKED(IDC_CHECK, OnClick)

La începutul fişierului adăugăm:

#include "mainFrm.h"

În OnDraw() adăugăm: CString message; if ( ((CMainFrame*)(AfxGetApp()->m_pMainWnd))->m_check.GetCheck()) message = "The box is checked"; else message = "The box is not checked"; pDC->TextOut(20,20,message);

Nimic mai simplu……..