Programare in Windows

141
Lista întrebărilor la disciplina PW 1.ISTORIA WINDOWS. CE ADUCE NOU WINDOWS. Conceptele şi fundamentele GUI. Consecvenţa privind interfaţă cu utilizatorul. Avantajul oferit de multitasking. Gestionarea memoriei. Interfaţa grafică independentă de dispozitiv. История WINDOWS. Что нового приносит WINDOWS. Концепты и фундаменты GUI. Последовательность в отношении интерфейса с пользователем. Преимущества multitasking-а. Управление памятью. Графический интерфейс независимый от устройства. UN SCURT ISTORIC AL SISTEMULUI DE OPERARE WINDOWS Crearea sistemului de operare Windows a fost anunţată de Microsoft Corporation în noiembrie 1983 (după Lisa, dar înainte de Macintosh) şi doi ani mai târziu, în noiembrie 1985, a fost lansată versiunea Windows 1.0. Versiunile lansate în următorii doi ani au fost actualizate şi adaptate pentru piaţa internaţională, furnizând noi drivere pentru alte plăci video şi inprimante. Versiunea Windows 2.0 a fost lansată în noiembrie 1987. Această versiune include câteva modificări asupra interfeţei cu utilizatorul. Cea mai semnificativă dintre acestea este folosirea ferestrelor suprapuse în locul ferestrelor alăturate din versiunile Windows 1.x. De asemenea. Windows 2.0 include unele îmbunătăţiri ale interfeţei cu mouse-ul şi cu tastatura, în special legate de meniuri şi de casetele de dialog. În orice istoric Windows ar trebui să fie menţionat şi sistemul de operare OS/2, o alternativă la MS-DOS şi Windows, dezvoltat iniţial de Microsoft în colaborare cu IBM. Versiunea OS/2 1.0 (numai în mod caracter) a fost lansată la sfârşitul anului 1987 şi rula pe microprocesoarele Intel 80286 sau mai noi. Mediul grafic Presentation Manager (PM) a apărut odată cu versiunea OS/2 1.1 în octombrie 1988. PM a fost proiectat iniţial ca o variantă în mod protejat a mediului Windows, dar interfaţa API s-a modificat atât de mult încât producătorii software nu au putut să susţină ambele platforme. Windows 95 (care a purtat anterior numele de cod Chicago şi este numit uneori Windows 4.0) a fost lansat în august 1995. Ca şi Windows NT, Windows 95 acceptă modelul de programare pe 32 de biţi (şi, ca urmare, are nevoie de un microprocesor 80386 sau mai nou). Deşi Windows 95 nu egalează performanţele sistemului de operare Windows NT în anumite aspecte cum ar fi securitatea sau portabilitatea pe calculatoarele RISC, Windows 95 are avantajul că poate fi rulat pe calculatoarele cu cel puţin 4 megaocteţi de memorie. 1

description

Programare in Windows

Transcript of Programare in Windows

Page 1: Programare in Windows

Lista întrebărilor la disciplina PW

1.ISTORIA WINDOWS. CE ADUCE NOU WINDOWS. Conceptele şi fundamentele GUI. Consecvenţa privind interfaţă cu utilizatorul. Avantajul oferit de multitasking. Gestionarea memoriei. Interfaţa grafică independentă de dispozitiv. История WINDOWS. Что нового приносит WINDOWS. Концепты и фундаменты GUI. Последовательность в отношении интерфейса с пользователем. Преимущества multitasking-а. Управление памятью. Графический интерфейс независимый от устройства.

UN SCURT ISTORIC AL SISTEMULUI DE OPERARE WINDOWSCrearea sistemului de operare Windows a fost anunţată de Microsoft Corporation în

noiembrie 1983 (după Lisa, dar înainte de Macintosh) şi doi ani mai târziu, în noiembrie 1985, a fost lansată versiunea Windows 1.0. Versiunile lansate în următorii doi ani au fost actualizate şi adaptate pentru piaţa internaţională, furnizând noi drivere pentru alte plăci video şi inprimante.

Versiunea Windows 2.0 a fost lansată în noiembrie 1987. Această versiune include câteva modificări asupra interfeţei cu utilizatorul. Cea mai semnificativă dintre acestea este folosirea ferestrelor suprapuse în locul ferestrelor alăturate din versiunile Windows 1.x. De asemenea. Windows 2.0 include unele îmbunătăţiri ale interfeţei cu mouse-ul şi cu tastatura, în special legate de meniuri şi de casetele de dialog.

În orice istoric Windows ar trebui să fie menţionat şi sistemul de operare OS/2, o alternativă la MS-DOS şi Windows, dezvoltat iniţial de Microsoft în colaborare cu IBM. Versiunea OS/2 1.0 (numai în mod caracter) a fost lansată la sfârşitul anului 1987 şi rula pe microprocesoarele Intel 80286 sau mai noi. Mediul grafic Presentation Manager (PM) a apărut odată cu versiunea OS/2 1.1 în octombrie 1988. PM a fost proiectat iniţial ca o variantă în mod protejat a mediului Windows, dar interfaţa API s-a modificat atât de mult încât producătorii software nu au putut să susţină ambele platforme.

Windows 95 (care a purtat anterior numele de cod Chicago şi este numit uneori Windows 4.0) a fost lansat în august 1995. Ca şi Windows NT, Windows 95 acceptă modelul de programare pe 32 de biţi (şi, ca urmare, are nevoie de un microprocesor 80386 sau mai nou). Deşi Windows 95 nu egalează performanţele sistemului de operare Windows NT în anumite aspecte cum ar fi securitatea sau portabilitatea pe calculatoarele RISC, Windows 95 are avantajul că poate fi rulat pe calculatoarele cu cel puţin 4 megaocteţi de memorie.

Evident, programele scrise pentru versiunile pe 16 biţi ale sistemului de operare Windows (apărute înainte de Windows 95 şi Windows NT) nu sunt direct portabile către versiunile mai noi, pe 32 de biţi; în capitolele următoare vom discuta unele dintre modificările care sunt necesare pentru a realiza acest lucru.

Microsoft a încercat să diferenţieze platformele prin definirea unor seturi de interfeţe API. Interfaţa Win16 API este acceptată de versiunea Windows 3.1. Interfaţa Win32 API este acceptată de versiunile Windows 95 şi Windows NT. Dar aşteptaţi - mai este ceva. Microsoft permite programatorilor să scrie programe pe 32 de biţi pentru Windows 3.1; apelurile de funcţii pe 32 de biţi au fost convertite în apeluri pe 16 biţi de o bibliotecă cu legături dinamice (DLL). Această interfaţa API a fost numită Win32s („s" de la „subset", deoarece această interfaţa acceptă numai funcţiile din Win16). Pentru o perioadă, interfaţa API din Windows 95 a fost numită Win32c („c" de la „compatibil") dar acest nume a fost abandonat mai târziu.

În acest moment se consideră că atât Windows NT cât şi Windows 95 acceptă interfaţa Win32 API. Cu toate acestea, fiecare dintre cele două sisteme de operare are unele caracteristici pe care celălalt sistem de operare nu le are. Partea comună fiind considerabilă, scrierea programelor care rulează sub ambele sisteme este totuşi posibilă. De asemenea, o părere larg răspândită susţine că cele două produse vor fi înglobate într-unul singur, în viitor.CE ADUCE NOU SISTEMUL DE OPERARE WINDOWS

Sistemul de operare Windows oferă avantaje semnificative faţă de mediul MS-DOS, atât pentru utilizatori, cât şi pentru programatori. Multe dintre aceste avantaje sunt comune pentru

1

Page 2: Programare in Windows

utilizatori şi pentru programatori, deoarece sarcina dezvoltatorilor unui program este să ofere utilizatorilor lucruri de care aceştia au nevoie. CONCEPTELE ŞI FUNDAMENTELE GUI

La început, ecranul era folosit numai pentru afişarea informaţiilor pe care utilizatorul le introducea de la tastatură. Într-o interfaţă grafică, ecranul devine chiar o sursă pentru celelalte intrări ale utilizatorului. Pe ecran sunt afişate diferite obiecte grafice sub forma pictogramelor şi a unor dispozitive de intrare, precum butoanele şi barele de derulare. Folosind tastatura (sau, mai direct, un dispozitiv de indicare precum mouse-ul) utilizatorul poate să manipuleze direct aceste obiecte de pe ecran. Obiectele grafice pot fi deplasate, butoanele pot fi apăsate şi barele de derulare pot fi derulate.

Interacţiunea dintre program şi utilizator devine mult mai directă. În locul unui ciclu unidirectional al informaţiilor de la tastatură la program şi de la program la ecran, utilizatorul interacţionează direct cu obiectele de pe ecran.

Consecvenţa privind interfaţă cu utilizatorulDupă ce învăţaţi să folosiţi un program Windows, veţi fi capabil deja să utilizaţi orice alt program pentru Windows. Meniurile şi casetele de dialog permit utilizatorului să experimenteze diferite funcţii ale programului şi să îi exploreze posibilităţile. Majoritatea programelor Windows au interfaţă atât cu mouse-ul, cât şi cu tastatura. Deşi majoritatea funcţiilor unui program pentru Windows pot fi controlate şi cu ajutorul tastaturii, mouse-ul este deseori mai convenabil pentru diferite sarcini.

Din punctul de vedere al programatorului, consecvenţa în realizarea interfeţei cu utilizatorul este rezultatul folosirii procedurilor integrate în Windows pentru construirea meniurilor şi a casetelor de dialog. Toate meniurile au aceeaşi interfaţă cu tastatura şi cu mouse-ul deoarece aceste operaţii sunt manipulate mai degrabă de Windows, decât de programul respectiv.

AVANTAJUL OFERIT DE MULTITASKINGVersiunile anterioare ale sistemului de operare Windows foloseau un tip de multitasking

numit „necontrolat". Aceasta înseamnă că Windows nu folosea ceasul sistemului ca să aloce timpi de procesare diferitelor programe care rulează în sistem. Programele trebuie să cedeze voluntar controlul, astfel încât alte programe să-şi poată continua execuţia. În Windows 95 multitaskingul este controlat şi programele se pot împărţi în mai multe fire de execuţie, care par să ruleze în acelaşi timp.Gestionarea memoriei

Un sistem de operare nu poate să implementeze multitaskingul fără o gestionare adecvată a memoriei. Pe măsură ce noi programe sunt încărcate în memorie şi altele vechi îşi încheie execuţia, memoria se poate fragmenta. Sistemul de operare trebuie să aibă posibilitatea să consolideze memoria liberă. Pentru aceasta, sistemul trebuie să poată muta blocuri de cod şi de date în memorie.

Programele rulate sub Windows pot să depăşească memoria existentă; un program poate conţine mai mult cod decât încape în memorie la un moment dat. Windows poate să elimine din memorie o parte din cod şi ulterior să reîncarce codul respectiv din fişierul executabil de pe hard-disc. Un utilizator poate rula simultan mai multe copii ale aceluiaşi program, numite „instanţe"; toate aceste instanţe folosesc acelaşi cod din memorie. Programele rulate sub Windows pot să partajeze rutine stocate în alte fişiere numite „biblioteci cu legături dinamice" (dynamic link libraries). Windows conţine un mecanism de editare a legăturilor dintre program şi rutinele din bibliotecile cu legături dinamice, în timpul execuţiei. Chiar sistemul de operare este, în esenţă, o colecţie de biblioteci cu legături dinamice.

2

Page 3: Programare in Windows

Interfaţa grafică independentă de dispozitivWindows este o interfaţă grafică şi programele pentru Windows pot folosi toate avantajele oferite de elementele grafice şi de textul formatat atât pentru ecran, cât şi pentru imprimantă. O interfaţă grafică nu are numai avantajul de a fi mai atractivă, ci şi pe acela de a furniza utilizatorului o cantitate mai mare de informaţii.Programele scrise pentru Windows nu au acces direct la componentele hardware ale dispozitivelor de afişare, cum ar fi ecranul sau imprimanta. În schimb, Windows foloseşte un limbaj de programare pentru grafică (numit GDI - Graphics Device Interface) care simplifică afişarea textului formatat şi a elementelor grafice. Windows transformă componentele hardware de afişare în dispozitive virtuale. Un program scris pentru Windows va rula cu orice placă video şi cu orice imprimanta pentru care Windows are un driver de dispozitiv. Programul nu trebuie să determine ce tip de dispozitiv fizic este ataşat la calculator.

2Apelurile de funcţii. Arhitectura bazată pe mesaje. Procedura de fereastră. WinMain şi WndProc. Вызова функций. Архитектура, основанная на сообщения. Оконная процедура. WinMain şi WndProc.

Apelurile de funcţiiWindows 95 asigură suportul pentru mai mult de o mie de apeluri de funcţii pe care le pot

folosi aplicaţiile. Fiecare funcţie Windows are un nume sugestiv, scris atât cu majuscule, cât şi cu minuscule,

cum ar fi CreateWindow. Această funcţie, aşa cum probabil v-aţi dat seama, creează o fereastră. Un alt exemplu: funcţia IsClipboardFormatAvailable determină dacă în memoria temporară (clipboard) sunt stocate date într-un anumit format.

Toate funcţiile Windows importante sunt declarate în fişiere antet. Principalul fişier antet se numeşte WINDOWS.H şi include multe alte fişiere antet. Aceste fişiere antet sunt furnizate de orice mediu de programare pentru Windows 95, aşa cum este, de pildă, C. Fişierele antet sunt o parte importantă a documentaţiei Windows. Puteţi să tipăriţi o copie a fişierelor antet sau să folosiţi un browser pentru o căutare rapidă.

În programele pentru Windows folosiţi apelurile de funcţii la fel cum folosiţi funcţiile de bibiotecă C, cum ar fi strlen. Principala diferenţă este faptul că în cazul funcţiilor C codul funcţiilor este legat direct de codul programului, pe când codul funcţiilor Windows este stocat în fişiere din afara programului, numite biblioteci cu legături dinamice (DLL - dynamic link libraries).Arhitectura bazată pe mesaje

În Windows, atunci când utilizatorul redimensionează o fereastră, sistemul de operare trimite programului un mesaj prin care îi comunică noile dimensiuni ale ferestrei. Programul poate apoi să modifice conţinutul ferestrei, astfel încât acesta să se adapteze la noile dimensiuni.

Windows apelează o funcţie din program. Parametrii acestei funcţii descriu mesajul respectiv. Această funcţie din programul Windows este cunoscută sub numele de „procedură de fereastră" („window procedure").Procedura de fereastră

Orice fereastră creată de un program are asociată o procedură de fereastră. Procedura de fereastră este e funcţie care se poate afla chiar în program sau într-o bibliotecă cu legături dinamice (DLL). Windows trimite un mesaj către o fereastră prin apelarea procedurii de fereastră. Procedura de fereastră prelucrează anumite informaţii pe baza mesajului primit, apoi returnează controlul sistemului de operare.

Mai precis, o fereastră este creată întotdeauna pe baza unei „clase de fereastră". Clasa specifică procedura de fereastră care prelucrează mesajele trimise către fereastră. Folosirea unei clase de fereastră permite ca pe baza aceleiaşi clase să se creeze mai multe ferestre care, ca urmare, folosesc aceeaşi procedură de fereastră. De exemplu, toate butoanele din toate programele pentru Windows sunt create pe baza aceleiaşi clase de fereastră. Această clasă are o

3

Page 4: Programare in Windows

procedură de fereastră (aflată într-una dintre bibliotecile cu legături dinamice ale sistemului de operare) care prelucrează mesajele trimise către ferestrele butoanelor.

O procedură de fereastră prelucrează mesajele trimise ferestrei respective. Deseori, aceste mesaje informează fereastra cu privire la acţiunile executate de utilizator cu ajutorul mouse-ului sau al tastaturii. Aceasta este calea prin care o fereastră „află", de exemplu, că un buton a fost apăsat. Alte mesaje comunică ferestrei că a fost redimensionată sau că trebuie să fie refăcută.

Atunci când un program este lansat în execuţie, Windows creează o „coadă de mesaje" („message queue") pentru programul respectiv, în această coadă de mesaje sunt păstrate mesajele trimise către toate ferestrele pe care le creează programul. Programul conţine o mică secvenţă de cod numită „ciclu de mesaje" („message loop") care preia mesajele din coada de aşteptare şi le distribuie procedurilor de fereastră corespunzătoare. Alte mesaje sunt trimise direct procedurii de fereastră, fără să mai fie plasate în coada de mesaje.

WinMain si WinProcFişierul conţine numai două funcţii: WinMain şi WndProc. Funcţia WinMain reprezintă

punctul de intrare în program. Aceasta este echivalentul funcţiei main din programele scrise în limbajul C. Orice program pentru Windows trebuie să aibă o funcţie WinMain.

WndProc este „procedura de fereastră" a ferestrei create de programu. Orice fereastră - indiferent dacă este fereastra principală a aplicaţiei sau fereastra unui mic buton de apăsare - are o procedură de fereastră. Procedura de fereastră este un mod de încapsulare a codului care răspunde intrărilor (în general de la tastatură şi de la mouse) şi afişează elementele grafice pe ecran. Aşa cum veţi vedea, procedura de fereastră face acest lucru prin prelucrarea „mesajelor" trimise către fereastră

Nici o instrucţiune din fişierul HELLOWIN.C nu apelează direct funcţia WndProc: WndProc este apelată de sistemul de operare Windows. Totuşi, în funcţia WinMain apare o referire la funcţia WndProc, acesta fiind motivul pentru care WndProc este declarată în partea de început a programului, înainte de WinMain1. Atributele contextului de dispozitiv. Salvarea contextului de dispozitiv. Funcţiile LineTo, Polyline şi

PolylineTo, PolyPolyline, Arc, PolyBezier şi PolyBezierTo, Rectangle, Ellipse, RoundRect, Chord şi Pie. Атрибуты DC. Сохранение DC. Функции LineTo, Polyline и PolylineTo, PolyPolyline, Arc, PolyBezier и PolyBezierTo, Rectangle, Ellipse, RoundRect, Chord и Pie.

Atributele contextului de dispozitiv

Windows foloseşte contextul de dispozitiv ca să stocheze atributele care specifică modul de lucru al funcţiilor GDI pentru afişare. De exemplu, atunci când afişaţi un text folosind funcţia TextOut nu trebuie să specificaţi culoarea textului sau fontul folosit. Windows foloseşte contextul de dispozitiv ca să obţină aceste informaţii.

Atunci când un program obţine o variabilă handle a unui context de dispozitiv, Windows creează un context de dispozitiv cu valorile prestabilite pentru toate atributele

Salvarea contextului de dispozitiv

În mod normal, Windows creează un nou context de dispozitiv cu valori prestabilite atunci când apelaţi una dintre funcţiile GetDC sau BeginPaint. Toate modificările făcute în atributele contextului de dispozitiv se pierd atunci când contextul de dispozitiv este şters din memorie prin apelarea funcţiei ReleaseDC sau a funcţiei EndPaint. Dacă programul trebuie să folosească un atribut cu o valoarea diferită de cea prestabilită va trebui să iniţializaţi contextul de dispozitiv de fiecare dată când obţineţi o variabilă handle:

caseWM_Paint:hd = BeginPaint (hwnd, &ps);[iniţializează atributele contextului de dispozitiv] [desenează zona client a ferestrei] EndPaint (hwnd, &ps);return 0;

4

Page 5: Programare in Windows

Un atribut al DC specifica modul de lucru al functiilor de desenare; SetTextColor,... Toate atributele DC au valori prestabilite care devin active la obtinerea DC. Pentru fiecare functie de tip Set exista si o functie de tip Get, folosita pt. obtinerea valorilor curente ale atributelor DC.

Functii care obtin (sau creaza) si elibereaza (sau distrug) un context de dispozitiv; (in API: BeginPaint ... EndPaint, GetDc, ReleaseDC;

Funcția LineTo trage o linie de la poziția curentă până la, dar fără a include, un punct specificatBOOL LineTo( HDC hdc, // дескриптор контекста устройства int nXEnd, // координата x конечной точки int nYEnd // координата y конечной точки);

Funcția Polyline atrage o serie de segmente de dreaptă, punctele de conectare specificate în matrice.BOOL Polyline(HDC hdc, // дескриптор контекста устройства CONST POINT *lppt, // массив конечных точек int cPoints // число точек в массиве);

Funcția PolylineTo trage una sau mai multe linii drepte.BOOL PolylineTo( HDC hdc, // дескриптор контекста устройства CONST POINT *lppt, // массив точек DWORD cCount // число точек в массиве);

Funcția PolyPolyline atrage mai multe segmente de linie de serie conectat.BOOL PolyPolyline( HDC hdc, // дескриптор контекста устройства CONST POINT *lppt, // массив точек CONST DWORD *lpdwPolyPoints, // массив значений DWORD cCount // число записей в массиве значений);

 Funcția Arc atrage un arc eliptic.BOOL Arc (

_In_ HDC HDC,

_In_   Int nLeftRect,

_In_   Int nTopRect,

_In_   Int nRightRect,

_In_   Int nBottomRect,

_In_   int nXStartArc,

_In_   int nYStartArc,

_In_   int nXEndArc,

_In_   Int nYEndArc

); Funcția PolyBezier atrage una sau mai multe curbe Bezier.

BOOL PolyBezier (

_In_ HDC HDC,

_In_   const POINT * LPPT,

_In_ CPoints DWORD

); Funcția PolyBezierTo atrage una sau mai multe curbe Bezier.

BOOL PolyBezierTo (

_In_ HDC HDC,

_In_   const POINT * LPPT,

_In_ DWORD cCount

5

Page 6: Programare in Windows

);Functia Rectangle deseneaza un dreptunghi cu penula selectata si il umple cu culoare cu periuta selectata

Rectangle(hdc, 25, 30, 100, 50) Funcția Ellipse atrage o elipsă. Centrul a elipsei este centrul dreptunghiului de încadrare specificat. Elipsa este conturat cu ajutorul stiloului curent și este umplut cu ajutorul periei curent.

BOOL Ellipse (

_In_ HDC HDC,

_In_   Int nLeftRect,

_In_   Int nTopRect,

_In_   Int nRightRect,

_In_   Int nBottomRect

); Funcția RoundRect atrage un dreptunghi cu colțuri rotunjite. Dreptunghiul este subliniat cu ajutorul pen-ului curent și este completat, cu ajutorul periei curent.

BOOL RoundRect (

_In_ HDC HDC,

_In_   Int nLeftRect,

_In_   Int nTopRect,

_In_   Int nRightRect,

_In_   Int nBottomRect,

_In_   Int nWidth,

_In_   Int nHeight

); Funcția Chord atrage o coardă (o regiune delimitată de intersecția de elipsă și un segment de linie, numită secantă). Coardă este conturat de utilizare a stiloului curent și umplut folosind pensula curent.

BOOL Chord (

_In_ HDC HDC,

_In_   Int nLeftRect,

_In_   Int nTopRect,

_In_   Int nRightRect,

_In_   Int nBottomRect,

_In_   Int nXRadial1,

_In_   Int nYRadial1,

_In_   Int nXRadial2,

_In_   Int nYRadial2

);Functia Pie trage un sector de elipsa

2. Bazele utilizării cronometrului. Основы использования таймера

Cronometrul Windows este un dispozitiv de intrare, ce comunică periodic unei aplicaţii trecerea unui anumit interval de timp. Programul specifică sistemului de operare acest interval de timp, spunându-i ceva de genul: „Fă-mi un semn la fiecare 10 secunde". Windows trimite periodic programului, mesaje WM_TIMER prin care îi semnalează trecerea intervalului de timp respectiv.

Iată câteva alte utilizări sub Windows ale cronometrului, deşi s-ar putea ca unele să nu fie atât de evidente:

■ Multitaskingul - Deşi Windows 95 este un mediu cu multitasking controlat, uneori este mult mai eficient ca un program să returneze controlul sistemului de operare, imediat ce este posibil. Dacă programul trebuie să execute o operaţie de durată, o poate împărţi în operaţii mai mici, pe care să le prelucreze la primirea unui mesaj WM_TIMER.

6

Page 7: Programare in Windows

■ Actualizarea unui raport de stare - Un program poate să utilizeze cronometrul pentru afişarea „în timp real" a unor informaţii care se schimbă continuu, în funcţie de resursele sistemului sau de evoluţia unei anumite operaţii.

■ Implementarea unei caracteristici de „autosalvare" - Cronometrul poate să „amintească" unui program Windows să salveze pe hard-disc documentul la care lucrează utilizatorul, după trecerea unui anumit interval de timp.

■ Închiderea versiunilor demonstrative ale unui program - Unele versiuni demonstrative ale programelor sunt proiectate astfel încât să-şi încheie execuţia, de exemplu, după trecerea a 30 de minute de la lansare. Cronometrul poate să semnaleze unei astfel de aplicaţii terminarea timpului permis de rulare.

■ Deplasări succesive - Obiectele grafice dintr-un joc sau imaginile afişate succesiv de un program de instruire asistată de calculator trebuie să fie prelucrate la anumite intervale de timp. Folosirea cronometrului elimină inconsecvenţele care pot apărea datorită diferenţelor dintre viteza de lucru a microprocesoarelor.

3. Care este punctul de intrare într-un program Windows? Что представляет собой точка входа в W-программу?

Funcţia WinMain reprezintă punctul de intrare în program. Aceasta este echivalentul funcţiei main din programele scrise în limbajul C. Orice program pentru Windows trebuie să aibă o funcţie WinMain.

Totuşi, în funcţia WinMain apare o referire la funcţia WndProc, acesta fiind motivul pentru care WndProc este declarată în partea de început a programului, înainte de WinMain.

Funcţia WinMain este de tipul WINAPI. Punctul de intrare într-un program Windows este o funcţie numită WinMain. Funcţia WinMain este întotdeauna definită astfel:

int WINAPI WinMain (HINSTANCE hinstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)

Această funcţie foloseşte secvenţa de apelare WINAPI şi la terminare returnează sistemului de operare o valoare întreagă. Numele funcţiei trebuie să fie WinMain. Această funcţie are patru parametri:

Parametrul hlnstance este numit „variabilă handle a instanţei" („instance handle"). Acesta este un număr care identifică în mod unic toate programele rulate în Windows. Utilizatorul poate rula simultan mai multe copii ale aceluiaşi program. Aceste copii se numesc „instanţe" şi fiecare are o valoare diferită pentru parametrul hlnstance. Variabila handle a instanţei este asemănătoare cu „identificatorul de operaţie" („task ID") sau „identificatorul de proces" („process ID") din alte sisteme de operare multitasking.

hPrevInstance („previous instance" - instanţa anterioară) este un parametru învechit. În versiunile Windows anterioare acest parametru conţinea variabila handle a celei mai recente instanţe încă activă a aceluiaşi program. Dacă nu erau încărcate alte instanţe ale programului, hPrevInstance avea valoarea 0 sau NULL. În Windows 95, parametrul hPrevInstance are întotdeauna valoarea NULL.

Parametrul szCmdLine este un pointer la un şir de caractere terminat cu zero care conţine eventualii parametri transmişi programului în linia de comandă. Puteţi să rulaţi un program Windows cu parametri incluzând parametrii respectivi după numele programului în linia de comandă MS-DOS sau specificându-i în caseta de dialog Run apelată din meniul Start.

Parametrul iCmdShow este un număr care indică modul iniţial de afişare a ferestrei în Windows. Acest număr este atribuit de programul care lansează în execuţie programul aflat în discuţie. Programele verifică rareori valoarea acestui parametru, dar o pot face dacă este nevoie. În majoritatea cazurilor, iCmdShow are valoarea 1 sau 7. Dar cel mai bine este să nu vă gândiţi la aceste valori numerice. Mai sugestivi sunt identificatorii SW_SHOWNORMAL (definit în Windows ca 1) şi SW_SHOWMINNOACTIVE (definit cu valoarea 7). Prefixul SW vine de la „show window" (afişare fereastră). Acest parametru specifică dacă fereastra programului este afişată normal sau dacă este afişată iniţial doar ca o pictogramă.

7

Page 8: Programare in Windows

4. Ce aduce nou SO Windows? Что вносит нового ОС Windows?Sistemul de operare Windows oferă avantaje semnificative faţă de mediul MS-DOS, atât pentru utilizatori, cât şi pentru programatori. Multe dintre aceste avantaje sunt comune pentru utilizatori şi pentru programatori, deoarece sarcina dezvoltatorilor unui program este să ofere utilizatorilor lucruri de care aceştia au nevoie. Windows 95 face posibil acest lucru.

5. Ce este notaţia ungară? Exemplificaţi. Что означает венгерская запись? Приведите примеры.

Mulţi programatori Windows folosesc „notaţia ungară", o convenţie de denumire a variabilelor intitulată astfel în onoarea legendarului programator de la Microsoft, Charles Simonyi. Convenţia este foarte simplă - fiecare nume de variabilă începe cu una sau mai multe litere mici care specifică tipul de date al variabilei. De exemplu, prefixul sz al variabilei szCmdLine semnifică „şir de caractere terminat cu zero". Prefixul h al variabilelor hInstance şi hPrevInstance înseamnă „variabilă handle"; prefixul i al variabilei iCmdShow înseamnă „întreg". Şi ultimii doi parametri ai funcţiei WndProc respectă notaţia ungară, deşi, aşa cum am arătat mai devreme, wParam ar fi trebuit să se numească uiParam (de la „unsigned integer"). Totuşi, deoarece aceşti doi parametri se definesc folosind tipurile de date WPARAM şi LPARAM, am păstrat denumirile originale.

Atunci când denumiţi variabilele de tip structură, puteţi să folosiţi numele de tip al structurii (sau o abreviere a acestuia) ca prefix al numelui variabilei sau chiar ca nume al variabilei. De exemplu, în funcţia WinMain din programul HELLOWIN.C, variabila msg este o structură de tip MSG iar wndclass este o variabilă de tip WNDCLASSEX. În funcţia WndProc, ps este o structură de tip PAINTSTRUCT iar rect este o structură de tip RECT.

Notaţia ungară vă ajută să descoperiţi erorile înainte ca acestea să ajungă în programele finale. Deoarece numele unei variabile descrie atât modul de folosire a acesteia, cât şi tipul de date, este mai puţin probabil să faceţi erori de programare care implică amestecarea unor tipuri de date incompatibile.

Prefixele folosite pentru variabilele din această carte sunt prezentate în tabelul următor:

Prefix Tip de datec charby BYTE (unsigned char)n shorti intx, y int (folosit pentru coordonate)cx, cy int (folosit pentru dimensiuni pe axele x si y, c vine de la „contor")b sau f BOOL (int); f vine de la „flag" (indicator)w WORD (unsigned short)l LONG (long)dw DWORD (unsigned long)fn funcţies şir de caracteresz sir de caractere terminat cu zeroh variabilă handlep pointer

6. Ce este o variabilă handle şi care este destinaţia ei? Что такое переменная handle и каково ее назначение?

Variabila handle este un numar pe care Windows il foloseste la indicarea unui obiect.

8

Page 9: Programare in Windows

7. Ce este programare controlată de evenimente? Что такое событийно управляемое программирование?

Programarea orientată eveniment este o paradigmă a programării calculatoarelor. Spre deosebire de programele tradiționale, care-și urmează propria execuție, schimbându-și câteodata doar cursul în puncte de ramificație (instrucțiuni test, etc), cursul execuției unui program orientat eveniment este condus în mare parte de evenimente externe.

Programele orientate eveniment sunt formate de obicei dintr-un număr de programe mici numite handlere de eveniment, care sunt apelate ca răspuns la evenimente externe și dintr-un coordonator (dispatcher), care apelează handlerele de evenimente, folosind de obicei o coadă a evenimentelor, care să rețină evenimentele care nu au fost procesate.

În multe cazuri, handlerele de evenimente pot declanșa ele însele evenimente, ducând la o cascadă de evenimente.

Programarea orientată eveniment accentuează ca virtuți flexibilitatea și asincronicitatea.

Programele cu interfețe grafice sunt de obicei programate într-o modalitate gestionată de evenimente. Sistemele de operare sunt un alt exemplu clasic de programe dirijate de evenimente pe cel puțin două nivele. La cel mai de jos nivel, handlerele de întreruperi se comportă ca handlere de evenimente hardware, cu procesorul în rol de coordonator (dispatcher). Sistemele de operare, de asemenea se comportă ca și coordonatori pentru procese, transmițând datele și întreruperile soft către procese user, care de multe ori sunt programate ca și handlere de eveniment.

8. Ce este un device context? Что такое device context

Contextul de dispozitiv (prescurtat DC - device context) este o structură de date întreţinută intern de interfaţa GDI. Fiecare context de dispozitiv este asociat unui anumit dispozitiv de afişare, cum ar fi imprimanta, plotterul sau monitorul video. În cazul monitoarelor video, un context de dispozitiv este de obicei asociat unei anumite ferestre de pe ecran

9. Ce este un multitasking controlat? Что такое контролируемый мултитаскинг?

Programele trebuie să cedeze voluntar controlul, astfel încât alte programe să-şi poată continua execuţia. În Windows 95 multitaskingul este controlat şi programele se pot împărţi în mai multe fire de execuţie, care par să ruleze în acelaşi timp.

10. Ce facem dacă cronometrul nu este ccesibil? Что делать, если таймер недоступен

Secvenţa de cod din exemplul următor afişează o casetă de mesaje atunci când funcţia SetTimer nu poate aloca programului un cronometru:

if (!SetTimer (hwnd, 1, 1000, NULL)) { MessageBox (hwnd, "Too many clocks or timers!", "Program Name", MB_ICONEXCLAMATION ] MB_OK); return FALSE ; }

11. Ce prezintă procedura unei ferestre? Что собой представляет процедура окна?

Orice fereastră creată de un program are asociată o procedură de fereastră. Procedura de fereastră este e funcţie care se poate afla chiar în program sau într-o bibliotecă cu legături dinamice (DLL). Windows trimite un mesaj către o fereastră prin apelarea procedurii de

Acest tip de multitasking se mai numeşte şi „multitasking cooperativ", (n.t)

9

Page 10: Programare in Windows

fereastră. Procedura de fereastră prelucrează anumite informaţii pe baza mesajului primit, apoi returnează controlul sistemului de operare.

Mai precis, o fereastră este creată întotdeauna pe baza unei „clase de fereastră". Clasa specifică procedura de fereastră care prelucrează mesajele trimise către fereastră. Folosirea unei clase de fereastră permite ca pe baza aceleiaşi clase să se creeze mai multe ferestre care, ca urmare, folosesc aceeaşi procedură de fereastră. De exemplu, toate butoanele din toate programele pentru Windows sunt create pe baza aceleiaşi clase de fereastră. Această clasă are o procedură de fereastră (aflată într-una dintre bibliotecile cu legături dinamice ale sistemului de operare) care prelucrează mesajele trimise către ferestrele butoanelor.

12. Clasa butoanelor. Класс кнопок

13. Coduri virtuale de taste. Starea tastelor de modificare. Utilizarea mesajelor de acţionare a tastelor. Виртуальные коды клавиш. Положения клавиш сдвига и клавиш-переключателей. Использование сообщений клавиатуры.

Coduri virtuale de taste

Zecimal HexaIdentificator WINDOWS.H

Necesar Tastatură IBM

1 01 VK_BUTTON2 02 VK_RBUTTON3 03 VK_CANCEL Ctrl-Break4 04 VK_MBUTTON8 08 VK_BACK Backspace9 09 VK_TAB Tab12 0C VK_CLEAR Tasta numerică 5 cu

tastaNum Lock inactivă

13 0D VK_RETURN Enter16 10 VK_SHIFT Shift17 11 VK_CONTROL Ctrl18 12 VK_MENU Alt

Starea tastelor de modificareParametrii lParam şi wParam care însoţesc mesajele WM_KEYUP, WM_SYSKEYUP,

WM_KEYDOWN şi WM_SYSKEYDOWN nu spun nimic programului despre starea tastelor de modificare. Puteţi să obţineţi starea oricărei taste virtuale folosind funcţia GetKeyState. Această funcţie este folosită, de obicei, pentru obţinerea stării tastelor de modificare (Shift, Alt şi Ctrl) şi a tastelor de comutare (Caps Lock, Num Lock şi Scroll Lock). De exemplu:

GetKeyState (VK_SHIFT) ;

returnează o valoare negativă (adică un număr în care primul bit are valoarea 1) dacă tasta Shift este apăsată. Valoarea returnată de apelul:

GetKeyState (VK_CAPITAL) ;

are un 1 în bitul cel mai puţin semnificativ dacă tasta Caps Lock este activă. De asemenea, puteţi să obţineţi starea butoanelor mouse-ului folosind codurile virtuale VK_LBUTTON, VK_MBUTTON şi VK_RBUTTON. Totuşi, majoritatea programelor pentru Windows care trebuie să monitorizeze combinaţiile între taste şi butoanele mouse-ului utilizează calea inversă - verificând starea tastelor atunci când primesc un mesaj de la mouse. De fapt, starea tastelor de modificare este inclusă în mesajele primite de la mouse (aşa cum veţi vedea în capitolul următor).

10

Page 11: Programare in Windows

Utilizarea mesajelor de acţionare a tastelor

În general, programele pentru Windows folosesc mesajele WM_KEYDOWN pentru tastele care nu generează caractere. Deşi s-ar putea să vă vină ideea să folosiţi mesajele pentru acţionări de taste în combinaţie cu informaţiile despre tastele de modificare (furnizate de funcţia GetKeyState) ca să transformaţi mesajele pentru acţionări de taste în mesaje pentru caractere, nu faceţi acest lucru. Veţi avea probleme datorită diferenţelor între tastaturile internaţionale. De exemplu, dacă primiţi un mesaj WM_KEYDOWN pentru care parametrul wParam are valoarea 33H, ştiţi că utilizatorul a apăsat tasta 3. Până aici totul este bine. Dacă apelaţi funcţia GetKeyState veţi afla că tasta Shift este apăsată şi s-ar putea să presupuneţi că utilizatorul a introdus un caracter diez (#), lucru care poate să nu fie adevărat. Un utilizator britanic ar fi introdus caracterul £. Mesajele WM_KEYDOWN sunt utile pentru tastele de deplasare, tastele funcţionale şi tastele speciale, precum Insert, Delete. Uneori tastele Insert, Delete si tastele funcţionale sunt folosite ca acceleratori pentru comenzi de meniu. Deoarece Windows transformă acceleratorii în comenzi de meniu, nu este nevoie să prelucraţi în program mesajele pentru acţionarea tastelor de accelerare. Unele programe non-Windows folosesc foarte des tastele funcţionale în combinaţie cu tastele Alt, Ctrl şi Shift. Puteţi face acest lucru şi în programele pentru Windows, dar nu este recomandat. Dacă vreţi să folosiţi tastele funcţionale, acestea trebuie să dubleze comenzi din meniuri. Unul dintre obiectivele sistemului de operare Windows este să nu oblige utilizatorii să memoreze sau să folosească liste complicate de comenzi.

14. Crearea ferestrelor child. Создание дочерних окон

Programul BTNLOOK defineşte o structură numită button care conţine stilurile de fereastră ale butoanelor şi şiruri de caractere descriptive pentru fiecare dintre cele 10 tipuri de butoane. Stilurile de fereastră ale butoanelor încep cu literele BS_ care sunt o prescurtare de la „button style".

Cele 10 ferestre descendent sunt create într-un ciclu for în cadrul procedurii WndProc, în timpul prelucrării mesajului WM_CREATE. Apelul funcţiei CreateWindow foloseşte următorii parametri:

Class name (numele clasei) "button"Window text (textul ferestrei) button[i].textWindow style (stilul ferestrei) WS_CHILD|WS_VISIBLE|button[i].stylex position (poziţia x) cxChary position (poziţia y) cyChar * (1 + 2 * i)Width (lăţime) 20 * cxCharHeight (înălţime) 7*cyChar/4

Parent window (fereastra părinte) hwnd

Child window ID (identificatorul ferestrei descendent) (HMENU) i

Instance handle (variabila handle a instanţei) ((LPCREATESTRUCT) lParam) -> hlnstanceExtra parameters (alţi parametri) NULL

Parametrul care reprezintă numele clasei este numele predefinit. Pentru stilul ferestrei sunt folosite valorile WS_CHILD, WS_VISIBLE sau unul dintre cele zece stiluri de butoane (BS_PUSHBUTTON, BS_DEFPUSHBUTTON şi aşa mai departe), definite în structura button. Textul ferestrei (care pentru o fereastră normală apare în bara de titlu) este textul care va fi afişat pe fiecare buton. În acest exemplu am folosit textul care identifică stilul butonului.

Parametrii x şi y indică poziţia colţului din stânga-sus al ferestrei descendent, raportată la colţul din stânga-sus al zonei client a ferestrei părinte. Parametrii width şi height specifică lăţimea şi înălţimea fiecărei ferestre descendent.

Identificatorul ferestrei descendent (ID) trebuie să fie unic pentru fiecare fereastră descendent. Acest identificator permite procedurii ferestrei părinte să identifice fereastra descendent atunci când prelucrează mesajele WM_COMMAND primite de la aceasta. Remarcaţi faptul că identificatorul

11

Page 12: Programare in Windows

ferestrei descendent este transmis prin parametrul funcţiei CreateWindow folosit în mod normal pentru specificarea meniului în program, aşa că trebuie să fie convertit la o valoare de tip HMENU.

Parametrul variabilei handle a instanţei din apelul funcţiei CreateWindow pare puţin ciudat, dar am profitat de faptul că în mesajul WM_CREATE lParam este de fapt un pointer la o structură de tipul CREATESTRUCT („structură de creare") care are un membru hlnstance. Ca urmare, am convertit parametrul lParam la un pointer la o structură CREATESTRUCT şi am obţinut variabila membru hlnstance.

După apelarea funcţiei CreateWindow nu mai trebuie să facem nimic în legătură cu aceste ferestre descendent. Procedura ferestrei buton din Windows execută întreţinerea acestor butoane şi toate operaţiile de redesenare. (Singura excepţie este butonul de tipul BS_OWNERDRAW; aşa cum vom arăta mai târziu, sarcina de desenare a butoanelor de acest tip revine programului.) La terminarea programului, Windows distruge toate ferestrele descendent odată cu distrugerea ferestrei părinte.

15. Cum poate fi obţinută variabila handle a unui device context? Как можно получить переменную handle device context-а?

Functii care obtin (sau creaza) si elibereaza (sau distrug) un context de dispozitivI: BeginPaint ... EndPaint, si GetDc, ReleaseDC;

16. Curbe Bezier. Funcţia PolyBezierTo. Кривые Bezier. Функция PolyBezierTo.

O curbă Bezier este definită prin patru puncte - două capete şi două puncte de control. Capetele curbei sunt ancorate în cele două puncte finale. Punctele de control acţionează ca nişte „magneţi" care deformează linia dreaptă dintre cele două puncte finaleFuncţia PolyBezierTo foloseşte poziţia curentă ca punct de început pentru prima curbă Bezier. Fiecare curbă desenată are nevoie doar de trei puncte. La returnarea funcţiei, poziţia curentă este punctul final al ultimei curbe desenate

17. Cursorul de editare (CARET). Funcţii pentru cursorul de editare. Soluţia Unicode pentru Windows. Курсор редактирования (CARET). Функции для курсора редактирования. Решение Unicode для Windows.

18. Desenarea suprafeţelor pline. Funcţia Polygon şi modul de umplere a poligoanelor. Funcţia SetPolyFillMode. Umplerea suprafeţelor interioare. Рисование поверхностей для заливки. Функция Polygon и способы заполнения многоугольников. Функция SetPolyFillMode. Заликва внутренних поверхностей.

Cele şapte funcţii Windows pentru desenarea figurilor sunt prezentate în tabelul următor:

Funcţie Figura

Rectangle Ellipse RoundRect Chord

Dreptunghi cu colţuri drepte Elipsă Dreptunghi cu colţuri rotunjite

Arc pe circumferinţa unei elipse, având capetele unite printr-o coardă

PiePolygon PolyPolygon

Suprafaţă de forma unei felii de plăcintă, reprezentând un segment de elipsă.Figură geometrică având mai multe laturi Mai multe figuri geometrice cu mai multe laturi

Windows desenează conturul figurilor folosind peniţa curentă selectată în contextul de dispozitiv. Pentru acest contur sunt folosite atributele stabilite pentru modul de desenare a

12

Page 13: Programare in Windows

fondului, culoarea fondului şi modul de desenare, ca şi în cazul desenării unei linii simple. Tot ceea ce aţi învăţat despre linii se aplică şi în cazul contururilor.

Figurile sunt umplute folosind pensula selectată în contextul de dispozitiv. În mod prestabilit, aceasta este pensula de stoc WHITE_BRUSH, ceea ce înseamnă că interiorul figurilor va fi umplut cu alb. Windows defineşte şase pensule de stoc: WHITE-_BRUSH, LTGRAY_BRUSH, GRAY_BRUSH, DKGRAY_BRUSH, BLACK_BRUSH şi NULL_BRUSH (sau HOLLOW_BRUSH).

Puteţi să selectaţi una dintre aceste pensule în contextul de dispozitiv la fel cum selectaţi o peniţă de stoc. Windows defineşte tipul HBRUSH ca variabilă handle a unei pensule, aşa că puteţi să definiţi mai întâi o variabilă de acest tip:

HBRUSH hBrush ;

Puteţi să obţineţi variabila handle a pensulei de stoc GRAY_BRUSH apelând funcţia GetStockObject:.

hBrush = Get&toCkObject (GB*Y_BRUSH) ;

Puteţi să o selectaţi apoi în contextul de dispozitiv folosind funcţia SelectObject:SelectObject (hdc, hBrush) ;

Din acest moment, figurile desenate vor avea interiorul gri.

Dacă vreţi să desenaţi o figură fără contur, selectaţi în contextul de dispozitiv peniţa NULL_PEN:

SelectObject (hdc, GetStockObject (NULL_PEN)) ;

Dacă vreţi să desenaţi o figură fără să îi umpleţi interiorul, selectaţi în contextul de dispozitiv pensula NULL_BRUSH:

SelectObject (hdc, GetStockObject (NULL_BRUSH)) ;

Funcţia Polygon şi modul de umplere a poligoanelor

Am discutat deja despre primele cinci funcţii de desenare a suprafeţelor pline. Polygon este cea de-a şasea funcţie de desenare a unei figuri colorate în interior, cu contur. Apelul funcţiei Polygon este asemănător cu cel al funcţiei Polyline:

Polygon (hdc, pt, iCount) ;

Parametrul pt este un pointer la o matrice de structuri POINT, iar iCount reprezintă numărul de puncte din matrice. Dacă ultimul punct din matrice este diferit de primul punct, Windows mai desenează o linie care conectează ultimul şi primul punct. (Acest lucru nu se întâmplă şi în cazul funcţiei Polyline.)

Suprafaţa închisă de poligon este colorată folosind pensula curentă în două moduri, în funcţie de modul de umplere a poligoanelor, definit în contextul de dispozitiv. Modul prestabilit de umplere a poligoanelor este ALTERNATE, ceea ce înseamnă că Windows colorează numai suprafeţele închise la care se poate ajunge din exteriorul poligonului prin traversarea unui număr impar de laturi (1, 3, 5 şi aşa mai departe). Celelalte suprafeţe interioare nu sunt umplute. Dacă selectaţi WINDING ca mod de umplere, Windows colorează toate suprafeţele închise de poligon. Puteţi să selectaţi modul de umplere cu ajutorul funcţiei SetPolyFillMode:

SetPolyFillMode (hdc, iMode) ;

Cele două moduri de umplere a poligoanelor sunt ilustrate cel mai bine pe modelul unei stele cu cinci colţuri. În Figura 4-17 steaua din partea stângă a fost desenată cu modul de umplere ALTERNATE, iar steaua din partea dreaptă a fost desenată cu modul de umplere WINDING.

13

Page 14: Programare in Windows

Figura 4-17. Figuri desenate cu cele două moduri de umplere a poligoanelor ALTERNATE (stângă) şi WINDING

Umplerea suprafeţelor interioare

Interiorul figurilor Rectangle, RoundRect, Ellipse, Chord, Pie, Polygon şi PollyPolygon este umplut cu pensula - numită uneori şi „model" („pattern") - selectată în contextul de dispozitiv. O pensulă este de fapt o imagine bitmap 8x8 repetată pe verticală şi pe orizontală, pentru umplerea unei suprafeţe.

19. Dimensiunea unui caracter. Funcţia GetTextMetrics. Tipul TEXTMETRIC. Dimensiunile textului. Размеры символа. Функция GetTextMetrics. Тип TEXTMETRIC. Размеры текста.

Dimensiunea unui caracterDimensiunile caracterelor sunt obţinute prin apelarea funcţiei GetTextMetrics. Funcţia

GetTextMetrics are nevoie de o variabilă handle a contextului de dispozitiv, deoarece returnează informaţii despre fontul selectat în contextul de dispozitiv. Windows copiază valorile referitoare la dimensiunile caracterelor într-o structură de tip TEXTMETRIC. Valorile sunt exprimate în unităţi de măsură care depind de modul de mapare selectat în contextul de dispozitiv. În contextul prestabilit de dispozitiv, modul de mapare este MM_TEXT, aşa că dimensiunile sunt date în pixeli.

Pentru folosirea funcţiei GetTextMetrics trebuie să definiţi mai întâi o variabilă de tipul TEXTMETRIC (numită, de obicei, tm):

TEXTMETRIC tm;În continuare obţineţi o variabilă handle a contextului de dispozitiv şi apelaţi funcţia

GetTextMetrics:hdc = GetDC(hwnd);GetTextMetrics(hdc, &tm) ;ReleaseDC(hwnd, hdc);

Apoi puteţi să examinaţi valorile din structura de dimensiuni a textului şi, dacă doriţi, să salvaţi unele dintre aceste dimensiuni pentru utilizarea în viitor.

Dimensiunile textului: detaliiStructura TEXTMETRIC furnizează o mulţime de informaţii despre fontul curent selectat în

contextul de dispozitiv. Totuşi, aşa cum puteţi vedea în Figura 3-3, dimensiunea verticală a unui font este definită de numai cinci valori.

14

Page 15: Programare in Windows

Figura 3-3. Cele cinci valori care definesc dimensiunea verticala a unui caracter.

Acestea sunt uşor de înţeles. Valoarea tmInternalLeading se referă la spaţiul păstrat deasupra unui caracter pentru semnele de accentuare. Dacă această valoare este zero, literele mari cu accent sunt afişate ceva mai mici, astfel încât accentul să încapă în partea de sus a caracterului. Valoarea tmExternalLeading se referă la spaţiul recomandat de către proiectantul fontului a fi lăsat între rândurile de text. Puteţi să acceptaţi sau nu această recomandare atunci când stabiliţi distanţa dintre rândurile de text.

Structura TEXTMETRIC conţine două câmpuri care descriu lăţimea unui caracter:tmAveCharWidth (lăţimea medie a literelor mici) şi tmMaxCharWidth (lăţimea celui mai

mare caracter al fontului). Pentru fonturile cu dimensiune fixă cele două valori sunt egale.Este important să reţineţi faptul că dimensiunile fontului sistem depind de rezoluţia ecranului

pe care rulează sistemul de operare Windows. Windows furnizează o interfaţă grafică independentă de dispozitiv, dar este nevoie de un mic efort şi din partea dumneavoastră. Nu scrieţi programe Windows care se bazează pe ghicirea dimensiunilor unui caracter. Nu introduceţi în cod valori fixe. Folosiţi funcţia GetTextMetrics ca să obţineţi valorile de care aveţi nevoie.

20. DIMENSIUNEA ZONEI CLIENT. Macroinstrucţiunile LOWORD şi HIWORD (LOWORD (lParam), HIWORD (lParam)). Barele de derulare. Domeniul şi poziţia unei bare de derulare. Размеры рабочей зоны. Макрокоманды LOWORD и HIWORD. Полосы прокрутки. Область полосы и положение бегунка.

Dimensiunea zonei clientO metodă obişnuită de determinare a dimensiunilor zonei client a unei ferestre este

prelucrarea mesajului WM_SIZE în procedura de fereastră. Windows trimite un mesaj WM_SIZE către procedura de fereastră, de fiecare dată când se modifică dimensiunile ferestrei. Parametrul lParam transmis procedurii de fereastră conţine lăţimea zonei client în cuvântul mai puţin semnificativ (LOWORD) şi înălţimea zonei client în cuvântul mai semnificativ (HIWORD). Codul pentru prelucrarea acestui mesaj arată astfel:

static int cxClient, cyClient ;[alte linii de program] case WM_SIZE :

cxClient = LOWORD (lParam) ;cyClient = HIWORD (lParam) ;return 0 ;

Macroinstrucţiunile LOWORD şi HIWORD sunt definite în fişierele antet din Windows. Ca şi variabilele cxChar şi cyChar, variabilele cxClient şi cyClient sunt definite ca statice în cadrul procedurii de fereastră, deoarece vor fi folosite ulterior pentru prelucrarea altor mesaje.

Mesajul WM_SIZE este urmat de un mesaj WM_PAINT, deoarece la definirea clasei am specificat următorul stil de fereastră:

CS_HREDRAW | CS_VREDRAW

15

Page 16: Programare in Windows

Aflarea dimensiunilor zonei client a ferestrei este primul pas pentru furnizarea unei metode de deplasare a textului în zona client atunci când aceasta nu este suficient de mare pentru a cuprinde tot textul afişat.

Barele de derulareBarele de derulare se numără printre cele mai reuşite componente ale unei interfeţe grafice

pentru mouse; sunt uşor de folosit şi determină o reacţie vizuală foarte rapidă. Puteţi să folosiţi bare de derulare oriunde afişaţi ceva - text, imagini, foi de calcul tabelar, înregistrări din baze de date - pentru care aveţi nevoie de mai mult spaţiu decât este disponibil în zona client a ferestrei.

Barele de derulare sunt poziţionale vertical (pentru deplasări în sus şi în jos) sau orizontal (pentru deplasări la stânga şi la dreapta). Puteţi să executaţi clic pe săgeţile de Ia capetele barei de derulare sau pe zona dintre acestea. Bara de derulare este parcursă longitudinal de o „casetă de derulare" care indică poziţia aproximativă a părţii afişate pe ecran faţă de întregul document. De asemenea, puteţi să trageţi caseta de derulare cu ajutorul mouse-ului, ca să o mutaţi într-o anumită poziţie. Figura 3-7 prezintă modul recomandat de folosire a unei bare de derulare verticale pentru text.

Programatorii au uneori probleme cu terminologia legată de barele de derulare, deoarece perspectiva lor este diferită de cea a utilizatorilor: un utilizator care derulează un document în jos vrea să aducă pe ecran o parte a documentului aflată mai jos; pentru aceasta, programul mută de fapt documentul mai sus în raport cu fereastra de pe ecran. Documentaţia Windows şi identificatorii definiţi în fişierele antet se bazează, însă, pe perspectiva utilizatorului: derularea în sus înseamnă parcurgerea documentului către început; derularea în jos înseamnă parcurgerea documentului către sfârşit.

Figura 3-7. Bara de derulare verticală.

Este foarte uşor să includeţi în fereastra aplicaţiei o bară de derulare orizontală sau verticală. Tot ce trebuie să faceţi este să includeţi identificatorul WS_VSCROLL (derulare verticală) şi/sau WS_HSCROLL (derulare orizontală) în stilul de fereastră din apelul funcţiei CreateWindow. Barele de derulare sunt plasate întotdeauna la marginea de jos sau la cea din dreapta a ferestrei şi se întind pe toată lăţimea, respectiv înălţimea zonei client. Zona client nu include spaţiul ocupat de barele de derulare. Lăţimea unei bare de derulare verticale şi înălţimea uneia orizontale sunt constante pentru un driver de afişare dat. Dacă aveţi nevoie de aceste valori, puteţi să le obţineţi (aşa cum aţi văzut) prin apelarea funcţiei GetSystemMetrics.

Domeniul şi poziţia unei bare de derulareFiecare bară de derulare are asociate un „domeniu" (definit printr-o pereche de numere întregi care reprezintă

valorile maximă şi minimă) şi o „poziţie" (punctul în care se află caseta de derulare în domeniul asociat barei de derulare). Atunci când caseta de derulare se află la capătul de sus (sau la capătul din partea stângă) al barei de derulare, poziţia corespunde valorii minime. Capătul de jos (sau capătul din partea dreaptă) al barei de derulare reprezintă valoarea maximă.21. Elemente de bază despre mouse. Funcţia GetSystemMetrics (SM_MOUSEPRESENT); cButtons =

GetSystemMetrics (SM_CMOUSEBUTTONS). Parametrul SM_SWAPBUTTON. Zona senzitivă a indicatorului. Основные понятия о мышке. Функция GetSystemMetrics (SM_MOUSEPRESENT);

16

Vizualizaţi următoarea linie în sus executând clic aici (conţinutul ferestrei se deplasează în jos)

Trageţi caseta de derulare cu mouse-ul, pentru a fixa o poziţie aproximativă în cadrul documentului.

Vizualizaţi următoarea linie în jos executând clic aici (conţinutul ferestrei se deplasează în sus)documentului.

Page 17: Programare in Windows

GetSystemMetrics (SM_CMOUSEBUTTONS). Параметр SM_SWAPBUTTON. Чувствительная зона указателя мышки.

Windows 95 permite folosirea mouse-ului cu un buton, cu două butoane sau cu trei butoane, precum şi folosirea unui joystick sau a unui creion optic, pentru simularea unui mouse cu un buton. Deoarece mouse-ul cu un singur buton reprezintă un numitor comun, mulţi programatori Windows au evitat multă vreme să folosească celelalte butoane ale mouse-ului. Totuşi, mouse-ul cu trei butoane a devenit un standard de facto, aşa că reticenţa tradiţională privind folosirea celui de-al doilea buton nu mai este justificată. Al doilea buton al mouse-ului este recomandat pentru apelarea „meniurilor de context" - meniuri care apar în fereastră în afara barei de meniu - sau pentru operaţii speciale de tragere (despre care vom discuta în curând). Puteţi să determinaţi dacă mouse-ul este prezent folosind funcţia GetSystemMetrics:

fMouse = GetSystemMetrics (SM_MOUSEPRESENT) ;

Variabila fMouse va avea valoarea TRUE (diferită de zero) dacă mouse-ul este instalat. Pentru determinarea numărului de butoane ale mouse-ului instalat, folosiţi tot funcţia GetSystemMetrics:

cButtons = GetSystemMetrics (SM_CMOUSEBUTTONS) ;

Această funcţie returnează valoarea 0 dacă mouse-ul nu este instalat.

Utilizatorii care folosesc mâna stângă pot să inverseze butoanele mouse-ului folosind Panoul de control din Windows. Deşi o aplicaţie poate să determine dacă butoanele mouse-ului au fost inversate apelând funcţia GetSystemMetrics cu para-metrul SM_SWAPBUTTON, de obicei acest lucru nu este necesar. Butonul apăsat cu indexul este considerat butonul din stângă al mouse-ului, chiar dacă fizic se află în partea dreaptă a mouse-ului. Totuşi, dacă scrieţi un program de instruire în care desenaţi un mouse pe ecran, poate că veţi avea nevoie să ştiţi dacă butoanele au fost inversate.

Zona senzitivă a acestui indicator este vârful săgeţii. Indicatorul IDC_CROSS (folosit în programele BLOKOUT dm acest capitol) are zona senzitivă situată la intersecţia unor linii subţiri. Indicatorul IDC_WAIT are forma unei clepsidre şi, în general, indică faptul că programul execută o operaţie de durată. Programatorii pot să creeze indicatoare proprii (aşa cum vom face în Capitolul 9). Indicatorul predefinit al unei ferestre este specificat la definirea structurii clasei de ferestre. De exemplu:

wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;

22. Ferestre child de control și culoare. Дочерние окна управления и цвета

Culorile de sistemWindows păstrează 25 de culori de sistem pentru afişarea diferitelor părţi ale ecranului. Puteţi să obţineţi şi să stabiliţi aceste culori cu ajutorul funcţiilor GetSysColor şi SetSysColors. Identificatorii definiţi în fişierele antet din Windows specifică culorile de sistem. Stabilirea unei culori de sistem cu ajutorul funcţiei SetSysColors afectează culoarea respectivă numai pe durata sesiunii Windows curente.

Puteţi să schimbaţi o parte dintre culorile de sistem folosind secţiunea Display a Panoului de control din Windows (Control Panel) sau modificând secţiunea [colors] din fişierul WIN.INI. Secţiunea [colors] foloseşte pentru cele 25 de culori de sistem cuvinte cheie (altele decât identificatorii folosiţi de funcţiile GetSysColor şi SetSysColors) urmaţi de valori pentru culorile roşu, verde şi albastru. Aceste valori sunt cuprinse în intervalul 0 - 255. Tabelul următor prezintă modul în care sunt identificate cele 25 de culori de sistem, cu ajutorul identificatorilor folosiţi de funcţiile GetSysColor şi SetSysColors şi al cuvintelor cheie folosite în fişierul WIN.INI. Tabelul este ordonat crescător, în funcţie de valoarea constantelor COLOR_ (de la 0 la 24):

17

Page 18: Programare in Windows

GetSysColor şi SetSysColors WIN.INICOLOR_SCROLLBAR ScrollbarCOLOR_BACKGROUND Background

COLOR_ACTIVECAPTION ActiveTitle

COLOR_NACTIVECAPTION InactiveTitle

COLOR_MENU Menu

COLOR_WINDOW Window

COLOR_WINDOWFRAME WindowFrame

COLOR_MENUTEXT MenuText

COLOR_WINDOWTEXT WindowText

COLOR_CAPTIONTEXT TitleText

COLOR_ACTIVEBORDER ActiveBorder

COLOR_NACTIVEBORDER InactiveBorder

COLOR_APPWORKSPACE AppWorkspace

COLOR_HIGHLIGHT Hilight

COLOR_HIGHLIGHTTEXT HilightText

COLOR_BTNFACE ButtonFace

COLOR_BTNSHADOW ButtonShadow

COLOR_GRAYTEXT GrayText

COLOR_BTNTEXT ButtonText

COLOR_NACTIVECAPTIONTEXT InactiveTitleText

COLOR_BTNHIGHLIGHT ButtonHilight

COLOR_3DDKSHADOW ButtonDkShadow

COLOR_3DLIGHT ButtonLight

COLOR_NFOTEXT InfoText

COLOR_INFOBK InfoWindow

Valorile prestabilite pentru aceste culori sunt furnizate de driverul de afişare. Sistemul de operare Windows foloseşte valorile prestabilite, dacă acestea nu sunt suprascrise de valorile din secţiunea [colors] a fişierului WIN.INI, care poate fi modificată din Panoul de control.

23. Ferestre child. Butoane definite de programator. Дочерние окна. Кнопки, определяемые пользователем

Executarea unui clic pe aceste butoane determină generarea mesajelor WM_COM-MAND. Pentru prelucrarea mesajului WM_COMMAND, OWNERDRW apelează funcţia GetWindcrwRect ca să stocheze poziţia şi dimensiunea întregii ferestre (nu numai a zonei client) într-o structură de tip RECT (dreptunghi). Această poziţie este definită faţă de ecran. OWNERDRW modifică apoi câmpurile structurii în funcţie de butonul pe care s-a executat clic. Programul repoziţionează şi redimensionează fereas tra prin apelarea funcţiei MoveWindow. Aceasta generează un nou mesaj WM_SIZE şi butoanele sunt repoziţionate în centrul zonei client.

Dacă nu ar mai face nimic altceva, programul ar fi complet funcţional, dar butoanele nu ar fi vizibile. Un buton creat cu stilul BS_OWNERDRAW trimite ferestrei părinte un mesaj WM_DRAWITEM de fiecare dată când butonul trebuie să fie redesenat. Aceasta se întâmplă la crearea butonului, de fiecare dată când este apăsat sau eliberat, când obţine sau pierde cursorul de intrare şi în orice altă situaţie când trebuie să fie redesenat.

în mesajul WM_DRAWITEM, parametrul IParam este un pointer către o structură de tip DRAWITEMSTRUCT. Programul OWNERDRW stochează acest pointer într-o variabilă numită pdis. Această structură conţine informaţiile necesare programului pentru redesenarea butonului. (Aceeaşi structură este folosită şi pentru casetele listă şi articolele de meniu desenate de proprietar.) Câmpurile din structură importante pentru lucrul cu butoane sunt hDC (contextul de dispozitiv al butonului), rcltem (o structură RECT în care este stocată dimensiunea butonului), CtllD (identificatorul ferestrei controlului) şi UemState (care indică dacă butonul este apăsat sau dacă deţine cursorul de intrare).

Programul OWNERDRW începe prelucrarea mesajului WM_DRAWITEM prin apelarea funcţiei FillRect, pentru a şterge suprafaţa butonului cu o pensulă de culoare albă. Apoi este apelată funcţia FrameRed, care trasează un chenar negru în jurul butonului. în continuare, OWNERDRW desenează cele patru triunghiuri negre pe buton, prin apelarea funcţiei Potygon. Aceasta este situaţia normală.

Dacă butonul este apăsat, unul dintre biţii câmpului UemState din structura DRAWITEMSTRUCT are valoarea 1. Puteţi să testaţi acest bit folosind constanta ODS_SELECTED. Dacă bitul are valoarea 1,

18

Page 19: Programare in Windows

OWNERDRW inversează culorile butonului, apelând funcţia InvertRect. Dacă butonul deţine cursorul de intrare, atunci bitul ODS_FOCUS din câmpul UemState are valoarea 1. în acest caz, programul OWNERDRW desenează o linie punctată în jurul textului afişat de buton, apelând funcţia DrawFocusRect.

Un avertisment legat de folosirea butoanelor desenate de proprietar: Windows obţine contextul de dispozitiv şi îl include în structura DRAWITEMSTRUCT. Lăsaţi contextul de dispozitiv în aceeaşi stare în care l-aţi găsit. Orice obiect GDI selectat în contextul de dispozitiv trebuie să fie deselectat. De asemenea, aveţi grijă să nu desenaţi în afara dreptunghiului care defineşte limitele butonului.

24. Ferestre child. Butoane și inăput focus. Дочерние окна. Кнопки и фокус ввода

Butoane şi cursorul de intrare

Aşa cum am menţionat mai devreme în acest capitol, butoanele de apăsare, butoanele radio şi butoanele desenate de proprietar primesc cursorul de intrare atunci când se execută clic pe acestea. Controlul indică faptul că deţine cursorul de intrare printr-o linie punctată care înconjoară textul. Atunci când o fereastră descendent primeşte cursorul de intrare, fereastra părinte îl pierde; toate intrările de la tastatură sunt direcţionate către control, nu către fereastra părinte. Totuşi, controalele de tip fereastră descendent răspund numai la apăsarea barei de spaţiu, care funcţionează în acest caz ca şi butonul mouse-ului. Situaţia prezintă o problemă serioasă: programul a pierdut controlul asupra tastaturii. Haideţi să vedem ce putem să facem în acest caz.

Aşa cum am discutat în Capitolul 5, atunci când sistemul de operare Windows mută cursorul de intrare de la o fereastră (cum ar fi fereastra părinte) la o altă fereastră (cum ar fi controlul de tip fereastră descendent), trimite mai întâi un mesaj WM_KILLFOCUS către fereastra care pierde cursorul de intrare. Parametrul wParam al mesajului este variabila handle a ferestrei care primeşte cursorul de intrare. Windows trimite apoi un mesaj WM_SETFOCUS către fereastra care primeşte cursorul de intrare. Parametrul wParam al mesajului este variabila de manipulare a ferestrei care pierde cursorul de intrare. (În ambele cazuri, wParam poate avea valoarea NULL, ceea ce arată că nici o fereastră nu primeşte sau nu pierde cursorul de intrare.)

O fereastră părinte poate împiedica un control de tip fereastră descendent să primească cursorul de intrare prelucrând mesajul WM_KILLFOCUS. Să presupunem că matricea hwndCmd conţine variabilele handle ale tuturor ferestrelor descendent. (Aceste variabile au fost salvate în matrice în timpul apelării funcţiei CreateWindow pentru crearea ferestrelor descendent.) NUM este numărul ferestrelor descendent.

case WM_KILLFOCUS :for (i = 0 ; i < NUM ; 1++)

if (hwndChild[i] == (HWND) wParamSetFocus (hwnd) ; break ;

return 0 ;

În această secvenţă de cod, atunci când fereastra părinte este avertizată că urmează să piardă cursorul de intrare în favoarea uneia dintre ferestrele descendent, apelează funcţia SetFocus ca să recâştige cursorul de intrare.

Iată o cale mai simplă (dar mai puţin evidentă) de a face acelaşi lucru:case WM_KILLFOCUS ;

~ if (hwnd == GetParent ((HWND) wParam))SetFocus (hwnd) ;

re tu rn 0 ;

25. Ferestre child. Butoane visibile și accesibile. Дочерние окна. Видимые и доступные кнопки

Butoane vizibile şi butoane activatePentru recepţionarea intrărilor de la mouse şi de la tastatură, o fereastră descendent trebuie să fie vizibilă (afişată), dar şi activată. Atunci când o fereastră este vizibilă, dar nu este activată, textul acesteia este afişat cu gri, în loc de negru.

19

Page 20: Programare in Windows

Dacă nu includeţi parametrul WS_VISIBLE în clasa ferestrei la crearea ferestrei descendent, aceasta nu va fi afişată până când nu apelaţi funcţia ShowWindow:

ShowWindow (hwndChild, SW_SHOWNORMAL) ;

Dacă includeţi parametrul WS_VISIBLE în clasa ferestrei, nu este nevoie să apelaţi funcţia ShowWindow. Totuşi, prin apelarea funcţiei ShowWindow puteţi să mascaţi o fereastră descendent afişată:

ShowWindow (hwndChild, SW_HIDE) ;

Puteţi să determinaţi dacă o fereastră descendent este afişată:

IsWindowVisible (hwndChild) ;

De asemenea, puteţi să activaţi sau să dezactivaţi o fereastră descendent. În mod prestabilit, ferestrele descendent sunt activate. Iată cum puteţi să le dezactivaţi:

EnableWindow (hwndChild, FALSE) ;

Pentru controalele de tip buton, această acţiune are ca efect afişarea cu gri a textului din buton. Butonul nu mai răspunde la intrările de la mouse sau de la tastatură. Aceasta este cea mai bună metodă de a indica faptul că opţiunea reprezentată de un anumit buton nu este disponibilă.

Puteţi să reactivaţi o fereastră descendent prin următorul apel:

EnableWindow (hwndChild, TRUE) ;

Puteţi să determinaţi dacă o fereastră descendent este activă sau nu prin următorul apel:

IsWindowEnabled (hwndChild) ;

26. Ferestre child. Check box. Дочерние окна. Флажки

Casetele de validare sunt dreptunghiuri etichetate cu un text; de obicei, textul apare în partea dreaptă a casetei de validare. (Dacă la crearea butonului folosiţi stilul BS_LEFTTEXT, textul apare în stânga casetei de validare.) De obicei, casetele de validare sunt incluse în aplicaţii pentru a permite utilizatorilor să selecteze diferite opţiuni. Casetele de validare funcţionează ca un comutator: executarea unui clic într-o casetă de validare determină apariţia unui marcaj de validare; un al doilea clic face ca marcajul de validare să dispară.

Cele mai cunoscute stiluri de casete de validare sunt BS_CHECKBOX şi BS_AUTOCHECKBOX. Atunci când folosiţi stilul BS_CHECKBOX, vă revine dumneavoastră sarcina de aplicare a marcajului de validare, prin trimiterea unui mesaj BS_SETCHECK. Parametrul wParam trebuie să aibă valoarea 1 pentru afişarea marcajului de validare şi valoarea 0 pentru ştergerea acestuia. Puteţi să folosiţi următoarea secvenţă de cod pentru inversarea marcajului de validare la prelucrarea mesajului WM_COMMAND primit de la control:

SendMessage ((HWND) 1Param, BM_SETCHECK, (WPARAM) SendMessage ((HWND) lParam, BM_GETCHECK, 0, 0), 0) ;

Remarcaţi folosirea operatorului .' înaintea celui de-al doilea apel al funcţiei SendMessage. Valoarea lParam este variabila handle a ferestrei descendent, transmisă către procedura ferestrei prin mesajul WM_COMMAND. Dacă ulterior doriţi să aflaţi starea butonului, trimiteţi un nou mesaj BM_GETCHECK. Sau puteţi să stocaţi starea curentă a casetei de validare într-o variabilă statică din procedura ferestrei.

Puteţi să iniţializaţi o casetă de validare de tip BS_CHECKBOX cu un X, trimiţând mesajul BM_SETCHECK:

SendMessage (hwndButton, BM_SETCHECK, 1, 0) ;

În cazul folosirii stilului BS_AUTOCHECKBOX, controlul este cel care actualizează afişarea marcajului de validare. Procedura ferestrei părinte poate să ignore mesajele WM_COMMAND. Atunci când vreţi să aflaţi starea butonului, trimiteţi către acesta mesajul BM_GETCHECK:

iCheck = (int) SendMessage (hwndButton, BM_GETCHECK, 0, 0) ;

iCheck are valoarea TRUE (sau o valoare diferită de 0) dacă butonul este validat şi valoarea FALSE (sau 0) în caz contrar.

20

Page 21: Programare in Windows

Celelalte două stiluri de casete de validare sunt BS_3STATE şi BS_AUTO3STATE. Aşa cum indică şi numele lor, aceste stiluri permit afişarea unei stări terţe - culoarea gri din caseta de validare - în cazul în care trimiteţi către control un mesaj WM_SETCHECK cu parametrul wParam egal cu 2. Culoarea gri indică utilizatorului că opţiunea respectivă este nedeterminată sau irelevantă. În acest caz, caseta nu poate fi validată - cu alte cuvinte, este dezactivată. Totuşi, caseta de validare continuă să trimită mesaje către fereastra părinte atunci când se execută clic. Vom descrie puţin mai târziu metode mai bune de dezactivare a unei casete de validare.

Caseta de validare este aliniată la partea stângă a dreptunghiului şi este centrată între marginile de sus şi de jos ale acestuia, conform dimensiunilor specificate la apelarea funcţiei CreateWindow. Executarea unui clic oriunde în cadrul dreptunghiului determină trimiterea unui mesaj WM_COMMAND către fereastra părinte, înălţimea minimă a casetei de validare este egală cu înălţimea unui caracter. Lăţimea minimă este egală cu numărul de caractere din text, plus două.

27. Ferestre child. Clasa barelor de derulare. Дочерние окна. Класс полос прокрутки

Spre deosebire de butoane ,barele de derulare nu trimit mesaje WM_COMMAND către fereastra părinte, în schimb, trimit mesaje WM_VSCROLL şi WM_HSCROLL, ca şi barele de derulare ale ferestrelor. Atunci când prelucraţi mesajele primite de la barele de derulare, puteţi să faceţi diferenţa între barele de control ale ferestrelor şi controalele de tip bare de derulare cu ajutorul parametrului IParatn. Acesta va fi 0 pentru barele de derulare ale ferestrelor sau o variabilă handle în cazul controalelor de tip bară de derulare. Cele două cuvinte care formează parametrul wParam au aceeaşi semnificaţie atât pentru barele de derulare ale ferestrelor, cât şi pentru controalele de tip bară de derulare.

Deşi barele de derulare ale ferestrelor au lăţime fixă, în cazul controalelor de tip bare de derulare Windows foloseşte dimensiunile dreptunghiului specificate la apelarea funcţiei CreateWindow sau a funcţiei MoveWindow. Puteţi să creaţi bare de derulare lungi şi subţiri sau bare de derulare scurte şi late.

Dacă vreţi să creaţi controale de tip bare de derulare cu aceleaşi dimensiuni ca şi barele de derulare ale ferestrelor, puteţi să folosiţi funcţia GetSystemMetrics ca să obţineţi înălţimea unei bare de derulare orizontale:

GetSystemMetrics (SH_CYHSCROLL) ;

sau lăţimea unei bare de derulare verticale:GetSystemMetrics (SM_CXVSCROLL) ;

(Conform documentaţiei, identificatorii SBS_LEFTALIGN, SBS_RIGHTALIGN, SBS_TOPALIGN şi SBS_BOTTOMALIGN pentru barele de derulare ale ferestrelor stabilesc dimensiuni standard pentru barele de derulare. Totuşi, acest lucru este valabil numai pentru casetele de dialog.)

Puteţi să stabiliţi intervalele şi poziţia unui control de tip bară de derulare cu aceleaşi apeluri pe care le folosiţi şi pentru barele de derulare ale ferestrelor:

SetScrollRange (hwndScroll , SB CTL, iMin, iMax, bRedraw) ; SetScrollPos (hwndScroll , SB CTL, iPos, bRedraw) ; SetScrollInfo (hwndScroll , SB_CTL, &si, bRedraw) ;

Diferenţa constă în faptul că barele de derulare ale ferestrelor folosesc o variabilă către fereastra principală ca prim parametru şi SB_VERT sau SB_HORZ ca al doilea parametru.

Destul de surprinzător, culoarea de sistem COLOR_SCROLLBAR nu mai este folosită pentru controalele de tip bară de derulare. Butoanele de la capătul barei şi caseta de derulare de pe bară folosesc culorile COLOR_BTNFACE, COLOR_BTN-HIGHUGHT, COLOR_BTNSHADOW, COLOR_BTNTEXT (pentru săgeţi) şi COLOR J3TNLIGHT. Spaţiul mai mare dintre cele' două butoane de la capete este o combinaţie a culorilor COLOR_BTNFACE şi COLOR_BTNHIGHLIGHT.

Dacă interceptaţi mesajul WM_CTLCOLORSCROLLBAR, puteţi să returnaţi o pensulă pentru suprascrierea culorilor folosite pentru această zonă. Haideţi să facem acest lucru.

28. Ferestre child. Clasa de redactare. Дочерние окна. Класс редактирования

21

Page 22: Programare in Windows

Clasa de editare este, din unele puncte de vedere, cea mai simpla clasa de fereastra predefinita, iar din alte puncte de vedere este cea mai complicate. Atunci cand creati o fereastra descendent folosind numele de clasa ,,edit", definiti un dreptunghi pe baza coordonatelor x si y si a parametrilorinaltime si latime - specificati la apelarea functiei CreateWindow. Acest dreptunghi contine text care poate fi editat. Atunci cand fereastra descendent detine cursorul de intrare, puteti sa introduced text de la tastatura, sa deplasati cursorul, sa selectati portiuni din text cu ajutorul mouse-ului sau al tastei Shift si al tastelor de deplasare, sa stergeti textul selectat si sa 11 pastrati in memoria temporara (Clipboard) apasand tastele Ctrl+X, sa il copiati cu ajutorul tastelor Ctrl+C sau sa inserati in controlul de editare textul din memoria temporara apasand tastele Ctrl+V.

Unul dintre cele mai simple moduri de folosire a controalelor de editare este crearea rubricilor de intrare cu o singura linie. Dar controalele de editare nu sunt limitate la o singura .

29. Ferestre child. Clasa static. Дочерние окна. Класс статических дочерних окон

Puteţi să creaţi un control static de tip fereastră descendent folosind clasa „static" în funcţia CreateWindow. Aceste ferestre sunt un tip destul de „inofensiv" - nu acceptă intrări de la mouse sau de la tastatură şi nu trimit mesaje WM_COMMAND către fereastra părinte. (Atunci când executaţi clic pe o fereastră descendent statică, aceasta interceptează mesajul WM_NCHITTEST şi returnează o valoare HTTRANSPARENT către Windows. Ca rezultat, Windows trimite acelaşi mesaj WM_NCHITTEST către fereastra aflată dedesubt, care de obicei este fereastra părinte. în general, fereastra părinte transmite mesajul primit către procedura DefWindowProc, unde acesta este convertit într-un mesaj de mouse din zona client.)

Primele şase stiluri de ferestre descendent statice nu fac decât să deseneze un dreptunghi sau un cadru în zona client a ferestrei descendent. în tabelul următor stilurile statice „RECT" (coloana din stânga) sunt dreptunghiuri pline, iar cele trei stiluri „FRAME" (coloana din dreapta) sunt chenare dreptunghiulare necolorate:

SS_BLACKRECT SS_BLACKFRAME SS_GRAYRECT SS_GRAYFRAME SS_WHITERECT SS_WHITEFRAME

„BLACK", „GRAY" şi „WHITE" nu sunt, de fapt, culorile dreptunghiurilor res-pective. Culorile reale folosite se bazează pe culori de sistem, aşa cum se poate vedea în tabelul următor:

Control static Culoare de sistemBLACK COLOR_3DDKSHADOWGRAY COLOR_BTNSHADOWWHITE COLOR_BTNHIGHLIGHTParametrul pentru textul ferestrei din apelul funcţiei CreateWindow este ignorat în

cazul acestor stiluri de fereastră. Colţul din stânga-sus al dreptunghiului începe la poziţia indicată de parametrii x şi y, relativ la fereastra părinte. (Puteţi să folosiţi şi stilurile SS_ETCHEDHORZ, SS_BTCHEDVERT sau SS_ETCHEDFRAME ca să creaţi chenare umbrite cu culorile „gray" şi „white".)

Clasele statice includ şi trei stiluri de text: SS_LEFT, SS_RIGHT şi SS_CENTER. Acestea creează rubrici de text aliniate la stânga, la dreapta sau centrate. Textul este furnizat ca parametru al funcţiei CreateWindow şi poate fi modificat prin apelarea

funcţiei SetWindowText. Atunci când procedura ferestrei pentru controalele statice afişează acest text, foloseşte funcţia DrawText cu parametrii DT_WORDBREAK, DT_NOCLIP şi DT_EXPANDTABS. Textul este încadrat în dreptunghiul ferestrei descendent.

Fondul ferestrelor descendent de tip text este, în mod normal, COLOR_BTNFACE, iar textul este scris cu culoarea dată de COLOR_WINDOWTEXT. Puteţi să interceptaţi mesajele WM_CTLCOLORSTATIC dacă doriţi să modificaţi culoarea

22

Page 23: Programare in Windows

textului prin apelarea funcţiei SetTextColor sau a fondului prin apelarea funcţiei SetBkColor şi prin returnarea unei variabile handle a pensulei pentru fond. Acest lucru va fi ilustrat în programul COLORS1, prezentat puţin mai jos.

In sfârşit, clasa „static" include stilurile de fereastră SS_CON şi SS_USERITEM. Acestea nu au însă nici o semnificaţie atunci când sunt folosite pe post de controale de tip fereastră descendent. Le vom prezenta pe larg atunci când vom discuta despre casetele de dialog.

30. Ferestre child. Coduri de înștiințare. Дочерние окна. Коды уведомления управляющих окон редактирования

Codurile de Instiintare sunt prezentate mai jos:

EN_SETFOCUS Controlul de editare a obfinut cursorul de intrareEN_KILLFOCUS Controlul de editare a pierdut cursorul de intrareEN_CHANGE Confinutul controlului de editare se va modificaEN_UPDATE Confinutul controlului de editare s-a modificatEN_ERRSPACE Controlul de editare nu mai are spafiuEN_MAXTEXT Controlul de editare nu mai are spajiu pentru inserareEN_HSCROLL S-a executat clic pe bara de derulare orizontala a controlului de editareEN_VSCROLL S-a executat clic pe bara de derulare verticala a controlului de editareControalele de editare stocheaza textul in memoria programului ferestrei pa rinte. Asa cum am

menjionat mai sus, confinutul unui control de editare este limitat la 32 kilooctefi.

31. Ferestre child. Colorarea barelor de derulare și a textului static. Дочерние окна. Окрашивание полос прокрутки и статического текста

In programul COLORS1, interiorul celor trei bare de derulare si textul din cele sase rubrici de text sunt colorate cu rosu, verde si albastru. Colorarea barelor de derulare se realizeaza prin prelucrarea mesajelor WM_CTLCOLORSCROLLBAR.

In procedura WndProc definim o matrice statica ce contine trei variabile handle ale pensulelor:static HBRUSH hBrush[3] ;

Cele trei pensule sunt create in timpul prelucrarii mesajului WM_CREATE:for (i = 0 ; i < 3 ; i++)

hBrush[i] • CreateSolidBrush (crPrim[i]) ;

unde matricea crPrim contine valorile RGB pentru cele trei culori primare. In timpul prelucrarii mesajului WMlCTLCOLORSCROLLBAR, procedura ferestrei returneaza una dintre cele trei pensule:

case WM_CHX0LORSCR0LLBAR : i = GetWindowLong ((HWND) lParam, GWWJD) ; return (LRESULT) hBrush[i ] ;

Aceste pensule trebuie distruse in timpul prelucrarii mesajului WM_DESTROY:for (i = 0 ; i < 3 ; DeleteObject (hBrush[i++])) ;

Textul din rubricile de text static este colorat mtr-un mod similar prin prelucrarea mesajului WM_CTLCOLORSTATIC si prin apelarea functiei SetTextColor. Fondul textului este stabilit cu ajutorul functiei SetBkColor, folosind culoarea de sistem COLOR_BTNHIGHLIGHT. In acest fel fondul textului are aceeasi culoare ca si dreptunghiul static din spatele barelor de derulare si al textului. In cazul controalelor de tip text static, culoarea de fond se aplica numai pentru dreptunghiul din spatele caracterelor care formeaza textul, nu pentru tot spatiul ocupat de fereastra controlului. Pentru aceasta, procedura ferestrei trebuie sa returneze o variabila handle la o pensula de culoarea COLOR_BTNHIGHLIGHT. Aceasta pensula se numeste hBrushStatic, este creata in timpul prelucrarii mesajului WM_CREATE si este distrusa in timpul prelucrarii mesajului WM_DESTROY.

Prin crearea unei pensule bazate pe culoarea COLOR_BTNHIGHLIGHT In timpul prelucrarii mesajului WM_CREATE si folosirea acesteia pe toata durata programului, ne-am expus la aparitia unei mici probleme. Daca in timpul rularii programului culoarea COLOR_BTNHIGHLIGHT este modificata, culoarea dreptunghiului static se va modifica, la fel si culoarea de fond a textului, dar culoarea de fond a controalelor de tip text va ramane tot vechea culoare COLOR_BTNHIGHLIGHT.

32. Ferestre child. Colorarea fondului. Дочерние окна. Закрашивание фона

23

Page 24: Programare in Windows

Atunci cand defineste clasa de fereastra, programul COLORS1 stabileste pentru fondul zonei client o pensula neagra plina:

wndclass.hbrBackground • CreateSolidBrush (OL) ;

Atunci cand modificati valorile barelor de derulare, programul trebuie sa creeze o noua pensula si sa puna variabila handle a acesteia in structura de clasa a ferestrei. Asa cum avem posibilitatea sa obtinem si sa stabilim procedura barelor de derulare cu ajutorul funcfiilor GetWindowLong si SetWindowLong, putem sa obtinem si sa stabilim si variabila handle a acestei pensule cu ajutorul functiilor GetClassWord si SetClassWord.

Puteti sa aveti o noua pensula, sa inserati variabila handle a acesteia in structura de clasa a ferestrei, apoi sa stergeti vechea pensula:

DeleteObject ((HBRUSH)SetClassLong (hwnd, GCL HBRBACKGROUND, (LONG)

CreateSolidBrush TRGB (color[0], color[l], color[2])))) ;

La urmatoarea recolorare a fondului ferestrei, Windows va folosi noua pensula. Pentru a forja stergerea fondului, invalidam partea dreapta a zonei client:

InvalidateRect (hwnd, ircColor, TRUE) ;

Valoarea TRUE (diferita de 0) folosita ca eel de-al treilea parametru indica faptul ca dorim ca fondul sa fie sters Inainte de a fi redesenat.

Functia InvalidateRect forteaza sistemul de operare Windows sa puna un mesaj WM_PAINT in coada de mesaje a ferestrei. Deoarece are prioritate scazuta, mesajul WMJPAINT nu va fi prelucrat imediat daca inch mai miscati caseta de pe bara de derulare cu ajutorul mouse-ului sau al tastaturii. Daca vreti ca fereastra sa fie actualizata imediat dupa modificarea culorii, puteti sa adaugati instructiunea:

UpdateWindow (hwnd) ;

dupa apelarea functiei InvalidateRect. Dar aceasta instructiune va incetini prelucrarea mesajelor de la mouse sau de la tastatura.

Functia WndProc din programul COLORS1 nu prelucreaza mesajele WM_PAINT, ci le trimite functiei DefWindowProc. Codul prestabilit de prelucrare a mesajului WM_PAINT consta numai in apelarea functiilor BeginPaint si EndPaint pentru validarea ferestrei. Deoarece am specificat in apelul functiei InvalidateRect ca fondul ferestrei trebuie sters, functia BeginPaint forfeaza sistemul de operare Windows sS genereze mesajul WM_ERASEBKGND (stergere fond). Functia WndProc ignora acest mesaj. Mesajul este prelucrat de Windows, care sterge fondul zonei client, folosind pensula specificata in clasa ferestrei.

Intotdeauna este bine sa faceti curatenie inainte de terminarea programului, asa că in timpul prelucrarii mesajului WM_DESTROY este apelata din nou funcţia DeleteObject:

DeleteObject ((HBRUSH) SetClassLong (hwnd, GCL_HBRBACKGROUND,(LONG) GetStockObject (WHITE_BRUSH))) ;

33. Ferestre child. Culori de sistem. Дочерние окна. Системные цвета

Windows păstrează 25 de culori de sistem pentru afişarea diferitelor părţi ale ecranului. Puteţi să obţineţi şi să stabiliţi aceste culori cu ajutorul funcţiilor GetSysColor şi SetSysColors. Identificatorii definiţi în fişierele antet din Windows specifică culorile de sistem. Stabilirea unei culori de sistem cu ajutorul funcţiei SetSysColors afectează culoarea respectivă numai pe durata sesiunii Windows curente.

Puteţi să schimbaţi o parte dintre culorile de sistem folosind secţiunea Display a Panoului de control din Windows (Control Panel) sau modificând secţiunea [colors] din fişierul WIN.INI. Secţiunea [colors] foloseşte pentru cele 25 de culori de sistem cuvinte cheie (altele decât identificatorii folosiţi de funcţiile GetSysColor şi SetSysColors) urmaţi de valori pentru culorile roşu, verde şi albastru. Aceste valori sunt cuprinse în intervalul 0 - 255. Tabelul următor prezintă modul în care sunt identificate cele 25 de culori de sistem, cu ajutorul identificatorilor folosiţi de funcţiile GetSysColor şi SetSysColors şi al cuvintelor cheie folosite în fişierul WIN.INI. Tabelul este ordonat crescător, în funcţie de valoarea constantelor COLOR_ (de la 0 la 24):

24

Page 25: Programare in Windows

GetSysColor şi SetSysColors WIN.INICOLOR_SCROLLBAR ScrollbarCOLOR_BACKGROUND Background

COLOR_ACTIVECAPTION ActiveTitle

COLOR_NACTIVECAPTION InactiveTitle

COLOR_MENU Menu

COLOR_WINDOW Window

COLOR_WINDOWFRAME WindowFrame

COLOR_MENUTEXT MenuText

COLOR_WINDOWTEXT WindowText

COLOR_CAPTIONTEXT TitleText

COLOR_ACTIVEBORDER ActiveBorder

COLOR_NACTIVEBORDER InactiveBorder

COLOR_APPWORKSPACE AppWorkspace

COLOR_HIGHLIGHT Hilight

COLOR_HIGHLIGHTTEXT HilightText

COLOR_BTNFACE ButtonFace

COLOR_BTNSHADOW ButtonShadow

COLOR_GRAYTEXT GrayText

COLOR_BTNTEXT ButtonText

COLOR_NACTIVECAPTIONTEXT InactiveTitleText

COLOR_BTNHIGHLIGHT ButtonHilight

COLOR_3DDKSHADOW ButtonDkShadow

COLOR_3DLIGHT ButtonLight

COLOR_NFOTEXT InfoText

COLOR_INFOBK InfoWindow

Valorile prestabilite pentru aceste culori sunt furnizate de driverul de afişare. Sistemul de operare Windows foloseşte valorile prestabilite, dacă acestea nu sunt suprascrise de valorile din secţiunea [colors] a fişierului WIN.INI, care poate fi modificată din Panoul de control.

34. Ferestre child. Interfața tastaturii. Дочерние окна. Интерфейс клавиатуры, поддерживаемый автоматически

Interfaţa automatizată cu tastaturaControalele de tip bară de derulare pot interpreta şi apăsările de taste, dar numai atunci când deţin cursorul de intrare. Tabelul următor prezintă modul în care apăsarea tastelor de deplasare este transformată în mesaje:

25

Page 26: Programare in Windows

Tasta de deplasareValoarea wParam din mesajul barei de derulare

26

Page 27: Programare in Windows

Home , SB_TOPEnd SB_BOTTOMPage Up SB_PAGEUPPage Down SB_PAGEDOWNLeft sau Up SB_LINEUPRight sau Down SB_LINEDOWN

De fapt, mesajele SB_TOP şi SB_BOTTOM nu pot fi generate decât prin folosirea tastaturii. Dacă doriţi ca un control de tip bară de derulare să obţină cursorul de intrare atunci când se execută clic pe aceasta, trebuie să includeţi identificatorul WS_TAB-STOP în parametrul de stil al ferestrei la apelarea funcţiei CreateWindow. Atunci când o bară de derulare deţine cursorul de intrare, pe caseta de derulare de pe ea este afişat un bloc gri care clipeşte.

Totuşi, pentru a furniza barelor de derulare o interfaţă completă cu tastatura, este necesară ceva mai multă muncă. Mai întâi, procedura WndProc trebuie să acorde explicit barei de derulare cursorul de intrare. Acest lucru se face prin prelucrarea mesajului WM_SETFOCUS, pe care fereastra părinte îl primeşte atunci când primeşte cursorul de intrare. WndProc cedează pur şi simplu cursorul de intrare uneia dintre barele de derulare:

SetFocus (hwndScrol[iFocus]) ;

35. Ferestre child. Introducerea unei proceduri de fereastră noi. Дочерние окна. Введение новой оконной процедуры

Deşi puteţi să creaţi propriile controale de tip fereastră descendent, puteţi să beneficiaţi şi de avantajele oferite de un set de clase de fereastră (şi proceduri specifice) predefinite, clase pe care programele pot să le folosească pentru crearea unor controale de tip fereastră descendent standard, pe care cu siguranţă le-aţi mai văzut şi în alte programe pentru Windows. Aceste controale apar sub forma butoanelor, casetelor de validare, casetelor de editare, casetelor listă, casetelor combinate, a şirurilor de caractere şi a barelor de derulare. De exemplu, dacă doriţi să puneţi un buton cu eticheta „Recalculare" într-un colţ al unui program de calcul tabelar, puteţi să îl creaţi printr-un simplu apel al procedurii CreateWindow. Nu trebuie să vă faceţi griji în privinţa logicii de interpretare a clicurilor de mouse, a redesenării butonului sau a modului în care butonul se mişcă atunci când este apăsat. Tot ce trebuie să faceţi este să interceptaţi mesajul WM_COMMAND - acesta este modul în care butonul informează procedura ferestrei despre declanşarea unui eveniment.

36. Ferestre child. Mesajul WM_CTLCOLORBT. Дочерние окна. Сообщение WM_CTLCOLORBT

O abordare mai potrivită (din nou, teoretic) este prelucrarea mesajului WM_CTLCOLORBTN. Acesta este un mesaj pe care controalele de tip buton îl trimit către procedura ferestrei părinte atunci când fereastra descendent este pe cale de a executa o redesenare. Fereastra părinte poate să folosească această ocazie ca să modifice culorile pe care procedura ferestrei descendent urmează să le folosească pentru desenare. (In versiunile pe 16 biţi ale sistemului de operare Windows, un mesaj numit WM_CTLCOLOR era folosit pentru toate controalele. Acesta a fost înlocuit cu mesaje separate pentru fiecare tip standard de control.)

Atunci când procedura părinte recepţionează mesajul WM_CTLCOLORBTN, parametrul wParam conţine variabila handle a contextului de dispozitiv al butonului, iar IParatn este variabila handle a ferestrei butonului. Atunci când fereastra părinte primeşte acest mesaj, controlul de tip buton a obţinut deja contextul de dispozitiv. În timpul prelucrării mesajului WM_CTLCOLORBTN:

■Stabiliţi (opţional) o culoare de text cu ajutorul funcţiei SetTextColor.

■Stabiliţi (opţional) o culoare de fond cu ajutorul funcţiei SetBkColor.

■Returnaţi către fereastra descendent o variabilă de manipulare a pensuleicu care se va desena.

Teoretic, fereastra descendent foloseşte pensula respectivă pentru colorarea fondului. Este sarcina dumneavoastră să distrugeţi pensula atunci când aceasta nu mai este necesară.

Totuşi, în legătură cu mesajul WM_CTLCOLORBTN este o problemă: acesta nu este trimis decât de butoanele de apăsare şi de butoanele desenate de proprietar; de asemenea, numai butoanele desenate de

Page 28: Programare in Windows

proprietar răspund la prelucrarea făcută mesajului de către fereastra părinte, folosind pensula trimisă pentru colorarea fondului. Oricum, acest lucru este inutil, deoarece fereastra părinte este răspunzătoare de desenarea acestor butoane.

37. Ferestre child. Radio buttons. Дочерние окна. Переключатели

Butoanele radio seamănă cu casetele de validare, cu excepţia faptului că au formă circulară, nu dreptunghiulară. Un punct mare în interiorul cercului indică butonul radio care a fost selectat. Stilurile de fereastră pentru butoanele radio sunt BS_RADIOBUTTON şi BS_AUTORADIOBUTTON, dar cel de-al doilea este folosit numai în casetele de dialog. În casetele de dialog, grupurile de butoane radio sunt folosite de obicei pentru indicarea opţiunilor care se exclud reciproc. Spre deosebire de casetele de validare, butoanele radio nu funcţionează precum comutatoarele - aceasta înseamnă că atunci când executaţi un al doilea clic pe un buton radio, starea acestuia rămâne neschimbată.

Atunci când recepţionaţi un mesaj WM_COMMAND de la un buton radio, trebuie să afişaţi marcajul de validare al acestuia, trimiţând mesajul BM_SETCHECK cu parametrul wParam egal cu 1:

SendMessage (hwndButton, BM_SETCHECK, 1, 0) ;

Pentru toate celelalte butoane radio din acelaşi grup puteţi să ştergeţi marcajul de validare trimiţându-le mesajul BM_SETCHECK cu parametrul wParam egal cu 0:

SendMessage (hwndButton, BM_SETCHECK, 0, 0) ;

38. Ferestre child. Stilurile clasa de redactare. Дочерние окна. Стили класса редактирования

Stilurile clasei editPutem crea un control de editare folosind clasa de ferestre ,,edit" la apelarea funcjiei CreateWindow - stilul ferestrei este WS_CHILD, plus cateva optiuni. Ca si in cazul controalelor statice de tip fereastra descendent, textul din controalele de editare poate fi aliniat la dreapta, aliniat la stanga sau centrat. Modul de aliniere este specificat prin stilurile de fereastra ES_LEFT, ES_RIGHT si ES_CENTER.

In mod prestabilit, un control de editare are o singura linie. Putefi sa creafi controale de editare multilinie folosind stilul de fereastra ES_MULTILINE. In cazul unui control de editare cu o singura linie puteti, In mod normal, sa introduce text numai pana la sfarsitul dreptunghiului care limiteaza controlul de editare. Pentru crearea unui control care face automat derularea orizontala a textului, folosifi stilul ES_AUTOHSCROLL. In cazul controalelor de derulare multilinie, textul este trecut automat pe o linie noua, exceptand situatiile in care folositi stilul de fereastra ES_AUTOHSCROLL, caz in care trebuie sa apasafi tasta Enter ca sa treceti la o linie noua. De asemenea, puteti sa included posibilitatea de derulare verticals automata a textului intr-un control de editare multilinie, folosind stilul ES_AUTOVSCROLL.

Atunci cand include^ intr-un control de editare multilinie stiluri de derulare a textului, puteti sa adaugafi si bare de derulare. Acest lucru se face cu ajutorul acelorasi identificatori ca si in cazul ferestrelor principale: WS_HSCROLL si WS_VSCROLL.'

In mod prestabilit, controalele de editare nu sunt incadrate de un chenar. Putefi sa adaugafi un chenar folosind stilul WS_BORDER.

Atunci cand selectafi un bloc de text intr-un control de editare, textul respectiv este afisat in mod video invers. Totusi, atunci cand controlul pierde cursorul de intrare, textul selectat nu mai este marcat. Daca vreti ca textul selectat sa fie marcat chiar si dupa ce controlul nu mai define cursorul de intrare, puteti sa folosifi stilul ES_NO-HIDESEL.

Atunci cand programul POPAD1 creeaza controlul de editare, stilul folosit la apelarea funcfiei CreateWindow este specificat astfel:

WS CHILD ! WSJISIBLE | WS_HSCROLL | WS VSCROLL | WS BORDER ! ES_LEFT | ES HULTILINE~| ES~AUTOHSCROLL | ES_AUTOVSCROLL

In POPPAD1, dimensiunile controlului de editare sunt stabilite ulterior, la apelarea funcfiei MoveWindow, dupa ce funcfia WndProc primeste mesajul WMJ5IZE. Dimen-siunea controlului de editare este stability initial la dimensiunile ferestrei principale:

MoveWindow (hwndEdit, 0, 0, LOWORD (IParam),

Page 29: Programare in Windows

HIWORD (IParam), TRUE) ;

Pentru controalele de editare pe o singura linie, inalfimea controlului trebuie sa corespunda inalfimii unui caracter. In cazul in care controlul de editare are un chenar

(si majoritatea au), folosifi ca dimensiune de 1,5 ori inalfimea unui caracter (indusiv spatiul extern).

39. Ferestre child. Utilizarea ferestrelor de redactare de control. Дочерние окна. Использование управляющих окон редактирования

40. Ferestre. Culoarea butoanelor. Дочерние окна. Цвета кнопок

O abordare mai potrivită (din nou, teoretic) este prelucrarea mesajului WM_CTLCOLORBTN. Acesta este un mesaj pe care controalele de tip buton îl trimit către procedura ferestrei părinte atunci când fereastra descendent este pe cale de a executa o redesenare. Fereastra părinte poate să folosească această ocazie ca să modifice culorile pe care procedura ferestrei descendent urmează să le folosească pentru desenare. (In versiunile pe 16 biţi ale sistemului de operare Windows, un mesaj numit WM_CTLCOLOR era folosit pentru toate controalele. Acesta a fost înlocuit cu mesaje separate pentru fiecare tip standard de control.)

Atunci când procedura părinte recepţionează mesajul WM_CTLCOLORBTN, parametrul wParam conţine variabila handle a contextului de dispozitiv al butonului, iar IParatn este variabila handle a ferestrei butonului. Atunci când fereastra părinte primeşte acest mesaj, controlul de tip buton a obţinut deja contextul de dispozitiv. În timpul prelucrării mesajului WM_CTLCOLORBTN:

■Stabiliţi (opţional) o culoare de text cu ajutorul funcţiei SetTextColor.

■Stabiliţi (opţional) o culoare de fond cu ajutorul funcţiei SetBkColor.

■Returnaţi către fereastra descendent o variabilă de manipulare a pensuleicu care se va desena.

41. Ferestrele child de control. Дочерние окна управления42. Folosirea peniţelor de stoc. Crearea, selectarea şi ştergerea peniţelor. Funcţiile CreatePen,

CreatePenIndirect,

Crearea, selectarea şi ştergerea peniţelor

Deşi folosirea peniţelor definite ca obiecte de stoc este foarte convenabilă, aveţi la dispoziţie doar un număr limitat de opţiuni: o peniţă neagră, pentru linie continuă, una albă pentru linie continuă sau una care nu desenează nimic. Dacă doriţi ceva diferit, trebuie să creaţi propriile dumneavoastră peniţe. Iată procedura generală: creaţi mai întâi o „peniţă logică" (logical pen) care este doar o descriere a unei peniţe, folosind funcţiile CreatePen sau CreatePenIndirect. (De asemenea, puteţi să folosiţi şi funcţia ExtCreatePen, despre care vom discuta atunci când vom ajunge la căile GDI.) Aceste funcţii returnează o variabilă handle a peniţei logice. Selectaţi peniţa în contextul de dispozitiv apelând funcţia SelectObject. Apoi puteţi să desenaţi linii cu peniţa selectată. La un moment dat nu poate fi selectată, într-un context de dispozitiv, decât o peniţă. După ce ştergeţi contextul de dispozitiv (sau după ce selectaţi o altă peniţă în contextul de dispozitiv) puteţi să ştergeţi peniţa logică pe care aţi creat-o apelând funcţia DeleteObject. După ce faceţi acest lucru, variabila handle a peniţei nu mai este validă.

O peniţă logică este un „obiect GDI". Dumneavoastră creaţi şi folosiţi peniţa, dar aceasta nu aparţine programului, ci modulului GDI. Peniţa este unul dintre cele şase obiecte GDI pe care puteţi să le creaţi. Celelalte cinci sunt pensulele, imaginile bitmap, regiunile, fonturile şi paletele. Exceptând paletele, toate celelalte obiecte pot fi selectate în contextul de dispozitiv folosind funcţia SelectObject.

Pentru folosirea obiectelor GDI trebuie să respectaţi următoarele reguli:

La sfârşitul programului ştergeţi toate obiectele GDI pe care le-aţi creat. Nu ştergeţi obiectele GDI în timp ce sunt selectate într-un context de dispozitiv valid. Nu ştergeţi obiectele de stoc.

Page 30: Programare in Windows

Chiar dacă sunt nişte reguli rezonabile, neplăcerile pot exista. Voi prezenta câteva exemple ca să înţelegeţi cum funcţionează aceste reguli şi pentru a putea evita eventuale probleme.

Sintaxa generică a funcţiei CreatePen este:hPen = CreatePen (iPenStyle, iWidth, rgbColor) ;

+Puteţi să creaţi o peniţă şi cu ajutorul unei structuri de tip LOGPEN („logical pen"), apelând funcţia CreatePenIndirect. Dacă programul foloseşte un număr mai mare de peniţe diferite pe care Ie iniţializaţi în codul sursă, această metodă este mai eficientă. Mai întâi definiţi o variabilă de tip LOGPEN, cum ar fi logpen:

LOGPEN logpen ;

Această structură are trei membri: lopnStyle (UINT) este stilul peniţei, lopn Width (POINT) este grosimea peniţei în unităţi logice, iar lopnColor (COLORREF) este culoarea peniţei. Membrul lopn Width este o structură de tip POINT, dar Windows foloseşte numai membrul lopnWidth.x al acestei structuri şi ignoră membrul lopnWidth.y. Apoi creaţi peniţa, transmiţând funcţiei CreatePenIndirect adresa structurii logpen:

hPen = CreatePenIndirect (&logpen) ;

De asemenea, puteţi să obţineţi informaţii despre o peniţă logică existentă. Dacă aveţi deja o variabilă handle a unei peniţe puteţi să copiaţi datele care definesc peniţa logică într-o structură de tip LOGPEN folosind funcţia GetObject:

GetObject (hPen, sizeof (LOGPEN), (LPVOID) &logpen) ;

Remarcaţi faptul că funcţiile CreatePen şi CreatePenIndirect nu au nevoie de o variabilă handle a contextului de dispozitiv. Aceste funcţii creează peniţe logice care nu au nici o legătură cu contextul de dispozitiv până când nu apelaţi funcţia SelectObject. De exemplu, puteţi să folosiţi aceeaşi peniţă logică pentru mai multe dispozitive, cum ar fi imprimanta şi ecranul.

Iată o metodă pentru crearea, selectarea şi ştergerea peniţelor. Să presupunem că programul foloseşte trei peniţe: una neagră cu grosimea de un pixel, una roşie cu grosimea de trei pixeli şi una neagră cu linie punctată. Mai întâi definiţi variabilele pentru stocarea variabilelor handle ale celor trei stilouri:

static HPEN hPen1, hPen2, hPen3;

În timpul prelucrării mesajului WM_CREATE, creaţi cele trei peniţe:hPen1 = CreatePen (PS_SOLID, 1, 0);hPen2 = CreatePen (PS_SOLID, 3, RGB (255, 0, 0));hPen3 = CreatePen (PS_DOT, 0, 0);

În timpul prelucrării mesajului WM_PAINT (sau în orice alt moment în care aveţi o variabilă handle validă a contextului de dispozitiv) puteţi să selectaţi oricare dintre aceste peniţe în contextul de dispozitiv şi să desenaţi:

SelectObject (hdc, hPen2) ;[funcţii de desenare a liniilor] SelectObject (hdc, hPen1) ;[alte funcţii de desenare a liniilor]

În timpul prelucrării mesajului WM_DESTROY puteţi să ştergeţi cele trei peniţe create:DeleteObject (hPen1) ;DeleteObject (hPen2) ;DeleteObject (hPen3) ;

Aceasta este metoda cea mai directă pentru crearea, selectarea şi ştergerea peniţelor, dar are dezavantajul că peniţele create ocupă spaţiu în memorie pe toată durata rulării programului. O altă soluţie este să creaţi peniţele necesare în timpul prelucrării fiecărui mesaj WM_PAINT şi să le ştergeţi după apelarea funcţiei EndPaint. (Puteţi să le ştergeţi şi înainte de apelarea funcţiei EndPaint, dar trebuie să aveţi grijă să nu ştergeţi peniţa curentă selectată în contextul de dispozitiv.)

Page 31: Programare in Windows

De asemenea, puteţi să creaţi peniţele atunci când sunt necesare, combinând funcţiile CreatePen şi SelectObject în aceeaşi instrucţiune:

SelectObject (hdc, CreatePen (PS_DASH, 0, RGB (255, 0, 0))) ;

Din acest moment, veţi folosi o peniţă pentru linii întrerupte de culoare roşie. După ce terminaţi de făcut desenele cu acest tip de linie, puteţi să ştergeţi peniţa. Hopa! Cum puteţi să ştergeţi peniţa dacă nu aţi salvat variabila handle a acesteia? Vă amintiţi că funcţia SelectObject returnează variabila handle a peniţei selectate anterior în contextul de dispozitiv. Aşa că puteţi să ştergeţi peniţa selectând în contextul de dispozitiv peniţa de stoc BLACK_PEN şi ştergând variabila handle returnată de funcţia SelectObject:

DeleteObject (SelectObject (hdc, GetStockObject (BLACK PEN))) ;

Există şi o altă metodă. Atunci când selectaţi în contextul de dispozitiv o peniţă nou creată, salvaţi variabila handle returnată de funcţia SelectObject;

hPen = SelectObject (hdc, CreatePen (PS_DASH, 0, RGB (255, 0, 0))) ;

Ce este hPen? Dacă aceasta este prima apelare a funcţiei SelectObject de la obţinerea contextului de dispozitiv, atunci hPen este variabila handle a obiectului de stoc BLACK_PEN. Puteţi acum să selectaţi peniţa BLACK_PEN în contextul de dispozitiv şi să o ştergeţi pe cea creată (variabila handle returnată la a doua apelare a funcţiei SelectObject) printr-o singură instrucţiune:

DeleteObject (SelectObject (hdc, hPen)) ;

43. ExtCreatePen. Parametrii iPenStyle, iWidth, rgbColor. Использование стоковых перьев. Создание, выбор, и удаление пера. Функции CreatePen, CreatePenIndirect, ExtCreatePen. Параметры iPenStyle, iWidth, rgbColor.

Parametrul iPenStyle precizează dacă se desenează o linie continuă, o linie punctată sau una întreruptă. Parametrul iPenStyle poate fi unul dintre identificatorii definiţi în fişierele antet din Windows. Figura 4-16 prezintă stilul de linie produs de fiecare identificator.

Figura 4-16. Cele şapte stiluri de penite.

Pentru stilurile PS_SOLID, PS_NULL şi PS_INSIDEFRAME, parametrul iWidth reprezintă grosimea liniei. Dacă iWidth are valoarea 0, liniile desenate de Windows vor avea grosimea de un pixel. Liniile desenate cu peniţele de stoc au grosimea de un pixel. Dacă parametrul iWidth are o valoare mai mare de 1, Windows va desena o linie continuă.

Parametrul rgbColor din funcţia CreatePen este un număr întreg fără semn reprezentând culoarea peniţei. Pentru toate stilurile de peniţe, exceptând PS_INSIDEFRAME, atunci când selectaţi peniţa în contextul de dispozitiv, Windows converteşte acest parametru la cea mai apropiată culoare pură pe care o poate reprezenta dispozitivul de afişare. PS_INSIDEFRAME este singurul stil care poate folosi culori amestecate, dar numai pentru grosimi mai mari de un pixel.

44. Formatarea textului. Funcţia wsprintf. Funcţia GetSystemMetrics. Форматирование текста. Функция wsprintf. Функция GetSystemMetrics.

Page 32: Programare in Windows

Fomatarea textuluiDeoarece dimensiunile fontului sistem nu se modifică în timpul unei sesiuni Windows, trebuie să

apelaţi funcţia GetTextMetrics o singură dată după lansarea în execuţie a programului. Un loc potrivit pentru acest apel este codul de prelucrare a mesajului WM_CREATE din procedura de fereastră. Mesajul WM_CREATE este primul mesaj pe care îl primeşte procedura de fereastră. Windows trimite un mesaj WM_CREATE procedurii de fereastră atunci când apelaţi funcţia CreateWindow din funcţia WinMain.

Să presupunem că scrieţi un program Windows care afişează mai multe linii de text una sub alta, în zona client a ferestrei. Trebuie să obţineţi valorile pentru înălţimea şi lăţimea caracterelor. În procedura de fereastră puteţi să definiţi două variabile în care să salvaţi lăţimea medie (cxChar) şi înălţimea totală a caracterelor (cyChar):

static int cxChar, cyChar ;Prefixul c adăugat la numele variabilelor provine de la „contor" şi în combinaţie cu x sau y se

referă la dimensiunea pe orizontală sau pe verticală a caracterelor. Aceste variabile sunt declarate ca statice deoarece trebuie să fie valide atunci când procedura de fereastră prelucrează alte mesaje (cum ar fi WM_PAINT). Dacă variabilele sunt declarate ca globale în alte funcţii nu mai este necesar să fie declarate statice. Iată codul de prelucrare a mesajului WM_CREATE:case WH_CREATE:

hdc = GetDC (hwnd);GetTextMetrics (hdc, &tm);cxChar = tm.tmAveCharWidth;cyChar = tm.tmHeight + tm.tmExternalLeading;ReleaseDC (hwnd, hdc);

return 0;Dacă nu vreţi să folosiţi spaţiul suplimentar extern pentru spaţierea liniilor de text, puteţi să

utilizaţi instrucţiunea:cyChar = tm.tmHeight;

Pentru afişarea mai multor linii de text aliniate la stânga, folosiţi valoarea cxChar pentru parametrul care reprezintă coordonata pe axa x în apelul funcţiei TextOut. Valoarea coordonatei pe axa y la apelarea funcţiei TextOut este:

cyChar * (1 + i);unde i reprezintă numărul liniei, începând de la 0.

Deseori este necesar să afişaţi atât numere formatate, cat şi şiruri simple de caractere. Dacă aţi scris programe DOS folosind funcţiile standard C de bibliotecă, probabil aţi folosit pentru formatarea numerelor funcţia printf. În Windows nu puteţi să folosiţi funcţia printf, deoarece ea determină afişarea la dispozitivul standard de ieşire, acesta fiind un concept care în Windows nu are nici un sens.

În schimb, puteţi să folosiţi funcţia sprintf. Funcţia sprintf lucrează la fel ca şi funcţia printf, cu excepţia faptului că şirul de caractere formatat este stocat într-o matrice de caractere. Apoi puteţi utiliza funcţia TextOut pentru a scrie şirul ce trebuie afişat. Funcţia sprintf returnează lungimea şirului de caractere, ceea ce este foarte convenabil - puteţi să transmiteţi valoarea returnată în locul parametrului iLength din apelul funcţiei TextOut. Secvenţa de cod de mai jos prezintă o combinaţie tipică a funcţiilor TextOut şi sprintf:

int iLenght;char szBuffer [40];[alte Iinii de program]iLenght = sprintf (szBuffer, "The sum of %d and %d is %d"), nA, nB, nA + nB);TextOut (hdc, x, y, szBuffer, iLength);Pentru operaţiile simple de afişare precum cea de mai sus puteţi să renunţaţi la definirea variabilei

iLength şi să combinaţi cele două instrucţiuni rămase într-una singură:TextOut (hdc, x, y, szBuffer, sprintf (szBuffer, "The sum of %d and %d is %d", nA, nB,

nA + nB));Nu arată prea frumos, dar funcţionează.

Page 33: Programare in Windows

Dacă nu trebuie să afişaţi numere în virgulă mobilă, puteţi folosi funcţia wsprintf în locul funcţiei sprintf. Funcţia wsprintf are aceeaşi sintaxă ca şi funcţia sprintf, dar este inclusă în Windows, aşa că nu va mări dimensiunea fişierului executabil.

45. Funcţia GetDeviceCaps. Dimensiunea dispozitivului. Valorile HORZSIZE şi VERTSIZE, HORZRES şi VERTRES, ASPECTX, ASPECTY şi ASPECTXY, LOGPIXELSX şi LOGPIXELSY. Функция GetDeviceCaps. Размеры устройства. Значения HORZSIZE и VERTSIZE, HORZRES и VERTRES, ASPECTX, ASPECTY и ASPECTXY, LOGPIXELSX и LOGPIXELSY.

Un context de dispozitiv se referă, de obicei, la un dispozitiv fizic de ieşire, cum ar fi un monitor video sau o imprimantă. Dacă aveţi nevoie de anumite informaţii despre acest dispozitiv, cum ar fi dimensiunile ecranului (dimensiunile în pixeli şi cele fizice) sau posibilităţile de folosire a culorilor, puteţi să le obţineţi prin apelarea funcţiei GetDeviceCaps („get device capabilities"):

iValue = GetDeviceCaps (hdc, iIndex) ;

Parametrul iIndex este unul dintre cei 28 de identificatori definiţi în fişierele antet din Windows. De exemplu, dacă iIndex are valoarea HORZRES funcţia GetDeviceCaps returnează lăţimea dispozitivului în pixeli; VERTRES returnează înălţimea dispozitivului în pixeli. Dacă hdc este o variabilă handle a contextului de dispozitiv pentru un monitor video, informaţiile obţinute sunt aceleaşi cu cele returnate de funcţia GetSystemMetrics. Dacă hdc este o variabilă handle a contextului de dispozitiv pentru o imprimantă, funcţia GetDeviceCaps returnează înălţimea şi lăţimea zonei pe care imprimantă o poate tipări.

Puteţi să folosiţi funcţia GetDeviceCaps şi ca să obţineţi informaţii despre posibilităţile unui dispozitiv de prelucrare a anumitor tipuri de elemente grafice. Această posibilitate nu este importantă pentru ecran, dar poate fi folosită în cazul imprimantelor. De exemplu, majoritatea plotterelor nu pot tipări imagini bitmap - iar funcţia GetDeviceCaps vă poate comunica acest lucru.

46. Funcţia TextOut. Contextul de dispozitiv. Obţinerea unei variabile handle a contextului de dispozitiv: Функция TextOut. Контекст устройства. Получение переменной handle DC:

Funcţia TextOut: detaliiFuncţia GDI TextOut:

TextOut (hdc, x, y, psString, iLength) ;Primul parametru este o variabilă handle a contextului de dispozitiv - valoarea hdc returnată de

funcţia GetDC sau valoarea hdc returnată de funcţia BeginPaint în timpul prelucrării mesajului WM_PAINT.

Atributele contextului de dispozitiv controlează caracteristicile textului afişat. De exemplu, unul dintre atributele contextului de dispozitiv stabileşte culoarea textului. Culoarea prestabilită este negru. De asemenea, contextul prestabilit de dispozitiv stabileşte ca fondul să fie alb. Atunci când un program afişează un text pe ecran, Windows foloseşte această culoare de fond ca să umple spaţiul dreptunghiular care înconjoară fiecare caracter, spaţiu numit „casetă caracter" („character box").

Parametrul psString este un pointer la un şir de caractere, iar iLength este lungimea acestui şir de caractere, adică numărul de caractere conţinut de şir. Şirul de caractere nu poate conţine caractere

Valorile x şi y din apelul funcţiei TextOut definesc începutul şirului de caractere în zona client a ferestrei. Valoarea x indică poziţia pe orizontală, iar valoarea y indică poziţia pe verticală. Colţul din stânga-sus al primului caracter se află în poziţia de coordonate (x,y).

O variabilă handle, aşa cum am mai spus, este pur şi simplu un număr pe care Windows îl foloseşte pentru indicarea unui obiect. Puteţi să obţineţi această variabilă din Windows şi apoi să o folosiţi în alte funcţii. Variabila handle a contextului de dispozitiv este calea de acces a ferestrei dumneavoastră la funcţiile GDI. Folosind această variabilă sunteţi liber să desenaţi zona client a ferestrei şi să o faceţi aşa cum doriţi.

Page 34: Programare in Windows

Contextul de dispozitiv (prescurtat DC - device context) este o structură de date întreţinută intern de interfaţa GDI. Fiecare context de dispozitiv este asociat unui anumit dispozitiv de afişare, cum ar fi imprimanta, plotterul sau monitorul video. În cazul monitoarelor video, un context de dispozitiv este de obicei asociat unei anumite ferestre de pe ecran.

O parte dintre valorile din contextul de dispozitiv sunt atribute grafice. Aceste atribute definesc unele particularităţi privind modul de lucru al unor funcţii de desenare din interfaţa GDI. În cazul funcţiei TextOut, de exemplu, atributele contextului de dispozitiv determină culoarea textului, culoarea fondului, modul de mapare a coordonatelor x şi y în zona client a ferestrei şi fontul folosit de Windows pentru afişarea textului.

Atunci când vrea să deseneze, programul trebuie să obţină mai întâi o variabilă handle a unui context de dispozitiv. După terminarea operaţiilor de desenare, programul ar trebui să elibereze variabila. După eliberarea variabilei handle, aceasta nu mai este validă şi, deci, nu mai poate fi folosită. Programul trebuie să obţină şi să elibereze variabila handle în timpul prelucrării unui singur mesaj. Cu excepţia contextelor de dispozitiv create cu funcţia CreateDC (despre care nu vom discuta în acest capitol) este recomandat să nu păstraţi variabilele handle ale contextelor de dispozitiv de la un mesaj la altul.

În general, aplicaţiile Windows folosesc două metode pentru obţinerea variabilelor handle ale contextelor de dispozitiv, atunci când se pregătesc pentru desenarea ecranului.

Obţinerea unei variabile handle a contextului de dispozitiv: prima metodăAceastă metodă este folosită în timpul prelucrării mesajelor WM_PAINT. Sunt implicate două

funcţii: BeginPaint şi EndPaint. Aceste funcţii au nevoie de variabila handle a ferestrei (transmisă procedurii de fereastră ca parametru) şi de adresa unei variabile de tipul PAINTSTRUCT. De obicei, programatorii Windows numesc această variabilă ps şi o definesc în procedura de fereastră astfel:

PAINTSTRUCT ps;În timpul prelucrării mesajului WM_PAINT, procedura de fereastră apelează mai întâi funcţia

BeginPaint ca să completeze câmpurile structurii ps. Valoarea returnată de funcţia BeginPaint este variabila handle a contextului de dispozitiv. În general, aceasta este salvată într-o variabilă numită hdc. În prcedura de fereastră definiţi această variabilă astfel:

HDC hdc;Tipul de date HDC este definit ca un întreg fără semn, pe 32 de biţi. Programul poate apoi să

folosească funcţii GDI, cum ar fi TextOut. Apelul funcţiei EndPaint eliberează variabila handle a contextului de dispozitiv.

În general, prelucrarea mesajului WM_PAINT se face astfel:case WM_PAINT :

hdc = BeginPaint (hwnd, &ps) ;[apelarea unor funcţii GDI] EndPaint (hwnd, &ps) ;

return 0 ;În timpul prelucrării mesajului WM_PAINT, procedura de fereastră trebuie să apeleze funcţiile

BeginPaint şi EndPaint. Dacă o procedură de fereastră nu prelucrează mesajele WM_PAINT, trebuie să le retransmită procedurii DefWindowProc (procedura de fereastră prestabilită).

DefWindowProc prelucrează mesajele WM_PAINT în următoarea secvenţă de cod:case WM_PAINT:BeginPaint (hwnd, &ps) ;EndPaint (hwnd, &ps) ;return 0 ;

Apelarea în secvenţă a funcţiilor BeginPaint şi EndPaint fără nici o altă instrucţiune intermediară nu face decât să valideze regiunea invalidată anterior. Aşadar, nu procedaţi astfel:

case WM_PAINT:return 0 ; // GREŞIT !!!

Page 35: Programare in Windows

Windows plasează un mesaj WM_PAINT în coada de aşteptare, deoarece o parte a zonei client este invalidă. Dacă nu apelaţi funcţiile BeginPaint şi EndPaint (sau ValidateRect) Windows nu validează zona de fereastră respectivă, ci vă trimite în continuare mesaje WM_PAINT.

47. Funcţiile SetScrollRange (hwnd, iBar, iMin, iMax, bRedraw), SetScrollPos (hwnd, iBar, iPos, bRedraw).

SetScrollRange (hwnd, iBar, iMin, iMax, bRedraw) ;Parametrul iBar poate avea una dintre valorile SB_VERT şi SB_HORZ, iar iMin şi iMax sunt

poziţiile minimă şi maximă din domeniu, în timp ce bRedraw trebuie să aibă valoarea TRUE dacă vreţi ca Windows să redeseneze bara de derulare pe baza noului domeniu stabilit.

Poziţia casetei de derulare este reprezentată întotdeauna printr-o valoare întreagă. De exemplu, o bară de derulare cu domeniul cuprins între 0 şi 4 are cinci poziţii ale casetei de derulare, aşa cum se poate vedea în Figura 3-8. Puteţi să folosiţi funcţia SetScrollPos ca să stabiliţi o nouă poziţie a casetei de derulare pe bara de derulare:

SetScrollPos (hwnd, iBar, iPos, bRedraw) ;

Figura 3-8. Bare de derulare cu cinci poziţii ale casetei de derulare.Parametrul iPos reprezintă noua poziţie, care trebuie să fie cuprinsă în domeniul delimitat de iMin

şi iMax. Windows conţine funcţii asemănătoare (GetScrollRange şi GefScrollPos) pentru obţinerea domeniului şi a poziţiei unei bare de derulare.

Atunci când folosiţi bare de derulare într-un program Windows, răspunderea pentru întreţinerea şi actualizarea acestora este împărţită între dumneavoastră şi sistemul de operare. Sistemul de operare Windows are următoarele sarcini:

Tratează operaţiile executate cu mouse-ul asupra barei de derulare. .Afişează în video invers zona pe care utilizatorul execută clic.

Mută caseta de derulare atunci când utilizatorul o trage cu ajutorul mouse-ului. Trimite mesaje din partea barei de derulare către procedura de fereastră care o conţine.Funcţiile programului în această privinţă sunt: Iniţializarea domeniului barei de derulare. Prelucrarea mesajului primit de la bara de derulare. Actualizarea poziţiei casetei de derulare de pe bară.

48. Ignorarea tastaturii. Cursorul de intrare. Acţionări de taste şi caractere. Игнорирование клавиатуры. Фокус ввода. Аппаратные и символьные сообщения.

PoziţiaPoziţia

Poziţia

PoziţiaPoziţia 4

Poziţia 0 Poziţia 1 Poziţia 2 Poziţia 3 Poziţia 4

Page 36: Programare in Windows

Deşi tastatura este principala sursă de intrări de la utilizatori a unui program pentru Windows, programele nu trebuie să reacţioneze la toate mesajele pe care le primeşte de la tastatură. Multe funcţii ale tastaturii sunt tratate chiar de Windows. De exemplu, puteţi să ignoraţi apăsările de taste legate de funcţii sistem. Acestea sunt, în general, combinaţii cu tasta Alt.

Programul nu este obligat să monitorizeze aceste taste, deoarece Windows îi comunică efectul acestora. (Totuşi, dacă este nevoie, programul poate face şi acest lucru.) De exemplu, dacă utilizatorul selectează un articol dintr-un meniu cu ajutorul tastaturii. Windows trimite programului un mesaj prin care îi comunică articolul de meniu selectat, indiferent dacă utilizatorul a folosit mouse-ul sau tastatura.

Unele programe pentru Windows folosesc „acceleratori" (sau „taste de accelerare") pentru opţiunile de meniu folosite mai frecvent. Acceleratorii sunt, de obicei, combinaţii de taste funcţionale - sau alte taste corespunzătoare unor caractere - cu tasta Ctrl. Tastele de accelerare sunt definite în fişierul de resurse al programului.

Casetele de dialog au şi ele o interfaţă cu tastatura, dar programele, în general, nu trebuie să monitorizeze tastatura cât timp este activă o casetă de dialog. Interfaţa cu tastatura este manipulată de Windows şi acesta trimite programului mesaje prin care îi comunică efectele tastelor apăsate. Casetele de dialog pot conţine controale de editare pentru introducerea textului. Acestea sunt mici casete, în care utilizatorul poate să scrie un şir de caractere. Windows asigură logica de funcţionare a controlului de editare şi furnizează programului conţinutul final al controlului, după ce utilizatorul a terminat.

Cursorul, cursorul, cine a luat cursorul de intrare?

Tastatura trebuie să fie partajată de toate aplicaţiile rulate simultan sub Windows. Unele aplicaţii pot avea mai multe ferestre, iar tastatura trebuie să fie partajată de toate ferestrele din cadrul aceleiaşi aplicaţii. Atunci când este apăsată o tastă, o singură fereastră trebuie să primească mesajul privind apăsarea tastei respective. Fereastra care primeşte acest mesaj este fereastra care deţine „cursorul de intrare" („input focus").

Conceptul cursorului de intrare este strâns legat de conceptul de „fereastră activă". Fereastra care deţine cursorul de intrare este fie fereastra activă, fie o fereastră descendent a ferestrei active. De obicei, fereastra activă este uşor de identificat. Dacă fereastra activă are o bară de titlu, Windows evidenţiază bara de titlu a acesteia. Dacă fereastra activă are un cadru de dialog (o formă des întâlnită în casetele de dialog) în locul unei bare de titlu, Windows evidenţiază acest cadru. Dacă fereastra activă a fost redusă la o pictogramă (minimizată), Windows evidenţiază textul afişat sub pictogramă.

Ferestrele descendent mai des întâlnite sunt controale, precum butoanele de apăsare, butoanele radio, casetele de validare, barele de derulare, casetele de editare şi casetele listă, care, de obicei, apar în casete de dialog. Ferestrele descendent nu sunt niciodată ferestre active. Dacă o fereastră descendent deţine cursorul de intrare, atunci fereastra activă este fereastra părinte. Controalele de tip fereastră descendent indică faptul că deţin cursorul de intrare prin afişarea unui cursor care clipeşte sau a unui cursor de editare.

Dacă fereastra activă a fost redusă la o pictogramă, atunci nici o fereastră nu deţine cursorul de intrare. Windows continuă să trimită programului mesaje de la tastatură, dar acestea sunt trimise într-o altă formă decât mesajele trimise unei ferestre active normale.

O procedură de fereastră poate să afle când are cursorul de intrare prin interceptarea mesajelor WM_SETFOCUS şi WM_KILLFOCUS. Mesajul WM_SETFOCUS indică faptul că fereastra primeşte cursorul de intrare (input focus), iar mesajul WM_KILLFOCUS indică faptul că fereastra pierde cursorul de intrare.

Acţionări de taste şi caractereMesajele privind tastatura pe care un program le recepţionează de la sistemul de operare fac

diferenţa între „acţionările de taste" („keystrokes") şi „caractere". Aceste noţiuni sunt legate de cele două moduri în care puteţi să priviţi tastatura. În primul rând, tastatura este o colecţie de taste. Tastatura are o singură tastă A; apăsarea tastei A este o acţionare de tastă, iar eliberarea tastei A este tot o acţionare de tastă. Tastatura este, însă, în acelaşi timp, şi un dispozitiv de intrare care generează

Page 37: Programare in Windows

caractere afişabile. Tasta A poate să genereze mai multe caractere, în funcţie de starea tastelor Ctrl, Shift şi Caps Lock. În mod normal, caracterul generat este a. Dacă tasta Shift este apăsată, sau tasta Caps Lock este activă, caracterul generat este A. Dacă tasta Ctrl este apăsată, caracterul generat este Ctrl+A. Dacă se foloseşte un driver de tastatură pentru o limbă străină, apăsarea tastei A poate să fie precedată de un „caracter mort" („dead-character key") sau de tastele Shift, Ctrl sau Alt în diferite combinaţii. Combinaţiile pot să genereze un caracter a sau A cu un accent.

Pentru acţionările de taste care generează caractere afişabile, Windows trimite programului atât mesaje pentru acţionarea de taste, cât şi mesaje pentru caractere. Unele taste nu generează caractere. Astfel de taste sunt Shift, tastele funcţionale, tastele de deplasare şi tastele speciale, precum Insert şi Delete. În cazul acestor taste, Windows generează numai mesaje pentru acţionari de taste.

49. În ce constă arhitectura orientată pe mesaje? В чем заключается архитектура ориентированная на сообщения?

Arhitectura bazată pe mesajeS-a dovedit că răspunsul la aceste întrebări este esenţial pentru înţelegerea arhitecturii folosite la

construirea interfeţelor grafice cu utilizatorul. În Windows, atunci când utilizatorul redimensionează o fereastră, sistemul de operare trimite programului un mesaj prin care îi comunică noile dimensiuni ale ferestrei. Programul poate apoi să modifice conţinutul ferestrei, astfel încât acesta să se adapteze la noile dimensiuni.

„Sistemul de operare trimite programului un mesaj." Sper că nu aţi luat ad-litteram sensul acestei propoziţii. Ce poate să însemne acest lucru? Pentru că nu este vorba despre un sistem de poştă electronică, ci despre cod, cum poate un sistem de operare să „trimită" mesaje către un program?

Atunci când am spus că „sistemul de operare trimite programului un mesaj" m-am referit la faptul că Windows apelează o funcţie din program. Parametrii acestei funcţii descriu mesajul respectiv. Această funcţie din programul Windows este cunoscută sub numele de „procedură de fereastră" („window procedure").

50. În ce constă consecvenţa privind interfaţa cu utilizatorul? В чем заключается последовательность интерфейса пользователя?

Consecvenţa privind interfaţă cu utilizatorulUtilizatorii nu mai sunt dispuşi să piardă prea mult timp pentru a învăţa cum se foloseşte

calculatorul sau cum să stăpânească un anumit program. Windows îi ajută în acest sens, deoarece toate programele pentru Windows au un aspect asemănător şi se comportă fundamental la fel. Programele ocupă o fereastră - o suprafaţă dreptunghiulară de pe ecran. Fereastra poate fi identificată datorită unei bare de titlu. Majoritatea funcţiilor oferite de program sunt apelate cu ajutorul unui meniu. Informaţiile afişate care nu încap pe un singur ecran pot fi vizualizate cu ajutorul barelor de derulare. Unele articole de meniu apelează casete de dialog în care utilizatorul introduce informaţii suplimentare. În majoritatea programelor mai mari pentru Windows există o casetă de dialog care deschide un fişier. Această casetă de dialog arată la fel (sau foarte asemănător) în mai multe programe Windows şi este apelată aproape întotdeauna cu aceeaşi opţiune de meniu.

După ce învăţaţi să folosiţi un program Windows, veţi fi capabil deja să utilizaţi orice alt program pentru Windows. Meniurile şi casetele de dialog permit utilizatorului să experimenteze diferite funcţii ale programului şi să îi exploreze posibilităţile. Majoritatea programelor Windows au interfaţă atât cu mouse-ul, cât şi cu tastatura. Deşi majoritatea funcţiilor unui program pentru Windows pot fi controlate şi cu ajutorul tastaturii, mouse-ul este deseori mai convenabil pentru diferite sarcini.

Din punctul de vedere al programatorului, consecvenţa în realizarea interfeţei cu utilizatorul este rezultatul folosirii procedurilor integrate în Windows pentru construirea meniurilor şi a casetelor de

Page 38: Programare in Windows

dialog. Toate meniurile au aceeaşi interfaţă cu tastatura şi cu mouse-ul deoarece aceste operaţii sunt manipulate mai degrabă de Windows, decât de programul respectiv.

51. În ce constă interfaţa grafică independentă de dispozitiv? В чем заключается независимость гр.интерфейса от устройства?

Interfaţa grafică independentă de dispozitivWindows este o interfaţă grafică şi programele pentru Windows pot folosi toate avantajele oferite de elementele grafice şi de textul formatat atât pentru ecran, cât şi pentru imprimantă. O interfaţă grafică nu are numai avantajul de a fi mai atractivă, ci şi pe acela de a furniza utilizatorului o cantitate mai mare de informaţii.Programele scrise pentru Windows nu au acces direct la componentele hardware ale dispozitivelor de afişare, cum ar fi ecranul sau imprimanta. În schimb, Windows foloseşte un limbaj de programare pentru grafică (numit GDI - Graphics Device Interface) care simplifică afişarea textului formatat şi a elementelor grafice. Windows transformă componentele hardware de afişare în dispozitive virtuale. Un program scris pentru Windows va rula cu orice placă video şi cu orice imprimanta pentru care Windows are un driver de dispozitiv. Programul nu trebuie să determine ce tip de dispozitiv fizic este ataşat la calculator.Crearea unei interfeţe grafice independente de dispozitiv pentru calculatoarele IBM PC nu a fost o sarcină uşoară pentru dezvoltatorii sistemului de operare Windows. Proiectarea calculatoarelor personale s-a făcut pe baza principiului arhitecturilor deschise. Producătorii terţi de componente hardware au fost încurajaţi să dezvolte dispozitive periferice, şi datorită acestui fapt au apărut o mulţime de astfel de dispozitive. Deşi au fost create anumite standarde, programele convenţionale pentru MS-DOS trebuie să asigure suportul individual pentru diferite configuraţii standard. De exemplu, este un lucru destul de obişnuit ca un program de procesare de texte pentru MS-DOS să fie livrat împreună cu câteva dischete care conţin mai multe fişiere mici, pentru adaptarea la diferite tipuri de imprimante. Programele Windows 95 nu au nevoie de drivere proprii, deoarece acestea sunt asigurate chiar de sistemul de operare.

52. Interfaţa pentru dispozitive grafice. Filozofia GDI. Structura interfeţei GDI. Tipuri de apeluri de funcţii. Primitive GDI. Интерфейс графических устройств. Филисофия GDI. Структура GDI. Типы вызовов функций. Примитивы GDI.

Filozofia GDI

În versiunile pe 32 de biţi ale sistemului de operare Windows elementele grafice sunt manipulate, în principal, prin funcţii exportate din biblioteca cu legături dinamice GDI32.DLL, care, la rândul ei, foloseşte biblioteca cu legături dinamice pe 16 biţi GDI.EXE. (În versiunile Windows anterioare, bibliotecile cu legături dinamice erau fişiere cu extensia .EXE, nu .DLL.) Aceste module apelează proceduri din diferite fişiere driver pentru afişare - un fişier .DRV pentru afişarea pe ecran şi, probabil, unul sau mai multe fişiere .DRV care controlează imprimantele şi plotterele. Driverele execută operaţiile de acces la componentele hardware ale monitorului video sau convertesc comenzile GDI în coduri sau comenzi pe care le pot interpreta diferite tipuri de imprimante. Pentru diferite tipuri de plăci video sau de imprimante este nevoie, desigur, de diferite fişiere driver.

Deoarece la calculatoarele compatibile PC pot fi ataşate diferite dispozitive de afişare, unul dintre scopurile principale ale interfeţei GDI este să permită manipularea elementelor grafice independent de dispozitiv. Programele scrise pentru Windows ar trebui să ruleze fără probleme, indiferent de dispozitivul grafic de afişare folosit, dacă acesta este acceptat de sistemul de operare. Interfaţa GDI realizează acest lucru prin furnizarea unor componente care izolează programele de caracteristicile particulare ale diferitelor dispozitive de ieşire.

Page 39: Programare in Windows

Lumea dispozitivelor grafice de ieşire este împărţită în două categorii: dispozitive rastru şi dispozitive vectoriale. Majoritatea dispozitivelor de ieşire pentru PC sunt dispozitive rastru, ceea ce înseamnă că reprezentarea imaginilor se face prin matrice de puncte. Această categorie include plăcile video, imprimantele matriceale şi imprimantele laser. Dispozitivele vectoriale, care desenează imaginile prin linii, sunt, în general, limitate la plottere.

Majoritatea limbajelor de programare cu posibilităţi grafice tradiţionale se bazează în exclusivitate pe vectori. Aceasta înseamnă că un program care foloseşte unul dintre aceste limbaje grafice este despărţit de componentele hardware printr-un nivel de abstractizare. Dispozitivul de ieşire foloseşte pixeli pentru reprezentarea elementelor grafice, dar programul nu comunică deloc cu interfaţa în limbajul pixelilor. Deşi puteţi să folosiţi interfaţa Windows GDI ca sistem de desenare vectorială la nivel înalt, puteţi să folosiţi aceeaşi interfaţa şi pentru manipularea la nivel scăzut a pixelilor.

Din acest punct de vedere, interfaţa Windows GDI este pentru limbajele grafice de interfaţa tradiţionale ceea ce este C pentru alte limbaje de programare. Limbajul C e bine cunoscut pentru gradul înalt de portabilitate între diferite medii şi sisteme de operare. Dar, în acelaşi timp, limbajul C e cunoscut şi pentru faptul că permite efectuarea unor operaţii de nivel scăzut, care în alte limbaje de nivel înalt sunt, deseori, imposibile. Aşa cum C este numit uneori „limbaj de asamblare de nivel înalt", puteţi să consideraţi că GDI este o interfaţă de nivel înalt către componentele hardware ale dispozitivelor grafice.

Aşa cum aţi văzut, în mod prestabilit. Windows foloseşte un sistem de coordonate bazat pe pixeli. Majoritatea limbajelor grafice tradiţionale folosesc un sistem de coordonate „virtual", cu o axă verticală şi una orizontală care merg, de exemplu, de la 0 la 32.767. Deşi unele limbaje grafice nu vă permit să folosiţi coordonate în pixeli, interfaţă GDI vă lasă să lucraţi în oricare dintre cele două sisteme de coordonate (şi chiar într-un alt sistem, bazat pe dimensiunile fizice). Puteţi să folosiţi un sistem de coordonate virtual şi să izolaţi astfel programul de componentele hardware, sau să folosiţi sistemul de coordonate al dispozitivului şi să scrieţi programul direct pentru componentele hardware utilizate.

De asemenea, trebuie să ştiţi că interfaţă Windows GDI îşi are limitele ei. Cel puţin în acest moment, interfaţă GDI nu poate să facă tot ce v-aţi putea dori de la o interfaţă grafică. Deşi puteţi să mutaţi pe ecran obiecte grafice, GDI este, în general, un sistem de afişare static, ce permite numai animaţii limitate. Aşa cum este implementată în Windows 95, interfaţă GDI nu asigură un suport direct pentru afişarea tridimensională sau pentru rotirea obiectelor. De exemplu, atunci când desenaţi o elipsă, axele acesteia trebuie să fie paralele cu axele sistemului de coordonate. Deşi unele limbaje grafice folosesc numere în virgulă mobilă pentru coordonatele virtuale. Windows 95 - din motive legate de performanţă - foloseşte numai numere întregi pe 16 biţi aceasta este una dintre deficienţele sistemului de operare Windows 95. Windows NT permite folosirea coordonatelor pe 32 de biţi.

Structura interfeţei GDIDin punctul de vedere al programatorului, interfaţa GDI este formată din câteva sute de apeluri de

funcţii şi unele tipuri de date, macroinstrucţiuni şi structuri asociate acestor funcţii, înainte de a studia în detaliu câteva dintre aceste funcţii, haideţi să vedem care este structura generală a interfeţei GDI.Tipuri de apeluri de funcţii

În general, apelurile de funcţii GDI pot fi clasificate în mai multe categorii. Chiar dacă nu sunt foarte stricte şi există unele suprapuneri, aceste categorii pot fi enunţate astfel:

Funcţii care obţin (sau creează) şi eliberează (sau distrug) un context de dispozitiv. Aşa cum am văzut în Capitolul 3, pentru a desena aveţi nevoie de un context de dispozitiv. Funcţiile GetDC şi ReleaseDC vă permit să faceţi aceste lucruri în timpul prelucrării altor mesaje decât WM_PAINT, pe când funcţiile BeginPaint şi EndPaint (deşi din punct de vedere tehnic fac parte din subsistemul USER din Windows) sunt folosite în timpul prelucrării mesajului WM_PAINT. Vom discuta în curând despre alte funcţii legate de contextul de dispozitiv.

Page 40: Programare in Windows

Funcţii care obţin informaţii despre contextul de dispozitiv. În programele SYSMETS, cu care aţi făcut cunoştinţă în Capitolul 3, am folosit funcţia GetTextMetrics ca să obţinem informaţii despre dimensiunile fontului selectat în contextul de dispozitiv. Mai târziu în acest capitol vom prezenta programul DEVCAPS1, pentru a obţine informaţii generale despre contextul de dispozitiv.

Funcţii care desenează ceva. Evident, după rezolvarea problemelor preliminare, acestea sunt funcţiile cu adevărat importante. În Capitolul 3 am folosit funcţia TextOut pentru afişarea textului în zona client a ferestrei. Aşa cum vom vedea, alte funcţii GDI sunt folosite pentru desenarea liniilor, a zonelor colorate şi a imaginilor de tip bitmap.

Funcţii care stabilesc sau obţin atribute ale contextului de dispozitiv. Un „atribut" al contextului de dispozitiv specifică modul de lucru al funcţiilor de desenare. De exemplu, folosiţi funcţia SetTextColor ca să precizaţi culoarea textului afişat cu funcţia TextOut (sau cu o altă funcţie de afişare a textului). În programele SYSMETS din Capitolul 3 am folosit funcţia SetTextAlign ca să arătăm interfeţei GDI faptul că poziţia de început a şirului de caractere este în partea dreaptă a şirului de caractere, nu în partea stângă, aşa cum se întâmplă de obicei. Toate atributele contextului de dispozitiv au valori prestabilite, care devin active la obţinerea contextului de dispozitiv. Pentru fiecare funcţie de tip Set există şi o funcţie Get corespondentă, folosită pentru obţinerea valorilor curente ale atributelor contextului de dispozitiv.

Funcţii care lucrează cu obiecte GDI. Aici este punctul în care lucrurile se încurcă puţin în interfaţa GDI. Mai întâi vom da un exemplu: în mod prestabilit, toate liniile pe care le desenaţi folosind interfaţa GDI sunt continue şi au o grosime standard. Ce se întâmplă, însă, dacă doriţi să desenaţi o linie mai groasă sau o linie punctată ori întreruptă? Grosimea şi stilul liniei nu sunt atribute ale contextului de dispozitiv. Acestea sunt caracteristici ale „peniţei logice" („logical pen"). Puteţi să indicaţi o peniţă logică prin specificarea acestor caracteristici în funcţiile CreatePen, CreatePenIndirect şi ExtCreatePen. Funcţiile de mai sus returnează o variabilă handle a peniţei logice create. (Deşi se consideră că aceste funcţii fac parte din interfaţa GDI, spre deosebire de majoritatea celorlalte funcţii acestea nu au nevoie, ca parametru, de o variabilă handle a contextului de dispozitiv.) Pentru folosirea peniţei selectaţi variabila handle a acesteia în contextul de dispozitiv. Din acest moment, orice linie va fi desenată eu peniţa selectată. Ulterior, deselectaţi obiectul peniţă din contextul de dispozitiv şi distrugeţi-l. în afara peniţelor, puteţi să folosiţi obiecte GDI pentru crearea pensulelor care colorează o suprafaţă închisă, pentru fonturi, pentru imagini bitmap şi pentru alte aspecte ale interfeţei GDI, despre care vom discuta în acest capitol.

Primitive GDI

Elementele grafice pe care le afişaţi pe ecran sau le tipăriţi la imprimantă pot fi împărţite în mai multe categorii, numite „primitive". Iată care sunt aceste categorii:

Linii şi curbe. Liniile reprezintă baza oricărui sistem de desenare vectorial. GDI permite folosirea liniilor drepte, a dreptunghiurilor, a elipselor (inclusiv subsetul de elipse cunoscute sub numele de cercuri), a arcelor - care sunt curbe reprezentând porţiuni din circumferinţa unei elipse sau a curbelor Bezier. Despre toate aceste clemente vom mai discuta în capitolul de faţă. Orice curbă mai complexă poate n desenată ea o linie poligonală, adică o serie de linii foarte scurte care definesc o curbă. Liniile sunt desenate folosind peniţa curentă selectată în contextul de dispozitiv.

Suprafeţe pline. Dacă o serie de linii sau de curbe închid o suprafaţă, aceasta poate fi „umplută" folosind pensula GDI curentă. Această pensulă poate fi o culoare compactă, un model (haşuri orizontale, verticale sau pe diagonală) sau o imagine bitmap repetată pe verticală sau pe orizontală.

Imagini bitmap. Imaginile bitmap sunt matrice dreptunghiulare de biţi, care corespund pixelilor unui dispozitiv de afişare. Imaginile bitmap sunt instrumente de bază pentru sistemele grafice de tip rastru. În general, acestea sunt folosite pentru afişarea imaginilor complexe (deseori preluate din lumea reală) pe ecran sau pentru tipărirea acestora la imprimantă. De asemenea, imaginile bitmap sunt folosite pentru afişarea unor mici imagini (cum ar fi pictograme, indicatoare de mouse

Page 41: Programare in Windows

şi butoane de pe barele cu instrumente de lucru ale aplicaţiilor) care trebuie afişate foarte rapid. Interfaţa GDI acceptă două tipuri de imagini bitmap: un tip mai vechi (dar util) de imagini bitmap dependente de dispozitiv şi un tip mai nou (precum cele din Windows 3.0) de imagini bitmap independente de dispozitiv (DIB - Device Independent Bitmap) care pot fi stocate în fişiere.

Text. Textul este mai puţin „matematic" decât alte aspecte ale graficii pe calculator. Textul, aşa cum îl ştim, este legat de sute de ani de tipografia tradiţională, apreciată adesea ca adevărată artă. Din acest motiv, textul este de multe ori nu doar cea mai complexă parte a sistemului grafic, ci şi cea mai importantă. Structurile create pentru definirea fonturilor şi pentru obţinerea informaţiilor despre fonturi sunt printre cele mai mari din Windows. Începând cu versiunea Windows 3.1, interfaţa GDI acceptă fonturile TrueType, bazate pe contururi umplute, care pot fi manipulate de alte funcţii GDI. Windows 95 acceptă în continuare şi fonturile mai vechi, de tip bitmap (cum este fontul sistem prestabilit) pentru compatibilitate şi pentru economisirea spaţiului de memorie.

53. Mesaje CARACTER. Mesajul WM_CHAR. Mesaje pentru „caractere moarte". Символьные сообщения. Сообщения WM_CHAR. Сообщения немых символов.

Mesajul WM_CHAR

Atunci când trebuie să prelucreze caracterele introduse de la tastatură (de exemplu, într-un procesor de texte sau într-un program de comunicaţii) programul prelucrează mesajele WM_CHAR. Probabil doriţi să prelucrati într-un mod mai special tastele Backspace, Tab şi Enter (eventual si tasta de salt la linie nouă) dar toate celelalte caractere sunt tratate la fel:

case WH_CHAR :

switch (wParam) {case '\b' : // backspace

[alte linii de program] break ;

case '\f' : // tab [alte linii de program] break ;

case '\n' : // salt la linie nouă [alte linii de program] break ;

case '\r' : // retur de car [alte linii de program] break ;

default : // cod de caractere [alte linii de program] break ;

} return 0 ;

Acest fragment de program este asemănător cu secvenţele de cod pentru tratarea caracterelor dintr-un program MS-DOS obişnuit.

Mesaje pentru „caractere moarte"De obicei, programele pot să ignore mesajele WM_DEADCHAR si WM_SYSDEADCHAR. Pe

unele tastaturi, în afară de tastatura de tip american, o serie de taste sunt folosite pentru adăugarea semnelor diacritice la o literă. Acestea se numesc „taste moarte" („dead keys") deoarece nu pot crea singure caractere. De exemplu, pe o tastatura germană, în locul tastei +/= de pe tastatura americană se află o tastă moartă pentru accentul ascuţit ('') dacă nu este apăsată tasta Shift, si pentru accentul grav (`) dacă este apăsată tasta Shift.

Atunci când utilizatorul apasă această tastă moartă, procedura de fereastră pri meşte un mesaj WM_DEADCHAR pentru care parametrul wParam conţine codul ASCII al semnului diacritic. Dacă utilizatorul apasă apoi o literă (de exemplu, tasta A), procedura de fereastră primeşte un mesaj WM_CHAR pentru care parametrul wParam conţine codul ASCII al literei „a" cu semnul diacritic respectiv. Ca urmare, programul nu trebuie să prelucreze mesajul WM_DEADCHAR, deoarece mesajul WM_CHAR următor îi furnizează toate informaţiile necesare. Codul Windows se ocupă chiar

Page 42: Programare in Windows

şi de tratarea erorilor: dacă tasta moartă este urmată de o literă care nu poate avea semnul diacritic respectiv (cum ar fi litera „s"), procedura de fereastră primeşte două mesaje WM_CHAR - pentru primul parametrul wParam conţine codul ASCII al semnului diacritic, iar pentru al doilea parametrul wParam conţine codul ASCII al literei „s".

54. Mesaje de la barele de derulare. Structurarea programului pentru desenare. ScrollWindow. Сообщения от полосы прокрутки. Структура программы для рисования. Функция ScrollWindow.

55. Mesaje generate de mouse în afara zonei client. Mesajul de testare a poziţiei. Mesajele generează mesaje. Сообщения от мышки вне рабочей зоны. Сообщение определения позиции. Сообщения генерируют сообщения.

Mesaje generate de mouse în afara zonei client

Cele zece mesaje discutate până acum sunt generate atunci când mouse-ul este deplasat sau când se execută clic în cadrul zonei client a unei ferestre. Dacă mouse-ul se află în afara zonei client, dar se află încă în fereastră, Windows trimite procedurii de fereastră un mesaj „non-client". Zona „non-client" include bara de titlu, meniul şi barele de derulare ale ferestrei.

În general nu este nevoie să prelucraţi mesajele generate în afara zonei client. Le transmiteţi pur şi simplu funcţiei DefWindowProc pentru a permite sistemului de operare Windows să execute funcţiile de sistem corespunzătoare. Din acest punct de vedere, mesajele „non-client" sunt asemănătoare cu mesajele de tastatură WM_SYSKEYDOWN, WM_SYSKEYUP şi WM_SYSCHAR.

Mesajele generate de mouse în afara zonei client corespund mesajelor din zona client, dar includ caracterele „NC" (de la „non-client"). Dacă mouse-ul este deplasat în afara zonei client a unei ferestre, procedura de fereastră primeşte următoarele mesaje:

ButonApăsat Eliberat

Apăsat (al doilea clic)

Stânga

WM_NCLBUTTON DOWN WM_NCLBUTTONUP

WM_NCLBUTTONDBLCLK

Mijloc

WM_NCMBUTTONDOWN WM_NCMBUTTONUP

WM_NCMBUTTONDBLCLK

Dreapta

WM_NCRBUTTONDOWN WM_NCRBUTTONUP

WM_NCRBUTTONDBLCLK

Totuşi, parametrii wParam şi lParam pentru mesajele generate de mouse din afara zonei client sunt diferite de cele generate din zona client. Parametrul wParam indică zona non-client din care a fost generat mesajul. Parametrul wParam poate conţine unul dintre identificatorii cu prefixul HT („hit test") definiţi în fişierele antet din Windows.

Variabila lParam conţine coordonata pe axa x în cuvântul mai puţin semnificativ şi coordonata pe axa y în cuvântul mai semnificativ. Totuşi, aceste coordonate sunt relative la ecran, nu la zona client. Pentru coordonatele de ecran, punctul de origine (0,0) este colţul din stânga-sus al zonei de afişare a ecranului. Valorile coordonatei x cresc către dreapta, iar valorile coordonatei y cresc în jos (vezi Figura 6.3).

Page 43: Programare in Windows

Figura 6-3. Coordonatele ecranului şi coordonatele zonei client.

Puteţi să transformaţi coordonatele ecranului în coordonate ale zonei client şi invers, folosind două funcţii Windows:

ScreenToClient (hwnd, pPoint) ;ClientToScreen (hwnd, pPoint) ;

Parametrul pPoint este un pointer la o structură de tip POINT. Aceste funcţii transformă valorile stocate în structura transmisă ca parametru fără să păstreze vechile valori. Remarcaţi faptul că dacă un punct se află deasupra zonei client a unei ferestre, în urma transformării coordonatelor de ecran în coordonate ale zonei client, valoarea coordonatei pe axa y va fi negativă. Similar, dacă un punct se află în stânga zonei client a unei ferestre, în urma transformării coordonatelor de ecran în coordonate ale zonei client, valoarea coordonatei pe axa x va fi negativă.

Mesajul de testare a poziţieiDacă aţi ţinut socoteala, am discutat până acum despre 20 dintre cele 21 de mesaje generate de

mouse. Ultimul mesaj este WM_NCHITTEST („non client hit test") folosit pentru verificarea poziţiei din care a fost generat mesajul. Acest mesaj precede toate celelalte mesaje generate de mouse, din zona client sau din afara acesteia. Parametrul IParam al mesajului conţine coordonatele x şi y ale indicatorului de mouse. Parametrul wParam nu este folosit.

De obicei, aplicaţiile Windows transmit acest mesaj funcţiei DefWindowProc. Windows foloseşte mesajul WM_NCHITTEST ca să genereze celelalte mesaje, în funcţie de poziţia mouse-ului. Pentru mesajele din afara zonei client, valoarea returnată de funcţia DefWindowProc în urma prelucrării mesajului WM_NCHITTEST devine parametrul wParam al mesajului generat. Această valoare poate fi oricare dintre valorile wParam care însoţesc mesajele generate de mouse din afara zonei client, plus următoarele:

HTCLIENT Zona client HTNOWHERE Nici o fereastrăHTTRANSPARENT O fereastră acoperită de o altă fereastrăHTERROR Determină funcţia DefWindowProc să emită un semnal sonor

Dacă funcţia DefWindowProc generează un mesaj HTCLIENT în urma prelucrării mesajului WM_NCHITTEST, Windows transformă coordonatele ecran în coordonate ale zonei client şi generează un mesaj pentru zona client.

Page 44: Programare in Windows

Dacă vă amintiţi cum am dezactivat toate funcţiile de sistem activate de la tastatură, prin interceptarea mesajului WM_SYSKEYDOWN, probabil vă întrebaţi dacă puteţi să faceţi ceva asemănător şi prin interceptarea mesajelor generate de mouse. Desigur, puteţi face acest lucru incluzând în procedura de fereastră liniile:

case WM_NCHITTESTreturn (LRESULT) HTNOWHERE ;

Veţi dezactiva toate mesajele de mouse trimise procedurii de fereastră pentru zona client şi pentru porţiunile din afara zonei client. Butoanele mouse-ului nu vor mai funcţiona cât timp indicatorul mouse-ului se află în fereastra dumneavoastră, inclusiv deasupra pictogramei meniului sistem, a butoanelor de redimensionare şi a butonului pentru închiderea ferestrei.

56. MESAJE GENERATE DE MOUSE ÎN ZONA CLIENT Mesaje non-client. Măştile MK_LBUTTON, MK_MBUTTON, MK_RBUTTON, MK_SHIFT, MK_CONTROL. Сообщения от мышки в рабочей зоне. Сообщения non-client. Маски MK_LBUTTON, MK_MBUTTON, MK_RBUTTON, MK_SHIFT, MK_CONTROL.

MESAJE GENERATE DE MOUSE ÎN ZONA CLIENT

o procedură de fereastră primeşte mesaje de la mouse de fiecare dată când indicatorul mouse-ului trece pe deasupra ferestrei sau când se execută clic în fereastra respectivă, chiar dacă fereastra nu este activă sau nu deţine cursorul de intrare. În Windows sunt definite 21 de mesaje generate de mouse. Totuşi, 11 dintre aceste mesaje nu se referă la zona client (le vom numi de aici înainte „mesaje non-client") şi de obicei sunt ignorate de programele Windows.

Atunci când mouse-ul este deplasat peste zona client a unei ferestre, procedura de fereastră primeşte mesajul WM_MOUSEMOVE. Dacă un buton al mouse-ului este apăsat sau eliberat în zona client a unei ferestre, procedura de fereastră primeşte următoarele mesaje:

Buton Apăsat Eliberat Apăsat (al doilea clic)Stânga WM_LBUTTONDOWN WM_LBUTTONUP WM_LBUTTONDBLCLKMijloc WM_MBUTTONDOWN WM_MBUTTONUP WM_MBUTTONDBLCLKDreapta WM_RBUTTONDOWN WM_RBUTTONUP WM_RBUTTONDBLCLK

Procedura de fereastră primeşte mesaje „MBUTTON" numai de la un mouse cu trei butoane şi mesaje „RBUTTON" numai de la un mouse cu două sau cu trei butoane. Procedura de fereastră primeşte mesaje „DBLCLK" numai în cazul în care clasa de ferestre a fost definită astfel încât să recepţioneze aceste mesaje (aşa cum vom vedea mai jos).

Pentru toate mesajele, parametrul lParam conţine poziţia mouse-ului. Cuvântul mai puţin semnificativ conţine coordonata pe axa x, iar cuvântul mai semnificativ conţine coordonata pe axa y, relative la colţul din stânga-sus al zonei client a ferestrei. Puteţi să extrageţi coordonatele pe axele x şi y din parametrul IParam folosind macroinstrucţiunile LOWORD şi HIWORD, definite în fişierele antet din Windows. Valoarea parametrului wParam indică starea buloanelor mouse-ului şi starea tastelor Shift şi Ctrl. Puteţi să testaţi parametrul wParam folosind o serie de măşti definite în fişierele antet din Windows. Prefixul MK vine de la „mouse key" („tastă de mouse").

MK_LBUTTON Butonul din stânga al mouse-ului este apăsatMK_MBUTTON Butonul din mijloc al mouse-ului este apăsatMK_RBUTTON Butonul din dreapta al mouse-ului este apăsatMK_SHIFT Tasta Shift este apăsatăMK_CONTROL Tasta Ctrl este apăsată

Atunci când deplasaţi indicatorul mouse-ului peste zona client a unei ferestre nu se generează un mesaj WM_MOUSEMOVE pentru fiecare pixel peste care trece indicatorul. Numărul mesajelor WM_MOUSEMOVE depinde de componentele hardware ale mouse-ului şi de viteza cu care procedura de fereastră poate să prelucreze aceste mesaje

Page 45: Programare in Windows

57. Mesajele de la o fereastră child la una părinte. Сообщения дочерних окон родительскому окну

Atunci când rulaţi programul BTNLOOK în partea stângă a zonei client, sunt afişate diferite tipuri de butoane. Aşa cum am menţionat anterior, atunci când executaţi clic pe un buton, controlul de tip fereastră descendent trimite mesajul WM_COMMAND către fereastra părinte. Programul BTNLOOK interceptează mesajul WM_COMMAND şi afişează valorile lParam şi wParam. Iată ce semnificaţie au acestea:

LOWORD (wParam) Identificatorul ferestrei descendentHIWORD (wParam) Codul de înştiinţarelParam Variabila handle a ferestrei descendent

Dacă faceţi conversia unor programe scrise pentru versiunile pe 16 biţi ale sis temului de operare Windows, reţineţi că aceşti parametri au fost modificaţi în vederea adaptării la dimensiunea pointerilor pe 32 de biţi.

Identificatorul ferestrei descendent este valoarea transmisă funcţiei CreateWindow la crearea ferestrei descendent. În programul BTNLOOK, aceşti identificatori au valori de la 0 la 9, corespunzătoare celor 10 butoane afişate în zona client. Variabila handle a ferestrei descendent este valoarea returnată de funcţia CreateWindow.

Codul de înştiinţare este un cod de submesaj pe care fereastra descendent îl foloseşte pentru a transmite ferestrei părinte mai multe informaţii despre semnificaţia mesajului. Valorile pe care le pot avea aceste coduri sunt definite în fişierele antet definite în Windows:

Identificatorul codului de înştiinţare al butonului ValoareBN_CLICKED 0BN_PAINT 1BN_HILITE 2BN_UNHILITE 3BN_DISABLE 4BN_DOUBLECLICKED 5

Codurile de înştiinţare de la 1 la 5 sunt păstrate pentru compatibilitatea cu un stil învechit de butoane, numit BS_SERBUTTON, aşa că nu veţi întâlni decât codul BN_CLICKED.

58. Mesajele de la timer. Таймерные сообщения (не асинхронныe)

Mesajele WM_TIMER seamănă cu mesajele WM_PAINT şi dintr-un alt punct de vedere. Windows nu inserează în coada de aşteptare mai multe mesaje WM_TIMER, ci le combină într-unul singur. Ca urmare, programul nu primeşte deodată mai multe mesaje WM_TIMER, deşi este posibil să primească două astfel de mesaje la un interval foarte scurt. O aplicaţie nu poate să determine numărul mesajelor WM_TIMER care „lipsesc" din secvenţă.

59. Mesajele privind tastatura: acţionări de taste şi „caractere". Taste obişnuite şi taste de sistem. WM_KEYDOWN, WM_SYSKEYDOWN, WM_KEYUP, WM_SYSKEYUP. Variabila lParam. Сообщения от клавиатуры: аппаратные и символьные сообщения.Системные и несистемные аппаратные сообщения клавиатуры. WM_KEYDOWN, WM_SYSKEYDOWN, WM_KEYUP, WM_SYSKEYUP. lParam.

60. Mesajele WM_CREATE, WM_PAINT şi WM_DESTROY. Сообщения WM_CREATE, WM_PAINT и WM_DESTROY.

Procedura de fereastră WndProc din fişierul SYSMETS1.C tratează trei mesaje: WM_CREATE, WM_PAINT şi WM_DESTROY. Mesajul WM_DESTROY indică faptul că sistemul de operare desfăşoară un proces de distrugere a ferestrei pe baza unei comenzi de la utilizator. Mesajul este trimis atunci când utilizatorul execută clic pe butonul Close, selectează opţiunea Close din meniul sistem sau apasă fastele Alt+F4.

Page 46: Programare in Windows

WM_CREATE este primul mesaj pe care îl primeşte procedura de fereastră. Acest mesaj este generat de Windows atunci când funcţia Create Window creează fereastra. În timpul prelucrării mesajului WM_CREATE, se obţine un context de dispozitiv pentru fereastră, apelând funcţia GetDC, şi dimensiunile fontului sistem prestabilit, apelând funcţia GetTextMetrics. SYSMETS1 salvează lăţimea medie a caracterelor în variabila cxChar şi înălţimea totală (inclusiv spaţiul suplimentar extern) a caracterelor în variabila cyChar.

61. Mesajul WM_PAINT. Dreptunghiuri valide şi invalide. Сообщение WM_PAINT. Валидные и инвалидные прямоугольники.

Mesajul WM_PAINTMajoritatea programelor Windows apelează funcţia UpdateWindow în timpul procesului de

iniţializare din procedura WinMain, puţin după intrarea în ciclul de tratare a mesajelor. Windows profită de această ocazie ca să trimită către procedura ferestrei primul mesaj WM_PAINT. Acest mesaj informează procedura ferestrei că zona client este pregătită pentru desen. Din acest moment, procedura ferestrei ar trebui să fie pregătită să prelucreze orice mesaj WM_PAINT în următoarele situaţii: O zonă anterior acoperită a ferestrei este adusă la suprafaţă atunci când utilizatorul mută o

fereastră. Utilizatorul redimensionează fereastra (dacă stilul clasei ferestrei include seturile de biţi

CS_HREDRAW si CS_VREDRAW). Programul foloseşte funcţiile ScrollWindow sau ScrollDC ca să deruleze o parte din zona client a

ferestrei.Programul foloseşte funcţiile InvalidateRect sau InvalidateRgn pentru a genera în mod explicit un

mesaj WM_PAINT.În anumite cazuri, când zona client este acoperită parţial cu text, Windows încearcă să salveze o

zonă a ecranului, pe care o va restaura mai târziu. Această metodă, însă, nu dă întotdeauna rezultate bune. Windows poate trimite, de aceea, un mesaj WM_PAINT, în situaţiile în care: Windows a şters o casetă de dialog sau casetă de mesaje care acoperea o parte a ferestrei. Un meniu este tras în jos şi apoi eliberat.

Windows salvează zona de ecran pe care a scris şi apoi o restaurează, în cazurile în care: Indicatorul mouse-ului este mişcat în zona client. O pictogramă este trasă în zona client.

Tratarea mesajelor WM_PAINT implică revizuirea modului de scriere pe ecran. Programul trebuie structurat astfel încât să acumuleze toate informaţiile necesare pentru redesenarea zonei client, dar să facă această operaţie numai „la cerere" - atunci când Windows îi trimite un mesaj WM_PAINT. Dacă programul trebuie să actualizeze zona client, poate forţa sistemul de operare să îi trimită un mesaj WM_PAINT. Deşi această metodă de afişare pare ocolitoare, ea contribuie la structurarea programului.

Dreptunghiuri valide şi invalideDeşi procedura unei ferestre trebuie să fie pregătită să actualizeze întreaga zonă client a ferestrei

atunci când primeşte mesajul WM_PAINT, deseori este necesară numai reactualizarea unei porţiuni mai mici (de cele mai multe ori o suprafaţă dreptunghiulară din zona client). O astfel de situaţie apare atunci când o parte a zonei client este acoperită de o casetă de dialog. Redesenarea este necesară numai pentru zona dreptunghiulară adusă la suprafaţă după închiderea casetei de dialog.

Această zonă este numită „regiune invalidă" („invalid region") sau „regiune de actualizare" („update region"). Prezenţa unei regiuni invalide în cadrul zonei client determină sistemul de operare să plaseze un mesaj WM_PAINT în coada de aşteptare a aplicaţiei. Procedura de fereastră a unui program recepţionează un mesaj WM_PAINT numai dacă o parte a zonei client a ferestrei este invalidă.

Page 47: Programare in Windows

Windows păstrează în interior o „structură cu informaţii pentru desenare" (paint information structure) pentru fiecare fereastră. Această structură conţine (printre alte informaţii) coordonatele celui mai mic dreptunghi în care se încadrează regiunea invalidă. Acesta este cunoscut sub numele de „dreptunghi invalid", dar uneori este numit tot „regiune invalidă". Dacă o altă regiune a zonei client devine invalidă înainte ca mesajul WM_PAINT să fie prelucrat, Windows calculează un nou dreptunghi invalid care cuprinde ambele regiuni şi stochează informaţiile actualizate în structura de informaţii pentru desenare, fără să plaseze un nou mesaj WM_PAINT în coada de aşteptare a aplicaţiei.

Procedura unei ferestre poate să invalideze un dreptunghi din zona client proprie prin apelarea funcţiei InvalidateRect. Dacă în coada de aşteptare există deja un mesaj WM_PAINT, Windows calculează un nou dreptunghi invalid. În caz contrar, plasează în coada de aşteptare un nou mesaj WM_PAINT. La recepţionarea mesajului WM_PAINT, procedura ferestrei poate obţine coordonatele dreptunghiului invalid .De asemenea, poate obţine aceste coordonate în orice alt moment, apelând funcţia GetUpdateRect.

După ce procedura de fereastră apelează funcţia BeginPaint în timpul prelucrării mesajului WM_PAINT, întreaga zonă client este validată. De asemenea, programul poate să valideze orice porţiune dreptunghiulară din zona client, apelând funcţia ValidateRect. Dacă în urma acestui apel întreaga zonă invalidă este validată, toate mesajele WM_PAINT aflate în coada de aşteptare sunt şterse.

62. Modificarea textului unui buton. Изменение текста кнопки

Modificarea textului unui buton

Puteţi să modificaţi textul unui buton (sau al unei alte ferestre) prin apelarea funcţiei SetWindowText:SetWindowText (hwnd, pszString) ;

unde hwnd este variabila handle a ferestrei al cărei text urmează să fie înlocuit, iar pszString este un pointer la un şir de caractere terminat cu caracterul nul. Pentru o fereastră normală, acesta este textul care va fi afişat în bara de titlu. Pentru un buton, acesta este textul afişat pe buton.

De asemenea, puteţi să obţineţi textul ferestrei curente:

iLength = GetWindowText (hwnd, pszBuffer, iMaxLength) ;

Parametrul iMaxLength specifică numărul maxim de caractere care va fi copiat în bufferul adresat de pointerul pszBuffer. Funcţia returnează lungimea şirului de caractere copiat. Puteţi să pregătiţi programul pentru un text de o anumită lungime apelând mai întâi funcţia GetWindowTextLength:

iLength = GetWindowTextLength (hwnd) ;

63. Moduri de desenare. Operaţii rastru (ROP - raster operation). Funcţiile SetROP2, iDrawMode, GetROP2. Режимы рисования. Растровые операции. Функции

Atunci când foloseşte o peniţă pentru desenarea unei linii, Windows face de fapt o operaţie booleană între pixelii peniţei şi pixelii de pe suprafaţa destinaţie. Efectuarea operaţiei booleene între pixeli se numeşte „operaţie rastru" (ROP - raster operation).

Deoarece desenarea unei linii implică numai două modele de pixeli (peniţa şi destinaţia), operaţia booleană se numeşte „operaţie rastru binară" sau ROP2. Windows defineşte 16 coduri ROP2 care specifică modul de combinare între pixelii peniţei şi pixelii suprafeţei, în contextul de dispozitiv prestabilit, modul de desenare definit este R2_COPYPEN, ceea ce înseamnă că Windows copiază pixelii peniţei pe suprafaţa destinaţie - adică modul normal în care suntem obişnuiţi să funcţioneze o peniţă. Există alte 15 coduri ROP2.

Care este originea celor 16 coduri ROP2 diferite? Pentru exemplificare, să presupunem că avem un sistem monocrom. Culoarea destinaţiei (culoarea zonei client a ferestrei) poate fi negru (reprezentată prin valorea 0) sau alb (1). La fel, culoarea peniţei poate fi alb sau negru. Există patru combinaţii

Page 48: Programare in Windows

posibile în cazul folosirii unei peniţe alb/negru pe o suprafaţă alb/negru: alb pe alb, alb pe negru, negru pe alb şi negru pe negru.

Ce se întâmplă cu destinaţia după ce desenaţi cu peniţa? O posibilitate este ca linia să fie desenată întotdeauna cu negru, indiferent de culoarea peniţei şi a destinaţiei: acest mod de desenare este indicat de codul R2_BLACK. O altă posibilitate este ca linia să fie desenată cu negru, exceptând situaţia în care atât peniţa cât şi destinaţia sunt negre, caz în care linia este desenată cu alb. Deşi pare puţin ciudat, Windows are un nume pentru acest mod de desenare: R2_NOTMERGEPEN. Windows face o operaţie orientată pe biţi de tip SAU între pixelii destinaţie şi pixelii peniţei, şi inversează rezultatul.

Tabelul de mai jos prezintă toate cele 16 moduri de desenare ROP2. Tabelul indică modul în care sunt combinate culoarea peniţei (P) şi culoarea destinaţiei (D) pentru obţinerea culorii afişate. În coloana „Operaţia booleană" sunt folosite notaţiile C pentru indicarea modului în care sunt combinaţi pixelii peniţei cu pixelii destinaţiei.

Peniţă (P): Destinaţie (D):

1 1

10

01

0 0

Operaţia booleană

Mod de desenare

Rezultate: 0 0 0 0 0 R2_BLACK

0 0 0 1 ~(P | D) R2_NOTMERGEPEN0 0 1 0 ~P & D R2_MASKNOTPEN0 0 1 1 ~P R2_NOTCOPYPEN0 1 0 0 P & ~D R2_MASKPENNOT0 1 0 1 ~D R2_NOT0 1 1 0 P ^ D R2_XORPEN0 1 1 1 ~(P & D) R2_NOTMASKPEN1 0 0 0 P&D R2_MASKPEN1 0 0 1 ~(P ^ D) R2_NOTXORPEN1 0 1 0 D R2_NOP1 0 1 1 ~P | D R2_MERGENOTPEN1 1 0 0 P R2_COPYPEN

(prestabilit)1 1 0 1 P | ~D R2_MERGEPENNOT1 1 1 0 P | D R2_MERGEPEN1 1 1 1 1 R2_WHITE

Puteţi să selectaţi un nou mod de desenare în contextul de dispozitiv astfel:SetROP2 (hdc, iDrawMode) ;

unde parametrul iDrawMode este una dintre valorile din coloana „Mod de desenare" a tabelului de mai sus. Puteţi să obţineţi modul curent de desenare astfel:

iDrawMode = GetROP2 (hdc) ;

Modul de desenare prestabilit al contextului de dispozitiv este R2_COPYPEN, care transferă la destinaţie culoarea peniţei. Modul R2_NOTCOPYPEN desenează cu alb dacă peniţa este neagră şi cu negru dacă peniţa este albă. Modul R2_BLACK desenează întotdeauna cu negru, indiferent de culoarea peniţei şi a destinaţiei. În mod similar, modul R2_WHITE desenează întotdeauna cu alb. Modul R2_NOP este o „operaţie nulă" - destinaţia rămâne nemodificată.

64. Modurile de mapare „metrice". Moduri de mapare proprii. Modul de mapare MM_ISOTROPIC. Modul de mapare MM_ANISOTROPIC sau ajustarea imaginii. Метрические режимы отображения. Собственные режимы отображения. Режим отображения MM_ISOTROPIC. MM_ANISOTROPIC: растягивание изображения

Page 49: Programare in Windows

Modurile de mapare „metrice"

Windows include două moduri de mapare în care coordonatele logice sunt exprimate în unităţi fizice. Deoarece coordonatele logice pe axele x şi y sunt mapate la unităţi fizice identice, aceste moduri de mapare vă ajută să desenaţi cercuri „mai rotunde" şi pătrate „mai pătrate".

Cele cinci moduri de mapare „metrice" sunt prezentate mai jos în ordinea crescătoare a preciziei. Cele două coloane din partea dreaptă prezintă dimensiunile unităţilor logice în inci (in.) şi în milimetri (mm) pentru comparare:

Mod de mapare Unităţi logice Inci Milimetri MM_LOENGLISH MM_LOMETRIC MM_HIENGLISH MM_TWIPS* MM_HIMETRIC

0,01 in.0,1 mm0,001 in.1/1440 in.0,01 mm

0,0010,00394 0,001 0,000694 0,000394

0,254 0,1 0,0254 0,0176 0,01

* Un twip este 1/20 dintr-un punct (care este 1/72 dintr-un inci), deci 1 /1440 dintr-un inci.

Ca să puteţi face o comparaţie între modul de mapare MM_TEXT şi aceste rezoluţii, trebuie să vă amintesc faptul că, pe un monitor VGA standard, fiecare pixel este un pătrat cu latura de 0,325 mm, ceea ce înseamnă că coordonatele unui dispozitiv VGA sunt mult mai grosiere decât oricare dintre modurile de mapare metrice.

Pentru o imprimantă laser cu rezoluţia de 300 dpi (puncte pe inci) fiecare pixel are 0,0033 inci - o rezoluţie mai mare decât cea a modurilor de mapare MM_LOENGLISH si MM_LOMETRIC, dar mai mică decât cea a modurilor de mapare MM_HIENGLISH, MM_TWIPS şi MM_HIMETRIC.

Originile şi extensiile prestabilite sunt următoarele:

Originea ferestrei: (0,0) Poate fi modificatăOriginea vizorului: (0,0) Poate fi modificatăExtensia ferestrei: (?, ?) Nu poate fi modificatăExtensia vizorului: (?, ?) Nu poate fi modificjată

Moduri de mapare proprii

Ultimele două moduri de mapare se numesc MM_ISOTROPIC şi MM_ANISOTROPIC. Acestea sunt singurele moduri de mapare care vă permit să modificaţi extensiile ferestrei şi ale vizorului, ceea ce înseamnă că vă permit să modificaţi factorul de scalare pe care Windows îl foloseşte pentru convertirea coordonatelor logice şi a coordonatelor de dispozitiv. Cuvântul „izotrop" înseamnă „egal în toate direcţiile"; „anizotrop" este antonimul acestuia. Ca şi modurile de mapare metrice prezentate mai înainte, MM_ISOTROPIC foloseşte axe scalate egal. Unităţile logice de pe axa x au aceeaşi dimensiune fizică ca şi unităţile logice de pe axa y. Acest fapt vă ajută să creaţi imagini care păstrează raportul corect de afişare, indiferent de raportul de afişare al dispozitivului.

Diferenţa dintre modurile de mapare metrice şi modul de mapare MM_ISOTROPIC constă în faptul că puteţi să controlaţi dimensiunea fizică a unităţilor logice. Dacă doriţi, puteţi să ajustaţi dimensiunea fizică a unităţilor logice în funcţie de dimensiunea zonei client, astfel încât imaginile pe care le desenaţi să fie întotdeauna conţinute de zona client, mărindu-se şi micşorându-se corespunzător. Programul ANACLOCK (ceasul analogic) din Capitolul 7 este un exemplu de imagine izotropă. Ceasul este întotdeauna rotund. Dacă redimensionaţi fereastra, ceasul se redimen-sionează automat. Un program Windows poate să manipuleze redimensionarea unei imagini prin ajustarea extensiilor ferestrei şi ale vizorului. Apoi programul poate să folosească aceleaşi unităţi logice la apelarea funcţiilor de desenare, indiferent de dimensiunea ferestrei.

Uneori, modurile de mapare metrice şi modul MM_TEXT sunt numite moduri de mapare „complet restricţionate" („fully constrained"). Aceasta înseamnă că nu puteţi să modificaţi extensiile ferestrei şi ale vizorului sau modul în care Windows scalează coordonatele logice faţă de coordonatele de

Page 50: Programare in Windows

dispozitiv. MM_ISOTROPIC este un mod de mapare „partial restricţionat" („partly constrained"). Windows vă permite să modificaţi extensiile ferestrei şi ale vizorului, dar le ajustează astfel încât unităţile logice x şi y să aibă aceleaşi dimensiuni fizice. Modul de mapare MM_ANISOTROPIC este „nerestricţionat". Puteţi să modificaţi extensiile ferestrei şi ale vizorului, fără ca Windows să mai facă vreo ajustare a valorilor.

Modul de mapare MM_ISOTROPIC

Modul de mapare MM_ISOTROPIC este ideal pentru folosirea unor axe arbitrare, cu unităţi logice egale pe cele două axe. Dreptunghiurile cu lăţimi şi înălţimi logice egale sunt afişate ca pătrate. Elipsele cu lăţimi şi înălţimi logice egale sunt afişate ca cercuri.

Atunci când selectaţi pentru prima dată modul de mapare MM_ISOTROPIC, Windows foloseşte aceleaşi extensii pentru fereastră şi pentru vizor, ca şi pentru modul de mapare MM_LOMETRIC. (Totuşi, nu vă bazaţi pe acest fapt.) Diferenţa este că acum puteţi să schimbaţi extensiile după cum dopţi, apelând funcţiile SetWindowExtEx şi SetViewportExtEx. Windows va ajusta apoi aceste extensii astfel încât unităţile logice de pe ambele axe să reprezinte distanţe fizice egale.

În general, veţi folosi ca parametri ai funcţiei SetWindowExtEx dimensiunile dorite pentru fereastra logică, iar ca parametri ai funcţiei SetViewportExtEx dimensiunile reale ale zonei client. Atunci când ajustează aceste extensii, Windows trebuie să încadreze fereastra logică în vizorul fizic, ceea ce înseamnă că o parte a zonei client poate să rămână în afara ferestrei logice. Pentru folosirea mai eficientă a spaţiului din zona client trebuie să apelaţi funcţia SetWindowExtEx înainte de apelarea funcţiei SetViewportExtEx.

De exemplu, să presupunem că vreţi să folosiţi un sistem de coordonate virtual, „tradiţional", cu un singur cadran în care punctul (0, 0) este colţul din stânga-jos al zonei client, iar lăţimea şi înălţimea au valori de la 0 la 32.767. Dacă doriţi ca unităţile logice x şi y să aibă aceleaşi dimensiuni, iată ce trebuie să faceţi:

SetMapMode (hdc, HH_ISOTROPIC) ;SetWindowExtEx (hdc, 32767, 32767, NULL) ;SetViewportExtEx (hdc, cxClient, -cyClient, NULL) ;SetViewportOrgEx (hdc, 0, cyClient, NULL) ;

Dacă apoi obţineţi extensiile ferestrei şi ale vizorului apelând funcţiile GetWindowExtEx şi GetViewportExtEx veţi vedea că acestea au alte valori decât cele specificate. Windows ajustează extensiile în funcţie de raportul de afişare (aspect ratio) al dispozitivului, astfel încât unităţile logice de pe cele două axe să reprezinte aceleaşi dimensiuni fizice.

Dacă zona client este mai mult lată decât înaltă (în dimensiuni fizice), Windows ajustează extensia x astfel încât fereastra logică să fie mai mică decât vizorul zonei client. Fereastra logică va fi poziţionată în partea stângă a zonei client:

Nu puteţi să afişaţi nimic în partea dreaptă a zonei client dincolo de capătul axei x, deoarece pentru aceasta ar trebui să folosiţi o coordonată logică x mai mare de 32.767. Dacă zona client este mai mult înaltă decât lată (în dimensiuni fizice), Windows ajustează extensia y. Fereastra logică va fi poziţionată în partea de jos a zonei client:

Page 51: Programare in Windows

Nu puteţi să afişaţi nimic în partea de sus a zonei client dincolo de capătul axei y, deoarece pentru aceasta ar trebui să folosiţi o coordonată logică y mai mare de 32.767. Dacă preferaţi ca fereastra logică să fie poziţionată întotdeauna în partea stângă şi în partea de sus a zonei client, puteţi să modificaţi astfel codul precedent:

SetMapMode (hdc, HH_ISOTROPIC) ;SetWindowExtEx (hdc, 32767, 32767, NULL) ;SetViewportExtEx (hdc, cxClient, -cyClient, NULL) ;SetWindowOrgEx (hdc, 0, 32767, NULL) ;

În acest fel, prin apelul funcţiei SetWihdowOrgEx precizăm că dorim ca punctul logic (0,32767) să fie mapat la punctul de dispozitiv (0,0). Dacă zona client este mai mult înaltă decât lată, sistemul de coordonate este poziţionat astfel:

Pentru imaginile de genul celei folosite de programul ANACLOCK puteţi să folosiţi un sistem de coordonate cartezian cu patru cadrane având axe arbitrar scalate în cele patru direcţii şi cu punctul de coordonate (0, 0) în centrul zonei client. Dacă, de exemplu, vreţi ca fiecare axă să ia valori de la 0 la 1000, folosiţi codul următor:

SetMapMode (hdc, MM_ISOTROPIC) ;SetWindowExtEx (hdc, 1000, 1000, NULL);SetViewportExtEx (hdc, cxClient/2, -cyClient/2, NULL) ;SetViewportOrgEx (hdc, cxClient/2, cyCllent/2, NULL) ;

Dacă zona client este mai mult lată decât înaltă, sistemul logic de coordonate arată astfel:

Sistemul logic de coordonate este centrat şi atunci când zona client este mai mult înaltă decât lată:

Page 52: Programare in Windows

Reţineţi faptul că extensiile ferestrei şi ale vizorului nu implică nici o operaţie de decupare. La apelarea funcţiilor GDI puteţi să folosiţi pentru coordonatele x şi y valori mai mici de -1000 sau mai mari de +1000. În funcţie de forma zonei client, aceste puncte pot să fie sau să nu fie vizibile.

Folosind modul de mapare MM_ISOTROPIC puteţi să faceţi ca unităţile logice să fie mai mari decât pixelii. De exemplu, să presupunem că doriţi să creaţi un sistem de coordonate în care punctul de coordonate (0, 0) se află în colţul din stânga-sus al ecranului şi valorile de pe axa y cresc de sus în jos (ca în modul de mapare MM_TEXT) dar cu coordonate logice măsurate în unităţi de 1/16 dintr-un inci. Acest mod de mapare v-ar permite să desenaţi o riglă având un capăt în colţul din stânga-sus al zonei client, cu diviziuni de şaisprezecimi de inci:

SetMapMode (hdc, MM_ISOTROPIC) ;SetWindowExtEx(hdc, 160*GetDeviceCaps (hdc, HORZSIZE)/254,160*GetDeviceCaps(hdc, VERTSIZE)/254, NULL);SetViewportExtEx(hdc, GetDeviceCaps(hdc, HORZRES),GetDeviceCaps(hdc, VERTRES), NULL);

În această secvenţă de cod extensiile vizorului sunt stabilite la dimensiunile în pixeli ale întregului ecran. Extensiile ferestrei trebuie să fie stabilite la dimensiunile întregului ecran în unităţi de şaisprezecimi de inci. Indicii HORZSIZE şi VERTSIZE ai funcţiei GetDeviceCaps returnează dimensiunile dispozitivului în milimetri. Dacă lucraţi cu numere în virgulă mobilă, trebuie să transformaţi milimetrii în inci împărţind valorile obţinute la 2,54, şi apoi să transformaţi incii în şaisprezecimi de inci înmulţind rezultatul operaţiei anterioare cu 16. Deoarece aici lucrăm cu numere întregi, am înmulţit rezultatul cu 160 şi l-am împărţit la 254.

Pentru majoritatea dispozitivelor, acest cod face ca unităţile logice să fie mult mai mari decât unităţile fizice. Tot ce desenaţi pe dispozitiv va avea pentru coordonate valori mapate la multipli de 1/16 inci. Nu puteţi să desenaţi două linii orizontale aflate la distanţa de 1/32 de inci, deoarece pentru aceasta aţi avea nevoie de o coordonată logică fracţionară.

Modul de mapare MM_ANISOTROPIC sau ajustarea imaginii

Atunci când stabiliţi extensiile ferestrei şi pe ale vizorului în modul de mapare MM_ISOTROPIC, Windows ajustează valorile astfel încât unităţile logice de pe cele două axe să aibă aceleaşi dimensiuni. În modul de mapare MM_ANISOTROPIC, Windows nu face nici o ajustare a valorilor pe care le stabiliţi. Aceasta înseamnă că în modul de mapare MM_ANISOTROPIC nu este obligatorie păstrarea raportului corect de afişare.Pentru a folosi modul de mapare MM_ANISOTROPIC, stabiliţi un sistem arbitrar de coordonate pentru zona client, ca şi pentru modul de mapare MM_ISOTROPIC. Codul de mai jos stabileşte punctul de coordonate (0, 0) în colţul din stânga-jos al zonei client, iar axele de coordonate x şi y pot lua valori de la 0 la 32.767:

SetMapMode (hdc, MM_ANISOTROPIC) ;SetWindowExtEx (hdc, 32767, 32767, NULL) ;SetViewportExtEx (hdc, cxClient, -cyClient, NULL) ;SetViewportOrgEx (hdc, 0, cyClient, NULL) ;

Page 53: Programare in Windows

65. Modurile de mapare. Coordonate de dispozitiv şi coordonate logice. Sistemele de coordonate ale dispozitivului. Vizorul şi fereastra. Folosirea modului de mapare MM_TEXT. Режимы отображения. Координаты устройства и устройства. Системы координат устройства. Область вывода и окно. Использование режима отображения MM_TEXT.

Modurile de mapare

Până acum am presupus că toate desenele se fac într-un sistem de coordonate raportate la colţul, din stânga-sus al zonei client şi folosind ca unitate de măsură pixelul. Acesta este modul de lucru prestabilit, dar nu este singurul mod de lucru.

Un atribut al contextului de dispozitiv care afectează aproape toate operaţiile de desenare din zona client este „modul de mapare" („mapping mode"). Alte patru atribute ale contextului de dispozitiv - originea ferestrei, originea vizorului (viewport), extensia ferestrei şi extensia vizorului - sunt strâns legate de modul de mapare.

Majoritatea funcţiilor GDI primesc coordonate sau dimensiuni ca parametri. Iată, ca exemplu, funcţia TextOut:

TextOut (hdc, x, y, szBuffer, iLength) ;

Parametrii x şi y indică poziţia în care începe textul. Parametrul x reprezintă poziţia pe axa orizontală, iar parametrul y reprezintă poziţia pe axa verticală. Deseori, pentru indicarea acestui punct este folosită notaţia (x, y).

În funcţia TextOut şi, de fapt, în toate funcţiile GDI, coordonatele sunt furnizate în „unităţi logice". Windows trebuie să transforme „unităţile logice" în „unităţi de dispozitiv", adică în pixeli. Această transformare este guvernată de modul de mapare, de originile ferestrei, ale vizorului şi de extensiile ferestrei şi ale vizorului. De asemenea, modul de mapare stabileşte şi orientarea axelor x şi y, adică determină sensul în care cresc valorile coordonatelor x şi y.

Windows defineşte opt moduri de mapare. Acestea sunt prezentate în tabelul următor, folosind identificatorii definiţi în fişierele antet din Windows:

Mod de mapare Unităţi logice Creşterea valorilor axax axayMM_TEXT Pixel Spre dreapta În josMM_LOMETRIC 0,1 mm Spre dreapta În sus

MM_HIMETRIC 0,01mm Spre dreapta În susMM_LOENGLISH 0,01 mei Spre dreapta În susMM_HIENGLISH 0,001 inci Spre dreapta ÎnsusMM_TWIPS 1/1440 inci Spre dreapta În susMM_ISOTROPIC Arbitrar (x = y) Selectabil SelectabilMM_ANISOTROPIC Arbitrar (x != y) Selectabil Selectabil

Puteţi să selectaţi modul de mapare folosind funcţia SetMapMode:SetMapMode (hdc, iMapHode) ;

unde iMapMode este unul dintre cei opt identificatori definiţi pentru modurile de mapare. Puteţi să obţineţi modul de mapare curent folosind funcţia GefMapMode:

iMapMode = GetMapMode (hdc) ;

Twip este un cuvânt fabricat, care înseamnă „a douăzecea parte dintr-un punct" („twentieth of a point"). Un punct - unitate de măsură folosită în tipografie - este aproximativ egal cu 1/72 dintr-un inci, dar în sistemele grafice precum GDI se consideră de cele mai multe ori că este exact 1/72 dintr-un inci. Un twip este 1/20 dintr-un punct, deci 1/1440 dintr-un inci.

Page 54: Programare in Windows

Modul de mapare prestabilit este MM_TEXT. În acest mod de mapare unităţile logice sunt aceleaşi cu unităţile fizice, ceea ce vă permite (sau, privind dintr-o altă perspectivă, vă forţează) să lucraţi în pixeli. Într-un apel al funcţiei TextOut care arată astfel:

TextOut (hdc, 8, 16, szBuffer, iLength) ;

textul începe la o distanţă de opt pixeli faţă de marginea din stânga a zonei client şi de 16 pixeli faţă de marginea de sus a acesteia.

Dacă modul de mapare este MM_LOENGLISH, o unitate logică este egală cu o sutime de inci:SetMapHode (hdc, HM_LOENGLISH) ;

Acum apelul de mai sus al funcţiei TextOut trebuie să arate astfel:TextOut (hdc, 50, -100, szBuffer, iLength) ;

Textul afişat începe la 0,5 inci faţă de marginea din stânga şi la 1 inci faţă de marginea de sus a zonei client. (Motivul folosirii unei valori negative pentru coordonata y va deveni evident ceva mai târziu, atunci când vom discuta în detaliu despre modurile de mapare.) Celelalte moduri de mapare permit programului să specifice coordonatele în milimetri, în puncte pentru imprimantă sau într-un sistem de coordonate arbitrar.

Dacă vi se pare convenabilă folosirea pixelilor ca unitate de măsură, puteţi să folosiţi numai modul de mapare MM_TEXT. Dacă trebuie să afişaţi o imagine la dimensiunile reale în inci sau milimetri, puteţi să obţineţi informaţiile necesare cu ajutorul funcţiei GetDeviceCaps şi să faceţi singur scalarea. Celelalte moduri de mapare sunt doar metode convenabile de evitare a acestor operaţii de scalare.

Indiferent de modul de mapare folosit, toate coordonatele specificate la apelarea funcţiilor Windows trebuie să fie numere întregi cu semn având valori de la -32 768 la 32 767. În plus, unele funcţii care folosesc coordonate pentru punctele de început şi de sfârşit ale unui dreptunghi cer ca lăţimea şi înălţimea dreptunghiului să nu depăşească 32 767.

Coordonate de dispozitiv şi coordonate logice

S-ar putea să vă puneţi următoarea întrebare: „Dacă folosesc modul de mapare MM_LOENGLISH voi primi mesajele WM_SIZE în sutimi de inci?" Bineînţeles că nu. Windows va folosi în continuare coordonatele de dispozitiv pentru toate mesajele (cum ar fi WM_SIZE, WM_MOVE şi WM_MOUSEMOVE), pentru toate funcţiile care nu fac parte din interfaţa GDI şi chiar pentru unele funcţii GDI. Lucrurile se petrec în felul următor: modul de mapare fiind un atribut al contextului de dispozitiv, are efect numai atunci când folosiţi funcţii GDI care primesc o variabilă handle a contextului de dispozitiv ca parametru. GetSystemMetrics nu este o funcţie GDI, aşa că va returna în continuare dimensiunile în unităţi de dispozitiv, adică în pixeli. Deşi GetDeviceCaps este o funcţie GDI care primeşte ca parametru o variabilă handle a contextului de dispozitiv, Windows continuă să returneze unităţi de dispozitiv pentru identificatorii HORZRES şi VERTRES, deoarece unul dintre scopurile acestei funcţii este să furnizeze programului dimensiunea în pixeli a dispozitivului.

Totuşi, valorile din structura TEXTMETRIC pe care le obţineţi prin apelarea funcţiei GetTextMetrics sunt furnizate în unităţi logice. Dacă modul de mapare este MM_LOENGLISH în momentul apelării funcţiei, GetTextMetrics returnează lăţimea şi înălţimea caracterelor, în sutimi de inci. Atunci când apelaţi funcţia GetTextMetrics ca să obţineţi înălţimea şi lăţimea caracterelor, modul de mapare trebuie să fie acelaşi cu cel pe care îl veţi folosi atunci când afişaţi textul pe baza acestor dimensiuni. Pe măsură ce voi prezenta diferite funcţii GDI în acest capitol, voi preciza dacă acestea utilizează coordonate logice sau coordonate de dispozitiv. Toate funcţiile despre care am discutat până acum utilizează coordonate logice, exceptând cele pentru stabilirea spaţiilor între puncte sau liniuţe pentru stilurile de linii şi între liniile de haşură din modele. Acestea sunt independente de modul de mapare.

Sistemele de coordonate ale dispozitivului

Page 55: Programare in Windows

Windows mapează coordonatele logice specificate în funcţiile GDI la coordonatele dispozitivului. Înainte de a discuta despre sistemele de coordonate logice folosite de diferitele moduri de mapare, vom discuta despre diferitele sisteme de coordonate de dispozitiv definite în Windows pentru zona de afişare. Deşi în general am lucrat doar în zona client a ferestrei, în anumite situaţii Windows foloseşte alte două sisteme de coordonate de dispozitiv. În toate sistemele de coordonate de dispozitiv sunt folosiţi pixelii ca unitate de măsură. Valorile de pe axa orizontală (x) cresc de la stânga la dreapta iar valorile de pe axa verticală (y) cresc de sus în jos.

Atunci când folosim întregul ecran, spunem că lucrăm în „coordonate ecran". Colţul din stânga-sus este punctul de coordonate (0, 0). Coordonatele ecran sunt folosite în mesajul WM_MOVE (pentru alte ferestre decât ferestrele descendent) şi în următoarele funcţii Windows: CreateWinriow şi MoveWindow (ambele pentru alte ferestre decât ferestrele descendent), GetMessagePos, GetCursorPos, SetCursorPos, GetWindowRect, WindowFromPoint şi SetBrushOrgEx. Acestea sunt funcţii care fie nu au o fereastră asociată (cum ar fi cele două funcţii pentru cursor), fie trebuie să mute (sau să găsească) o fereastră pe baza unui punct de pe ecran. Dacă folosiţi funcţia CreateDC cu parametrul DISPLAY ca să obţineţi un context de dispozitiv pentru întregul ecran, atunci coordonatele logice specificate la apelarea funcţiilor GDI vor fi mapate la coordonatele ecranului.

„Coordonatele de fereastră" se referă la întreaga fereastră a ecranului, inclusiv bara de titlu, meniu, barele de derulare şi chenarul ferestrei. Pentru o fereastră normală, punctul (0, 0) este colţul din stânga-sus al chenarului de redimensionare. Coordonatele de fereastră sunt folosite mai rar în Windows, dar dacă obţineţi un context de dispozitiv cu ajutorul funcţiei GetWindowDC, atunci coordonatele logice specificate la apelarea funcţiilor GDI vor fi mapate la coordonatele ferestrei.

Al treilea sistem de coordonate de dispozitiv - cu care vom lucra cel mai des -foloseşte „coordonatele zonei client". Punctul (0,0) este colţul din stânga-sus al zonei client. Dacă obţineţi un context de dispozitiv cu ajutorul funcţiei GetDC sau al funcţiei BeginPaint, atunci coordonatele logice specificate Ia apelarea funcţiilor GDI vor fi mapate la coordonatele zonei client.

Puteţi să transformaţi coordonatele zonei client în coordonatele ecranului şi invers folosind funcţiile ClientToScreen şi ScreenToClient. De asemenea, puteţi şă obţineţi poziţia şi dimensiunea întregii ferestre în coordonate ecran folosind funcţia GetWindowRect. Aceste funcţii vă furnizează suficiente informaţii ca să transferaţi coordonatele între oricare dintre sistemele de coordonate de dispozitiv.

66. Obţinerea informaţiilor despre culori. PLANES, BITSPIXEL, NUMCOLORS: Tipul COLORREF. Получение информации о цветности. PLANES, BITSPIXEL, NUMCOLORS: Тип COLORREF.

Obţinerea informaţiilor despre culori

Pentru afişarea culorilor este nevoie de mai mulţi biţi. Cu cât se folosesc mai mulţi biţi, cu atât pot fi afişate mai multe culori. Sau, mai precis, numărul culorilor care pot fi afişate simultan este egal cu 2 la o putere egală cu numărul de biţi folosiţi. De obicei, biţii sunt organizaţi în planuri de culori - un plan pentru roşu, un plan pentru verde, unul pentru albastru şi unul pentru intensitatea culorii. Adaptoarele video cu 8,16 sau 24 de biţi pentru fiecare pixel au un singur plan de culoare, în care un număr de biţi adiacenţi reprezintă culoarea fiecărui pixel.

Funcţia GetDeviceCaps vă permite să determinaţi modul de organizare a memoriei în adaptoarele video şi numărul de culori care pot fi reprezentate. Apelul de mai jos returnează numărul de planuri de culoare:

iPlanes = GetDeviceCaps (hdc, PLANES);

Apelul următor returnează numărul de biţi de culoare folosiţi pentru fiecare pixel:iBitsPixel = GetDeviceCaps (hdc, BITSPIXEL)

Majoritatea adaptoarelor video care pot afişa culori folosesc fie mai multe planuri de culoare, fie mai mulţi biţi de culoare pentru fiecare pixel, dar nu pe amândouă; cu alte cuvinte, unul dintre cele

Page 56: Programare in Windows

două apeluri de mai sus va returna valoarea 1. Numărul de culori care pot fi redate de o placă video se poate calcula cu formula următoare:

iColors = 1<<(iPlanes * iBitsPixel);

Această valoare poate să nu fie identică cu numărul de culori obţinut prin apelarea funcţiei GetDeviceCaps cu parametrul NUMCOLORS:

iColors = GetDeviceCaps (hdc, NUMCOLORS);

De exemplu, cele două numere vor fi diferite pentru majoritatea plotterelor. În cazul unui plotter, PLANES şi BITSPIXEL vor avea valoarea 1, dar NUMCOLORS va reprezenta numărul de peniţe pe care le foloseşte plotterul. Pentru dispozitivele monocrome, funcţia GetDeviceCaps apelată cu parametrul NUMCOLORS returnează valoarea 2.107PARTEA 1 ÎNCEPUTUL

Aceste valori pot fi diferite şi în cazul adaptoarelor video care permit încărcarea paletelor de culori. Funcţia GetDeviceCaps apelată cu parametrul NUMCOLORS returnează numărul de culori rezervate de Windows, adică 20. Celelalte 236 de culori pot fi stabilite de program folosind un manager de palete.

Windows foloseşte pentru reprezentarea culorilor o valoare întreagă fără semn, pe 32 de biţi. Tipul de date folosit pentru culori se numeşte COLORREF. Ultimii trei octeţi ai numărului (cei mai puţin semnificativi) specifică valorile pentru culorile roşu, verde şi albastru, de la O la 255, aşa cum se poate vedea în Figura 4-3. Rezultă o paletă potenţială de 224 culori (aproximativ 16 milioane de culori).

Figura 4-3. Valoarea pe 32 de biţi folosita pentru reprezentarea culorilor.

Valoarea pe 32 de biţi de mai sus e numită deseori „culoare RGB". În fisierele antet din Windows sunt definite mai multe macroinstrucţiuni pentru lucrul cu valorile RGB. Macroinstructiunea RGB acceptă trei argumente, care reprezintă valorile pentru culorile roşu, verde şi albastru şi le combină într-o valoarea întreagă pe 32 de biţi, fără semn:

#define RBG (r, g, b) ((COLORREF)(((BYTE)(r)|\ ((WORD)(g)<<8))|\ (((DWORD) (BYTE) (b))<<16)))

Astfel, valoareaRGB (255, 0, 255)

este de fapt 0x00FF00FF, valoarea RGB pentru magenta. Dacă toate cele trei argumente au valoarea 0, se obţine negrul, iar dacă au valoarea 255, se obţine albul. Macroinstrucţiunile GetRValue, GetGValue şi GetBValue extrag valorile pentru culorile primare, sub forma unor octeţi fără semn, din valoarea RGB a culorii. Aceste macroinstructiuni sunt utile atunci când apelaţi funcţii Windows care returnează culori RGB.

Numărul de culori returnat de funcţia GetDeviceCaps reprezintă numărul de culori pure pe care le poate afişa dispozitivul respectiv. Windows poate să folosească amestecarea culorilor - aceasta implică folosirea unui model de pixeli de diferite culori - pentru reprezentarea altor culori în afara celor pure. Nu toate combinaţiile de valori pentru roşu, verde şi albastru produc modele diferite de amestecare a culorilor. De exemplu, în cazul unui monitor VGA pe care pot fi afişate 16 culori, valorile pentru roşu, verde şi albastru trebuie să fie incrementate cu patru, ca să genereze un model diferit de amestecare. Ca urmare, pentru aceste tipuri de adaptoare aveţi la dispoziţie 218 (sau 262.144) culori amestecate.

Puteţi să determinaţi cea mai apropiată culoare pură pentru o anumită valoare apelând funcţia GetNearestColor:

rgbPureColor = GetNearestColor (hdc, rgbColor) ;

Page 57: Programare in Windows

67. Obţinerea variabilei handle a contextului de dispozitiv. Obţinerea informaţiilor despre contextul de dispozitiv.

Obţinerea variabilei handle a contextului de dispozitiv

Cea mai cunoscută metodă de obţinere şi de ştergere a variabilei handle a contextului de dispozitiv implică folosirea funcţiilor BeginPaint şi EndPaint în timpul prelucrării mesajului WM_PAINT:

hdc - BeginPaint (hwnd, &ps);[alte Unii de program] EndPaint (hwnd, &ps);

Variabila ps este o structură de tip PAINTSTRUCT. Câmpul hdc al acestei structuri conţine variabila handle a contextului de dispozitiv. Structura PAINTSTRUCT conţine şi o structură de tip RECT numită rcPaint, care defineşte dreptunghiul ce cuprinde regiunea invalidă a zonei client a ferestrei. Folosind variabila handle a contextului de dispozitiv, obţinută prin apelarea funcţiei BeginPaint, nu puteţi să desenaţi decât în regiunea invalidă a ferestrei. Funcţia BeginPaint validează regiunea invalidă.

Programele Windows pot să obţină variabila handle a contextului de dispozitiv şi în timpul prelucrării altor mesaje decât WM_PAINT:

hdc = GetDC (hwnd);(alte linii de program] ReleaseDC (hwnd, hdc);

Acest context de dispozitiv se aplică zonei client a ferestrei care are variabila handle hwnd. Principala diferenţă între apelul de mai sus şi metoda folosirii funcţiilor BeginPaint şi EndPaint este că variabila handle returnată de funcţia GetDC vă permite să desenaţi în toată zona client a ferestrei. În plus, funcţiile GetDC şi ReleaseDC nu validează eventualele regiuni invalide ale zonei client.

Un program Windows poate să obţină şi o variabilă handle a unui context de dispozitiv care se aplică întregii ferestre, nu numai zonei client a ferestrei:

hdc = GetWindowDC (hwnd);[alte linii de program] ReleaseDC (hwnd, hdc);

Contextul de dispozitiv include, în afară de zona client, bara de titlu a ferestrei, barele de derulare şi chenarul. Funcţia GetWindowDC este rareori folosită de aplicaţii. Dacă vreţi să experimentaţi folosirea acestei funcţii, trebuie să interceptaţi mesajele WM_NCPAINT („nonclient paint"), împiedicând sistemul de operare să redeseneze porţiunile din fereastră care nu fac parte din zona client.

Funcţiile BeginPaint, GetDC şi GetWindowDC obţin variabila handle a contextului de dispozitiv asociat unei anumite ferestre de pe ecran. O funcţie mai generală pentru obţinerea variabilei handle a unui context de dispozitiv este CreateDC:

hdc = CreateDC (pszDriver, pszDevice, pszOutput, pData);[alte linii de program] DeleteDC (hdc);

De exemplu, puteţi să obţineţi variabila handle a contextului de dispozitiv pentru tot spaţiul de afişare, cu următorul apel:

hdc = CreateDC ("DISPLAY", NULL, NULL, NULL);

Scrierea în afara ferestrei proprii nu este în general recomandată, dar poate fi convenabilă pentru unele aplicaţii speciale. (Deşi această metodă nu e documentată, se poate obţine o variabila handle a contextului de dispozitiv pentru întregul ecran şi prin apelarea funcţiei GetDC, cu parametrul NULL.) În Capitolul 15 vom folosi funcţia CreateDC pentru a obţine o variabilă handle a contextului de dispozitiv pentru o imprimantă.

Uneori aveţi nevoie de unele informaţii despre un context de dispozitiv fără să desenaţi nimic. În această situaţie puteţi să obţineţi o variabila handle a contextului de informaţii („information context") folosind funcţia CreateIC. Parametrii sunt aceiaşi ca şi pentru funcţia CreateDC:

hdclnfo = CreatelC ("DISPLAY", NULL, NULL, NULL);[alte linii de program] DeleteDC (hdclnfo);

Nu puteţi să executaţi operaţii de scriere la un dispozitiv folosind această variabilă handle. Atunci când lucraţi cu imagini bitmap, poate fi uneori utilă obţinerea unui „context de dispozitiv în memorie":

Page 58: Programare in Windows

hdcMem = CreateCompatibleDC (hdc);[alte linii de program] DeleteDC (hdcHem)

Acesta este un concept destul de abstract. În esenţă, puteţi să selectaţi o imagine bitmap într-un context de dispozitiv în memorie şi apoi să desenaţi peste folosind funcţiile GDI. Vom discuta mai târziu despre această tehnică şi o vom folosi în programul GRAFMENU din Capitolul 10.

Aşa cum am menţionat mai devreme, un metafişier este o colecţie de apeluri GDI codificate într-o formă binară. Puteţi să creaţi un metafişier prin obţinerea unui context de dispozitiv pentru metafişiere:

hdcMeta = CreateMetaFile (pszFilename);[alte linii de program] hmf = CloseMetaFile (hdcMeta);

Cât timp acest context este valid, nici un apel GDI pe care îl faceţi folosind parametrul hdcMeta nu afişează nimic pe ecran, ci devine parte a metafişierului. Apelaţi apoi funcţia CloseMetaFile şi contextul de dispozitiv este invalidat. Funcţia returnează o variabilă handle a metafişierului (hmf).

Obţinerea informaţiilor despre contextul de dispozitiv

Un context de dispozitiv se referă, de obicei, la un dispozitiv fizic de ieşire, cum ar fi un monitor video sau o imprimantă. Dacă aveţi nevoie de anumite informaţii despre acest dispozitiv, cum ar fi dimensiunile ecranului (dimensiunile în pixeli şi cele fizice) sau posibilităţile de folosire a culorilor, puteţi să le obţineţi prin apelarea funcţiei GetDeviceCaps („get device capabilities"):

iValue = GetDeviceCaps (hdc, iIndex) ;

Parametrul iIndex este unul dintre cei 28 de identificatori definiţi în fişierele antet din Windows. De exemplu, dacă iIndex are valoarea HORZRES funcţia GetDeviceCaps returnează lăţimea dispozitivului în pixeli; VERTRES returnează înălţimea dispozitivului în pixeli. Dacă hdc este o variabilă handle a contextului de dispozitiv pentru un monitor video, informaţiile obţinute sunt aceleaşi cu cele returnate de funcţia GetSystemMetrics. Dacă hdc este o variabilă handle a contextului de dispozitiv pentru o imprimantă, funcţia GetDeviceCaps returnează înălţimea şi lăţimea zonei pe care imprimantă o poate tipări.

Puteţi să folosiţi funcţia GetDeviceCaps şi ca să obţineţi informaţii despre posibilităţile unui dispozitiv de prelucrare a anumitor tipuri de elemente grafice. Această posibilitate nu este importantă pentru ecran, dar poate fi folosită în cazul imprimantelor. De exemplu, majoritatea plotterelor nu pot tipări imagini bitmap - iar funcţia GetDeviceCaps vă poate comunica acest lucru.

68. Parametrul PitchAndFamily. Funcţia SetTextAlign. Параметр PitchAndFamily. Функция SetTextAlign.

Funcţii care stabilesc sau obţin atribute ale contextului de dispozitiv. Un „atribut" al contextului de dispozitiv specifică modul de lucru al funcţiilor de desenare. De exemplu, folosiţi funcţia SetTextColor ca să precizaţi culoarea textului afişat cu funcţia TextOut (sau cu o altă funcţie de afişare a textului). În programele SYSMETS din Capitolul 3 am folosit funcţia SetTextAlign ca să arătăm interfeţei GDI faptul că poziţia de început a şirului de caractere este în partea dreaptă a şirului de caractere, nu în partea stângă, aşa cum se întâmplă de obicei. Toate atributele contextului de dispozitiv au valori prestabilite, care devin active la obţinerea contextului de dispozitiv. Pentru fiecare funcţie de tip Set există şi o funcţie Get corespondentă, folosită pentru obţinerea valorilor curente ale atributelor contextului de dispozitiv.

69. Pensule „haşurate". Stiluri de haşura. Funcţia CreatePatternBrush. Кисти со штриховкой. Стили штриховки. Функция CreatePatternBrush.

Atunci când Windows foloseşte metoda amestecării culorilor (dithering) pentru afişarea unui număr mai mare de culori decât ar fi în mod normal disponibile pe monitorul respectiv, de fapt foloseşte o pensulă pentru fiecare culoare. Pe un ecran monocrom, Windows poate să afişeze 64 de

Page 59: Programare in Windows

tonuri de gri prin amestecarea pixelilor pentru alb cu cei pentru negru. Pentru negru toţi biţii din matricea 8x8 au valoarea zero. Unul dintre cei 64 de biţi are valoarea 1 (adică alb) pentru primul ton de gri, doi biţi au valoarea 1 pentru al doilea ton de gri şi aşa mai departe, până când toţi cei 64 de biţi au valoarea 1 ca să se obţină albul pur. În cazul unui monitor color, culorile amestecate sunt tot imagini bitmap, dar domeniul disponibil de culori este mult mai mare.

Windows conţine patru funcţii pe care puteţi să le folosiţi pentru crearea pensulelor logice. Selectaţi o pensulă în contextul de dispozitiv folosind funcţia Select-Object. Ca şi peniţele logice, pensulele logice sunt obiecte GDI. Trebuie să ştergeţi toate pensulele pe care le-aţi creat, dar nu trebuie să le ştergeţi cât timp sunt selectate în contextul de dispozitiv.

Iată prima funcţie pentru crearea unei pensule logice:hBrush = CreateSolidBrush (rgbColor) ;

Cuvântul Solid din numele acestei funcţii nu înseamnă că pensula foloseşte o culoare pură. Atunci când selectaţi pensula în contextul de dispozitiv, Windows creează o imagine bitmap 8x8 pentru culorile amestecate şi foloseşte imaginea respectivă atunci când este necesară pensula selectată.

Puteţi să creaţi şi o pensulă „haşurată" cu linii orizontale, verticale sau oblice. Acest stil de pensule este folosit frecvent pentru colorarea barelor din diagrame sau grafice şi pentru desenele executate de plottere. Funcţia care creează o pensulă haşurată este:

hBrush = CreateHatchBrush (iHatchStyle, rgbColor) ;

Parametrul iHatchStyle precizează aspectul haşurii şi poate avea una dintre următoarele valori: HS_HORIZONTAL, HS_VERTICAL, HS_FDIAGONAL, HS_BDIAGONAL, HS_CROSS şi HS_DIAGCROSS. Figura 4-18 prezintă haşurile produse de fiecare dintre stilurile de mai sus.

Parametrul rgbColor din funcţia CreateHatchBrush reprezintă culoarea liniilor cu care se face haşurarea. Atunci când selectaţi pensula în contextul de dispozitiv, Windows converteşte această culoare în cea mai apropiată culoare pură. Spaţiul dintre liniile de haşură sunt colorate în funcţie de modul de desenare a fondului şi de culoarea fondului, definite în contextul de dispozitiv. Dacă modul de desenare a fondului este OPAQUE, culoarea fondului (care este convertită, la rândul ei, într-o culoare pură) este folosită pentru umplerea spaţiilor dintre linii, în acest caz, nici liniile de haşură, nici culoarea de umplere nu pot fi culori amestecate. Dacă modul de desenare a fondului este TRANSPARENT, Windows desenează liniile de haşura fără să umple spaţiile dintre acestea.

Figura 4-18. Cele şase stiluri de haşura.

Deoarece pensulele sunt întotdeauna imagini bitmap 8x8, aspectul pensulelor haşurate va depinde de rezoluţia dispozitivului pe care se face afişarea. Fiecare dintre haşurile din Figura 4-18 a fost desenată într-un dreptunghi de 32x16 pixeli, ceea ce înseamnă că imaginea bitmap 8x8 a fost repetată de patru ori pe orizontală şi de două ori pe verticală. Pe o imprimantă laser cu rezoluţia 300 dpi (dots per inch) acelaşi dreptunghi de 32x16 pixeli ar ocupa o suprafaţă cu înălţimea de 1 /9 inci si lăţimea de 1/19 inci.

Cu ajutorul funcţiei CreatePatternBrush puteţi să creaţi pensule proprii, bazate pe o imagine bitmap:

hBrush = CreatePatternBrush (hBitmap) ;

Vom discuta despre această funcţie în secţiunea rezervată imaginilor bitmap din capitolul de faţă.

Windows conţine o funcţie care poate înlocui toate celelalte trei funcţii de creare a pensulelor (CreateSolidBrush, CreateHatchBrush şi CreatePatternBrush):

Page 60: Programare in Windows

hBrush = CreateBrushIndirect (&logbrush) ;

Variabila logbrush este o structură de tip LOGBRUSH („logical brush"). Cele trei câmpuri ale structurii sunt prezentate mai jos. Valoarea câmpului lbStyle determină modul în care Windows interpretează celelalte două câmpuri:

lbStyle (UINT) lbColor (COLORREF) lbHatch (LONG)BS_SOLID Culoarea pensulei IgnoratBS_HOLLOW Ignorat IgnoratBS_HATCHED Culoarea haşurii Stilul de haşurăBS_PATTERN Ignorat Variabila handle a unei imagini bitmap

Am folosit mai devreme funcţia SelectObject ca să selectăm în contextul de dispozitiv o peniţă logică, funcţia DeleteObject ca să ştergem o peniţă logică şi funcţia GetObject ca să obţinem informaţii despre o peniţă logică. Puteţi să folosiţi aceleaşi funcţii şi pentru pensule. Dacă aveţi o variabilă handle a unei pensule, puteţi să selectaţi pensula în contextul de dispozitiv folosind funcţia SelectObject:

SelectObject (hdc, hBrush) ;

O pensulă creată poate fi ştearsă cu ajutorul funcţiei DeleteObject:DeleteObject (hBrush) ;

Totuşi, nu ştergeţi pensula selectată în contextul de dispozitiv. Dacă vreţi să obţineţi informaţii despre o pensulă, apelaţi funcţia GetObject:

GetObject (hBrush, sizeof (LOGBRUSH), (LPVOID) &logbrush) ;

unde variabila logbrush este o structură de tip LOGBRUSH („logical brush").

70. Redesenarea zonei client. Ciclul de mesaje. Structura de tip MSG. Funcţiile GetMessage, TranslateMessage, DispatchMessage. Восстановление рабочей зоны. Цикл сообщений. Структура MSG. Функции GetMessage, TranslateMessage, DispatchMessage.

UpdateWindow (hwnd) ;determină redesenarea zonei client. Acest lucru se face prin trimiterea către procedura de fereastră

(funcţia WndProc din HELLOWIN.C) a unui mesaj WM_PAINT. Vom vedea imediat cum tratează funcţia WndProc aceste mesaje.

GetMessage - preia un mesaj din coada de mesaje. TranslateMessage - converteşte unele dintre mesajele de la tastatură. DispatchMessage - trimite un mesaj către o procedură de fereastră.

Funcţia GetMessage apelată la începutul ciclului de mesaje preia un mesaj din coada de aşteptare:GetMessage (&msg, NULL, 0, 0)

Acest apel transmite sistemului de operare un pointer, numit msg, la o structură de tip MSG. Al doilea, al treilea şi al patrulea parametru au valoarea NULL sau 0, ceea ce indică faptul că programul vrea să preia toate mesajele, pentru toate ferestrele create de program. Windows completează câmpurile structurii de mesaje cu urmă torul mesaj din coada de aşteptare. Câmpurile acestei structuri sunt: hwnd - variabila handle a ferestrei căreia îi este destinat mesajul. În pro gramul HELLOWIN,

aceasta este aceeaşi cu valoarea hwnd returnată de funcţia CreateWindow, deoarece aceasta este singura fereastră a programului.

message - identificatorul mesajului. Acesta este un număr folosit pentru identificarea mesajului. Pentru fiecare mesaj în fişierele antet din Windows este definit un identificator care începe cu prefixul WM_ („window message"). De exemplu, dacă poziţionaţi indicatorul mouse-ului în zona client a programului HELLOWIN şi apăsaţi butonul din stânga, Windows va insera în coada de aşteptare un mesaj pentru care câmpul message conţine identificatorul WM_LBUTTONDOWN, adică valoarea 0x0201.

Page 61: Programare in Windows

wParam - un parametru pe 32 de biţi a cărui valoare depinde de mesajul trimis. lParam - un alt parametru pe 32 de biţi dependent de mesaj. time - momentul inserării mesajului în coada de mesaje. pt - coordonatele poziţiei mouse-ului în momentul inserării mesajului în coada de mesaje.

Dacă în câmpul message este transmisă orice altă valoare decât WM_QUIT (egală cu 0x0012), funcţia GetMessage returnează o valoare diferită de zero. Mesajul WM_QUIT determină ieşirea din ciclul de mesaje. Programul se încheie, returnând valoarea parametrului wParam al structurii msg.

Instrucţiunea:TranslateMessage (&msg) ;

retransmite structura msg sistemului de operare, pentru convertirea unor mesaje de la tastatură. (Vom discuta mai multe despre aceasta în Capitolul 5.)

Instrucţiunea:

DispatchMessage (&msg) ;

ca şi funcţia TranslateMessage, retransmite structura msg sistemului de operare. Windows trimite apoi mesajul către procedura de fereastră corespunzătoare, în vederea prelucrării - cu alte cuvinte, Windows apelează procedura de fereastră. În programul HELLOWIN, procedura de fereastră este WndProc. După ce prelucrează mesajul, funcţia WndProc predă controlul sistemului de operare, care încă elaborează răspunsul la apelul DispatchMessage. Atunci când Windows returnează controlul programului HELLOWIN, după executarea apelului DispatchMessage, ciclul de tratare a mesajelor continuă cu următorul apel al funcţiei GetMessage.

Ciclul de mesajeDupă apelarea funcţiei UpdateWindow, fereastra devine vizibilă pe ecran. Programul trebuie să fie

acum pregătit să citească intrările de la mouse şi de la tastatură. Windows formează o „coadă de mesaje" pentru fiecare program rulat concurenţial. Atunci când apare un eveniment exterior, Windows converteşte acest eveniment într-un mesaj pe care îl plasează în coada de aşteptare.

Un program preia mesajele din coada de aşteptare prin executarea unei secvenţe de cod numită „ciclu de mesaje" („message loop"):

while (GetMessage (&msg, NULL, 0, 0)){TranslateMessage (&msg) ;DispatchMessage (&msg) ;} return msg.wParam ;

Variabila msg este o structură de tip MSG, definită în fişierele antet din Windows astfel:

typedef struct tagMSG { HWND hwnd ;UINT message ;WPARAM wParam ;LPARAM lParam ;DWORD time ;POINT pt ;}MSG ,Tipul de date POINT este tot o structură, definită astfel:typedef struct tagPOINT { LONG x ;

Page 62: Programare in Windows

LONG y ;} POINT ;

Funcţia GetMessage apelată la începutul ciclului de mesaje preia un mesaj din coada de aşteptare:GetMessage (&msg, NULL, 0, 0)

Acest apel transmite sistemului de operare un pointer, numit msg, la o structură de tip MSG. Al doilea, al treilea şi al patrulea parametru au valoarea NULL sau 0, ceea ce indică faptul că programul vrea să preia toate mesajele, pentru toate ferestrele create de program. Windows completează câmpurile structurii de mesaje cu urmă torul mesaj din coada de aşteptare. Câmpurile acestei structuri sunt: hwnd - variabila handle a ferestrei căreia îi este destinat mesajul. În pro gramul HELLOWIN,

aceasta este aceeaşi cu valoarea hwnd returnată de funcţia CreateWindow, deoarece aceasta este singura fereastră a programului.

message - identificatorul mesajului. Acesta este un număr folosit pentru identificarea mesajului. Pentru fiecare mesaj în fişierele antet din Windows este definit un identificator care începe cu prefixul WM_ („window message"). De exemplu, dacă poziţionaţi indicatorul mouse-ului în zona client a programului HELLOWIN şi apăsaţi butonul din stânga, Windows va insera în coada de aşteptare un mesaj pentru care câmpul message conţine identificatorul WM_LBUTTONDOWN, adică valoarea 0x0201.

wParam - un parametru pe 32 de biţi a cărui valoare depinde de mesajul trimis. lParam - un alt parametru pe 32 de biţi dependent de mesaj. time - momentul inserării mesajului în coada de mesaje. pt - coordonatele poziţiei mouse-ului în momentul inserării mesajului în coada de mesaje.

Dacă în câmpul message este transmisă orice altă valoare decât WM_QUIT (egală cu 0x0012), funcţia GetMessage returnează o valoare diferită de zero. Mesajul WM_QUIT determină ieşirea din ciclul de mesaje. Programul se încheie, returnând valoarea parametrului wParam al structurii msg.

Instrucţiunea:TranslateMessage (&msg) ;

retransmite structura msg sistemului de operare, pentru convertirea unor mesaje de la tastatură.Instrucţiunea:

DispatchMessage (&msg) ;

ca şi funcţia TranslateMessage, retransmite structura msg sistemului de operare. Windows trimite apoi mesajul către procedura de fereastră corespunzătoare, în vederea prelucrării - cu alte cuvinte, Windows apelează procedura de fereastră. În programul HELLOWIN, procedura de fereastră este WndProc. După ce prelucrează mesajul, funcţia WndProc predă controlul sistemului de operare, care încă elaborează răspunsul la apelul DispatchMessage. Atunci când Windows returnează controlul programului HELLOWIN, după executarea apelului DispatchMessage, ciclul de tratare a mesajelor continuă cu următorul apel al funcţiei GetMessage.

71. Sistemul și cronometrul. Timpul Windows standard. Система и таймер. Стандартное время Windows72. Tipul PAINTSTRCUT, MM_TEXT. Regiuni de decupare (clipping region). Fontul sistem SYSTEM_FONT.

Тип PAINTSTRCUT, MM_TEXT. Сlipping regions. SYSTEM_FONT.

Folosirea modului de mapare MM_TEXT

Pentru modul de mapare MM_TEXT, originile şi extensiile prestabilite sunt următoarele:

Originea ferestrei: (0, 0) Poate fi modificată Originea vizorului: (0, 0) Poate fi modificată Extensia ferestrei: (1, 1) Nu poate fi modificată

Page 63: Programare in Windows

Extensia vizorului: (1, 1) Nu poate fi modificată

Raportul din extensia ferestrei şi extensia vizorului este 1, aşa că nu este necesară nici o operaţie de scalare pentru transformarea coordonatelor logice în coordonate de dispozitiv. Formulele prezentate anterior se reduc la acestea:

xViewport = xWindow - xWinOrg + xViewOrg yViexport = yWindow - yWinOrg + yViewOrg

Acest mod de mapare se numeşte „mapare de tip text", nu fiindcă este cea mai potrivită pentru text, ci datorită orientării axelor. În general, citim textul de la stânga spre dreapta şi de sus în jos, iar în modul de mapare MM_TEXT, valorile cresc în acelaşi sens:

Windows furnizează funcţiile SetViewportOrgEx şi SetWindowOrgEx pentru modificarea originii

vizorului şi a ferestrei. Aceste funcţii au ca efect deplasarea axelor, astfel încât punctul de coordonate (0, 0) nu se mai referă la colţul din stânga-sus al ecranului. În general, veţi folosi ori funcţia SetViewportOrgEx, ori SetWindowOrgEx, dar nu pe amândouă.

Iată cum lucrează aceste funcţii: dacă schimbaţi originea vizorului la (xViewOrg, yViewOrg), atunci punctul logic de coordonate (0, 0) va fi mapat la punctul de dispozitiv (xViewOrg, yViewOrg). Dacă schimbaţi originea ferestrei la (xWinOrg, yWinOrg), atunci punctul logic (xWinOrg, yWinOrg) va fi mapat la punctul de dispozitiv (0, 0). Indiferent de modificările pe care le faceţi asupra originii ferestrei şi a vizorului, punctul de dispozitiv (0, 0) este întotdeauna colţul din stânga-sus al zonei client.

De exemplu, să presupunem că zona client are lăţimea cxClient şi înălţimea cyClient. Dacă vreţi ca punctul logic de coordonate (0, 0) să se afle în centrul zonei client, puteţi să apelaţi funcţia următoare:

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

Argumentele funcţiei SetViewportEx sunt exprimate întotdeauna în unităţi de dispozitiv. Punctul logic (0, 0) va fi acum mapat la punctul .de dispozitiv (cxClient/2, cyClient/2). Din acest moment folosiţi zona client ca şi cum ar avea următorul sistem de coordonate:

Valorile logice ale axei x sunt cuprinse în intervalul de la -cxClient/2 la +cxClient/2 iar valorile logice ale axei y sunt cuprinse în intervalul de la -cyClient/2 la +cyClient/2. Colţul din dreapta-jos al zonei client are coordonatele (cxClient/2, cyClient/2). Dacă vreţi să afişaţi text începând din colţul din

Page 64: Programare in Windows

stânga-sus al zonei client, care are coordonatele de dispozitiv (0, 0), trebuie să folosiţi coordonate logice negative:

TextOut (hdc, -cxClient/2, -cyClient/2, "Hello", 5) ;

Acelaşi rezultat poate fi obţinut cu ajutorul funcţiei SetWindowOrgEx în locul funcţiei SetViewportOrgEx:

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

Argumentele funcţiei SetWindowOrgEx sunt exprimate întotdeauna în coordonate logice. După apelul de mai sus, punctul logic (-cxClient/2, -cyClient/2) este mapat la punctul de dispozitiv (0, 0), care este colţul din stânga-sus al zonei client.

Ceea ce probabil nu doriţi să faceţi (decât dacă ştiţi ce rezultat veţi obţine) este să folosiţi cele două funcţii împreună:

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

Aceasta înseamnă că punctul logic (-cxClient/2, -cyClient/2) este mapat la punctul de dispozitiv (cxClient/2, cyClient/2), rezultând următorul sistem de coordonate:

Puteţi să obţineţi originea vizorului apelând funcţia GetViewportOrgEx:GetViewportOrgEx (hdc, &pt) ;

şi originea ferestrei apelând funcţia GetWindowOrgEx:GetWindowOrgEx (hdc, &pt) ;

unde pt este o structură de tip POINT. Valorile returnate de funcţia GetViewportOrgEx sunt în coordonate de dispozitiv, iar valorile returnate de funcţia GetWindowOrgEx sunt în coordonate logice.

Uneori este de dorit să modificaţi originea ferestrei sau a vizorului ca să deplasaţi imaginea afişată pe ecran - de exemplu, ca răspuns la acţionarea barei de derulare de către utilizator. Modificarea originii nu determină deplasarea imediată a imaginii afişate. Mai întâi modificaţi originea, apoi redesenaţi ecranul. De exemplu, în programul SYSMETS2 din Capitolul 2 am folosit valoarea iVscrollPos (poziţia curentă a casetei de derulare de pe bara de derulare verticală) ca să ajustăm coordonatele de afişare pe axa y:

case WM_PAINT :BeginPaint (hwnd, &ps) ;

for 0 = 0; i<NUMLINES ; i++){y = cyChar * (1 - iVscrollPos + 1) ;[afişează textul]}

EndPaint (hwnd, &ps) ;

return 0 ;

Putem să obţinem acelaşi rezultat cu ajutorul funcţiei SetWindowOrgEx:case WM_PAINT :

BeginPaint (hwnd, &ps);SetWindowOrgEx (ps.hdc, 0, cyChar * iVscrollPos) ;for (i = 0; i < NUMLINES ; i++)

{

Page 65: Programare in Windows

y = cyChar * (1 + i) ;[afişează textul]}

EndPaint (hwnd, &ps) ;return 0 ;

Acum nu mai este nevoie să fie folosită valoarea iVscrollPos pentru calcularea coordonatei y pentru funcţia TextOut. Aceasta înseamnă că puteţi să apelaţi funcţiile de afişare a textului dintr-o subrutină, fără să transmiteţi subrutinei valoarea iVscrollPos, deoarece ajustarea poziţiei de afişare a textului s-a făcut prin modificarea originii ferestrei.

Dacă aţi mai lucrat cu sistemele de coordonate rectangulare (carteziene), probabil deplasarea punctului logic de coordonate (0, 0) în centrul zonei client vi se pare o operaţie logică. Totuşi, modul de mapare MM_TEXT prezintă o mică problemă: de obicei, într-un sistem de coordonate cartezian, valorile de pe axa y cresc în sus, pe când în modul de mapare MM_TEXT acestea cresc în jos. In acest sens, modul de mapare MM_TEXT este un caz izolat, această problemă fiind corectată de celelalte cinci moduri de mapare.

Modul de mapare prestabilit este MM_TEXT. În acest mod de mapare unităţile logice sunt aceleaşi cu unităţile fizice, ceea ce vă permite (sau, privind dintr-o altă perspectivă, vă forţează) să lucraţi în pixeli. Într-un apel al funcţiei TextOut care arată astfel:

TextOut (hdc, 8, 16, szBuffer, iLength) ;

73. Umplerea golurilor. Modul de desenare a fondului OPAQUE. Funcţiile SetBkColor, SetBkMode. Заливка. Режим рисования фона OPAQUE. Функции SetBkColor, SetBkMode.

Umplerea golurilor

Folosirea peniţelor pentru linii punctate sau pentru linii întrerupte ridică o întrebare interesantă: ce se întâmplă cu pauzele dintre puncte sau dintre liniuţe? Culoarea acestor spaţii depinde de atributele pentru culoarea fondului şi de modul de desenare a fondului, definite în contextul de dispozitiv.

Modul prestabilit de desenare a fondului este OPAQUE, ceea ce înseamnă că Windows umple spaţiile cu culoarea fondului, care în mod prestabilit este alb. Rezultatele sunt aceleaşi în cazul pensulei WHITE_BRUSH, folosită de majoritatea programelor în clasa ferestrei pentru ştergerea fondului.

Puteţi să schimbaţi culoarea fondului pentru completarea spaţiilor goale dintre linii, prin apelarea funcţiei SetBkColor:

SetBkColor (hdc, rgbColor) ;

Ca şi în cazul valorii rgbColor folosită pentru culoarea peniţei, Windows converteşte valoarea specificată într-o culoare pură. Puteţi să obţineţi culoarea definîtă în contextul de dispozitiv prin apelarea funcţiei GetBkColor.

De asemenea, puteţi să împiedicaţi colorarea spaţiilor stabilind modul TRANSPARENT de desenare a fondului:

SetBkMode (hdc, TRANSPARENT) ;

Windows va ignora culoarea fondului şi nu va mai colora spaţiile goale. Modul de desenare a fondului (TRANSPARENT sau OPAQUE) poate fi obţinut cu ajutorul funcţiei GetBkMode.

74. Utilizarea cronometrului pentru a realiza un ceas. Использование таймера для часов

FOLOSIREA CRONOMETRULUI PENTRU IMPLEMENTAREA UNUI CEAS

Page 66: Programare in Windows

Un program de afişare a ceasului este cea mai evidentă aplicaţie pentru un cronometru, aşa că vom prezenta un ceas digital şi un ceas analogic. Programul DIGCLOCK, prezentat în Figura 7-5, creează o fereastră popup, care se poziţionează în colţul din dreapta-sus al ecranului. Programul afişează ziua, data şi ora, aşa cum se poate vedea în Figura 7-6.

Toate programele prezentate până acum au folosit pentru cel de-al treilea parametru al funcţiei CreateWindow stilul de fereastră WS_OVERLAPPEDWINDOW. În programul DIGCLOCK este folosit stilul de fereastră:

WS_POPUP | WS_DLGFRAME | WS_SYSMENUAcest stil creează o fereastră popup, cu un chenar ca al casetelor de dialog şi un meniu sistem. Acest

stil este folosit în mod frecvent pentru casete de dialog şi casete de mesaje, dar mai rar pentru aplicaţii. DIGCLOCK foloseşte şi o altă variantă de apelare a funcţiei ShowWindow.

ShowWindow (hwnd, SW_SHOWNOACTIVATE) ;în mod normal, fereastra unui program devine activă în momentul rulării programului.

Identificatorul SW_SHOWNOACTIVATE cere sistemului de operare să lase activată fereastra programului care a lansat în execuţie programul DIGCLOCK. Puteţi să activaţi fereastra programului DIGCLOCK executând clic pe aceasta sau apăsând tastele Alt+Tab sau Alt+Esc. Deşi programul DIGCLOCK nu are o casetă pentru meniul de sistem, aveţi acces la acest meniu apăsând tastele Alt+bara de spaţiu în timp ce fereastra programului este activă. Dacă selectaţi opţiunea Move, puteţi să mutaţi fereastra pe ecran cu ajutorul tastaturii.

75. Utilizarea cronometrului pentru animații. Использование timer-a для анимации76. Utilizarea cronometrului. Prima metodă. Использование таймера: Первый способ

Prima metodăPrima metodă (şi cea mai simplă) determină sistemul de operare să trimită mesajele

WM_TIMER către procedura de fereastră normală a aplicaţiei. Apelul funcţiei SetTimer arată astfel:

SetTimer (hwnd, 1, iMsecInterval, NULL);

Primul parametru este o variabilă handle pentru fereastra a cărei procedură de fereastră va primi mesajele WM_TIMER. Al doilea parametru este identificatorul cronometrului, care trebuie să fie o valoare diferită de zero şi care în acest exemplu este în mod arbitrar 1. Al treilea parametru este o valoare întreagă fără semn pe 32 de biţi, care specifică intervalul de timp în milisecunde. Dacă acest parametru are valoarea 60000, programul va primi un mesaj WM_TIMER la fiecare minut. Puteţi să opriţi generarea mesajelor WM_TIMER în orice moment, chiar şi în timpul prelucrării unui mesaj WM_TIMER, apelând funcţia:

KillTimer (hwnd, 1);Al doilea parametru al funcţiei KillTimer este acelaşi identificator cu cel folosit la apelarea funcţiei

SetTimer. Înaintea terminării programului, ca răspuns la mesajul WM_DESTROY, este recomandat să distrugeţi toate cronometrele active din program.

Atunci când procedura de fereastră primeşte un mesaj WM_TIMER, parametrul wParam conţine identificatorul cronometrului (care în acest caz este 1) iar parametrul lParam este 0. Dacă aveţi nevoie de mai multe cronometre, folosiţi un identificator unic pentru fiecare. Valoarea parametrului wParam va diferenţia mesajele WM_TIMER trimise ferestrei. Pentru ca programul să fie mai uşor de citit, puteţi să folosiţi instrucţiuni define pentru definirea identificatorilor fiecărui cronometru:

#define TIMERSEC 1 #define TIMERMIN 2

Apoi puteţi să creaţi cele două cronometre prin apelarea funcţiei SetTimer:SetTimer(hwnd, TIMERSEC, 1000, NULL); SetTjmer (hwnd, TIMERMIN, 60000, NULL);

Logica de prelucrare a mesajului WM_TIMER poate să arate astfel:case WM_ TIMER :

switch (wParam) { case TIMERSEC : [operaţii de prelucrare la fiecare secunda] break ;

Page 67: Programare in Windows

case TIMERMIN : [operaţii de prelucrare la fiecare minut] break ; } return 0 ;

Dacă vreţi să schimbaţi intervalul de timp al unui cronometru existent, opriţi cronometrul şi apelaţi din nou funcţia SetTimer:

KillTimer (hwnd, 1);SetTimer (hwnd, 1, iMsecInterval, NULL);

Parametrul iMsecInterval reprezintă noul interval de timp, în milisecunde. Puteţi să folosiţi această tehnică într-un program care conţine o opţiune de afişare a secundelor. Nu trebuie decât să schimbaţi intervalul de măsurare al cronometrului de la 1000 ms la 60000 ms.

77. Utilizarea cronometrului. Metoda a doua. Использование таймера: второй способ

Prima metodă de folosire a cronometrelor determină trimiterea mesajelor WM_TIMER către procedura normală de fereastră. A doua metodă vă permite să folosiţi un cronometru astfel încât Windows să trimită mesajele WM_TIMER către o altă funcţie din program.

Funcţia care recepţionează mesajele de cronometru este numită funcţie cu apel invers (callback). Aceasta este o funcţie din program care poate fi apelată direct de sistemul de operare. Programul comunică sistemului de operare adresa funcţiei, iar sistemul de operare Windows apelează ulterior funcţia. Acest mod de lucru ar trebui să vi se pară cunoscut, deoarece procedura de fereastră a unui program este tot o funcţie cu apel invers. Atunci când înregistraţi clasa de fereastră îi comunicaţi sistemului de operare adresa procedurii de fereastră şi Windows apelează apoi procedura de fereastră atunci când trimite mesaje către program.

SetTimer nu este singura funcţie Windows care foloseşte funcţii cu apel invers. Funcţiile CreateDialog şi DialogBox (despre care vom discuta în Capitolul 14) folosesc funcţii cu apel invers pentru prelucrare mesajelor trimise către casetele de dialog; mai multe funcţii Windows (cum ar fi EnumChildWindows, EnumFonts, EnumObjects, EnumProps şi EnumWindows) transmit informaţii de tip enumerare unor funcţii cu apel invers; şi alte funcţii mai puţin folosite, cum ar fi GrayString, LineDDA si SetWindowsHookEx) impun folosirea unor funcţii cu apel invers.

Ca şi procedurile de fereastră, funcţiile cu apel invers trebuie să fie definite de tipul CALLBACK, deoarece sunt apelate de Windows din afara spaţiului de cod al programului. Parametrii transmişi funcţiilor cu apel invers şi valoarea returnată de acestea depind de scopul funcţiei. În cazul unei funcţii cu apel invers asociate unui cronometru, parametrii de intrare sunt aceiaşi cu parametrii de intrare ai unei proceduri de fereastră. Funcţiile cu apel invers folosite pentru cronometre nu returnează nici o valoare către sistemul de operare.Presupunem că funcţia cu apel invers se numeşte TimerProc. (Puteţi să daţi orice nume doriţi.) Această funcţie va prelucra numai mesajele WM_TIMER.

VOID CALLBACK TimerProc (HWND hwnd, UINT iMsg, UINT iTimerlD, DWORD

dwTime) [prelucrează mesajele WM_TIMER]

Parametrul de intrare hwnd reprezintă variabila handle a ferestrei specificate la apelarea funcţiei SetTimer. Windows va trimite funcţiei TimerProc numai mesajele WM_TIMER, aşa că parametrul iMsg va avea întotdeauna valoarea WM_TIMER. Parametrul iTimerlD conţine identificatorul cronometrului, iar parametrul dwTime reprezintă timpul sistemului.

Aşa cum am menţionat anterior, prima metodă de pornire a unui cronometru impune apelarea funcţiei SetTimer în felul următor:

SetTimer (hwnd, iTimerlD, iHsednterval, NULL) ;Atunci când folosiţi o funcţie cu apel invers pentru prelucrarea mesajelor WM_ TIMER, al patrulea parametru al funcţiei SetTimer trebuie să conţină adresa funcţiei cu apel invers, ca în exemplul următor:

SetTimer (hwnd, iTimerlD, iHsednterval, (TIMERPROC) TimerProc) ;

78. Utilizarea cronometrului. Metoda a treia. Использование таймера: третий способ

Page 68: Programare in Windows

A treia metodăA treia metodă este asemănătoare cu cea de-a doua, cu diferenţa că parametrul hwnd din apelul funcţiei SetTimer are valoare NULL, iar al doilea parametru (care ar fi trebuit sa fie identificatorul cronometrului) este ignorat. Rezultatul este faptul că funcţia SetTimer returnează un identificator al cronometrului:

iTimerID = SetTimer (NULL, 0, iMsecInterval, (TIMERPROC) TimerProc) ;Variabila iTimerID returnată de funcţia SetTimer va avea valoarea NULL dacă nu este disponibil nici un cronometru.

Primul parametru al funcţiei KillTimer (de obicei folosit pentru transmiterea variabilei handle a ferestrei) trebuie să aibă valoarea NULL. Al doilea parametru (identificatorul cronometrului) trebuie să fie valoarea returnată de funcţia SetTimer:

KillTimer (NULL, iTimerlD) ;

Parametrul hwnd transmis funcţiei TimerProc trebuie să aibă tot valoarea NULL.Această metodă de pornire a cronometrelor este rareori folosită. Se poate dovedi totuşi utilă dacă folosiţi

în program mai multe cronometre în diferite puncte, şi nu vreţi să ţineţi socoteala identificatorilor pe care deja i-aţi folosit.

Acum ştiţi să folosiţi cronometrele din Windows, aşa că este timpul să vedeţi câteva programe utile.

79. Variabilele handle. Notaţia ungară. Punctul de intrare în program. Înregistrarea clasei de fereastră. Crearea ferestrei. Afişarea ferestrei. Переменные handle. Венгерская запись. Точка входа в программу.

80. Arhitectura .NET Framework. Compilarea programelor. De ce am alege .NET?

2. Platforma .NET 2.1. Prezentare .NET este un cadru (framework) de dezvoltare software unitară care permite realizarea, distribuirea şi rularea aplicaţiilor-desktop Windows şi aplicaţiilor WEB. Tehnologia .NET pune laolaltă mai multe tehnologii (ASP, XML, OOP, SOAP, WDSL, UDDI) şi limbaje de programare (VB, C++, C#, J#) asigurând totodată atât portabilitatea codului compilat între diferite calculatoare cu sistem Windows, cât şi reutilizarea codului în programe, indiferent de limbajul de programare utilizat. .NET Framework este o componentă livrată înpreună cu sistemul de operare Windows. De fapt, .NET 2.0 vine cu Windows Server 2003 şi Windows XP SP2 şi se poate instala pe versiunile anterioare, până la Windows 98 inclusiv; .NET 3.0 vine instalat pe Windows Vista şi poate fi instalat pe versiunile Windows XP cu SP2 şi Windows Server 2003 cu minimum SP1. Pentru a dezvolta aplicatii pe platforma .NET este bine sa avem 3 componente esenţiale: • un set de limbaje (C#, Visual Basic .NET, J#, Managed C++, Smalltalk, Perl, Fortran, Cobol, Lisp, Pascal etc), • un set de medii de dezvoltare (Visual Studio .NET, Visio), • şi o bibliotecă de clase pentru crearea serviciilor Web, aplicaţiilor Web şi aplicaţiilor desktop Windows. Când dezvoltăm aplicaţii .NET, putem utiliza: • Servere specializate - un set de servere Enterprise .NET (din familia SQL Server 2000, Exchange 2000 etc), care pun la dispoziţie funcţii de stocare a bazelor de date, email, aplicaţii B2B (Bussiness to Bussiness – comerţ electronic între partenerii unei afaceri). • Servicii Web (în special comerciale), utile în aplicaţii care necesită identificarea utilizatorilor (de exemplu, .NET Passport - un mod de autentificare folosind un singur nume şi o parolă pentru toate ste-urile vizitate) • Servicii incluse pentru dispozitive non-PC (Pocket PC Phone Edition, Smartphone, Tablet PC, Smart Display, XBox, set-top boxes, etc.) 2.2. .NET Framework Componenta .NET Framework stă la baza tehnologiei .NET, este ultima interfaţă între aplicaţiile .NET şi sistemul de operare şi actualmente conţine: • Limbajele C#, VB.NET, C++ şi J#. Pentru a fi integrate în platforma .NET toate aceste limbaje respectă nişte specificaţii OOP numite Common Type System (CTS). Ele au ca elemente de bază: clase, interfeţe, delegări, tipuri valoare şi referinţă, iar ca mecanisme: moştenire, polimorfism şi tratarea excepţiilor. • Platforma comună de executare a programelor numită Common Language Runtime (CLR), utilizată de toate cele 4 limbaje. • Ansamblul de biblioteci necesare în realizarea aplicaţiilor desktop sau Web numit

Page 69: Programare in Windows

Framework Class Library (FCL). Arhitectura ,NET Framework

81. Introducere în C#. Caracterizare. Crearea aplicaţiilor consolă

3.1. Caracterizare Limbajul C# fost dezvoltat de o echipă restrânsă de ingineri de la Microsoft, echipă din care s-a evidenţiat Anders Hejlsberg (autorul limbajului Turbo Pascal şi membru al echipei care a proiectat Borland Delphi). C# este un limbaj simplu, cu circa 80 de cuvinte cheie, şi 12 tipuri de date predefinite. El permite programarea structurată, modulară şi orientată obiectual, conform perceptelor moderne ale programării profesioniste. Principiile de bază ale programării pe obiecte (INCAPSULARE, MOSTENIRE, POLIMORFISM) sunt elemente fundamentale ale programării C#. În mare, limbajul moşteneşte sintaxa şi principiile de programare din C++. Sunt o serie de tipuri noi de date sau funcţiuni diferite ale datelor din C++, iar în spiritul realizării unor secvenţe de cod sigure (safe), unele funcţiuni au fost adăugate (de exemplu, interfeţe şi delegări), diversificate (tipul struct), modificate (tipul string) sau chiar eliminate (moştenirea multiplă şi pointerii către funcţii). Unele funcţiuni (cum ar fi accesul direct la memorie folosind pointeri) au fost păstrate, dar secvenţele de cod corespunzătoare se consideră ”nesigure”. 3.2. Compilarea la linia de comandă Se pot dezvolta aplicaţii .NET şi fără a dispune de mediul de dezvoltare Visual Studio, ci numai de .NET SDK (pentru 2.0 şi pentru 3.0). În acest caz, codul se scrie în orice editor de text, fişierele se salvează cu extensia cs, apoi se compilează la linie de comandă. Astfel, se scrie în Notepad programul: using System; class primul { static void Main() { Console.WriteLine("Primul program"); Console.ReadKey(true); } } Limbajul C# 15 Dacă se salvează fişierul primul.cs, în directorul WINDOWS\Microsoft.NET\Framework\V2.0, atunci scriind la linia de comandă: csc primul.cs se va obţine fişierul primul.exe direct executabil pe o platformă .NET. 3.3. Crearea aplicaţiilor consolă Pentru a realiza aplicaţii în mediul de dezvoltare Visual Studio, trebuie să instalăm o versiune a acestuia, eventual versiunea free Microsoft Visual C# 2005 Express Edition de la adresa http://msdn.microsoft.com/vstudio/express/downloads/default.aspx. Pentru început, putem realiza aplicaţii consolă (ca şi cele din Borland Pascal sau Borland C). După lansare, alegem opţiunea New Project din meniul File. În fereastra de dialog (vezi figura), selectăm pictograma Console Application, după care, la Name, introducem numele aplicaţiei noastre. Fereastra în care scriem programul se numeşte implicit Programs.cs şi se poate modifica prin salvare explicită (Save As). Extensia

Page 70: Programare in Windows

cs provine de la C Sharp. În scrierea programului suntem asistati de IntelliSense, ajutorul contextual. Compilarea programului se realizează cu ajutorul opţiunii Build Solution (F6) din meniul Build. Posibilele erori de compilare sunt listate în fereastra Error List. Efectuând dublu click pe fiecare eroare în parte, cursorul din program se poziţionează pe linia conţinând eroarea. Rularea programului se poate realiza în mai multe moduri: rapid fără asistenţă de depanare (Start Without Debugging Shift+F5) , rapid cu asistenţă de depanare (Start Debugging F5 sau cu butonul din bara de instrumente), rulare pas cu pas (Step Into F11 şi Step Over F12) sau rulare rapidă până la linia marcată ca punct de întrerupere (Toggle Breakpoint F9 pe linia respectivă şi apoi Start Debugging F5). Încetarea urmăririi pas cu pas (Stop Debugging Shift+F5) permite ieşirea din modul depanare şi revenirea la modul normal de lucru. Toate opţiunile de rulare şi depanare se găsesc în meniul Debug al meniului. Fereastra de cod şi ferestrele auxiliare ce ne ajută în etapa de editare pot fi vizualizate alegând opţiunea corespunzătoare din meniul View. Ferestrele auxiliare utile în etapa de depanare se pot vizualiza alegând opţiunea corespunzătoare din meniul Debug/Windows.

82. Structura unui program C#. Sintaxa limbajului.

3.4. Structura unui program C# Să începem cu exemplul clasic “Hello World” adaptat la limbajul C#: 1 using System; 2 3 namespace HelloWorld 4 { 5 class Program 6 { 7 static void Main() 8 { 9 Console.WriteLine("Hello World!"); 10 } 11 } 12 } O aplicatie C# este formată din una sau mai multe clase, grupate în spaţii de nume (namespaces). Un spaţiu de nume cuprinde mai multe clase cu nume diferite având funcţionalităţi înrudite. Două clase pot avea acelaşi nume cu condiţia ca ele să fie definite în spaţii de nume diferite. În cadrul aceluiaşi spaţiu de nume poate apărea definiţia unui alt spaţiu de nume, caz în care avem de-a face cu spaţii de nume imbricate. O clasă poate fi identificată prin numele complet (nume precedat de numele spaţiului sau spaţiilor de nume din care face parte clasa respectivă, cu separatorul punct). În exemplul nostru, HelloWorld.Program este numele cu specificaţie completă al clasei Program. O clasă este formată din date şi metode (funcţii). Apelarea unei metode în cadrul clasei în care a fost definită aceasta presupune specificarea numelui metodei. Apelul unei metode definite în interiorul unei clase poate fi invocată şi din interiorul altei clase, caz în care este necesară specificarea clasei şi apoi a metodei separate prin punct. Dacă în plus, clasa aparţine unui spaţiu de nume neinclus în fişierul curent, atunci este necesară precizarea tuturor componentelor numelui: spaţiu.clasă.metodă sau spaţiu.spaţiu.clasă.metodă etc.

Page 71: Programare in Windows

În fişierul nostru se află două spaţii de nume: unul definit (HelloWorld) şi unul extern inclus prin directiva using (System). Console.Writeln reprezintă apelul metodei Writeln definită în clasa Console. Cum în spaţiul de nume curent este definită doar clasa Program, deducem că definiţia clasei Console trebuie să se găsească în spaţiul System. Pentru a facilita cooperarea mai multor programatori la realizarea unei aplicaţii complexe, există posibilitatea de a segmenta aplicaţia în mai multe fişiere numite assemblies. Într-un assembly se pot implementa mai multe spaţii de nume, iar parţi ale unui aceeaşi spaţiu de nume se pot regăsi în mai multe assembly-uri. Pentru o aplicaţie consolă, ca şi pentru o aplicaţie Windows de altfel, este obligatoriu ca una (şi numai una) dintre clasele aplicaţiei să conţină un „punct de intrare” (entry point), şi anume metoda (funcţia) Main. Să comentăm programul de mai sus: linia 1: este o directivă care specifică faptul că se vor folosi clase incluse în spaţiul de nume System. În cazul nostru se va folosi clasa Console. linia 3: spaţiul nostru de nume linia 5: orice program C# este alcătuit din una sau mai multe clase linia 7: metoda Main, „punctul de intrare” în program linia 9: clasa Console, amintită mai sus, este folosită pentru operaţiile de intrare/ieşire. Aici se apelează metoda WriteLine din acestă clasă, pentru afişarea mesajului dorit pe ecran. 3.5. Sintaxa limbajului Ca şi limbajul C++ cu care se înrudeşte, limbajul C# are un alfabet format din litere mari şi mici ale alfabetului englez, cifre şi alte semne. Vocabularul limbajului este format din acele ”simboluri”27 cu semnificaţii lexicale în scrierea programelor: cuvinte (nume), expresii, separatori, delimitatori şi comentarii. Comentarii • comentariu pe un rând prin folosirea // Tot ce urmează după caracterele // sunt considerate, din acel loc, până la sfârşitul rândului drept comentariu // Acesta este un comentariu pe un singur rand • comentariu pe mai multe rânduri prin folosirea /* şi */ Orice text cuprins între simbolurile menţionate mai sus se consideră a fi comentariu. Simbolurile /* reprezintă începutul comentariului, iar */ sfârşitul respectivului comentariu. /* Acesta este un comentariu care se intinde pe mai multe randuri */ Nume Prin nume dat unei variabile, clase, metode etc. înţelegem o succesiune de caractere care îndeplineşte următoarele reguli: • numele trebuie să înceapă cu o literă sau cu unul dintre caracterele ”_” şi ”@”; • primul caracter poate fi urmat numai de litere, cifre sau un caracter de subliniere; • numele care reprezintă cuvinte cheie nu pot fi folosite în alt scop decât acela pentru care au fost definite • cuvintele cheie pot fi folosite în alt scop numai dacă sunt precedate de @ • două nume sunt distincte dacă diferă prin cel puţin un caracter (fie el şi literă mică ce diferă de aceeaşi literă majusculă) Convenţii pentru nume: • în cazul numelor claselor, metodelor, a proprietăţilor, enumerărilor, interfeţelor, spaţiilor de nume, fiecare cuvânt care compune numele începe cu majusculă • în cazul numelor variabilelor dacă numele este compus din mai multe cuvinte, primul începe cu minusculă, celelalte cu majusculă Cuvinte cheie în C#

Page 72: Programare in Windows
Page 73: Programare in Windows

83. C#: Expresii şi operatori. Instrucţiunile try-catch-finally şi throw

3.10. Expresii şi operatori Prin expresie se înţelege o secvenţă formată din operatori şi operanzi. Un operator este un simbol ce indică acţiunea care se efectuează, iar operandul este valoarea asupra căreia se execută operaţia. În C# sunt definiţi mai mulţi operatori. În cazul în care într-o expresie nu intervin paranteze, operaţiile se execută conform priorităţii operatorilor. În cazul în care sunt mai mulţi operatori cu aceeaşi prioritate, evaluarea expresiei se realizează de la stânga la dreapta.

Page 74: Programare in Windows
Page 75: Programare in Windows
Page 76: Programare in Windows

3.13. Instrucţiunile try-catch-finally şi throw Prin excepţie se înţelege un obiect care încapsulează informaţii despre situaţii anormale în funcţionarea unui program. Ea se foloseşte pentru a semnala contextul în care 28 de exemplu, pentru prelucrarea sistematică a tuturor componentlelor unei ferestre (butoane, liste, casete de text etc.) 24 POO şi Programare vizuală (suport de curs) apare o situaţie specială. De exemplu: erori la deschiderea unor fişiere, împărţire la 0 etc. Aceste erori se pot manipula astfel încât programul să nu se termine abrupt. Sunt situaţii în care prefigurăm apariţia unei erori într-o secvenţă de prelucrare şi atunci integrăm secvenţa respectivă în blocul unei instrucţiuni try, precizând una sau mai multe secvenţe de program pentru tratarea excepţiilor apărute (blocuri catch) şi eventual o secvenţă comună care se execută după terminarea normală sau după ”recuperarea” programului din starea de excepţie (blocul finally). Exemplu: using System; using System.IO; class tryCatch { static void Main(string[] args) { string s; Console.Write("Numele fisierului:"); Console.Readln(s); try { File.OpenRead(s); } catch (FileNotFoundException a) { Console.WriteLine(a.ToString()); } catch (PathTooLongException b) { Console.WriteLine(b.ToString()); } finally { Console.WriteLine("Programul s-a sfarsit"); Console.ReadLine(); } } Alteori putem simula prin program o stare de eroare ”aruncând” o excepţie (instrucţiunea throw) sau putem profita de mecanismul de tratare a erorilor pentru a implementa un sistem de validare a datelor prin generarea unei excepţii proprii pe care, de asemenea, o ”aruncăm” în momentul neîndeplinirii unor condiţii puse asupra datelor. Clasa System.Exception şi derivate ale acesteia servesc la tratarea adecvată şi diversificată a excepţiilor. Exemplu: Considerăm clasele Copil, Fetita, Baiat definite fragmentat în capitolul 1. O posibilitate de validare la adăugara unui copil este aceea care generează o excepţie proprie la depăşirea dimensiunii vectorului static copii: public static void adaug_copil(Copil c) { if (nr_copii < nr_max) copii[nr_copii++] = c; else throw new Exception("Prea multi copii"); }

84. Evoluţia tehnicilor de programare. Tipuri de date obiectuale. Încapsulare. Supraîncărcare.

1.1. Evoluţia tehnicilor de programare • Programarea nestructurată (un program simplu, ce utilizează numai variabile globale); complicaţiile apar când prelucrarea devine mai amplă, iar datele se multiplică şi se diversifică. • Programarea procedurală (program principal deservit de subprograme cu parametri formali, variabile locale şi apeluri cu parametri efectivi); se obţin avantaje privind depanarea şi reutilizarea codului şi se aplică noi tehnici privind transferul parametrilor şi vizibilitatea

Page 77: Programare in Windows

variabilelor; complicaţiile apar atunci când la program sunt asignaţi doi sau mai mulţi programatori care nu pot lucra simultan pe un acelaşi fişier ce conţine codul sursă. • Programarea modulară (gruparea subprogramelor cu funcţionalităţi similare în module, implementate şi depanate separat); se obţin avantaje privind independenţa şi încapsularea (prin separarea zonei de implementare, păstrând vizibilitatea numai asupra zonei de interfaţă a modulului) şi se aplică tehnici de asociere a procedurilor cu datele pe care le manevrează, stabilind şi diferite reguli de acces la date şi la subprograme. Se observă că modulele sunt ”centrate” pe proceduri, acestea gestionând şi setul de date pe care le prelucrează (date+date1 din figură). Daca, de exemplu, dorim să avem mai multe seturi diferite de date, toate înzestrate comportamental cu procedurile din modulul module1, această arhitectură de aplicaţie nu este avantajoasă. • Programarea orientată obiect (programe cu noi tipuri ce integrează atât datele, cât şi metodele asociate creării, prelucrării şi distrugerii acestor date); se obţin avantaje prin abstractizarea programării (programul nu mai este o succesiune de prelucrări, ci un ansamblu de obiecte care prind viaţă, au diverse proprietăţi, sunt capabile de acţiuni specifice şi care interacţionează în cadrul programului); intervin tehnici noi privind instanţierea, derivarea şi polimorfismul tipurilor obiectuale. 1.2. Tipuri de date obiectuale. Încapsulare Un tip de date abstract (ADT) este o entitate caracterizată printr-o structură de date şi un ansamblu de operaţii aplicabile acestor date. Considerând, în rezolvarea unei probleme de gestiune a accesului utilizatorilor la un anumit site, tipul abstract USER, vom obseva că sunt multe date ce caracterizează un utilizator Internet. Totuşi se va ţine cont doar de datele semnificative pentru problema dată. Astfel, ”culoarea ochilor” este irelevantă în acest caz, în timp ce ”data naşterii” poate fi importantă. În aceeaşi idee, operaţii specifice ca ”se înregistrează”, ’comandă on-line” pot fi relevante, în timp ce operaţia ”manâncă” nu este, în cazul nostru. Evident, nici nu se pun în discuţie date sau operaţii nespecifice (”numărul de laturi” sau acţiunea ”zboară”). Operaţiile care sunt accesibile din afara entităţii formează interfaţa acesteia. Astfel, operaţii interne cum ar fi conversia datei de naştere la un număr standard calculat de la 01.01.1900 nu fac parte din interfaţa tipului de date abstract, în timp ce operaţia ”plasează o comandă on-line” face parte, deoarece permite interacţiunea cu alte obiecte (SITE, STOC etc.) O instanţă a unui tip de date abstract este o ”concretizare” a tipului respectiv, formată din valori efective ale datelor. Un tip de date obiectual este un tip de date care implementează un tip de date abstract. Vom numi operaţiile implementate în cadrul tipului de date abstract metode. Spunem că datele şi metodele sunt membrii unui tip de date obiectual. Folosirea unui astfel de tip presupune: existenţa definiţiei acestuia, apelul metodelor şi accesul la date. Un exemplu de-acum clasic de tip de date abstract este STIVA. Ea poate avea ca date: numerele naturale din stivă, capacitatea stivei, vârful etc. Iar operaţiile specifice pot fi: introducerea în stivă (push) şi extragerea din stivă (pop). La implementarea tipului STIVA, vom defini o structura de date care să reţină valorile memorate în stivă şi câmpuri de date simple pentru: capacitate, număr de elemente etc. Vom mai defini metode (subprograme) capabile să creeze o stivă vidă, care să introducă o valoare în stivă, să extragă valoarea din vârful stivei, să testeze dacă stiva este vidă sau dacă stiva este plină etc. Crearea unei instanţe noi a unui tip obiectual, presupune operaţii specifice de ”construire” a noului obiect, metoda corespunzătoare purtând numele de constructor. Analog, la desfiinţarea unei instanţe şi eliberarea spaţiului de memorie aferent datelor sale, se aplică o metodă specifică numită destructor1. O aplicaţie ce utilizează tipul obiectual STIVA, va putea construi două sau mai multe stive (de cărţi de joc, de exemplu), le va umple cu valori distincte, va muta valori dintr-o stivă în alta după o anumită regulă desfiinţând orice stivă golită, până ce rămâne o singură stivă. De observat că toate aceste prelucrări recurg la datele, constructorul, destructorul şi la metodele din interfaţa tipului STIVA descris mai sus. Principalul tip obiectual întâlnit în majoritatea mediilor de dezvoltare (Viisual Basic, Delphi, C++, Java, C#) poartă numele de clasă (class). Există şi alte tipuri obiectuale (struct, object). O instanţă a unui tip obiectual poartă numele de obiect.

Page 78: Programare in Windows

La implementare, datele şi metodele asociate trebuie să fie complet şi corect definite, astfel încât utilizatorul să nu fie nevoit să ţină cont de detalii ale acestei implementări. El va accesa datele, prin intermediul proprietăţilor şi va efectua operaţiile, prin intermediul metodelor puse la dispoziţie de tipul obiectual definit. Spunem că tipurile de date obiectuale respectă principiul încapsulării. Astfel, programatorul ce utilizează un tip obiectual CONT (în bancă) nu trebuie să poarte grija modului cum sunt reprezentate în memorie datele referitoare la un cont sau a algoritmului prin care se realizează actualizarea soldului conform operaţiilor de depunere, extragere şi aplicare a dobânzilor. EL va utiliza unul sau mai multe conturi (instanţe ale tipului CONT), accesând proprietăţile şi metodele din interfaţă, realizatorul tipului obiectual asumându-şi acele griji în momentul definirii tipului CONT. Permiţând extensia tipurilor de date abstracte, clasele pot avea la implementare: • date şi metode caracterisitice fiecărui obiect din clasă (membri de tip instanţă), • date şi metode specifice clasei (membri de tip clasă). Astfel, clasa STIVA poate beneficia, în plus, şi de date ale clasei cum ar fi: numărul de stive generate, numărul maxim sau numărul minim de componente ale stivelor existente etc. Modificatorul static plasat la definirea unui membru al clasei face ca acela să fie un membru de clasă, nu unul de tip instanţă. Dacă în cazul membrilor nestatici, există câte un exemplar al membrului respectiv pentru fiecare instanţă a clasei, membrii statici sunt unici, fiind accesaţi în comun de toate instanţele clasei. Mai mult, membrii statici pot fi referiţi fără a crea vreo instanţă a clasei respective. 1.3. Supraîncărcare Deşi nu este o tehnică specifică programării orientată obiect, ea creează un anumit context pentru metodele ce formează o clasă şi modul în care acestea pot fi (ca orice subprogram) apelate. Prin supraîncarcare se înţelege posibilitatea de a defini în acelaşi domeniu de vizibilitate2 mai multe funcţii cu acelaşi nume, dar cu parametri diferiti ca tip şi/sau ca număr. Astfel ansamblul format din numele funcţiei şi lista sa de parametri reprezintă o modalitate unică de identificare numită semnătură sau amprentă. Supraîncărcarea permite obţinerea unor efecte diferite ale apelului în contexte diferite3. 1 Datorită tehnicii de supraîncărcare C++, Java şi C# permit existenţa mai multor constructori 2 Noţiunile generale legate de vizibilitate se consideră cunoscute din programarea procedurală. Aspectele specifice şi modificatorii de acces/vizibilitate pot fi studiaţi din documentaţiile de referinţă C#. 3 Capacitatea unor limbaje (este şi cazul limbajului C#) de a folosi ca ”nume” al unui subprogram un operator, reprezintă supraîncărcarea operatorilor. Aceasta este o facilitate care Programarea Orientată Obiect (POO) 5 Apelul unei funcţii care beneficiază, prin supraîncărcare, de două sau mai multe semnături se realizează prin selecţia funcţiei a cărei semnătură se potriveşte cel mai bine cu lista de parametri efectivi (de la apel). Astfel, poate fi definită metoda ”comandă on-line” cu trei semnături diferite: comanda_online(cod_prod) cu un parametru întreg (desemnând comanda unui singur produs identificat prin cod_prod. comanda_online(cod_prod,cantitate) cu primul parametru întreg şi celalalt real comanda_online(cod_prod,calitate) cu primul parametru întreg şi al-II-ilea caracter

85. Moştenire. Polimorfism. Metode virtuale. Principiile programării orientate pe obiecte.

1.4. Moştenire Pentru tipurile de date obiectuale class este posibilă o operaţie de extindere sau specializare a comportamentului unei clase existente prin definirea unei clase noi ce moşteneşte datele şi metodele clasei de bază, cu această ocazie putând fi redefiniţi unii membri existenţi sau adăugaţi unii membri noi. Operaţia mai poartă numele de derivare. Clasa din care se moşteneştea se mai numeşte clasă de bază sau superclasă. Clasa care moşteneşte se numeşte subclasă, clasă derivată sau clasă descendentă. Ca şi în Java, în C# o subclasă poate moşteni de la o singură superclasă, adică avem de-a face cu moştenire simplă; aceeaşi superclasă însă poate fi derivată în mai multe subclase distincte. O subclasă, la randul ei, poate fi superclasă pentru o altă clasă derivată. O clasă de bază impreună cu toate clasele descendente (direct sau indirect) formeaza o ierarhie de clase. În C#, toate clasele moştenesc de la clasa de bază Object. În contextul mecanismelor de moştenire trebuie amintiţi modificatorii abstract şi sealed aplicaţi unei clase, modificatori ce obligă la şi respectiv se opun procesului de derivare. Astfel, o

Page 79: Programare in Windows

clasă abstractă trebuie obligatoriu derivată, deoarece direct din ea nu se pot obţine obiecte prin operaţia de instanţiere, în timp ce o clasă sigilată (sealed) nu mai poate fi derivată (e un fel de terminal în ierarhia claselor). O metodă abstractă este o metodă pentru care nu este definită o implementare, aceasta urmând a fi realizată în clasele derivate din clasa curentă4. O metodă sigilată nu mai poate fi redefinită în clasele derivate din clasa curentă. 1.5. Polimorfism. Metode virtuale Folosind o extensie a sensului etimologic, un obiect polimorfic este cel capabil să ia diferite forme, să se afle în diferite stări, să aibă comportamente diferite. Polimorfismul obiectual5 se manifestă în lucrul cu obiecte din clase aparţinând unei ierarhii de clase, unde, prin redefinirea unor date sau metode, se obţin membri diferiţi având însă acelaşi nume. Astfel, în cazul unei referiri obiectuale, se pune problema stabilirii datei sau metodei referite. Comportamentul polimorfic este un element de flexibilitate care permite stabilirea contextuală, în mod dinamic6, a membrului referit. De exemplu, dacă este definită clasa numită PIESA (de şah), cu metoda nestatică muta(pozitie_initiala,pozitie_finala), atunci subclasele TURN şi PION trebuie să aibă metoda muta definită în mod diferit (pentru a implementa maniera specifică a pionului de a captura o piesă ”en passant”7). Atunci, pentru un obiect T, aparţinând claselor ”reduce” diferenţele dintre operarea la nivel abstract (cu DTA) şi apelul metodei ce realizează acestă operaţie la nivel de implementare obiectuală. Deşi ajută la sporirea expresivităţii codului, prin supraîncărcarea operatorilor şi metodelor se pot crea şi confuzii. 4 care trebuie să fie şi ea abstractă (virtuală pură, conform terminologiei din C++) 5 deoarece tot aspecte polimorfice îmbracă şi unele tehnici din programarea clasică sau tehnica supraîncărcărcării funcţiilor şi operatorilor. 6 Este posibil doar în cazul limbajelor ce permit “legarea întârziată”. La limbajele cu "legare timpurie", adresa la care se face un apel al unui subprogram se stabileşte la compilare. La limbajele cu legare întârziată, această adresa se stabileste doar in momentul rulării, putându-se calcula distinct, în funcţie de contextul în care apare apelul. 7 Într-o altă concepţie, metoda muta poate fi implementată la nivelul clasei PIESA şi redefinită la nivelul subclasei PION, pentru a particulariza acest tip de deplasare care capturează piesa peste care trece pionul în diagonală. 6 POO şi Programare vizuală (suport de curs) derivate din PIESA, referirea la metoda muta pare nedefinită. Totuşi mecanismele POO permit stabilirea, în momentul apelului, a clasei proxime căreia îi aparţine obiectul T şi apelarea metodei corespunzătore (mutare de pion sau tură sau altă piesă). Pentru a permite acest mecanism, metodele care necesită o decizie contextuală (în momentul apelului), se decalră ca metode virtuale (cu modificatorul virtual). În mod curent, în C# modificatorului virtual al funcţiei din clasa de bază, îi corespunde un specificator override al funcţiei din clasa derivată ce redefineşte funcţia din clasa de bază. O metodă ne-virtuală nu este polimorfică şi, indiferent de clasa căreia îi aparţine obiectul, va fi invocată metoda din clasa de bază.

86. STRUCTURA UNEI APLICAŢII ORIENTATĂ PE OBIECTE ÎN C#. Clasă de bază şi clase derivate.

3.4. Structura unui program C# Să începem cu exemplul clasic “Hello World” adaptat la limbajul C#: 1 using System; 2 3 namespace HelloWorld 4 { 5 class Program 6 { 7 static void Main() 8 { 9 Console.WriteLine("Hello World!"); 10 } 11 } 12 }

Page 80: Programare in Windows

O aplicatie C# este formată din una sau mai multe clase, grupate în spaţii de nume (namespaces). Un spaţiu de nume cuprinde mai multe clase cu nume diferite având funcţionalităţi înrudite. Două clase pot avea acelaşi nume cu condiţia ca ele să fie definite în spaţii de nume diferite. În cadrul aceluiaşi spaţiu de nume poate apărea definiţia unui alt spaţiu de nume, caz în care avem de-a face cu spaţii de nume imbricate. O clasă poate fi identificată prin numele complet (nume precedat de numele spaţiului sau spaţiilor de nume din care face parte clasa respectivă, cu separatorul punct). În exemplul nostru, HelloWorld.Program este numele cu specificaţie completă al clasei Program. O clasă este formată din date şi metode (funcţii). Apelarea unei metode în cadrul clasei în care a fost definită aceasta presupune specificarea numelui metodei. Apelul unei metode definite în interiorul unei clase poate fi invocată şi din interiorul altei clase, caz în care este necesară specificarea clasei şi apoi a metodei separate prin punct. Dacă în plus, clasa aparţine unui spaţiu de nume neinclus în fişierul curent, atunci este necesară precizarea tuturor componentelor numelui: spaţiu.clasă.metodă sau spaţiu.spaţiu.clasă.metodă etc. În fişierul nostru se află două spaţii de nume: unul definit (HelloWorld) şi unul extern inclus prin directiva using (System). Console.Writeln reprezintă apelul metodei Writeln definită în clasa Console. Cum în spaţiul de nume curent este definită doar clasa Program, deducem că definiţia clasei Console trebuie să se găsească în spaţiul System. Pentru a facilita cooperarea mai multor programatori la realizarea unei aplicaţii complexe, există posibilitatea de a segmenta aplicaţia în mai multe fişiere numite assemblies. Într-un assembly se pot implementa mai multe spaţii de nume, iar parţi ale unui aceeaşi spaţiu de nume se pot regăsi în mai multe assembly-uri. Pentru o aplicaţie consolă, ca şi pentru o aplicaţie Windows de altfel, este obligatoriu ca una (şi numai una) dintre clasele aplicaţiei să conţină un „punct de intrare” (entry point), şi anume metoda (funcţia) Main. Să comentăm programul de mai sus: linia 1: este o directivă care specifică faptul că se vor folosi clase incluse în spaţiul de nume System. În cazul nostru se va folosi clasa Console. linia 3: spaţiul nostru de nume linia 5: orice program C# este alcătuit din una sau mai multe clase linia 7: metoda Main, „punctul de intrare” în program linia 9: clasa Console, amintită mai sus, este folosită pentru operaţiile de intrare/ieşire. Aici se apelează metoda WriteLine din acestă clasă, pentru afişarea mesajului dorit pe ecran.

87. STRUCTURA UNEI APLICAŢII ORIENTATĂ PE OBIECTE ÎN C#. Constructori. Supraîncărcarea constructorilor şi definirea constructorilor în clasele derivate. Destructor. Metode.

1.3. Supraîncărcare Deşi nu este o tehnică specifică programării orientată obiect, ea creează un anumit context pentru metodele ce formează o clasă şi modul în care acestea pot fi (ca orice subprogram) apelate. Prin supraîncarcare se înţelege posibilitatea de a defini în acelaşi domeniu de vizibilitate2 mai multe funcţii cu acelaşi nume, dar cu parametri diferiti ca tip şi/sau ca număr. Astfel ansamblul format din numele funcţiei şi lista sa de parametri reprezintă o modalitate unică de identificare numită semnătură sau amprentă. Supraîncărcarea permite obţinerea unor efecte diferite ale apelului în contexte diferite3. 1 Datorită tehnicii de supraîncărcare C++, Java şi C# permit existenţa mai multor constructori 2 Noţiunile generale legate de vizibilitate se consideră cunoscute din programarea procedurală. Aspectele specifice şi modificatorii de acces/vizibilitate pot fi studiaţi din documentaţiile de referinţă C#. 3 Capacitatea unor limbaje (este şi cazul limbajului C#) de a folosi ca ”nume” al unui subprogram un operator, reprezintă supraîncărcarea operatorilor. Aceasta este o facilitate care Programarea Orientată Obiect (POO) 5 Apelul unei funcţii care beneficiază, prin supraîncărcare, de două sau mai multe semnături se realizează prin selecţia funcţiei a cărei semnătură se potriveşte cel mai bine cu lista de parametri efectivi (de la apel). Astfel, poate fi definită metoda ”comandă on-line” cu trei semnături diferite: comanda_online(cod_prod) cu un parametru întreg (desemnând comanda unui singur produs identificat prin cod_prod. comanda_online(cod_prod,cantitate) cu primul parametru întreg şi celalalt real comanda_online(cod_prod,calitate) cu primul parametru întreg şi al-II-ilea caracter.

Page 81: Programare in Windows

1.8. Constructori Sintaxa: [atrib]o [modificatori]o [nume_clasă] ([listă_param_formali]o) [:iniţializator]o [corp_constr]o Modificatori: public protected internel private extern Iniţializator: base([listă_param]o), this([listă_param]o) ce permite invocarea unui constructor anume12 înainte de executarea instrucţiunilor ce formează corpul constructorului curent. Dacă nu este precizat niciun iniţializator, se asociază implicit iniţializatorul base(). Corpul constructorului este format din instrucţiuni care se execută la crearea unui nou obiect al clasei respective (sau la crearea clasei, în cazul constructorilor cu modificatorul static). • pot exista mai mulţi constructori care se pot diferenţia prin lista lor de parametri • constructorii nu pot fi moşteniţi • dacă o clasă nu are definit niciun constructor, se va asigna automat constructorul fără parametri al clasei de bază (clasa object, dacă nu este precizată clasa de bază) Instanţierea presupune declararea unei variabile de tipul clasei respective şi iniţializarea acesteia prin apelul constructorului clasei (unul dintre ei, dacă sunt definiţi mai mulţi) precedat de operatorul new. Acestea se pot realiza şi simultan într-o instrucţiune de felul: [Nume_clasă] [nume_obiect]=new [Nume_clasă] ([listă_param]o) ¾ Utilizarea unui constructor fără parametri şi a constructorului implicit în clasă derivată public abstract class Copil { protected string nume; public Copil() {nume = Console.ReadLine();} //la iniţializarea obiectului se citeşte //de la tastatură un şir de caractere ce va reprezenta numele copilului } class Fetita:Copil {} ... Fetita f=new Fetita(); Copil c= new Copil(); //Pentru clasa Copil abstractă, s-ar fi obţinut eroare aici ¾ Supraîncărcarea constructorilor şi definirea explicită a constructorilor în clase derivate public class Copil { protected string nume; //dată acceesibilă numai în interiorul clasei şi claselor derivate public Copil() {nume = Console.ReadLine();} public Copil(string s) {nume=s;} } class Fetita:Copil { public Fetita(string s):base(s) {nume=”Fetita ”+nume}13 public Fetita(){} //preia constructorul fără parametri din clasa de bază14 //public Fetita(string s):base() {nume=s} } ... Copil c1= new Copil(); //se citeste numele de la tastatură Copil c2= new Copil(“Codrina”); Fetita f1=new Fetita();Fetita f2=new Fetita("Ioana”); Există două motive pentru care definiţia constructorului al treilea din clasa Fetita este greşită şi de aceea este comentată. Care sunt aceste motive? 1.9. Destructor Sintaxa: [atrib]o [extern]o ~[nume_clasă] () [corp_destructor]o Corpul destructorului este format din instrucţiuni care se execută la distrugerea unui obiect al clasei respective. Pentru orice clasă poate fi definit un singur constructor. Destructorii 12 Din clasa de bază (base) sau din clasa insăşi (this) 13 Preia şi specializează constructorul al doilea din clasa de bază 14 Este echivalent cu public Fetita():base(){} 8 POO şi Programare vizuală (suport de curs) nu pot fi moşteniţi. În mod normal, destructorul nu este apelat în mod explicit, deoarece procesul de distrugere a unui obiect este invocat şi gestionat automat de Garbagge Collector. 1.10. Metode Sintaxa:[atrib]o[modificatori]o[tip_returnat] [nume] ([listă_param_formali]o) [corp_metoda]o Modificatori: new public protected internal private static virtual abstract sealed override extern15 Tipul rezultat poate fi un tip definit sau void. Numele poate fi un simplu identificator sau, în cazul în care defineşte în mod explicit un membru al unei interfeţe, numele este de forma [nume_interfata].[nume_metoda] Lista de parametri formali este o succesiune de declarări despărţite prin virgule, declararea unui parametru având sintaxa: [atrib]o [modificator]o [tip] [nume] Modificatorul unui parametru poate fi ref (parametru de intrare şi ieşire) sau out (parametru

Page 82: Programare in Windows

care este numai de ieşire). Parametrii care nu au niciun modificator sunt parametri de intrare. Un parametru formal special este parametrul tablou cu sintaxa: [atrib]o params [tip][] [nume]. • Pentru metodele abstracte şi externe, corpul metodei se reduce la un semn ; • Semnătura fiecărei metode este formată din numele metodei, modificatorii acesteia, numărul şi tipul parametrilor16 • Numele metodei trebuie să difere de numele oricărui alt membru care nu este metodă. • La apelul metodei, orice parametru trebuie să aibă acelaşi modificator ca la definire Invocarea unei metode se realizează prin sintagma [nume_obiect].[nume_metoda] (pentru metodele nestatice) şi respectiv [nume_clasă].[nume_metoda] (pentru metodele statice). ¾ Definirea datelor şi metodelor statice corespunzătoare unei clase public class Copil { public const int nr_max = 5; //constantă public static int nr_copii=0; //câmp simplu (variabilă) static Copil[] copii=new Copil[nr_max]; //câmp de tip tablou (variabilă) public static void adaug_copil(Copil c) //metodă { copii[nr_copii++] = c; if (nr_copii==nr_max) throw new Exception("Prea multi copii"); } public static void afisare() //metodă { Console.WriteLine("Sunt {0} copii:", nr_copii); for (int i = 0; i<nr_copii; i++) Console.WriteLine("Nr.{0}. {1}", i+1, copii[i].nume); } ...17 } ... Fetita c = new Fetita();Copil.adaug_copil(c); referinţa noului obiect se memorează în tabloul static copii (caracteristic clasei) şi se incrementează data statică nr_copii Baiat c = new Baiat(); Copil.adaug_copil(c); Copil c = new Copil(); Copil.adaug_copil(c); Copil.afisare();//se afişează o listă cu numele celor 3 copii 15 Poate fi folosit cel mult unul dintre modificatorii static, virtual şI override ; nu pot apărea împreună new şi override, abstract nu poate să apară cu niciunul dintre static, virtual, sealed, extern; private nu poate să apară cu niciunul dintre virtual, override şi abstract; seald obligă şi la override 16 Din semnătură (amprentă) nu fac parte tipul returnat, numele parametrilor formali şi nici specificatorii ref şi out. 17 Se are în vedere şi constructorul fără parametri definit şi preluat implicit în subclasele din cadrul primului exemplu din subcapitolul 1.8: public Copil() {nume = Console.ReadLine();} Programarea Orientată Obiect (POO) 9 ¾ Definirea datelor şi metodelor nestatice corespunzătoare clasei Copil şi claselor derivate public class Copil { ... public string nume; public virtual void se_joaca() //virtual Æ se poate suprascrie la derivare {Console.WriteLine("{0} se joaca.", this.nume);} public void se_joaca(string jucaria) //nu permite redefinire18 {Console.WriteLine("{0} se joaca cu {1}.", this.nume, jucaria);} } //supraîncărcarea metodei se_joaca class Fetita:Copil { public override void se_joaca() //redefinire Æ comportament polimorfic {Console.WriteLine("{0} leagana papusa.",this.nume);} } class Baiat:Copil { public override void se_joaca() {Console.WriteLine("{0} chinuie pisica.",this.nume);} } ... Fetita c = new Fetita();c.se_joaca("pisica");c.se_joaca(); //polimorfism Baiat c = new Baiat();c.se_joaca("calculatorul");c.se_joaca(); //polimorfism Copil c = new Copil();c.se_joaca(); //polimorfism Pentru a evidenţia mai bine comportamentul polimorfic, propunem secvenţa următoare în care nu se ştie exact ce este obiectul copii[i] (de tip Copil, Fetita sau Baiat?): for (int i=0; i<nr_copii; i++) copii[i].se_joaca;

Page 83: Programare in Windows

88. CLASE ŞI FUNCŢII GENERICE. DERIVAREA CLASELOR (MOŞTENIRE). Principiile moştenirii. Accesibilitatea membrilor moşteniţi.

89. Metode. Interfeţe în C#.

1.10. Metode Sintaxa:[atrib]o[modificatori]o[tip_returnat] [nume] ([listă_param_formali]o) [corp_metoda]o Modificatori: new public protected internal private static virtual abstract sealed override extern15 Tipul rezultat poate fi un tip definit sau void. Numele poate fi un simplu identificator sau, în cazul în care defineşte în mod explicit un membru al unei interfeţe, numele este de forma [nume_interfata].[nume_metoda] Lista de parametri formali este o succesiune de declarări despărţite prin virgule, declararea unui parametru având sintaxa: [atrib]o [modificator]o [tip] [nume] Modificatorul unui parametru poate fi ref (parametru de intrare şi ieşire) sau out (parametru care este numai de ieşire). Parametrii care nu au niciun modificator sunt parametri de intrare. Un parametru formal special este parametrul tablou cu sintaxa: [atrib]o params [tip][] [nume]. • Pentru metodele abstracte şi externe, corpul metodei se reduce la un semn ; • Semnătura fiecărei metode este formată din numele metodei, modificatorii acesteia, numărul şi tipul parametrilor16 • Numele metodei trebuie să difere de numele oricărui alt membru care nu este metodă. • La apelul metodei, orice parametru trebuie să aibă acelaşi modificator ca la definire Invocarea unei metode se realizează prin sintagma [nume_obiect].[nume_metoda] (pentru metodele nestatice) şi respectiv [nume_clasă].[nume_metoda] (pentru metodele statice). ¾ Definirea datelor şi metodelor statice corespunzătoare unei clase public class Copil { public const int nr_max = 5; //constantă public static int nr_copii=0; //câmp simplu (variabilă) static Copil[] copii=new Copil[nr_max]; //câmp de tip tablou (variabilă) public static void adaug_copil(Copil c) //metodă { copii[nr_copii++] = c; if (nr_copii==nr_max) throw new Exception("Prea multi copii"); } public static void afisare() //metodă { Console.WriteLine("Sunt {0} copii:", nr_copii); for (int i = 0; i<nr_copii; i++) Console.WriteLine("Nr.{0}. {1}", i+1, copii[i].nume); } ...17 } ... Fetita c = new Fetita();Copil.adaug_copil(c); referinţa noului obiect se memorează în tabloul static copii (caracteristic clasei) şi se incrementează data statică nr_copii Baiat c = new Baiat(); Copil.adaug_copil(c); Copil c = new Copil(); Copil.adaug_copil(c); Copil.afisare();//se afişează o listă cu numele celor 3 copii 15 Poate fi folosit cel mult unul dintre modificatorii static, virtual şI override ; nu pot apărea împreună new şi override, abstract nu poate să apară cu niciunul dintre static, virtual, sealed, extern; private nu poate să apară cu niciunul dintre virtual, override şi abstract; seald obligă şi la override 16 Din semnătură (amprentă) nu fac parte tipul returnat, numele parametrilor formali şi nici specificatorii ref şi out. 17 Se are în vedere şi constructorul fără parametri definit şi preluat implicit în subclasele din cadrul primului exemplu din subcapitolul 1.8: public Copil() {nume = Console.ReadLine();} Programarea Orientată Obiect (POO) 9 ¾ Definirea datelor şi metodelor nestatice corespunzătoare clasei Copil şi claselor derivate public class Copil { ...

Page 84: Programare in Windows

public string nume; public virtual void se_joaca() //virtual Æ se poate suprascrie la derivare {Console.WriteLine("{0} se joaca.", this.nume);} public void se_joaca(string jucaria) //nu permite redefinire18 {Console.WriteLine("{0} se joaca cu {1}.", this.nume, jucaria);} } //supraîncărcarea metodei se_joaca class Fetita:Copil { public override void se_joaca() //redefinire Æ comportament polimorfic {Console.WriteLine("{0} leagana papusa.",this.nume);} } class Baiat:Copil { public override void se_joaca() {Console.WriteLine("{0} chinuie pisica.",this.nume);} } ... Fetita c = new Fetita();c.se_joaca("pisica");c.se_joaca(); //polimorfism Baiat c = new Baiat();c.se_joaca("calculatorul");c.se_joaca(); //polimorfism Copil c = new Copil();c.se_joaca(); //polimorfism Pentru a evidenţia mai bine comportamentul polimorfic, propunem secvenţa următoare în care nu se ştie exact ce este obiectul copii[i] (de tip Copil, Fetita sau Baiat?): for (int i=0; i<nr_copii; i++) copii[i].se_joaca;1.13. Interfeţe Interfeţele sunt foarte importante în programarea orientată pe obiecte, deoarece permit utilizarea polimorfismului într-un sens mai extins.O interfaţă este o componentă a aplicaţiei, asemănătoare unei clase, ce declară prin membrii săi (metode, proprietăţi, evenimente şi indexatori) un ”comportament” unitar aplicabil mai multor clase, comportament care nu se poate defini prin ierarhia de clase a aplicaţiei. De exemplu, dacă vom considera arborele din figura următoare, în care AVERE este o clasă abstractă, iar derivarea claselor a fost concepută urmărind proprietăţile comune ale componentelor unei averi, atunci o clasă VENIT nu este posibilă, deoarece ea ar moşteni de la toate clasele evidenţiate, iar moştenirea multiplă nu este admisă în C#. 22 De exmplu indicii componentelor interschimbate 23 A se observa că evenimentul în sine este anonim, doar delegatul asociat are nume 24 într-o atribuire de felul x.E+=new [tip_delegat](M) VENITURI (din produse, din chirii, din dobânzi, dividende) • calc() AVERE Proprietate Bani Imobiliara Bun Depunere Investiţie Credit_primitTeren Imobil B_inchiriat Productiv Neproductiv De_folosinţă I_inchiriat Mobilier Altul Actiune Cotă Credit_acordat 12 POO şi Programare vizuală (suport de curs) Pentru metodele din cadrul unei interfeţe nu se dă nici o implementare, ci sunt pur şi simplu specificate, implementarea lor fiind furnizată de unele dintre clasele aplicaţiei25. Nu există instanţiere în cazul interfeţelor, dar se admit derivări, inclusiv moşteniri multiple. În exemplul nostru, se poate defini o interfaţă VENIT care să conţină antetul unei metode calc (să zicem) pentru calculul venitului obţinut, fiecare dintre clasele care implementează interfaţa VENIT fiind obligată să furnizeze o implementare (după o formulă de calcul specifică) pentru metoda calc din interfaţă. Orice clasă care doreşte să adere la interfaţă trebuie să implementeze toate metodele din interfaţă. Toate clasele care moştenesc dintr-o clasă care implementează o interfaţă moştenesc, evident, metodele respective, dar le

Page 85: Programare in Windows

pot şi redefini (de exemplu, clasa Credit_acordat redefineşte metoda calc din clasa Investiţie, deoarece formula de calcul implementată acolo nu i se ”potriveşte” şi ei26). De exemplu, dacă presupunem că toate clasele subliniate implementează interfaţa VENIT, atunci pentru o avere cu acţiuni la două firme, un imobil închiriat şi o depunere la bancă, putem determina venitul total: Actiune act1 = new Actiune();Actiune act2 = new Actiune(); I_inchiriat casa = new I_inchiriat();Depunere dep=new Depunere(); Venit[] venituri = new Venit()[4]; venituri[0] = act1; venituri[1] = act2; venituri[2] = casa; venituri[3] = dep; ... int t=0; for(i=0;i<4;i++) t+=v[i].calc(); Găsiţi două motive pentru care interfaţa VENIT şi rezovarea de mai sus oferă o soluţie mai bună decât: t=act1.calc()+act2.calc()+casa.calc()+dep.calc().

90. TRATAREA EXCEPŢIILOR ÎN C#. Aruncarea şi prinderea excepţiilor.

Atunci cand programam este aproape imposibil sa nu primim erori: de sintaxa (abatere de la sintaxa limbajului de programare), de logica (greseli in logica programului) sau erori la executie (exceptii).O exceptie reprezinta o eroare care intervine la runtime, la momentul executiei. In C#, exceptiile se pot trata intr-o maniera structurata si controlata, acest lucru insemanand faptul ca programatorul nu trebuie sa mai verifice manual daca o operatie se executa sau nu cu succes.C# defineste exceptii standard pentru tipurile de erori obisnuite dintr-un program. De exemplu: impartire la zero, depasirea capacitatii unui vector, memorie insuficienta, etc.Blocurile Try – Catch – FinallyCuvintele cheie in C# rezervate pentru tratarea exceptiilor sunt try, catch, finally, throw. Acestea reprezinta un sistem unitar, utilizarea unuia dintre ele implicand si utilizarea altuia.Intr-un bloc try vom scrie instructiunile care trebuie verificate pentru aparitia erorilor. Daca pe parcursul executiei acestor intructiuni apare o exceptie, aceasta este “aruncata”, lansata (thrown).Cu ajutorul lui catch programul poate intercepta exceptia si o poate trata in functie de logica programului. Instructiunile din catch se executa doar daca se lanseaza o exceptie.Instructiunile din finally se vor executa intotdeauna.O imagine de ansamblu a codului folosit pentru tratarea exceptiilor:try{ //in this block exception may get thrown }

catch{ //handle exception }

finally{ //cleanup code, optionally }Cuvantul cheie try nu poate aparea fara a fi completat de cuvantul cheie catch sau definally si nici invers. Finally este optional.Un exemplu simplu de tratare a exceptiei DivideByZeroException si de folosire a blocului try – catch – finally:try{ int x = 0; //raise the exception int y = 4 / x; }

catch { //catch the exception Console.WriteLine("X must be greater than zero");

Page 86: Programare in Windows

}

finally { //this code will be allways executed Console.WriteLine("Program completed"); Console.Read(); } Pot exista mai multe instructiuni catch asociate unui try. Instructiunea care se va executa se va stabili in functie de tipul exceptiei, celelalte sunt ignorate.static void Compute(int [] numbers){ try { int secondNumber = numbers[0]; Console.Write(secondNumber); }

catch (IndexOutOfRangeException) { Console.WriteLine("Must provide more than an argument"); }

catch (NullReferenceException) { Console.WriteLine("Argument is null"); }

catch (FormatException) { Console.WriteLine("Argument is not a number"); }

catch (Exception) { Console.WriteLine("Another exception occured"); } } Se adauga instructiunile pentru blocurile catch cat mai specifice, inainte celor generale.CLR cauta instructiunea catch care va trata exceptia. Daca metoda curenta in care ne aflam cu debug-erul nu contine un bloc catch, atunci CLR va cauta in metoda care a apelat metoda curenta, si asa mai departe conform call stack. Daca nu este gasit nici un catch, atunci CLR va afisa un mesaj cum ca exceptia nu este tratata, unhandled exception message, apoi va opri executia programului.In general, instructiunea catch nu are un parametru. Totusi, il putem adauga in cazul in care vom avea nevoie de accesul la obiectul care reprezinta exceptia. Este folositor pentru furnizarea informatiilor suplimentare despre eroarea produsa.catch (Exception exception){ Console.WriteLine(exception.Message); Console.WriteLine(exception.GetType()); } Fluxul executiei programului continua cu instructiunile aflate dupa blocul catch.ThrowPentru a lansa manual o exceptie se foloseste throw.if (numbers == null)

throw new ArgumentNullException("Array is null"); Tipul obiectului trebuie sa fie o clasa derivata din Exception.Exemplu de folosire a blocului finallyInstructiunile dintr-un bloc finally se executa intotdeauna, chiar daca o exceptie este prinsa sau nu. Acest bloc este folosit pentru a “curata” resursele folosite intr-un bloc trysi pentru a asigura executarea unor instructiuni de cod indiferent de modul in care se iese din blocul precedent de try. static void ReadFile() {

Page 87: Programare in Windows

StreamReader reader = null;

try { //open text file for reading reader = File.OpenText(@"C:\fisier.txt"); //return if the current position is at the end of the stream if (reader.EndOfStream) return; //read the content of the stream Console.WriteLine(reader.ReadToEnd()); }

catch (FileNotFoundException) { Console.WriteLine("File not found"); }

finally { //cleanup code, close the file if(reader != null) reader.Dispose(); } }Relansarea unei exceptiiPresupunem urmatoarea clasa. In metoda Divide vom trimite exceptiaDivideByZeroException la un nivel mai sus, in metoda parinte. class Compute {

public void DivideTwoNumbers(int x, int y) { try { var result = x / y; }

catch(DivideByZeroException) { // the control goes back to parent method throw; } } }In metoda Main : static void Main(string[] args) { Compute comp = new Compute();

try { comp.DivideTwoNumbers(5, 13); }

catch(DivideByZeroException) { Console.WriteLine("Exception caught here"); } }System.ExceptionDupa cum s-a putut observa in exemplele de mai sus, exceptiile sunt reprezentate prin clase. Toate deriva din clasa de baza Exception. Se disting doua clase prin reprezentarea a doua categorii generale de exceptii: exceptii generate de motorul de executie SystemException si exceptii generate de programele de aplicatieApplicationException. Ele nu adauga membri

Page 88: Programare in Windows

noi la clasa de baza, ci doar definesc radacile celor doua ierarhii de clase care reprezinta exceptii. De exemplu, in cazul impartirii la zero se produce o exceptie de tipul DivideByZeroException.Programatorul isi poate defini propriile clase care reprezinta exceptii prin derivarea clasei ApplicationException.Pentru mai multe detalii, se poate consulta documentatia de pe msdn.Un program trebuie sa trateze exceptiile intr-o maniera logica si eleganta si sa isi continue apoi executia. Daca programul nu intercepteaza exceptia, el va fi fortat sa se inchida.Chiar daca nu este considerata optima, o alta modalitate de raportare a erorilor o reprezinta codurile de retur. Ce parere aveti? Voi ati utilizat-o?Pentru ca tratarea erorilor constituie un subiect dificil voi continua intr-un viitor articol crearea propriilor clase de exceptii si consecintele exceptiilor neinterceptate.

91. POLIMORFISM. Polimorfismul parametric. Polimorfismul ad-hoc. Polimorfismul de moştenire.

 “O singura interfata, mai multe metode” – sintagma pe care se bazeaza conceptul de polimorfism. Se incearca stabilirea

unei interfete generice pentru un intreg grup de activitati asemanatoare.

    Un obiect polimorfic este capabil sa ia mai multe forme,  sa se afle in diferite stari, sa aiba comportamente diferite.

Polimorfismul parametric

    O metoda va prelua orice numar de parametri.

    Cand cream o metoda, de regula se stie numarul parametrilor care vor fi transmisi. Sunt cazuri in care nu se intampla

acest lucru si va fi nevoie de un numar arbitrar de parametri. Se va recurge la un tip special de parametru, de

tipul params. Acesta va declara un tablou de parametri, care poate memora zero sau mai multe elemente.

Exemplu ://metoda va returna numarul minimpublic int metoda(params int[] numere){

int minim; //in cazul in care nu e transmis functiei //nici un parametru, afiseaza un mesaj if (numere.Length == 0) { Console.WriteLine("Nu sunt parametri"); return 0; } //initializam variabila //cu primul element al tabloului minim = numere[0]; //comparam fiecare element al tabloului //cu valoarea minima initiala foreach (int i in numere)

if (i < minim) //atribuim valoarea minima variabilei minim minim = i; //inapoi la programul apelant al functiei return minim; }

Polimorfismul ad-hoc

Page 89: Programare in Windows

Se mai numeste si supraincarcarea metodelor, una dintre cele mai interesante facilitati oferite de limbaju C#. Cu ajutorul

acesteia, se pot defini in cadrul unei clase mai multe metode, toate avand acelasi nume, dar cu tipul si numarul parametrilor

diferit. La compilare, se va apela functia dupa numarul parametrilor folositi la apel.

Pentru a supraincarca o metoda, pur si simplu trebuie doar declararea unor versiuni diferite ale sale. Nu este suficient insa,

ca diferenta dintre doua metode sa fie facuta doar prin tipul valorii returnate, ci e nevoie si de tipurile sau numarul

parametrilor.

Exemplu:class SupraincarcareMetoda{

public void CalculeazaMedia() { Console.WriteLine("Nici un parametru"); }//supraincarcam cu un parametru intreg public void CalculeazaMedia(int nota) { Console.WriteLine("O nota:" + nota); }//supraincarcam cu doi parametri intregi public void CalculeazaMedia(int nota1, int nota2) { Console.WriteLine("Doi parametri: " + nota1 + " " + nota2); }//supraincarcam cu parametri double public void CalculeazaMedia(double nota) { Console.WriteLine("Un parametru double:" + nota); } //eroare, daca incerc sa suprascriu //doar prin tipul de date returnat public int CalculeazaMedia(double nota) { // }}

In metoda principala a programului :

//apelam toate versiunile lui CalculeazaMedia SupraincarcareMetoda sup = new SupraincarcareMetoda(); sup.CalculeazaMedia(); sup.CalculeazaMedia(10); sup.CalculeazaMedia(23, 23); sup.CalculeazaMedia(34.34);

Polimorfismul de mostenire

Intr-o ierarhie de clase, se pune problema apelarii metodelor care au aceeasi lista de parametri, dar care sunt in clase

diferite.

Exemplu:class Baza{ public void Afiseaza() { Console.WriteLine("Apelul functiei Afiseaza din clasa de baza\n");

Page 90: Programare in Windows

}

}

class Derivata : Baza{ public void Afiseaza() { Console.WriteLine("Apelul functiei Afiseaza din clasa derivata"); }

}

La compilare se rezolva problema apelarii metode Afiseaza, pe baza tipului declarat al obiectelor :

Derivata obiect2 = new Derivata();//instantiem pe un obiect din clasa derivataBaza obiect1 = obiect2;//afiseaza functia din clasa de Bazaobiect1.Afiseaza();obiect2.Afiseaza();

Modificatorii virtual si override

    Virtual este folosit in declararea unei metode sau a unei proprietati. Acestea se vor numi membri virtuali. Implementarea

unui membru virtual poate fi schimbata prin suprascrierea membrului intr-o clasa derivata.

    Override se foloseste pentru a modifica o metoda sau o proprietate si furnizeaza o noua implementare a unui membru

mostenit dintr-o clasa de baza. Metoda de baza suprascrisa si metoda de suprascriere trebuie sa aiba aceeasi signatura ( tip

si numar de parametri ).

    Implicit, metodele nu sunt virtuale. Nu se pot suprascrie metodele care nu sunt virtuale.

Exemplu:class Baza{ public virtual void Afiseaza() { Console.WriteLine("Apelul functiei Afiseaza din clasa de baza\n"); }}

class Derivata : Baza{ public override void Afiseaza() { Console.WriteLine("Apelul functiei Afiseaza din clasa derivata"); }

}Derivata obiect2 = new Derivata();//instantiem pe un obiect din clasa derivataBaza obiect1 = obiect2;//afiseaza functia din clasa de Bazaobiect1.Afiseaza();obiect2.Afiseaza();

    Polimorfismul ajuta la reducerea complexitatii pentru ca permite unei interfete sa fie folosita de fiecare data pentru

specificarea unei clase generice de actiuni. Programatorul nu va efectua manual selectia. Selectia actiunii

specifice (metoda) va fi facuta de compilator.

Page 91: Programare in Windows

92. Modificatorii virtual şi override. Modificatorul new. Metoda sealed.

93. CONCEPTE DE BAZĂ ALE PROGRAMĂRII VIZUALE.

4.1. Concepte de bază ale programării vizuale Programarea vizuală trebuie privită ca un mod de proiectare a unui program prin operare directă asupra unui set de elemente grafice (de aici vine denumirea de programare vizuală). Această operare are ca efect scrierea automată a unor secvenţe de program, secvenţe care, împreună cu secvenţele scrise textual29, vor forma programul. Spunem că o aplicaţie este vizuală dacă dispune de o interfaţă grafică sugestivă şi pune la dispoziţia utilizatorului instrumente specifice de utilizare (drag, click, hint etc.) Realizarea unei aplicaţii vizuale nu constă doar în desenare şi aranjare de controale, ci presupune în principal stabilirea unor decizii arhitecturale30, decizii ce au la bază unul dintre modelele arhitecturale de bază: a) Modelul arhitectural orientat pe date. Acest model nu este orientat pe obiecte, timpul de dezvoltare al unei astfel de aplicaţii este foarte mic, o parte a codului este generată automat de Visual Stdio.Net, codul nu este foarte uşor de întreţinut şi este recomandat pentru aplicaţii relativ mici sau cu multe operaţii de acces (in/out) la o bază de date. b) Modelul arhitectural Model-view-controller Este caracterizat de cele trei concepte de bază : Model (reprezentarea datelor se realizează într-o manieră specifică aplicaţiei: conţine obiectele de „business”, încapsulează accesul la date), View (sunt utilizate elemente de interfaţă, este format din Form-uri), Controller( procesează şi răspunde la evenimente iar SO, clasele Form şi Control din .Net rutează evenimentul către un „handler”, eveniment tratat în codul din spatele Form-urilor). c) Modelul arhitectural Multi-nivel Nivelul de prezentare ( interfaţa) Se ocupă numai de afişarea informaţiilor către utilizator şi captarea celor introduse de acesta. Nu cuprinde detalii despre logica aplicaţiei, şi cu atât mai mult despre baza de date sau fişierele pe care aceasta le utilizează. Cu alte cuvinte, în cadrul interfeţei cu utilizatorul, nu se vor folosi obiecte de tipuri definite de programator, ci numai baza din .NET. Nivelul de logică a aplicaţiei Se ocupă de tot ceea ce este specific aplicaţiei care se dezvoltă. Aici se efectuează calculele şi procesările şi se lucrează cu obiecte de tipuri definite de programator. Nivelul de acces la date Aici rezidă codul care se ocupă cu accesul la baza de date, la fişiere, la alte servicii. Această ultimă structură este foarte bună pentru a organiza aplicaţiile, dar nu este uşor de realizat. De exemplu, dacă în interfaţa cu utilizatorul prezentăm date sub formă ListView şi la un moment dat clientul ne cere reprezentarea datelor într-un GridView, modificările la nivel de cod nu se pot localiza doar în interfaţă deoarece cele două controale au nevoie de modele de acces la date total diferite. 29 Se utilizează ades antonimia dintre vizual (operaţii asupra unor componente grafice) şi textual (scriere de linii de cod); proiectarea oricărei aplicaţii ”vizuale” îmbină ambele tehnici. 30 Deciziile arhitecturale stabilesc în principal cum se leagă interfaţa de restul aplicaţiei şi cât de uşor de întreţinut este codul rezultat. 26 POO şi Programare vizuală (suport de curs) Indiferent de modelul arhitectural ales, în realizarea aplicaţiei mai trebuie respectate şi principiile proiectării interfeţelor: • Simplitatea Interfaţa trebuie să fie cât mai uşor de înţeles31 şi de învăţat de către utilizator şi să permită acestuia să efectueze operaţiile dorite în timp cât mai scurt. În acest sens, este vitală culegerea de informaţii despre utilizatorii finali ai aplicaţiei şi a modului în care aceştia sunt

Page 92: Programare in Windows

obişnuiţi să lucreze. • Poziţia controalelor Locaţia controalelor dintr-o fereastră trebuie să reflecte importanţa relativă şi frecvenţa de utilizare. Astfel, când un utilizator trebuie să introducă nişte informaţii – unele obligatorii şi altele opţionale – este indicat să organizăm controalele astfel încât primele să fie cele care preiau informaţii obligatorii. • Consistenţa Ferestrele şi controalele trebuie să fie afişate după un design asemănător („template”) pe parcursul utilizării aplicaţiei. Înainte de a implementa interfaţa, trebuie decidem cum va arăta aceasta, să definim „template”-ul. • Estetica Intefaţa trebuie să fie pe cât posibil plăcută şi atrăgătoare.

94. MEDIUL DE DEZVOLTARE VISUAL C# (INTERFEŢA).

95. ELEMENTELE POO ÎN CONTEXT VIZUAL.96. Barele de instrumente.97. CONSTRUIREA INTERFEŢEI UTILIZATOR. Ferestre. Controale

4.3. Ferestre Spaţiul Forms ne oferă clase specializate pentru: creare de ferestre sau formulare (System.Windows.Forms.Form), elemente specifice (controale) cum ar fi butoane (System.Windows.Forms.Button), casete de text (System.Windows.Forms.TextBox) etc. Proiectarea unei ferestre are la bază un cod complex, generat automat pe măsură ce noi desemnăm componentele şi comportamentul acesteia. În fapt, acest cod realizează: derivarea unei clase proprii din System.Windows.Forms.Form, clasă care este înzestrată cu o colecţie de controale (iniţial vidă). Constructorul ferestrei realizază instanţieri ale claselor Button, MenuStrip, Timer etc. (orice plasăm noi în fereastră) şi adaugă referinţele acestor obiecte la colecţia de controale ale ferestrei. Dacă modelul de fereastră reprezintă ferestra principală a aplicaţiei, atunci ea este instanţiată automat în programul principal (metoda Main). Dacă nu, trebuie să scriem noi codul ce realizează instanţierea. Clasele derivate din Form moştenesc o serie de proprietăţi care determină atributele vizuale ale ferestrei (stilul marginilor, culoare de fundal, etc.), metode care implementează anumite comportamente (Show, Hide, Focus etc.) şi o serie de metode specifice (handlere) de tratare a evenimentelor (Load, Click etc.). O fereastră poate fi activată cu form.Show() sau cu form.ShowDialog(), metoda a doua permiţând ca revenirea în fereastra din care a fost activat noul formular să se facă numai după ce noul formular a fost inchis (spunem că formularul nou este deschis modal). Un propietar este o fereastră care contribuie la comportarea formularului deţinut. Activarea propietarului unui formular deschis modal va determina activarea formularului deschis modal. Când un nou formular este activat folosind form.Show() nu va avea nici un deţinător, acesta stabilindu-se direct : public Form Owner { get; set; } F_nou form=new F_nou(); form.Owner = this; form.Show(); Formularul deschis modal va avea un proprietar setat pe null. Deţinătorul se poate stabili setând proprietarul înainte să apelăm Form.ShowDialog() sau apelând From.ShowDialog() cu proprietarul ca argument. F_nou form = new F_nou();form.ShowDialog(this); Vizibilitatea unui formular poate fi setată folosind metodele Hide sau Show. Pentru a ascunde un formular putem folosi : this.Hide(); // setarea propietatii Visible indirect sau this.Visible = false; // setarea propietatii Visible direct Printre cele mai uzuale proprietăţi ale form-urilor, reamintim: • StartPosition determină poziţia ferestrei atunci când aceasta apare prima dată, poziţie ce poate fi setată Manual sau poate fi centrată pe desktop (CenterScreen), stabilită de Windows, formularul având dimensiunile şi locaţia stabilite de programator (WindowsDefaultLocation) sau Windows-ul va stabili dimensiunea iniţială şi locaţia pentru formular (WindowsDefaultBounds) sau, centrat pe formularul care l-a afişat (CenterParent) atunci când formularul va fi afişat modal. 28 POO şi Programare vizuală (suport de curs)

Page 93: Programare in Windows

• Location (X,Y) reprezintă coordonatele colţului din stânga sus al formularului relativ la colţul stânga sus al containerului. (Această propietate e ignorată dacă StartPosition = Manual). Mişcarea formularului ( şi implicit schimbarea locaţiei) poate fi tratată în evenimentele Move şi LocationChanged . Locaţia formularului poate fi stabilită relativ la desktop astfel: void Form_Load(object sender, EventArgs e) { this.Location = new Point(1, 1); this.DesktopLocation = new Point(1, 1); } //formularul in desktop • Size (Width şi Height) reprezintă dimensiunea ferestrei. Când se schimbă propietăţile Width şi Height ale unui formular, acesta se va redimensiona automat, această redimensionare fiind tratată în evenimentele Resize sau in SizeChanged. Chiar dacă propietatea Size a formularului indică dimensiunea ferestrei, formularul nu este în totalitate responsabil pentru desenarea întregului conţinut al său. Partea care este desenată de formular mai este denumită şi Client Area. Marginile, titlul şi scrollbar-ul sunt desenate de Windows. • MaxinumSize şi MinimumSize sunt utilizate pentru a restricţiona dimensiunile unui formular. void Form_Load(object sender, EventArgs e) { this.MinimumSize = new Size(200, 100);... this.MaximumSize = new Size(int.MaxValue, 100);...} • IsMdiContainer precizează dacă form-ul reprezintă un container pentru alte form-uri. • ControlBox precizează dacă fereastra conţine sau nu un icon, butonul de închidere al ferestrei şi meniul System (Restore,Move,Size,Maximize,Minimize,Close). • HelpButton-precizează dacă butonul va apărea sau nu lângă butonul de închidere al formularului (doar dacă MaximizeBox=false, MinimizeBox=false). Dacă utilizatorul apasă acest buton şi apoi apasă oriunde pe formular va apărea evenimentul HelpRequested (F1). • Icon reprezintă un obiect de tip *.ico folosit ca icon pentru formular. • MaximizeBox şi MinimizeBox precizează dacă fereastra are sau nu butonul Maximize şi respectiv Minimize • Opacity indică procentul de opacitate32 • ShowInTaskbar precizează dacă fereastra apare in TaskBar atunci când formularul este minimizat. • SizeGripStyle specifică tipul pentru ‘Size Grip’ (Auto, Show, Hide). Size grip (în colţul din dreapta jos) indică faptul că această fereastră poate fi redimensionată. • TopMost precizează dacă fereastra este afisată în faţa tuturor celorlalte ferestre. • TransparencyKey identifică o culoare care va deveni transparentă pe formă. Definirea unei funcţii de tratare a unui eveniment asociat controlului se realizează prin selectarea grupului Events din ferestra Properties a controlului respectiv şi alegerea evenimentului dorit. Dacă nu scriem nici un nume pentru funcţia de tratare, ci efectuăm dublu click în căsuţa respectivă, se generează automat un nume pentru această funcţie, ţinând cont de numele controlului şi de numele evenimentului (de exemplu button1_Click). Dacă în Designer efectuăm dublu click pe un control, se va genera automat o funcţie de tratare pentru evenimentul implicit asociat controlului (pentru un buton evenimentul implicit este Click, pentru TextBox este TextChanged, pentru un formular Load etc.). Printre evenimentele cele mai des utilizate, se numără : • Load apare când formularul este pentru prima data încărcat în memorie. 32 Dacă va fi setată la 10% formularul şi toate controalele sale vor fi aproape invizibile. Programare vizuală 29 • FormClosed apare când formularul este închis. • FormClosing apare când formularul se va inchide ca rezultat al acţiunii utilizatorului asupra butonului Close (Dacă se setează CancelEventArgs.Cancel =True atunci se va opri închiderea formularului). • Activated apare pentru formularul activ. • Deactivate apare atunci când utilizatorul va da click pe alt formular al aplicatiei. ControaleUnitatea de bază a unei interfeţe Windows o reprezintă un control. Acesta poate fi „găzduit” de un container ce poate fi un formular sau un alt control. Un control este o instanţă a unei clase derivate din System.Windows.Forms şi este reponsabil cu desenarea unei părţi din container. Visual Studio .NET vine cu o serie de

Page 94: Programare in Windows

controale standard, disponibile în Toolbox. Aceste controale pot fi grupate astfel: • Controale de actiune (de exemplu button) care, atunci când sunt acţionate, se poate executa o prelucrare. De exemplu, cel mai important eveniment pentru Button este Click (desemnând acţiunea click stânga pe buton). În exemplul PV1 se adaugă pe formular două butoane şi o casetă text. Apăsarea primului buton va determina afişarea textului din TextBox într-un MessageBox iar apăsarea celui de-al doilea buton va închide închide aplicaţia. După adăugarea celor două butoane şi a casetei text a fost schimbat textul afişat pe cele două butoane au fost scrise funcţiile de tratare a evenimentului Click pentru cele două butoane: private void button1_Click(object sender, System.EventArgs e) { MessageBox.Show(textBox1.Text);} private void button2_Click(object sender, System.EventArgs e) { Form1.ActiveForm.Dispose();} • Controale valoare (label, textbox, picturebox) care arată utilizatorului o informaţie (text, imagine). Label este folosit pentru plasarea de text pe un formular. Textul afişat este conţinut în propietatea Text şi este aliniat conform propietăţii TextAlign. TextBox - permite utilizatorului să introducă un text. Prevede, prin intermediul ContextMenu-ului asociat, un set de funcţionalităţi de bază, ca de exemplu (Cut, Copy, Paste, Delete, SelectAll). PictureBox permite afişarea unei imagini. Exemplul PV2 afişează un grup alcătuit din 3 butoane, etichetate A,B respectiv C având iniţial culoarea roşie. Apăsarea unui buton determină schimbarea culorii acestuia în galben. La o nouă apăsare butonul revine la culoare iniţială. Acţionarea butonului “Starea butoanelor” determină afişarea într-o casetă text a etichetelor butoanelor galbene. Caseta text devine vizibilă atunci când apăsăm prima oară acest buton. Culoarea butonului mare (verde/portocaliu) se schimbă atunci când mouse-ul este poziţionat pe buton. După adăugarea butoanelor şi a casetei text pe formular, stabilim evenimentele care determină schimbarea culoriilor şi completarea casetei text. private void button1_Click(object sender, System.EventArgs e) {if (button1.BackColor== Color.IndianRed) button1.BackColor=Color.Yellow; else button1.BackColor= Color.IndianRed;} 30 POO şi Programare vizuală (suport de curs) private void button4_MouseEnter(object sender, System.EventArgs e) {button4.BackColor=Color.YellowGreen;button4.Text="Butoane apasate";} private void button4_MouseLeave(object sender, System.EventArgs e) {textBox1.Visible=false;button4.Text="Starea butoanelor"; button4.BackColor=Color.Orange;} private void button4_Click(object sender, System.EventArgs e) {textBox1.Visible=true;textBox1.Text=""; if( button1.BackColor==Color.Yellow)textBox1.Text=textBox1.Text+'A'; if( button2.BackColor==Color.Yellow)textBox1.Text=textBox1.Text+'B'; if( button3.BackColor==Color.Yellow)textBox1.Text=textBox1.Text+'C'; } Exerciţiu Modificaţi aplicaţia precedentă astfel încât să avem un singur eveniment button_Click, diferenţierea fiind făcută de parametrul sender. Exerciţiu ( Password) Adăugaţi pe un formular o casetă text în care să introduceţi un şir de caractere şi apoi verificaţi dacă acesta coincide cu o parolă dată. Textul introdus în casetă nu este vizibil (fiecare caracter este înlocuit cu*). Rezultatul va fi afişat într-un MessageBox. • Controale de selecţie (CheckBox,RadioButton) au propietatea Checked care indică dacă am selectat controlul. După schimbarea stării unui astfel de control, se declanşează evenimentul Checked. Dacă propietatea ThreeState este setată, atunci se schimbă funcţionalitatea acestor controale, în sensul că acestea vor permite setarea unei alte stări. În acest caz, trebuie verificată propietatea CheckState(Checked, Unchecked,Indeterminate) pentru a vedea starea controlului.

Page 95: Programare in Windows

98. Proprietăţi comune ale controalelor şi formularelor. Metode şi evenimente.99. Obiecte grafice. Validarea informaţiilor de la utilizator. MessageBox. Interfaţă definită de către utilizator.

4.6. Validarea informaţiilor de la utilizator Înainte ca informaţiile de la utilizator să fie preluate şi transmise către alte clase, este necesar să fie validate. Acest aspect este important, pentru a preveni posibilele erori. Astfel, dacă utilizatorul introduce o valoare reală (float) când aplicaţia aşteaptă un întreg (int), este posibil ca aceasta să se comporte neprevăzut abia câteva secunde mai târziu, şi după multe apeluri de metode, fiind foarte greu de identificat cauza primară a problemei. Validarea la nivel de câmp Datele pot fi validate pe măsură ce sunt introduse, asociind o prelucrare unuia dintre handlerele asociate evenimentelor la nivel de control (Leave, Textchanged, MouseUp etc.) private void textBox1_KeyUp(object sender, System.Windows.Forms.KeeyEventArgs e) {if(e.Alt==true) MessageBox.Show ("Tasta Alt e apasata"); // sau if(Char.IsDigit(e.KeyChar)==true) MessageBox.Show("Ati apasat o cifra"); } Validarea la nivel de utilizator În unele situaţii (de exemplu atunci când valorile introduse trebuie să se afle într-o anumită relaţie între ele), validarea se face la sfârşitul introducerii tuturor datelor la nivelul unui buton final sau la închiderea ferestrei de date. private void btnValidate_Click(object sender, System.EventArgs e) { foreach(System.Windows.Forms.Control a in this.Controls) { if( a is System.Windows.Forms.TextBox & a.Text=="") { a.Focus();return;} } } ErrorProvider O manieră simplă de a semnala erori de validare este aceea de a seta un mesaj de eroare pentru fiecare control . myErrorProvider.SetError(txtName," Numele nu are spatii in stanga"); Aplicatii recapitulative. Urmăriţi aplicaţiile şi precizaţi pentru fiecare dintre ele controalele utilizate, evenimentele tratate: Forma poloneza (PV14), Triunghi (PV15), Ordonare vector(PV16), Subsir crescător de lungime maximă(PV17), Jocul de Nim (PV18) Exerciţiu (Test grila) Realizaţi un generator de teste grilă (întrebările sunt preluate dintr-un fisier text, pentru fiecare item se precizează punctajul, enunţul, răspunsul corect, distractorii şi o imagine asociată enunţului (dacă există). După efectuarea testului se afişează rezultatul obţinut şi statistica răspunsurilor.