Sunteți pe pagina 1din 14

1.

Introducere şi un pic de programare obiectuală

În cele ce urmează, vom încerca să ne familiarizăm cu programarea sub Windows.


Aşa cum bănuiţi, având în faţă un sistem de operare grafic, programele pe care le vom
scrie diferă într-o oarecare măsură de cele cu care sunteţi obişnuiţi, În fapt, există câteva
diferenţe semnificative, dintre care putem aminti:
• Windows este un sistem de operare bazat pe interfeţe grafice, deci metodele de
intrare-ieşire alfanumerice, care accesează fişierele standard de intrare şi ieşire nu mai
sunt efective. Aceasta, bineînţeles că nu înseamnă ca nu le vom putea utiliza în
programe.
• Windows este un sistem de operare în care, aşa cum ştiţi, se pot rula mai multe
programe simultan. Se spune că Windows este un sistem de operare multitasking. În
astfel de sisteme de operare, fiecare program (proces) va trebui să fie executat într-un
spaţiu de memorie binedeterminat, fără a avea posibilitatea ca mărimi dintr-un
program să poată accesa adrese din spaţiul altui program. Este motivul pentru care
majoritatea limbajelor noi (C#, Java, etc) au renunţat să ofere programatorilor
posibilitatea manipulării adreselor fizice prin intermediul pointerilor, preferând ân
locul acestora utilizarea referinţelor. Trebuie amintit faptul că, spre deosebire de aşa
numitele programe pe 16 biţi, în Windows orice adresă este generată pe 32, 64 sau
chiar 128 de biţi, depinzând de tipul procesorului cu care este dotat calculatorul.
• Spre deosebire de programarea pe 16 biţi cu care sunteţi obişnuiţi, în Windows
intrările-ieţirile dintr-un program nu sunt în mod necesar sincrone cu execuţia
acestuia. Adică citirea sau afişarea datelor nu se face doar în momentul execuţiei unor
instrucţiuni de intrare-ieşire, ci se poate face şi asincron, în momentul producerii unor
evenimente.
• În general, aplicaţiile Windows sunt bazate exclusiv pe programarea orientată pe
obiecte.
• Marea majoritate a mediilor de programare noi, oferă aplicaţii de tip wizard, care
uşurează semnificativ efortul programatorilor, în special pentru construirea
interfeţelor.

1.1 Windows Form Designer

Şi acum, pe cai. Vom începe prin a studia un limbaj nou, apărut în acest mileniu,
numit C# (se citeşte C sharp). Mediul de programare Microsoft Visual C# 2005 Express
Edition, în care vom lucra în cele ce urmează, oferă un cadru (wizard) pentru construirea
interfeţelor, comun mediilor de dezvoltare de programe de tip Visual al firmei Microsoft,
numit Windows Form Designer. Pentru a vedea ce ne oferă acest wizard, să deschidem un
noi proiect Visula C#, numit unu. Pentru aceasta, vom deschide mediul de programare
Microsoft Visual C# 2005 Express Edition, în caseta Recent Projects vom alege
Create: Project… . Apoi, în caseta New Project apărută, vom alege Windows
Application şi vom apăsa OK. Veţi avea în faţă o aplicaţie ca şi cea prezentată în fig. 1.

1
Figura 1. Interfaţa Form Designer

Se poate observa că în centru imaginii se găseşte o machetă a formei care urmează


să constituie interfaţa grafică a aplicaţiei. Toate controalele necesare proiectării interfeţei
pot fi aduse pe această formă prin mecanismul drag and drop, utilizând tab-ul Toolbox
din stânga imaginii.

Observaţie: Daca tab-ul Toolbox lipseşte, poate fi afişat alegând din meniu View şi
respectiv Toolbox.

În partea dreaptă a imaginii, se pot vedea două panouri. În dreapta sus se găseşte
panoul Solution Explorer, în care popt fi văzute toate proiectele deschise precum şi
fişierele asociate acestora. În dreapta jos, poate fi văzut panoul Properties. Acest panou
conţine toate proprietăţile elementelor selectate, oferind posibilitatea editării acestora fără
prea mare efort.

Înainte de a merge mai departe, va trebui să ne reamintim cîte ceva despre


programarea obiectuală. Vom vedea de asemenea ca există mici diferenţe faţă de ceea ce
am studiat în cursul de C.

1.2 Să ne reamintim…un pic de programare obiectuală

Să ne reamintim că în cadrul programării obiectuale, am introdus un tip nou de


date, care încapsulează un set de atribute şi un set de metode de acces la acestea, numit
clasă. Să ne reamintim de asemenea, că trivial spus, o variabilă de tipul unei clase, sau
mai corect o instanţiere a unei clase, poartă denumirea de obiect. Datorită faptului că
majoritatea aplicaţiilor pot conţine o mulţime de clase şi instanţieri ale acestora, pentru a

2
înţelege structura acestora şi relaţiile dintre ele, a fost dezvoltat un limbaj special de
descriere, numit UML (Universal Modeling Language). Vom încerca în cele ce urmăază,
să introducem şi câteva noţini referitoare la acest limbaj.
Să presupunem de exemplu că
avem o clasa Prăjitură. Reprezentarea Prăjitură Savarina:Prăjitură
UML a acestei clase poate fi văzută în
fig. 2.a. Se poate observa că numele
clasei este scris în partea de sus casetei.
În mod absolut similar, se poate a) b)
reprezenta şi o instanţiere a clasei, fie Figura 2
aeasta Savarină (fig. 2.b). Numele
instanţei este prezentat împreună cu numele clasei, separate prin :.

1.2.1 Proprietăţi şi câmpuri


Proprietăţile şi câmpurile (atributele) asigură accesul la datele conţinute în
interiorul unui obiect. Practic, prin aceasta se pot diferenţia diferite obiecte ce reprezintă
instanţieri ale aceleiaşi clase. Aceste date vor furniza în fapt starea obiectului respectiv.
Atât câmpurile cât şi proprietăţile sunt mărimi de un anumit tip. Un câmp se utilizează
pentru a reprezenta o mărime cantitativă asociată obiectului, iar o proprietate pentru a
limita valoarea acesteia la un anumit interval. De exemplu, vom utiliza un câmp pentru a
reprezenta proprietatea Zahar a obiectului Savarină şi repectiv o proprietate pentru a
limita valoarea acestui câmp, de exemplu între 0 şi 6.
Atât la câmpuri cât şi la proprietăţi, accesul se poate
face în 2 moduri: public sau privat (private). Un membru Prăjitură
public este accesibil întregului cod al programului, în timp ce
un membru privat este accesibil doar codului din interiorul +Faina: string
clasei. Uzual, câmpurile private sunt acei membri ai clasei ale +Drojdie: bool
căror valori sunt supuse unor restricţii. Ei sunt expuşi restului +Lapte: bool
+Zahar: byte
codului (adică accesibili) în general prin intermediul unor
+Altele: string
proprietăţi publice. O reprezentre UML a câmpurilor şi
proprietăţilor poate fi văzută în fig. 3.
Se poate observa că în UML, cea de-a doua casetă
este rezervată câmpurilor şi proprietăţilor. Prefixul + este Figura 3
utilizat pentru a reprezenta caracterul public al câmpului sau
proprietăţii, iar prefixul – pentru a reprezenta caracterul privat. Prefixul este urmat de
numele câmpului sau proprietăţii şi respectiv tipul acestora.

1.2.2 Metode
O metodă este o funcţie expusă de un obiect. Metodele sunt utilizate pentru a
permite accesul la atributele obiectului. Pot fi de asemenea publice sau private. Prin
intermediul metodelor se poate verifica sau modifica starea unui obiect, inclusiv în
situaţia în care accesul se face asupra unor atribute private. În UML, metodele sunt
prezentate în caseta a treia (fig. 4).

3
În fig. 4, a fost adăugată metoda AdaugZahar, care Prăjitură
primeşte ca parametru cantitatea de zahăr adaugata sub +Faina: string
forma unui octet fără semn şi returnează noua valoare a +Drojdie: bool
atributului Zahar. În UML, se utilizează de asemenea +Lapte: bool
+Zahar: byte
identificatorii in, out şi inout pentru a specifica sensul +Altele: string
fluxului de date. +AdaugZahar(in cantitate:byte):byte

1.2.3 Constructori Figura 4


Iniţializarea unui obiect este automată, alocarea
memoriei pentru acesta facându-se fără intervenţia programatorului. Rezervarea
memoriei se face de către o funcţie numită constructor. Constructorul este o funcţie
având acelaşi nume cu numele clasei, care treebuie sa fie neaparat publică şi care poate
sau nu să aibă parametri. Orice clasă are un constructor implicit, care nu are parametri.
Acest constructor, în cazul utilizării programelor de tip wizard, este adăugat de către
acestea şi nu mai trebuie definit. În C#, constructorul unui obiect este apelat prin
intermediul cuvântului new.
Exemplu: Prajitura Savarina = new Prajitura();

O clasă poate expune mai mulţi constructori neimpliciţi, care pot avea parametri.
Aceştia vor fi apelaţi de o manieră smilară.

Exemplu: Prajitura Isler = new Prajitura(“000”, TRUE, TRUE, 150, “?”);

1.2.4 Destructori
Destructorul este metoda care eliberează memoria calculatorului de obiectele a
căror ciclu de viaţă a expirat. Spre deosebire de C++, în C# destructorul nu trebuie
definit. Mediul de programare implementează un colector de reziduri (garbage collector),
care periodic inspectează dacă există memorie alocată unor obiecte care nu mai există şi
realizează eliberarea automată a acesteia.

1.3 Tehnici de programare obiectuală

1.3.1 Interfeţe
În programarea obiectuală, poate apare situaţia în care clase diferite pot avea un
număr de membri (atribute sau metode) comuni, care asigură legătura cu exteriorul. În
această situaţie, aceşti membri pot fi grupaţi împreună, constituind o interfaţă. Deci o
interfaţă este o colecţie de atribute şi metode publice, grupate împreună pentru a
încapsula o funcţionalitate specifică. O interfaţă odată definită, poate fi implementată în
interiorul unei clase.
O interfaţă nu poate exista doar ea insăşi. O interfaţă nu poate fi instanţiată şi în
plus o interfaţă nu poate implementa cod în nici unul din membrii săi.
Ca exemplu, să considerăm încă o clasa, numită Budincă, care expune aceeaşi
membri ca şi clasa Prajitură, diferind doar prin câmpurile Faina respectiv Orez. Toţi
membri comuni vor putea fi încapsulaţi în interfaţa IDulciuri.

4
Prajitura IDulciuri
<<Interface>>
IDulciuri +Faina: string

+Drojdie: bool
+Lapte: bool
+Zahar: byte Budinca IDulciuri
+Altele: string
+Orez: string
+AdaugZahar(in cantitate:byte): byte

Figura 5
O clasă poate utiliza mai multe interfeţe, iar o interfaţă poate fi comună mai
multor clase.

1.3.2 Moştenirea
Moştenirea este cea mai importantă caracteristică a programării obiectuale. Prin
intermediul ei, o clasă, numită clasă
derivată, poate moşteni trăsăturile unei alte Animal
clase, numită clasă de bază, sau superclasă,
pe care le poate extinde şi eventual
+Hraneste()
modifica. Se pot realiza astfel ierarhii de +Reproduce()
clase, fără a fi necesară redefinirea fiecări
clase în parte. Spre deosebire de C++, C#
permite doar moştenirea simplă. În figura 6
este prezentată o astfel de ierarhie de clase.
Clasa Animal este clasa de bază şi expune 2 Pasare Mamifer
metode: Hraneste() şi Reproduce(). Din
această clasă sunt derivate clasele Pasare şi +Zboara() +Fuge()
Mamifer, care moştenesc metodele clasei de +FaceOua() +Alapteaza()
bază, dar adaugă fiecare 2 metode noi:
Zboara() şi FaceOua(), respectiv Fuge() şi Figura 6
Alaptează().
Să ne reamintim accesibilitatea membrilor clasei. Membrii privaţi ai clasei de
bază nu vor fi accesibili claselor derivate, în timp ce membri publici vor fi accesibili atât
claselor derivate cât şi restului codului. Va apare un al treilea tip de acces, respectiv
protejat (protected). Un membru protejat al clasei de bază va fi accesibil claselor
derivate, dar nu va fi accesibil restului codului.
Oricare dintre membrii clasei poate fi definit ca virtual, adică poate fi redefinit
(supraânscris) în clasele derivate. Cu alte cuvinte, un membru virtual poate furniza o
implementare alternativă în clasele derivate. Această implementare alternativă nu şterge
implementarea originală din clasa de bază, care rămâne accesibilă claselor derivate, dar o
ascunde pentru restul codului. Un membru virtual nu poate fi privat, pentru că în acest
caz nu ar fi accesibil claselor derivate pentru a fi redefinit. Dacă un membru este virtual,
el va apare în diagrama UML a clasei derivate, având evident o altă implementare.
Metoda Hraneste() este implementată virtual în clasa Mamifer (fig. 7).

5
Există situaţii în care anumite metode virtuale ale clasei de bază nu pot fi
implementate. Ele trebuie declarate, pentru a fi redefinite în clasele derivate. Astfel de
metode se numesc virtuale pure. O clasă care conţine cel puţin o metodă virtuală pură
este o clasă abstractă. O clasă abstractă nu poate fi instanţiată, deci nu se pot defini
obiecte de acel tip. În UML numele unei clase abstracte se reprezintă prin caractere
italice.
O clasă poate fi sigilată (sealed). O
astfel de clasă nu poate fi folosită ca şi clasă Animal
de bază, sau cu alte cuvinte, din ea nu se pot
deriva alte clase.
+Hraneste()
Ierahia de clase este pusă în evidenţă +Reproduce()
în numele unei clase prin specificarea
numelui tuturor claselor din amonte,
separate prin punct. Vom avea astfel, în
exemplul nostru Animal.Pasare, respectiv
Animal.Mamifer, ş.a.m.d. Pasare Mamifer

1.3.3 Polimorfismul
+Zboara() +Fuge()
Ca o consecinţă a moştenirii, clasele +FaceOua() +Alapteaza()
+Hraneste()
de bază şi clasele derivate pot realiza o
suprapunere a metodelor şi proprietăţilor pe
Figura 7
care le expun. Atfel, obiecte instanţiate din
clase având aceeaşi clasă de bază, pot fi
tratate în comun. De exmplu, cum clasa Animal expune metoda Hraneste(), aceasta va
putea fi expusă prin aceeaşi sintaxă de obiecte ale claselor derivate:

Mamifer pisica = new Mamifer();


Pasare vrabie = new Pasare()
pisica.Hraneste();
vrabie.Hraneste();

Mai mult, prin intermediul polumorfismului, vom putea atribui unui obiect al
clasei de bază, un obiect al unei clase derivate, fără a fi nevoie de nici o transformare
explicită de tip:
Animal unanimal = pisica;
unanimal.Hraneste();

Astfel, la apelarea metodei Hraneste() prin intermediul obiectului clasei de bază,


surprinzător, va fi apelată metoda clasei derivate. Dar, o metodă definită în clasa derivată,
nu va putea fi apleată. Astfel, codul de mai jos, va genera eroare.
unanimal.Fuge();

Această metodă va fi expusă doar printr-o conversie explicită de tip:


Mamifer vulpe = (Mamifer) unanimal;
vulpe.Fuge();

6
1.3.4 Relaţii între obiecte
Moştenirea reprezintă o relaţie simplă între obiecte, în care membrii unei clase de
bază sunt expuşi complet de obiecte aparţinând unor clase derivate. Există însă şi alte
situaţii în care relaţiile între obiecte devin importante:
• Continut (containement) – situaţie în care o clasă conţine altă clasă. Este oarecum
sismilară moştenirii, dar permite clasei care conţine să controleze accesul la membrii
clasei conţinute şi chiar să execute acţiuni suplimentare înaninte de a permite
utilizarea membrilor clasei conţinute.
• Colecţie (collection) – situaţie în care o clasă se manifestă ca şi container pentru
instabţe multiple a altei clase. Este similar cu a avea tablouri unidimensionale de
obiecte, asupra cărora se pot executa indexări, sortări, etc.

1.3.5 Conţinerea
Conţinerea este simplu de realizat prin declararea unui obiect ca şi câmp al unei
clase. Acest câmp trebuie să fie public, pentru
ca utilizatorii obiectului conţinut să aibă acces Alapteaza
1
la metodele şi proprietăţile expuse de acesta.
Ca alternativă, acest câmp poate fi
+Lapte()
privat. Astfel, nici unul din membrii clasei din
care face parte acel obiect nu va fi accesibil.
Accesul la aceştia se va putea face doar prin
intermediul membrilor clasei care conţine. Animal 1
Astfel, controlul asupra membrilor clasei
contineAlapteaza: Alapteaza
conţinute va fi total, putându-se controla care
din ei sa fie expuşi şi care nu. +Hraneste()
+Reproduce()
De exmplu, clasa Mamifer poate
conţine clasa Alaptează,care are metoda
publică Lapte(). Clasa Mamifer poate apela Figura 8
această metodă dacă este nevoie, de exmplu
prin intermediul metodei Hraneste() (fig. 8).

1.3.6 Colecţia
Un tablou unidimensional de obiecte de acleaşi tip
poate fi declarat ca mai jos: Animal
0..*

Animal[] animale = new Animal[5];

O colecţie este în esenţă un şir de elemente de


acelaşi tip. Colecţiile sunt implementate în clase de aceeaşi
manieră ca şi alte obiecte şi sunt numite în general prin 1
Animale
pluralul obiectelor pe care le stochează. Spre exemplu, o
colecţie de obiecte de tip Animal, va putea fi numotă
Animale. Complexitatea unei colecţii este superioară unui
simplu tablou unidimensional, prin faptul că Figura 9

7
implementează de obicei metode de tipul Add() şi Remove() care adaugă sau şterg
elemente din colecţie. De asemenea, este în general utilizată o proprietate de tip Item,
care returnează obiectul de la un indice dat. Reprezentarea UML a unei astfel de colecţii
poate fi vazută în fig. 9. Prin marcajul 0..* se specifică faptul ca colecţia Animale va
conţine zero sau mai multe obiecte de tip Animal.
O descriere pe larg a POO, în sens clasic, puteţi găsi în Anexa 1.

1.3.7 Evenimente
Obiectele pot genera evenimente (events). Pentru aceasta, codul va trebui
completat cu un handler (event handler), care în fapt este o funcţie specială, ce va trebui
apelată pentru generarea unui eveniment specific. Acestă funcţie, trebuie de asemnea să
fie capabilă să asculte (listen) pentru a detecta producerea evenimentului specific. În
acest mod, se poate crea o aplicaţie orientată pe evenimente (event-driven). Aplicaţiile
Windows sunt în totalitate aplicaţii conduse de evenimente. Orice acşiune asupra
interfeţei, apăsarea unui buton, scrierea unei litere, mişcarea mouse-lui, va genera un
astfel de eveniment. Astfel, intrările-ieşirile din program devin asincrone cu execuţia lui.

1.4 Să revenim la forme….

E timpul să începem să vedem cu ce ne ajută Form Designer în construirea unei


aplicaţii Windows.

1.4.1 Toolbox
Prin intermediul Toolbox-ului, programatorul are
la dispoziţie toate tipurile de controale necesare pentru
construirea unei aplicaţii. Toolbox-ul poate fi
particularizat pentru a satisface nevoile programatorului,
dar pe moment vom studia moful de utilizare al toolbox-
ului implicit.

Figura 10. Toolbox


1.4.2 Controale
Marea majoritate a controalelor sunt obiecte de clase derivate din clasa
System.Windows.Forms.Control. Această clasă furnizează funcţionalitatea de bază
pentru controale, de aceea multe din proprietăţile şi evenimentele controalelor, chiar dacă
acestea sunt structural diferite, vor fi identice. La rândul lor, clasele care definesc unele
controale, pot fi clase de bază pentru alte controale, mai complexe.

1.4.3 Proprietăţi ale controalelor


Toate controalele vor dispune de un set de proprietăţi, care definesc
comportamentul acestora. Clasa de bază Control va furniza un set de proprietăţi,
moştenite şi eventual supraânscrise de toate controalele. În cele ce urmează, vom
prezenta un set de astfel de proprietăţi, furnizate de clasa Control, deci comune tuturor
controalelor. O descriere detaliată a acestor proprietăţi poate fi găsită în MSDN.

8
• Anchor – prin această proprietate se poate specifica comportamenul controlului la
redimensionarea ferestrei ce-l conţine. Ancorarea lui de una din marginile ferestrei va
face ca poziţia acestuia faţă de marginea respectivă să nu se schimbe. Ancorarea lui
de toate marginile ferestrei va avea ca efect redimensionarea controlului odată cu
redimensionarea ferestrei.
• BackColor – stabileşte culoarea de fond a controlului.
• Bottom – stabileşte distanţa de la marginea de sus a ferestrei la marginea de jos a
controlului.
• Dock – ataşează controlul de una din marginile ferestrei.
• Enabled – activează controlul, adică permite acestuia să recepţioneze evenimente de
la utilizator. Dacă această proprietate este FALSE, controlul este inactiv.
• ForeColor – defineşte culoarea textului.
• Height – defineşte înălţimea controlului.
• Left – defineşte marginea stângă a controlului, relativ la marginea stângă a ferestrei.
• Name – identificatorul controlului. Poate fi utilizat în cod pentru a individualiza şi
accesa controlul.
• Parent – părintele controlului.
• Right - defineşte marginea dreaptă a controlului, relativ la marginea stângă a
ferestrei.
• TabIndex – numărul asociat controlului în ordinea de tab. Ordinea de tab stabileşte
ordinea in care controalele primesc input focusul (se selectează) la apăsarea tastei
TAB.
• TabStop – Stabileşte dacă controlul primeşte input focusul la apăsarea tastei TAB.
Dacă controlul nu are această proprietate, la apăsarea tastei TAB va fi sărit.
• Tag – este o proprietate care în general nu este utilizată de control. Poate fi doar un
şir de caractere, care este stocat în interiorul controlului.
• Top - stabileşte distanţa de la marginea de sus a ferestrei la marginea de sus a
controlului.
• Visible – dacă această proprietate este TRUE, controlul este vizibil. În caz contrar,
deşi există în formă, el nu poate fi vazut.
• Width - stabileşte lăţimea controlului.

1.4.4 Evenimente generate de controale


Atunci când utilizatorul acţionează asupra unui control (apasă un buton,
selectează o opţiune radio, scrie într-o listă, etc) aplicaţia trebuie să fie capabilă să
sesizeze faptul ca s-a acţionat asupra controlului respectiv, să identifice tipul acţiunii şi să
execute o secvenţă de cod care să rezolve problema în concordanţă cu tipul acţiunii.
Pentru a sesiza acţiunea asupra lor, controalele generează evenimente. Clasa Control
implementează un set de evenimente comune tuturor controalelor, iar acestea, la rândul
lor, vor genera şi un set de evenimente specifice. Cele mai uzuale evenimente sunt
prezentate mai jos, lista completă a acestora fiind disponibilă în MSDN.

• Click – este generat când se apasă click cu mouse-ul asupra unui control. Poate fi
uneori generat şi de apăsarea tastei Enter.

9
• DoubleClick – este generat cînd se apasă dublu click asupra unui control. Dacă
controlul este buton, acest eveniment nu va putea fi generat, pentru ca la prima
apăsare se generează automat Click.
• DragDrop – este generat când se finalizează (este eliberat butonul mouse-lui) o
operaţie drag-and-drop în care este implicat controlul.
• DragEnter – este generat atunci când obiectul implicat într-o operaţie drag-and-drop
ajunge în interiorul controlului.
• DragLeave - este generat atunci când obiectul implicat într-o operaţie drag-and-drop
părăseşte suprafaţa controlului.
• DragOver – este generat când un obiect implicat într-o operaţie drag-and-drop
ajunge deasupra controlului.
• KeyDown – este generat când o tastă devine apăsată în timp ce controlul deţine input
focusul. Se generează întotdeauna înainte de KeyPress şi KeyUp.
• KeyPress - este generat când o tastă devine apăsată în timp ce controlul deţine input
focusul. Se generează întotdeauna după KeyDown şi înainte de KeyUp. Spre deosebire
de KeyDown care furnizează codul de scanare al tastei apăsate, KeyPress furnizează
codul ascii al tastei.
• KeyUp - este generat când o tastă este eliberată în timp ce controlul deţine input
focusul. Se generează întotdeauna după KeyDown şi KeyPress.
• GotFocus – este generat când controlul primeşte input focusul.
• LostFocus – este generat când controlul pierde input focusul.
• MouseDown – este generat când prompterul mouse-lui este deasupra controlului şi
se apasă o tastă a mouse-lui.
• MouseMove – este generat continuu atâta timp cât prompterul mouse-lui traversează
controlul.
• MouseUp - ste generat când prompterul mouse-lui este deasupra controlului şi se
eliberează o tastă a mouse-lui.
• Paint – se generează la desenarea controlului.
• Validated – este generat când un control este în curs de a primi focusul. Se
generează după ce evenimentul Validating se termină şi indică faptul că validarea
controlului este completă.
• Validating - ste generat când un control este în curs de a primi focusul.

1.5 Primul exemplu


Să încercăm acum să aplicăm ce-am învăţat într-un prim program Windows. Vom
completa proiectul unu, deschis la paragraful 1.1. Pentru început, să adăugăm formei un
buton. Îl vom trage din caseta common controls şi îl vom poziţiona în colţul stânga jos al
formei. Îl vom mări astfel încât sa ajunngă de formă pătrată. Observaţi că Form Designer
i-a asociat eticheta button1. O putem schimba, modificând poziţia corespunzătoare în
caseta de proprietăţi. Căutăm proprietatea Text şi introducem textul Activează (fig. 11).
Astfel, am schimbat în mod static o proprietate a obiectului buton, numit de către Form
Designer button1, care este un obiect al clasei Button.

10
Urmează să-i asociem un handler de eveniment. Dacă apăsăm dublu click pe
buton, Form Designer va adăuga un handler asociat evenimentului Click şi ne va permite
să-i completăm codul. Observăm că s-a generat următorul cod:

Proprietatea Text

Figura 11

namespace unu
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)


{

}
}
}

Metoda cu codul bold este handlerul asociat evenimentului Click produs de


butonul button1. Ea primeşte ca parametri obiectul care produce acel eveniment şi
respectiv un obiect EventArgs. Codul implementat în interiorul funcţiei va fi executat la
apăsarea butonului button1. Ce observăm? Şi în C#, o funcţie returnează un tip (void în
cazul nostru), are un nume şi un număr de parametri formali. În acest caz special, funcţia
fiind un handler, ea nu trebuie apelată, fci este lansată în execuţie de producerea
evenimentului asociat.
Haideţi să încercăm să adăugăm noi handleri pentru alte evenimente produse de
buton. De exemplu, să interceptăm evenimentele generate de intrarea prompterului

11
mouse-lui în suprafaţa butonului, respectiv părăsirea acesteia. Aceste evenimente sunt
MouseHover şi MouseLeave. Vom completa codul ca mai jos:

public Form1()
{
InitializeComponent();
button1.MouseHover += new EventHandler(button1_MouseHover);
button1.MouseLeave += new EventHandler(button1_MouseLeave);
}

private void button1_MouseHover(object sender, EventArgs e)


{

}
private void button1_MouseLeave(object sender, EventArgs e)
{

Ce am făcut? Am adăugat aplicaţiei doi noi handleri, asociaţi celor 2 evenimente


şi le-am definit funcţiile asociate. Observaţi că funcţiile poartă numele declarate ca
parametru pentru metoda EventHandler(). Şi acum, să completăm codul celor 2 funcţii.

public partial class Form1 : Form


{
private Color culoare;
public Form1()

private void button1_MouseHover(object sender, EventArgs e)
{
culoare = button1.BackColor;
button1.BackColor = Color.DarkGray;
}
private void button1_MouseLeave(object sender, EventArgs e)
{
((Button)sender).BackColor = culoare;
}
Am adăugat clasei Form1 o variabilă privată culoare, care este un obiect de clasa
Culoare. În funcţia button1_MouseHover(), am atribuit acestei variabile valoarea
atributului BackColor al obiectului button1 şi apoi am modivicat valoarea acestui atribut
la culoarea DarkGray. În metoda button1_MouseLeave(), am refăcut vechea culoare a
butonului. Astfel, am modificat atributul butonului în mod dinamic. Ce observăm?
Atributele unei clase sunt adăugate la fel ca şi în C++.

Observaţii:
1. Observaţi că în timp ce scriem codul, Form Designer ne oferă informaţii asupra
codului şi îl poate completa singur (la apăsarea tastei TAB).
2. Se poate vedea că atributul BackColor l-am modificat în 2 moduri: fie prin intermediul
numelui button1, fie cu secvenţa ((Button) sender). Este corect oricum, pentru că
handlerul primeşte în argumentul sender numele obiectului care a generat evenimentul.

12
Compilaţi programul (Build, Build Solution) şi executaţi-l (Debug, Start Without
Debugging). Observaţi efectul trecerii prompterului mouse-lui pe deasupra butonului.
Haideţi să încercăm acum să creăm dinamic, prin program, un nou buton. Acesta
va fi creat la apăsarea butonului button1, deci codul va fi implementat în handlerul
asociat acestui eveniment.

private void button1_MouseLeave(object sender, EventArgs e)
{
((Button)sender).BackColor = culoare;
}

private void button1_Click(object sender, EventArgs e)


{
Button butonnou = new Button();
butonnou.Width = 120;
butonnou.Height = 100;
butonnou.ForeColor = Color.Red;
butonnou.Text = "Apasa-ma si pe mine!";
butonnou.Click += new EventHandler(butonnou_Click);
Controls.Add(butonnou);
}

private void butonnou_Click(object sender, EventArgs e)


{
}

Ce am făcut? Am creat un nou buton, numit butonnou, apelînd constructorul


clasei Button. Am setat atributele Width, Height, ForeColor şi Text pentru acest obiect,
am creat un handler pentru evenimentul Click şi apoi am adăugat efectiv noul obiect
formei, prin intermediul metodei Add() a clasei Controls. Recompilaţi proiectul (Build,
Rebuild Solution) şi executaţi-l. Ce se întîmplă cind apăsaţi pe buton?
Ultimul lucru pe care îl mai avem de făcut este să completăm handlerul
butonnou_Click().


private Color culoare;
private bool apasat = false;

private void butonnou_Click(object sender, EventArgs e)
{
int i;
if (apasat == false)
{
((Button)sender).ForeColor = Color.Green;
apasat = true;
for (i = 0; i < 60; i++)
{
int j, k;
j = 1000;
while (j >= 0)
{
k = 10000;

13
do
k--;
while (k >= 0);
j--;
}
((Button)sender).Width--;
((Button)sender).Height--;
}
}
else
{
((Button)sender).ForeColor = Color.Red;
apasat = false;
for (i = 0; i < 60; i++)
{
((Button)sender).Width++;
((Button)sender).Height++;
}
}
}

Codul nu reprezintă o problemă. Ce ar fi de observat, este că tipurile simple din


C# sunt aceleaşi ca şi cele din C++. Mai mult, la fel variabilele trebuie declarate înainte
de a fi utilizate, iar domeniul lor de vizibilitate este similar. Operatorii ++ şi --
funcţionează similar. De asemenea, instrucţiunile if-then, for, do-while şi while au
formatul absolut similar celui din C++. Fără a apare în acest cod, se poate spune că şi
instrucţiunea switch este similară.

Şi acum, putem trece la studiul diferitelor controale. Dar asta, în capitolul


următor.

14

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