Sunteți pe pagina 1din 180

Dezvoltare de aplicatii in Visual Studio .

NET
Presented by developerWorks, your source for great tutorials ibm.com/developerWorks

Table of Contents
If you're viewing this document online, you can click any of the topics below to link directly to that section.

1. Despre manual si despre autorul sau............................... 2. Introducere in .NET Framework - I .................................. 3. Tipuri de date si membri- II ........................................... 4. Gestionarea interfetelor utilizator - III ............................... 5. Concepte POO in Visual Studio .NET - IV ......................... 6. Testarea si debugging-ul aplicatiilor - V ............................ 7. Accesarea bazelor de date. ADO.NET - VI ........................ 8. GDI+. Controale utilizator. Atribute - VII............................ 9. Programare folosind fire de executie - VIII......................... 10. .NET Framework Advanced - IX ................................... 11. Assemblies. Aplicatii - configurare, securizare, instalare, rulare - X .................................................................... 12. XML, Web services - XI.............................................. 13. ASP.NET - XII ......................................................... 14. Appendix ............................................................... 15. Bibliografie, referinte .................................................

2 3 12 35 61 80 98 122 140 142 158 177 178 179 180

Dezvoltare de aplicatii in Visual Studio .NET

Page 1 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Section 1. Despre manual si despre autorul sau Despre manual...


Manualul contine tematica cursului "Dezvoltare de aplicatii in Visual Studio .NET" si se adreseaza, in primul rand, studentilor Facultatii de Informatica care au optat sa urmeze acest curs. Cititorii acestui manual trebuie sa posede cunostinte medii de programare orientata obiect si sa fie familiarizati cu medii de programare vizuala. In plus, o doza de ambitie este suficienta pentru a garanta o buna asimilare a cunostintelor de Visual Studio .NET si, in particular, de C# pe care autorul intentioneaza sa le transmita.

Despre autor...
Gabriel NEGARA Preparator universitar, Facultatea de Informatica, IASI Lucrare de licenta, iunie 2002 - Ant Colony Optimisation Master, iunie 2003 - Tehnici de clasificare in Data Mining MCP 70-316 feb. 2004 Pagina web: http://www.infoiasi.ro/~ngabi E-mail:ngabi@infoiasi.ro Telefon: 0232 201549

Gabriel NEGARA februarie-mai 2004, Iasi

Page 2 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

Section 2. Introducere in .NET Framework - I 1. NET Framework - privire de ansamblu


.NET Framework este o tehnologie care a reusit sa atraga simpatia lumii IT mondiale prin: -usurinta in dezvoltare si utilizare (developing and deploying) -disponibilitatea pe termen lung (high availability) -stabilitatea (reliability) -suportul pentru o gama larga de aplicatii (multi-target application support) -rezultatul rapid (strength) -modernitatea -interoperabilitatea .NET Framework este o platforma de dezvoltare de aplicatii care implementeaza un mecanism eficient de alocare de memorie pentru stocarea datelor si instructiunilor, permitand executia unei aplicatii utilizator numai dupa verificarea unui set de permisiuni; daca sunt indeplinite conditiile de lansare in executie a aplicatiei, mediul de dezvoltare initiaza si gestioneaza executia aplicatiei, gestionand, de asemenea, mecanismul de realocare zonelor de memorie provenite de la resurse care nu mai sunt utlizate de aplicatie. .NET Framework consta in doua mari componente: CommonLanguage Runtime si NET Framework Class Library. .NET Framework este baza dezvoltarii de programe. CLR furnizeaza mare parte din serviciile de baza necesare executiei programelor. Libraria de clase de baza BCL .NET expune un set de clase pre-implementate, care faciliteaza dezvoltarea de programe. Common Language Specification (CLS) defineste un set minim de standarde pe care toate limbajele care folosesc .NET Framework trebuie sa le suporte. Common Type System(CTS) stabileste compatibilitatea de tipuri intre componente implementate in diverse limbaje. Unitatea de baza, elementara a unei aplicatii .NET este assembly-ul, care include un manifest al assembly-ului. Acest manifest descrie assembly-ul si unul sau mai multe module, iar modulele contin codul sursa al aplicatiei. Un executabil .NET este stocat ca un fisier IL (MSIL - Microsoft Intermediate Language). La incarcare, compilatorul transforma codul sursa in code managed (compilat si administrat de CLR), se verifica daca assembly-ul verifica conditiile de securitate impuse de sistemul local. Daca se permite rularea, se creaza un fisier .exe sau .dll numit PE - Executabil Portabil. Acesta este incarcat de motorul de executie care extrage separat MSIL si Metadata. MSIL este JIT-compilat in cod masina (binar) si executat. Prezenta metadatei (data describing data, self describing data) face ca acest cod administrat sa poata sa se auto-descrie (definitii de tipuri, versiune, restrictii legate de securitate, etc). Aceasta extra-informatie asigura practic o interoperabilitate fara compromisuri.
Dezvoltare de aplicatii in Visual Studio .NET Page 3 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

CLR - interoperabilitate multi-limbaj Integrare compilatorul limbajului L in care se programeaza trebuie sa stie sa furnizeze cod intermediar pentru masina virtuala .NET -CLR. Exista, in prezent, cateva zeci de limbaje compatibile cu .NET (C#, C++, Visual Basic .NET, Jscript .NET, COBOL, Perl, etc). Interoperabilitate Modulele scrise in limbajul L pot fi utilizate in orice alt limbaj compatibil .NET. Spre exemplu, o clasa scrisa in L poate fi mostenita in C#, etc. In plus, in .NET, nu se mai pune problema vulnerabilitatii legate de versiunea modulului scris in L (in care se regaseste signatura si implementarea clasei de mai sus). Introducerea metadatei rezolva aceasta problema importanta.

Figura 1 - structura .NET Framework. Interoperabilitate

Page 4 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

Figura 2 - privire de ansamblu .NET Framework - aplicatii, tehnologii, protocoale

2. Libraria de clase de baza .NET (BCL)


Spatii de nume .NET reprezentative Spatiu de nume System Descriere Reprezinta spatiul de nume radacina pentru tipurile low-level din .NET Framework si, de asemenea, pentru tipurile primitive de data, si este baza tuturor celorlalte spatii de nume din .NET BCL. Contine clase care reprezinta o varietate de tipuri container, cum ar fi ArrayList, SortedList, Queue si Stack. Contine, de asemenea, clase abstracte, cum ar fi CollectionBase, foarte utile pentru implementarea propriilor clase cu functionalitate de container. Contine clase utile pentru implementarea comportamentelor componentelor si controalelor la run-time si design-time convertori de tipuri si atribute, legare la surse de date (binding), componente care asigura managamentul licentelor. Contine clase necesare accesarii si manipularii bazelor de
Page 5 of 180

System.Collections

System.ComponentModel

System.Data

Dezvoltare de aplicatii in Visual Studio .NET

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

date, precum si spatii de nume aditionale utilizate pentru accesul la date. System.Data.Common System.Data.OleDb System.Data.SQLClient System.Drawing System.IO System.Math System.Reflection System.Security Contine un set de clase distribuite pentru provideri de date .NET. Contine clase care pot efectua operatii cu provideri de date de tip managed, pentru acces OLE DB la surse de date. Contine clase optimizate pentru interactiunea cu Microsoft SQL Server. Expune functionalitati GDI+, continand clase care faciliteaza randarea grafica. inglobeaza tipuri pentru efectuarea operatiilor de I/O in sistemul de fisiere. Contine un set de functii matematice. Furnizeaza suport pentru obtinerea de informatii si generarea dinamica de tipuri la runtime. Tipuri cu ajutorul carora se pot crea si manipula permisiuni, aspectele legate de criptografie si securitatea accesarii codului constituie, de asemenea, subiecte strans legate de acest spatiu de nume. Contine clase care faciliteaza implementarea de aplicatii cu fire de executie multiple. Contine tipuri implicate in crearea aplicatiilor Windows standard. Clasele care reprezinta forme si controale sunt, de asemenea, incluse aici.

System.Threading System.Windows.Forms

Numele spatiilor de nume sunt auto-descriptive prin design. Aceasta modalitate de utilizare a numelor face ca .NET Framework sa fie usor de folosit si permite utilizatorului o familiarizare rapida cu mediul de lucru. Libraria de clase de baza .NET Framework este o librarie de cod care furnizeaza functionalitati folositoare pentru construirea aplicatiilor. BCL este organizata in spatii de nume, care contin tipuri si spatii de nume aditionale, legate prin functionalitate comuna. Se poate folosi cuvantul rezervat using pentru a permite referentierea unor membri ai unui spatiu de nume fara a folosi un nume specificat prin calea comnpleta catre membrul respectiv. Daca se doreste folosirea unei librarii externe, trebuie creata mai intai o referinta catre aceasta. Tipuri valoare si tipuri referinta Tipurile pot fi: tipuri valoare sau tipuri referinta. O variabila de tip valoare contine toate datele asociate cu tipul respectiv. O variabila de tip referinta contine un pointer la o instanta a unui obiect de tipul respectiv. Tipurile valoare predefinite sunt create la declarare si ramin vide pana in momentul in care li se asigneaza o valoare. Tipurile referinta trebuie instantiate dupa declarare pentru a crea efectiv un obiect de acel tip. Declararea si instantierea pot fi combinate
Page 6 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

intr-un singur pas, fara nici o pierdere legata de performanta. Cand o variabila de tip valoare este asignata unei alte variabile de tip valoare, datele continute in prima variabila sunt copiate in cea de-a doua. Atunci cand o variabila de tip referinta este asignata unei alte variabile de acelasi tip, numai referinta catre obiect este copiata, si ambele variabile vor face referire catre acelasi obiect.

3. Clase si structuri
Clase versus Structuri La prima vedere, clasele si structurile par foarte asemanatoare. Ambele pot contine membri, cum ar fi campuri si metode, ambele necesita un constructor pentru a se crea o noua instanta si, ca toate tipurile din .NET Framework, ambele sunt derivate din Object. Diferenta dintre clase si structuri este accea ca structurile sunt tipuri valoare, iar clasele sunt tipuri referinta. La nivel de alocare, aceasta inseamna ca datele de instantiere pentru clase sunt alocate pe heap, in timp ce pentru structuri acestea sunt alocate pe stack. Acesul la stack a fost proiectat in asa fel incat sa fie facil si rapid, dar stocarea unor date de dimensiuni mari pe stack pot conduce la diminuari ale performantei aplicatiei. in termeni practici, structurile reprezinta varianta optima pentru obiecte de dimensiuni mici si pentru care se definesc putine instante, persistente pentru perioade relativ mici in memorie. Clasele sunt optim folosite pentru obiecte de dimensiuni mai mari si pentru care de definesc un numar mai mare de instante, cu posibilitatea mentinerii lor in memorie pentru perioade mari de timp. Sinteza Tipurile definite de uzilizator includ clase si structuri. Ambele pot avea membri campuri, proprietati, metode sau evenimente. Clasele sunt tipuri referinta iar structurile sunt tipuri valoare. Cuvantul rezervat class este folosit in Visual C# pentru a defini clase. Structurile sunt create utilizand cuvantul cheie struct. Atat clasele, cat si structurile pot contine tipuri imbricate. Tipurile definite de utilizator sunt instantiate in acceasi maniera ca tipurile predefite, exceptand faptul ca atat tipurile caloare cat si tipurile referinta trebuie sa foloseasca cuvantul cheie new pentru instantiere.

4. Cum folosim metodele?


Metodele executa manipulari ale datelor care imprima claselor si structurilor un anumit comportament. Metodele pot returna o valoare, dar acest lucru nu este necesar. in Visual C#, daca o metoda nu returneaza o valoare, se specifica void drept tip returnat. Metodele sunt apelate prin plasarea numelui metodei in cod, specificandu-se eventualii parametrii necesari.
Dezvoltare de aplicatii in Visual Studio .NET Page 7 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Metodele pot avea parametri, care reprezinta valori solicitate de metoda. Parametrii sunt transmisi, implicit, prin valoare. Transmiterea parametrilor prin referinta se face in Visual C# folosind cuvantul rezervat ref. Visual C# permite specificarea parametrilor de iesire pentru o anumita metoda. Constructorul este prima metoda apelata la instantierea unui tip. Constructorul furnizeaza o modalitate de a seta valori implicite ale datelor sau de a executa alte operatii necesare, inainte ca obiectul sa fie disponibil pentru operatii ulterioare. Destructorii sunt apelati exact inainte ca un obiect sa fie distrus si pot fi folositi pentru a executa cod de clean-up, de dealocare de memorie. Pentru ca dealocarea memoriei alocate obiectelor este controlata de common language runtime, utilizatorul nu poate controla momentul apelarii destructorului.

5. Nivele de acces si domenii de vizibilitate (scope)


C# - Access Levels Modificator de acces public private internal protected protected-internal Efect asupra membrilor poate fi accesat de oriunde poate fi accesat doar de membrii din cadrul tipului care il defineste poate fi accesat de toate tipurile din cadrul assembly-ului parinte, dar nu dinafara acestuia poate fi accesat numai de membrii din cadrul tipului care il defineste sau de tipuri care mostenesc acest tip poate fi accessed de toate tipurile din assembly sau de tipuri mostenite din tipul care contine membrul in discutie; reprezinta reuniunea tipurilor de acces protected si internal

Modificatorii de acces sunt folositi pentru a controla domeniul de vizibilitate a membrilor unui tip de date. Exista 5 modificatori de acces: public, internal, private,protected si protected internal. Fiecare dintre acestea determina un anumit nivel de acces. Daca pentru o metoda nu se specifica nici un modificator de acces, nivelul de acces asociat implicit este private pentru clasele si structurile din Visual C#. Daca nu este specificat nici un modificator de acces pentru o variabila membru, acesta este considerata private intr-o clasa si publicintr-o structura. Modificatorii de acces pot fi, de asemenea, folositi pentru a controla modul in care este instantiat un tip. Nivelele de acces pentru tipuri sunt urmatoarele: tipurile public pot fi instantiate de oriunde. Tipurile internal pot fi instantiate numai de membrii assembly-ului, tipurile private pot fi instantiate numai de ele insele sau dintr-un tip care le contine. Daca nu se specifica un modificator de acces pentru o clasa sau structura, este considerata public. Tipurile imbricate se supun acelorasi reguli ca si tipurile ne-imbricate dar, in practica,
Page 8 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

ele nu vor avea niciodata un nivel de acces mai mare decat cel al tipului parinte. Membrii de tip static apartin tipului si nu unei instante anume a tipului respectiv. Ei pot fi accesati fara a crea o instanta a tipului si sunt accesati folosind numele tipului.

6. Garbage Collection
.NET Framework executa managementul automat al memoriei, ceea ce inseamna ca atunci cand un obiect nu mai este folosit, .NET Framework elibereaza automat memoria folosita de acel obiect. Acest proces se numeste garbage collection. Sa consideram urmatorul exemplu: void GarbageCollectionExample() { TipObiect obiect = new TipObiect(); } La sfarsitul acestei proceduri, variabila obiect iese domeniul sau de vizibilitate, iar obiectul la care face referire nu mai este referentiat de nici o variabila din cadrul aplicatiei. Garbage collector-ul gestioneaza in background arborele de referinte si identifica obiectele catre care nu se mai face referinta. Cand gaseste un astfel de obiect, cum ar fi TipObiect din exemplul anterior, il sterge si solicita eliberarea zonei de memorie ocpate de obiectul respectiv. Pentru ca garbage collector-ul ruleaza continuu, utilizatorul nu trebuie sa distruga explicit obiectele la care nu se mai face referire in program de la un anumit moment. Garbage collector-ul reprezinta, de fapt, un fir de executie de prioritate scazuta, in conditii normale. El intervine in executia programului cand timpul procesor nu este consumat de sarcini mai importante. Totusi, atunci cand zonele de memorie libera se imputineaza semnificativ, firul de executie asociat garbage collector-ului creste in prioritate. Memoria utilizata de obiecte care nu mai sunt referentiate in program este solicitata intr-o maniera mai insistenta, pana cand se elibereaza o zona suficienta de memoria, caz in care firul de executie al garbage collector-ului trece la o prioritate scazuta. Aceasta abordare non-deterministica de eliberare a memoriei are ca scop maximizarea performantei aplicatiei si furnizeaza un mediu de aplicatii mai putin predispus la actiunea bug-urilor (less bug-prone ). Exista, insa, un cost. Datorita mecanismului prin care opereza garbage collector-ul, utilizatorul nu poate sti momentul in care un obiect va fi dealocat. Deci, nu exista control asupra momentului apelarii destructorului clasei Visual C# in cauza. Aceste metode ar trebui sa nu contina cod care se proiecteaza a fi executat la un anumit moment. in schimb, pot fi scrise clase care utlizeaza o cantitate importanta de resurse si care contin o metoda Dispose() pentru eliberarea explicita a acestor resurse, atunci cand o instanta a acestei clase nu mai este necesara in program. Referinte circulare

Dezvoltare de aplicatii in Visual Studio .NET

Page 9 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Procesul de garbage collection are in vedere, de asemenea, si referintele circulare, o forma foarte comuna de utilizare ineficienta a memoriei in medii de dezvoltare anterioare. Sa consideram urmatorul exemplu: class Widget { public Widget ChildWidget; public Widget Parent; } class aClass { Widget GrandParent; void Demo() { Widget Parent; Widget Child; GrandParent = new Widget(); GrandParent.ChildWidget = new Widget(); Parent = GrandParent.ChildWidget; Parent.ChildWidget = new Widget(); Child = Parent.ChildWidget; Child.Parent = Parent; GrandParent = null; //acum, Parent si Child se autorefera - referinta circulara } } Desi referintele circulare pot crea probeleme in ceea ce priveste detectarea zonelor de memorie ocupate inutil in alte platforme de dezvoltare, garbage collector-ul din .NET Framework este capabil sa detecteze si sa faca disponibile aceste zone. Deci, daca o pereche de obiecte se autoreferentiaza, vor fi supuse procesului de garbage collection. .NET Framework gestioneaza procesul de eliberare automata a memoriei prin intermediul garbage collector-ului. Garbage collector-ul este un fir de executie de prioritate scazuta care ruleaza intotdeauna im background-ul aplicatiei. Cand memoria disponibila s-a diminuat, prioritatea garbage collector-ului creste pana cand se vor elibera destule resurse.

Page 10 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

Pentru ca nu se poate stabili momentul in care un anumit obiect va fi eliminat de garbage collector, utilizatorul nu trebuie sa mizeze de codul scris in interiorul destructorilor. in schimb, pentru eliberarea resurselor, se poate folosi o metoda Dispose() care va fi apelata explicit. Garbage collector-ul analizeaza in mod continuu arborele de referinte si elimina obiectele la care nu se mai face referinta, precum si obiectele cu referinte circulare. Detalii - MSDN Library, criteriu de cautare circular reference

Dezvoltare de aplicatii in Visual Studio .NET

Page 11 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Section 3. Tipuri de date si membri- II 1. Tipuri de date. Utilizare.


Microsoft .NET Framework furnizeaza un sistem foarte robust de tipuri primitive care pot fi folosite pentru stocarea si reprezentarea datelor din cadrul aplicatiei. Datele primitive reprezinta numere intregi, flotante, valori booleene, caractere si string-uri. Sistemul de tipuri de date .NET Framework este type-safe, adica conversiile implicite intre diferitele tipuri de date au loc numai daca acest lucru nu conduce la pierderi de date. Conversiile explicite pot fi executate in situatii cate pot provoca pierderi de date. Tipurile de date .NET contin functionalitati pentru a executa un set diversificat de conversii de tip si alte sarcini similare. OBIECTIVE: intelegerea mecanismului conversiilor implicite si explicite dintre tipuri descrierea functionalitatilor oferite de tipurile .NET Framework prezentarea operatiilor cu string-uri, folosind metodele clasei String .NET Framework pune la dispozitia utilizatorului un sistem extensibil de tipuri care permit stocarea, manipularea si transmiterea valorilor intre diferitele entitati din cadrul aplicatiei. Limbajele .NET sunt puternic tipizate (strongly typed). Aceasta inseamna ca obiectele de un anumit tip nu pot fi schimbate cu obiecte de un alt tip. Apelul unei metode va esua daca nu se specifica parametri avand tipurile corespunzatoarte. Conversiile implicite si explicite permit transformari asupra datelor atunci cand este nevoie. Acest lucru este posibil deoarece toate tipurile din .NET Framework sunt derivate din Object, tipul de baza al tuturor claselr si structurilor. Tipuri de date .NET Tipuri valoare - subcategorii: tipuri intregi tipuri flotante tipul Boolean tipul Char

Tipuri referinta - frecvent folosite in aplicatii: String Object Tipuri intregi Tabelul urmator sintetizeaza aceste tipuri, prezentand tipurile din Visual C# corespunzatoare.

Page 12 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

Tip System.Byte System.Int16 System.Int32 System.Int64

Reprezentare C# byte short int long

Descriere intreg fara semn pe 8 biti intreg cu semn pe 16 biti intreg cu semn pe 32 biti intreg cu semn pe 64 biti intreg cu semn pe 8 biti intreg fara semn pe 16 biti intreg fara semn pe 32 biti intreg fara semn pe 64 biti

Domeniu de la 0 la 255 de la -32768 la 32767 de la -2 la 31 la 2 la 31 - 1 de la -2 la 63 la 2 la 63 - 1 de la -128 la 127 de la 0 la 65535 de la 0 to 2 la 32 -1 de la 0 to 2 la 64 -1

System.SByte sbyte System.UInt16 ushort System.UInt32 uint System.UInt64 ulong

Tabel 1 - Tipuri intregi Se pot atribui valori tipurilor intregi folosind fie notatia zecimala, fie hexazecimala. Pentru a folosi notatia hexazecimala pentru un literal intreg, acesta trebuie prefixat cu 0x in Visual C#. De exemplu: int nr_intreg; nr_intreg = 0x36BA; Tipuri flotante Exista trei astfel de tipuri care pot fi folosite pentru a reprezenta numere cu parte fractionara. Tabelul urmator sintetizeaza aceste tipuri: Tip System.Single System.Double System.Decimal Tabel 2 - Tipuri flotante Observatii: System.Single - util pentru calcule in virgula mobila care necesita un grad scazut de precizie System.Double - pentru calcule cu un grad mai mare de precizie, operand cu numere dintr-un interval foarte vast System.Decimal - grad foarte mare de precizie, folosind insa numere dintr-un interval mai restrans Tipuri non-numerice
Dezvoltare de aplicatii in Visual Studio .NET Page 13 of 180

Reprezentare C# float double decimal

Descriere variabila flotanta pe 32 biti, precizie - 7 cifre semnificative pe 64 biti semnificativi, precizie 15-16 pe 128 biti, precizie - 28 semnificativa

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

System.Boolean System.Char System.String System.Object

System.Boolean Tipul System.Boolean este folosit pentru a reprezenta o valoare care este fie true, fie false. In C#, o variabila booleana este desemnata prin intermediul tipului bool, cu valorile posibile true si false. System.Char Tipul System.Char reprezinta un caracter Unicode pe 16 biti. In C#, o variabila de tip char (tipul C# asociat) poate fi definita prin specificarea explicita a unui caracter intre apostroafe: char caracter; caracter = 't'; sau atribuind valoarea numerica a codului Unicode al caracterului variabilei de tip char, folosind o valoare hexazecimala din 4 cifre, corespunzatoare: char caracter; caracter = '\u01fe'; System.String Tipul System.String este un tip referinta care reprezinta o succesiune de date de tip Char. Cu alte cuvinte, un string poate desemna un cuvant, un paragraf, un cod sau orice alta succesiune de caractere. Tipul C# asociat este desemnat de cuvantul rezervat string. Exemplu de utilizare: string sir; sir = "Acesta este un string."; System.Object Tipul Object este parintele tuturor tipurilor din .NET Framework. Orice tip, fie valoare, fie referinta, este derivat din System.Object. Tipul C# asociat - object. Unei variabile object i se poate asocia orice valoare sau obiect: object obj; obj = 233; obj = new System.Collections.ArrayList(); ! Daca un obiect de un anumit tip este stocat intr-o variabila object, trebuie reconvertit explicit la acel tip pentru a accesa intreaga sa functionalitate.

Page 14 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

Tipuri de conversii Datele pot convertite in doua feluri: implicit, caz in care conversia este executata automat, si explicit - conversie executata de utilizator. Conversii implicite Sunt conversii care se executa atunci cand nu exista riscul pierderilor de date. De exemplu: int un_short = 100; long un_long; un_long = un_short; Daca un tip poate fi convertit implicit la un alt tip, atunci primul tip poate fi utilizat oriunde in program unde se solicita cel de-al doilea tip, fara a folosi o sintaxa speciala. De exemplu: // functia prelucare_long // are un parametru de tip long int x = 100; // conversie implicita de la int la long // efectuata la apelul functiei prelucare_long(I); Tabelul urmator ilustreaza conversiile implicite permise in C#: De la byte short int long float char sbyte ushort uint ulong Conversii explicite In cazul unei conversii in care tipurile nu pot fi convertite implicit, trebuie aplicata o conversie explicita. Aceasta conversie se mai numeste cast. In C#, conversiile explicite necesita folosirea unei sintaxe speciale. Exemplu: int un_int = 100; La short, ushort, int, uint, long, ulong, float, double, decimal int, long, float, double, decimal long, float, double, decimal float, double, decimal double int, uint, long, ulong, float, double, decimal short, int, long, float, double, decimal int, uint, long, ulong, float, double, decimal long, ulong, float, double, decimal float, double, decimal

Dezvoltare de aplicatii in Visual Studio .NET

Page 15 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

short un_short; un_short = (short)un_int; ! Conversiile explicite pot fi riscante. In exemplul anterior, conversia s-a efectuat fara nici un fel de problema pentru ca atat o variabila int, cat si una de tip short poate retine valoarea 100. Sa consideram acum urmatorul exemplu: int un_alt_int = 80000; short un_alt_short; un_alt_short = (short)un_alt_int; Codul acestui exemplu nu va furniza eroare de compilare sau la run-time. Totusi, examinand valoarea lui short, aceasta nu va fi cea dorita, deoarece valoarea maxima a domeniului tipului short este mai mica decat valoarea la care se incearca conversia. ! Deoarece conversiile explicite sunt adesea riscante, este indicata folosirea lor numai in cazuri absolut necesare, implementand un sistem adecvat de tratare a conversiilor nereusite si a tuturor exceptiilor care ar putea fi aruncate. (vezi cursul 5). Functionalitati asociate tipurilor de date Toate tipurile de date au o functionalitate implicita (built-in ). Acestea suporta urmatoarele patru metode: Equals - determina daca doua instante sunt egale GetHashCode - actioneaza ca o functie hash pentru un tip de date GetType - returneaza tipul obiectului pentru instanta curenta ToString - returneaza o descriere a obiectului, intr-un format accesibil utilizatorului

? Din cele prezentate mai sus, reiese faptul ca tipurile de date ocupa un relativ mic de memorie. Totusi, cum este posibil ca fiecare tip sa implementeze aceste metode? Raspunsul este dat in cele ce urmeaza. Boxing Boxing reprezinta conversia implicita a tipurilor valoare la tipuri referinta. Toate clasele si tipurile sunt derivate din Object, fiecare din cele patru metode amintite fiind membre ale clasei Object. Deoarece toate clasele sunt derivate din Object, fiecare clasa poate fi convertita implicit la acest tip. La apelul uneia dintre cele patru metode, Common Language Runtime creaza o referinta temporara variabila de tip valoare declarata de utilizator si se permite tratarea acesteia ca fiind de tip referinta. Boxing explicit(manual) - prin atribuirea variabilei detip valoare unei variabile de tip Object. Exemplu: short s = 10; object O; O = s; Unboxing reprezinta conversia unei variabile boxed la un tip valoare. Se realizeaza prin conversia explicita a obiectului la tipul dorit. Se poate executa unboxing numai pentru obiectele care au fost supuse operatiei de boxing. Metoda Parse
Page 16 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

Parse - metoda statica, asociata tuturor tipurilor de date. Efectueaza conversia string-ului primit ca parametru la tipul din care face parte. Daca string-ul nu are un continut specific(potrivit) tipului care apeleaza metoda, se va genera o eroare. short s; string input; input = "321"; // s = 321 s = short.Parse(input); Functii pentru operatii cu siruri de caractere Metode de instanta pentru siruri de caractere Metoda String.Insert String.PadLeft, String.PadRight String.Remove String.Replace String.Insert String.Split String.Substring String.ToCharArray String.ToLower, String.ToUpper String.TrimEnd, String.TrimStart, String.Trim Descriere Insereaza un string specificat in instanta curenta Adauga caractere la stanga, respectiv la dreapta sirului Elimina un numar specificat de caractere din string, incepand de la un caracter precizat Inlocuieste toate aparitiile unui caracter specificat din string cu un alt caracter Insereaza un string specificat in instanta curenta Returneaza un array de substringuri, obtinut prin separarea token-urilor din stringul initial, dupa un separator specificat Returneaza un substring al stringului initial Returneaza caracterele compunente ale sirului, sub forma de array de caractere Transforma stringul, convertind caracterele la litere mici, respectiv mari Elimina spatiile de la sfarsitul, inceputul stringului sau, respectiv toate spatiile din cadrul sirului

Metode statice cu siruri de caractere Metoda String.Compare String.Concat String.Format String.Join Descriere compara doua stringuri returneaza un string obtinut prin concatenarea a doua sau mai multe siruri de caractere returneaza stringul formatat returneaza un string obtinut prin concatenarea unui array de stringuri, specificand un separator si inserandu-l intre componentele array-ului

Dezvoltare de aplicatii in Visual Studio .NET

Page 17 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Sinteza .NET Framework furnizeaza un sistem robust, puternic tipizat de tipuri de date. Principalele tipuri - intregi, flotante, boolean, caracter si string. Exista doua tipuri de conversii - implicite, executate automat de CLR, atunci cand nu exista riscul pierderii de date si explicite, efectuate explicit prin scriere de cod, programatorul trebuind sa analizeze ci atentie cazurile care ar putea conduce la erori. Boxing - macanismul care permite tratarea tipurilor valoare ca tipuri referinta. Unboxing - converteste un tip referinta asupra caruia s-a efectuat anterior boxing catre un tip valoare. Tipurile .NET contin functionalitate built-in specifica tipului. Metoda Parse este implementata de toate tipurile valoare, fiind utila pentru conversia string-uriloe la tipuri valoare. Clasa String contine un set de functii care faciliteaza manipularea string-urilor.

2. Constante, enumerari, tablouri, colectii


Utilizatorul simte adesea necesitatea organizarii grupurilor de obiecte intr-un anumit tip de structura. De exemplu, anumite obiecte, de acelasi tip sau de tipuri diferite trebuie accesate secvential si este necesara o sintaxa pentru gestionarea organizarii obiectelor. Tablourile (array) permit organizarea grupurilor de obiecte similare si referirea lor printr-un index, adesea in defavoarea accesarii prin nume. Colectiile sunt similare tablourilor, dar implementeaza o functionalitate mai complexa, fiind utilizate, in special, pentru organizarea grupurilor de obiecte de tipuri diferite. Constantele pot primi nume familiare si contin valori utilizate frecvent in program. Enumerarile (enum) permit organizarea unor multimi de constante, facilitand executia anumitor operatii din cadrul programului. Constante si enumerari Constantele permit asignarea unor nume familiare anumitor valori utilizate frecvent in cod. Enumerarile sunt multimi definite de utilizatori continand constante asociate unei multimi de nume. Folosirea constantelor si enumerarilor faciliteaza scrierea de cod, debugging-ul codului si reduc numarul erorilor de scriere. Utilizarea constantelor Se foloseste cuvantul rezervat const Exemplu: public const int MAX = 25000; Constantele pot fi de orice tip intrinsec sau enumerativ, dar nu de un tip definit de utilizator sau array. Constantele au o valoare fixata care odata stabilita nu mai poate fi schimbata sau redefinita. Dupa definire, o constanta poate fi folosita in cod, prin specificarea numelui asociat. De exemplu: public const int MAX = 25000;

Page 18 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

public int half_max() { return MAX/2; } Ca si variabilele, constantele pot avea orice nivel de acces. Pentru a defini o constanta care va putea fi accesata de toti utilizatorii aplicatiei sau componentei, trebuie declarata public. O constanta declarata private va putea fi accesata doar in cadrul domeniului clasei care o defineste. internal permite accesarea constantei din cadrul assembly-ului care o contine, iar o constanta protected permite accesul la constanta si tipurilor mostenite din clasa care contine constanta. Enumerari Enumerarile permit lucrul cu multimi de constante, prin asocierea acestora cu nume familiare utilizatorului. Exemplu: public enum LunileAnului { Ianuarie = 1, Februarie = 2, // si celelalte..., cu valorile uzuale Decembrie = 12 } Tipul asociat implicit elementelor enumerarilor int, dar poate fi setat la orice alt tip numeric integral de date (byte, short, int si long). Modalitatea de definirea explicita a tipului elementelor se poate observa in exemplul urmator: public enum LunileAnului : short // cod ... NU este obligatorie asocierea de valori membrilor enumerarii. Implicit, membrii vor primi valori implicite, secvential, pornind de zero. Exemplu: public enum Cifre { zero, // egal cu zero unu, // egal cu doi doi // egal cu trei } Dupa definirea membrilor enumerarii, acestia pot fi folositi in cod. In C#, trebuie efectuata o conversie explicita a membrilor la tipul dorit pentru a putea accesa valoarea reprezentata. De exemplu:
Dezvoltare de aplicatii in Visual Studio .NET Page 19 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

// se utilizeaza enumerarea Cifre anterioara MessageBox.Show(((int)Cifre.doi * 3).ToString()); // se afiseaza 6 ! Pot fi scrise metode care primesc enumerari ca parametri; codul scris astfel este mai putin supus la erorile de scriere. Exemplu: public void PlanLunar(LunileAnului luna) //enumerarea definita mai sus { switch(luna) { case LunileAnului.Februarie: // cod... break; case LunileAnului.Martie: // cod... break; // alte case-uri... } } Tablouri (array) Reprezinta o modalitate de gestionare a unei multimi de elemente de tip valoare sau a unei multimi de obiecte. Folosind tablouri, se pot grupa o serie de variabile, la care se poate face referire folosind un index. Se pot accesa toate sau o parte dintre variabile si examina sau modifica fiecare dintre aceste variabile. Se pot defini, de asemenea, tablouri multidimensionale. In .NET Framework, tablourile contin functionalitati intrinseci(buit-in) care faciliteazaa o serie de operatii. Declararea si initializarea tablourilor Declararea si initializarea se pot face in cadrul aceleasi instructiuni. Pentru declarare, se specifica tipul si numarul elementelor din tablou. Pentru toate tablourile din C#, indexul primului element este zero, celelalte avand indecsi calculati secvential, pornind de la zero. Exemplu: // tablou cu 20 de elemente de tip short short[] array_shorts = new short[20]; Declararea si initializarea se pot efectua, desigur, si in pasi separati. Exemplu:
Page 20 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

//declarare short[] array_shorts; // initializare array_shorts = new short[20]; Un tablou poate fi redefinit, modificandu-i dimensiunea la run-time. Exemplu: // declarare si initializare int[] intregi = new int[20]; // reinitializare intregi = new int[45]; In exemplul anterior, datele continute de tablou sunt pierdute la reinitializare. Nu exista nici o modalitate de salvare a datelor la reinitializare (excluzand o varianta manuala). La crearea unui tablou cu elemente de tip referinta, declararea si initializarea tabloului nu conduc la umplerea tabloului cu membri de tipul respectiv. De fapt, se creaza un tablou de referinte null care pointeaza la acel tip. Pentru a completa tabloul cu membrii, trebuie asignata fiecare variabila la un obiect. De exemplu: // se creaza un tablou de obiecte TipObiect[] obiecte = new TipObiect[10]; // se asigneaza elementului obiecte[0] un nou obiect de tip TipObiect obiecte[0] = new TipObiect(); // asigneaza elementului obiecte[1] un obiect existent TipObiect un_obiect = new TipObiect(); obiecte[1] = un_obiect; // se atribuie celorlalte elemente cate un nou obiect for (int contor = 2; contor < 10; contor++) { obiecte[contor] = new TipObiect(); } Tablouri multidimensionale Tablourile prezentate pana acum sunt liniare, adica, tablouri cu o singura dimensiune. Exista in .NET Framework doua tipuri de tablouri multidimensionale: tablouri rectangulare si tablouri jagged. Tablouri rectangulare Sunt tablouri in care fiecare membru al fiecarei dimensiuni este extins in fiecare alta
Dezvoltare de aplicatii in Visual Studio .NET Page 21 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

dimensiune folosind aceeasi lungime. De exemplu, un tablou bidimensional, rectangular, poate fi gandit ca un tabel tabel, continand linii si coloane, fiecare linie avand acelasi numar de coloane. Exemplu: // tablou de 4 pe 3 int[ , ] intregi = new int[4, 3]; // tablou bidimensional cu valori initiale specificate int[ , ] intregi2 = {{1, 2, 3}, {4, 5, 6}}; // tablou tridimensional int[ , , ] cubeArray = {{{2, 2}, {2, 4}}, {{5, 5}, {4, 4}}}; // tablou complex: 2 x 3 x 5 x 5 x 6 int[ , , , , ] tablou_complex = new int[2, 3, 5, 5, 6]; Tablouri jagged Un astfel de tablou, bidimensional, poate fi conceput ca un tabel in care fiecare linie poate avea un numar diferit de coloane. De exemplu, sa consideram un tabel in care fiecare linie reprezinta un grad universitar, iar coloanele contin numele profesorilor care au gradul respectiv. Liniile vor putea avea, deci, un numar diferit de elemente. Pentru a gestiona o astfel de structura, se foloseste un tablou jagged, ca mai jos: // tablou de 5 tablouri string[][] grade = new string[5][]; // specificam numai prima dimensiune // primul tablou are 4 membri grade[0] = new string[] {"Popescu", "Negara", "Ionescu", "Radulescu"}; //o alta varianta de declarare si initializare // al doilea tablou are 3 elemente grade[1] = new string[] {"Vidrescu", "Alexandrescu", "Macovei"}; // si asa mai departe... grade[3] = new string[] {"Buburuzan", "Georgescu"}; ... Colectii Colectiile contin functionalitati avansate pentru gestionarea grupurilor de obiecte. O colectie este o clasa specializata care organizeaza si permite manipularea unui grup de obiecte. La fel ca la tablouri, membrii unei colectii pot fi accesati prin index. In plus, colectiile pot fi redimensionate dinamic, existand posibilitatea adaugarii si eliminarii de elemente la run time.
Page 22 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

Colectiile sunt utile pentru gestionarea grupurilor de elemente create dinamic la run time. De exemplu, ne putem imagina o colectie in care un element contine informatii despre un salariat al unei institutii. Creand o colectie de obiecte de tip Salariat, va fi posibila accesarea obiectelor, iterarea acestora, adaugarea de noi obiecte sau eliminarea obiectelor care devin irelevante. Clasa ArrayList System.Collections.ArrayList - confera functionalitati specifice de colectie utile intr-o serie intreaga de situatii. Aceasta clasa permite adaugarea si eliminarea dinamica de elemente din lista. Elementele sunt accesate prin index Exemplu: System.Collections.ArrayList lista = new System.Collections.ArrayList(); Metoda Add Dupa instantiere, pot fi adaugate elemente intr-un ArrayList utilizand metoda Add: TipObiect obiect = new TipObiect(); lista.Add(obiect); ! Orice lista de tip ArrayList este o colectie zero-based . Primul obiect introdus in lista are indexul zero, elementele adaugate ulterior primind indexul imediat urmator elementului anterior. In C#, indexatorii sunt folositi pentru accesarea elementelor colectiilor, utilizand o sintaxa similara tablourilor. Exemplu: object obj; obj = lista[0]; ! Toate referintele dintr-o colectie sunt de tipul object. Se impune, deci, folosirea conversiilor explicite, pentru a obtine un element avand acelasi tip ca tipul elementului din lista, returnat de indexator: TipObiect obj; obj = (TipObiect)listaObiecte[1]; Metodele Remove si RemoveAt Remove - elimina obiectul a carui referinta este primita ca parametru din colectie RemoveAt - elimina din colectie elementul de la indexul a carui valoare este primita ca parametru Exemplu:

Dezvoltare de aplicatii in Visual Studio .NET

Page 23 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

TipObiect obj = new TipObiect(); System.Collections.ArrayList lista = new System.Collections.ArrayList(); // adauga obj la colectie lista.Add(obj); // elimina obiectul obj din colectie lista.Remove(obj); System.Collections.ArrayList lista = new System.Collections.ArrayList(); // adauga 5 elemente la colectie for (int i = 0; i < 5; i++) { TipObiect obj = new TipObiect(); lista.Add(obj); } // elimina elementul cu indexul 3 lista.RemoveAt(3); ! Incercarea de eliminare a unui obiect care nu este continut in colectie va fi ignorata, fara a se genera eroare. La eliminarea unor elemente din colection, are loc o reasignare a indecsilor pentru a se ocupa spatiile disponibile. Deci, valorile indecsilor sunt dinamice si pot returna referinte diferite la momente diferite ale executiei aplicatiei. Proprietatea Count Returneaza numarul de elemente din colectie (cu 1 mai mult decat indexul maxim). Alte tipuri de colectii Clasa BitArray Descriere tablou compact de biti

CollectionBase serveste drept clasa de baza pentru implementarea propriilor clase cu functionalitate de colectie Hashtable Queue SortedList colectie de perechi de tip cheie-valoare, organizate dupa codul hash al cheii gestioneaza un grup de obiecte dupa principiul first-in, first-out contine un grup de obiecte care pot fi accesate dupa index sau dupa valoare
Dezvoltare de aplicatii in Visual Studio .NET

Page 24 of 180

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

Stack

gestioneaza un grup de obiecte dupa principiul last-in, first-out

Enumerarea (iterarea) membrilor unui tablou sau colectii C# ofera o sintaxa specializata pentru parcurgerea membrilor unui tablou sau colectii. Utilizarea cuvantului rezervat foreach permite examinarea succesiva a fiecarui membru al unui tablou sau colectii. Exemplu: int[] tab = new int[] {1,2,3,4}; foreach (int i in tab) { MessageBox.Show(i.ToString()); } ! Pentru o utilizare corecta a acestei sintaxe, toti membrii colectiei trebuie sa aiba acelasi tip ca al variabilei desemnate ca iterator (i pentru exemplul anterior). In caz contrat, va fi aruncata o exceptie de tipul InvalidCastException. Solutie:declararea variabilei iterator de tip object si utilizarea metodei GetType pentru a inspecta tipul obiectelor din colectie. Exemplu: // lista este un ArrayList care contine // elemente de tip string si alte elemente de alte tipuri foreach (object o in lista) { if (o.GetType() == typeof(string)) { MessageBox.Show(o.ToString()); } } ! Referinta continuta de iterator este read-only . Deci, sintaxa foreach nu poate fi folosita pentru a efectua modificari ale tabloului sau colectiei. Solutie: folosirea unei sintaxe de tip for. Exemplu: int[] tab = new int[] {1,2,3,4}; for (int i = 0; i <= 3; i++) { tab[i] ++; // modificare permisa MessageBox.Show(tab[i].ToString());

Dezvoltare de aplicatii in Visual Studio .NET

Page 25 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

} Sinteza: Constante si enumerari (const si enum). Constantele pot avea orice tip. Enumerarile trebuie sa aiba un tip numeric integral. Tablourile pot fi unidimensionale sau multidimensionale. Tablourile multidimensionale pot fi rectangulare sau jagged. Colectiile permit gestionarea grupurilor de obiecte, cu elemente de acelasi tip sau de tipuri diferite System.Collections ofera un set de clase cu diverse functionalitati specifice colectiilor. Sintaxa foreach permite iterarea membrilor unui tablou sau colectii, fara a putea modifica membrii. Pentru a altera valorile membrilor unui tablou sau colectii, se poate utiliza o sintaxa de tip for.

3.Proprietati
Proprietatile sunt membri ai claselor care acceseaza variabile sau obiecte membre. Prezinta similaritati atat fata de campuri cat si fata de metode. Valorile sunt setate si inspectate folosind acceasi sintaxa ca in cazul campurilor: de fapt aceste doua operatii sunt efectuate prin apelul unor metode specializate. Proprietatile pot contine cod care valideaza valorile inainte de a le seta sau pot indeplini alte functionalitati impuse de aplicatie. Implementarea proprietatilor Proprietatile permit accesarea valorilor si obiectelor membre intr-o maniera mai robusta in comparatie cu inspectarea obisnuita a campurilor. O proprietate este o metoda specializata care se comporta ca un camp. Valorile proprietatilor sunt setate si inspectate in acelasi mod ca si campurile, fapt ilustrat in exemplul urmator: // seteaza proprietatea Text a butonului btnExit la "Exit" btnExit.Text = "Exit"; string sir; // returneaza valoarea lui btnExit.Text si o asigneaza string-ului sir sir = btnExit.Text; Pana acum, o proprietate arata si se comporta exact ca un camp. Totusi, codul asociat unei proprietati este mai complex. Exista doua metode speciale asociate unei proprietati, un getter si un setter care returneaza si, respectiv, seteaza valoarea proprietatii. Exemplu: // proprietatea se numeste Xmax // o variabila locala care stocheaza valoarea
Page 26 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

private short x; // implementarea proprietatii public short Xmax { get { // returneaza valoarea variabilei locale return x; } set { // seteaza valoarea variabilei locale x = value; } } Cuvantul rezervat value este un cuvant special folosit in setter-ul unei proprietati si reprezinta intotdeauna valoarea la care se seteaza proprietatea. Proprietatile pot expune orice tip de obiecte, tablouri sau colectii. Valoarea sau obiectul este retinut, de obicei, intr-o variabila privata locala (x in exemplul anterior) si returnat atunci cand proprietatea este accesata. Exemplul anterior poate fi considerat trivial, proprietatea Xmax comportandu-se exact ca un camp. Avantajul utilizarii proprietatilor consta in posibilitatea scrierii de cod aditional in getter-ul si setter-ul proprietatii pentru a efectua calcule sau valida input-ul. Exemplu: private short x; public short Xmax { get { // returneaza valoarea variabilei locale return x; } set { // valoarea nu trebuie sa depaseasca 500

Dezvoltare de aplicatii in Visual Studio .NET

Page 27 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

if (value > 500) MessageBox.Show(value.ToString() + " coordonata x nu poate depasi valoarea 500!"); else x = value; } } De retinut: Pentru a crea o proprietate: creati o variabila private care va retine valoarea sau obiectul returnat de proprietate scrieti cod pentru returnarea valorii proprietatii adaugati cod pentru validare sau efectuare de calcule in getter-ul si setter-ul proprietatii

Proprietati read-only si write-only Uneori, programatorul trebuie sa implementeze a proprietate care returneaza o valoare care nu mai poate fi modificata dupa ce clasa a fost initializata. Alteori, intr-un numar mai mic de situatii, se impune crearea unei proprietati a carei valori poate fi modificata dar nu poate fi citita. Aceste proprietati sunt numite read-only si, respectiv, write-only . Proprietati read-only Crearea unei proprietati read-only se realizeaza prin specificarea getter-ului si omitand setter-ul proprietatii. Variabila private care retine valoarea proprietatii este declarata, in cele mai multe cazuri, readonly (acest lucru nu este obligatoriu). Exemplu: private readonly short s; public short ShortNumber { get { return s; } } ! Setarea variabilei care retine valoarea proprietatii trebuie efectuata in constructorul clasei sau la initializarea variabilei. Proprietati write-only

Page 28 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

In anumite cazuri, pot fi implementate proprietati care pot fi scrise, dar nu citite, de catre utilizator. Exemplu: pozitionarea unei forme in functie de setari specifice localizarii(culturii) aplicatiei (localizare - concept care va fi prezentat intr-un curs viitor) forma este pozitionata la lansarea aplicatiei, nefiind necesara consultarea valorii proprietatii respective. O proprietate write-only se creaza similar unei proprietati obisnuite, omitand implementarea getter-ului. Exemplu: private short x; public short XmaxForm { set { x = value; } } Indexatori Sunt proprietati specializate care permit accesarea obiectelor dintr-un grup. Numele proprietatii este this, asociindu-se o variabila index. Exemplu: // tablou care va stoca valorile retunate de indexator private int[] tablou; // sintaxa speciala care foloseste cuvantul rezervat this public int this[int index] // indexatorul { get { return tablou[index]; } set { tablou[index] = value; } } Proprietati ale colectiilor Expunerea colectiilor de obiecte ca proprietati permite controlarea accesului la obiectele componente si validarea operatiilor de atribuire. Modalitatile de implementare
Dezvoltare de aplicatii in Visual Studio .NET Page 29 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

a acestor proprietati depind de natura si functionalitatea colectiilor. O proprietate simpla este cea care returneaza colectia ca un singur obiect (pentru siguranta, proprietatea poate fi definita read-only, utilizatorul putand modifica obiectele din colectie folosind metodele colectiei). Exemplu: private readonly System.Collections.ArrayList list_objects = new System.Collections.ArrayList(); public System.Collections.ArrayList objects { get { return list_objects; } } ! Aceasta abordare nu rezolva problema verificarii tipurilor obiectelor adaugate la colectie (membrii colectiei sunt tratati ca obiecte). Iata o abordare mai pertinenta: private System.Collections.ArrayList list_objects = new System.Collections.ArrayList(); public ItemType GetItem(int i) { return (ItemType)list_objects[i]; } public void SetItem(int i, ItemType item) { list_objects[i] = item; } Acesta abordare rezolva problema tipizarii, insa rapeste utilizatorului posibilitatea folosirii sintaxei foreach pentru parcurgerea elementelor colectiei. ! Pentru implementarea unor proprietati care implica functionalitati avansate se pot defini colectii derivate din System.Collections.CollectionBase. Sinteza:

Page 30 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

proprietati - expun variabile sau obiecte membre, furnizand cod pentru validarea valorilor proprietatilor sau pentru efectuarea altor operatii; proprietatile read-only si write-only limiteaza abilitatea utilizatorului de a consulta sau modifica propriatatile. indexator - proprietate implicita a colectiilor proprietatile colectiilor expun grupuri de obiecte din colectie

4.Delegati.Evenimente
Evenimentele reprezinta o notificare determinata de o actiune survenita intr-o sectiune a a aplicatiei. La aparitia unui eveniment, altor parti ale aplicatiei li se ofera oportunitatea de a raspunde prin executis unor metode denumite event handler-e. Delegati Un delegat este, in principiu, un pointer type-safe la o functie care permite transmiterea unei referinte catre inceputul unei metode si apelarea metodei fara a folosi un apel explicit al acesteia. La declararea unui delegat, se specifica signatura metodei apelate si tipul returnat Exemplu: public delegate int firstDelegate(double D); // acest delegat poate invoca metode care returneaza un int // si primesc un parametru double // metoda care va fi invocata de delegat public int ReturnInt(double D) { // implementare... } // declararea unei instante firstDelegate si // invocarea metodei ReturnInt public void invokeMethod() { firstDelegate aDelegate = new firstDelegate(ReturnInt); } Invocarea metodei pentru care s-a instantiat delegatul: aDelegate(6780.87); Declararea si folosirea unui delegat:
Dezvoltare de aplicatii in Visual Studio .NET Page 31 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

se declara delegatul, specificand o signatura identica cu cea a metodelor care vor fi invocate se creaza o instanta a delegatului care pointeaza catre o metoda care are signatura adecvata se apeleaza delegatul prin folosirea numelui sau, cu parametrii corespunzatori Declararea si lansarea evenimentelor Declaring and Raising Events Formele si controalele au evenimente membre implicite (built-in) care sunt apelate cand au loc anumite actiuni in program. De exemplu, evenimentul Click este lansat la executia unui clic asupra butonului. In C#, pentru declararea unui eveniment, trebuie desemnat explicit tipul delegatului care va fi utilizat de eveniment: public delegate void calculationDelegate(double d); public event calculationDelegate CalculationComplete; Odata declarat, evenimentul poate fi lansat in code atunci cand actiunile care vor fi notificate de eveniment au loc. De exemplu, o componenta BankAccount lanseaza un eveniment Closing atunci cand se doreste inchiderea contului. //Evenimentle in C# sunt lansate prin nume, //ca in cazul unei metode CalculationComplete(100000); Tratarea evenimentelor Dupa declarare, evenimentul trebuie asociat unuia sau mai multor event handlere inainte de a putea fi lansat raised. Un event handler este o metoda apelata prin intermediul unui delegat atunci cand un eveniment este lansat. Lansarea unui eveniment care nu are asociat nici un event handler provoaca aparitia unei erori. Event Handlere Un event handler C# poate returna o valoare care poate fi asignata unei variabile la fel ca in cazul unui apel de functie. Asocierea unei metode cu un eveniment se realizeaza prin crearea unei instante a unui delegat adecvat evenimentului care specifica, la randul sau, metoda pe care o invoca; se utilizeaza operatorul += pentru realizarea asocierii. Se pot crea, de aemenea, asocieri intre un evenimentt si o instanta a unui delegat existent. De exemplu: // metoda DisplayResults are signatura adecvata // delegatului CalculationDelegate // se creaza un nou delegat pentru a crea asocierea Account.CalculationComplete += new calculationDelegate(DisplayResults); // asociere cu un delegat existent

Page 32 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

calculationDelegate calc = new calculationDelegate(DisplayResults); Account.CalculationComplete += calc; Pentru evenimentele asociate controalelor si claselor .NET Framework exista delegati impliciti; acestia nu mai trebuie declarati ci doar instantiati. Exemplu: clasa System.EventHandler este clasa cea mai utilizata pentru instantierea delegatilor. button1.Click += new System.EventHandler(clickHandler); Sinteza: pentru a trata un eveniment in C#, se creaza o instanta a delegatului care va fi asociat evenimentului care va fi tratat si se foloseste operatorul += pentru a realiza asocierea eveniment - delegat. Event-handlere care trateaza mai multe evenimente // ClickHandler este o metoda cu signatura adecvata // tratarii evenimentului clic pentru un buton button1.Click += new System.EventHandler(ClickHandler); button2.Click += new System.EventHandler(ClickHandler); Evenimente cu handlere multiple Un eveniment poate fi tratat de mai multe handlere,acestea apelandu-se in ordinea asocierii (operatorul +=), valoarea returnata de eveniment avand tipul returnat de ultimul event handler asociat. Eliminarea handlerelor la run-time // se foloseste operatorul -= Account.CalculationComplete -= new calculationDelegate(DisplayResults); Sinteza evenimentele sunt membri ai claselor utilizati pentru notificarea anumitor actiuni din program. O instanta a unei clase poate lansa un eveniment membru pentru a transmite notificarea. Evenimentul poate fi trata de metode desemnate drept event handlere. Aceste metode sunt executate la lansarea evenimentului. delegatii implementeaza functionalitatea de la baza evenimentelor. Un delegat este pointer la o functie, puternic tipizat. Poate invoca o metoda fara apelul explicit al acesteia. Asocierile dintre evenimente si event handlere implica utilizarea explicita a delegatilor. Evenimentele sunt declarate ca membri ai clasei; pot fi apelati prin nume si pot returna o valoare.
Dezvoltare de aplicatii in Visual Studio .NET Page 33 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Metodele care trateaza evenimentele se numesc event handlere. Un event handler se creaza prin utilizarea operatorului += pentru a asocia un delegat evenimentului. Evenimentele pot avea mai multe event handlere asociat, si evenimente multiple pot fi tratate de acceasi metoda. Event handlerele pot fi eliminate dinamic (folosind operatorul -=).

Page 34 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

Section 4. Gestionarea interfetelor utilizator - III 1. Design-ul interfetelor utilizator - principii


Descrierea importantei interfetei utilizator Rolul formelor, controalelor si a meniurilor in cadrul interfetelor Importanta compozitiei si culorilor interfetei utilizator Explicarea folosirii imaginilor, icon-urilor si a fonturilor in design-ul interactiv

Forme, controale, meniuri formele contin informatii si optiuni utile utilizatorilor fiecare forma este o clasa, pentru care se pot crea instante sau care poate fi folosita drept clasa de baza controalele fac informatiile accesibile utilizatorilor meniurile si tool box-urile furnizeaza o modalitate structurata de a expune comenzile disponibile utilizatorilor aplicatiei Aspectul si functionalitatea aplicatiei simplitate pozitionarea controalelor consistenta: organizare, culori, forme, marime, tipuri, imagini, transparenta estetica

Sinteza: o interfata consistenta din punct de vedere vizual si logic este mult mai usor de inteles si folosit elementele de baza ale unei interfete sunt formele, controalele si meniurile o interfata reusita trebuie sa tina cont de cateva aspecte legate de look si feel sunt importante deciziile de design adresate direct utilizatorilor - semnificatii culturale, consistenta, simplitate

2. Forme
Formele reprezinta unitatea fundamentala a unei interfete utilizator. Ele furnizeaza o platforma pe care se plaseaza controalele si permit prezentarea aplicatiei intr-o maniera consistenta si atractiva. Formele afiseaza date si primesc input-ul utilizatorilor. De obicei, aplicatiile contin una sau mai multe forme, organizate astfel incat sa urmeze logica fireasca a aplicatiei. stabilirea rolului unei forme intr-o aplicatie
Dezvoltare de aplicatii in Visual Studio .NET Page 35 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

adaugarea de forme la o aplicatie setarea formei de start si a locatiei de start setarea aspectului vizual al unei forme folosirea metodelor unei forme evenimente din cadrul metodelor

Adaugarea de forme la o aplicatie La crearea unui proiect de tip Windows Forms, se creaza si se adauga la proiect o forma initiala, denumita Form1. Form1 nu reprezinta, efectiv, o instanta a unei forme, ci mai degraba o clasa care contine codul asociat unei instante a formei. Designer-ul este o reprezentare grafica a componentei(de obicei o forma) supuse design-ului, conferind posibilitatea adaugarii de controale, meniuri sau alte elemente vizuale formei respective. Odata cu cresterea in dimensiuni a aplicatiei, se creaza noi forme sau alte componente necesare bunei functionalitati a aplicatiei. Adaugarea unei noi forme la un proiect: 1. Meniul Project, Add Windows Form. Se deschide astfel fereastra de dialog Add New Item. 2. Se selecteaza Windows Form, apoi Open. Este adaugata o noua forma mediului de lucru. Adaugarea unei forme la o aplicatie la run time: Se declara si se instantiaza o variabila reprezentand forma in aceeasi maniera ca in cazul oricarei alte clase. De exemplu: // se presupune existenta unei forme de tip DialogForm DialogForm forma1; forma1 = new DialogForm(); Mostenirea vizuala Mostenirea vizuala este un concept care se refera la crearea de noi forme, pornind de la forme existente, in ideea de a pastra caracteristicile vizuale si functionalitatea claselor de baza. Aceasta tehnica permite crearea unei forme care incorporeaza toti membrii, controalele, meniurile si codul asociat unei forme existente, folosind aceasta forma ca baza pentru implementarea unei functionalitati aditionale. Pentru a crea relatii de mostenire se poate folosi fie Inheritance Picker-ul, fie varianta scrierii de cod. Crearea unei forme derivate folosind Inheritance Picker-ul: 1. Meniul Projects, Add Inherited Form. Se deschide fereastra de dialog Add New Item. 2. In panoul din stanga se selecteaza Local Project Item, in dreapta, Inherited Form. Se introduce un nume si se deschide Inheritance Picker-ul. 3. Acesta afiseaza formele existente in proiect, dintre acestea selectandu-se forma de
Page 36 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

baza care va fi asociata noii forme create. ! se poate alege drept clasa de baza si o forma dinafara proiectului, aceasta trebuie sa fie compilata intr-un fisier EXE sau DLL. Crearea unei forme derivate folosind scrierea de cod: 1. Meniul Projects, Add Windows Form. 2. Se modifica, in codul asociat clasei create, declaratia clasei. // FormaBaza este o clasa existenta, // asociata unei forme din proiect public class FormaDerivata : FormaBaza { // implementare } ! Proiectul trebuie sa aiba acces la forma din care de deriveaza. Deci, el trebuie sa includa fie o referinta la assembly-ul care contine forma parinte (in acest exemplu, MainForm) sau forma trebuie sa fie inclusa in proiect. Setarea formei de start Daca o aplicatie de tip Windows Forms contine mai multe forme, trebuie sa se desemneze o forma de start. Forma de start va fi prima forma incarcata la executia aplicatiei. public static void Main() { Application.Run(new myForm()); } Setarea formei de start in Visual C# se face utilizand meniul Projects, optiunea Properties, apoi la StartupObject se alege forma dorita. Setarea pozitiei de start Proprietatea StartPosition se poate utiliza pentru a determina pozitia pe ecran la care se va afisa prima data o forma. Aceasta poate fi setata la oricare dintre valorile enumerarii FormStartPosition. Valori FormStartPosition Valoare Manual Efect Forma se deschide la pozitia determinata de proprietatea Location.

Dezvoltare de aplicatii in Visual Studio .NET

Page 37 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

CenterScreen

Forma se deschide centrat pe ecran.

WindowsDefaultLocation Forma se deschide la locatia implicita determinata de setarile Windows. WindowsDefaultBounds CenterParent Forma se deschide la pozitia Windows implicita, avand marimea stabilita prin setari Windows implicite. Forma se deschide centrat, peste forma parinte.

Modificarea aspectului unei forme Aspectul unei interfete este o parte importanta a unei aplicatii. Utilizarea proprietatilor atasate diferitelor obiecte permite utilizatorului modificarea aspectului formelor. Modificarea proprietatilor: - din fereastra Properties - modificand codul, la run-time // schimbarea culorii unei forme form1 form1.BackColor = System.Drawing.Color.Red; Proprietatile BackColor, ForeColor si Text Text - stabileste titlul formei. BackColor si ForeColor - reprezinta culorile atasate unei forme. ForeColor este culoarea textului in foreground. BackColor reprezinta culoarea de background a formei. Alte proprietati:

Valoare Font Cursor BackGroundImage Opacity

Efect specifica fontul folosit in cadrul formei specifica icon-ul care apare atunci cand sageata mouse-uluise afla deasupra formei permite setarea unei imagini de background

- variaza gradul de transparenta al formei - valori posibile: in intervalul [0, 1]. Valoarea 1 indica faptul ca forma este complet opaca, iar valoarea 0 creaza o forma complet transparenta. Valorile intermediare imprima formei o transparenta partiala. Valoarea implicita este 1 (opacitate totala). // o forma semi-transparenta
Page 38 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

MyForm.Opacity = .5; ! in fereastra Properties, Opacity este reprezentata ca o valoare procentuala. Utilizarea metodelor specifice formelor Orice forma incapsuleaza un set functional de baza mostenit din clasa System.Windows.Forms.Form. Printre acestea, se gasesc metodele responsabile de modul de afisare si de posibilitatile de accesare a formei in mediul de lucru. Cele mai importante astfel de metode: Form.Show Form.ShowDialog Form.Activate Form.Hide Form.Close

Folosirea acestor metode implica existenta unei instante a formei in memorie. Pe langa instantele formelor create in cod, aplicatia creaza o instanta a formei de start-up in lansarii in executie a programului. Show si ShowDialog - Form.Show - face forma vizibila (se incarca in memorie o instanta a formei, se afiseaza forma pe ecran si primeste focus-ul aplicatiei). Proprietatea Visible este setata la true dupa apelul lui Form.Show. Pentru o forma incarcata in memorie dar care este invizibila (de exemplu, daca proprietatea Visible a fost setata la valoarea false), apelul Form.Show are acelasi efect ca setarea proprietatii Visible la valoarea true. - Form.ShowDialog - in plus, afiseaza forma modal, adica forma trebuie inchisa inainte ca orice alta forma sa poata primi focus-ul. // DialogForm este o clasa asociata unei forme existente DialogForm myForm = new DialogForm(); //afiseaza fereastra in mod uzual myForm.Show(); //afizeaza fereastra modal myForm.ShowDialog(); Activate pentru o forma vizibila, dar care nu a primit inca focus-ul, se poate utiliza metoda Form.Activate apelata in cadrul aplicatiei active, metoda Form.Activate aduce forma in prim-planul aplicatiei si ii transmite focus-ul apelarea metodei pentru o aplicatie inactiva in interfata utilizator, determina clipirea titlului aplicatiei din taskbar forma pentru care se apeleaza metoda Activate trebuie sa fie vizibila pentru a obtine
Dezvoltare de aplicatii in Visual Studio .NET Page 39 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

efectul scontat. //apelarea metodei Activate myForm.Activate(); Hide face forma invizibila. Desi forma persista in memorie, nu va mai fi vizibila pana la apelarea metodei Form.Show sau la setarea proprietatii Visible la valoarea true in cod apelul metodei Hide seteaza proprietatea Visible la false //apelarea metodei Hide myForm.Hide(); Close Form.Close inchide forma si o elimina din memorie se inchid toate resursele continute de forma, fiind preluate de garbage collector apelul Form.Close anuleaza efectul unui apel ulterior Form.Show, pentru ca resursele asociate formei nu mai sunt disponibile apelul Form.Close pentru forma de start-up determina inchidera aplicatiei //apelarea metodei Close myForm.Close(); Evenimente din cadrul formelor Eveniment - o notificare survenita in urma unei actiuni din program. Aplicatia lanseaza evenimentul, iar o alta componenta a aplicatiei are oportunitatea de a trata evenimentul respectiv. Fiecare dintre metodele prezentate anterior lanseaza unul sau mai multe evenimente; utilizatorul are oportunitatea de a trata prin cod aceste evenimente. Exemple: apelul metodei Form.Hide determina lansarea evenimentelor Deactivate si VisibleChanged. Un event handler este o metoda care se executa ca raspuns la lansarea unui eveniment. Crearea unui event handler pentru o forma, un control sau o componenta 1. Design view - selectarea formei sau a controlului pentru care se va crea event handler-ul 2. Properties - butonul Events 3. din lista evenimentelor disponibile se selecteaza evenimentul dorit 4. se scrie codul pentru event handler

Argumentele event handler-elor Exemplu:


Page 40 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

private void Form1_Load(object sender, System.EventArgs e) { // codul metodei } Argumente: - sender - obiect care contine o referinta catre obiectul care a lansat evenimentul - e - instanta a clasei EventArgs; poate contine informatii suplimentare ! se poate obtine o referinta catre tipul care a cauzat lansarea evenimentului, in cazul in care se cunoaste acest tip, printr-o conversie explicita a argumentului sender la tipul respectiv. De exemplu: Form1 myForm; myForm = (Form1)sender; In multe cazuri, parametrul EventArgs nu contine informatii folositoare din punct de vedere programatic. Exista insa situatii in care acest parametru furnizeaza informatii utile. Evenimente referitoare la durata de viata a unei forme Load Activated/Deactivate VisibleChanged Closing Closed

Load Se lanseaza in momentul in care o forma este incarcata pentru prima oara in program la primul apel al metodelor Form.Show sau Form.ShowDialog. Exemplu: Form myForm = new Form(); myForm.Show(); // se lanseaza Load myForm.Hide(); // forma este acum invizibila myForm.Show(); // nu se mai lanseaza Load myForm.Close(); // se inchide forma
Dezvoltare de aplicatii in Visual Studio .NET Page 41 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

myForm.Show(); // exceptie, forma nu mai este disponibila ! evenimentul Load este lansat o singura data pe parcursul duratei de viata a unei instante a formei Activated/Deactivate Evenimentul Activated poate aparea in urmatoarele situatii (la primirea focus-ului): - la apelul metodelor Form.Show, Form.ShowDialog, Form.Activate - cand o forma este adusa in prim-planul aplicatiei Deactivate apare ori de cite ori o forma pierde focus-ul: - prin interactiunea utilizatorului cu interfata - la apelul metodelor Form.Hide sau Form.Close (Form.Close lanseaza evenimentul numai daca forma este activa) ! Activated si Deactivate sunt lansate numai cand focus-ul este modificat prin intermediul programului. VisibleChanged - lansat la schimbarea proprietatii visible a formei la apelul metodelor Form.Show, Form.ShowDialog, Form.Hide si Form.Close. Closing - lansat in cazul in care forma este in curs de inchidere, dar nu este inca inchisa complet - prin apelul Form.Close sau prin apasarea butonului Close al formei - signatura event handler-ului asociat evenimentului Closing include o instansa a clasei CancelEventArgs //anularea inchiderii formei private void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e) { e.Cancel = true; } Closed - se lanseaza dupa ce forma a fost inchisa prin apelul Form.Close sau prin inchiderea manuala a formei; apare dupa lansarea evenimentului Closing si dupa executia tuturor handler-elor asociate acestuia Sinteza:
Page 42 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

Formele reprezinta elementul de baza al interfetei utilizator pentru un program de tip Windows Forms. Formele trebuie sa contina o interfata consistenta, completa si atractiva pentru utilizator. Proprietati care controleaza aspectul unei forme: BackColor ForeColor Text Font Cursor BackGroundImage Opacity

Metode care controleaza durata de viata si modul de afisare a formelor: Form.Show Form.ShowDialog Form.Activate Form.Hide Form.Close

Fiecare dintre aceste metode provoaca schimbari de vizualizare si lanseaza diverse evenimente. Printre acestea: Load Activated/Deactivate VisibleChanged Closing Closed

Event handler-e: metode specializate de tratare a evenimentelor.

3. Controale si componente
Controalele reprezinta al doilea element al unei interfete vizuale, in ordinea importantei, dupa forme. Functionalitate: - o parte dintre controale, cum ar fi Button sau TextBox, sunt desemnate sa accepte input-ul utilizatorului si sa execute operatii bazate pe interactiunea utilizatorului - o alta parte constituie componente specializate, proiectate sa execute interactiuni complexe cu alte parti ale aplicatiei.

Dezvoltare de aplicatii in Visual Studio .NET

Page 43 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Asemanari si deosebiri intre controale si componente: ambele reprezinta unitati de cod care incapsuleaza functionalitati specifice; controalele au o reprezentare vizuala, in timp ce componentele nu. Controale Adaugarea unui control intr-o aplicatie Windows Forms 1. din Toolbox, se selecteaza controlul dorit. 2. se adauga controlul pe forma 3. eventual, se face o repozitionare a controlului. Setarea proprietatilor unui (sau mai multor) control (controale) se realizeaza, cel mai frecvent, folosind fereastra Properties Adaugarea de componente la o forma se face in acelasi mod, la fel si setarea proprietatilor. Diferenta principala fata de controale este aceea ca nu exista o reprezentare vizuala a componentelor, odata cu adaugarea lor la o forma. Control Tab Order Utilizatorii pot folosi tasta Tab pentru a transmite focus-ul de la un control la altul, intr-o anumita ordine - tab order. Aceasta ordine este specificata de proprietatea TabIndex. Pentru a stabili ordinea de primire a focus-ului, se seteaza proprietatea TabIndex a fiecarui control la o anumita valoare. Focus-ul va fi transmis controalelor in ordinea crescatoare a valorilor proprietatii TabIndex. Visual Studio ofera a alta modalitate de setare a ordinii de primire a focus-ului. Din meniul View, se alege Tab Order. In designer, apare o casuta in interiorul fiecarui control care, prin clic ofera posibilitatea setarii unui numar. Ordinea de primire a focus-ului este ordinea data de valorile numerelor respective. ! Exista controale care nu pot primi focus-ul, deci nu au asociata proprietatea TabIndex. Controale container Sunt controale speciale care ofera posibilitatea gruparii mai multor butoane in interiorul lor. Exemple de astfel de controale - Panel, GroupBox si TabControl. Pot fi folosite pentru organizarea logica a grupurilor de controale pe o forma. De exemplu, se pot grupa un set de butoane radio intr-un control GroupBox. ! - Un control container se comporta ca o gazda pentru alte controale, dar este independent de aceste controale. In contrast, exista controale utilizator care pot lega controale multiple intr-o singura unitate interdependenta. - Modificarea proprietatilor unui control container poate afecta proprietatile controalelor continute. De exemplu, daca proprietatea Enabled a unui GroupBox este setata la false, toate controalele continute devin disabled. La fel se intampla in cazul proprietatilor BackColor, ForeColor, Visible si Font. Acesta confera un aspect
Page 44 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

consistent interfetei utilizator. GroupBox si Panel Sunt doua controale container similare. Amandoua furnizeaza posibilitatea gruparii logice si fizice a controalelor. Pot fi proiectate ca subdiviziuni ale formei. Modificarea proprietatilor unui control Panel sau GroupBox afecteaza toate controalele continute. Acestea pot fi grupate sau repozitionate ca o singura unitate in momentul design-ului. La run-time, tot grupul poate fi dezactivat prin setarea proprietatii Enabled la false. GroupBox - furnizeaza un titlu care va fi asociat intregului grup controale (modificand proprietatea Text). Panel - un container cu scroll, nu contine o proprietate text. Setand proprietatea AutoScroll la true, se ataseaza bare de defilare panel-ului respectiv. TabControl - grupeaza controalele intr-o multime de tabele (similar diviziunilor dintr-o carte de adrese) - un TabControl este un suport pentru un anumit numar de TabPages, care gazduiesc alte controale - exemplu de TabControl: pagini de proprietati pentru o aplicatie, fiecare tab continand proprietati referitoare la o componenta specifica aplicatiei. Proprietatea TabPages reprezinta o colectie de controale TabPage, fiecare cu setul sau de proprietati. O pagina din TabPages este similara controlului de tip Panel. Adaugarea de TabPages se efectueaza prin utilizarea proprietatii TabPages. Docking si Anchoring Reprezinta proprietati ale controalelor care dicteaza comportamentul acestora in cadrul formei sau controlului parinte. - Anchor permite definirea unei distante constante intre un control si una sau mai multe margini ale formei - redimensionarea unei forme la run time implica pastrarea unei distante specifice fata de marginile indicate - valori posibile : o enumerare care poate contine, separate prin virgule una sau mai multe dintre valorile Top, Left, Right, Bottom - Dock permite atasarea controlului la una din marginile formei sau ocuparea completa a formei de catre control - redimensionarea formei atrage dupa sine redimensionarea controlului - setarea valorii se face prin folosirea micro-interfetei grafice puse la dispozitie in fereastra Properties Utilizarea colectiei Controls Fiecare container, incluzand formele, expun o colectie a tuturor controalelor continute controls collection. Colectia expune o proprietate Count care returneaza numarul de
Dezvoltare de aplicatii in Visual Studio .NET Page 45 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

elemente continute si o proprietate Item care returneaza un element specific. Exista, de asemene, metode care pot fi folosite pentru a adauga si elimina controale in cadrul colectiei. // se presupune existenta unei instante myForm Control aControl; aControl = myForm.Controls[3]; Label aLabel = new Label(); aLabel.Text = "Eticheta adaugata dinamic"; myForm.Controls.Add(aLabel); myForm.Controls.Remove(Button1); myForm.Controls.RemoveAt(3); //sintaxa similara pentru adaugare si eliminare //de controale intr-un //Panel, GroupBox sau TabPage control // myTabControl este o instanta a unui TabControl Button aButton = new Button(); // se adauga un buton paginii cu indexul 1 in colectia // TabPages din TabControl myTabControl.TabPages[1].Controls.Add(aButton); Adaugarea de noi controale in Toolbox 1. Se alege colectia din Toolbox la care se doreste adaugarea controlului. 2. Right-click in interiorul colectiei selectate - Customize Toolbox. ! se pot adauga componente .NET sau componente COM existente 3. se poate selecta un control deja inregistrat in lista sau se foloseste Browse pentru a localiza un control inca neinregistrat. ! un control care se doreste adaugat trebuie recunoscut ca ActiveX Control, componenta COM sau assembly .NET Event Handlere pentru controale Fiecare control poate lansa o varietate de evenimente care corespund interactiunii cu utilizatorul. Un exemplu este apasarea unui buton care determina lansarea unui eveniment, cautandu-se apoi eventualele handlere; handlerele gasite sunt lansate in executie. Fiecare control are un eveniment implicit asociat, care reprezinta evenimentul lansat cel mai frecvent. De exemplu, evenimentul implicit pentru Button este Click, iar

Page 46 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

pentru Checkbox, CheckChanged. Crearea unui event handler pentru evenimentul implicit al unui control: 1. In designer, se executa dublu clic pe control. Se deschide fereastra de cod asociata event handler-ului. 2. Se scrie codul dorit pentru event handler. Pentru a crea un event handler pentru un eveniment in general: 1. Design view - se selecteaza controlul. 2. Din fereastra Properties - se selecteaza Events. Se afiseaza lista evenimentelor disponibile pentru controlul respectiv. 3. Se alege evenimentul dorit, apoi se scrie codul. 4. Daca exista deja event handler-e implementate pentru controlul respectiv, se poate alege una din metodele existente, din drop-down-ul asociat evenimentului respectiv. Interactiunea cu mouse-ul Tabel - Evenimente determinate de interactiunea cu mouse-ul Eveniment MouseEnter MouseMove MouseHover MouseDown MouseWheel MouseUp MouseLeave Descriere mouse-ul intra in zona controlului mouse-ul se misca pe suprafata controlului mouse-ul se afla pe suprafata controlului mouse-ul se afla pe suprafata controlului si se apasa un buton miscarea rotitei de scroll a mouse-ului atunci cand controlul are focus-ul mouse-ul se afla pe suprafata controlului si se elibereaza un buton mouse-ul paraseste suprafata controlului Tip EventArgs System.EventArgs System.MouseEventArgs System.EventArgs System.MouseEventArgs System.MouseEventArgs System.MouseEventArgs System.EventArgs

MouseEnter, MouseHover si MouseLeave semnaleaza faptul ca mouse-ul se afla in regiunea unui control, transmitand putine informatii event handler-elor. MouseMove, MouseDown, MouseWheel si MouseUp, din contra, pot fi folosite pentru a implementa interactiuni substantiale intre utilizator si interfata. Un obiect de tip MouseEventArgs contine informatii despre starea si locatia mouse-ului. Tabel - proprietati MouseEventArgs Proprietate Descriere
Page 47 of 180

Dezvoltare de aplicatii in Visual Studio .NET

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Button Clicks Delta

specifica daca s-a apasat un buton al mouse-ului returneaza numarul de click-uri efectuate Returneaza numarul de actionari asupra rotitei mouse-ului; acesta poate fi pozitiv sau negativ - deplasare inainte sau inapoi; fiecare astfel de deplasare adauga sau scade 120 din valoarea anterioara returneaza coordonta x a mouse-ului in momentul executiei unui click returneaza coordonta y a mouse-ului in momentul executiei unui click

x y

Componente de tip Extender Provider Furnizeaza proprietati aditionale controalelor. Exemplu: ToolTipProvider. La adaugarea unei instante a unui ToolTipProvider pe o forma, fiecare control de pe forma capata o noua propritate - ToolTip on n, unde n este numele ToolTipProvider-ului. La run-time, valoara acestei proprietati este afisata intr-o casuta galbena atunci cand mouse-ul se afla deasupra controlului. ToolTipProvider - specifica mesaje gen tips la run time HelpProvider - specifica mesaje de help ErrorProvider - specifica mesaje de eroare Un ToolTipProvider detine metode denumite Getn si Setn, unde n este numele proprietatii. Exemple: GetToolTip si SetToolTip, care pot fi folosite dinamic in cod pentru a modifica mesajele asociate controlului respectiv (! la run time, aceste modificari nu pot fi facute prin intermediul controlului). // preluarea ToolTip-ului asociat unui Button button1 string myToolTip; myToolTip = toolTip1.GetToolTip(button1); //setarea ToolTip-ului unui Button button1 toolTip1.SetToolTip(button1, "Click for help"); Sinteza Ordinea de primire a focus-ului poate fi setata folosind proprietatea TabIndex sau selectand Tab Order din meniul View si executand click pe controale in ordinea dorita. Controale container: - Panel - GroupBox

Page 48 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

- TabPage Proprietatile Dock si Anchor implementeaza redimensionarea automata controalelor pe o forma. Colectia de controale asociate unei forme permite adaugarea dinamica de controale la run time. Adaugarea de noi controale la un ToolBox se face prin selectarea din lista controalelor inregistrate in sistem a controlului dorit sau prin selectarea fisierului .dll sau .exe asociat, din file system. Event handlere - metode care trateaza evenimentele Componente de tip Extender Provider - furnizeaza informatii aditionale pentru controalele de pe o forma.

4. Meniuri
Meniurile faciliteaza accesul la sectiuni importante din cadrul aplicatiei. OBIECTIVE: * Explicarea importantei meniurilor in cadrul interfetelor * Crearea unui meniu folosind componenta MainMenu * Crearea unui meniu contextual folosind componenta ContextMenu * Activarea si dezactivarea unui menu item * Crearea de shortcut-uri pentru un menu item * Crearea unui check mark sau a unui buton radio pentru un menu item * Explicarea procedurii prin care un menu item poate deveni invizibil * Adaugarea dinamica a unui menu item la un meniu * Clonarea dinamica a unui meniu Meniurile permit utilizatorilor sa acceseze comenzi si functii intr-o maniera familiara, usor de inteles. In momentul proiectarii meniurilor, trebuie avut in vedere parcursul logic al aplicatiei, elementele componente ale meniului trebuie grupate in concordanta cu o anumita functionalitate. Crearea meniurilor folosind Design View Se foloseste componenta MainMenu. Aceasta contine si gestioneaza o colectie de controale MenuItem care reprezinta elementele vizuale ale unui meniu la run time. Componenta MainMenu

Dezvoltare de aplicatii in Visual Studio .NET

Page 49 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

* Crearea de noi meniuri si bare de meniu * Adaugarea de menu items la meniuri existente * Modificarea proprietatilor meniurilor si a elementelor meniurilor prin intermediul ferestrei Properties * Crearea de event handlere La adaugarea unei optiuni intr-un meniu, designer-ul creaza o instanta a unui obiect MenuItem. Fiecare obiect MenuItem are proprietatile si membrii sai care pot fi modificati in fereastra Properties. Doua proprietati importante: Text - contine textul afisat la run time si Name - numele asociat instantei MenuItem, prin care se va face referire in cod. Crearea de meniuri la design time - se adauga o componenta MainMenu la forma - se adauga elemente la meniu, folosind interfata grafica Separarea elementelor din meniu Un astfel de separator reprezinta o linie orizontala care delimiteaza anumite elemente ale meniului, folosta de obicei pentru a face o separarea logica a functionalitatilor din cadrul unui meniu. Se realizeaza prin tastatea caracterului - in spatiul rezervat numelui elementului de meniu respectiv. Separatorul va constitui un element de meniu. Taste de acces si taste de shortcut Sunt utile pentru facilitarea accesului la optiuni din meniuri folosind tastatura. Taste de acces Permit utilizatorilor accesarea unui meniu prin apasarea unei combinatii de taste formate din Alt si o litera. Atunci cand meniul este deschis, se poate selecta o anumita comanda folosind combinatia de taste Alt + litera de acces corecta. Exemplu : Alt + F deschide meniul File. Pentru a atribui o tasta de acces unui menu item: 1. se selecteaza in designer elementul de meniu dorit 2. se tasteata un ampersand in fata literei care va desemna accesul la optiunea respectiva Taste de shortcut Permit accesul instant la comenzi de meniu, furnizand un shortcut pentru comenzi frecvente din meniu. Combinatia de taste asociata unei comenzi poate fi formata dintr-o singura tasta, cum ar fi Delete, F2 sau Insert, sau din mai multe, de exemplu Ctrl+B, Ctrl+F3 sau Alt+Shift+S. In cazul in care proprietatea ShowShortcut a unui element de meniu este setata la true si daca exista o combinatie de taste asociata, aceasta va fi afisata in dreapta elementului de meniu in cauza. Pentru a asigna un shortcut: - se seteaza proprietatea Shortcut la combinatia de taste dorita

Page 50 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

Evenimente asociate elementelor meniurilor Metodele care trateaza evenimentele lansate de elementele meniurilor se creeaza similar altor controale. - Cel mai frecvent folosit eveniment este Click - Evenimentul Select este lansat la vizualizarea unui meniu prin click sau folosind tastele de acces. Poate fi utilizat pentru crearea unui help privitor la modul de utilizare a unei anumite comenzi. - Evenimentul Popup este lansat inainte ca un element al meniului sa fie afisat. O aplicatie utila poate fi activarea sau dezactivarea anumitor elemente de meniu la run time. Meniuri contextuale Un meniu contextual este vizualizat la apasarea butonului dreapta al mouse-ului pe forma sau controlul caruia ii este asociat. Se creaza folosind componenta ContextMenu. Se editeaza similar componentei MainMenu. Meniurile contextuale gestioneaza o colectie de controale de tip menu-item, permite folosirea tastelor shortcut, dar nu se pot defini taste de acces. Pentru a asocia un meniu contextual unei forme sau unui control, se seteaza proprietatea ContextMenu a formei sau controlului respectiv la numele meniului contextual dorit. ! un meniu contextual poate fi asociat mai multor controale, insa un singur meniu contextual poate fi asociat unui control. Modificarea meniurilor la run time In functie de anumite conditii indeplinite la run time: - se pot dezactiva anumite comenzi din meniu - se pot afisa cate un check mark sau un buton radio in dreptul unui menu item pentru a furniza informatii utilizatorului - se pot ascunde anumite elemente de meniu atunci cand utilizarea lor este inadecvata - se pot adauga elemente in meniuri la run time sau pot fi clonate sau concatenate cu alte elemente de meniu Activarea sau dezactivarea comenzilor din meniuri Se utilizeaza proprietatea Enabled. Exemplu: menuItem1.Enabled = false; Afisarea casutelor de selectare (check marks) Se utilizeaza proprietatea Checked. Exemplu:

Dezvoltare de aplicatii in Visual Studio .NET

Page 51 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

menuItem1.Checked = true; menuItem1.Checked = false; Afisarea butoanelor radio Se utilizeaza proprietatea RadioCheck. ! butoanele radio sunt folosite frecvent pentru afisarea unor optiuni exclusive Ascunderea elementelor meniurilor Se utilizeaza proprietatea Visible. menuItem1.Visible = false; ! Submeniurile continute de menu item-ul care devine invizibil vor deveni, de asemenea, invizibile si, deci, innaccesibile. Clonarea meniurilor Se refera la copierea unor elemente de meniu la run time. Clonare imseamna transmiterea tuturor informatiilor posibile obiectului caruia i se asigneaza obiectul rezultat in urma acestei operatii. Exemplu: clonarea meniului Edit (si a submeniurilor asociate) dintr-un main menu pentru a servi drept meniu contextual unui control. // fileMenuItem este un menu item existent, // myButton este un Button // declararea si instantierea unui meniu contextual

ContextMenu myContextMenu = new ContextMenu(); // se cloneaza fileMenuItem si se adauga meniului contextual myContextMenu.MenuItems.Add(fileMenuItem.CloneMenu()); // se asigneaza noul meniu contextual butonului myButton myButton.ContextMenu = myContextMenu; Concatenarea meniurilor la run time Pot fi concatenate mai multe meniuri de tip MainMenu cu meniuri contextuale, meniuri cu menu items sau mai multe menu items. Exemplu: fileMenuItem.MergeMenu(myContextMenu); Adaugarea de elemente de meniu la run-time Exista posibilitatea adaugarii de noi elemente de meniu pe durata executiei aplicatiei.
Page 52 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

De exemplu, intr-un meniu, pot fi adaugate item-uri care sa afiseze caile spre cele mai recente fisiere deschise de aplicatie. Aceste noi elemente de meniu nu vor avea event handlere asociate, dar poate fi specificata o metoda de tratare a evenimentului Click, ca argument al constructurului noului item. Aceasta metoda trebuie sa fie void si sa aiba aceeasi signatura ca celelalte event handlere. Exemplu: public void ClickHandler (object sender, System.EventArgs e) { // implementare... } Adaugarea dinamica a elementelor de meniu Exemplu: // ClickHandler este o metoda cu signatura // corespunzatoare unui event handler MenuItem myItem; myItem = new MenuItem("Item 1", new EventHandler(ClickHandler)); //se adauga item-ul creat la un alt menu item fileMenuItem.MenuItems.Add(myItem); Sinteza * meniurile permit accesul facil la comenzi din carul aplicatiei prin intermediul unei interfete usor de folosit * meniurile contextuale permit accesul la anumite comenzi in situatii contextuale * meniurile se pot modifica la run time in functie de anumite situatii

5. Validarea input-ului utilizatorilor


In multe aplicatii, utilizatorul introduce informatii necesare aplicatiei folosind interfata utilizator. Validarea datelor verifica faptul ca informatiile introduse respecta anumiti parametri impusi de aplicatie, inainte de lansarea in executie a programului. De exemplu, ar putea exista un camp in care utilizatorul introduce un cod ca parte a unei adrese. Folosind validarea, se poate verifica daca s-a introdus un anumit numar de caractere, si toate acestea sunt cifre, inainte de a continua. Validarea input-ului utilizatorilor reduce sansa aparitiei unei erori logice sau de executie si confera aplicatiei un caracter mult mai robust. OBIECTIVE: * Explicarea diferentei dintre validarea form-level si field-level
Dezvoltare de aplicatii in Visual Studio .NET Page 53 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

* Directionarea focus-ului folosind metode si evenimente asociate controalelor * Implementarea validarii form-level * Implementarea validarii field-level Validarea de tip form-level verifica datele dupa ce utilizatorul a completat toate campurile de pe o forma. Folosind validare form-level, toate campurile de pe o forma ar putea fi validate atunci cand utilizatorul a apasat butonul OK. Pe de alta parte, validarea field-level verifica daca datele introduse in fiecare camp respecta conditii specifice. De exemplu, daca un utilizator completeaza un camp asociat unui numar de telefon, validarea field-level poate verifica daca numarul este valid, inainte de a avansa la urmatorul camp. Odata cu tastarea unui caracter, anumite evenimente asociate controlului respectiv pot verifica daca s-au introdus numai cifre. Validare field-level Proprietati ale clasei TextBox Controlul TextBox este controlul cel mai frecvent folosit pentru input-ul utilizatorilor. O serie de proprietati ale acestui tip de control restrictioneaza valorile introduse la multimea de valori acceptabile. Cele mai importante proprietati de acest fel sunt: * MaxLength - limiteaza numarul de caractere care pot fi introduse * PasswordChar - ascunde input-ul la run time * ReadOnly - true sau false (permite sau nu editarea) * MultiLine - true sau false (permite sau nun introducerea datelor pe mai multe linii) Validare field-level - utilizarea evenimentelor Controalele care pot primi input de la tastatura lanseaza urmatoarele evenimente specifice: * KeyDown * KeyPress * KeyUp KeyDown si KeyUp Evenimentele KeyDown si KeyUp survin la apasarea si, respectiv, eliberarea unei taste. Controlul care detine focus-ul lanseaza evenimentul. La lansarea acestor evenimente, informatiile legate de tasta sau combinatia de taste care au fost apasate sau eliberate pot fi preluate folosind o instanta a clasei KeyEventArgs. Un event handler asociat evenimentelor KeyDown si KeyUp trebuie sa includa un parametru de tip KeyEventArgs in signatura sa. Proprietati KeyEventArgs Proprietate Alt Descriere specifica daca s-a apasat sau nu tasta Alt

Page 54 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

Control Handled KeyCode KeyData KeyValue Modifiers Shift

specifica daca s-a apasat sau nu tasta Ctrl returneaza o valoare indicand daca evenimentul a fost tratat returneaza un enum care specifica tasta apasata intoarce informatii despre tasta care a fost apasata, verificand si daca s-a apasat Alt, Ctrl sau Shift returneaza o reprezentare sub forma de intreg a proprietatii KeyData returneaza flag-uri care indica ce combinatie a tastelor Alt, Ctrl sau Shift s-a folosit specifica daca s-a apasat tasta Shift

Evenimentele KeyUp si KeyDown sunt cel mai frecvent utilizate pentru a determina daca s-a apasat Alt, Ctrl sau Shift, prin intermediul proprietatilor instantei KeyEventArgs transmisa ca parametru handler-ului. Proprietatile KeyEventArgs Alt, Control si Shift returneaza o valoare booleana care indica daca aceste taste sunt apasate - true daca tasta corespunzatoare este apasata si false daca a fost eliberata. Exemplu: se verifica daca tasta Alt este apasata private void textBox1_KeyUp(object sender, System.Windows.Forms.KeyEventArgs e) { if (e.Alt == true) MessageBox.Show("Tasta ALT este inca apasata"); } Proprietatea KeyEventArgs.KeyCode se poate utiliza pentru a examina tasta care a lansat evenimentul. Returneaza o valoare Key care reprezinta tasta care a fost apasata (evenimentul KeyDown) sau eliberata (evenimentul KeyUp). Exemplu: afisarea intr-un MessageBox a reprezentarii sub forma de string a tastei care a fost apasate. private void textBox1_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e) { MessageBox.Show(e.KeyCode.ToString()); } KeyPress Atunci cand utilizatorul apasa o tasta care are o valoare ASCII asociata, se lanseaza evenimentul KeyPress. Printre tastele care au o valoare ASCII asociata sunt incluse
Dezvoltare de aplicatii in Visual Studio .NET Page 55 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

caracterele alfabetice si numerice (a-z, A-Z, 0-9), precum si anumite taste speciale, cum ar fi Enter and Backspace. Daca o tasta sau o combinatie de taste nu genereaza o valoare ASCII, apasarea lor nu va determina aparitia evenimentului KeyPress. Exemple de astfel de taste includ Ctrl, Alt si tastele functionale. La aparitia acestui eveniment, se trasmite handler-ului ca parametru o instanta a clasei KeyPressEventArgs. Aceasta instanta contine informatii despre tasta apasata care pot fi folosite pentu validarea input-ului. Proprietatea KeyPressEventArgs.KeyChar contine caracterul ASCII reprezentat de combinatia de taste care a lansat evenimentul. KeyPressEventArgs.Handled este proprietatea care indica daca evenimentul a fost tratat. Validarea caracterelor Tipul Char contine mai multe metode statice utile pentru validarea caracterelor furnizate de evenimentul KeyPress. Cele mai importante astfel de metode: * Char.IsDigit * Char.IsLetter * Char.IsLetterOrDigit * Char.IsPunctuation * Char.IsLower * Char.IsUpper Exemplu: utilizarea metodei Char.IsDigit pentru a testa daca s-a apasat o tasta numerica: private void textBox1_KeyPress (object sender, System.Windows.Forms.KeyPressEventArgs e) { if (Char.IsDigit(e.KeyChar) == true) MessageBox.Show("Ai apasat o tasta numerica"); } Tratarea focus-ului Focus-ul reprezinta abilitatea unui obiect de a primi input prin intermediul mouse-ului sau tastaturii. Un singur control poate detine focus-ului la un moment dat. Acesta se afla pe forma activa a aplicatiei. Fiecare control implementeaza metoda Focus care seteaza focus-ul controlului care a apelat-o. Returneaza o valoare booleana care indica daca setarea focus-ului s-a executat cu succes sau nu. Proprietatea CanFocus returneaza true in cazul in care controlul poate primi focus-ul si false in caz contrar. if (textBox1.CanFocus == true) textBox1.Focus(); Evenimentele referitoare la primirea sau pierderea focus-ului au loc in urmatoarea
Page 56 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

ordine: 1. Enter 2. GotFocus 3. Leave 4. Validating 5. Validated 6. LostFocus Enter si Leave - controlul primeste si, respectiv, pierde focusul. GotFocus si LostFocus - controlul obtine pentru prima oara focus-ul, respectiv,controlul pierde focus-ul. Validating si Validated - cele mai utile evenimente pentru validarea field-level. Validating si Validated Validating - are loc inainte ca un control sa piarda focus-ul. Proprietatea CausesValidation a controlului care va primi focus-ul si a celui care trebuie validat trebuie setata la true pentru ca evenimentul sa poata fi lansat. Exemple de cazuri de utilizare: un event handler care testeaza daca input-ul respecta un anumit format, imposibilitatea parasirii unui control pana la introducerea unor informatii de un anumit tip. Validated - este lansat dupa ce controlul a fost validat cu succes. Poate fi folosit pentru a executa operatii bazate pe input-ul validat. Exemplu: event handler pentru evenimentul Validating private void textBox1_Validating(object sender, System.ComponentModel.CancelEventArgs e) { // verifica valoarea introdusa in textBox1 if (textBox1.Text == "") // se reda focus-ul controlului e.Cancel = true; } Validare form-level Reprezinta procesul validarii simultane (in acelasi moment al executiei aplicatiei) a tuturor campurilor de pe o forma. O procedura centralizatoare implementeaza validarea form-level si este apelata, de obicei, in momentul in care utilizatorul doreste sa treaca la un nou pas al aplicatiei.
Dezvoltare de aplicatii in Visual Studio .NET Page 57 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Exemplu: la apasarea unui buton, se verifica toate toate controalele de tip TextBox au fost completate si se transmite focus-ul primului TextBox care nu indeplineste aceasta conditie, in cazul in care exista un asemenea TextBox. private void btnValidate_Click(object sender, System.EventArgs e) { // parcurgerea iterativa a tuturor controalelor de pe forma foreach (System.Windows.Forms.Control aControl in this.Controls) { // se verifica daca este un TextBox si // daca contine stringul vid if (aControl is System.Windows.Forms.TextBox) { if (aControl.Text == "") // se transmite focus-ul primului control care // indeplineste conditia de mai sus aControl.Focus(); return; } } } Keyboard Handler pentru validare form-level Reprezinta o tehnica mai sofisticata de validare form-level. Un keyboard handler centralizat permite gestionarea input-ului din toate campurile de pe o forma. Se folosesc evenimentele KeyPress, KeyDown si KeyUp. ! pentru ca o forma sa lanseze aceste evenimente, proprietatea KeyPreview formei trebuie setata la valoarea true. In acest caz, forma lanseaza evenimente legate de apasarea tastelor inaintea controlului care detine focus-ul. Pentru a preveni executia event handler-ului asociat evenimentului KeyPress al unui control, se seteaza proprietatea KeyPressEventArgs.Handled la true: private void Form1_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e) { //evita pasarea evenimentului event handler-ului
Page 58 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

//asociat controlului care detine focus-ul e.Handled = true; } Feedback La introducerea unui input invalid, utilizatorul trebuie alertat si trebuie sa i se ofere oportunitatea de a corecta eroarea. Modalitati de atentionare: - pentru erori simplu de intuit, este suficienta o notificare auditiva - schimbarea proprietatilor BackColor sau ForeColor ale controlului la care a survenit eroarea - utilizarea metodei MessageBox.Show, in diferite formate Exemplu: MessageBox.Show("Valoare invalida pentru acest control!"); Componenta ErrorProvider Permite setarea unui mesaj de eroare pentru fiecare control de pe forma de fiecare data cand se introduce un input invalid. Se afiseaza un icon de eroare in dreptul controlului, iar textul mesajului de eroare este afisat ca Tool Tip atunci cand mouse-ul este pozitionat deasupra controlului afectat. Afisarea unui mesaj de eroare Se utilizeaza metoda SetError a componentei ErrorProvider. Exemplu: // nameTextBox este o instanta a clasei TextBox, // myErrorProvider este un ErrorProvider myErrorProvider.SetError(nameTextBox, "numele nu poate fi vid!"); Setarea mesajului de eroare la design-time se poate efectua prin setarea proprietatii Error on x asociata controlului, unde x este numele ErrorProvider-ului. Alte proprietati ale componentei ErrorProvider: - Icon - specifica icon-ul vizualizat in dreptul controlului - BlinkStyle - determina daca icon-ul va clipi in momentul aparitiei unei erori - BlinkRate - stabileste cat de repede va clipi icon-ul Exemplu:
Dezvoltare de aplicatii in Visual Studio .NET Page 59 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

private void passTextBox_Validating(object sender, System.ComponentModel.CancelEventArgs e) { // validarea input-ului if (passTextBox.Text == "") // setarea mesajului de eroare myErrorProvider.SetError(passTextBox, "Parola nu poate fi vida!"); else // nu se afiseaza nici un mesaj de eroare myErrorProvider.SetError(pswordTextBox, ""); } Sinteza * Validare form-level vs. field-level * Proprietati ale controlului TextBox control care restrictioneaza input-ul utilizatorului: - MaxLength - PasswordChar - ReadOnly - MultiLine * Evenimente lansate de intercatiunea utilizatorului cu tastatura: - KeyDown - KeyUp - KeyPress * Metode statice ale clasei Char utile in validarea input-ului: - Char.IsDigit - Char.IsLetter - Char.IsLetterOrDigit - Char.IsPunctuation - Char.IsLower - Char.IsUpper * Validating - principalul eveniment folosit pentru validare * ErrorProvider - componenta utila pentru setarea mesajelor de eroare

Page 60 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

Section 5. Concepte POO in Visual Studio .NET - IV 1. Concepte generale POO


Programarea in .NET Framework este realizata cu ajutorul obiectelor. Obiectele sunt constructii programatice care reprezinta pachete de date cu functionalitate unitara. Obiectele furnizeaza mediului de dezvoltare de aplicatii o functionalitate specifica, fara a se detalia comportamentul intern al obiectului. Obiectele sunt create pe baza unui sablon, denumit clasa. Libraria de clase .NET furnizeaza un set de clase de la care se pot crea obiecte in cadrul aplicatiilor. Exista, desigur, posibilitatea crearii propriilor clase in Microsoft Visual Studio. Obiecte, membri, abstractizare Un obiect este o constructie programatica care reprezinta o anumita entitate in cadrul aplicatiei. In lumea reala, obiectele sunt computere, case, masini etc. Fiecare dintre aceste elemente au o anumita functionalitate si sunt caracterizate de o serie de proprietati specifice. Intr-o aplicatie, un obiect poate fi o forma, un control, o conexiune la o baza de date, si asa mai departe. Fiecare obiect reprezinta o unitate functionala, continand toate datele si functionalitatea necesare pentru a isi indeplini sarcinile in cadrul aplicatiei. Abilitatea obiectelor programatice de a reprezenta obiecte din lumea reala este numita abstractizare. Clasele - sabloane pentru obiecte Clasele reprezinta tipuri referinta definite de utilizator. Clasele ar putea fi privite ca o amprenta pe care toate obiectele de acest tip o poarta: ele definesc toti membrii unui obiect, comportamentul acestuia si seteaza valorile initiale ale datelor membre, dupa caz. La instantierea unei clase, se creaza o instanta a clasei respective in memorie. Aceasta instanta este denumita obiect. O clasa se instantiaza utilizand cuvantul rezervat new, dupa cum urmeaza: // se declara o variabila de tip TipObiect TipObiect un_obiect; // instantiere si asignare un_obiect = new TipObiect(); La instantierea unei clase, se creaza in memorie o copie a datelor care formeaza instanta si acestea sunt asignate, unitar, variabilei referinta. Instantele individuale ale unei clase sunt independente intre ele si reprezinta constructii programatice separate. In principiu, nu exista o limita a numarului de copii (instante) ale unei aceleasi clase la un moment dat. Prin analogie cu lumea reala, daca un computer reprezinta un obiect, atunci datele tehnice de fabricatie ar putea reprezenta sablonul (clasa), pornind de la care se pot fabrica un numar practic nelimitat de computere. O modificare adusa unui astfel de obiect nu afecteaza in vreun fel celelalte obiecte. Obiecte si membri Obiectele sunt compuse din membri. Membrii pot fi: proprietati, campuri, metode si
Dezvoltare de aplicatii in Visual Studio .NET Page 61 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

evenimente, reprezentand datele si functionalitatea care compun obiectul. Campurile si proprietatile reprezinta datele membre ale unui obiect. Metodele reprezinta actiuni care pot fi executate de obiect, iar evenimentele sunt notificari pe care un obiect le poate trimite sau primi la (de la) alte obiecte in urma unor actiuni din cadrul aplicatiei. Pentru a continua exemplul din lumea reala legat de un obiect de tip Computer, putem considera ca un obiect Computer are campuri si proprietati, cum ar fi Frecventa, Firma, Tip etc. Acestea sunt date care descriu starea obiectului. Un astfel de obiect poate avea mai multe metode, cum ar fi Restart, StandBy sau Shutdown. Aceste metode determina comportamentului obiectului, reprezentand actiuni care pot fi executate. Evenimentele reprezinta notificari. De exemplu, un Computer poate primi un eveniment ExtraHeating din partea procesorului sau un eveniment StartShutDown atunci cand se apasa butonul de shutdown. Modelarea obiectelor Obiectele simple pot contine numai cateva proprietati, metode si, eventual, unul sau doua evenimente. Obiectele mai complexe pot necesita un numar mare de proprietati si metode si pot, chiar, ingloba alte obiecte. Obiectele pot contine si expune alte obiecte ca membri. De exemplu, un control TextBox are o proprietate Font care consta intr-un obiect de tip Font. Similar, orice instanta a clasei Form contine si gestioneaza o colectie Controls care include toate controalele de pe forma. Modelul unui obiect defineste ierarhia obiectelor care formeaza structura obiectului modelat. Modelul unui obiect este o organizare ierarhica a obiectelor subordonate continute de un obiect parinte. Incapsulare Incapsularea este conceptul prin care implementarea unui obiect este independenta de interfata sa. Cu alte cuvinte, o aplicatie interactioneaza cu un obiect prin intermediul interfetei sale, care consta in metodele si proprietatile sale publice. Cat timp aceasta interfata ramane neschimbata, applicatia poate continua interactiunea cu respectiva componenta, chiar daca implementarea interfetei a fost complet rescrisa de la o versiune la alta. Obiectele ar trebui sa interactioneze cu alte obiecte prin intermediul metodelor si proprietatilor publice. Deci, obiectele trebuie sa contina toate datele necesare, precum si functionalitatea completa necesara manipularii acestor date. Datele interne ale unui obiect nu trebuie expuse in interfata; deci, campurile nu trebuie declarate public. Polimorfism Polimorfismul reprezinta abilitatea unor clase diferite de a efectua implementari diferite ale aceleeasi interfete publice. Cu alte cuvinte, polimorfismul permite metodelor si proprietatilor unui obiect sa fie apelate fara a se tine cont de o implementare particulara a acelor membri. De exemplu, un obiect User poate interactiona cu un obiect Computer prin intermediul interfetei publice a obiectului Computer. Daca un alt obiect, cum ar fi ServerStation, expune aceeasi interfata publica, obiectul User poate interactiona cu ambele obiecte, fara a fi importanta implementarea specifica a interfetei. Exista doua tipuri importante de polimorfism: polimorfism bazat pe interfete si polimorfism bazat pe mostenire.
Page 62 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

Polimorfism bazat pe interfete O interfata este o structura comportamentala a unei clase. In principal, o interfata defineste membrii pe care o clasa ar trebui sa-i implementeze, dar nu specifica detalii referitoare la aceasta implementare. Un obiect poate implementa mai multe interfete diferite, si mai multe clase diferite pot implementa aceeasi interfata. Toate obiectele care implementeaza aceeasi interfata sunt capabile sa interactioneza cu alte obiecte prin intermediul acestei interfete. De exemplu, un obiect WorkStation poate implementa o interfata ICalculation, care specifica metodele BeginCalculation, ContinueCalculation si StopCalculation. Alte clase, cum ar fi Pda sau Laptop pot implementa aceeasi interfata, fiind, deci, capabile sa interactioneza cu un obiect User. Obiectul de tip User nu stie cu care dintre implementarile interfetei interactioneaza; el foloseste pur si simplu interfata. Polimorfism bazat pe mostenire Mostenirea permite incorporarea functionalitatii unor clase definite anterior intr-o noua clasa si implementarea unor alti membri. Despre o clasa care mosteneste o alta clasa spunem ca este derivata din acea clasa, sau mosteneste clasa respectiva. O clasa poate fi derivata direct dintr-o singura clasa, numita clasa de baza. Clasa derivata are aceeasi membri ca si clasa de baza, adaugandu-se eventuali membri aditionali. In plus, implementarea membrilor de baza poate fi modificata in noua clasa supraincarcand implementarea clasei de baza. Clasele derivate retin toate caracteristicile clasei de baza si pot interactiona cu alte obiecte in aceeasi maniera cu obiectele de tipul clasei de baza. Sinteza Abstractizarea este reprezentarea obiectelor din lumea reala prin constructii programatice. Obiectele programatice pot reprezenta obiecte din lumea reala prin implementari ale membrilor lor. Clasele sunt sabloane pentru obiecte. La crearea unui obiect, se creaza o copie a clasei in memorie, fiind initializate valorile variabilelor membre. O clasa se poate comporta ca un numar, teoretic nelimitat, de obiecte distincte. Incapsularea este un principiu al programarii orientate obiect. Un obiect ar trebui sa contina toate datele de care are nevoie si tot codul necesar manipularii acestor date. Datele specifice unui obiect nu trebuie sa fie disponibile altor obiecte. Numai proprietatile si metodele ar trebui expuse in interfata. Polimorfismul reprezinta abilitatea unor obiecte diferite de a expune implementari diferite ale aceleasi interfete publice. Polimorfism bazat pe interfete O interfata defineste trasaturile comportamentale ale unei clase. Ea specifica care dintre membri trebuie implementati, dar nu specifica detalii legate de implementarea acestora. Un obiect poate implementa mai multe interfete diferite, si mai multe obiecte diferite pot implementa aceeasi interfata. Polimorfism bazat pe mostenire Obiectele pot mosteni functionalitati de la alte obiecte. O clasa derivata retine toata
Dezvoltare de aplicatii in Visual Studio .NET Page 63 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

implementarea clasei de baza, iar instantele claselor derivate pot fi tratate ca instante ale clasei de baza. Clasele derivate pot implementa functionalitati aditionale.

2. Overloading
Supraincarcarea (overloading) permite crearea mai multor membri ai unei clase avand acelasi nume. Fiecare astfel de membru trebuie sa aiba o signatura diferita. Supraincarcarea este cel mai frecvent folosita pentru metode, dar C# permite si supraincarcarea operatorilor. Sa consideram situatia crearii unei metode care poate avea diversi parametri. Sa consideram urmatorul exemplu: public void Afisare(int valoare) { // Implementare... } Sa presupunem ca utilizatorul doreste modificarea numarului si/sau tipurilor parametrilor. Solutia acestei probleme este data de supraincarcare. Acesta implica definirea mai multor functii cu acelasi nume. Metodele supraincarcate trebuie sa aiba acelasi nume, dar acest lucru nu se respecta pentru tipul returnat sau pentru modificatorii de acces asociati. La apelul unei metode supraincarcate, common language runtime examineaza tipurile argumentelor transmise la apelul metodei. Se cauta apoi o potrivire a listei argumentelor printre signaturile metodei supraincarcate si se face apelul corespunzator. Daca nu se gaseste o astfel de potrivire, se genereaza o eroare. Metodele sunt membrii cel mai frecvent folositi pentru supraincarcare. C# permite supraincarcarea operatorilor, oferind astfel functionalitati ale operatorilor pentru tipurile definite de utilizator. Supraincarcarea metodelor Se poate crea o metoda supraincarcate similar crearii unei metode oarecare: prin declararea unei metode cu un nume, un nivel de acces, returnand un anumit tip si specificand o lista de argumente. O metoda supraincarcata trebuie sa aiba acelasi nume ca si o alta metoda existent, dar trebuie sa aiba o signatura diferita. Nivelul de acces si tipul returnat pot fi identice sau diferite. Exemplu: // metoda supraincarcata public void Afisare(int i) { MessageBox.Show(i.ToString());
Page 64 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

} // metoda cu acelasi nume, dar signatura diferita public void afisare(string s) { MessageBox.Show(s); } Supraincarcarea operatorilor in C# In aplicatii care utilizeaza tipuri definite de utilizator, se impune frecvent necesitatea definirii unui set de operatii aritmetice, logice sau a unor operatori de comparare care utilizeaza aceste tipuri de date. Exemplu: public struct PrezenteLab { int nrPrez; int nrPrezExtra; } Acesta structura poate fi utilizata pentru a gestiona evidenta situatiei prezentelor la un laborator de informatica. Pentru o utilizare facila si eficienta, apare necesitatea supraincarcarii operatorilor. Sintaxa generala a supraincarcarii unui operator este urmatoarea: public static tip operator op (argument1[, argument2]) { //implementare... } Componenta tip din cadrul sintaxei reprezinta tipul returnat de operator. argument1, argument2 etc. reprezinta argumentele asociate operatorului. Pentru un operator unar, vom avea un singur argument, si trebuie sa aiba acelasi tip ca tipul returnat. Pentru un operator binar, vom avea doua argumente, dintre care cel putin unul trebuie sa aiba acelasi tip ca tipul retunat. op reprezinta insusi operatorul, de exemplu +, -, >, !=, si asa mai departe. Un operator supraincarcat trebuie sa fie public, pentru a putea fi accesat. Un operator supraincarcat trebuie sa fie, de asemenea, static. Definitia unui operator supraincarcat trebuie sa apara in cadrul definitiei tipului asupra caruia i se aplica operatorul. Sintaxa supraincarcarii operatorilor este similara pentru structuri si clase. Exemplu: supraincarcarea operatorului + pentru structura definita anterior. public struct PrezenteLab { int nrPrez;
Dezvoltare de aplicatii in Visual Studio .NET Page 65 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

int nrPrezExtra; // supraincarcarea operatorului + public static PrezenteLab operator + (PrezenteLab a, PrezenteLab b) { PrezenteLab rezultat = new PrezenteLab(); rezultat.nrPrez = a.nrPrez + b.nrPrez; rezultat.nrPrezExtra = a.nrPrezExtra + b.nrPrezExtra; return rezultat; } } Utilizarea operatorului supraincarcat in cod: // Saptamana2, Saptamana3 sunt instante // ale structurii PrezenteLab PrezenteLab total = new PrezenteLab(); total = Saptamana2 + Saptamana3; Pornind de la ideea generala de supraincarcare, putem defini mai multe signaturi pentru a suprincarca un acelasi operator. Exemplu: public struct PrezenteLab { int nrPrez; int nrPrezExtra; // operatorul + supraincarcat anterior public static PrezenteLab operator + (PrezenteLab a, PrezenteLab b) { PrezenteLab rezultat = new PrezenteLab(); rezultat.nrPrez = a.nrPrez + b.nrPrez; rezultat.nrPrezExtra = a.nrPrezExtra + b.nrPrezExtra; return rezultat; } // supraincarcare aditionala (o alta signatura, // o alta functionalitate)
Page 66 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

public static PrezenteLab operator + (PrezenteLab a, int b) { PrezenteLab rezultat = new PrezenteLab(); rezultat.nrPrez = a.nrPrez + b; return rezultat; } } Sinteza - Supraincarcarea permite crearea de metode multiple avand acelasi nume, insa cu implementari diferite. Metodele supraincarcate trebuie sa difere prin signatura dar pot returna acelasi tip de date sau pot avea aceeasi modificatori de acces asociati. Declararea unei metode supraincarcate este similara definirii oricarei alte metode. - C# permite supraincarcarea operatorilor. Operatori supraincarcati trebuie sa fie publici si statici. Pentru a declara un operator supraincarcat se utlizeaza cuvantul rezervat operator.

3. Polimorfism
Polimorfism bazat pe interfete Interfetele permit definirea comportamentelor obiectelor. Clase diferite pot implementa aceeasi interfata si pot, deci, interactiona cu alte obiecte intr-o maniera polimorfica. O interfata actioneaza ca un sablon. Orice obiect care implementeaza o anumita interfata garanteaza implementarea membrilor definiti in acea interfata. Daca un obiect necesita interactiune cu o interfata specifica, orice obiect care implementeaza interfata poate raspunde acestei interactiuni. O interfata defineste numai membrii care vor deveni disponibili prin implementarea unui obiect. Definitia unei interfete nu implica aspecte legate de implementarea membrilor, specificandu-se, in schimb, lista parametrilor si tipul returnat. Sarcina implementarii unei interfete revine, in totalitate, clasei care utilizeaza interfata. Ca urmare, este posibila, pentru obiecte diferite, scrierea de implementari total diferite ale acelorasi membri. Sa consideram, de exemplu, o interfata IForma, care defineste o metoda CalculArie. O clasa Cerc care implementeaza aceasta interfata va calcula aria intr-un mod diferit fata de o clasa Patrat care implementeaza aceeasi interfata. Definirea interfetelor Interfetele se definesc folosind cuvantul rezervat interface. De exemplu:

Dezvoltare de aplicatii in Visual Studio .NET

Page 67 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

public interface ICalculate { ... } Acesta declaratie defineste interfata ICalculate, dar nu defineste nici un membru. Metodele membre trebuie definite specificand signatura, dar fara modificatori de acces. Modificatorul de acces asociat interfetei determina modificatorul de acces al fiecarui membru al interfetei. Deci, in cadrul unei interfete publice, toti membrii vor fi publici. Exemplul urmator demonstreaza cum se pot adauga metode unei interfete: public interface ICalculate { bool CheckInput(int input); void StartCalculation(); int GetResult(); } O interfata poate contine definitii de proprietati. Definitia unei proprietati trebuie sa includa getter-i, setter-i, sau ambele, precum si specificarea tipului returnat de proprietate. Exemplu: public interface ICalculate { // alti membri... string CalcType { get; // eventual, urmeaza set... } } ! In cadrul interfetelor nu se pot defini campuri! Aceasta restrictie blocheaza accesul claselor care interactioneaza cu interfata la datele interne ale unui obiect. Interfetele pot defini evenimente. Evenimentele definite in cadrul interfetelor reprezinta evenimente lansate de obiecte care implementeaza interfata. Desi orice clasa care implementeaza o interfata trebuie sa furnizeze o implementare pentru toate evenimentele membre, obiectele care interactioneaza prin intermediul acestei interfete nu sunt obligate sa trateze evenimentele lansate. In C#, trebuie desemnat explicit tipul delegatului asociat evenimentului. Exemplu de definire a unui eveniment membru al

Page 68 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

unei interfete: public interface ICalculate { // alti membri... event System.EventHandler OutOfMemory; } Definirea unei interfete Se declara interfata folosind cuvantul rezervat interface. In cadrul definitiei interfetei, se definesc signaturile metodelor, proprietatilor si evenimentelor membre ale interetei. Polimorfism bazat pe interfete Orice obiect care implementeaza o anumita interfata poate interactiona cu un alt obiect care solicita acea interfata. Sa consideram, drept exemplu, urmatoarea metoda: public void CalculateSomething(ICalculate ic) { // implementare... } Aceasta metoda necesita o implementare a interfetei ICalculate. Orice obiect care implementeaza aceasta interfata poate fi transmis ca parametru acestei metode. Obiectul va fi implicit convertit la interfata corespunzatoare. Cand un obiect interactioneaza prin intermediul interfetei sale, numai membrii interfetei sunt accesibili. Se poate executa, de asemenea, o conversie explicita a obiectelor care implementeaza interfete specifice. Exemplu - conversia unui obiect WorkStation la interfata ICalculate (obiectul ws trebuie sa implementeze ICalculate): WorkStation ws = new WorkStation(); ICalculate calculateTool; // converteste ws la interfata ICalculate calculateTool = (ICalculate)ws; Implementarea interfetelor In C#, se utilizeaza caracterul : pentru a specifica o clasa sau o structura care implementeaza o anumita interfata. Urmatorul exemplu indica faptul ca o clasa implemeneaza o anumita interfata: public class WorkStation : ICalculate { // cod aditional...

Dezvoltare de aplicatii in Visual Studio .NET

Page 69 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

} Clasele pot implementa mai multe interfete. Exemplu: public class WorkStation : ICalculate, IMovieViewer, IInternetExplorer { // cod aditional.... } Atunci cand o clasa sau o structura implementeaza o interfata, trebuie scrise implementari separate pentru fiecare membru al interfetei. Daca se implementeaza mai multe interfete, trebuie furnizate implementari pentru fiecare membru al fiecarei interfete. Implementarea membrilor interfetelor in C# In C#, implementarea unui membru al interfetei in cadrul clasei sau structurii se realizarea prin definirea unui membru care are acelasi nume cu membrul definit in cadrul interfetei. Acest membru trebuie sa aiba acelasi modificator de acces ca in cadrul interfetei. Exemplu: public interface ICalculate { void BeginCalculation(int input); } public class WorkStation : ICalculate { public void BeginCalculation(int i) { // implementare... } } In cazul unei astfel de implementari a membrilor unei interfete, ei sunt disponibili atat interfetei, cat si clasei insesi. Deci, acesti membri pot fi accesati daca obiectul este convertit la propria sa clasa sau la interfata implementata. Este, de asemenea, posibila implementarea explicita a interfetei si blocarea accesului la membrii sai pentru clasa care implementeaza interfata. Un membru astfel implementat poate fi accesat numai cand obiectul parinte este convertit la interfata pe care o implementeaza (si care contine membrul respectiv). Implementarea explicita a unui membru al unei interfete se realizeaza prin specificarea completa a numelui interfetei si a membrului. Exemplu:

Page 70 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

public class WorkStation : ICalculate { void ICalculate.Begin Calculation(int input) { // implementare... } } ! Membrul definit explicit din exemplul anterior nu are modificator de acces asociat. Deoarece s-a efectuat o implementare explicita a unui membru al interfetei, acesta va avea acelasi nivel de acces ca si membrul definit de interfata. Sinteza O interfata descrie comportamentul obiectelor. Ele definesc membrii care vor fi accesibili prin intermediul interfetei, si parametrii si tipul returnat de acesti membri. Orice obiect care implementeaza o interfata poate interactiona cu orice obiect care solicita interfata respectiva. Atat clasele, cat si structurile pot implementa una sau mai multe interfete. Implementarea membrilor unei interfete este realizata de clasele sau structurile care implementeaza interfata. In C#, pentru a specifica faptul ca o clasa sau structura implementeaza o interfata, se utilizeaza : (doua puncte). Daca o clasa sau structura implementeaza o interfata, aceasta trebuie sa implementeze fiecare membru definit in cadrul interfetei. In C#, exista doua modalitati de a implementa membrii unei interfete: - implementand un membru cu acelasi nume, signatura, si nivel de access ca si membrul definit in interfata. Acest membru va fi disponibil atat clasei care il implementeaza, cat si interfetei. - implementand explicit membrul interfetei, utilizand calificarea completa a numelui membrului. Un membru implementat in aceasta maniera va fi disponibil numai interfetei din care face parte.

Polimorfism bazat pe mostenire Mostenirea (derivarea) permite declararea unei noi clase care retine toti membrii si functionalitatea unei clase definite anterior. Aceast mecanism permite crearea de clase care implementeaza functionalitati comune, de baza si apoi scrierea de subclase specializate care introduc functiuni diferite, dar strans legate de cele ale clasei de baza.

Dezvoltare de aplicatii in Visual Studio .NET

Page 71 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Mostenirea Mostenirea permite crearea mai multor clase distincte, dar care au in comun o anumita functionalitate. Clasele specializate, denumite clase derivate, mostenesc o clasa comuna, numita clasa de baza. Clasa de baza incapsuleaza functionalitatea comuna care va fi prezenta in fiecare clasa derivata, in timp ce clasele derivate implementeaza functionalitati specifice. De exemplu, sa consideram o clasa Laptop. Aceasta clasa implementeza toata functionalitatea specifica unei clase Computer. In plus ea poate implementa diverse metode specifice, cu ar fi StandByMode, apelata, de exemplu, la inchiderea capacului laptop-ului. Comportamente polimorfice in clasele derivate Clasele derivate sunt, in general, intr-o relatie de tipul is-a fata de clasa de baza. De exemplu, un Laptop este un (is-a ) Computer, iar un Computer este un Dispositif. Orice instanta a unei clase derivate se poate comporta polimorfic ca o instanta a clasei de baza. Deci, daca o metoda necesita un parametru de tip Computer, i se poate furniza in schimb un obiect Laptop. Orice clasa derivata poate fi convertita implicit la clasa sa de baza. In momentul conversiei catre clasa de baza, fiecare membru implementat de clasa derivata va deveni inaccesibil; numai membrii clasei de baza vor fi disponibili. Crearea claselor derivate Se utilizeaza : (doua puncte). Exemplu: public class Laptop : Computer { // implementare... } Clasele pot mosteni o singura clasa de baza, dar pot implementa una sau mai multe interfete. Daca o clasa implementeaza mai multe interfete, odata cu derivarea dintr-o clasa de baza, numele interfetelor trebuie enumerate dupa : (doua puncte), despartite prin virgula. De exemplu: public class Laptop : Computer, ICalculate, IFileManager { // implementare... } Odata ce o clasa derivata a fost declarata, se pot implementa membrii aditionali pentru a adauga functionalitati specifice clasei. Crearea de clase care nu pot fi derivate Uneori, este necesara definirea unor clase care nu vor putea fi mostenite. De exemplu, o clasa specializata care va fi utilizata pentru scrierea unor componente si nu va fi utila altor programatori, se defineste in asa fel incat sa nu poate fi mostenita. In C#, pentru a realiza acest lucru, se utilizeaza cuvantul rezervat sealed:
Page 72 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

public sealed class ClassNotInheritable { // implementare... } Membri mosteniti La crearea unei clase derivate, noua clasa poseda toata functionalitatea implementata in clasa de baza. Pe langa adaugarea de noi membri, se poate modifica si implementarea membrilor mosteniti, pentru anumite scopuri. Suprascrierea (overriding) membrilor din clasa de baza se realizeaza printr-o noua implementare a unui membru existent, implementat in clasa de baza. C# permite ascunderea membrilor clasei de baza, prin implementarea unui nou membru, in clasa derivata, cu acelasi nume si signatura, dar avand diverse alte caracteristici. Suprascrierea membrilor din clasa de baza La derivarea dintr-o clasa de baza, se poate furniza o implementare diferita pentru membrii clasei de baza prin suprascrierea lor folosind o noua implementare in clasa derivata a unui membru cu acelasi nume. De exemplu, o clasa Computer are o metoda Hibernate. Daca derivam o clasa Laptop din clasa Computer, se poate furniza o implementare diferita pentru metoda Hibernate. ! Pot fi suprascrise numai proprietatile si metodele din cadrul unei clase de baza. Variabilele si evenimentele membre nu pot fi suprascrise. Pentru a declara o noua implementare a unui membru dintr-o clasa intr-o clasa derivata, se utilizeaza cuvantul rezervat override. Noua implementare trebuie sa aiba o signatura si tip returnat identice cu cele ale membrului suprascris, si trebuie sa aiba asociat acelasi nivel de acces. De exemplu: // Calculate este o metoda a clasei Computer public class Laptop : Computer { public override void Calculate(int input) { // implementare.... } } La suprascrierea unui membru, noul membru va fi apelat in locul membrului din clasa de baza. Aceasta afirmatie este dependenta de contextul in care este apelat un membru. De exemplu, daca o instanta a unei clase derivate este convertita la clasa sa de baza si este apelata o metoda membra suprascrisa, se va executa noua implementare, desi variabila este de tipul clasei de baza. Tipul obiectului, si nu variabila, determina care membru va fi apelat.
Dezvoltare de aplicatii in Visual Studio .NET Page 73 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Pentru a suprascrie un membru al unei clase de baza, acesta trebuie declarat virtual. Membrii non-virtuali sunt nu pot fi suprascrisi. Exemplu de declarare a unei metode virtuale: public virtual void OverridableMethod() { // implementare... } Ascunderea membrilor clasei de baza Un membru al clasei de baza poate fi ascuns prin inlocuirea sa cu o implementare complet noua. Mecanismul este cunoscut sub numele de ascundere (hiding). La ascunderea unui membru, se inlocuieste implementarea din clasa de baza cu o noua implementare. Noua implementare trebuie sa aiba aceeasi signatura ca a membrului ascuns si trebuie sa fie asociata unui membru de acelasi tip, dar poate avea nivel de acces, tip returnat diferite, si o implementare complet diferita. Orice metoda care are acelasi nume ca o metoda existenta, dar o signatura diferita, este tratata ca o supraincarcare a metodei initiale si nu se va realiza ascunderea. Pentru a ascunde un membru al unei clase de baza se defineste un membru in clasa de baza avand aceeasi signatura si folosind cuvantul rezervat new: // clasa de baza public class ClasaDeBaza { public string metoda(int i) { // implementare... } } // clasa derivata public class ClasaDerivata : ClasaDeBaza { // ascunderea metodei din clasa de baza // signatura identica, insa nivel de acces diferit, // tip returnat diferit internal new int metoda(int i) { // implementare... }

Page 74 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

} Mentinerea compatibilitatii cu membrii ascunsi La ascunderea unui membru al unei clase, se ascunde implementarea din clasa de baza si se creaza o noua implementare care nu trebuie sa aiba caracteristicile implementarii membrului din clasa de baza. Acest aspect poate conduce la implicatii nedorite in ceea ce priveste interoperarea cu alte obiecte. Daca un obiect apeleaza metoda din exemplul anterior, in ideea de a obtine un String, la returnarea unui int, de catre metoda care ascunde metoda initiala, se poate genera o eroare. Ascunderea membrilor trebuie facuta deci cu atentie. Desi ascunsa, implementarea membrilor din clasa de baza poate fi accesibila sub anumite circumstante. Aceasta depinde de clasa, si nu de tipul obiectului, ca in cazul membrilor suprascrisi. De exemplu, sa consideram urmatorul fragment de cod: // folosim clasele din exemplul anterior ClasaDerivata X = new ClasaDerivata(); ClasaDeBaza Y; // X si Y refera acelasi obiect, dar // variabilele au tipuri diferite Y = X; // se apeleaza noul membru X.metoda(23); // se apeleaza metoda originala Y.metoda(23); Deci, tipul variabilei determina daca se va apela membrul din clasa derivata sau membrul original (ascuns). Ca urmare, se poate modifica implementarea unui membru fara a se distruge abilitatea unei clase de a se comporta polimorfic. Accesarea membrilor clasei de baza La suprascrierea sau ascunderea unor membri, poate aparea necesitatea accesarii implementarii acestor astfel de membri din clasa de baza. Pentru aceasta, se utilizeaza cuvantul rezervat base. Se face astfel o referinta la implementarea clasei de baza si se permite invocarea membrilor implementati in clasa de baza. Exemplu: // apelul unei metode din clasa de baza // dintr-o metoda care suprascrie metoda respectiva public override string metoda(int i) { base.metoda(i); // implementare...

Dezvoltare de aplicatii in Visual Studio .NET

Page 75 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

} Membri protected membri public - accesibili din orice sectiune a aplicatiei, incluzand clase externe membri internal - accesibili membrilor assembly-ului local, dar nu si apelantilor externi membri private - disponibili numai in interiorul clasei membri protected si protected internal - discutati in cele ce urmeaza Un membru definit protected are aceeasi vizibilitate fata de apelantii externi ca si membrii private. Diferenta consta in faptul ca membrii protected pot fi accesati din clasele derivate. Exemplu: // o metoda private si o metoda protected // in clasa de baza public class BaseClass { // o metoda private nu poate fi apelata // din clase derivate private void private_method() { // implementare.... } // un membru protected poate fi // apelat din clase derivate protected void protected_method() { // implementare... } } // clasa derivata public class InheritedClass : BaseClass { public void Demo() { // apel legal, metoda protected this.protected_method();
Page 76 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

// apel nepermis, metoda private this.private_method(); } } Membri protected internal Acest modificator de acces este reuniunea modificatorilor de acces protected si internal. Deci, un membru protected internal poate fi accesat de clasele din assembly sau de clasele care mostenesc clasa care contine membrul, desigur, pe langa posibilitatea accesarii membrului din interiorul clasei care il contine. Clase si membri abstracti La crearea de componente, poate surveni situatia crearii unei clase de baza care furnizeaza o functionalitate invarianta dar lasa in seama claselor derivate implementarea anumitor membri. Acest aspect este surprins cu ajutorul claselor abstracte, clase care trebuie mostenite. Clasele abstracte sunt similare interfetelor, dar au multe caracteristici in comun cu clasele. O clasa abstracta nu poate fi instantiata; trebuie mai intai mostenita. Clasele abstracte pot furniza sau nu implementari ale unei clase. Precum interfetele, pot specifica membri care tebuie implementati in clasele derivate. Spre deosebire de interfete, o clasa poate mosteni numai o clasa abstracta. Similar claselor uzuale, clasele abstracte pot implementa complet anumiti membri, dar pot specifica membri care trebuie implementati in clasele derivate. Crearea claselor abstracte O clasa se defineste ca fiind abstracta prin utilizarea cuvantului rezervat abstract. De exemplu: public abstract class AbstractClass { // implementare... } Membri abstracti O clasa abstracta poate implementa orice membru. Membrii unei clase virtuale pot fi declarati virtual, caz in care clasele derivate pot crea implementari proprii ale membrilor, sau pot fi declarati non-virtual, avand deci o implementare fixa, comuna in clasele derivate. Un membru abstract este similar unui membru al unei interfete. Se specifica numai tipul, nivelul de acces, parametrii si tipul returnat. Implementarea este sarcina clasei derivate. Membrii abstracti se declara in clase abstracte. Declararea unui membru abstract intr-o clasa non-abstracta conduce la eroare.
Dezvoltare de aplicatii in Visual Studio .NET Page 77 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

public abstract class Computer { public abstract void Calculate(int); public abstract int GetResult(); public abstract string Info { get; set; } } ! In C#, trebuie specificati un getter si/sau un setter pentru o proprietate abstracta. Daca sunt specificate ambele, atunci trebuie implementate impreuna in clasa derivata. Mostenirea unei clase abstracte Clasa derivata dintr-o clasa abstracta trebuie sa implementeze fiecare membru abstract definit in clasa abstracta de baza. Implementarea se realizeaza prin macanismul suprascrierii, la fel ca in cazul claselor uzuale. Exemplu (bazat pe exemplul anterior): public class MyComp : Computer { public override void Calculate(int input) { // implementare... } public override int GetResult() { // implementare... } public override string Info { get { // implementare obligatorie a // getter-ului si a setter-ului
Page 78 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

} set { // implementarea setter-ului } } } ! Se pot crea noi clase abstracte prin derivarea din alte clase abstracte. In acest caz, clasa abstracta derivata nu trebuie sa implementeze membrii abstracti definiti in clasa de baza (acest lucru este, insa, permis). Sinteza - cuvinte cheie mostenire (derivare) supraincarcare (overriding) ascundere (hiding) base protected, protected internal virtual abstract

Dezvoltare de aplicatii in Visual Studio .NET

Page 79 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Section 6. Testarea si debugging-ul aplicatiilor - V 1. Unelte de debugging


Erorile de programare sunt inevitabile. Chiar si programatorii cu experienta mare in domeniu comit frecvent erori (bug-uri) la scrierea de cod. Debugging-ul este procesul localizarii si corectarii erorilor. Tipuri de erori Exista trei tipuri de erori care pot surveni pe durata de viata a unei aplicatii. Erorile de sintaxa sunt determinate de scrierea de cod care nu poate fi inteles de catre compilator. Erorile de tip run-time sunt erorile care au loc atunci cand se efectueaza o operatie imposibil de tratat in context. Erorile logice sunt erorile care rezulta atunci cand programul se compileaza si se executa corect, dar returneaza un rezultat neasteptat. Erori de sintaxa O eroare de sintaxa are loc atunci cand compilatorul nu poate compila codul scris. De exemplu, o eroare de sintaxa apare atunci cand cuvintele cheie sunt scrise incorect, cand lipsesc semne de punctuatie sau cand entitatile din program nu sunt bine structurate. Exemplu: public void EroareDeSintaxa() { System.Windows.Forms.MessageBoxShow("Care este eroarea?"); Mai intai, lipsa unui punct intre MessageBox si Show creaza o comanda care nu poate fi inteleasa de compilator. Apoi, constructia prin care se specifica sfarsitul functiei lipseste (acolada inchisa). Ambele erori vor crea o conditie pe care compilatorul nu o poate interpreta. Erorile de sintaxa pot fi usor identificate odata cu scrierea codului (colorarea adecvata a portiunilor de cod). Erorile detectate vor aparea, de asemenea, in fereastra Task List, odata cu prima compilare efectuata. Executand dublu clic pe o eroare din fereastra Task List, cursorul se va pozitiona pe linia de cod care a generat eroare. Frecvent, aceasta actiune furnizeaza suficiente informatii pentru a corecta eroarea. Pentru mai multe informatii se tasteaza F1 pe codul de eroare selectat. Erori la executie (run-time) Erorile la executie apar in momentul in care aplicatia incearca sa efectueaze o operatie nepermisa in context. Aceasta include operatii imposibil de tratat, cum ar fi impartirea la zero, si operatii nepermise, ca in cazul exceptiilor legate de securitate. La aparitia unei astfel de erori, se arunca o exceptie care descrie eroarea. Exceptiile sunt clase
Page 80 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

speciale care sunt utilizate pentru a comunica starile de eroare ale diferitelor sectiuni ale aplicatiei. Se poate scrie cod care sa trateze exceptiile, astfel incat acestea sa nu stopeze executia aplicatiei. Erori logice Erorile logice apar atunci cand aplicatia este compilata si executata corect, dar nu se obtin rezultatele scontate. Acestea pot fi considerate eroriele cel mai dificil de tratat, pentru ca, adesea, nu exista informatii aditionale care sa indice sursa erorii. Erori logice pot surveni, de exemplu, din plasarea inadecvata a punctului zecimal. De exemplu: public float CalculNota(int punctajLab, int punctajExamen) { float nota; nota = punctajLab * 0.3 + punctajExamen * 0.07; return nota; } Modul break Modul break permite intreruperea executiei programului si executia codului linie cu linie. In mod break, se pot utiliza uneltele de debugging pentru a examina valorile variabilelor si proprietatile aplicatiei. In Visual Studio .NET, se intra in mod Break sub una din urmatoarele circumstante: s-a selectat Step Into, Step Over sau Step Out din meniul Debug sau din toolbar. executia programului avanseaza la o linie care contine un breakpoint activ. se intalneste un Stop se arunca o exceptie netratata

Elementele meniului Debug Optiune Windows Start/Continue F5 Break All Ctrl+Alt+Break Stop Debugging Shift+F5 Detach All Descriere Deschide un submenu de ferestre de debugging. Se ruleaza aplicatia in mod Debug. Daca aplicatia este in mod Break, se continua executia programului. Se intrerupe executia programului si se intra in mod Break la linia curenta din program. Se poate folosi Continue pentru a continua executia. Se opreste modul Debug si se intra in mod Design. Se stopeaza toate procesele in care era implicat debugger-ul. Acesta este inchis, insa nu se intrerupe executia programului.
Page 81 of 180

Dezvoltare de aplicatii in Visual Studio .NET

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Restart Ctrl+Shift+F5 Apply Code Changes Processes Exceptions Ctrl+Alt+E Step Into F11

Se finalizeaza si se restarteaza executia aplicatiei. Optiune specifica programarii C/C++. Vizualizeaza fereastra Processes. Vizualizeaza fereastra Exceptions. Se executa urmatoarea linie de cod. Daca aceasta contine apelul unei metode, Step Into se opreste la inceputul acelei metode. Se executa urmatoarea linie de cod. Daca aceasta contine apelul unei metode, Step Over executa metoda si se opreste la linia urmatoare a metodei curente. Executa liniile ramase din metoda curenta si se opreste la urmatoarea linie executabila de cod din metoda apelanta. Afiseaza fereastra QuickWatch. Afiseaza fereastra New Breakpoint. Elimina toate breakpoint-urile din aplicatie.

Step Over F10

Step Out Shift+F11 QuickWatch Ctrl+Alt+Q New Breakpoint Ctrl+B Clear All Breakpoints Ctrl+Shift+F9 Disable All Breakpoints

Dezactiveaza toate breakpoint-urile, fara a le elimina efectiv.

Mai mult, anumite functii de debugging pot fi accesate prin clic dreapta pe un element din fereastra de cod si alegand o functie meniul pop-up respectiv. Tabelul urmator sintetizeaza o parte din aceste functii.

Tabel: functii de debugging accesibile din meniuri pop-up accesibile din cadrul ferestrei de cod.

Optiune Insert Breakpoint New Breakpoint Add Watch QuickWatch Show Next Statement

Descriere Insereaza un breakpoint la linia selectata. Afiseaza fereastra New Breakpoint. Acest element este identic cu optiunea cu acelasi nume din meniul Debug. Adauga expresia specificata in fereastra Watch. Afiseza fereastra QuickWatch. Identica cu optiunea cu acelasi nume din meniul Debug. Indica urmatorul bloc de program care se va executa.

Page 82 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

Run To Cursor Set Next Statement

Deplaseaza executia programului la linia selectata. Desemneaza linia selectata drept urmatoarea linie de cod care se va executa. Aceasta trebuie sa fie in cadrul procedurii curente.

Examinarea valorilor variabilelor Valorile variabilelor din program pot fi examinate in mod Break mentinand mouse-ul pozitionat deasupra variabilei, in fereastra de cod. Valoarea curenta a variabilei va fi afisata intr-o casuta de tip pop-up. Aceasta tehnica furnizeaza o modalitate facila si rapida de a determina valoarea curenta a unei variabile. Pentru mai multe informatii referitoare la starea unei variabile, se pot folosi ferestrele Watch, Autos sau Locals. Breakpoints Se pot desemna linii de cod sau conditii care vor determina aplicatia sa isi opreasca executia in debugger. Aceste entitati se numesc breakpoints. Se pot utiliza breakpoints pentru a seta linii de cod sau conditii care vor opri executia program. Exista 4 tipuri de breakpoints: Setarea unui breakpoint de tip funtie: - prin clic stanga pe bara gri din stanga ferestrei de editare de cod, in dreptul liniei dorite - prin clic dreapta pe linia dorita si selectand Insert Breakpoint din fereastra pop-up - meniul Debug - New Breakpoint Fereastra Breakpoints permite gestionarea multimii de breakpoints din program intr-o singura fereastra. Breakpoints - proprietati, conditii Proprietatile unui breakpoint se pot vizualiza prin intermediul ferestrei Breakpoints - clic dreapta, Properties. Fereastra Properties a oricarui breakpoint are trei taburi asociate: Function, File si Address, fiecare dintre acestea fiind asociind breakpoint-ul la tipul respectiv. Deci, chiar daca un breakpoint este asociat in fereastra de cod la un anumit tip, el se exprima in fereastra Properties prin toate cele trei tipuri. In plus, fereastra Breakpoint Properties contine doua butoane: butonul Conditions si butonul Hit Count. Primul va vizualiza o fereastra care permite specificarea unei expresii care va activa breakpoint-ul numai daca expresia este evaluata la true sau daca expresia se modifica. Expresia poate fi orice expresie booleana care poate fi evaluata la o valoare booleana. De exemplu, conditia poate fi setata la expresia bool_var == true, breakpoint-ul devenind activ numai daca variabila bool_var are valoarea true. O alta metoda este desemnarea unei variabile sau expresii care, atunci cand se modifica, va determina oprirea executiei programului. Expresia specificata trebuie sa fie o expresie valida. Butonul Hit Count determina vizualizarea ferestrei Breakpoint Hit, in care se poate seta

Dezvoltare de aplicatii in Visual Studio .NET

Page 83 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

ca breakpoint-ul sa intrerupa executia aplicatiei dupa ce a fost intalnit de un anumit numar de ori, sau de un multiplu a unui numar predeterminat de ori sau de un numar de ori mai mare sau egal cu un numar predeterminat. De exemplu, daca se seteaza conditia hit count la un multiplu de 10, executia programului se va opri la fiecare a zecea intalnire a breakpoint-ului. ! Breakpoint-ul trebuie sa fie activ pentru a putea fi numarat. Debugging Windows Sunt unelte de monitorizare a executiei programului. Unele, precum fereastra Output sau fereastra Command, sunt disponibile in mod Design. Altele, cum ar fi fereastra Locals, sunt vizibile numai pe durata debugging-ului. Fereastra Output - vizualizeaza output-ul generat in urma compilarii si executiei aplicatiei. Output-ul include notificari legate de incarcarea assembly-urilor, precum si output generat de constructii Debug si Trace. Ferestrele Locals, Autos si Watch Permit monitorizarea starii variabilelor din program pe durata debugging-ului aplicatiei. Mai mult, aceste ferestre permit editarea valorilor variabilelor. Sunt utile pentru testarea modului in care anumite proceduri raspund la diverse input-uri. Fereastra Locals Cand programul ruleaza in mod Debug, fereastra Locals permite monitorizarea valorilor tuturor variabilelor din procedura curenta. Fereastra este structurata pe trei coloane: Name, Value si Type. Tipurile mai complexe sunt vizualizate intr-un tree care poate fi expandat pentru a vizualiza valorile membrilor. Pe parcursul executiei programului, continutul ferestrei se mosdifica, in functie de procedura curenta. Se pot modifica valorile tipurilor primitive prin editarea valorii variabilelor. Tipurile complexe, precum clase sau structuri nu pot fi modificate, incercarea de a atribui unui obiect continutul unui alt obiect neputandu-se realiza. Fereastra Autos Este o forma abreviata a ferestrei Locals, vizualizand numai variabilele din linia curenta si din cea anterioara, in acelasi format si cu posibilitatea de editare a valorilor variabilelor. Fereastra Watch Permite studierea evolutiei valorilor unor variabile specificate. Se poate efectua o evaluare rapida a unei variabile utilizand dialogul QuickWatch. Immediate Mode in fereastra Command Este utilizat pentru executia procedurilor, evaluarea expresiilor sau modificarea valorilor variabilelor pe durata debugging-ului. Vizualizarea ferestrei: Meniul Debug Windows - Immediate. Afisarea valorii obtinute prin evaluarea unei expresii: folosind semnul intrebarii (?). Exemplu: ? X*Y (o astfel de linie nu se termina prin punct si virgula).

Page 84 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

? Label2.Text - afiseaza valoarea curenta a proprietatii Text a variabilei Label2. Alte ferestre Tabelul urmator sintetizeaza alte ferestre disponibile din meniul Debug. Tabel: alte ferestre disponibile din meniul Debug Optiune Running Documents This Call Stack Threads Modules Memory Descriere Lista documentelor incarcate in cadrul procesului care ruleaza. Afiseaza datele membre ale obiectului asociat metodei curente. Afiseaza numele functiilor din stiva de apeluri, tipul parametrilor si valorile parametrilor. Permite examinarea si controlul firelor de executie din program. Listeaza modulele (fisiere DLL sau EXE) utilizate in program. Afiseaza valorile stocate in memorie. Utila pentru vizualizarea bufferelor si stringurilor de mari dimensiuni, sau a altor date care nu pot afisate corespunzator in ferestre Locals, Autos sau Watch. Afiseaza cod assembly corespunzator instructiunilor create de catre compilator. Vizualizeaza continutul registrilor.

Disassembly Registers Sinteza

- tipuri de erori: de sintaxa, la executie si logice - modul break - ferestre din meniul Debug

2. Clasele Debug si Trace


Clasele Debug si Trace permit generarea si afisarea (de obicei sub forma de log-uri) de mesaje informative legate de anumite conditii specifice aplicatiei, fara a fi necesara intreruperea executiei aplicatiei. Pentru aplicatii simple, frecvent utilizate, nu sunt necesare unelte de debuging complexe, mijloacele prezentate anterior fiind suficiente. Aplicatiile complexe ridica, insa, probleme. La aparitia unei erori logice, examinarea variabilelor din aplicatie, urmarind liniile programului, conduce rar la identificarea si rezolvarea erorii. Clasa Debug furnizeaza o modalitate de a crea si salva sub forma de log-uri mesaje informative despre conditiile din program, pe masura ce aplicatia se executa. Clasa Trace permite crearea unor instrumente de diagnostic care pun la dispozitie mesaje informative chiar dupa ce aplicatia a fost compilata.

Dezvoltare de aplicatii in Visual Studio .NET

Page 85 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Cum functioneaza tracing-ul? Clasele Trace si Debug fac parte din spatiul de nume System.Diagnostics care contine metode statice care permit testarea anumitor conditii la run time si salvarea rezultatelor sub forma de log-uri. Output-ul generat de aceste metode este afisat in fereastra Output si poate fi vizualizat pe durata debugging-ului. Output-ul este transmis, de asemenea, colectiei Listeners. Aceasta colectie contine un grup de clase care pot primi output de la clasele Trace si Debug. Trace si Debug partajeaza aceeasi colectie Listeners si toti asculatatorii din colectie primesc input de la Trace si Debug. Exista mai multe tipuri de Listeners, incluzand ascultatori care scriu in fisiere text si Trace Listeners care scriu in loguri de evenimente. Dupa executia programului, se pot examina aceste fisiere pentru identificarea erorilor din aplicatie. Tracing-ul este, de asemenea, util in optimizarea programelor. Clasele Trace si Debug sunt aproape identice. Clasa Debug este folosita, in principal in faza de dezvoltare, in timp ce Trace poate fi utilizata pentru testare si optimizare dupa ce o aplicatie a fost compilata si s-a generat o versiune release a aplicatiei. Scrierea output-ului furnizat de clasele Trace si Debug Metodele care realizeaza acest lucru sunt: Write - scrie text in colectia Listeners, neconditionat. WriteLine - similar, adaugand un enter la textul scris. WriteIf - se scrie text daca conditia booleana specificata are valoarea true. WriteLineIf - similar cu WriteLine, adaugandu-se un enter. Assert - se scrie un mesaj de asertiune catre colectia Listeners daca expresia booleana specificata are valoarea false. Se afiseaza, de asemenea, un message box. Fail - creaza o asertiune care este considerata falsa, fara a se testa vreo conditie. Se scrie in colectia Listeners mesajul specificat de asertiune si se afiseaza, de asemenea, un message box. Exemplu de apelare a acestor metode: // metodele sunt statice, nu este necesara declararea unor instante Trace.Write("Mesaj Trace 1"); Trace.WriteLine("Mesaj Trace 2"); // se scrie text daca expresia este true Debug.WriteIf(x==2, "x are valoarea 2"); Debug.WriteLineIf(x==3, "x este egal cu 3"); // se scrie output si se afiseaza un message box // daca valoarea conditiei este false Trace.Assert(x==5, "x nu este egal cu 5!"); // se scrie output si se afiseaza un message box, neconditionat

Page 86 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

Debug.Fail("Discul D nu mai este valid!"); Se poate modifica nivelul de indentare asociat mesajelor Trace atunci cand sunt transmise colectiei Listeners apeland metodele Indent si Unindent si setand proprietatile IndentSize si IndentLevel. Aceste metode si proprietati sunt utile pentru crearea crearea si vizualizarea ierarhica a mesajelor de eroare. Proprietatea IndentSize seteaza si returneaza numarul de spatii pentru o indentare, iar IndentLevel seteaza si returneaza numarul de indentari aplicate. Metoda Indent incrementeaza IndentLevel cu o unitate, iar Unindent decrementeaza. Exemplu: // incrementare IndentLevel Trace.Indent(); Colectia Listeners Output-ul generat de clasele Trace si Debug este directionat catre colectia Listeners. Colectia Listeners organizeaza si expune clase capabile sa primeasca output-ul furnizat de Trace. Fiecare membru al colectieiListeners primeste output de la clasele Trace si Output. Tratarea output-ului depinde de tipul ascultatorului. Colectia Listeners este initializata cu un membru, o instanta a clasei DefaultTraceListener. Acest ascultator este creat automat si va primi output-ul claselor Trace si Debug, chiar daca nu se ataseaza un alt ascultator. Output-ul primit de instanta DefaultTraceListener este directionat catre debugger. DefaultTraceListener-ul este responsabil de vizualizarea output-ului in fereastra Output. Daca se doreste salvarea mesajelor, in afara de afisarea lor in fereastra Output, trebuie adaugat cel putin un ascultator in plus. Clase specializate pentru scrierea de log-uri: EventLogTraceListener si TextWriterTraceListener. Ambele clase sunt derivate din clasa abstracta TraceListener. Crearea de log-uri sub forma de text Clasa TextWriterTraceListener scrie output-ul primit ca text, fie catre un obiect de tip Stream, fie catre un obiect TextWriter. De exemplu, Console.Out este un TextWriter. Deci, se poate crea un TextWriterTraceListener care isi afiseaza intreg output-ul la consola. Stream si TextWriter scriu in fisiere text. Mecanismul crearii de fisiere log este urmatorul: - se creaza un fisier care va contine output-ul - se creaza o instance a clasei TextWriterTraceListener, specificandu-se fisierul anterior creat - se adauga instanta TextWriterTraceListener la colectia Listeners Exemplu: // se deschide fisierul log.txt // (daca acesta nu exista, se creaza) System.IO.FileStream firstLog = new System.IO.FileStream("C:\\log.txt",
Dezvoltare de aplicatii in Visual Studio .NET Page 87 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

System.IO.FileMode.OpenOrCreate); // se creaza un TraceListener care specifica // firstLog drept fisier tinta TextWriterTraceListener firstListener = new TextWriterTraceListener(firstLog); // se adauga firstListener la colectia Listeners Trace.Listeners.Add(firstListener); ! Daca fisierul specificat in constructorul clasei FileStream exista, deschiderea fisierului folosind IO.FileMode.OpenOrCreate va avea ca efect suprascrierea continutului existent. Utilizand IO.FileMode.Append, noile informatii se vor adauga la sfarsitul fisierului. O alta supraincarcare a constructorului TextWriterTraceListener permite simpla specificare a fisierului in care se va scrie. Folosind acest constructor, fisierul text va fi creat, daca nu exista sau, in caz contrar, noile informatii se vor adauga la sfarsit. Exemplu: TextWriterTraceListener secondListener = new TextWriterTraceListener("C:\\log.txt"); La executia codului oricaruia dintre aceste doua exemple, tot output-ul claselor Trace si Debug este scris in ascultatorul asociat. Pentru ca acest output sa fie scris efectiv in fisier, trebuie apelata metods Flush a clasei Trace: Trace.Flush(); O alta posibilitate este setarea proprietatii Trace.AutoFlush la true, ca in exemplul urmator. Aceasta tehnica determina golirea bufferului dupa fiecare scriere: Trace.AutoFlush = true; Salvarea output-ului clasei Trace intr-un EventLog EventLogTraceListener este clasa care permite salvarea output-ului clasei Trace intr-un obiect EventLog. Mecanismul este in mare parte similar scrierii intr-un fisier text. Se creaza un nou EventLog sau se specifica un log existent, se creaza un nou EventLogTraceListener si se adauga la colectia Listeners. Output-ul este salvat in EventLog ca obiecte EventLogEntry. Crearea unui nou event log si setarea numelui sub care va fi vizualizat: EventLog firstLog = new EventLog("Debug Log"); Se seteaza proprietatea Source pentru EventLog. Daca proprietatea Source nu este setata, se va genera o eroare. firstLog.Source = "Trace Output"; In continuare, se creaza o instanta EventLogTraceWriter care specifica noul log ca target pentru output-ul Trace. EventLogTraceListener logListener = new EventLogTraceListenerfirstLog);
Page 88 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

Daca este necesar, se poate seta proprietatea Trace.AutoFlush la valoarea true, sau se poate apela Trace.Flush dupa fiecare scriere. Comutatoare Trace (Trace Switches) De obicei, la debugging, se doreste primirea intregului output provenit din constructii care utilizeaza Trace si Debug. Totusi, dupa ce o aplicatie a fost compilata si lansata, este de dorit activarea tracing-ului numai in anumite cazuri. Comutatoarele Trace sunt comutatoare configurabile utilizate pentru a vizualiza constructii Trace. Acestea pot fi configurate prin intermediul aplicatiei prin modificarea fisierului de configurare al aplicatiei dupa ce aceasta a fost compilata si distribuita. Exista in .NET Framework doua tipuri de comutatoare Trace. Instantele clasei BooleanSwitch returneaza o valoare booleana. Deci, acestea sunt caracterizate prin doua stari: on sau off. Clasa TraceSwitch permite utilizatorilor sa seteze nivelul reprezentat de comutator, care poate fi o valoare intre 1 si 5, depinzand de tipul de output pe care utilizatorul doreste sa il primeasca. Ambele comutatoare necesita doi parametri in constructor: un NumeDeVizualizare, care reprezinta numele comutatorului in cadrul interfetei utilizator si o Descriere, care contine o scurta descriere a comutatorului. De exemplu: BooleanSwitch firstBooleanSwitch = new BooleanSwitch("Switch1", "Controls Data Tracing"); TraceSwitch firstTraceSwitch = new TraceSwitch("Switch2", "Controls Forms Tracing"); Clasa TraceSwitch contine cinci setari care reprezinta diverse nivele de eroare. Aceste setari sunt expuse prin intermediul proprietatii TraceSwitch.Level. Proprietatea poate fi setata la una din cele cinci valori ale enumeratorului TraceLevel: TraceLevel.Off. Desemneaza un TraceSwitch inactiv. Valoare 0. TraceLevel.Error. Asociat cu mesaje de eroare scurte. Valoare 1. TraceLevel.Warning. Pentru mesaje de eroare si warning-uri. Valoare 2. TraceLevel.Info. Pentru mesaje de eroare, warning-uri si mesaje informative scurte. Valoare 3. TraceLevel.Verbose. Reprezinta mesaje de eroare, warning-uri si descrieri detaliate ale executiei programului. Valoare asociata 4. Mai mult, clasa TraceSwitch expune patru proprietati booleene read-only care reprezenta nivelele Trace: TraceSwitch.TraceError TraceSwitch.TraceWarning TraceSwitch.TraceInfo TraceSwitch.TraceVerbose

Aceste proprietati corespund nivelurilor Trace cu aceleasi nume. La setarea proprietatii


Dezvoltare de aplicatii in Visual Studio .NET Page 89 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

TraceSwitch.Level, are loc o setare automata a acestor patru proprietati la un nivel corespunzator. De exemplu, daca TraceSwitch.Level este setat la TraceLevel.Info, TraceSwitch.TraceInfo va returna true. De asemenea, TraceSwitch.TraceError si TraceSwitch.TraceWarning vor returna true. Atunci cand in nivel este specificat, toate nivelurile inferioare vor returna true. Exemplu: // se presupune declararea anterioara // a unui obiect BooleanSwitch, // firstBooleanSwitch, si a unui obiect // TraceSwitch, firstTraceSwitch Trace.WriteIf(firstBooleanSwitch.Enabled == true, "Eroare"); Trace.WriteLineIf(firstTraceSwitch.TraceInfo == true, "Incompatibilitate de tipuri"); Dupa cum se poate observa, utilizatorul trebuie sa creeze propriile conexiuni intre instructiunile care folosesc metodele clasei Trace si valorile comutatorilor. Daca se folosesc metodele Trace.Write si Trace.WriteLine, valorile comutatorilorvor fi ignorate. Configurarea comutatoarelor Comutatoarele Trace pot fi activate sau dezactivate dupa ce aplicatia a fost compilata si distribuita. Configurarea manuala se poate realiza prin intermediul fisierului de configurare a aplicatiei (.config), un fisier XML care contine informatii despre aplicatie. Fisierul .config trebuie plasat in acelasi director cu executabilul aplicatiei si trebuie sa aiba acelasi nume: nume_aplicatie.exe.config. Nu toate aplicatiile au un fisier .config file, deci pentru a folosi comutatoare Trace, este necesara crearea unui fisier .config. In momentul in care aplicatia executa cod care creaza un comutator Trace, se verifica fisierul .config pentru a extrage eventualele informatii in legatura ce acel comutator. Fisierul este examinat cate o data pentru fiecare comutator. Orice modificare din fisierul de configurare legata de un anumit comutator necesita oprirea si apoi restartarea aplicatiei (fara o noua compilare!). La crearea unui comutator, unul dintre parametrii este NumeleDeVizualizare. Acesta este numele folosit pentru a configura comutatorul Trace in fisierul .config. La editarea fisierului .config, trebuie specificat nu numai numele comutatorului, ci si valoarea la care este setat. Valoarea trebuie sa fie intreaga. Pentru instante BooleanSwitch, zero reprezinta off, iar o valoare nenula reprezinta on. Pentru obiecte TraceSwitch, valorile zero, unu, doi, trei si patru corespund, respectiv, cu TraceLevel.Off, TraceLevel.Error, TraceLevel.Warning, TraceLevel.Info si TraceLevel.Verbose. Orice valoare mai mare decat 4 este tratata ca TraceLevel.Verbose. Daca aplicatia nu are un fisier .config, acesta se poate crea, de exemplu, folosind meniul Project, optiunea Add New Item. Fisierul trebuie sa aiba numele app.config, unde app este numele aplicatiei. Se editeaza in cadrul fisierului tagul de inceput, xml, si tagul configuration. Intre tagurile configuration, se declara comutatoarele si se seteaza valorile acestora.
Page 90 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

Exemplu: <system.diagnostics> <switches> <add name="firstBooleanSwitch" value="0" /> <add name="firstTraceSwitch" value="3" /> </switches> </system.diagnostics> In acest exemplu, firstBooleanSwitch este setat pe off, iar firstTraceSwitch este setat la TraceLevel.Info. Se modifica apoi valorile comutatoarelor, in functie de output-ul pe care dorim sa il vizualizam. Se pot adauga, eventual, comentarii adecvate, pentru a explica configurarile efectuate prin intermediul fisierului XML. <system.diagnostics> <switches> <!-- pentru a primi mesaje Trace, setati valoarea la un intreg nenul... --> <add name="firstBooleanSwitch" value="0" /> <!-- setati valoarea la 1,2,3 sau 4 pentru, respectiv, mesaje minimale, normale, detaliate sau complete --> <add name="firstTraceSwitch" value="3" /> </switches> </system.diagnostics> Sinteza - clasele Debug si Trace classes allow you to display and log messages - colectia Trace.Listeners: * DefaultTraceListener * TextWriterTraceListener * EventLogTraceListener - comutatoare Trace: BooleanSwitch, TraceSwitch - fisierul de configurare al aplicatiei

3. Exceptii
Chiar si dupa testarea si debugging-ul complet al aplicatiei, pot inca aparea erori la
Dezvoltare de aplicatii in Visual Studio .NET Page 91 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

run-time. De exemplu, o aplicatie care salveaza date pe un floppy disk va genera o eroare daca se incearca salvarea pe un disc care nu este disponibil. Chiar daca codul aplicatiei functioneaza, eroarea poate aparea. C# confera posibilitatea tratarii exceptiilor, adica scrierea de cod care sa permita continuarea executiei programului in astfel de situatii. Cum se trateaza exceptiile? Atunci cand o aplicatie intalneste o eroare la run-time, se arunca o exceptie. Exceptiile sunt instante ale unor clase specializate care mostenesc clasa de baza System.Exception. Clasele folosite pentru tratarea exceptiilor includ aspecte care faciliteaza diagnosticarea si managementul erorilor, cum ar fi: - proprietatea Message - contin o descriere a erorii, precum si alte informatii relevante despre aceasta - proprietatea StackTrace - ofera posibilitatea localizarii fragmentului de cod care a generat eroarea Utilizatorul poate crea propriile exceptii, derivand din clasa System.ApplicationException. La intalnirea unei erori la run-time, se creaza o instanta a exceptiei corespunzatoare erorii si este transmisa de la metoda care a generat eroarea catre metoda apelanta, prin intermediul stivei de apeluri. Daca, la nivelul metodei apelante, se intalneste o structura de tratare a exceptiei, aceasta este tratata. In caz contrar, exceptia este pasata urmatoarei metode apelante s.a.m.d. Daca nu se intalneste nici o astfel de structura, se efectueaza o tratare implicita a exceptiei. Aceasta are ca efect afisarea unui message box care vizualizeaza informatii legate de tipul exceptiei si alte informatii aditionale si apoi aplicatia se termina, fara a mai avea posibilitatea salvarii datelor sau tratarii erorii. Utilizand structuri de tratare a exceptiilor, se pot defini modalitati de tratare a erorilor sau, cel putin, de salvare a datelor inainte de inchiderea aplicatiei. Crearea unui exception handler Structurile de tratare a exceptiilor sunt implementate pentru metode specifice. Deci, fiecare metoda poate avea propriul exception handler, specific metodei respective si exceptiilor pe care le poate arunca. Metodele care pot arunca exceptii, cum ar fi metodele care implica acces la fisiere, trebuie sa implementeze structuri de tratare a exceptiilor. Acestea utilizeaza structuri de forma Try...Catch...Finally. Urmatorii trei pasi sunt specifici crearii unei structuri de tratare a exceptiilor: - se scrie codul care se va asocia handler-ului intr-un bloc try - se adauga unul sau mai multe blocuri catch pentru tratarea posibilelor exceptii - se adauga intr-un bloc finally cod care se va executa neconditionat (chiar daca se arunca sau nu exceptia) Exemplu: try { // cod care poate arunca exceptii
Page 92 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

} // urmeaza blocuri catch sau finally Un bloc try poate fi urmat de unul sau mai multe blocuri catch. Un bloc catch contine cod care va fi executat atunci cand exceptia va fi aruncata. Blocurile catch pot fi generice sau specifice exceptiei. Exemplu: un bloc catch generic. // bloc try... catch { // se executa la aruncarea oricarei exceptii } Se poate obtine o referinta a exceptiei care a fost aruncata. Acest lucru permite accesarea in cadrul rutinei de tratare a exceptiei a informatiei continute de exceptie. Se pot crea astfel blocuri catch multiple, specifice exceptiilor respective. Exemplu: // bloc try... catch (System.NullReferenceException e) { // se prinde o exceptie NullReferenceException } catch (System.Exception e) { // acest bloc va prinde toate celelalte exceptii } Blocurile finally contin cod care trebuie executat independent de faptul ca o exceptie a fost aruncata sau nu. Exemplu: public void Parsare(string sir) { try { double x; x = Double.Parse(sir); } catch (System.ArgumentNullException e) { // cod pentru cazul in care s-a
Dezvoltare de aplicatii in Visual Studio .NET Page 93 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

// aruncat o exceptie de acest tip } catch (System.Exception e) { // cod pentru tratarea celorlalte exceptii } finally { // cod care se va executa neconditionat } } Se va executa tot timpul un singur bloc catch. Se executa apoi blocul finally. Executia aplicatiei se continua cu urmatoarea linie de cod de dupa linia care a apelat rutina ce contine erori. ! Deoarece se executa un singur bloc catch, acestea trebuie scrise de la cel mai specific la cel mai putin specific bloc. Uneori, se doreste tratarea exceptiilor in cadrul rutinei care a apelat rutina ce contine blocul try (cu un nivel mai sus). Chiar si in acest caz, poate aparea necesitatea scrierii de cod care se va executa indiferent de aruncarea sau nu a exceptiei. Pentru aceasta, se omite blocul catch si se utilizeaza o structura try...finally. Exemplu: try { // codul metodei... } finally { // cod de clean-up } Aruncarea exceptiilor Se disting doua situatii specifice: - eroare care poate fi patial tratata local: se trateaza partial eroarea, apoi aceasta este transmisa stivei de apeluri - eroare care nu poate fi tratata local: se arunca o exceptie .NET sau o exceptie specifica

Page 94 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

Rearuncarea exceptiiilor Uneori, structura de tratare a exceptiilor, initial proiectata, nu este capabila sa trateze total o anumita exceptie. In acest caz, se utilizeaza cuvantul rezervat throw, in cadrul blocului catch, pentru rearuncarea exceptiei, adica transmiterea acesteia catre functia apelanta. Exemplu: try { // cod... } catch (System.NullReferenceException e) { // cod care testeaza posibilitatea tratarii exceptiei // daca aceasta nu va putea fi tratata complet, va fi rearuncata throw e; } In cazul unui catch generic, se utilizeaza cuvantul rezervat throw, fara a mai specifica o referinta catre o anumita exceptie: // bloc catch catch { throw; } La rearuncare, se poate crea o noua exceptie care contine mesaje informative, incluzand caracteristicile exceptiei initiale (in cazul in care dorim furnizarea de informatii suplimentare catre nivelul superior al aplicatiei, caruia i se transmite exceptia prin throw) Exemplu: // bloc catch catch (NullReferenceException e) { throw new NullReferenceException( "Obiectul X este neinitializat", e); } Exceptii definite de utilizator (custom) Un al doilea scenariu in care se poate arunca o noua exceptie este dezvoltarea de
Dezvoltare de aplicatii in Visual Studio .NET Page 95 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

componente. In cazul unei conditii care nu poate fi rezolvate, este indicata aruncarea unei exceptii definite de utilizator in cadrul aplicatiei client. ! Aruncarea exceptiilor nu trebuie sa devina o obisnuinta, ci trebuie efectuata numai in conditii exceptionale din program. Exceptiile de tip custom se creaza prin derivarea din clasa System.ApplicationException. Aceasta incapsuleaza intreaga functionalitate necesara unei exceptii, incluzand proprietatile Message, StackTrace si InnerException. Exemplu: public class CustomException: System.ApplicationException { KindOfObject an_object; public KindOfObject ErrorObject // proprietate { get { return an_object; // read-only } } // constructor - parametri de tip KindOfObject si string public CustomException(KindOfObject o, string a) : base(s) // se apeleaza constructorul din clasa de baza { // se seteaza proprietatea an_object = o; } } Dupa crearea unei clase exceptie, se poate arunca o noua instanta a acestei clase: Exemplu: KindOfObject a = new KindOfObject(); // cod care corupe obiectul a omis... throw new CustomException(a, "Obiectul a a fost corupt!"); Sinteza

Page 96 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

- exceptii - tratarea exceptiilor: try, catch, finally - rearuncarea exceptiilor - clasa System.ApplicationException

Dezvoltare de aplicatii in Visual Studio .NET

Page 97 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Section 7. Accesarea bazelor de date. ADO.NET - VI 1. Privire de ansamblu


O mare parte dintre aplicatii necesita un anumit tip de acces la baze de date. Aplicatiile desktop trebuie sa integreze modele care sa interactioneze cu baze de date centrale, colectii de date in format XML sau baze de date locale. Tehnologia ADO.NET de acces la baze de date permite accesul facil si eficient la colectiile de date, prin eficientizarea utilizarii resurselor. Acces la baze de date fara conexiune Tehnologiile anterioare functionau pe baza accesului la baze de date prin stabilirea unei conexiuni permanente pe durata efectuarii operatiilor specifice. Intr-un astfel de model, aplicatia creaza o conexiune la baza de date si aceasta este activa pana la sfarsitul aplicatiei, sau cel putin pe durata interogarii bazei de date. Dezavantaje ale conexiunilor permanente la baze de date: -conexiunile deschise la bazele de date sunt costisitoare din punct de vedere al resurselor -aplicatiile sunt dificil de scalat; o aplicatie care functioneaza foarte bine cu 5 clienti poate avea performante foarte scazute pentru 50 de conexiuni deschise simultan -utilizarea unui numar mare de conexiuni poate conduce la epuizarea licentelor disponibile pentru baza de date ADO.NET vine in intampinarea acestor probleme implementand un model de accesare a bazelor de date fara conexiune permanenta. In cadrul acestui model, conexiunile sunt initializate si mentinute active doar pe durata efectiva a executiei operatiilor cu baza de date. De exemplu, daca o aplicatie solicita anumite date dintr-o baza, conexiunea este deschisa doar pe durata incarcarii datelor in aplicatie, apoi conexiunea este inchisa. Similar, in cazul altor operatii. Arhitectura ADO.NET Accesul la date in ADO.NET implica doua entitati: -un DataSet, care stocheaza datele pe masina locala -un Data Provider, un set de componente care mediaza interactiunea dintre program si baza de date DataSet Un DataSet este o reprezentare non-conexiune, in-memory , a unei multimi de date. Poate fi gandit ca o copie locala a sectiunilor bazei de date utile aplicatiei. Datele pot fi incarcate intr-un DataSet din orice sursa valida, cum ar fi un server SQL, o baza de date Microsoft Access sau un fisier XML. DataSet-ul persista in memorie, iar datele continute pot fi modificate, manipulate, independent de baza se date. La momentul oportun, DataSet-ul poate actiona ca un sablon pentru modificarea bazei de date
Page 98 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

centrale. Un obiect DataSet contine o colectie de zero sau mai multe obiecte DataTable, fiecare dintre acestea fiind o reprezentare in-memory a unei tabele. Structura unui obiect DataTable este definita de colectia DataColumns, care enumereaza coloanele dintr-o tabela si de colectia Constraint care enumereaza legaturile, constrangerile din tabela. Aceste doua colectii definesc schema tabelei. Un obiect DataTable contine, de asemenea, o colectie DataRows, care contine datele corespunzatoare din DataSet. Un DataSet contine o colectie DataRelations care permite crearea asocierilor intre liniile unei tabele si liniile altei tabele. Colectia DataRelations enumereaza un set de obiecte DataRelation care definesc relatiile intre tabelele din DataSet. Data Provider Legatura cu baza de date este creata si mentinuta de un data provider. Acesta este format dintr-un set de componente interconectate care coopereaza pentru a furniza date intr-o maniera eficienta, performanta. Exista patru tipuri de furnizori de date in .NET Framework: - SQL Server .NET Data Provider - OleDb .NET Data Provider - ODBC Data Provider - Oracle Data Provider Fiecare dintre acesti furnizori contin versiuni ale urmatoarelor clase generice: - Connection - realizeaza conexiunea la baza de date - Command - executa o comanda asupra unei surse de date: comenzi non-query (INSERT, UPDATE sau DELETE) sau comanda SELECT, returnand un DataReader - DataReader - desemneaza o multime de inregistrari (recordset) bazata pe conexiune, de tip forward-only , read-only - DataAdapter - populeaza un DataSet sau DataTable non-conexiune si executa modificari Mecanismul accesarii bazelor de date in ADO.NET este urmatorul: - un obiect Connection stabileste o conexiune intre aplicatie si baza de date. Aceasta conexiune poate fi accesata direct de un obiect Command sau de un obiect DataAdapter. Obiectul Command executa o comanda asupra bazei de date. Daca se returneaza valori multiple, se utilizeaza un obiect DataReader care va contine datele returnate. Aceste date pot fi procesate direct de de aplicatie. Alternativ, se poate utiliza un DataAdapter pentru a popula un obiect DataSet. Modificarile asupra bazei de date se pot efectua prin intermediul unui obiect Command sau unui obiect DataAdapter. Connection Reprezinta conexiunea curenta la baza de date. Tipuri de conexiuni:
Dezvoltare de aplicatii in Visual Studio .NET Page 99 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

- SqlConnection - pentru conectarea la SQL Server 7 sau versiuni ulterioare - OleDbConnection - conexiuni la diverse tipuri de baze de date - ODBCConnection - OracleConnection Un obiect Connection contine toate informatiile necesare deschiderii unui canal de comunicatie cu baza de date in cadrul proprietatii ConnectionString. Sunt incorporate, de asemenea, metode pentru facilitarea tranzactiiilor. Command Este reprezentat de doua clase: SqlCommand si OleDbCommand Utilizat pentru a efectua apeluri de proceduri stocate sau de comenzi SQL asupra bazei de date sau pentru a returna tabele. Metode: - ExecuteNonQuery - executa comenzi care nu returneaza inregistrari - INSERT, UPDATE, DELETE - ExecuteScalar - returneaza o singura valoare dintr-o interogare - ExecuteReader - returneaza o multime rezultat, sub forma unui obiect DataReader DataReader - contine un recordset bazat pe conexiune, forward-only, read-only - obiectele DataReader nu pot fi instantiate direct, sunt returnate ca rezultat al metodei ExecuteReader a unui obiect Command (SqlCommand - SqlDataReader etc) - o singura linie din recordset este in memorie la un moment dat, deci se foloseste un minim de resurse, dar este necesara mentinerea activa a unui obiect Connection pe durata de viata a obiectului DataReader DataAdapter DataAdapter este clasa din nucleul tehnologiei ADO.NET, bazata pe mecanismul non-conexiune. - faciliteaza comunicarea intre baza de date si DataSet - populeaza obiectele DataTable sau DataSet ori de cate ori se apeleaza metoda Fill - metoda Update inregistreaza modificarile, efectuate local, in baza de date Proprietati: - SelectCommand - contine textul sau obiectul asociat comenzii care se va efectua la apelul metodei Fill - InsertCommand - contine textul sau obiectul asociat comenzii prin care se insereaza o linie in tabela - DeleteCommand - similar, pentru stergerea unei linii din tabela - UpdateCommand - similar, pentru modificarea valorilor din baza de date La apelul metodei Update, se copie modificarile din DataSet in baza de date,
Page 100 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

executandu-se una din comenzile reprezentate de InsertCommand, DeleteCommand sau UpdateCommand.

2. Comenzi SQL
Structured Query Language (SQL) - limbajul universal al bazelor de date relationale. SQL poate fi utilizat pentru a prelua si filtra inregistrari din baza de date, pentru a adauga inregistrari, pentru a sterge inregistrari din baza de date si pentru a modifica valorile unei inregistrari existente. Exista 4 constructii de baza: - SELECT - pentru selectarea anumitor inregistrari din baza de date - UPDATE - pentru modificarea unor inregistrari in baza de date - INSERT - pentru inserarea unei noi linii intr-o tabela - DELETE - pentru eliminarea unei inregistrari

SELECT Sintaxa: SELECT lista_campuri FROM lista_tabele WHERE (Optional) filtre ORDER BY (Optional) criteriu_de_ordonare Sintaxa minimala: SELECT campuri FROM tabele Exemplu: SELECT StudentID, Prenume FROM Studenti; Selectarea tuturor campurilor dintr-o tabela: SELECT * FROM Studenti WHERE - filtreaza inregistrarile dupa un anumit criteriu SELECT * FROM Studenti WHERE Prenume = 'Vlad' O conditie mai complexa: SELECT * FROM Studenti WHERE Prenume = 'Vlad' AND Nume = 'Tepes' Operatorul IN:

Dezvoltare de aplicatii in Visual Studio .NET

Page 101 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

SELECT Prenume, Nume FROM Studenti WHERE OrasNatal IN ('Deva', 'Bod') Operatorul BETWEEN: SELECT * FROM Studenti WHERE Media BETWEEN 8.5 AND 10 Sabloane: SELECT * FROM Studenti WHERE Prenume LIKE 'Vla_' Sablonul se va potrivi pentru prenume de 4 litere care incep cu Vla. SELECT * FROM Studenti WHERE Prenume LIKE 'Vla%' Se vor returna studentii al caror prenume incepe cu Vla. Clausa ORDER BY SELECT * FROM Studenti ORDER BY Medie DESC DELETE Elimina in mod ireversibil inregistrari din baza de date. DELETE FROM Studenti WHERE Prenume = 'Vlad' AND Nume = 'Tepes'; Clauza WHERE are acceasi sintaxa ca in cazul comenzii SELECT. UPDATE Sintaxa generala: UPDATE tabela SET coloana1 = valoare1, ... ,coloanaN = valoareN [WHERE predicat] Exemplu: UPDATE Studenti SET Nume = 'Diaconu' WHERE Prenume = 'Maria' AND Nume = 'Popescu' Operatorii IN, BETWEEN, LIKE si clauza WHERE au aceeasi utilizare ca in cazul comenzii SELECT. INSERT INTO Insereaza o inregistrare intr-o tabela. Sintaxa: INSERT INTO tabela [(coloana1, ..., coloanaN)] VALUES (valoare1, ... , valoareN) Exemplu:

Page 102 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

INSERT INTO Studenti (Prenume, Nume, Varsta, Sex) VALUES ('Vlad', 'Tepes', 500, 'M')

3. Accesarea datelor
Conectarea la o baza de date Exista mai multe posibilitatati de conectare la o baza de date: - crearea unei conexiuni in mod design utilizand uneltele grafice din Visual Studio .NET Conexiunile curente sunt vizualizate in fereastra Server Explorer, ca noduri copil ale nodului Data Connections. Adaugarea unei conexiuni la proiect se face prin drag and drop - in fereastra Server Explorer se executa clic dreapta pe nodul Data Connections si se selecteaza Add Connection, setandu-se proprietatile conexiunii in fereastra Data Link care se deschide - varianta manuala - adaugarea unui obiect Connection din Toolbox sau declararea si instantierea obiectului in cod - crearea unei conexiuni direct in cod - se seteaza manual proprietatea ConnectionString. Exemplu: // declararea si instantierea unui obiect OleDbConnection OleDbConnection firstConnection = new OleDbConnection(); // setarea proprietatii connection string firstConnection.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;DataSource=" + "C:\\Mdb_databases\\studenti.mdb"; Utilizarea comenzilor asupra bazelor de date Un obiect Command contine o referinta la o procedura stocata in cadrul bazei de date sau o comanda SQL si poate executa comanda respectiva prin intermediul unei conexiuni active la baza de date. Un astfel de obiect contine toate informatiile necesare executiei comenzii, inclusiv o referinta la conexiunea activa, specificarea efectiva a comenzii si, eventual, parametrii solicitati de comanda. Clase de tip Command: -OleDbCommand - interactiunea cu diverse tipuri de baze de date -SqlCommand - interactiunea cu SQL Server 7 sau cu versiuni mai noi Deoarece obiectele de tip Command implica existenta unei conexiuni active si nu necesita interactiunea cu un DataAdapter, ele furnizeaza o modalitate rapida si eficienta de interactiune cu baza de date. Actiuni posibile:
Dezvoltare de aplicatii in Visual Studio .NET Page 103 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

- executia unor comenzi care nu returneaza inregistrari - INSERT, UPDATE, DELETE - executia unor comenzi care returneaza o singura valoare - executia unor comenzi de tipul Database Definition Language (DDL), cum ar fi CREATE TABLE sau ALTER - interactiunea cu un DataAdapter pentru a returna un DataSet - extragerea unei multimi de inregistrari utilizand direct o instanta a unui obiect DataReader - returnarea unei multimi de inregistrari in format XML (numai utilizand SqlCommand) - returnarea unei multimi de inregistrari din mai multe tabele sau constructii care desemneaza comenzi Crearea si configurarea unei comenzi (Command): - prin atasarea la proiect (in mod design) a unei proceduri stocate pe serverul de baze de date, utilizand fereastra Server Explorer - prin selectarea unui obiect Command din Toolbox, adaugarea acestuia in proiect si configurarea sa folosind fereastra Properties - prin declararea, instantierea si configurarea manuala a unui obiect Command, in cod La crearea manuala a unui obiect Command, trebuie setate proprietatile Connection, CommandType si CommandText. CommandType - tipul comenzii specificate de proprietatea CommandText. Exista 3 valori posibile: - Text - valoarea proprietatii CommandText va fi tratata drept comanda SQL (una sau mai multe, separate prin punct si virgula (;), comenzile executandu-se secvential) - StoredProcedure - valoarea proprietatii CommandText trebuie sa contina, in acest caz, numele unei proceduri stocate pe serverul de baze de date; executia acestei comenzi determina executia procedurii specificate - TableDirect - se indica numele unei (mai multor) tabela(e); se va returna continutul tabelei(lor) specificate Proprietatea Connection trebuie setata la o conexiune existenta, avand tipul adecvat (SqlCommand - SqlConnection, OleDbCommand - OleDbConnection). Executia comenzilor Se face prin intermediul a trei metode specifice: -ExecuteNonQuery - specifica comenzilor de tip INSERT, UPDATE si DELETE (comenzi non-query) si comenzilor de tip DDl, CREATE si ALTER -ExecuteScalar - returneaza prima coloana a primei linii din multimea de inregistrari returnate de comanda -ExecuteReader - returneaza un obiect DataReader (forward-only, read-only), cu acces rapid la date, utilizat atunci cand nu este necesara modificarea datelor in baza

Page 104 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

-ExecuteXmlReader (numai pentru SqlCommand)-returneaza un obiect XmlReader (forward-only, read-only), in format XML Parametri Frecvent, comenzile implica utilizarea parametrilor, valorile anumitor entitati din cadrul unei comenzi nefiind cunoscute pana in momentul executiei aplicatiei. Exemplu de constructie SQL cu parametri: SELECT * FROM Studenti WHERE (Prenume LIKE [sir]) sir este o valoare furnizata de utilizator la executie. Parametrii sunt valori care inlocuiesc entitati din cadrul textului unei comenzi. Fiecare parametru este identificat efectiv in program ca o instanta a clasei OleDbParameter sau SqlParameter. Ei sunt stocati in proprietatea Parameters a clasei Command si, la run-time, valorile sunt citite din proprietate si plasate in constructia SQL sau furnizate procedurii. Colectia Parameters contine obiecte Parameter, avand urmatoarele proprietati mai importante: - DbType - Direction - cu valorile posibile Input, Output, InputOutput, or ReturnValue - OleDbType - ParameterName - Precision - Scale - Size - SourceColumn - SourceVersion - SQLType - Value Exemplu: // setarea valorii primului parametru din colectie prin index OleDbCommand1.Parameters[0].Value = "Primul parametru..."; // setarea valorii unui parametru identificat prin nume OleDbCommand1.Parameters["nume_parametru1"].Value = "valoare..."; Proprietatile Precision, Scale si Size stabilesc dimensiunea si acuratetea parametrilor pentru parametri numerici, binari sau de tip string. SourceColumn si SourceVersion sunt utilizati atunci cand parametrul este legat de o coloana intr-un DataTable. Proprietatea Value contine valoarea reprezentata de parametru. Pentru obiecte OleDbCommand, atunci cand CommandType este setata la valoarea

Dezvoltare de aplicatii in Visual Studio .NET

Page 105 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Text, parametri sunt specificati in cadrul comenzii SQL prin caractere (?). De exemplu: SELECT StudentId, Prenume, Nume FROM Studenti WHERE (Prenume = ?) AND (Nume = ?) Parametrii sunt inserati in ordinea in care apar in colectia Parameters. Obiectele de tip SqlCommand utilizeaza parametri identificati prin nume, mai precis, in cadrul comenzii SQL, un parametru este identificat prin simbolul @, urmat de valoarea proprietatii ParameterName a parametrului respectiv: SELECT StudentId, Prenume, Nume FROM Studenti WHERE (Nume = @Nume) Executia unei comenzi non-query utilizand un obiect Command - INSERT, UPDATE, DELETE, CREATE TABLE si ALTER - CommandType - setata la StoredProcedure sau Text - se specifica eventualii parametri - se apeleaza metoda Command.ExecuteNonQuery // apel identic pentru OleDbCommand sau SqlCommand firstCommand.ExecuteNonQuery(); Utilizarea unui obiect Command pentru a returna o valoare singulara - CommandType - setata la StoredProcedure sau Text - se specifica eventualii parametri - se apeleaza metoda Command.ExecuteScalar Object o; o = firstCommand.ExecuteNonScalar(); DataReader Pentru a utiliza un obiect Command care are asociate interogari care returneaza valori multiple, se apeleaza metoda ExecuteReader pentru a returna un DataReader. Un DataReader este un obiect care gestioneaza date intr-o maniera read-only, forward-only, eficienta si rapida. Un DataReader itereaza inregistrarile returnate intr-o multime rezultat. Viteza de accesare a datelor este mare, dar limitarile provin din faptul ca maniera de prelucrarea este read-only, forward-only si nu se pot face modificari asupra bazei de date. Mai mult, este necesara mentinerea unei conexiuni active pe durata efectuarii acestor operatii, ceea ce determina un consum destul de semnificativ de resurse. Crearea unui DataReader Un DataReader nu poate fi creat explicit. El este instantiat printr-un apel al metodei
Page 106 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

ExecuteReader a unui obiect Command. Exemplu: // firstOleDbCommand si firstSqlCommand sunt doua // obiecte OleDbCommand, respectiv SqlCommand create anterior System.Data.OleDb.OleDbDataReader firstOleDbReader; System.Data.SqlClient.SqlDataReader firstSqlReader; // creare si asignare firstOleDbReader = firstOleDbCommand.ExecuteReader(); firstSqlReader = firstSqlCommand.ExecuteReader(); Accesarea datelor utilizand un DataReader Iterarea inregistrarilor se poate realiza folosind metoda Read: while (firstDataReader.Read()) { // code care se va executa pentru fiecare inregistrare } Valorile coloanelor individuale din fiecare inregistrare pot fi accesate prin index sau prin nume. De exemplu: while (firstDataReader.Read()) { object o = firstDataReader[3]; object oo = firstDataReader["StudentID"]; } Toate valorile furnizate de un DataReader sunt preluate ca obiecte, dar se pot retuna valori si in maniera puternic tipizata. Orice obiect DataReader mentine un acces exclusiv asupra conexiunii utilizate, de accea, dupa efectuarea operatiilor, trebuie apelata metoda Close a obiectului DataReader. firstDataReader.Close(); Accesarea coloanelor de date cu un DataReader Exemplu: afisarea valorilor unei coloane la consola. firstConnection.Open(); // se creaza un DataReader System.Data.OleDb.OleDbDataReader firstReader = firstOleDbCommand.ExecuteReader(); // se apeleaza Read

Dezvoltare de aplicatii in Visual Studio .NET

Page 107 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

while (firstReader.Read()) { // accesarea coloanelor prin nume Console.WriteLine(firstReader["Prenume"].ToString()); } firstReader.Close(); // se inchide DataReader-ul firstConnection.Close(); // se inchide conexiunea DataReader - tipizare Clasa DataReader expune, de asemenea, metode tipizate de extragere a informatiilor, de tip Get: bool bool_var; bool_var = myDataReader.GetBoolean(3); // se utilizeaza numarul coloanei Metoda GetOrdinal int StudID; string student; // se preia indexul coloanei, utilizand numele acesteia StudID = firstDataReader.GetOrdinal("StudentID"); student = firstDataReader.GetString(StudID); Multimi rezultat multiple Daca proprietatea CommandType a obiectului Command este setata la valoarea Text, se pot returna mai multe multimi de inregistrari drept rezultat. Exemplu de comanda SQL care va furniza rezultate multiple: SELECT * FROM Studenti; SELECT * FROM Profesori; Un DataReader returneaza implicit prima multime de inregistrari in mod automat. Pentru a accesa urmatoarea multime rezultat, se apeleaza metoda NextResult. Exemplu: do {
Page 108 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

while (firstReader.Read()) { // iterarea inregistrarilor multimii rezultat curente } } while (firstReader.NextResult()); Executia interogarilor SQL Ad Hoc Sunt comenzi ale caror interogari se construiesc la run-time. Exemplu 1: string cmd; cmd = "SELECT * FROM Studenti WHERE Nume = '" + sir_de_caractere + "'"; Exemplu 2: public void DeleteRecord(string sir) { string cmd; cmd = "DELETE * FROM Studenti WHERE Nume = '" + sir + "'"; OleDbCommand firstCommand = new OleDbCommand(cmd, firstConnection); firstConnection.Open(); firstCommand.ExecuteNonQuery(); firstConnection.Close(); } ! Atentie la validarea continutului variabilelor utilizate in constructia comenzilor SQL! Crearea si configurarea obiectelor DataAdapter Asigura legatura intre o sursa de date si un DataSet, gestionand schimbul de date intre aceste doua entitati. Un DataAdapter incorporeaza functionalitatea necesara extragerii datelor, popularii unui DataSet si aplicarii modificarilor in baza de date. Clase .Net Framework: SqlDataAdapter, OleDbDataAdapter. Un DataAdapter este utilizat, in general, pentru gestionarea schimbului de date dintre un obiect DataTable continut intr-un DataSet si o singura tabela sursa. Modalitati de creare a unui DataAdapter: - utilizand fereastra Server Explorer (se adauga, prin drag and drop, o tabela in design-ul aplicatiei) - folosind Data Adapter Configuration Wizard (utilizand componenta DataAdapter din Toolbox)

Dezvoltare de aplicatii in Visual Studio .NET

Page 109 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

- manual

Extragerea multimilor de date utilizand obiecte DataAdapters Un DataSet este o reprezentare in-memory a datelor extrase la un moment dat din baza de date si nu implica existenta unei conexiuni active. Un DataSet poate contine tabele multiple, relatii intre tabele si constrangeri. Pot fi incluse intr-un DataSet date provenind de la diverse surse de date. Obiectele DataAdapter gestioneaza interactiunea dintre obiectele DataSet si bazele de date. Popularea unui DataSet cu date se efectueaza prin apelarea metodei Fill a obiectului DataAdapter. Metoda Fill executa instructiunile specificate de proprietatea SelectCommand, in cadrul conexiunii specificate de proprietatea Connection si populeaza DataSet-ul cu datele returnate. Exemplu: DataSet firstDataSet = new DataSet(); firstDataAdapter.Fill(firstDataSet); Popularea unui DataSet care contine mai mumte tabele: - se creaza o instanta DataSet - se creaza un DataAdapter pentru fiecare tabela din DataSet - se apeleaza secvential metoda Fill pentru fiecare DataAdapter, specificand DataSet-ul drept target Obiecte DataSet tipizate Exemplu 1: obiecte DataSet netipizate. string stud; stud = (string)dsFacultate.Tables["Studenti"].Rows[0]["StudentID"]; Exemplu 2: obiecte DataSet tipizate. string stud; stud = dsFacultate.Studenti[0].StudentID; Un DataSet tipizat este, de fapt, o instanta a unei clase noi, derivare din clasa DataSet. Structura noii clase este bazata pe un fisier XSD (XML Schema Definition) care defineste structura DataSet-ului, incluzand numele tabelelor si al coloanelor. Se pot crea DataSet-uri tipizate numai in momentul in care se cunoaste structura exacta a datelor. Pentru a genera un DataSet se poate selecta optiunea Generate Dataset din meniul Data. Sinteza - Connection

Page 110 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

- Command * ExecuteNonQuery * ExecuteScalar * ExecuteReader - parametri - DataReader - DataAdapter

4. Mai multe despre DataSet


DataSet reprezinta componenta centrala a arhitecturii ADO.NET, fiind o reprezentare in-memory a datelor. Obiectele DataSet pot fi populate cu date prin intermediul obiectelor DataAdapter, din fisiere XML, din fisiere text sau prin constructii programatice. Crearea si popularea obiectelor DataSet fara a utiliza un DataAdapter Exemplu de creare a unui DataSet prin scrierea de cod: DataSet firstDataSet = new DataSet(); DataTable firstTable = new DataTable(); firstDataSet.Tables.Add(firstTable); In cadrul unui DataSet, tabelele sunt gestionate prin intermediul colectiei Tables, de asemenea, in cadrul unei tabele, colectia Columns manipuleaza coloanele. DataColumn StudentColumn = new DataColumn("Student"); firstDataSet.Tables[0].Columns.Add(StudentColumn); Dupa adaugarea tuturor coloanelor dintr-o tabela, se pot adauga inregistrari, prin intermediul obiectelor DataRows. DataRow firstRow; // nu se instantiaza, se apeleaza metoda NewRow firstRow = firstDataSet.Tables[0].NewRow(); Un obiect DataRow poate fi populat cu date provenind dintr-o colectie, un array sau input-ul utilizatorului. Exemplu: // SourceString este un ArrayList ... firstRow[i] = SourceString[i];
Dezvoltare de aplicatii in Visual Studio .NET Page 111 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Adaugarea unui DataRow la colectia Rows a unui DataTable: firstDataSet.Tables[0].Rows.Add(firstRow); Accesarea fisierelor text In multe aplicatii, datele sunt stocate in fisiere text de dimensiuni mari, fiind necesara citirea datelor din aceste fisiere si popularea unui DataSet ADO.NET. Spatiul de nume utilizat este System.IO. Un fisier text poate fi manipulat prin intermediul clasei System.IO.StreamReader. Un format foarte des utilizat de structurare a informatiilor in cadrul fisierelor text este .CSV. Exemplu: vezi resurse - Curs6, 1_flat_file.txt. DataRelation Un obiect DataRelation reprezinta o relatie intre doua coloane din doua tabele diferite. De exemplu, pot exista doua tabele, Studenti si Optionale, fiecare continand o coloana StudentID. Fiecare student apare o singura data in tabela Studenti, dar poate aparea de mai multe ori in tabela Optionale. StudentID in tabela Optionale specifica studentul din tabela Studenti care a ales optionalul respectiv. Acesta este un exemplu de relatie one-to-many . Obiectele DataRelation sunt utilizate pentru identificarea acestor tipuri de relatii. Obiectele DataRelation ale unui DataSet particular sunt continute in proprietatea Relations a DataSet-ului. Un obiect DataRelation este creat specificand numele relatiei, coloana parinte si coloana copil. Intr-un DataSet tipizat, ambele coloane trebuie sa aiba acelasi tip. Exemplu: // coloana1, coloana2 sunt doua instante DataColumns DataRelation oRelatie = new DataRelation("Data Relation 1", coloana1, coloana2); Adaugarea la colectia Relations a DataSet-ului unDataSet.Relations.Add(oRelatie); Accesarea inregistrarilor implicate intr-o relatie Se utilizeaza metodele GetChildRows si GetParentRow ale clasei DataRow. Exemplu: DataRow[] ChildRows; DataRow ParentRow; // se returneaza liniile din Optionale care sunt in relatie cu // linia 1 din tabela Studenti ChildRows = unDataSet.Tables["Studenti"].Rows[1].GetChildRows(oRelatie); // linia din Studenti care este in relatie cu linia 3 din // tabela Studenti

Page 112 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

ParentRow = unDataSet.Tables["optionale"]. Rows[2].GetParentRow(oRelatie); Constrangeri Constrangerile definesc reguli de adaugare si manipulare a datelor in tabele. Tipuri de constrangeri: UniqueConstraint (o coloana nu trebuie sa aiba valori duplicate) si ForeignKeyConstraint (defineste regulile de modificare a liniilor copil atunci cand se modifica o linie parinte). Constrangerile sunt manipulate prin intermediul colectiei Constraints a tabelei si sunt active atunci cand proprietatea EnforceConstraints a DataSet-ului este setata la true. Exemple: myDataColumn.Unique = true; // se creaza o canstrangere UniqueConstraint Creare manuala: UniqueConstraint myConstraint = new UniqueConstraint(myDataColumn); myDataTable.Constraints.Add(myConstraint); Stabilirea unei chei multiple: DataColumn[] KeyColumns = new DataColumn[2]; KeyColumns[0] = StudentsTable.Columns["FirstName"]; KeyColumns[1] = StudentsTable.Columns["LastName"]; UniqueConstraint myConstraint = new UniqueConstraint(KeyColumns); StudentsTable.Constraints.Add(myConstraint); Constrangeri Foreign Key ForeignKeyConstraint myConstraint = new ForeignKeyConstraint(Studenti.Columns["StudentID"], Optionale.Columns["StudentID"]); Adaugarea la colectia Constraints: Studenti.Constraints.Add(myConstraint); Reguli ForeignKeyConstraint referitoare la relatia parent-child : UpdateRule: invocata la modificarea unei linii parinte DeleteRule: invocata la stergerea unei linii parinte AcceptRejectRule: invocata la apelarea metodei AcceptChanges a obiectului DataTable caruia ii apartine constrangerea.

Dezvoltare de aplicatii in Visual Studio .NET

Page 113 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Fiecare dintre aceste reguli sunt expuse ca proprietati ale obiectului care desemneaza constrangerea si pot avea diverse valori: Cascade, None, SetDefault, SetNull. Fiecare DataSet mentine doua versiuni ale informatiilor continute: versiunea curenta, care contine copia client a DataSet-ului si evidenta modificarilor efectuate, si versiunea originala, care contine datele in formatul in care se gaseau la prima populare a DataSet-ului. La apelul metodei Update a obiectului DataAdapter, valorile initiale sunt utilizate de comenzi UPDATE, INSERT si DELETE pentru a executa modificarile in baza de date. Editarea datelor - prin intermediul controalelor din interfata utilizator conectate la anumite entitati din baza de date - prin constructii programatice (vezi exemplu) // accesare prin index sau nume aDataRow[3] = "M"; aDataRow["Studenti"] = "Vlad"; Metoda RejectChanges anuleaza modificarile efectuate asupra unui DataRow: aDataRow.RejectChanges(); AcceptChanges - se aplica modificarile efectuate asupra obiectului DataRow apelant: with the edited version. aDataRow.AcceptChanges(); Se poate determina starea unui obiect DataRow analizand proprietatea RowState. Valori posibile: - Unchanged - Modified - Added - Deleted - Detached Obiectele DataTable si DataSet contin, de asemenea, metode RejectChanges si AcceptChanges, care permit refuzarea sau acceptarea modificarilor din DataTable, respectiv DataSet. Modificarea bazei de date firstDataAdapter.Update(); secondOtherDataAdapter.Update(); Metoda Update are ca efect copierea versiunii client a datelor in baza de date.
Page 114 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

// se pot modifica numai anumite entitati din DataSet aDataAdapter.Update(someDataSet); aDataAdapter.Update(someDataTable); aDataAdapter.Update(someDataRows); Tranzactii Reprezinta un set de operatii asupra bazei de date care se efectueaza ca un ansamblu: daca una dintre operatii esueaza, tranzactia nu se efectueaza. Exemplu: // tranzactiile trebuie incluse in blocuri //Try...Catch...Finally System.Data.OleDb.OleDbTransaction aTransaction = null; try { aConnection.Open(); aTransaction = aConnection.BeginTransaction(); // crearea unei tranzactii si asocierea la conexiune // adaugare de operatii la tranzactie Update1.Transaction = aTransaction; Update2.Transaction = aTransaction; // se executa comenzile Update1 si Update2 Update1.ExecuteNonQuery(); Update2.ExecuteNonQuery(); // daca nu s-au lansat exceptii, se efectueaza tranzactia aTransaction.Commit(); } catch (Exception ex) { // tranzactia nu s-a efectuat, s-au lansat exceptii aTransaction.Rollback(); } finally { // se inchide conexiunea aConnection.Close();

Dezvoltare de aplicatii in Visual Studio .NET

Page 115 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

} Tratarea erorilor de Update Clasele SqlDataAdapters si OledbDataAdapters contin un eveniment RowUpdated care se lanseaza dupa modificare unei linii, inainte de lansarea eventualelor exceptii. Acest eveniment trebuie tratat in mod adecvat. Evenimentul RowUpdated contine o instanta SqlRowUpdatedEventArgs sau OleDbRowUpdatedEventArgs, care furnizeaza informatii utile in determinarea erorii care a survenit. Proprietati ale RowUpdatedEventArgs: Command Errors RecordsAffected Row Status Continue ErrorsOccurred SkipAllRemainingRows SkipCurrentRow

Exemplu: private void aDataAdapter_RowUpdated(object sender, System.Data.SqlClient.SqlRowUpdatedEventArgs e) { // se verifica proprietatea Status if (e.Status == UpdateStatus.ErrorsOccurred) { MessageBox.Show("A survenit o eroare de tipul " + e.Errors.ToString()+". Informatii aditionale : " + e.Errors.Message); // se ignora modificarile de la linia curenta, // continuandu-se cu urmatoarele e.Status = UpdateStatus.SkipCurrentRow; } }

5. Accesarea, vizualizarea si filtrarea datelor

Page 116 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

Vizualizarea datelor reorezinta o parte vitala a multor aplicatii. Pentru aceasta, inregistrarile din cadrul unei surse de date trebuie asociate cu o serie de controale de pe o forma (binding), permitandu-se accesaream parcurgerea si modificarea datelor. Data Binding - crearea unei relatii intre un data provider si un data consumer. Un data provider poate fi orice obiect la care poate fi legat un consumator. In .NET Framework, orice obiect care implementeaza interfata IList poate fi furnizor de date. Aceasta nu include obiecte ADO.NET, cum ar fi DataSet, DataTable sau DataColumn, care pot fi prin constructie furnizori de date. Parcurgerea datelor se realizeaza prin intermediul unui obiect CurrencyManager asociat implicit fiecarei surse de date. Data Consumer Orice control conectat, intr-un anumit fel, la o sursa de date poate fi considerat consumator. Exemplu: o forma care permite introducerea de date. In cadrul acesteia, anumite controale, cum ar fi TextBox, CheckBox, ListBox, etc. sunt conectate la anumite coloane dintr-un DataSet. Tipuri de legare (binding): simpla si complexa. Un control simplu legat identifica o singura inregistrare. De exemplu, un Label asociat unei coloane dintr-un DataTable. Legarea complexa permite asocierea controlului cu mai multe inregistrari. Exemplu: ListBox sau ComboBox asociate unei coloane dintr-un DataTable sau un DataGrid asociat unui DataTable sau chiar unui DataSet. Crearea unui control simplu legat Prin intermediul proprietatii DataBindings. Aceasta este o instanta a clasei ControlBindingsCollection care gestioneaza controalele legate la surse de date. Pentru un anumit control, in fereastra Properties se poate expanda nodul din dreptul proprietatii DataBindings si selecta un anumit data provider. Legare la executie Scenarii: - modificarea unei surse de date pentru un control legat - identificarea sursei de date a unui control legat - legarea unui control la un array sau colectie care este instantiata la run time Exemplu: // legarea unei etichete la coloana StudentID // din tabela Students LabelID.DataBindings.Add("Text", DataSet1.Students, "StudentID"); // Text este proprietatea care va fi legata

Dezvoltare de aplicatii in Visual Studio .NET

Page 117 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Exemplu 2: String[] studenti = new String[3]; studenti[0] = "Aionesei"; studenti[1] = "Agafitei"; studenti[2] = "Georgescu"; TextBox1.DataBindings.Add("Text", studenti, ""); // al treilea parametru este vid, sursa de date // nu este complexa, nu are membri Eliminarea unei legaturi: Label1.DataBindings.Remove(Label1.DataBindings["Text"]); // se elimina legatura existenta pentru proprietatea // Text a etichetei Label1 Eliminarea tuturor legaturilor asociate unui control - prin apelarea metodei Clear: Label1.DataBindings.Clear(); Navigare (Data Currency) Fiecare forma gestioneaza obiecte CurrencyManager asociate surselor de date prin intermediul unui obiect central BindingContext. Accesarea unui anumit currency manager se face prin intermediul proprietatii BindingContext, specificand sursa de date a obiectului in cauza. De exemplu: this.BindingContext[DataSet1.Students]; Inregistrarea curenta poate fi setata prin intermediul proprietatii Position a unui BindingContext: // tabela Studenti face parte din DataSet1 // setarea inregistrarii curente la prima // inregistrare din sursa de date this.BindingContext[DataSet1.Studenti].Position = 0; // setarea inregistrarii curente la a //treia inregistrare din sursa de date this.BindingContext[DataSet1.Studenti].Position = 2; // avansarea la urmatoarea inregistrare this.BindingContext[DataSet1.Studenti].Position ++; Navigarea prin colectia de date dintr-o forma Windows Se seteaza proprietatea Position a membrului BindingContext dorit.
Page 118 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

Este imposibila deplasarea inaintea primei inregistrari sau dupa ultima inregistrare. Exemplu: utilizarea evenimentului PositionChanged din cadrul clasei CurrencyManager pentru dezactivarea butoanelor de navigare atunci cand linia curenta este pozitionata pe prima sau ultima inregistrare. // handler public void OnPositionChanged(object sender, System.EventArgs e) { if (this.BindingContext[DataSet1.Students].Position == 0) // dezactivarea butonului Back BackButton.Enabled = false; else // activare BackButton.Enabled = true; // similar pentru butonul Forward } // linia de adaugat in constructorul formei this.BindingContext[DataSet1.Customers].PositionChanged += new EventHandler(this.OnPositionChanged); Legare complexa Prin setarea proprietatii DataSource: DataGrid1.DataSource = DataSet1.Students; Proprietatea DisplayMember desemneaza numele coloanei la care se leaga controlul. Exemplu: ComboBox1.DataSource = DataSet1.Students; ComboBox1.DisplayMember = "StudentID"; Filtrarea si sortarea datelor DataView permite lucrul cu un subset al datelor continute intr-un DataTable. Aceasta clasa contine metode pentru sortarea si filtrarea datelor, permitand si modificarea obiectului DataTable pe care il reprezinta. Crearea unui DataView Se specifica obiectul DataTable care va fi filtrat. Exemplu: DataView aDataView = new DataView(aDataTable); Alternativa:
Dezvoltare de aplicatii in Visual Studio .NET Page 119 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

DataView anotherDataView = new DataView(); anotherDataView.Table = anotherDataTable; Un DataView poate fi creat utilizand Toolbox-ul. Filtrare si sortare in DataSet Exemple: sortare dupa una sau mai multe coloane. aDataView.Sort = "StudentID"; anotherDataView.Sort = "Prenume, Nume"; Filtrare: prin setarea proprietatii RowFilter. Se specifica un string care reprezinta o expresie prin care se selecteaza inregistrari. Exemplu: aDataView.RowFilter = "Nume = 'Popescu'"; In general, expresiile RowFilter urmeaza sintaxa SQL. Proprietatea DataView.RowState permite filtrarea obiectelor DataRow in functie de starea lor. Valori posibile ale acestei proprietati (se poate seta la una sau mai multe din aceste valori): - Unchanged - Added - Deleted - OriginalRows - CurrentRows - ModifiedCurrent - ModifiedOriginal Editarea datelor folosind un DataView Proprietati care stabilesc daca datele dintr-un DataView pot fi editate. - AllowDelete - se permite sau nu stergerea - AllowEdit - se permite sau nu editarea - AllowNew - se permite sau nu adaugarea de noi linii Clasa DataViewManager O instanta a acestei clase este asociata cu un DataSet, gestionand obiecte DataView pentru diverse tabele. Exemplu: DataViewManager aDataViewManager = new DataViewManager(aDataSet); DataViewManager anotherDataViewManager = new DataViewManager();

Page 120 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

anotherDataViewManager.DataSet = anotherDataSet; Colectia DataViewSettings contine proprietatile DataView asociate tabelelor dintr-un DataSet. Exemplu: aDataViewManager.DataViewSettings["Students"].RowFilter = "Prenume = 'Costel'"; Sinteza: - data binding - data provider - data consumer - CurrencyManager - DataView - RowFilter si Sort - DataViewManager

Dezvoltare de aplicatii in Visual Studio .NET

Page 121 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Section 8. GDI+. Controale utilizator. Atribute - VII 1. GDI+


Interfetele grafice sunt entitati frecvent intalnite in lumea utilizatorilor. Desi exista foarte multi adepti ai interfetelor text, cu siguranta nimeni nu va ezita sa foloseasca o interfata grafica prietenoasa, cu ajutorul careia sa interactioneze cu usurinta cu aplicatia care o pune la dispozitie. Microsoft Windows este, in principiu, o interfata grafica. Utilizatorii interactioneaza cu aplicatiile prin intermediul ferestrelor, care sunt reprezentari grafice ale optiunilor si datelor din cadrul aplicatiilor. Pentru o utilizare facila, aplicatiile trebuie sa isi expuna functionalitatea prin intermediul interfetei grafice. Desi .NET Framework pune la dispozitie un set complex de controale grafice, apare adesea necesitatea vizualizarii propriului continut grafic si crearii unui aspect personalizat pentru un anumit control. Toate acestea implica utilizarea Graphic Device Interface (GDI). In .NET Framework, aceasta interfata este gestionata prin intermediul GDI+. Clasele .NET Framework care expun functionalitatea GDI+ sunt incluse in spatiul de nume System.Drawing si in spatiile de nume asociate. GDI+ este o implementare managed a GDI, utilizata pentru vizualizarea informatiilor graphice pe ecranul computerului. Aceasta interfata utilizeaza clase divizate in sase spatii de nume, pe baza functionalitatii. Spatii de nume System.Drawing

Spatiu de nume System.Drawing System.Drawing.Design System.Drawing.Drawing2D System.Drawing.Imaging System.Drawing.Printing System.Drawing.Text

Continut O mare parte din clasele implicate in randarea continutului grafic pe ecran. Este principalul spatiu de nume utilizat in programarea grafica. Clase care expun functionalitati aditionale pentru operatiile grafice. Clase care transpun efecte vizuale avansate. Clase care permit manipularea avansata a fisierelor imagine. Clase care faciliteaza printarea continuturilor textuale sau grafice. Clase care faciliteaza manipularea avansata a fonturilor.

Majoritatea claselor care faciliteaza randarea grafica sunt incluse in spatiul de nume System.Drawing. Pentru randare avansata, se utilizeaza clase din spatiul de nume System.Drawing.2D, iar pentru implementarea unor functionalitati de printare, clase din System.Drawing.Printing. Clasa Graphics Clasa Graphics, localizata in spatiul de nume System.Drawing, este principala clasa utilizata pentru randare grafica. Un obiect Graphics reprezinta suprafata grafica a unui element vizual, cum ar fi o forma, un control sau un obiect Image. Deci, o forma are asociat un obiect Graphics utilizat pentru a desena in interiorul formei; un control are asociat un obiect Graphics folosit pentru a desena in interiorul controlului, s.a.m.d.
Page 122 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

Obiectul Graphics este, deci, responsabil de randarea elementelor vizuale. Deoarece fiecare obiect Graphics trebuie asociat cu un element vizual, nu se poate efectua o instantiere directa a acestuia. In schimb, un obiect Graphics se creaza direct pornind de la elementul vizual respectiv. Clasele derivate din Control (incluzand Form) expun o metoda CreateGraphics care permite preluarea unei referinte a obiectului Graphics asociat controlului. Urmatorul exemplu demonstreaza crearea unui obiect Graphics pentru o forma aForm: System.Drawing.Graphics firstGraphics; firstGraphics = aForm.CreateGraphics(); Obiectul Graphics astfel creat poate fi ulterior utilizat pentru vizualizarea elementelor grafice pe forma. In cazul imaginilor, se poate utiliza metoda statica Graphics.FromImage pentru a crea un obiect Graphics asociat unui obiect particular Image. Obiectul Image poate fi orice obiect derivat din clasa Image, de exmplu, Bitmap. Exemplu: crearea unui obiect Bitmap utilizand un fisier existent si apoi asocierea unui obiect Graphics: Bitmap aImage = new Bitmap("C:\\someImage.bmp"); System.Drawing.Graphics secondGraphics; secondGraphics = Graphics.FromImage(aImage); ! Imaginea nu trebuie sa fie vizibila pentru a se putea crea obiectul Graphics asociat sau pentru a il putea manipula. Coordonate Randarea se efectueaza pe ecran in regiunea determinata de marginile controlului. Acesta regiune este masurata in coordonate bidimensionale, constand in valori (x, y). Implicit, originea coordonatelor sistemului pentru fiecare control este data de coltul din stanga sus, care are coordonatele (0,0). Coordonatele sunt masurate in pixeli. Spatiul de nume System.Drawing contine o varietatea de structuri utilizate pentru a descrie locatii sau regiuni din acdrul sistemului de coordonate. Tabelul urmator sumarizeaza aceste structuri: Structura Point PointF Size SizeF Rectangle Descriere un punct reprezentat prin doua valori Integer (int), x si y un punct reprezentat prin doua valori Single (float), x si y o suprafata rectangulara specificata printr-o pereche de intregi Height si Width o suprafata rectangulara specificata printr-o pereche de valori Single (float), reprezentand Height si Width o reprezentare a unei regiuni rectangulare prin patru valori intregi Top, Bottom, Left si Right
Page 123 of 180

Dezvoltare de aplicatii in Visual Studio .NET

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

RectangleF

o reprezentare a unei regiuni rectangulare prin patru valori flotante Top, Bottom, Left si Right

Exemplu: // conversie de la o structura punct reprezentata // prin valori float la un punct // reprezentat prin intregi Point aPoint; PointF aPointF = new PointF(10.9F,31.23F); aPoint = new Point((int)aPointF.X, (int)aPointF.Y); Exemplu: crearea unei structuri Rectangle prin specificarea unui structuri Size si a unei structuri Point: Point pointOrigin = new Point(100, 50); Size aSize = new Size(40, 50); // se creaza un Rectangle 40 pe 50 avand // coltul din stanga sus specificat de Point(100,50) Rectangle aRectangle = new Rectangle(pointOrigin, aSize); Desenarea formelor Orice obiect Graphics incapsuleaza o varietate de metode pentru randarea formelor simple si complexe pe ecran. Acestea sunt incadrate in doua categorii generale: - metode de tip Draw - linii, arce, contururi ale formelor; - metode de tip Fill - forme solide - dreptunghiuri, elipse sau poligoane Metode pentru desenarea structurilor bazate pe linii: Metoda DrawArc DrawBezier DrawBeziers DrawClosedCurve DrawCurve DrawEllipse DrawLine DrawLines
Page 124 of 180

Descriere deseneaza un arc reprezentand o portiune a unei elipse deseneaza o curba Bezier deseneaza o serie de curbe Bezier deseneaza o curba inchisa pe baza unei serii de puncte deseneaza o curba deschisa pe baza unei serii de puncte deseneaza o elipsa pe baza unui dreptunghi deseneaza o linie conectata prin doua puncte deseneaza o serie de linii pe baza unui tablou de puncte
Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

DrawPath DrawPie DrawPolygon DrawRectangle DrawRectangles

deseneaza un obiect GraphicsPath care reprezinta o forma complexa deseneaza o forma de tip pie ca sector de elipsa deseneaza un poligon pe baza unei serii de puncte deseneaza un dreptunghi deseneaza o serie de dreptunghiuri

Metode pentru desenarea formelor solide: Metoda FillClosedCurve FillEllipse FillPath FillPie FillPolygon FillRectangle FillRectangles FillRegion Descriere deseneaza o curba inchisa specificata printr-un tablou de puncte deseneaza o elipsa plina reda un obiect solid GraphicsPath reprezentand o forma complexa deseneaza o forma pie solida reda un poligon solid specificat printr-un tablou de puncte deseneaza un dreptunghi solid o serie de dreptunghiuri solide umple un obiect Region care corespunde de obicei unei forme complexe

Fiecare dintre aceste metode primesc diversi parametri care specifica coordonatele punctelor si locatia formelor care se vor desena. Fiecare metoda necesita un obiect care sa efectueze randarea. Pentru structuri liniare, se utilizeaza un obiect Pen. Pentru forme solide, se folosesc obiecte Brush. Color, Brush si Pen Aceste tipuri sunt utilizate pentru a determina modul de randare a unei imagini grafice. Obiectele Pen redau linii si arce, obiectele Brush redau forme solide, iar obiectele de tip Color definesc culorile folosite in vizualizare. Color Structura Color reprezinta o singura culoare si se gaseste in spatiul de nume System.Drawing. Culorile individuale sunt derivate din patru valori: Alpha - gradul de transparenta, Red, Green si Blue, cuprinse intre 0 to 255. O noua culoare se poate crea folosind metoda Color.FromArgb: Color aColor; aColor = Color.FromArgb(128, 255, 12, 50); Pentru culori opace, parametrul Alpha se omite:

Dezvoltare de aplicatii in Visual Studio .NET

Page 125 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Color aColor; aColor = Color.FromArgb(255, 12, 43); Exista un set de culori definite in .NET Framework. Exemplu: Color aColor; aColor = Color.Tomato; Brush Obiectele Brush sunt sunt folosite pentru randarea formelor solide. Toate aceste obiecte sunt derivate din clasa abstracta de baza Brush si furnizeaza implementari diferite ale obiectelor utilizate pentru randarea obiectelor solide, in diferite stiluri. Tipuri de obiecte Brush: Tip SolidBrush TextureBrush HatchBrush Spatiu de nume System.Drawing System.Drawing System.Drawing.Drawing2D Descriere o singura culoare solida umple obiecte inchise cu o imagine hasureaza interiorul obiectelor combina doua culori pe baza unui gradient randarea de efecte vizuale complexe

LinearGradientBrush System.Drawing.Drawing2D PathGradientBrush System.Drawing.Drawing2D

Crearea unui SolidBrush: SolidBrush sBrush = new SolidBrush(Color.PapayaWhip); Constructorii altor tipuri de obiecte brush necesita parametri aditionali. Pen Obiectele de acest tip sunt utilizate pentru a desena linii si arce, putand fi utilizate pentru aplicarea unei varietati de efecet speciale structurilor bazate pe linii. Clasa Pen nu poate fi mostenita direct. Exemplu de creare a unui obiect Pen: Pen aPen = new Pen(Color.BlanchedAlmond); Grosimea implicita este 1. O alta grosime poate fi specificata prin intermediul constructorului, ca in exemplul urmator: Pen aPen = new Pen(Color.Red, 5); Crearea unui pen pe baza unui obiect brush existent: Pen aPen = new Pen(aBrush); Anumite aplicatii trebuie sa aiba interfete grafice care sa aiba aceleasi caracteristici ca
Page 126 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

si interfata sistemului pe care va rula aplicatia. .NET Framework pune la dispozitie culori specifice sistemului prin intermediul clasei SystemColors. Aceasta clasa contine un set de membri statici care contini culorile utilizate de sistem. Deci, se pot defini elemente ale interfetei utilizator care sa foloseasca culori preluate la executie din setul de culori puse la dispozitie de sistemul pe care ruleaza aplicatia. Exemplu: Color aColor = SystemColors.HighlightText; Similar, exista clasele SystemPens si SystemBrushes. Randarea formelor simple Toate metodele care redau forme simple, bazate pe linii, necesita un obiect valid de tip Pen. Metodele specializate in vizualizarea formelor solide au ca parametru un obiect Brush. Exemplu: // un obiect Rectangle Rectangle aRectangle = new Rectangle(0, 0, 30, 50); // crearea obiectului Graphics asociat formei Graphics g = this.CreateGraphics(); g.DrawRectangle(SystemPens.ControlDark, aRectangle); // dealocarea obiectului Graphics g.Dispose(); Exemplu 2: SolidBrush aBrush = new SolidBrush(Color.MintCream); Graphics g = this.CreateGraphics(); // o elipsa care va fi inscrisa intr-un dreptunghi Rectangle aRectangle = new Rectangle(0, 0, 50, 20); g.FillEllipse(aBrush, aRectangle); g.Dispose(); aBrush.Dispose(); ! Este indicata apelarea metodei Dispose pentru obiectele Graphics, deoarece acestea sunt mari consumatoare de resurse. Vizualizarea textelor Se utilizeaza metoda DrawString a obiectelor Graphics, care vizualizeaza un string reprezentand un text pe ecran. Textul este vizualizat folosit un font specificat si este redat printr-un obiect Brush. Trebuie specificate, de asemenea, coordonatele la care se va face afisarea. Exemplu: Graphics g = this.CreateGraphics();
Dezvoltare de aplicatii in Visual Studio .NET Page 127 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

String aString = "Salut, anul 4!"; Font aFont = new Font("Times New Roman", 40, FontStyle.Regular); g.DrawString(aString, aFont, SystemBrushes.Highlight, 20, 30); g.Dispose(); Vizualizarea formelor complexe Vizualizarea formelor complexe necesita parcurgerea catorva pasi in proiectare. Principalul obiect utilizat pentru randarea formelor complexe este o instanta a clasei GraphicsPath. Membru al spatiului de nume System.Drawing.Drawing2D, clasa GraphicsPath poate descrie orice fel de obiecte inchise sau multimi de forme complexe. Crearea unui GraphicsPath Se utilizeaza unul din constructorii clasei GraphicsPath. Exemplu 1: constructorul fara parametru. GraphicsPath aPath = new Drawing2D.GraphicsPath(); Exemplul 2: constructor cu parametri care specifica un set de puncte din cadrul formei si caracteristicile anumitor sectiuni ale formei: GraphicsPath aPath = new GraphicsPath(new Point[] {new Point(1, 1), new Point(38, 70), new Point(33, 20)}, new byte[] { (byte)PathPointType.Start, (byte)PathPointType.Line, (byte)PathPointType.Bezier}); Dupa creare, se pot adauga figuri obiectului GraphicsPath. O astfel de figura reprezinta o forma inchisa - forme simple (elipse, dreptunghiuri) sau complexe (curbe neregulate s.a.). Clasa GraphicsPath contine mai multe metode care permit adaugarea de figuri la obiectul respectiv. Aceste metode sunt sintetizate in tabelul urmator: Metoda AddClosedCurve AddEllipse AddPath AddPie AddPolygon AddRectangle AddRectangles AddString Descriere adauga o curba inchisa descrisa printr-un tablou de puncte adauga o elipsa adauga o instanta GraphicsPath la instanta curenta adauga o forma de tip pie adauga un poligon descris printr-un tablou de puncte adauga un dreptunghi adauga o multime de dreptunghiuri adauga o reprezentare grafica a unui string, cu un font specificat

Page 128 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

O figura poate fi creata si pas cu pas, prin adaugare de linii, arce si curbe (se utilizeaza metodele din tabelul de mai jos). Primul pas in crearea figurii este aplearea metodei GraphicsPath.StartFigure. Se adauga apoi elemente la figura respectiva, apelandu-se, in final, metoda GraphicsPath.CloseFigure pentru inchiderea figurii. Exemplu: GraphicsPath somePath = new GraphicsPath(); somePath.StartFigure(); // adaugare de elemente ... somePath.CloseFigure(); O figura pentru care s-a apelat StartFigure, fara a se apela CloseFigure, va ramane deschisa. ! La run-time, o figura deschisa se va inchide prin adaugarea unei linii intre primul si ultimul punct ale figurii. Metode GraphicsPath pentru adaugarea de elemente liniare la o figura: Metoda AddArc AddBezier AddBeziers AddCurve AddLine AddLines Sinteza - Graphics - Pen, Brush, Color - SystemPens, SystemBrushes, SystemColors - GraphicsPath Descriere adauga un arc adauga o curba Bezier adauga o serie de curbe Bezier adauga o curba descrisa de un tablou de puncte adauga o linie adauga o serie de linii conectate

2. Controale utilizator
Programarea Windows Forms este bazata pe controale. Controalele permit incapsularea unui set de unitati functionale discrete si furnizeaza utilizatorului o reprezentare grafica a acestor functionalitati. .NET Framework pune la dispozitie o varietate de unelte si tehnici care ajuta la crearea controalelor personalizate din cadrul aplicatiei utilizator.
Dezvoltare de aplicatii in Visual Studio .NET Page 129 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

In .NET Framework, controale sunt clase specializate care includ cod pentru randarea unei interfete grafice. Exista mai multe surse posibile pentru controalele utilizate in aplicatii. Controalele incluse in Microsoft Visual Studio .NET acopera o gama larga de functionalitati si permit dezvoltarea de aplicatii cu o serie intreaga de caracteristici. Pot fi utilizate, de asemenea, o serie de controale implementate de alti programatori sau pot fi folosite controale ActiveX. Controale personalizate - privire de ansamblu Toate controalele din .NET Framework sunt derivate direct sau indirect din clasa de baza Control. Acesta clasa pune la dispozitie functionalitatea de baza necesara unui control. De exemplu, clasa Control implementeaza logica tratarii input-ului utilizatorului prin interactiunea cu tastatura si mouse-ul si logica interactiunii cu mediul Windows. In plus, un control derivat din clasa Control contine un set de proprietati, metode si evenimente comune tuturor controalelor. Crearea functionalitatii specifice controlului, precum si vizualizarea grafica a acestuia sunt sarcini ce revin utilizatorului. Exista trei tehnici de baza pentru crearea unui control: - derivarea dintr-un control existent : reprezinta cel mai rapid si facil mod de dezvoltare a unui control; noul control contine toata functionalitatea controlului de baza si poate actiona ca baza pentru alte controale, mostenind reprezentarea vizuala a controlului din care de deriveaza. Aceasta tehnica este folosita atunci cand este necesara duplicarea functionalitatii unui control Windows Forms si adaugarea de functionalitati specifice sau definirea unui aspect vizual nou. Majoritatea controalelor Windows Forms pot fi utilizate drept clase de baza pentru crearea de controale noi. Exemplu: crearea unui TextBox cu validare implicita (functionalitate specifica) sau crearea unui Button cu colturile rotunjite (aspect vizual specific). - derivarea din clasa UserControl: tehnica utilizata pentru crearea de controle care inglobeaza un numar de controale Windows Form sub forma unei singure unitati functionale. Se deriveaza din clasa UserControl care pune la dispozitie o functionalitate de baza, la care se pot adauga proprietati, metode, evenimente si alte controale. Dupa adaugarea controalelor componente, se scrie cod pentru implementarea functionalitatilor specifice. Rezultatul este un control proiectat de utilizator, alcatuit dintr-un set de controale care se comporta ca o singura unitate functionala. Majoritatea modificarilor vizuale aduse controalelor componente constau in simpla configurare si repozitionare a controalelor constituente. - derivarea din clasa Control: utilizata in cazul in care este necesara o interfata vizuala sau/si o functionalitate complexa care nu pot fi implementate prin intermediul unui user-control sau a unui control derivat. Controalele personalizate sunt derivate din clasa Control, clasa de baza a tuturor controalelor. Pasii necesari dezvoltarii unuii astfel de control pot fi consumatori de timp, oferind insa posibilitatea implementarii unui control personalizat, cu functionalitate si aspect vizual specifice.

Adaugarea de membri unui control

Page 130 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

Se pot adauga proprietati, metode, campuri si evenimente controalelor in aceeasi maniera in care se adauga membri unei clase. Pot fi declarati membri cu diverse nivele de acces, iar forma care gazduieste controlul va putea apela orice membru public al controlului. Proprietatile publice adaugate controalelor sunt vizualizate automat in fereastra Properties la design-time. Deci, acestea pot fi editate si configurate de orice utilizator al aplicatiei. Daca nu se doreste vizualizarea unei proprietati in fereastra Properties, se seteaza atributul Browsable la false. Exemplu: [System.ComponentModel.Browsable(false)] public int CustomNumber { // cod... } Crearea unui control derivat Un control derivat contine toata functionalitatea pusa la dispozitie de clasa de baza si poate servi, de asemenea, ca baza pentru alte controale. Un control derivat se creaza specificand un control Windows Forms drept clasa de baza pentru noul control. Acesta pastreaza acelasi aspect si functionalitate ca si controlul de baza. Exemplu: public class DerivedButton : System.Windows.Forms.Button { // implementare... } Adaugarea de noi functionalitati unui control derivat Acest lucru se poate realiza prin suprascrierea membrilor clasei de baza. De exemplu, pentru crearea unui text box care accepta numai numere ca input, se suprascrie metoda OnKeyPress, ca in exemplul urmator: // nu se permite transmiterea focusului unui alt // control daca se introduc alte caractere // decat cifre in textBox public class NumberBox : System.Windows.Forms.TextBox { protected override void OnKeyPress(KeyPressEventArgs e) { if (char.IsNumber(e.KeyChar) == false) e.Handled = true;
Dezvoltare de aplicatii in Visual Studio .NET Page 131 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

} } Modificarea aspectului vizual al unui control Se suprascrie motoda OnPaint. Pentru modificarea formei unui control, se seteaza proprietatea Region a controlului in metoda OnPaint. Region este o clasa care descrie o regiune regulata sau neregulata de pe ecran, similar unui GraphicsPath. O regiune poate fi creata pe baza unui GraphicsPath. Exemplu - un buton care este vizualizat sub forma unui string: public class TextButton : System.Windows.Forms.Button { protected override void OnPaint(PaintEventArgs pe) { System.Drawing.Drawing2D.GraphicsPath aPath = new System.Drawing.Drawing2D.GraphicsPath(); aPath.AddString("Cool", Font.FontFamily, (int)Font.Style, 72, new PointF(0, 0), StringFormat.GenericDefault); // se creaza o noua regiune pornind de la aPath Region aRegion = new Region(aPath); // se asigneaza regiunea proprietatii Region a controlului this.Region = aRegion; } } ! - butonul astfel creat nu este rectangular, ci are forma determinata de literele cuvantului "Cool". Pentru a executa clic pe buton, trebuie utilizata regiunea determinata de literele acestui cuvant; - exista controale pentru care nu se lanseaza evenimentul Paint, fiind desenate de forma parinte. Crearea unui user-control Un astfel de control consta in unul sau mai multe controale legate sub forma unei singure unitati. Controalele componente pot fi adaugate controlului utilizator la design-time. Un control utilizator poate fi adaugat la proiect utilizand optiunea Add User Control din meniul Project. Exemplu, crearea unui control utilizator format din doua TextBox-uri si un Label care
Page 132 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

afiseaza automat suma celor doua numere introduse in TextBox-uri. Pentru aceasta, se suprascrie metoda OnKeyPress pentru fiecare TextBox: protected override void OnKeyPress(object sender, KeyPressEventArgs e) { // se verifica daca s-a tastat o cifra if (char.IsNumber(e.KeyChar) == false) e.Handled = true; Label1.Text = (int.Parse(TextBox1.Text) + int.Parse(TextBox2.Text)).ToString(); } La crearea unui control utilizator in Visual Studio .NET, controalele constituente sunt tratate implicit ca private. Pentru a permite altor utilizatori sa modifice proprietatile controalelor constitutive, acestea trebuie expuse prin intermediul proprietatilor controlului utilizator parinte. Exemplu: public Color ButtonColor // proprietate a controlului parinte // Button1 este un constituent al acestuia { get { return Button1.BackColor; } set { Button1.BackColor = value; } } Expunerea completa a unui control constituent se poate efectua prin intermediul proprietatii Modifiers, disponibila in fereastra Properties, numai in faza de design. Crearea unui control custom Controalele de acest tip confera cel mai inalt nivel de configurabilitate si personalizare, dar sunt si cele mai consumatoare de timp in faza de dezvoltare. Deoarece clasa Control nu furnizeaza o reprezentare vizuala, utilizatorul are sarcina de a scrie cod care sa redea vizualizarea grafica a controlului. In cazurile in care este necesara o reprezentare vizuala complexa, aceasta faza poate ocupa cea mei mare perioada de
Dezvoltare de aplicatii in Visual Studio .NET Page 133 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

timp in procesul de dezvoltare a controlului. Procesul vizualizarii unui control pe o suprafata se numeste painting. Atunci cand un control primeste instructiuni de desenare, se lanseaza evenimentul Paint. Aceasta determina executia handlerelor evenimentului Paint. Pentru clasa Control, handlerul implicit al evenimentului Paint este metoda OnPaint. Metoda OnPaint are un singur argument, o instanta PaintEventArgs. Acesta clasa contine informatii despre suprafata de desenare disponibila controlului. Membri PaintEventArgs: - Graphics - un obiect Graphics care reprezinta suprafata de desenare a controlului - ClipRectangle - o suprafata rectangulara disponibila controlului pentru desenare. La prima desenare a controlului, obiectul ClipRectangle defineste marginile intregului control. Uneori, anumite regiuni ale controlului pot fi acoperite de alte controale. La redesenarea controlului, obiectul ClipRectangle reprezinta numai regiunea care trebuie redesenata, fiind folosit, deci, pentru determinarea marimii controlului. Coordonatele, in interiorul unui control, sunt masurate relativ la coltul din stanga sus, considerat drept origine. Exemplu de custom-control: // o elipsa protected override void OnPaint(PaintEventArgs e) { Brush aBrush = new SolidBrush(Color.Red); Rectangle clientRectangle = new Rectangle(new Point(0,0), this.Size); e.Graphics.FillEllipse(aBrush, clientRectangle); } La redimensionarea unui control, membrul ClipRectangle este modificat, dar controlul nu este neaparat redesenat. Pentru redesenarea controlului la fiecare redimensionare, se apeleaza metoda Control.SetStyle pentru a seta flagul ResizeRedraw la true. Acest apel trebuie plasat in constructor. Exemplu: SetStyle(ControlStyles.ResizeRedraw, true); Ca alternativa, se poate apela metoda Refresh, ori de cate ori se doreste redesenarea: Refresh(); Sinteza: - controale derivate (derived controls) - controale utilizator (user controls) - controale personalizate (custom controls)

Page 134 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

3. Gestionarea, utilizarea, configurarea controalelor


Adaugarea controalelor la Toolbox Dupa crearea unui control, acesta va putea fi utilizat in proiecte de dezvoltare. Adaugarea unui control in Toolbox va permite utilizarea acestuia in proiecte. Adaugarea unui control la Toolbox se face prin intermediul optiunii Customize Toolbox. (.NET Framework Controls, Browse, se alege fisierul DLL sau EXE asociat controlului). Controlul este, in urma selectarii fisierului asociat, adaugat in lista afisata de optiunea Customize Toolbox. Controlul este adaugat astfel la Toolbox. In Visual Studio .NET 2003, controalele utilizator construite ca parte a unui proiect sunt adaugate automat in Toolbox, in lista de controale My UserControls. Pot fi adaugate si controale derivate sau controale custom folosind procedura de mai sus. Asocierea unui Toolbox Bitmap unui control Visual Studio .NET asociaza un icon implicit care apare in dreptul controlului in Toolbox. Se poate specifica un icon dorit, in format bitmap. Pentru aceasta se utilizeaza clasa ToolboxBitmapAttribute. Acasta este o clasa specializata bazata pe atribute, o clasa care contine metadate despre un control. Folosind ToolboxBitmapAttribute, se poate specifica fie un bitmap de dimensiune 16 pe 16, fie type. Daca se specifica type, controlul va avea asociat acelasi bitmap ca si controlul .NET avand acel tip (type) specificat. Exemple care ilustreaza cele doua situatii: // icon specificat printr-un fisier bitmap [ToolboxBitmap(@"C:\some.bmp")] public class SomeUserControl : Control { // implementare... } [ToolboxBitmap(typeof(Label))] public class UserLabelControl : Label { // implementare... } Debugging-ul unui control In orice proiect de dezvoltare, poate aparea necesitatea debugging-ului controalelor. Deoarece acestea nu sunt proiecte de sine statatoare, ele trebuie gazduite in cadrul unei forme Windows pe durata debugging-ului.
Dezvoltare de aplicatii in Visual Studio .NET Page 135 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Controlul trebuie construit (built) inainte de a putea fi testat. Odata ce proiectul care contine controlul a fost construit, controlul poate fi adaugat la o forma si testat ca in cazul oricarui alt proiect. In cazul in care controlul face parte dintr-un proiect executabil, precum un proiect Windows Forms, se poate adauga o noua forma la proiect care sa gazduiasca controlul. In cazul in care controlul face parte dintr-un proiect non-executabil, cum ar fi o librarie de clase sau un control Windows, trebuie creat un proiect aditional pentru a putea testa controlul (se adauga proiectul continand controlul ca referinta a noului proiect creat - Solution Explorer - References - Add Reference ). Gazduirea unui control in Internet Explorer Orice control Windows Forms poate fi incarcat in Internet Explorer; pentru a profita de aceasta posibilitate, se creaza pagini HTML care contin controale Windows Forms. Pentru a putea incarca un control in Internet Explorer, acesta trebuie instalat in Global Assembly Cache (vezi cursul 10...) sau trebuie plasat in acelasi director virtual in care se gaseste pagina HTML care utilizeaza controlul. Adaugarea unui control Windows Forms intr-o pagina HTML se efectueaza utilizand tagul <OBJECT>. Acest tag specifica faptul ca un obiect compilat va fi inserat in pagina. Tagul <OBJECT> examineaza proprietatea classid a obiectului pentru a identifica tipul obiectului care va fi incarcat. Deci, in proprietatea classid se va specifica propriul control Windows Forms. classid-ul unui control Windows Forms consta in doua parti. Prima parte este calea catre fisierul care contine controlul, cea de-a doua este numele complet al controlului. Separarea celor doua parti se face utilizand caracterul #. Exemplu: utilizarea tagului <OBJECT> pentru un control din fisierul ControlLibraryExample.dll (aflat in acelasi director ca si pagina HTML) avand numele ControlLibraryExample.firstControl: <OBJECT id="firstControl" classid="http:ControlLibraryExample.dll#ControlLibraryExample.firstControl VIEWASTEXT> </OBJECT> Sinteza - adaugarea controalelor utilizator la Toolbox prin intermediul optiunii Customize Toolbox - asocierea unui bitmap controalelor utilizand clasa ToolboxBitmapAttribute - debugging-ul controalelor - incarcarea controalelor in Internet Explorer utilizand tagul <OBJECT>

Page 136 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

4. Atribute
Atributele sunt entitati din program utilizate pentru a asocia informatii, cum ar fi adnotari, tipurilor C# definite de utilizator. Aceste informatii sunt arbitrare, adica nu sunt dependente de limbaj in sine, utilizatorul avand libertatea de a crea si asocia informatii de orice tip. Utilizari: - furnizarea de informatii la design-time (documentatii) - furnizarea de informatii la run-time (de exemplu numele unei coloane dintr-o tabela) - stabilirea de comportamente la run-time (stabilirea caracteristicilor unei entitati din program, configurari etc) Definirea atributelor Un atribut este, de fapt, o clasa derivata din clasa System.Attribute. Exemplu: public class FirstAttribute: Attribute { public FirstAttribute(string s) { // cod... } // alte campuri, proprietati, metode... } Aplicare: [First("John")] // lipseste prefixul Attribute public class Test { // clasa Test are asociat atributul First, cu valoarea "John" } Interogarea atributelor Se utilizeaza mecanismul de Reflection - determinarea dinamica la run-time a caracteristicilor tipurilor dintr-o aplicatie. Exemplu: determinarea listei tuturor claselor, tipurilor si metodelor definite intr-o aplicatie.
Dezvoltare de aplicatii in Visual Studio .NET Page 137 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Atribute asociate claselor: - se asociaza atributul - interogarea se executa folosind metoda GetCustomAttributes din clasa Type: ... Type t = typeof(MyAttribClass) // clasa careia i se aplica atributele foreach (Attribute attr in t.GetCustomAttributes(true)) // do something ... Atribute asociate metodelor - se aplica atributele metodei. Exemplu: ... [InputMethod] public ReadNumbers() { ... } - se interogheaza atributele folosind metoda GetMethods din clasa Type si, apoi, metoda GetCustomAttributes din clasa MethodInfo. Atribute asociate campurilor - se aplica atributele campului. Exemplu: ... [InputField("Popescu")] public string nume; { ... } - se interogheaza atributele folosind metoda GetFields din clasa Type si, apoi, metoda GetCustomAttributes din clasa FieldInfo. Exista, desigur, un set de alte utilizari si aspecte legate de utilizarea atributelor. De
Page 138 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

retinut este ca atributele furnizeaza practic facilitati nelimitate, fiind foarte utile in faza de reflection si introspectie a aplicatiei.

Dezvoltare de aplicatii in Visual Studio .NET

Page 139 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Section 9. Programare folosind fire de executie - VIII 1. Fir de executie - definitie, abordare .NET Framework
Un fir de executie este unitatea minimala (atomul) careia un procesor ii aloca timpi de executie. Mecanismul gestionarii firelor de executie este urmatorul: - procesorul executa un thread atat timp cat mai exista timp alocat; - dupa scurgerea timpului, firului de executie curent ii este salvat contextul (registri, stiva, etc); - procesorul restaureaza contextul urmatorului fir de executie (firele de executie suspendate sunt pastrate intr-o coada) . Deoarece unitatile de timp alocate fiecarui fir de executie sunt mici (marimea lor depinde de sistemul de operare si de procesor), exista impresia unor executii paralele chiar si pe o masina cu un singur procesor. Fire de executie Clasa prin care sunt abstractizate firele de executie .NET este System.Threading.Thread. Aceasta clasa pune la dispozitie metode si proprietati pentru crearea, suspendarea, trezirea sau distrugerea unui fir de executie. Mai mult, sunt permise operatii de control a starii sau de planificare a unui thread. Un proces poate crea unul sau mai multe fire de executie care sa execute o sectiune din codul programului asociat procesului. Pentru a specifica codul programului executat de un thread, se utilizeaza un delegat ThreadStart. Pe durata existentei sale, un thread se gaseste intotdeauna in una sau mai multe din starile definite de ThreadState. Object.GetHashCode identifica firele de executie managed. Pe durata de viata a unui thread, nu vor exista coliziuni (in sens hash) cu alte fire de executie, indiferent de domeniul de aplicatie utilizat pentru a obtine valoarea hash. Application Domain Un AppDomain este un proces logic in cadrul unui proces fizic. La formarea unui AppDomain se creaza un fir de executie, dar la un moment dat, in cadrul unui AppDomain pot exista mai multe fire (derivate in cele din urma din firul initial). Dintr-un AppDomain pot fi apelate metode sau invocate obiecte care fac parte dintr-un alt AppDomain, spre deosebire de platforma Win32, in care un thread face parte dintr-un singur proces si nu poate apela o metoda dintr-un thread din alt proces (vezi clasa AppDomain). Exemplu: vezi resurse, curs 8.

Page 140 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

2. Mai multe despre clasa Thread


Membri ai clasei Thread IsAlive - proprietate, contine o valoare care indica starea executiei firului curent IsBackground - proprietate, contine o valoare care indica daca firul se executa sau nu in background Name - proprietate, contine numele firului de executie Priority - proprietate, stabileste prioritatea firului de executie; valorile posibile sunt continute de enumerarea Thread.Priority Priority - proprietate, enumerare cu urmatoarele valori: AboveNormal, BelowNormal, Highest, Lowest, Normal; valoarea implicita este Normal; ordinea planificarii firelor: Highest, AboveNormal, Normal, BelowNormal, Lowest ThreadState - proprietate, enumerare care contine valori indicand starile firului de executie curent abort - metoda, stopeaza firul de executie, lansand o exceptie ThreadAbortException GetDomain - metoda, returneaza o instanta AppDomain, reprezentand domeniul curent, in care ruleaza firul de executie Interrupt - metoda, intrerupe un thread aflat in starea WaitSleepJoin Join - metoda, blocheaza firul apelant, pana la terminarea unui alt fir Resume - metoda, reia executia unui fir, suspendat anterior Sleep - metoda, blocheaza executia firului curent pentru un numar specificat de milisecunde Start - metoda, schimba starea firului la valoarea ThreadState.Running Suspend - metoda, suspenda firul curent

Dezvoltare de aplicatii in Visual Studio .NET

Page 141 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Section 10. .NET Framework Advanced - IX 1. Functionalitati avansate de printare


Printarea (tiparirea) datelor si documentelor constituie o sarcina comuna oricarui mediu de dezvoltare de aplicatii complexe. .NET Framework pune la dispozitie componenta PrintDocument pentru a facilita tiparirea si pentru a gazdui alte clase care ofera suport si posibilitati de configurare a setarilor printerului. Printarea va fi, fara indoiala, o componenta indispensabila a oricarei aplicatii complexe. Cu toate ca dispozitivele electronice de stocare a datelor se bucura de o popularitate crescanda, materialele tiparite nu vor putea fi inlocuitere in viitorul apropiat. Deci, orice programator trebuie sa fie capabil sa implementeze propriile functionalitati de tiparire in aplicatiile pe care le dezvolta. .NET Framework ofera clase si componente care permit implementarea rapida si facila in programe a suportului de printare. Componenta PrintDocument Un document care urmeaza si/sau va fi tiparit este reprezentat in .NET Framework prin componenta PrintDocument. Desi aceasta nu are o reprezentare vizuala, ea poate fi gasita in tabul Windows Forms din Toolbox, de unde poate fi adaugata la aplicatie in mod design. Un obiect PrintDocument incapsuleaza toata informatia necesara printarii unei pagini. - proprietatea PrinterSettings contine informatii despre functionalitatea si setarile printerului; - proprietatea DefaultPageSettings incapsuleaza configurarile pentru printarea unei pagini; - proprietatea PrintController descrie modalitatea in care fiecare pagina este gestionata in procesul de printare. Prin intermediul acestor proprietati, se poate atinge un nivel avansat de control asupra procesului de printare. Crearea unui obiect PrintDocument - in mod design - prin adaugarea unei instance PrintDocument direct din Toolbox - utilizand scrierea de cod // using System.Drawing.Printing; PrintDocument firstPrintDocument = new PrintDocument(); O instanta PrintDocument creata in cod este configurata automat la setarile implicite ale printerului sistemului. Cum functioneaza printarea?
Page 142 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

In modelul de printare .NET Framework, continutul care va fi printat este furnizat direct de logica aplicatiei. Un proces de printare este initiat utilizand metoda PrintDocument.Print. Aceasta starteaza procesul si lanseaza unul sau mai multe evenimente PrintPage. Daca nu exista metode client pentru tratarea acestui eveniment, printarea nu va avea loc. Furnizand o metoda care sa trateze evenimentul, se poate specifica continutul care va fi printat. Daca continutul care va fi printat contine mai multe pagini, se va lansa cate un eveniment PrintPage pentru fiecare pagina din procesul de printare. Acest lucru cauzeaza event-handlerului asociat evenimentului PrintPage sa se execute de mai multe ori. Deci, aceasta metoda trebuie sa implementeze functionalitati care sa gestioneze procesul de printare si sa asigure faptul ca sunt printate, succesiv, paginile anumit document. Altfel, prima pagina a documentului va fi printata de mai multe ori. Evenimentul PrintPage Evenimentul PrintPage este principalul eveniment implicat in printarea documentelor. Pentru transmiterea efectiva a continutului care va fi printat catre printer, trebuie implementata o procedura de tratare a acestui eveniment, in cadrul careia sa se scrie cod pentru redarea continutului care va fi printat in formatul dorit. Toate obiectele si informatiile necesare transmiterii continutului catre printer sunt incapsulate in obiectul PrintPageEventArgs, primit ca parametru de event handler. Acest obiect contine proprietatile listate in tabelul urmator: Proprietate Cancel Graphics HasMorePages MarginBounds Descriere indica daca un proces de printare va fi sau nu anulat obiectul Graphics utilizat pentru redarea continutului paginilor care vor fi printate gestioneaza o valoare indicand daca vor fi printate pagini aditionale returneaza un obiect Rectangle care reprezinta portiunea din pagina obtinuta excluzand marginile returneaza un obiect Rectangle care reprezinta intreaga suprafata a paginii gestioneaza setarile PageSettings pentru pagina curenta

PageBounds PageSettings

Continutul care va fi printat este redat prin intermediul obiectului Graphics continut de instanta PrintPageEventArgs. In acest caz, pagina printata se va comporta ca o forma, ca un control sau ca orice alta suprafata de desenare care poate fi reprezentata printr-un obiect Graphics. Pentru a reda continutul, se utilizeaza aceleasi metode ca in cazul utilizarii formelor. Exemplu, printarea unei elipse: // acesta metoda va trata evenimentul PrintPage public void PrintEllipse(object sender,

Dezvoltare de aplicatii in Visual Studio .NET

Page 143 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

System.Drawing.Printing.PrintPageEventArgs e) { e.Graphics.DrawEllipse(Pens.Black, e.MarginBounds); } Proprietatile MarginBounds si PageBounds reprezinta dimensiunile care definesc suprafata paginii. Se poate specifica ca printarea sa aiba loc in interiorul limitelor paginii calculand coordonatele de printare pe baza dreptunghiului definit de MarginBounds. Printarea care se va efectua inafara limitelor marginilor, ca in cazul antetelor sau notelor din subsolul paginilor, poate fi specificata calculand coordonatele de printare pe baza dreptunghiului definit de PageBounds. Similar desenarii pe ecran, coordonatele de printare sunt specificate, implicit, in pixeli. Se poate specifica faptul ca un proces de printare contine mai multe pagini utilizand proprietatea HasMorePages. Implicit, aceasta proprietate este setata la false. Atunci cand, prin logica programului, se stabileste ca vor fi mai multe pagini de printat in cazdrul unui astfel de proces, se seteaza aceasta proprietate la valoarea true. Atunci cand ultima pagina este printata, proprietatea trebuie resetata la false. ! Metoda care trateaza evenimentul PrintPage trebuie sa tina cont de numarul de pagini din proces, in caz contrar, putandu-se obtine comportamente nedorite ale aplicatiei. De exemplu, daca se omite resetarea proprietatii HasMorePages la valoarea false dupa ce ultima pagina a fost printata, aplicatia va continua sa lanseze evenimente PrintPage. Un proces de printare poate fi oprit, fara a fi necesara terminarea printarii paginii curente, prin setarea proprietatii Cancel la valoarea true. Crearea unei metode de tratare a evenimentului PrintPage: prin dublu-clic pe instanta PrintDocument in mod design sau prin declararea in codul programului. Printarea Initierea unui proces de printare se realizeaza, simplu, prin apelul metodei PrintDocument.Print. Ramane de indeplinit sarcina selectarii continutului care este trimis la printer. Printarea elementelor grafice Acesta implica randarea elementelor grafice pe ecran. Se utilizeaza obiectul Graphics din cadrul instantei PrintPageEventArgs pentru a randa elementele grafice pe ecran. Se pot printa forme simple sau forme complexe, utilizand System.Drawing sau System.Drawing.Drawing2D. Exemplu: printarea unei forme complexe utilizand un obiect GraphicsPath: // event handler pentru PrintPage public void PrintGraphics(object sender, System.Drawing.Printing.PrintPageEventArgs e)

Page 144 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

{ System.Drawing.Drawing2D.GraphicsPath myPath = new System.Drawing.Drawing2D.GraphicsPath(); myPath.AddPolygon(new Point[] {new Point(1, 1), new Point(12, 55), new Point(34, 8), new Point(52, 53), new Point(99, 5)}); myPath.AddRectangle(new Rectangle(33, 43, 20, 20)); e.Graphics.DrawPath(Pens.Black, myPath); } Pentru a printa elemente grafice care ocupa mai multe pagini, se divide, prin logica programului, continutul care va fi printat. De exemplu, urmatoarea metoda va desena o elipsa pe doua pagini: bool FirstPagePrinted = false; // event handler pentru PrintPage public void PrintBigEllipse(object sender, System.Drawing.Printing.PrintPageEventArgs e) { if (FirstPagePrinted == false) { FirstPagePrinted = true; e.HasMorePages = true; e.Graphics.DrawEllipse(Pens.Black, new Rectangle(0, 0, e.PageBounds.Width, e.PageBounds.Height * 2)); } else { e.HasMorePages = false; FirstPagePrinted = false; e.Graphics.DrawEllipse(Pens.Black, new Rectangle(0, -(e.PageBounds.Height), e.PageBounds.Width, e.PageBounds.Height * 2)); } }

Dezvoltare de aplicatii in Visual Studio .NET

Page 145 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Acest exemplu utilizeaza variabila FirstPagePrinted pentru a stabili pagina care se printeaza: prima sau a doua pagina. ! Acesta pagina este declarata inafara procedurii. Daca aceasta metoda ar fi fost declarata in interiorul metodei, s-ar fi reinitializat la fiecare apel al metodei, returnand intotdeauna false. Dupa ce programul printeaza fiecare pagina, se redeseneaza imaginea de fiecare data, schimband pozitia dreptunghiului care incadreaza elipsa, pentru a orienta corect elipsa pe cele doua pagini. Printarea textelor Un text se printeaza in aceeasi maniera in care este redat pe ecran. Se utilizeaza metoda Graphics.DrawString pentru a trimite textul catre printer. In mod uzual, se specifica un font pentru a randa textul, textul de randat, un obiect Brush si coordonatele la care se printeaza. De exemplu: Font afont = new Font("Arial", 36, FontStyle.Regular, GraphicsUnit.Pixel); string Hello = "Hello Soccer Team!"; e.Graphics.DrawString(Hello, afont, Brushes.Red, 50, 50); ! La printarea textului, trebuie eliminata posibilitatea printarii inafara marginilor paginii. Daca se incearca acest lucru, continutul dinafara marginilor paginii nu va fi printat. Printarea textelor pe mai multe linii La printarea mai multor linii de text, ca in cazul tablourilor de stringuri sau a liniilor citite din fisiere text, trebuie inclusa in program o logica de calculare a spatierii dintre linii. Se poate calcula numarul liniilor de pe o pagina impartind inaltimea spatiului de printare la inaltimea fontului. Similar, se poate calcula pozitia fiecarei linii multiplicand numarul liniei cu inaltimea fontului. Exemplu: printarea unui tablou de stringuri myStrings: // pozitia curenta din tablou // declarare inafara procedurii int ArrayCounter = 0; private void PrintStrings(object sender, PrintPageEventArgs e) { // variabile care vor gestiona // spatierea si paginarea float LeftMargin = e.MarginBounds.Left; float TopMargin = e.MarginBounds.Top; float MyLines = 0;

Page 146 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

float YPosition = 0; int Counter = 0; string CurrentLine; // numarul de linii pe pagina MyLines = e.MarginBounds.Height/myFont.GetHeight(e.Graphics); // printeaza fiecare linie, // oprindu-se la sfarsitul fiecarei pagini while (Counter < MyLines and ArrayCounter <= myStrings.GetUpperBound(0)) { CurrentLine = myStrings[ArrayCounter]; YPosition = TopMargin + Counter * myFont.GetHeight(e.Graphics); e.Graphics.DrawString(CurrentLine, myFont, Brushes.Black, LeftMargin, YPosition, new StringFormat()); Counter +=1; ArrayCounter +=1; } // daca mai sunt linii de printat, // se trece la o alta pagina if (!(ArrayCounter == myStrings.GetUpperBound(0))) e.HasMorePages = true; else e.HasMorePages = false; } Daca printerul ofera suport pentru folosirea culorilor, aplicatia trebuie sa tina cont de acest lucru. Proprietatea PrinterSettings.SupportsColor poate fi consultata pentru a determina daca printerul suporta sau nu utilizarea culorilor. Pentru a forta printarea alb-negru, chiar daca printerul este color, se seteaza proprietatea DefaultPageSettings.Color la false. Exemplu: // using System.Drawing, // System.Drawing.Drawing2D si System.Drawing.Printing... Brush BrushOne; Brush BrushTwo;
Dezvoltare de aplicatii in Visual Studio .NET Page 147 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

if (printDocument1.PrinterSettings.SupportsColor == true) { // color... BrushOne = Brushes.Red; BrushTwo = Brushes.Blue; } else { // alb-negru... BrushOne = new HatchBrush(HatchStyle.DarkVertical, Color.Black); BrushTwo = new HatchBrush(HatchStyle.DashedHorizontal, Color.Black); } PrintPreviewControl .NET Framework implementeaza un control PrintPreviewControl care permite efectuarea unei previzualizari grafice a continutului care va fi printat inainte ca acesta sa fie trimis catre printer. Controlul PrintPreviewControl se gaseste in tabul Windows Forms din Toolbox, putandu-se adauga o instanta a sa prin drag-and-drop pe o forma. Pentru a putea previzualiza o pagina, controlul PrintPreviewControl trebuie asociat unei instante PrintDocument. Aceasta asociere se creaza prin setarea proprietatii PrintPreviewControl.Document: aPrintPreview.Document = aPrintDocument; Din acest moment, controlul PrintPreviewControl va putea afisa continutul care va fi printat. Acest lucru se realizeaza prin apelarea metodei care trateaza evenimentul PrintDocument.Print si preluand outputul grafic al acelei metode. Acest output este apoi redat prin intermediul controlului PrintPreviewControl. Pe masura ce conditiile din program se modifica, docuentul printat se poate modifica, de asemenea. Actualizarea previzualizarii se poate realiza prin apelarea metodei InvalidatePreview: aPrintPreview.InvalidatePreview(); O alta proprietate importanta a controlului PrintPreviewControl este proprietatea Zoom. Valoarea 1 determina vizualizarea la dimensiunea maxima. O valoare fractionara va reduce imaginea, iar o valoare supraunitara o va mari. PrintPreviewDialog Acest control reprezinta o resursa pentru previzualizarea documentelor printate si crearea de previzualizari personalizate. .NET Framework implementeaza un control
Page 148 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

PrintPreviewDialog standard care expune o serie de functionalitati uzuale legate de previzualizarea continutului care va fi printat. Odata vizualizat, controlul PrintPreviewDialog ofera posibilitatea vizualizarii mai multor pagini, redimensionarii sau printarii documentului. Ca in cazul oricarei alte forme, vizualizarea unei ferestre de dialog PrintPreviewDialog se realizeaza prin apelarea metodelor Show sau ShowDialog. Exemplu: asocierea unei componente PrintPreviewDialog cu o componenta PrintDocument si vizualizarea ferestrei de dialog PrintPreviewDialog. // using System.Drawing.Printing; PrintDocument aDocument = new PrintDocument(); PrintPreviewDialog aDialog = new PrintPreviewDialog(); aDialog.Document = aDocument; aDialog.ShowDialog(); Configurarea printarii Interfata .NET Framework care contine functionalitatile pe printare pune la dispozitie o gama bogata de optiuni de configurare. Proprietatea PrintDocument.PrinterSettings contine informatii legate de dispozitivele de printare disponibile in sistem. Proprietatea PrintDocument.DefaultPageSettings contine setarile legate de paginare care vor fi utilizate implicit. La crearea unui obiect PrintDocument, configuratia implicita a printerului implicit este incarcate in proprietatea PrinterSettings, iar setarile implicite de paginare sunt incarcate in proprietatea DefaultPageSettings. Deci, se poate crea si executa un proces de printare fara a modifica configuratia implicita. PrintDialog Fereastra de dialog PrintDialog permite utilizatorilor sa seteze proprietatea PrinterSettings a unui obiect PrintDocument la run-time. // vizualizarea ferestrei PrintDialog PrintDialog1.ShowDialog(); Un PrintDialog trebuie asociat unui obiect PrintDocument. La vizualizare, fereastra PrintDialog este legata de proprietatea PrinterSettings a obiectului PrintDocument specificat de proprietatea Document si furnizeaza o interfata grafica pentru a permite utilizatorului stabilirea unei configuratii. Exemplu: // using System.Drawing.Printing; PrintDocument aDocument = new PrintDocument(); PrintDialog aDialog = new PrintDialog(); aDialog.Document = myDocument; aDialog.ShowDialog(); PageSetupDialog
Dezvoltare de aplicatii in Visual Studio .NET Page 149 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Fereastra PageSetupDialog este similara ferestrei PrintDialog, furnizand i interfata grafica care permite utilizatorilor configurarea setarilor legate de paginare la run-time. Se poat modifica orientarea in pagina, formatul utilizat, marginile, s.a. Exemplu: // using System.Drawing.Printing; PrintDocument aDocument = new PrintDocument(); PageSetupDialog aDialog = new PageSetupDialog(); aDialog.Document = aDocument; aDialog.ShowDialog(); Configurarea PageSettings la run-time Setarile de paginare pot fi modificate la run-time utilizand proprietatea PrintPageEventArgs.PageSettings. Acesta proprietate contine setarile asociate paginilor care sunt printate. Modificarile efectuate acestei proprietati sunt utilizate pentru a printa pagina curenta, dar nu vor fi pastrate pentru celelalte pagini. Exemplu: modificarea formatului unei pagini la landscape. // variabila e este o instanta // PrintPageEventArgs e.PageSettings.Landscape = true; Sinteza - un document printat este reprezentat de o instanta a clasei PrintDocument - proprietatea PrinterSettings specifica setarile printerului - proprietatea DefaultPageSettings specifica setarile implicite de paginare - printarea unui document: prin apelul metodei PrintDocument.Print - Evenimentul PrintPage lansat la apelul metodei PrintDocument.Print - obiectul PrintPageEventArgs contine toate informatiile si functionalitatea necesare randarii outputului catre printer - controlul PrintPreviewControl permite previzualizarea unui document care va fi printat - fereastra de dialog PrintPreviewDialog - PrintDialog si PageSetupDialog

2.Facilitati de help in aplicatii Visual Studio .NET


Adesea, existenta unei documentatii elaborate in program este foarte importanta.
Page 150 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

Furnizarea de help in aplicatii permite utilizatorilor sa inteleaga mai usor aplicatia, ceea ce conduce la cresterea productivitatii. Pentru aplicatii complexe si dificile, este necesara scrierea de help built-in , integrat in aplicatie. Help-ul poate fi scris in fisiere HTML care contin un set informatii accesibile prin linkuri sau in fisiere CHM (Compressed HTML) care se pot crea cu Microsoft HTML Help Workshop. Ambele abordari permit vizualizarea dinamica a informatiile accesibile prin linkuri. Clasa Help O aplicatie poate vizualiza fisiere HTM sau CHM prin intermediul clasei Help. Aceasta clasa incapsuleaza HTML Help 1 engine si pune la dispozitie metode statice care permit vizualizarea fisierelor de help. Cele doua metode expuse de clasa Help sunt ShowHelp si ShowHelpIndex. Metoda ShowHelp afiseaza un fisier de help pentru un anumit control. Controlul care contine fereastra de dialog HelpDialog trebuie sa fie vizualizat pentru a putea afisa fisierul de help specificat printr-un URL. URL-ul poate fi un fisier (de exemplu, C:\fileHelp.htm) sau un HTTP URL (de exemplu, http://someserver.com/someHelp.htm). Pentru ca metoda ShowHelp este statica, nu este necesara crearea unei instante a clasei Help pentru a o putea utiliza. De fapt, clasa Help nu poate fi instantiata. Exemplu: Help.ShowHelp(someForm, @"C:\someHelpFile.htm"); Aditional, se poate specifica un parametru HelpNavigator. Acesta specifica care dintre elementele fisierului de help vor fi vizualizate. Acest parametru poate fi setat la TableOfContents, Find, Index sau Topic. Se poate specifica, de asemenea, un cuvant de cautare, ca in exemplul urmator: Help.ShowHelp(someForm, @"C:\someHelpFile.htm", "HelpMenu"); Clasa Help contine metoda ShowHelpIndex care vizualizeaza indexul unui fisier de help specificat. Apelul metodei ShowHelpIndex se executa in aceeasi maniera ca si metoda ShowHelp. Help.ShowHelpIndex(someForm, @"C:\someHelpFile.htm"); Metodele clasei Help pot fi apelate ca raspuns la o varietate de evenimente lansate de interactiunea cu interfata utilizator. De exemplu, mai multe aplicatii includ un meniu de Help sau meniuri care pot fi accesate prin clic dreapta pe un anumit control. De aceea, aplicatiile trebuie sa ofere facilitati de help organizate intr-o maniera logica si eficienta. Componenta HelpProvider Componenta HelpProvider permite furnizarea de help pentru controalele din interfata utilizator. HelpProvider este un extender provider; deci, coordoneaza si mentine proprietati pentru fiecare control adaugat pe o forma. Se poate specifica un HelpString pentru fiecare control de pe forma. Acest string este vizualizat atunci cand controlul
Dezvoltare de aplicatii in Visual Studio .NET Page 151 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

primeste focusul si se tasteaza F1. Se poate specifica, de asemenea, un HelpNameSpace care specifica URL-ul fisierului de help asociat HelpProvider-ului. Componenta HelpProvider asociaza trei proprietati fiecarui control de pe forma parinte: - HelpString - HelpKeyWord - HelpNavigator Aceste proprietati pot fi setate in fereastra Properties pentru fiecare control sau prin scrierea de cod: aHelpProvider.SetHelpString(Button1, "Apasati si veti fi multumit..."); Daca proprietatea HelpNameSpace nu este setata, se va vizualiza stringul continut de HelpString si celelalte doua proprietati sunt ignorate. Daca proprietatea HelpNameSpace este setata la un fisier de help, se vizualizeaza fisierul de help specificat utilizand parametrii proprietatilor HelpNavigator si HelpKeyword. Proprietatea HelpNavigator poate fi setata la una din urmatoarele valori: - TableOfContents - vizualizeaza scheletul continutului help-ului - Find - afiseaza pagina de cautare - Index - vizualizeaza indexul - Topic - afiseaza un anumit element de help (topica) - AssociatedIndex - vizualizeaza indexul unei topici specificate - KeywordIndex - afiseaza rezultatul cautarii dupa un anumit cuvant cheie Daca este setata proprietatea HelpNameSpace, nu se mai afiseaza stringul din HelpString atunci cand se apasa tasta F1, dar acesta poate fi returnat prin alte modalitati. Se poate obtine HelpString-ul asociat unui anumit control prin apelarea metodei HelpProvider.GetHelpString ca in exemplul urmator: someHelpProvider.GetHelpString(Button1); Sinteza: - clasa Help - metoda Help.ShowHelp - componenta HelpProvider - proprietatile HelpString, HelpKeyWord si HelpNavigator - HelpNameSpace

Page 152 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

3. Globalizare si localizare
.NET Framework pune la dispozitia programatorilor un suport bogat pentru scrierea de aplicatii accessibile global. Se pot crea aplicatii care se adapteaza la diverse limbaje, formate monetare, calendaristice sau alte informatii legate de cultura. In cazul dezvoltarii de aplicatii pentru o companie care face afaceri pe plan international, trebuie avut in vedere faptul va vor exista utilizatori provenind dintr-o variatate de culturi. Utilizatorii din anumite regiuni ale lumii pot fi nefamiliarizati cu standardele legate de monede sau date calendaristice din tara din care provine dezvoltatorul aplicatiei, sau nu pot intelege limba engleza, de exemplu. Integrand suport international in aplicatii, se largeste aria de utilizare a aplicatiei. Globalizare si localizare Globalizarea si localizarea sunt procese interdependente dar diferite. Globalizarea implica aplicarea formatarii datelor existente pe baza specificului unei anumite culturi, in timp ce localizarea implica extragerea anumitor date pe baza unei culturi. Exemplu: * Globalizare: scrierea numerelor reprezentand sume de bani este in anumite tari implica utilizarea (.) ca separator pentru mii si o virgula ca separator zecimal. O aplicatie globalizata formateaza numerele reprezentand sume de bani pe baza regulilor de acest gen din regiunea respectiva. * Localizare: titlul unei forme este afisat intr-un anumit limbaj, dependent de tara in care este utilizata aplicatia. O aplication localizata preia stringul potrivit si il afiseaza in functie de locatie. Culture Intr-o aplicatie, termenul cultura se refera la informatia culturala legat de regiunea in care este utilizata aplicatia. In .NET Framework, culturile sunt identificate utilizand un cod cultural care reprezinta limba curenta folosita in mediul de lucru. Un cod de cultura poate, de asemenea, specifica informatii despre o anumita regiune. In general, codul culturii este fie un cod din doua litere, care specifica limba, fie un cod din doua litere, urmat de o liniuta si alte doua litere care specifica regiunea. Codurile care specifica numai limbajul se numesc culturi neutre,in timp ce codurile care specifica limba si regiunea sunt cunoscute drept culturi specifice. Exemple: * en - limba engleza, nici o regiune * en-CA - limba engleza, Canada * ro - limba romana Lista completa a codurilor culturale poate fi consultata utilizand documentatia aferenta clasei CultureInfo in the .NET Framework. Setarea culturii curente

Dezvoltare de aplicatii in Visual Studio .NET

Page 153 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Orice aplicatie citeste automat setarile legate de cultura sistemului si le implementeaza. Se poate medifica cultura curenta dintr-o aplicatie setand, in mod programatic, cultura curenta la o noua instanta a clasei CultureInfo. Acesta clasa contine informatii despre o cultura si despre modalitatea in care va interactiona cu aplicatia. De exemplu, clasa CultureInfo contine informatii despre tipul de calendar, formatarea monetara, formatul datei, s.a.m.d. pentru o cultura specificata. Stabilirea unei culturi: setand proprietatea CurrentThread.CurrentCulture la o noua instanta a clasei CultureInfo. Clasa CultureInfo are un constructor care primeste ca parametru un string reprezentand codul culturii respective. Exemplu: System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("fr-CA"); Returnarea culturii curente se se realizeaza prin accesarea clasei CultureInfo.CurrentCulture dupa cum urmeaza: // using System.Globalization CultureInfo theCurrentCulture; theCurrentCulture = CultureInfo.CurrentCulture; Implementarea globalizarii Atunci cand proprietatea Thread.CurrentThread.CurrentCulture este setata la o noua instanta CultureInfo, toate datele formatate de aplicatie sunt modificate pe baza noului format. Datele neformatate nu vor fi afectate in vreun fel. Sa consideram o forma cu un contro Label. Sa presupunem ca urmatoarea atribuire: label1.Text = "$1000.00"; Daca se schimba cultura la en-GB , eticheta va afisa exact acelasi lucru, contrar asteptarilor. Setarile legate de cultura au efect numai pentru informatiile formatate. Pe de alta parte, sa presupunem ca textul etichetei a fost formatat in felul urmator: label1.Text = (1000).ToString("C"); Setarea culturii la en-GB , va face ca eticheta sa afiseze L1000.00. Valoarea este formatata pe baza comventiilor monetare locale. Nu se executa, insa, nici un fel de conversie monetara. Implementarea localizarii .NET Framework faciliteaza localizarea prin crearea de fisiere de resurse care retin datele pentru forme alternative asociate culturilor pe care le suporta aplicatia. La run-time, se incarca o anumita forma, pe baza proprietatii CultureInfo.CurrentUICulture. Setarea UI-culturii curente O UI-cultura este reprezentata de o instanta CultureInfo, si este diferita de proprietatea CultureInfo.CurrentCulture. Setarile CurrentCulture determina formatarile care vor fi aplicate datelor, in timp ce CurrentUICulture specifica resurse pentru forme localizate care vor fi incarcate la run-time. Se poate prelua instanta CurrentUICulture accesand proprietatea CultureInfo.CurrentUICulture, dupa cum urmeaza:

Page 154 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

// using System.Globalization CultureInfo theCurrentCulture; theCurrentCulture = CultureInfo.CurrentUICulture; UI-cultura curenta se seteaza in acceasi maniera ca si cultura curenta: accesand Thread-ul curent. De exemplu: System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("th-TH"); La setarea culturii curente, sistemul de operare incarca resursele specifice acelei culturi, in cazul in care sunt disponibile. Daca resursele specifice nu sunt disponibile, interfata utilizator vizualizeaza resursele pentru cultura respectiva. ! UI-cultura trebuie setata inainte de o forma sa poata vizualiza resursele localizate. Daca UI-cultura se seteaza programatic, ea trebuie setata in constructorul formei principale sau in metoda main a aplicatiei. Crearea formelor localizate Crearea formelor localizate in .NET Framework este un proces facil. Fiecare forma contine o proprietate Localizable care determina daca forma este localizata sau nu. Setarea acestei proprietati la true permite localizarea. In acest caz, Visual Studio .NET creaza automat fisierele resursa necesare, utilizand proprietatea Language a formei. Atunci cand aceasta proprietate este setata la (Default), pot fi editate toate UI-proprietatile sau controalele pentru a furniza o reprezentare a UI-culturii curente. Pentru a crea o versiune localizata a formei, se poate seta proprietatea Language la o alta valoare, diferita de (Default). Visual Studio .NET va crea un fisier resursa pentru noul limbaj si va stoca in acel fisier toate valorile UI-specifice setate. ! Desi, de obicei, UI-elementele sunt siruri de caractere, orice proprietate poate fi localizata. Deci, putem avea, de exemplu, butoane care isi modifica dimensiunile pentru a putea vizualiza corect texte in diverse limbi. Aceste fisiere pot fi vizualizate in Solution Explorer, existand cate un fisier resursa pentru fiecare limbaj pentru care forma are o versiune. Atunci cand CurrentUICulture este setata la o cultura localizata, aplicatia va incarca versiunea corespunzatoare a formei consultand fisierele resursa aferente. Daca nu exista fisierele resursa necesare, se va vizualiza UI-cultura implicita. Validarea input-ului cu specific international se pot utiliza metodele de validare din structura Char pentru a valida inputul introdus (acesta poate contine caractere specifice unei anumite limbi). Metodele Char.IsDigit, Char.IsLetter si altele vor returna true sau false, independent de caracterele utilizate. Deci, validarile de acest gen vor functiona corect, fara alte modificari speciale. Formatari specifice culturilor
Dezvoltare de aplicatii in Visual Studio .NET Page 155 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Se pot specifica valori pentru anumiti membri ai clasei CultureInfo pentru a croi aplicatia astfel incat sa raspunda unor necesitati cu specific cultural. De exemplu, sa consideram o aplicatie pentru un client care colaboreaza cu un grup din Japonia, dar moneda utilizata in aplicatie este dolarul american. Aplicatia trebuie sa implementeze o formatare cu specific japonez pentru anumite elemente. Membri ai clasei CultureInfo care specifica formatari legate de globalizare: * DateTimeFormat - formatarea datelor calendaristice si a timpului * NumberFormat - formatarea datelor numerice si financiare * TextInfo - formatarea textelor Exemplu: // using System.Globalization // using System.Threading CultureInfo modJPCulture = new CultureInfo("jp-JN"); modJPCulture.NumberFormat.CurrencySymbol = "$"; // se foloseste caracterul $ Thread.CurrentThread.CurrentCulture = modJPCulture; Implementarea scrierii de la dreapta la stanga Anumite limbaje folosesc o astfel de scriere. Formele Windows tandard contin o proprietate RightToLeft care permite implementarea unei interfete cu un astfel de specific. Valori posibile ale proprietatii RightToLeft : Yes, No si Inherit, ultima fiind valoarea implicita, caz in care afisarea este determinata de valoarea setata pentru controlul parinte. Efectul setarii proprietatii RightToLeft a unui control la valoarea Yes: - aliniera textului este inversata - pentru o forma, titlul va fi aliniat la dreapta - barele de derulare verticale vor aparea pe partea stanga - barele de derulare orizontale sunt initializate cu slider-ul in partea dreapta - check-box-urile vor avea proprietatea CheckAlign inversata - butoanele taburilor vor fi inversate - la fel, alinierea elementelor in list box si combo box Pe short, formatarea fiecarui control consta in oglindirea continutului acestuia. Continutul unui control RightToLeft ramane, totusi, neschimbat. Conversia caracterelor codificate .NET Framework utilizeaza o reprezentare Unicode UTF-16 a caracterelor. Un caracter
Page 156 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

Unicode este un standard universal standard de reprezentare a caracterelor, acoperind o multime foarte mare de caractere, cu posibilitate de extindere. .NET Framework permite conversii ale datelor din alte formate in date reprezentate in Unicode. Aceste conversii sunt indeplinite de clasa Encoding, din spatiul de nume System.Text. Aceasta clasa nu poate fi instantiata direct, dar se poate obtine o instanta a acestei clase utilizand metoda statica Encoding.GetEncoding, pentru a obtine o codificare specifica: // using System.Text Encoding jpEncoding; // pagina de coduri 932 reprezinta caracterele japoneze jpEncoding = Encoding.GetEncoding(932); Dupa ce a fost creata o instanta a unei codificari, poate fi utilizata pentru a converti caractere din aceasta codificare in caractere in format Unicode si vice versa. Exemplu: // using System.Text byte[] tgtData; Encoding srcEncoding; UnicodeEncoding tgtEncoding = new UnicodeEncoding(); srcEncoding = Encoding.GetEncoding(932); // caracterele japoneze sursa // tgtData contine acum un tablou de bytes care reprezinta codificarea // Unicode a tabloului myData tgtData = Encoding.Convert(srcEncoding, tgtEncoding, myData); Sinteza: - globalizare - localizare - cultura - UI-cultura - conversii de caractere

Dezvoltare de aplicatii in Visual Studio .NET

Page 157 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Section 11. Assemblies. Aplicatii - configurare, securizare, instalare, rulare - X 1. Assemblies si resurse
Un assembly reprezinta unitatea primara de dezvoltare intr-o aplicatie Microsoft .NET Framework, constand intr-o colectie de tipuri si resurse care sunt interconectate pentru a crea o unitate functionala logica. Assembly-urile sunt auto-descriptive si contin toata informatia solicitata de common language runtime pentru a interpreta continutul si pentru a configura executia acestora. Pe scurt, un assembly este un proiect care este compilat intr-un fisier executabil sau intr-un fisier DLL. Structura interna a unui assembly este, intrucatva, diferita de cea a fisierelor .exe sau .dll create cu unelte de dezvoltare anterioare. Un assembly contine patru sectiuni interne: - un manifest (metadata) - metadate despre tipuri - limbajul intermediar al assembly-ului - fisiere de resurse Manifestul assembly-ului contine metadata care descrie assembly-ul in contextul CLR. Common language runtime utilizeaza aceste informatii pentru a lua decizii legate de executia assembly-ului. Un manifest contine urmatoarele informatii: - identitate: contine numele si numarul versiunii assembly-ului, putand contine informatii optionale, cum ar fi semnatura aplicatiei - tipuri si resurse: lista tuturor tipurilor continute, precum si informatii despre modalitatea de accesare a tipurilor - fisiere: lista tuturor fisierelor din assembly, precum si informatii legate de dependentele dintre acestea - permisiuni de securitate: descriu restrictiile de securitate impuse la executia assembly-ului Identitatea unui assembly este continuta in fisierul AssemblyInfo.vb sau .cs din cadrul proiectului, putand fi modificata de catre utilizator. [assembly: AssemblyTitle("")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("")]

Page 158 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

[assembly: AssemblyCopyright("")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] Informatiile legate de identitatea assembly-ului pot fi modificate prin setarea valorilor acestor atribute in fisierul AssemblyInfo. Urmatorul exemplu de cod demonstreaza cum se poate seta atributul AssemblyTitle: [assembly: AssemblyTitle("Acesta este un assembly!")] Crearea assembly-urilor de tip biblioteca de clase (class library) Acestea reprezinta o multime de tipuri care pot fi referentiate si utilizate in alte assembly-uri. De exemplu, un control personalizat care va fi utilizat in alte aplicatii va fi construit in cadrul unui assembly de aces tip. Un astfel de assembly nu este executabil, trebuind adaugat ca referinta intr-o alta aplicatie executabila. Tipurile de proiecte asociate unui astfel de assembly sunt Class Library sau Windows Control Library. Se scrie codul, se seteaza informatiile legate de identitatea assembly-ului in fisierul AssemblyInfo si se executa Build. Resurse si assembly-uri de resurse O mare parte dintre aplicatii utilizeaza resurse. Acestea reprezinta date non-executabile partajate in cadrul aplicatiei. Exemple de resurse: siruri de caractere vizualizate in cadrul interfetei utilizator pe baza culturii setate in aplicatie sau un set de imagini. Impachetarea acestor date in fisiere resursa permit modificarea datelor necesare programului fara a recompila intreaga aplicatie. Crearea fisierelor de resurse Aplicatia ResEditor din .NET Framework poate fi utilizata pentru a crea fisiere resursa care contin texte si imagini. ResEditor este o aplicatie de sine statatoare, nefiind integrata in Visual Studio .NET. Ea va fi rulata separat, utilizand command prompt-ul Visual Studio .NET. ResEditor permite crearea de fisiere .resources sau .resx care contin siruri de caractere sau imagini. Se pot specifica tipul si numele resurselor din cadrul fisierului. Dupa adaugarea elementelor din cadrul fisierului, se pot specifica valori ale sirurilor de caractere resursa sau imaginile care vor fi adaugate in cadrul resurselor de imagini. In final, fisierele pot fi salvate ca fisiere binare de tip .resources sau fisiere XML de tip .resx. Odata create, fisierele .resources sau .resx pot fi adaugate la proiect din meniul Project, optiunea Add Existing Item si selectand apoi fisierul resursa dorit. Executand Build, resursele vor fi partajate corespunzator in cadrul proiectului. Crearea assembly-urilor de resurse Pot fi create assembly-uri care contin numai resurse. Acest lucru este util in situatia in care este necesara modificarea datelor continute in fisiere resursa fara a recompila aplicatia pentru modificarea efectiva a acestora. Un assembly de tip resursa se poate crea prin adaugarea de fisiere resursa la un
Dezvoltare de aplicatii in Visual Studio .NET Page 159 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

proiect vid. Atunci cand se va executa build, resursele vor fi compilate intr-un assembly comun care va putea fi apoi referentiat si accesat. Crearea unui assembly satelit La crearea aplicatiilor internationalizate, este necesara furnizarea diferitelor multimi de resurse pentru culturile utilizate in aplicatie. Assembly-urile satelit permit acestor multimi de resurse sa fie incarcate automat, pe baza setarii CurrentUICulture pentru firul de executie curent. Pentru a fi incorporata intr-un assembly satelit, un fisier resursa trebuie sa se supuna unei scheme specifice de nume, bazata pe cultura pentru care a fost proiectat. Numele unui fisier resursa specific unei culturi este similar numelui fisierului resursa pentru cultura invarianta, avand inserat, in plus, codul culturii respective intre numele de baza si extensie. Deci, daca exista un fisier de resurse fileResources.resx, un fisier resursa care va contine resurse pentru cultura neutra fr (Franta) va avea numele fileResources.fr.resx. Pentru culturi specifice, un exemplu ar putea fi: anotherFileResources.en-CA.resx. Dupa adaugare, Visual Studio .NET va compila aceste fisiere resursa alternative in assembly-uri satelit, creandu-se o structura de directoare asociata. La run-time, resursele specifice culturii curente vor fi localizate automat de catre common language runtime. Interogarea resurselor la run-time La run-time, se poate utiliza clasa ResourceManager pentru a returna resursele partajate. Un ResourceManager gestioneaza accesul si interogarea resurselor partajate in assembly. Fiecare instanta a clasei ResourceManager este asociata cu un assembly care contine resurse. Un ResourceManager se poate crea prin specificarea a doi parametri: numele de baza al fisierului resursa si assembly-ul in care se gaseste acest fisier. Noul ResourceManager va fi dedicat fisierului resursa specificat. Numele de baza specificat este numele spatiului de nume care contine fisierul, urmat de numele fisierului, fara a specifica extensiile. De exemplu, un fisier resursa denumit fileResources.de-DE.resx intr-un spatiu de nume Namespace1 va avea numele de baza Namespace1.fileResources. Al doilea parametru refera assembly-ul in care este localizat fisierul resursa. Daca assembly-ul care contine resursele este acelasi assembly care contine obiectul in cadrul caruia de creaza ResourceManager-ul, se poate obtine o referinta la assembly utilizand tipul obiectului respectiv. De exemplu: // crearea unui ResourceManager pentru // a accesa fisiere resursa dintr-un fisier partajat // myResources.resx // intr-un namespace myNamespace si acelasi assembly in care // se gaseste obiectul curent // se utilizeaza System.Resources
Page 160 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

ResourceManager myManager = new ResourceManager ("myNamespace.myResources", this.GetType().Assembly); Daca resursele care vor fi accesate se gasesc intr-un alt assembly, cum ar fi un assembly de resurse, assembly-ul trebuie incarcat inainte de a accesa resursele continute. Acest lucru se poate realiza prin intermediul unui obiect Assembly din spatiul de nume System.Reflection. Exemplu: // using System.Resources System.Reflection.Assembly myResources; // numele assembly-ului ca parametru myResources = System.Reflection.Assembly.Load("ResourceAssembly"); ResourceManager myManager = new ResourceManager("ResourceAssembly.Resources", myResources); Proiectul trebuie sa contina o referinta la assembly-ul care va fi accesat. Odata ce a fost creat un manager de resurse, acesta poate fi utilizat pentru a returna siruri de caractere si obiecte continute in fisierul de resurse. Pentru a accesa un sir de caractere, se utilizeaza metoda ResourceManager.GetString, specificand numele resursei care va fi accesata. Imaginile sau alte obiecte dintr-un fisier resursa pot fi obtinute prin metoda ResourceManager.GetObject. Aceasta metoda returneaza un obiect corespunzator numelui specificat. In plus, trebuie executata o conversie explicita pentru a obtine rezultatul scontat. Exemplu: System.Drawing.Image myImage; myImage = (System.Drawing.Image)myManager.GetObject("ImageResource"); Assembly-uri partajate Assembly-urile pot fi private sau partajate: - un assembly privat este un assembly utilizat de o singura aplicatie - un assembly partajat poate fi utilizat de mai multe aplicatii Majoritatea assembly-urilor care se creaza sunt assembly-uri private, find create implicit. Un astfel de assembly poate fi utilizat de o singura aplicatie, fiind integrat si disponibil numai acelei aplicatii. Din aceste motive, nu exista informatii legate de versiunea sau identitatea assembly-ului. Toate assembly-urile cu care ne-am obisnuit pana acum au fost private. La adaugarea unei referinte catre un assembly privat in proiect, Visual Studio .NET creaza o copie a DLL-ului care contine assembly-ul si aceasta este copiata in directorul proiectului. Deci, mai multe proiecte pot referentia acelasi DLL si utiliza tipurile pe care le contine, dar
Dezvoltare de aplicatii in Visual Studio .NET Page 161 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

fiecare proiect are propria sa copie a DLL-ului, acesta fiind, deci, un assembly privat. Pentru ca mai multe aplicatii sa partajeze un assembly, acesta trebuie instalat in Global Assembly Cache. Avantaje: - locatie partajata - securitate (assembly-ul este instalat in directorul sisteului de operare) - versionare (pot fi instalate mai multe versiuni ale aceluiasi assembly Partajarea unui assembly si instalarea in Global Assembly Cache implica semnarea assembly-ului cu un strong name. Strong Naming Un strong name este un nume care garanteaza identitatea assembly-ului. Acesta contine informatii despre assembly, cum ar fi numele, numarul versiunii, informatia legata de cultura si o pereche de chei publica/privata. Aceste informatii sunt incriptate utilizand cheia privata si pot fi decriptate folosind cheia publica. Dezvoltatorul aplicatiei este singurul care detine cheia privata, ceea cea determina securitatea aplicatiei. O pereche de chei poate fi generata prin intermediul aplicatiei sn.exe (strong name). Un fisier care contine astfel de chei are extensia .snk. Exemplu: sn k myKey.snk Pentru a semna un assembly cu un strong name: - se deschide fisierul AssemblyInfo din cadrul proiectului - se verifica numarul versiunii assembly-ului: [assembly: AssemblyVersion("1.0.1.1")] - se specifica fisierul care contine cheile de criptare. Exemplu: [assembly: AssemblyKeyFile("..\\..\\myKey.snk")] Se executa build. Strong name-ul va fi generat si aplicat assembly-ului. Instalarea in Global Assembly Cache Se utilizeaza aplicatia Global Assembly Cache utility (gacutil.exe): gacutil /i mypath\myAssembly.dll Sinteza: - assembly - manifest, tipuri de date, fisiere de cod si reurse - assembly-uri de tip biblioteca de clase - resurse - assembly-uri de resurse - resurse asociate culturilor
Page 162 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

- clasa ResourceManager - assembly-uri private si partajate - Global Assembly Cache - strong name - aplicatiile sn.exe si gacutil.exe

2. Configurarea si optimizarea aplicatiilor


.NET Framework pune la dispozitie unelte de configurare si optimizare a aplicatiei dupa ce aceasta a fost instalata. Fisierele de configurare permit specificarea locatiei sau informatiilor legate de versiune assembly-urilor si permit configurarea proprietatilor aplicatiei. Pentru orice aplicatie se poate crea un fisier de configurare. Acesta permite configurarea proprietatilor aplicatiei dupa ce aceasta a fost instalata, fara a recompila codul. Fisierul de configurare este un fisier XML care contine informatii despre comportamente care vor fi imprimate aplicatiei. Crearea fisierului de configurare Un fisier de configurare este un fisier XML care contine o serie de taguri specifice si un nume care trebuie sa respecte anumite regului. Un fisier de configurare pentru o aplicatie va avea numele <name>.<extension>.config, unde <name> este numele aplicatiei, iar <extension> este extensia aplicatiei (de exemplu .exe). Un fisier de configurare trebuie sa se afle in acelasi director ca si assembly-ul asociat aplicatiei pentru care se face configurarea. Structura de baza a unui fisier de configurare este urmatoarea: <?xml version="1.0" encoding="utf-8" ?> <configuration> <...elemente de configurare...> </configuration> Inafara primului element, care specifica versiunea XML si codificarea, si a ultimului element, <configuration>, nu exista elemente obligatorii pentru un fisier de configurare. Toate celelalte elemente sunt optionale. In C#, fisierul de configurare .config trebuie creat manaul, cu ajutorul unui editor, pe baza schemei descrise mai sus, adaugand o serie de elemente. Schema fisierului de configurare .config

Dezvoltare de aplicatii in Visual Studio .NET

Page 163 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Elemente ale schemei fisierului de configurare: <startup>- contine numai elementul <requiredRuntime>, care permite specificarea versiunii common language runtime utilizate <runtime> - permite configurarea informatiilor legate de executia assembly-ului si conportamentul garbage colectorului <system.runtime.remoting> - contine informatii despre configurarea canalelor de comunicatie si a obiectelor de tip remote <system.net> - contine informatii despre aplicatii pe Internet <mscorlib> - contine un tag <cryptographySettings> care permite configurarea elementelor criptografice din cadrul aplicatiei <configSections> - contine configurari personalizate <system.diagnostics> - informatii de configurare a claselor Trace si Debug din cadrul aplicatiei Configurarea aplicatiei utilizand proprietati dinamice Proprietatile dinamice permit configurarea valorilor de startup ale obiectelor din cadrul aplicatiei. Se poate realiza o mapare a proprietatilor specifice ale obiectelor la elemente din cadrul fisierului de configurare file si returnarea dinamica a acestora la run-time. Proprietatile dinamice sunt utile in cazul resurselor externe care se pot modifica pe durata timpului de viata a aplicatiei, cum ar fi un string de conectare la o baza de date. Consultand valoarea unei astfel de proprietati dinamice, se poate reconfigura aplicatia fara a fi necesara recompilarea si reinstalarea. Configurarea proprietatilor dinamice utilizand ferestrea Properties Fereastra Properties corespunzatoare fiecarui control din interfata utilizator contine un nod expandabil care permite utilizatorului setarea proprietatilor care vor fi tratate ca proprietati dinamice. Proprietatile care vor fi legate la resurse externe sunt adaugate implicit la acest nod, putandu-se adauga si alte proprietati (Advanced ...). Pentru a putea consulta o proprietate dinamica din fisierul .config, trebuie mai intai definita o cheie. Aceasta cheie este scrisa in fisierul .config si corespunde valorii corespunzatoare care va fi returnata din fisierul .config. La setarea unei chei pentru o proprietate dinamica, cheia si valoarea proprietatii sunt scrise automat in fisierul .config. Exemplu: transformarea proprietatii Button1.Text in proprietate configurabila. <add key="Button1.Text" value="Button1" /> Cheia (Button1.Text) este utilizata de aplicatie pentru a returna valoarea (Button1) la run-time. Deoarece cheile sunt citite de utilizatori, trebuie sa primeasca valori care sa respecte un pattern consistent. Pattern-ul implicit este <control>.<propertyname>, unde control este numele controlului, iar propertyname este numele proprietatii. Dupa ce programul a fost instalat, pot fi configurate proprietatile dinamice prin editarea directa a fisierului de configurare. La urmatoarea rulare a aplicatiei, modificarile efectuate isi vor face simtite efectul. !

Page 164 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

Nu toate proprietatile sunt disponibile in fereastra Dynamic Properties. Pot fi configurate doar proprietatile reprezentate ca siruri de caractere sau tipuri care pot fi convertite explicit de la tipul string. Setarea si consultarea manuala a proprietatilor dinamice Uneori, este util ca si alte proprietati, inafara proprietatilor din interfata utilizator, sa poata fi configurabile. Sa consideram exemplul unei clase instantiate la run-time. Pot fi stabilite proprietati implicite pentru obiecte create dinamic in fisierul de configurare; acestea pot fi setate si consultate dinamic la run-time utilizand clasa AppSettingsReader. Clasa AppSettingsReader se gaseste in spatiul de nume System.Configuration si utilizeaza chei pentru a returna valori din fisierul de configurare. Principala metoda a acestei clase este GetValue. Prametri: un string reprezentand cheia si un tip care reprezinta valoarea returnata. Desi se specifica un tip, valoarea este returnata ca un Object si trebuie convertita explicit la tipul corect de date. Exemplu: // crearea unui obiect AppSettingsReader System.Configuration.AppSettingsReader myReader = new System.Configuration.AppSettingsReader(); // crearea unui obiect utilizator Widget myWidget = new Widget(); // returnarea valorii unei proprietati dinamice // DynamicWidget.Text repreinta cheia, // valoarea este returnata ca un string // urmeaza o conversie explicita myWidget.Text = myReader.GetValue("DynamicWidget.Text", typeof(System.String)).ToString(); La incercarea de utilizare a unei chei inexistente in fisierul de configurare, se arunca o exceptie de tipul InvalidOperationException. Elementele continand perechi de tipul key/data se adauga prin intermediul tagului <add> si vor fi introduse drept copii ai tagului <appSettings>. Exemplu: <appSettings> <... setari ale proprietatilor configurabile etc... > <... Example: <add key="settingName" value="settingValue"/>... > <add key="Widget.Visible" value="True" /> <add key="Widget.Text" value="I love my Widget!" /> </appSettings> Optimizarea performantei aplicatiiilor
Dezvoltare de aplicatii in Visual Studio .NET Page 165 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Optimizare in faza de dezvoltare Un cod eficient, optimizat este rezultatul unei bune planificari si a unei experiente bogate in scrierea de cod. Cateva idei mai importante legate de tehnica scrierii de cod: - evitarea legarii tarzii (late binding) - evitarea, pe cat posibil, a folosirii tipului object si, deci, a conversiilor - evitarea utilizarii variabilelor globale - traterea atenta a structurilor iterative - reprezinta structurile de program care implica executia unui numar foarte mare de operatii Optimizarea este un proces iterativ Planul general de optimizare a codului este urmatorul: * masurarea datelor legate de performanta * identificarea bottleneck-urilor * rafinarea codului Masurarea performantei Windows 2000 si Windows XP includ un utilitar denumit perfmon.exe care poate monitoriza o serie de parametri legati de performanta aplicatiei. Rezultatele pot fi redate in forma grafica sau salvate sub forma de log-uri.Exista, de asemenea, posibilitatea optimizarii aplicatiei prin setari ale compilatorului. Sinteza: - fisiere de configurare - proprietati dinamice - clasa AppSettingsReader - tagul <add> - utilitarul perfmon.exe - optimizari in faza de compilare

3. Securitatea aplicatiilor
Securitate inseamna protectie. Pot fi utilizate facilitatile de securitate puse la dispozitie de .NET Framework pentru a proteja codul aplicatiilor fata de utilizatori neautorizati si pentru a proteja sistemul in cazul utilizarii neautorizate a anumitor fragmente de cod. Administratorul sistemului seteaza politica de securitate a sistemului. Acesta decide tipurile de cod care vor putea fi utilizate de o masina, daca un anumit assembly poate fi rulat pe o masina, s.am.d. Politica de securitate setata de administratorul sistemului nu poate fi suprascrisa in cod: ea confera cel mai inalt nivel de securitate pe o anumita

Page 166 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

masina. Permisiuni O permisiune este un obiect care reprezinta un utilizator, o identitate sau o resursa (cod). Obiectele de tip permisiune sunt utilizate intr-o varietate de functii legate de securitate. Interfata IPermission Orice permisiune de securitate in .NET Framework trebuie sa implementeze interfata IPermission, care contine un nivel de functionalitate comun tuturor obiectelor de securitate deci, arareori va fi necesara o implementare efectiva a acestei interfete. Metodele membre ale interfetei IPermission sunt descrise in continuare: * Copy - creaza si returneaza o copie identica a permisiunii * Demand - parcurge stiva de apeluri si arunca o exceptie de tipul SecurityException daca un apelant de pe stiva nu are permisiunea sa execute apelul respectiv * Intersect - creaza si returneaza o permisiune care reprezinta intersectia a doua permisiuni * IsSubsetOf - determina daca permisiunea curenta este un subset al unei permisiuni specificate * Union - creaza o permisiune care reprezinta reuniunea permisiunii curente cu o alta permisiune specificata. Configurarea autorizarilor bazate pe roluri Securitatea role-based reprezinta garantarea sau blocarea accesului la o aplicatie sau resursa pe baza unei identitati sau rol al unui utilizator. De exemplu, sa presupunem ca pentru o aplicatie utilizata in mediu universitar, numai profesorii vor avea acces la o anumita sectiune a aplicatiei. Autorizarea bazata pe roluri implementeaza securitatea de acest tip. Clasa Principal In .NET Framework, utilizatorii care se autentifica sunt reprezentati de obiecte Principal. Acestea contin informatii despre identitatea sau rolul utilizatorului si poate fi folosit pentru validarea identitatii fata de un obiect PrincipalPermission, utilizat pentru a proteja partile sensibile ale unei aplicatii fata de utilizatori neautorizati. Exemplu de implementare a unui model de securitate built-in: // WindowsPrincipal reprezinta identitatea // utilizatorului curent AppDomain.CurrentDomain.SetPrincipalPolicy (PrincipalPolicy.WindowsPrincipal); WindowsPrincipal contine o referinta la un obiect WindowsIdentity care reprezinta

Dezvoltare de aplicatii in Visual Studio .NET

Page 167 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

utilizatorul curent. Se pot obtine informatii despre utilizatorul curent accesand proprietatea Identity a obiectului WindowsPrincipal. WindowsPrincipal returneaza proprietatea Identity ca o interfata IIdentity, va fi nevoie, deci, de o conversie explicita la un obiect WindowsIdentity. Exemplu: WindowsPrincipal myPrincipal; myPrincipal = (WindowsPrincipal) System.Threading.Thread.CurrentPrincipal; WindowsIdentity myIdentity; // se preia identitatea WindowsIdentity myIdentity = (WindowsIdentity)myPrincipal.Identity; // se afiseaza numele utilizatorului curent MessageBox.Show(myIdentity.Name); Securitate role-based imperativa (la run-time) Se utilizeaza obiecte PrincipalPermission. Un astfel de obiect poate specifica o identitate si un rol, putand impune faptul ca utilizatorul curent trebuie sa aiba numele si rolul specificat de obiectul PrincipalPermission. Metoda Demand verifica concordanta intre obiectul Principal si numele si rolul specificate de PrincipalPermission. Exemplu: // constructorul are ca parametri // numele si rolul utilizatorului ca stringuri PrincipalPermission myPermission = new PrincipalPermission("John", "Manager"); // CurrentPrincipal trebuie sa contina user-ul John, // cu rol de manager myPermission.Demand(); Utilizarea metodei Union pentru a crea permisiuni care combina doua permisiuni existente. Exemplu: PrincipalPermission Permission1 = new PrincipalPermission("John", "Manager"); PrincipalPermission Permission2 = new PrincipalPermission("Smith", "Group Manager"); PrincipalPermission Permission3; // reuniunea celor doua permisiuni
Page 168 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

Permission3 = (PrincipalPermission)Permission2.Union(Permission1); // John sau Smith vor putea accesa codul programului Permission3.Demand(); La crearea unui obiect PrincipalPermission, se poate specifica null pentru nume sau rol. Aceasta permite crearea unei permisiuni care valideaza numai numele sau numai rolul permisiunii. Exemplu: validarea rolului obiectului CurrentPrincipal (fara validarea numelui): // utilizatorul trebuie sa aiba rol de manager PrincipalPermission myPermission = new PrincipalPermission(null, "Manager"); Intersectia a doua permisiuni: PrincipalPermission Permission1 = new PrincipalPermission(null, "Manager"); PrincipalPermission Permission2 = new PrincipalPermission("John", null); PrincipalPermission Permission3; // intersectia permisiunilor // Permission1 si Permission2 Permission3 = (PrincipalPermission)Permission2.Intersect(Permission1); // Principal-ul care acceseaza acest cod trebuie sa fie John // cu rol de manager Permission3.Demand(); Verificarea apartenentei utilizatorilor la roluri stabilite de sistemul de operare: // se utilizeaza sintaxa cu doua backslash-uri (\\) PrincipalPermission myPermission = new PrincipalPermission("John", "BUILTIN\\Administrators"); Securitate role-based declarativa (in cadrul manifestului assembly-ului) Orice obiect Permission are un atribut corespunzator. Aceste atribute pot fi atasate claselor si/sau membrilor si sunt utilizate pentru a controla accesul la aceste clase si membri. In contextul securitatii declarative, atributele de permisiune sunt atasate membrilor protejati. Administratorul poate lua o decizie care sa permita executia
Dezvoltare de aplicatii in Visual Studio .NET Page 169 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

assembly-ului pe baza metadatei. Fiecare atribut de permisiune contine ca parametru al constructorului un obiect SecurityAction care indica actiunea care se va executa. In securitate role-based , acesta este de obicei o actiune Demand. Exemplu: securitate pentru o metoda myMethod. [PrincipalPermission(SecurityAction.Demand, Name="John", Role="Student")] public void myMethod() { // Method implementation omitted } Configurarea securitatii accesului la cod Securitatea accesarii codului previne executia apelurilor neautorizate catre anumite fragmente de cod. Se pot stabili, de asemenea, nivele de securitate care sa comunice cerintele de securitate administratorului de sistem. La fel ca in cazul securitatii bazate pe roluri, securitatea accesului la cod se bazeaza pe permisiuni. In acest context, o permisiune reprezinta resursele sistemului si controlul accesarii acestor resurse. Un exemplu foarte bun este sistemul de fisiere. In cazul unei aplicatii care utilizeaza scrierea in fisiere, trebuie sa ne asiguram ca apelurile neautorizate nu sunt capabile sa utilizeze anumite resurse, evitandu-se astfel producerea de erori in structura de fisiere. Permisiuni de accesare a codului Fiecare permisiune reprezinta o resursa. Majoritatea permisiunilor se gasesc in spatiul de nume System.Security. Cele mai importante permisiuni referitoare la accesarea codului: * DirectoryServicesPermission - controleaza accesul la Active Directory * EnvironmentPermission - controleaza accesul la variabilele de mediu * EventLogPermission - controleaza scrierea/citirea in/din loguri de evenimente * FileDialogPermission - controleaza accesul la fisiere sau directoare prin intermediul unei ferestre de dialog * FileIOPermission - controleaza crearea, citirea si scrierea in sistemul de fisiere * OleDbPermission - stabileste accesul la o baza de date OleDb * PrintingPermission - controleaza accesul la dispozitivele de printare * ReflectionPermission - controleaza abilitatea claselor din System.Reflection de a extrage informatii despre tipuri la run-time * RegistryPermission - controleaza citirea/scrierea din/in registri * SecurityPermission - stabileste drepturi, incluzand dreptul de a executa cod, controleaza manipularea firelor de executie si a obiectelor Principals, si apelurile de code unmanaged
Page 170 of 180 Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

* SQLClientPermission - stabileste accesul la baze de date Microsoft SQL Server * UIPermission - controleaza accesul la interfata utilizator Crearea permisiunilor de acces la cod Fiecare permisiune contine un anumit set de constructori care permit specificarea resurselor protejate. O permisiune care permite accesul nerestrictionat la resurse este descrisa de flagul PermissionState.Unrestricted. PermissionState.None permite crearea unei permisiuni care restrictioneaza total accesul la resursele specificate. Exemplu: // acces nerestrictionat la resurse Reflection ReflectionPermission myPermission = new ReflectionPermission(PermissionState.Unrestricted); // acces complet restrictionat la resurse UI UIPermission anotherPermission = new UIPermission(PermissionState.None); Fiecare permisiune contine constructori aditionali care permit configurari specifice ale permisiunii. Exemplu: crearea unei permisiuni care reprezinta dreptul de a scrie intr-un singur fisier din sistemul de fisiere: FileIOPermission myPermission = new FileIOPermission(FileIOPermissionAccess.Write, "C:\\aFile.txt"); Membrii clasei CodeAccessPermission Toate permisiunile de acces la cod sunt derivate din clasa CodeAccessPermission. Metode ale clasei CodeAccessPermission : * Assert - se presupune ca fragmentul de cod care apeleaza aceasta metoda poate accesa resursele reprezentate de permisiune chiar daca apelantii aflati la un nivel superior in stiva de executie nu au aceasta permisiune * Demand - impune faptul ca toti apelantii de la un nivel superior in stiva au permisiunea de a accesa resursa reprezentata de aceasta permisiune * Deny - interzice codului apelant al acestei metode sa acceseze resursa reprezentata de aceasta permisiune * PermitOnly - Interzice codului apelant sa acceseze resursa reprezentata de permisiune, cu exceptia unui subset al resursei specificat de permisiune * RevertAll - elimina efectul tuturor metodelor Assert, Deny si PermitOnly apelate anterior * RevertAssert - elimina apelurile Assert anterioare * RevertDeny - elimina apelurile Deny anterioare

Dezvoltare de aplicatii in Visual Studio .NET

Page 171 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

* RevertPermitOnly - elimina apelurile PermitOnly anterioare Securitate imperativa de acces la cod Securitatea imperativa actioneaza la run-time. Metoda Demand este si, in acest caz, metoda principala utilizata. Permisiunea de a accesa resurse protejate este verificata de common language runtime, care consulta politica de securitate a assembly-ului setata de administratorul sistemului. La apelul metodei Demand a unui obiect de tip permisiune, se verifica in stiva de executie daca fiecare dintre apelantii de pe nivele superioare au garantata permisiunea de a accesa resursele reprezentate de permisiune. Deci, un assembly poate avea o metoda care apeleaza o alta metoda protejata de o permisiune de acces la cod. Daca apelul catre cea de-a doua metoda provine de la un assembly securizat (trusted), apelul va fi permis si resursa protejata va putea fi utilizata. In cazul unui assembly untrusted, care nu are permisiunea necesara, apelul se va solda cu esec. Exemplu: // permisiune care reprezinta acces // nerestrictionat la sistemul de fisiere FileIOPermission myPermission = new FileIOPermission(PermissionState.Unrestricted); // toti apelantii acestui cod au permisiune pentru // acces nerestrictionat la sistemul de fisiere myPermission.Demand(); Metoda Deny interzice apelantilor codului accesul la resursa protejata, chiar daca permisiunea era garantata de common language runtime. De exemplu: FileIOPermission myPermission = new FileIOPermission(PermissionState.Unrestricted); myPermission.Deny(); Metoda PermitOnly interzice accesul la resurse, cu exceptia celor specificate de permisiune. Exemplu: // o permisiune care garanteaza accesul la un // fisier specific FileIOPermission myPermission = new FileIOPermission(FileIOPermissionAccess.Write, "C:\\myFile.txt"); myPermission.PermitOnly();

Page 172 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

Metoda Assert. Exemplu: // acces nerestrictionat la // sistemul de fisiere FileIOPermission myPermission = new FileIOPermission(PermissionState.Unrestricted); // asertiune: acces la sistemul de fisiere myPermission.Assert(); Metoda statica Revert: elimina toate apelurile Deny, Assert sau PermitOnly anterioare. Exemplu: ReflectionPermission.RevertAll(); EnvironmentPermission.RevertDeny(); FileIOPermission.RevertAssert(); MessageQueuePermission.RevertPermitOnly(); Securitate declarativa de acces la cod Ca si in cazul securitatii role-based , fiecare permisiune de acces la cod are un atribut corespunzator care poate fi atasat metodelor sau claselor pentru a specifica actiuni de securitate. Aditional, se pot specifica permisiuni pentru intregul assembly. Se specifica in cadrul atributelor o valoare SecurityAction (nu un rol, cum era in cazul securitatii role-based). Exemplu: [FileIOPermission(SecurityAction.Deny)] public class aClass { // Class implementation omitted } Flagurile SecurityAction.Demand, SecurityAction.Deny, SecurityAction.Assert si SecurityAction.PermitOnly corespund metodelor Demand, Deny, Assert si PermitOnly. Exista si actiuni aditionale de securitate, in contextul securitatii declarative. SecurityAction.LinkDemand impune primului apelant al clasei sau metodei sa aiba permisiunea ceruta. SecurityAction.InheritanceDemand verifica daca clasele derivate ale acestei clase sau care suprascriu metoda au permisiunea potrivita. Flag-uri asociate intregului assembly: - SecurityAction.RequestMinimum executa o cerere catre common language runtime
Dezvoltare de aplicatii in Visual Studio .NET Page 173 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

sa indeplineasca o anumita permisiune. In cazul unui raspuns negativ, assembly-ul nu se va executa. - SecurityAction.RequestOptional este similar, dar assembly-ul va rula chiar daca permisiunea nu este indeplinita. - SecurityAction.RequestRefuse cere assembly-ului sa interzica permisiunea specificata. Pentru aceste actiuni, se utilizeaza directiva assembly: [assembly: FileIOPermission(SecurityAction.RequestMinimum)] [FileIOPermission(SecurityAction.Assert, Write="C:\\myFile.txt")] public void WriteFile() { // implementare... } Sinteza * securitate role-based * securitate de acces la cod * clasa Permission * interfata IPermission * securitate imperativa si declarativa * obiecte Principal (pentru securitate role-based ) * resurse sistem (pentru acces la cod) * directiva assembly

4. Instalarea si rularea aplicatiilor


Dupa fazele de proiectare, dezvoltare, compilare si executie a unei aplicatii Visual Studio .NET, urmeaza, in mod firesc, o procedura de instalare si rulare a aplicatiilor pe o anumita masina. Iata, pe scurt, scenarii posibile in cadrul unei astfel de proceduri: * In anumite conditii, o aplicatie .NET se poate instala utilizand comanda XCOPY pentru a copia directorul principal si toate subdirectoarele aplicatiei pe o anumita masina. Acest lucru este posibil (va avea efectul scontat) numai daca pe masina respectiva este instalat .NET Framework si daca aplicatia nu are referinte catre assembly-uri sau resurse partajate. In multe circumstante, XCOPY nu constituie o modalitate viabila de instalare a aplicatiilor.

Page 174 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

* Utilizand Visual Studio .NET, se pot crea Windows Installer setup projects sau Windows Installer merge modules. Un proiect de setup poate fi executat pe un calculator care are instalat Windows Installer 1.5 si care contine toata informatia necesara instalarii unei aplicatii pe un calculator client. Un merge module este utilizat pentru impachetarea fisierelor DLL si nu poate fi instalat aparte, ci numai prin concatenare la un proiect de setup. Setup Project Wizard permite crearea unui proiect de setup care poate fi apoi, eventual, configurat intr-un mediu integrat de dezvoltare (IDE). Pentru a asocia un proiect de setup unei aplicatii, se selecteaza meniul File, Add Project, New Project, Setup And Deployment Projects, Setup Project Wizard. Se creaza astfel un proiect de setup, specificandu-se tipul outputului proiectului si alte fisiere aditionale care vor fi adaugate la proiect. * Pot fi configurate proprietatile de Build ale proiectului, utilizand Setup Property Pages. Se poate specifica modalitatea de impachetare a fisierelor, setarile de compresie, daca se va crea sau nu un bootstrapper, directorul de instalare si setarile Authenticode (referitoare la semnatura aplicatiei). - Output File Name: reprezinta locatia in care va fi plasat fisierul de instalare rezultat in ultima construirii proiectului de setup. Acest fisier are extensia .msi, in cazul aplicatiilor Windows Installer si .msm, pentru Windows Installer merge modules. - gruparea si compresia fisierelor - se pot utiliza fisiere de tip Cabinet (.cab), pentru integrarea fisierelor de instalare in fisiere de o anumita dimensiune (de exemplu, salvarea fisierelor de instalare in fisiere .cab de dimensiune 1.44M) - optiunea Bootstrapper - se selecteaza in cazul in care pe masina pe care se va instala proiectul nu exista Windows Installer 1.5, avand ca efect instalarea acestui program, adaugand la proiectul de instalare 4 fisiere aditionale: Setup.exe, InstMsiA.exe, InstMsiW.exe si Setup.ini. * Dupa ce proiectul de instalare a fost construit, poate fi distribuit intr-o varietate de moduri: cd-uri, dvd-uri, dischete, prin retea, pe web etc. Aplicatiile de tip setup prezinta un nivel inalt de configurabilitate. Pot fi setate proprietatile care furnizeaza informatii legate de originea aplicatiei care va fi instalate si comportamentul aplicatiei la design-time, in fereastra Properties. proprietate Register ofera posibilitatea inregistrarii unei componente COM sau a unui font pe durata instalarii aplicatiei File System Editor permite editarea sistemului de fisiere de pe masina pe care se instaleaza aplicatia (target) Registry Editor permite adaugarea/editarea intrarilor din registrii masinii target File Types Editor permite crearea asocierilor dintre fisiere si programe de pe masina target (exemplu: specificarea programului care va deschide implicit fisiere de tip .wav - Winamp, Media Player etc). User Interface Editor faciliteaza personalizarea interfetei programului de instalare Custom Actions Editor permite stabilirea unor actiuni personalizate pe durata instalarii (fragmente de cod care se vor executa ca raspuns la anumite evenimente lansate pe durata instalarii) Launch Conditions Editor permite adaugarea de criterii/conditii de cautare si executie
Dezvoltare de aplicatii in Visual Studio .NET Page 175 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

(conditiile sunt impuse masinii target, se pot cauta fisiere, intrari in registri sau componente Windows Installer; in functie de respectarea conditiilor impuse si/sau rezultatele cautarii, se permite sau nu executia anumitor actiuni la instalare) utilitarul Ngen.exe permite crearea unei imagini native a aplicatiei/assembly-ului (la jit-compilare, fisierele sunt compilate in cod nativ, ceea ce permite maximum de eficienta, viteza si utilizare a resurselor; Ngen.exe creaza o imagine nativa a aplicatiei, ceea ce duce la posibilitatea obtinerii acestor avantaje) utilitarul Permview.exe permite vizualizarea permisiunilor asociate unui assembly

Page 176 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

Section 12. XML, Web services - XI 1.


...

2.
...

Dezvoltare de aplicatii in Visual Studio .NET

Page 177 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Section 13. ASP.NET - XII 1.


...

2.
...

Page 178 of 180

Dezvoltare de aplicatii in Visual Studio .NET

Presented by developerWorks, your source for great tutorials

ibm.com/developerWorks

Section 14. Appendix ...


...

Dezvoltare de aplicatii in Visual Studio .NET

Page 179 of 180

ibm.com/developerWorks

Presented by developerWorks, your source for great tutorials

Section 15. Bibliografie, referinte Lista materialelor didactice


Nr. 1. 2. 3. 4. Descriere Alexandru PRUTEANU, Dezvoltare de aplicatii in Visual Studio .NET Iasi, 2002 Training Kit MCAD/MCSD 70-316, Developing Windows-Based Applications with Microsoft Visual C# .NET Microsoft Academic resource Kit for .NET Technology, Micosoft Corporation 2003 Tom ARCHER, Andrew WHITECHAPEL, Inside C#, Second Edition, Micosoft Corporation 2002

Colophon
This tutorial was written entirely in XML, using the developerWorks Toot-O-Matic tutorial generator. The open source Toot-O-Matic tool is an XSLT style sheet and several XSLT extension functions that convert an XML file into a number of HTML pages, a zip file, JPEG heading graphics, and two PDF files. Our ability to generate multiple text and binary formats from a single source file illustrates the power and flexibility of XML. (It also saves our production team a great deal of time and effort.) You can get the source code for the Toot-O-Matic at www6.software.ibm.com/dl/devworks/dw-tootomatic-p. The tutorial Building tutorials with the Toot-O-Matic demonstrates how to use the Toot-O-Matic to create your own tutorials. developerWorks also hosts a forum devoted to the Toot-O-Matic; it's available at www-105.ibm.com/developerworks/xml_df.nsf/AllViewTemplate?OpenForm&RestrictToCategory=11. We'd love to know what you think about the tool.

Page 180 of 180

Dezvoltare de aplicatii in Visual Studio .NET

S-ar putea să vă placă și