Sunteți pe pagina 1din 10

www.cartiaz.

ro – Carti si articole online gratuite de la A la Z

Programarea aplicaţiilor Windows în .NET Framework


.NET Framework asigură şi uneltele şi tehnologiile necesare dezvoltării aplicaţiilor distribuite, atât
Windows, cât şi Web, inclusiv servicii Web.

Prin arhitectura sa .NET Framework asigură suportul necesar proceselor de compilare şi rulare,
necesare dezvoltării aplicaţiilor .NET.

În acest articol îmi propun să prezint aspecte legate de dezvoltarea aplicaţiilor Windows.

Tipuri de aplicaţii Windows

În .NET putem vorbi de două tipuri de aplicaţii: Console Applications şi Windows Applications.

O singură aplicaţie poate avea elemente caracteristice celor de tip consola cât şi celor Windows. De
exemplu, într-o aplicaţie consolă putem să introducem o fereastră MessageBox. Compilatorul C# face
diferenţa dintre cele două tipuri de aplicaţii printr-un switch numit "target" de pe linia de comandă a
acestuia.

/target:exe - pentru aplicaţii consolă

/target:winexe - pentru aplicaţii Windows

Switch-ul target poate lua şi valorile, "libray" sau "module" dacă vrem să generăm module de cod.

Dacă un executabil marcat ca fiind de tip consolă şi este lansat în execuţie direct din Windows, atunci
Windows-ul va crea o fereastră consolă pentru aplicaţie. Dacă aplicaţia este pornită dintr-o fereastră
Windows, la lansarea în execuţie Windows nu se va deschide nici o fereastră consolă.

Hello World

Aşa ar arăta clasicul "Hello World" scris în C#, ca aplicaţie consolă:

using System;
namespace ConsoleApplication
{
class MyClass
{
static void Main(string[] args)
{
Console.WriteLine("Hello World");
}
}
}

Punctul de start în orice aplicaţie .NET Framework este metoda statică Main. Dacă într-un proiect
există două clase, fiecare cu câte o metodă statică Main, atunci trebuie specificat compilatorului va fi
folosită ca punct de start pentru firul de execuţie principal.

MessageBox

Dacă dorim să afişăm ferestre de dialog standard, de tipul "MessageBox", putem modifica codul astfel:

using System;
namespace ConsoleApplication
www.cartiaz.ro – Carti si articole online gratuite de la A la Z

{
class MyClass
{
static void Main(string[] args)
{
System.Windows.Forms.MessageBox.Show("Hello World");
}
}
}

MessageBox este o clasă din namespace-ul: System.Windows.Forms.

Metoda Show face sa apară pe ecran fereastra de dialog. Metoda a fost suprascrisă, având astfel mai
multe variante, la care diferă lista de parametri.

Forme

În lumea reală, aplicaţiile Windows nu se rezumă doar la MessageBox, ci folosesc aşa numitele
ferestre. În .NET ferestrele sunt implementate de către form-uri. Pentru a crea o fereastră trebuie să
instanţiem o clasă "Form" sau una derivată din aceasta. Clasa form o găsim definită în namespace-ul
"System.Windows.Forms". După instanţiere, o putem afişa în două moduri: prin apelul metodei "Show"
sau prin setarea proprietăţii "Visible" la valoarea True.

using System;
using System.Windows.Forms;
public class NewForm
{
public static void Main()
{
Form form=new Form();
form.Show();
}
}

Ce este cu bucla de mesaje? Este un ciclu "while" care se ocupă cu extragerea şi tratarea mesajelor
din coada de mesaje, În . NET Framework un astfel de mecanism este implementat cu ajutorul clasei
"Application".

public class NewForm


{
public static void Main()
{
Form form=new Form();
form.Show();
Application.Run(form);
}
}

Daca se doreşte terminarea aplicaţiei, se apelează metoda statică "Application.Exit()", care trimite
mesajul WM_QUIT în coada de mesaje a aplicaţiei.

Deşi se pot crea forme direct din clasa "Form", este preferabil să folosim clase care o moştenesc-cazul
designer-ul de forme din Visual Studio.NET.

Să creăm un proiect nou de tip "Windows Application" în C#, iar în forma creată automat, adăugăm un
textbox şi un button, prin "tragerea" lor de pe ToolBox.
www.cartiaz.ro – Carti si articole online gratuite de la A la Z

Între directivele #region şi #endregion, găsim codul generat automat de VS.NET, la adăugarea celor
două controale pe suprafaţa formei. Acolo unde începe definirea clasei, observăm declaraţiile celor
două obiecte - controalele adăugate.

IDispose

Înainte de a continua, fac o mică paranteză. Metoda "Dispose" este declarată în interfaţa "IDispose". În
C# avem operatorul "new", dar nu avem complementarul său "delete". Asta deoarece cu dealocările
în .NET Framework se ocupă "Garbage Collector". Prin urmare nu avem un control strict când anume
şi în ce ordine vor fi dealocate obiectele la care nu mai există nici o referinţă sau au ieşit din domeniul
lor de vizibilitate. .NET Framework, pune la dispoziţie o interfaţă "IDispose", care poate fi folosită în
mecanismul de moştenire, prin metoda "Dispose". Când un obiect urmează a fi dealocat, "Garbage
Collector" verifică dacă acel obiect suportă interfaţa "IDispose" şi dacă da, atunci apelează metoda
"Dispose".

"Anchor" şi "Dock"

Toate controale, au în .NET două proprietăţi, ceva cu totul nou faţă de cum erau obişnuiţi programatorii
în Visual Basic 6.0, care scutesc de o grămadă de efort: anchor - permite unui control să păstreze
distanţa absolută faţă de una sau mai multe margini ale containerului în care se află, prin
redimensionarea sa; dock - un control este efectiv, lipit de una din marginile containerului său,
modificându-şi şi dimensiunea. Folosind această proprietate putem zice că un control capătă
comportamentul unui ToolBar.

Tratarea evenimentelor

Un control sau o formă devin mai utile când putem să şi captăm evenimentele generate de acestea.
Dacă vrem să tratăm evenimentul "Click" pe butonul din formă, se selectează tab-ul events din
fereastra de proprietăţi a butonului şi se dă dublu-clic pe evenimentul "Click". În acel moment VS.NET
generează codul necesar captării evenimentului. Sau am putea scrie manual codul respectiv. Se
adaugă o metoda care va trata evenimentul generat de acţiunea de clic pe buton:

private void button1_Click(object sender,


System.EventArgs e)
{
MessageBox.Show("Button");
}

Iar pentru cuplarea acestei metode la evenimentul click trebuie executată următoarea secvenţă:

this.button1.Click += new System.EventHandler(this.button1_Click);

Să zicem că la un moment nu mai vrem să captăm evenimentele de clic pe buton. Pentru aceasta
trebuie executată următoarea secvenţă:

this.button1.Click -= new

System.EventHandler(this.button1_Click);

Moştenirea formelor

Sa zicem că avem de dezvoltat o aplicaţie în care există mai multe forme de introducere a datelor.
Unele seamănă între ele, singura diferenţă fiind un control nou sau un set nou de butoane. În .NET
Framework, o formă este descrisă de o clasă. Prin urmare, formele se pot moşteni. Pentru aceasta,
creăm o formă care ar reprezenta intersecţia formelor finale, după care creăm celelalte forme pe baza
acesteia. Plecând de la forma pe care tocmai am creat-o să creăm una nouă care mai are doar un
www.cartiaz.ro – Carti si articole online gratuite de la A la Z

buton în plus. Mai întâi trebuie dat un "Build" la soluţia noastră ca să avem generat un assembly pe
baza căruia se va genera noua formă. Într-un assembly, o formă se găseşte ca metadate. Astfel putem
să moştenim o formă în C# care a fost creată în VB sau în Cobol.

Se selectează din meniul "Project", "Add Inherited Form…", după care va apărea o fereastră (vezi
figura de sus), unde putem să schimbăm numele fişierului. După ce apăsăm ok, din următoarea
fereastră de dialog trebuie să selectăm forma şi assembly-ul în care se găseşte forma pe care vrem să
o moştenim. Mai adăugăm aici un buton cu proprietatea "Test" la valoarea "Afişează":

Se observă aici că avem cele două controale moştenite. Proprietăţile acestora apar în culoarea gri şi
sunt read-only, deoarece cele două controale au fost implementate în forma de bază ca proprietăţi de
tip "private". Puteam opta şi pentru "protected"sau "public".

Ferestre de dialog

Ferestrele de dialog sunt tot nişte forme, dar care sunt afişate "modal", adică până la închiderea
casetei de dialog, execuţia programului nu poate continua cu o alta formă.

Crearea formelor modale, pe partea de design nu presupune nimic în plus faţă de crearea formelor
obişnuite. Diferenţa apare la modul în care se vor afişa. La cele nemodale, afişarea se face prin apelul
metodei "Show" sau prin setarea proprietăţii "Visible" pe true. În cazul celor modale, vom apela metoda
www.cartiaz.ro – Carti si articole online gratuite de la A la Z

"ShowDialog()". Aceasta întoarce o valoare de tip enumeraţie, "DialogResult", la fel ca-n cazul clasei
"MessageBox".

Dacă dorim ca un buton să aibă comportamentul butonului "OK", atunci trebuie să atribuim proprietăţii
"AcceptButton" o referinţă către butonul respectiv:

this.AcceptButton=button1;

În cazul butonului "Cancel", trebuie să atribuim proprietăţii "CancelButton" o referinţă către butonul
respectiv:

this.CancelButton=button2;

Ferestre de dialog standard

Aproape toate aplicaţiile Windows afişează pe ecran o fereastră de dialog standard "OPEN",
implementată în Windows, şi care poate fi folosită de orice aplicaţie. În .NET Framework avem la
dispoziţie un set de clase care ne facilitează accesul la ferestrele de dialog standard din Windows.
Acestea sunt:

OpenFileDialog Folosită în cazul deschiderii de fişiere, "Open" din


meniul "File".
SaveFileDialog Folosită pentru a selecta un nume şi o cale unde
se doreşte să se salveze fişierul.
ColorDialog Folosită pentru selectarea unei culori dintr-o paletă
de culori.
FontDialog Folosită pentru selectarea unui font.
PrintDialog Folosită pentru a selecta imprimanta pe care
urmează a se efectua tipărirea.
PageSetupDialog Folosită pentru a modifica setările imprimantei.
PrintPreviewDialog Folosită pentru pre-vizualizarea documentelor care
se doresc a fi tipărite.

Validarea datelor şi controlul ErrorProvider

Mecanismul de validare a datelor este asemănător cu cel din Visual Basic 6. Toate controalele folosite
la preluarea datelor de la utilizator au un eveniment numit "Validating", lista de parametri pentru acest
eveniment (mai exact a lui delegate şi nu a evenimentului ca atare) este:

(object sender, System.ComponentModel.CancelEventArgs e)

Dacă într-un control datele introduse sunt eronate atunci în metoda de tratare a evenimentului
"Validating" pentru acel control, putem executa:

e.Cancel=true;

În urma acestei execuţii controlul nu-şi mai pierde focusul, obligând utilizatorul să introducă o alta
valoare.

Evenimentul "Validating" se declanşează când se cere schimbarea focusului pe alt control, dar numai
dacă următorul control are proprietatea "CauseValidation" setată pe true.
www.cartiaz.ro – Carti si articole online gratuite de la A la Z

În cazul aplicaţiilor Web a devenit o modă ca dacă se introduce o valoare greşită într-un control dintr-
un formular, în urma validării să se afişeze un mic semn (ceva cu roşu). În .NET Framework avem la
dispoziţie un control care face acelaşi lucru, dar pentru aplicaţiile Windows. Acest control este
"ErrorProvider" şi este o alternativă la "MessageBox". Printre proprietăţile lui putem enumera:

-ContainerControl: reprezintă o referinţă către containerul său. Implicit se referă la forma care conţine
controlul;

-DataSource: un ErrorProvider este capabil să afişeze şi erorile care se găsesc într-un DataSet. Vom
seta această proprietate doar dacă vrem sa o folosim cu un DataSet;

-Icon: Iconul implicit în caz de eroare este o mica bulină roşie cu semnul exclamării alb în interior.
Poate fi schimbat printr-o referinţă către un nou obiect de tip Icon.

Acest control expune şi o metodă, care face să apară sau să dispară semnul de eroare.

De exemplu, errorProvider1.SetError(textBox1,"Not an integer value") face ca în partea dreaptă a


controlului "textBox1" să apară un icon care indică o eroare, iar dacă poziţionăm mouse-ul deasupra
icoanei, va apărea sub formă de "ToolTip" mesajul "Not an integer value".

Crearea controalelor

În .NET Framework controalele Windows Forms sunt componente reutilizabile care încapsulează o
funcţionalitate user-interface şi sunt folosite în aplicaţii client-ceva similar cu ActiveX.

Când construim un control avem la dispoziţie trei variante:

Extinderea unui control existent

Dacă se doreşte ca plecând de la un control de tip "TextBox", să creăm un control similar cu un


"TextBox", dar care acceptă numai valori numerice.

public class NumericTextBox : System.Windows.Forms.TextBox

În C# constructorii nu sunt moşteniţi. Din această cauză când implementăm constructorul noii clase,
trebuie să apelăm constructorul clasei de bază:

public NumericTextBox():base()
{
...

Fiind derivată din TextBox, clasa noastră va avea acces la toate proprietăţile, metodele şi
evenimentele clasei "TextBox". În cazul nostru suntem interesaţi doar de captarea intrării de la
utilizator şi de filtrarea acesteia ca să eliminăm tot ce este alfabetic, adică trebuie să rescriem metoda
OnKeyPress:

protected override void OnKeyPress(KeyPressEventArgs e)


{
int asciiInteger=Convert.ToInt32(e.KeyChar);
if(asciiInteger>=47 && asciiInteger<=57)
{
e.Handled=false;
return;
}
if(asciiInteger==8)
{
www.cartiaz.ro – Carti si articole online gratuite de la A la Z

e.Handled=false;
return;
}
e.Handled=true;
}

Controale compuse

Este situaţia în care controlul este un container pentru alte controale. Clasa acestui control trebuie
derivata din: System.Windows.Forms.UserControl

Pentru această categorie avem suport şi din partea designerului existent în VS.NET. Cu el,
dezvoltarea unui control compus este similară cu dezvoltarea unei forme.

Custom Contols

Aici discutăm despre controalele care nu moştenesc alte controale şi care se desenează singure prin
apeluri directe către GDI+. Clasa acestor controale trebuie derivată din:
System.Windows.Forms.Control.

Proprietăţi cu atribute

Pentru fiecare proprietate sau eveniment al unui control sau pentru clasa controlului putem specifica
următoarele atribute:

Browsable Indică dacă proprietatea sau evenimentul va fi afişat


în fereastra de proprietăţi
Category Afişează proprietatea respectivă într-o anumită
categorie în fereastra de proprietăţi
Description Afişează o scurtă descriere în fereastra de
proprietăţi când este selectată proprietatea
respectivă
DefaultProperty Specifică la nivelul clasei care este proprietatea
implicită. La un "TextBox", implicită este
proprietatea "Text"
DefaultValue Specifică o valoare implicită pentru proprietate
TypeConverter Specifică ce convertor să se folosească la citirea şi
afişarea informaţiilor în fereastra de proprietăţi.
Editor Specifică editorul folosit în fereastra de proprietăţi
pentru o anumită proprietate.
RefreshProperties Specifică designerului cum şi când să facă refresh-
ul proprietăţii

Data Binding

Nu voi face o prezentare a ceea ce este ADO.Net şi cum anume se foloseşte, ci am să prezint
mecanismul în care controalele se pot cupla la sursele de date.

.Net Framework permite cuplarea (binding) nu numai la surse de date de tip ADO ci la aproape toate
structurile care conţin date. De exemplu se poate face bind la: colecţii, şiruri, proprietăţi ale altor
controale, precum şi la obiecte ADO.Net, cum ar fi coloane din tabele şi view-uri. Mai exact, prin
procesul de binding, un control care are o proprietate legată la o sursă de date, la modificarea
www.cartiaz.ro – Carti si articole online gratuite de la A la Z

proprietăţii, controlul va cere permisiunea sursei de date, sursa de date controlând valorile pe care le
pe poate avea proprietatea legată. Bind-ul poate fi de două feluri:

-Simplu: prin care o proprietate este legată la un singur câmp al sursei de date.

-Complex: când un control este ataşat la o întreagă tabelă de date: cazul în care ataşăm un "DataGrid"
la un "DataTable" dintr-un "DataSet".

Sincronizarea dintre controale şi sursa de date este făcută de un obiect numit "CurrencyManager",
fiecare sursă de date având ataşată câte o instanţă de CurrencyManager:

Fiecare formă are o proprietate numită "BindingContext" care este o colecţie ce asigură accesul la
toate obiectele "CurrencyManager":

Localizarea aplicaţiilor

Când creăm aplicaţii care se adresează unor utilizatori din ţări diferite, se pune problema scrierii
interfeţelor grafice în mai multe limbi, precum şi afişarea corespunzătoare a datelor în formatul limbii
respective, cum este cazul datei calendaristice. Pentru astfel de aplicaţii trebuie ajut în vedere:

-globalizarea: nucleul aplicaţiei care asigură funcţionalitatea de bază a aplicaţiei, trebuie construit fără
modulele care trebuie localizate;

-localizarea: care se realizează prin traducerea modulelor resursă folosite de aplicaţie şi în alte limbi.

Suportul pentru localizare în .NET Framework este asigurat de către clasa "CultureInfo", care conţine
informaţii despre limba, ţara, calendarul, convenţiile culturale. Această clasă asigura existenţa unui
singur nume pentru fiecare cultură. Dar cel mai bine să facem o aplicaţie ca exemplu.

Creăm un nou proiect de tip Windows Application în C#, pe care-l vom numi "LocalizedApp". Deoarece
versiunea mea de Windows 2000 are suport pentru limba româna (la Regional Options în Control
Panel) voi alege următoarea politică de localizare: dacă aplicaţia va rula pe un calculator cu setările
regionale setate pe română, toate informaţiile vor fi afişate în română, iar pentru orice altă limbă se va
folosi limba implicită, limba engleză.
www.cartiaz.ro – Carti si articole online gratuite de la A la Z

În fereastra de proprietăţi a formei setez proprietatea "Localizable" pe true, pentru a activa suportul
pentru localizare, iar proprietatea language o las pe "Default". Adaug un control "Label" , un edit box
lângă acea etichetă şi mai adaug un buton. Schimb proprietatea "Text" a etichetei în "First Name" iar
pe cea a butonului în "Show". Să zicem că forma noastră este completă. Acum să facem şi versiunea
în română. Din fereastra de proprietăţi a formei pentru proprietatea "Language" selectam "Romanian",
după care schimbăm textul celor două controale în "Nume" şi respectiv a butonului în "Mostră".

Dacă în fereastra "Solution Explorer" apăsăm pe butonul "Show All Files" observăm că la forma
noastră s-a mai adăugat un fişier cu extensia "resx", în care se vor stoca datele ce ţin de localizarea
pentru cea de-a doua limbă, româna. Acum trebuie să facem următoarele modificări în codul formei.

Adăugăm următoarele clauze, Pentru a putea folosi clasele necesare accesării proprietăţilor firelor de
execuţie şi "Globalization" pentru a putea folosi clasa "CultureInfo":

using System.Threading;

using System.Globalization;

Iar în constructorul formei adăugăm următoarea secvenţă de cod înainte de apelul lui
"InitializeComponents()":

Thread.CurrentThread.CurrentUICulture=
Thread.CurrentThread.CurrentCulture;

CurrentThread este o proprietate statică a clasei Thread şi conţine o referinţă către firul de execuţie
principal. Fiecare fir de execuţie (thread) are două proprietăţi, "CurrentUICulture" şi "CurrentCulture".
Prima este o referinţă către un obiect "CultureInfo" folosit pentru afişarea interfeţei utilizator, iar cea de
a doua este o referinţă către un obiect "CultureInfo", creat pe baza informaţiilor luate din "Control
Panel - Regional Options".

Compilaţi aplicaţia şi rulati-o. Veţi observa că toate informaţiile sunt în engleză. În "Regional Options"
din "Control Panel", în fişa "General", schimbaţi, "Your locale(location)" pe Romanian. Rulaţi din nou
aplicaţia. Toate informaţiile se vor afişa în română.

Daca ne uităm în directorul unde compilatorul a depus executabilul, observăm că se mai găseşte un
subdirector "ro" ce conţine fişierul "LocalizedApp.resources.dll" - forma localizată în limba română.
Versiunea pentru limba implicită - engleza, este inclusă în fişierul executabil. Pentru încărcarea unei
resurse se foloseşte un obiect numit "ResourceManager". Vom folosi şi noi un astfel de obiect în
secţiunea care urmează.

Localizarea resurselor

Să vedem cum localizăm şi alte tipuri de resurse, cum ar fi şirurile de caractere, utilizând resource
manager.

Pentru a folosi clasa "ResourceManager" şi clasa "Assembly" în aplicaţia noastră adăugăm


următoarele clauze:

using System.Resources;

using System.Reflection;

Adăugăm proiectului două fişiere de tip resursă. Din meniul "Project" selectăm "Add New Item …", în
fereastra de dialog care urmează selectăm ca tip "Assembly Resource File", pe care îl vom numi
"MyResource.resx". Acest fişier de resurse îl deschidem pentru editare şi adăugăm un nou articol cu
www.cartiaz.ro – Carti si articole online gratuite de la A la Z

numele "msg1" şi cu valoarea "The name is:". Mai adăugăm un fişier de tip "Assembly Resource File"
pe care-l vom numi "MyResource.ro.resx". Observăm că am inclus indicativul de limbă ca şi cum ar fi o
extensie. În acest fişier adăugăm un articol tot cu numele "msg1", dar cu valoarea "Numele:".
Adăugăm un handler pentru evenimentul de clic pe butonul din formă şi în acest handler scriem
următoarea secvenţă de cod:

ResourceManager rm=new ResourceManager("LocalizedApp.MyResource",

Assembly.GetExecutingAssembly());
MessageBox.Show(rm.GetString("msg1")+" "+textBox1.Text);

Compilaţi şi rulaţi, pentru limbile română şi engleză. ResourceManager ştie să încarce resursa în
funcţie de limba setată. Când creăm un fişier resursă pentru o anumită limbă, specificarea limbii se
face în numele fişierului.

Concluzii

Sper că în aceste pagini am reuşit să vă fac o scurtă descriere a ceea ce înseamnă dezvoltarea
aplicaţiilor Windows pentru .NET Framework. Oricum, cele mai multe lucruri le veţi învăţa prin practică
şi citind multă documentaţie.