Sunteți pe pagina 1din 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

– Carti si articole online gratuite de la A la Z Aplicaţii, Fire şi Ferestre O

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

– Carti si articole online gratuite de la A la Z Procese, fire şi ferestre Exista

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.

De exemplu WM_LBUTTONDOWN, WM_MOUSEMOVE,

Marea majoritate a mesajelor încep cu WM 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.);

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.

ar fi utilitarul Character Map. Aplicaţiile nu au meniu. OBSERVAŢIE:: Aplicaţiile bazate pe dialog sunt diferite
ar fi utilitarul Character Map. Aplicaţiile nu au meniu. OBSERVAŢIE:: Aplicaţiile bazate pe dialog sunt diferite
ar fi utilitarul Character Map. Aplicaţiile nu au meniu. OBSERVAŢIE:: Aplicaţiile bazate pe dialog sunt diferite

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

de cele de tip SDI sau MDI. Vor fi tratate în mod separat. Mai există un
de cele de tip SDI sau MDI. Vor fi tratate în mod separat. Mai există un
de cele de tip SDI sau MDI. Vor fi tratate în mod separat. Mai există un

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.

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.

ActiveX se face selectând o altă opţiune din Projects. Etapa 4. Opţiuni pentru interfaţă. Alte Opţiuni
ActiveX se face selectând o altă opţiune din Projects. Etapa 4. Opţiuni pentru interfaţă. Alte Opţiuni
ActiveX se face selectând o altă opţiune din Projects. Etapa 4. Opţiuni pentru interfaţă. Alte Opţiuni

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.

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.

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.

Î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"

/////////////////////////////////////////////////////////////////////////////

// CFirstSDIApp:

// See FirstSDI.cpp for the implementation of this class // class CFirstSDIApp : public CWinApp

// main symbols

{

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.

#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

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

Enable3dControls();

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.

CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(CFirstSDIDoc), RUNTIME_CLASS(CMainFrame), RUNTIME_CLASS(CFirstSDIView)); AddDocTemplate(pDocTemplate);

// main SDI frame window

// 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

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.

obiect.

În

final

se

procesează

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

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 #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:

// 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

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

Enable3dControls();

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),

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 #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

//{{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();

#else

// Call this when using MFC in a shared DLL

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;

}

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 ciclează pe bucla de mesaje.

) practic se

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 către DefWindowProc.

WM_DESTROY. Alte mesaje decât cele indicate sunt tratate de

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:

default:

goto ExitLoop;

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()

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.

care trimite mesaje la procedura fereastră (funcţia) WindProc() şi

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()

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()

END_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*.

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.

o funcţie virtuală a

În continuare el apelează WindowProc(),

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 IC

Arbitrar (x!=y)

Selectabil

Selectabil

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

MapWindowPoints, POINT, ScreenToClient

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, HWND hWndTo, LPPOINT lpPoints, UINT cPoints

// handle of window to be mapped from // handle of window to be mapped to // address of structure array with points to map // 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

is NULL or HWND_DESKTOP, the points are converted to screen coordinates.

lpPoints

be converted. This parameter can also point to a RECT structure, in which case the cPoints parameter should be set to 2.

cPoints

to by the lpPoints parameter.

Identifies the window to which points are converted. If this parameter

Points to an array of POINT structures that contain the set of points to

Specifies the number of POINT structures in the array pointed

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

MK_RBUTTON

MK_MBUTTON

MK_SHIFT

MK_CONTROL

Buton stg mouse apasat Buton dr mouse apasat

Tasta shift apasata

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

aplicaţii tip SDI; Tratarea mesajelor simple de mouse; defilarea orizontală şi/sau verticală (opţional).
aplicaţii tip SDI; Tratarea mesajelor simple de mouse; defilarea orizontală şi/sau verticală (opţional).
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

OnBeginPrinting()

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

OnDraw()

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

OnEndPrinting()

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

OnPrepareDC()

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

OnPreparePrinting()

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

OnPrint()

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

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

print1View.cpp --CPrint1View::OnPreparePrinting() as Generated by 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.

GetMaxPage()

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

GetMinPage()

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

GetToPage()

Gets the number of the last page users selected for printing.

m_bContinuePrinting

Controls the printing process. Setting the flag to FALSE ends the print job.

m_bDirect

Indicates whether the document is being directly printed.

m_bPreview

Indicates whether the document is in print preview.

m_nCurPage

Holds the current number of the page being printed.

m_nNumPreviewPages

Holds the number of pages (1 or 2) being displayed in print preview.

m_pPD

Holds a pointer to the print job's CPrintDialog object.

m_rectDraw

Holds a rectangle that defines the usable area for the current page.

m_strPageDesc

Holds a page-number format string.

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

Versiune posibilă OnPrint() cu Headers şi 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.

ultima pagină, apelează funcţia OnEndPrinting(), unde putem distruge orice resursă de care nu mai avem nevoie.
ultima pagină, apelează funcţia OnEndPrinting(), unde putem distruge orice resursă de care nu mai avem nevoie.
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”,

MENUITEM SEPARATOR

END POPUP “&View” BEGIN

END

END

Indicatii:

ID_FILE_NEW

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”),

MAKEINTRESOURCE(IDR_MAINFRAME));

WS_OVERLAPPEDWINDOW,

rectDefault,

NULL,

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;

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?

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

}

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:

WM_COMMAND.

combinatii

de taste pentru a selecta o comanda. Un accelerator

produce mesajul

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.

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.

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:

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;

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.

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; dlg.m_check = TRUE; dlg.m_edit = "hi there"; CString msg;

if (dlg.DoModal() == IDOK) // are loc afisarea dialogului

// se creaza obiectul pe stiva // variabila atasata unui control check // variabila atasata unui control Edit

{

 

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
OBSERVAŢIE: Anumite funcţii DDX nu sunt generate de Class Wizard. De exemplu, când ne conectăm
OBSERVAŢIE: Anumite funcţii DDX nu sunt generate de Class Wizard. De exemplu, când ne conectăm

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

Box. Indexul pentru primul articol are valoarea 0 (zero). OBSERVAŢIE: În mod obişnuit, articolele în list
Box. Indexul pentru primul articol are valoarea 0 (zero). OBSERVAŢIE: În mod obişnuit, articolele în list
Box. Indexul pentru primul articol are valoarea 0 (zero). OBSERVAŢIE: În mod obişnuit, articolele în list

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
Pentru a menţine (memora) articolul (string-ul) selectat în list box, vom adăuga o variabilă membru
Pentru a menţine (memora) articolul (string-ul) selectat în list box, vom adăuga o variabilă membru

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

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

În final se apelează funcţia OnOK() din clasa de bază pentru a executa alte

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

BS_PUSHBUTTON

BS_DEFPUSHBUTTON

BS_CHECKBOX BS_AUTOCHECKBOX

BS_3STATE

BS_AUTO3STATE

BS_RADIOBUTTON

BS_AUTORADIOBUTTON

BS_GROUPBOX

Description Creates a standard push button control Creates a default push button; used in dialog boxes to identify the push button that's clicked if Enter is pressed Creates a check box control Creates a check box control that checks and unchecks itself when clicked Creates a three-state check box control Creates a three-state check box control that cycles through three states— checked, unchecked, and indeterminate—when clicked Creates a radio button control Creates a radio button control that, when clicked, checks itself and unchecks other radio buttons in the group 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

BS_LEFTTEXT

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

m_wndCheckBox.SetCheck (BST_UNCHECKED); // Uncheck

// Check

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 LBS_STANDARD

Description Creates a "standard" list box that has a border and a vertical scroll bar,

LBS_SORT

notifies its parent window when the selection changes or an item is double-clicked, and sorts items alphabetically. 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

LBS_DISABLENOSC ROLL

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

LBS_MULTIPLESEL

an unneeded scroll bar is hidden rather than disabled. 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

LBS_HASSTRINGS

LBS_WANTKEYBOARDINPUT

LBS_NOINTEGRALHEIGHT

is added or removed. 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. 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. 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