Vous êtes sur la page 1sur 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.
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.
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.
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
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).
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.
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).
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.
Crearea unei aplicaţii Windows
VC++ 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 compuse


Tehnologia 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
• 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
• 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ţiuni
Dorim 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 claselor


Ultima 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 AppWizard


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

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

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

11
DevStudio Add-In Wizard
Add-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 Wizard


ISAPI 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.

Makefile
Dacă 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 ControlWizard


ActiveX 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 Application
Se crează o aplicaţie obişnuită în C++.

Win32 Console Application


O aplicaţie consolă arată ca o aplicaţie obişnuită DOS.

Win32 Dynamic Link Library


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 Static Library


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

12
Înţelegerea codului generat de AppWizard
Aplicaţie SDI
O 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
#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
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 Action
None 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
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
// 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
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 dialog


AppWizard 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
//{{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
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.
Proceduri fereastră

#include <windows.h>
// ---------------- Apelata pe mesajul WM_PAINT
void 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 fereastra
LRESULT 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 principal
int 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);
// 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);
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
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);
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ă
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.
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()
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 mesaje
BEGIN_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.
Cum lucrează harta de mesaje
Fiecare 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 Type
ABM, 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
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 comenzilor
O 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.
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()
void CWhoisView::OnUpdateEditPaste(CCmdUI* pCmdUI)
{
pCmdUI->Enable(::IsClipboardFormatAvailable(CF_TEXT));
}
Moduri de mapare (mapping mode)

SetMapMOde(hdc, iMapMode);

iMapMode = GetMapMode(hdc);

vezi si GetDeviceCaps(...)

Mod de mapare Unitati logice Axa x Axa y


MM_TEXT Pixel spre dreapta in jos
MM_LOMETRIC 0,1 mm spre dreapta in sus
MM_HIMETRIC 0,01 mm spre dreapta in sus
MM_LOENGLISH 0.01 inci spre dreapta in sus
MM_HIENGLISH 0.001 inci spre dreapta in sus
MM_TWIPS 1/1440 inci spre dreapta in sus
MM_ISOTROPIC arbitrar (x = y) Selectabil selectabil
MM_ANISOTROP Arbitrar (x!=y) Selectabil Selectabil
IC

twips = twentieth of a point = a douazecea parte dintr-un punct


1 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
);

Parameters
hWnd 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
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
);

Parameters
hWndFrom 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 Values
If 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,
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 fereastra
Pt. 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) + xViewOrg


yViewport = (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 POINT


iNumber = r. de puncte care urmeaza sa fie convertite.

Modul MM_TEXT
Fct 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.
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 apasat
MK_RBUTTON Buton dr mouse apasat
MK_MBUTTON
MK_SHIFT Tasta shift apasata
MK_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 apasate
else
Shift apasata
else if (MK_CONTROL & wParam)
Ctrl apasata
else
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.
Tipărire şi Previzualizare
Bazele tipăririi şi previzualizării în MFC
Cerinţ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.

Scalarea
Documentul 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.
Moduri de mapare
Mode Unit X Y
MM_HIENGLISH 0.001 inch Increases right Increases up
MM_HIMETRIC 0.01 millimeter Increases right Increases up
MM_ISOTROPIC User-defined User-defined User-defined
MM_LOENGLISH 0.01 inch Increases right Increases up
MM_LOMETRIC 0.1 millimeter Increases right Increases up
MM_TEXT Device pixel Increases right Increases down
MM_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 pagini


MFC 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);
}
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.
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 originii
Va 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ărirea
Alte funcţii importante în procesul de tipărire sunt:
Printing Functions of a View Class
Function Description
Override this function to create resources, such as fonts, that you need for printing the
OnBeginPrinting()
document. You also set the maximum page count here.
This function serves triple duty, displaying data in a frame window, a print preview
OnDraw()
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().
Override this function to modify the device context used to display or print the document.
OnPrepareDC()
You can, for example, handle pagination here.
Override this function to provide a maximum page count for the document. If you don't set
OnPreparePrinting()
the page count here, you should set it in OnBeginPrinting().
Override this function to provide additional printing services, such as printing headers and
OnPrint()
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 AppWizard
BOOL CPrint1View::OnPreparePrinting(CPrintInfo* pInfo)
{
// 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 Class


Member Description
SetMaxPage() 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.
Gets the document's maximum page number, which may be changed in
GetMaxPage()
OnBeginPrinting().
Gets the document's minimum page number, which may be changed in
GetMinPage()
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 Footers


void 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)
{
// 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.
Meniuri
Se 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 meniu


Metoda 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 DISCARDABLE


BEGIN
POPUP “&File”
BEGIN
MENUITEM “&New\tCtrl+N”, ID_FILE_NEW
...
MENUITEM SEPARATOR
END
POPUP “&View”
BEGIN
...
END
END

Indicatii:
43
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 meniu


In 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
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 meniului


Cind 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 comenzi


Sunt 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
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
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 ACCELERATORS
BEGIN
...
END
In MFC tabela de acceleratori are structura:

IDR_MAINFRAME ACCELERATORS PRELOAD MOVEABLE


BEGIN
“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
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); sau
pMenu->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
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_MEASUREITEM
afx_msg OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpmis);

nIDCtl = ID controlului
typedef struct tagLP{
UINT CtlType; // ODT_MENU
UINT CtlID;
UINT itemID; // ID art meniu
UINT itemWidth; // lat. orizontala in pixeli
UINT itemHeight; // inalt in pixeli
UINT 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
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 meniul
2 = daca W ar trebui sa selecteze unul din art. afisate din meniu
In 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_CONTEXTMENU


Se 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
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
Dialoguri şi Controale
Pentru 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 dialog


Controalele 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 Controlului


Fiecare 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
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.
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.).
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 radio


La 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().
Controale clasice
W 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 Class


Buttons "BUTTON" CButton
List boxes "LISTBOX" CListBox
Edit controls "EDIT" CEdit
Combo boxes "COMBOBOX" CComboBox
Scroll bars "SCROLLBAR" CScrollBar
Static 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;
}
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 Description
BS_PUSHBUTTON Creates a standard push button control
BS_DEFPUSHBUTTON Creates a default push button; used in dialog boxes to identify the push
button that's clicked if Enter is pressed
BS_CHECKBOX Creates a check box control
BS_AUTOCHECKBOX Creates a check box control that checks and unchecks itself when clicked
BS_3STATE Creates a three-state check box control
BS_AUTO3STATE Creates a three-state check box control that cycles through three states—
checked, unchecked, and indeterminate—when clicked
BS_RADIOBUTTON Creates a radio button control
BS_AUTORADIOBUTTON Creates a radio button control that, when clicked, checks itself and
unchecks other radio buttons in the group
BS_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 Description
BS_LEFTTEXT Moves the text accompanying a radio button or check box control from the
button's right (the default) to its left
BS_RIGHTBUTTON Same as BS_LEFTTEXT
BS_LEFT Left justifies the button text in the control rectangle
BS_CENTER Centers the button text in the control rectangle
BS_RIGHT Right justifies the button text in the control rectangle
BS_TOP Positions the button text at the top of the control rectangle
BS_VCENTER Positions the button text in the center of the control rectangle vertically
BS_BOTTOM Positions the button text at the bottom of the control rectangle
BS_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 Push
Un 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); // Check


m_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.)
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.


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 Styles


Style 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
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
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.
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 Notifications


Notification Sent When Message-Map Macro LBS_NOTIFY
Required?
LBN_SETFOCUS The list box gains the input focus. ON_LBN_SETFOCUS No
LBN_KILLFOCUS The list box loses the input focus. ON_LBN_KILLFOCUS No
LBN_ERRSPACE An operation failed because of ON_LBN_ERRSPACE No
insufficient memory.
LBN_DBLCLK An item is double-clicked. ON_LBN_DBLCLK Yes
LBN_SELCHANGE The selection changes. ON_LBN_SELCHANGE Yes
LBN_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 map


ON_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 Description
SS_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 borders
SS_ETCHEDHORZ Hollow rectangle with etched top and bottom borders
SS_ETCHEDVERT Hollow rectangle with etched left and right borders
SS_GRAYFRAME Hollow rectangle painted in the system color COLOR_BACKGROUND (default
= 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 Description
SS_BITMAP A static control that displays a bitmap
SS_ENHMETAFILE A static control that displays a metafile
SS_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 Notifications


Notification Sent When Message-Map Macro
STN_CLICKED The control is clicked. ON_STN_CLICKED
STN_DBLCLK The control is double-clicked. ON_STN_DBLCLK
STN_DISABLE The control is disabled. ON_STN_DISABLE
STN_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 map


ON_STN_CLICKED (IDC_STATIC, OnClicked)

// In CMainWindow::OnCreate
m_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);
}
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 Styles


Style 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.
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:


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


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 map


ON_EN_CHANGE (IDC_EDIT, OnUpdatePushButton)

void CMainWindow::OnUpdatePushButton ()
{
m_wndPushButton.EnableWindow (m_wndEdit.LineLength ());
}

Edit Control Notifications


Notification Sent When Message-Map Macro
EN_UPDATE The control's text is about to change. ON_EN_UPDATE
EN_CHANGE The control's text has changed. ON_EN_CHANGE
EN_KILLFOCUS The edit control loses the input focus. ON_EN_KILLFOCUS
EN_SETFOCUS The edit control receives the input focus. ON_EN_SETFOCUS
EN_HSCROLL The edit control is scrolled horizontally using a scroll bar. ON_EN_HSCROLL
EN_VSCROLL The edit control is scrolled vertically using a scroll bar. ON_EN_VSCROLL
EN_MAXTEXT A character can't be entered because the edit control ON_EN_MAXTEXT
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.
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.
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 Styles


Style 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.
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 Sim Drop- Drop-Down
ple Down List
CBN_DROPDOWN ON_CBN_DROPDOWN √ √
Sent when the drop-down list is displayed.
CBN_CLOSEUP ON_CBN_CLOSEUP √ √
Sent when the drop-down list is closed.
CBN_DBLCLK ON_CBN_DBLCLK √
Sent when an item is double-clicked.
CBN_SELCHANGE ON_CBN_SELCHANGE √ √ √
Sent when the selection changes.
CBN_SELENDOK ON_CBN_SELENDOK √ √ √
Sent when a selection is made.
CBN_SELENDCANCEL ON_CBN_SELENDCANCEL √ √
Sent when a selection is canceled.
CBN_EDITUPDATE N_CBN_EDITUPDATE √ √
Sent when the text in the edit control is
about to change.
CBN_EDITCHANGE ON_CBN_EDITCHANGE √ √
Sent when the text in the edit control has
changed.
CBN_KILLFOCUS ON_CBN_KILLFOCUS √ √ √
Sent when the combo box loses the input
focus.
CBN_SETFOCUS ON_CBN_SETFOCUS √ √ √
Sent when the combo box receives the
input focus.
CBN_ERRSPACE ON_CBN_ERRSPACE √ √ √
Sent when an operation fails because of
insufficient memory.

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.
Clasa The CScrollBar Class
MFC'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.
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 instrumente


Vom 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ţiei
3. selectăm Toolbar
4. 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;
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 CToolBar
Function Description
CommandToIndex() Obtains the index of a button, given its ID
Create() Creates the toolbar
GetButtonInfo() Obtains information about a button
GetButtonStyle() Obtains a button's style
GetButtonText() Obtains a button's text label
GetItemID() Obtains the ID of a button, given its index
GetItemRect() Obtains an item's display rectangle, given its index
GetToolBarCtrl() Obtains a reference to the CToolBarCtrl object represented by the CToolBar object
LoadBitmap() Loads the toolbar's button images
LoadToolBar() Loads a toolbar resource
SetBitmap() Sets a new toolbar button bitmap
SetButtonInfo() Sets a button's ID, style, and image number
SetButtons() Sets the IDs for the toolbar buttons
SetButtonStyle() Sets a button's style
SetButtonText() Sets a button's text label
SetHeight() Sets the toolbar's height
SetSizes() 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 Description
CommandToIndex() Obtains an indicator's index, given its ID
Create() Creates the status bar
GetItemID() Obtains an indicator's ID, given its index
GetItemRect() Obtains an item's display rectangle, given its index
GetPaneInfo() Obtains information about an indicator
GetPaneStyle() Obtains an indicator's style
GetPaneText() Obtains an indicator's text
GetStatusBarCtrl() Obtains a reference to the CStatusBarCtrl object represented by the CStatusBar object
SetIndicators() Sets the indicators' IDs
SetPaneInfo() Sets the indicators' IDs, widths, and styles
SetPaneStyle() Sets an indicator's style
SetPaneText() 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 implicit


Presupunem 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 .
Adăugarea Id-ului la tabloul (array) indicatorilor
MFC 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 Array
static 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ăugare
static 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 functions
protected:
//{{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 Map


BEGIN_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)
{
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 Rebar
Rebar sunt toolbar-uri care conţin alte controale decât butoanele toolbar.
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();


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……..

Vous aimerez peut-être aussi