Sunteți pe pagina 1din 201

CAPITOLUL FUNDAMENTELE FORMULARELOR WEB

Stocarea setărilor personalizate în fișierul web.config


ASP.NET vă permite, de asemenea, să stocați propriile setări în fișierul web.config, într-un element
numit <appSettings>. Rețineți că elementul <appSettings> este imbricat în elementul rădăcină
<configurare>. Iată structura de bază:
<?xml version="1.0" ?> <configurare>

<appSettings> <!-- Setările personalizate ale aplicației merg aici. --> </appSettings> <system.web> <!-- ASP.NET
Secțiunile de configurare merg aici. --> </system.web> </configuration>

Notă: acest exemplu adaugă un comentariu în locul în care ați găsi în mod normal setări suplimentare. Comentariile XML sunt puse între paranteze cu secvențele de caractere <!-- și -->. De asemenea, puteți utiliza comentarii XML pentru a dezactiva temporar o setare dintr-un fișier de configurare.

Setările particularizate pe care le adăugați sunt scrise ca variabile șir simple. Este posibil să doriți să
utilizați o setare specială web.config din mai multe motive:

Pentru a centraliza o setare importantă care trebuie utilizată în mai multe pagini diferite: De exemplu,
puteți crea o variabilă care stochează o interogare a bazei de date. Orice pagină care trebuie să
utilizeze această interogare poate apoi să regăsească această valoare și să o utilizeze.
Pentru a facilita comutarea rapidă între diferite moduri de funcționare: De exemplu, puteți crea o
variabilă specială de depanare. Paginile dvs. web ar putea verifica această variabilă și, dacă este
setată la o valoare specificată, să emită informații suplimentare pentru a vă ajuta să testați aplicația.
Pentru a seta unele valori inițiale: În funcție de operație, utilizatorul poate modifica aceste valori,
dar fișierul web.config ar putea furniza valorile implicite.

Puteți introduce setări personalizate utilizând un element <add> care identifică un nume unic al variabilei
(cheie) și conținutul variabilei (valoare). Următorul exemplu adaugă o variabilă care definește o cale de fișier
în care sunt stocate informații importante:
<Setări aplicație>
<add key="DataFilePath" value="e:\NetworkShare\Documents\WebApp\Shared" />
Setări </app>
Puteți adăuga câte setări de aplicație doriți, deși acest exemplu definește doar una. Puteți crea o pagină de test simplă pentru a
interoga aceste informații și pentru a afișa rezultatele, așa cum se arată în exemplul următor (care este furnizat cu exemplul de
cod ca ShowSettings.aspx și ShowSettings.aspx.cs). Regăsiți setările de aplicație particularizate de la web.config după numele
cheii, utilizând clasa WebConfigurationManager, care se găsește în spațiul de nume System.Web.Configuration. Această clasă

166
CAPITOLUL FUNDAMENTELE FORMULARELOR WEB
5 •

oferă o proprietate statică numită AppSettings cu o colecție de setări de aplicație.

folosind System.Web.UI; folosind


System.Web.UI.WebControls;
folosind System.Web.Configuration;
public clasa parțială ShowSettings : System.Web.UI.Page
{
protejat void Page_Load() { Results.InnerHtml = "Această aplicație va căuta date în directorul:<br />"; Results.InnerHtml +=
"<b>" Results.InnerHtml += WebConfigurationManager.AppSettings["DataFilePath"]; } Results.InnerHtml += "</b>";

Sfat: Observați că acest cod formatează textul inserând etichete HTML în etichetă alături de conținutul textului, inclusiv etichete aldine (<b>) pentru a sublinia anumite cuvinte și un sfârșit de linie (<br />) pentru a împărți ieșirea pe mai multe linii. Aceasta este o tehnică comună.

Mai târziu, în capitolul 17, veți învăța cum să obțineți informații despre fișiere și directoare și să citiți și să
scrieți fișiere. Pentru moment, aplicația simplă afișează doar setarea web.config personalizată, așa cum se
arată în Figura 5-12.

Figura 5-12. Afișarea setărilor particularizate ale aplicației

167
CAPITOLUL FUNDAMENTELE FORMULARELOR WEB
5 •

ASP.NET este configurat, în mod implicit, să refuze orice solicitări pentru fișiere .config. Aceasta înseamnă că
utilizatorii de la distanță nu vor putea accesa fișierul. În schimb, vor primi mesajul de eroare afișat în Figura 5-13.
Descărcați de la Wow! eBook <www.wowebook.com>

Figura 5-13. Cererile pentru web.config sunt refuzate.

Instrumentul de administrare a site-ului web (WAT)


Editarea manuală a fișierului web.config este revigorant de simplă, dar poate fi puțin obositoare. Pentru a ajuta la atenuarea
corvoadei, ASP.NET include un instrument de configurare grafică numit Website Administration Tool (WAT), care vă permite
să configurați diferite părți ale fișierului web.config utilizând o interfață de pagină web. Pentru a rula WAT pentru a configura
proiectul web curent în Visual Studio, selectați Site web
→ ASP.NET Configurare (sau faceți clic pe pictograma ASP.NET Configurare din partea de sus a
Exploratorului de soluții). Va apărea o fereastră de browser web (a se vedea figura 5-14). Internet Explorer
vă va conecta automat sub contul de utilizator Windows curent, permițându-vă să efectuați modificări.

168
CAPITOLUL FUNDAMENTELE FORMULARELOR WEB
5 •

Figura 5-14. Rularea WAT

Puteți utiliza WAT pentru a automatiza modificările web.config pe care le-ați făcut în exemplul anterior.
Pentru a încerca acest lucru, faceți clic pe fila Aplicație. Utilizând această filă, puteți crea o setare nouă
(faceți clic pe legătura Creare setări aplicație). Dacă faceți clic pe Gestionare setări aplicație, veți vedea o
listă cu toate setările aplicațiilor definite în aplicație (Figura 5-15). Apoi puteți alege să eliminați sau să
editați oricare dintre ele.

169
CAPITOLUL FUNDAMENTELE FORMULARELOR WEB
5 •

Figura 5-15. Editarea unei setări de aplicație cu WAT

Aceasta este ideea esențială din spatele WAT. Efectuați modificările utilizând o interfață grafică (o pagină
web), iar WAT generează setările de care aveți nevoie și le adaugă la fișierul web.config pentru aplicația dvs.
din culise. Desigur, WAT are o serie de setări pentru configurarea setărilor ASP.NET mai complexe și le veți
folosi pe tot parcursul acestei cărți.

Notă WAT funcționează numai în timp ce dezvoltați o aplicație web. Cu alte cuvinte, nu puteți implementa un site web pe un server web live și apoi să încercați să utilizați WAT. Cu toate acestea, dacă trebuie să reconfigurați o aplicație deja implementată, puteți utiliza instrumentul grafic IIS Manager, care oferă unele dintre aceleași caracteristici ca WAT (și multe altele suplimentare). Veți afla mai multe despre configurația IIS în capitolul 26.

Ultimul cuvânt
Acest capitol v-a prezentat prima privire asupra aplicațiilor web, paginilor web și configurației. Acum ar trebui să
înțelegeți cum să creați o pagină web ASP.NET și să utilizați controale de server HTML. Controalele HTML sunt
un compromis între controalele web ASP.NET și HTML tradițional. Acestea folosesc elementele HTML familiare,
dar oferă o interfață limitată orientată pe obiecte. În esență, controalele HTML sunt concepute pentru a fi simple,

170
CAPITOLUL FUNDAMENTELE FORMULARELOR WEB
5 •

previzibile și compatibile automat cu programele existente. Cu controale HTML, pagina HTML finală care este
trimisă clientului seamănă foarte mult cu pagina .aspx originală.
În capitolul următor, veți afla despre controalele web, care oferă o interfață obiect mai sofisticată care
abstractizează HTML-ul de bază. Dacă începeți un proiect nou sau trebuie să adăugați unele dintre cele mai
puternice controale ASP.NET, controalele web sunt cea mai bună opțiune.

171
CAPITOLUL6

•■■

Controale web

Capitolul anterior a introdus modelul de programare bazat pe evenimente și control al ASP.NET. Acest model vă
permite să creați programe pentru Web utilizând aceleași tehnici orientate pe obiecte pe care le-ați utiliza pentru a
scrie o aplicație Windows.
Cu toate acestea, controalele serverului HTML arată într-adevăr doar o privire asupra a ceea ce este posibil cu
modelul de control al serverului ASP.NET. Pentru a vedea unele dintre avantajele reale, trebuie să vă scufundați în
controalele web mai bogate și mai extensibile. În acest capitol, veți explora controalele web de bază și ierarhia lor
de clasă. De asemenea, veți aprofunda gestionarea evenimentelor ASP.NET, veți afla detaliile ciclului de viață al
paginii web și vă veți pune cunoștințele la lucru prin crearea unei pagini web care permite utilizatorului să proiecteze
o felicitare.

Trecerea la controalele web


Acum că ați văzut noul model de controale ale serverului, s-ar putea să vă întrebați de ce aveți nevoie de
controale web suplimentare. Dar, de fapt, controalele HTML sunt mult mai limitate decât trebuie să fie
controalele serverului. De exemplu, fiecare control HTML corespunde direct unui singur element HTML.
Controalele web, pe de altă parte, nu au o astfel de restricție - pot trece de la un element la altul în funcție de
modul în care le utilizați sau se pot reda folosind o combinație complexă de mai multe elemente. Acestea
sunt câteva dintre motivele pentru care ar trebui să comutați la controalele web:
Acestea oferă o interfață de utilizator bogată: un control web este programat ca obiect, dar nu corespunde
neapărat unui singur element din pagina HTML finală. De exemplu, puteți crea un singur control Calendar sau
GridView, care va fi redat ca zeci de elemente HTML în pagina finală. Când utilizați ASP.NET programe, nu
trebuie să știți nimic despre HTML. Controlul creează etichetele HTML necesare pentru dvs.

Ele oferă un model de obiect consistent: HTML este plin de ciudățenii și idiosincrazii. De exemplu, o casetă
text simplă poate apărea ca unul dintre cele trei elemente, inclusiv <textarea>, <input type="text"> și <input
type="password">. Cu controalele web, aceste trei elemente sunt consolidate ca un singur control TextBox. În
funcție de proprietățile pe care le setați, elementul HTML subiacent redat de ASP.NET poate diferi. În mod
similar, numele proprietăților nu urmează numele atributelor HTML. De exemplu, controalele care afișează
text, indiferent dacă este o legendă sau o casetă text care poate fi editată de utilizator, expun o proprietate
Text.
Acestea își adaptează automat rezultatele: ASP.NET controale de server pot detecta tipul de browser și pot ajusta
automat codul HTML pe care îl scriu pentru a profita de caracteristici precum suportul pentru JavaScript. Nu trebuie să
știți despre client, deoarece ASP.NET gestionează acel strat și utilizează automat cel mai bun set posibil de entități.
Această caracteristică este cunoscută sub numele de redare adaptivă. Acestea oferă funcții de nivel înalt: veți vedea că

173
CAPITOLUL CONTROALE WEB
6 •

controalele web vă permit să accesați evenimente, proprietăți și metode suplimentare care nu corespund direct
controalelor HTML tipice. ASP.NET implementează aceste caracteristici utilizând o combinație de trucuri.

De-a lungul acestei cărți, veți vedea exemple care utilizează setul complet de controale web. Pentru a
stăpâni dezvoltarea ASP.NET, trebuie să vă simțiți confortabil cu aceste ingrediente ale interfeței cu
utilizatorul și să le înțelegeți abilitățile. Controalele serverului HTML, pe de altă parte, sunt mai puțin
importante pentru dezvoltarea formularelor web. Le veți utiliza numai dacă migrați o pagină HTML existentă
în lumea ASP.NET sau dacă trebuie să aveți un control fin asupra codului HTML care va fi generat și trimis
clientului.

Clase de control web de bază


Dacă ați creat vreodată o aplicație Windows, probabil că sunteți familiarizați cu setul de bază de controale standard,
inclusiv etichete, butoane și casete text. ASP.NET oferă controale web pentru toate aceste standby-uri. (Și dacă ați creat
aplicații .NET Windows, veți observa că numele și proprietățile claselor au multe asemănări izbitoare, care sunt
concepute pentru a facilita transferul experienței pe care o dobândiți într-un tip de aplicație în altul.)

Tabelul 6-1 listează clasele de control de bază și elementele HTML pe care le generează. Unele controale
(cum ar fi Buton și Casetă text) pot fi redate ca elemente HTML diferite. În acest caz, ASP.NET utilizează
elementul care corespunde proprietăților pe care le-ați setat. De asemenea, unele controale nu au
echivalent HTML real. De exemplu, atunci când controalele CheckBoxList și RadioButtonList se redau
singure, acestea pot afișa un <tabel> care conține mai multe casete de selectare HTML sau butoane radio.
ASP.NET le expune ca un singur obiect pe partea serverului pentru programare convenabilă, ilustrând astfel
unul dintre punctele forte principale ale controalelor web.
Tabelul 6-1. Controale web de bază

Clasa de control Element HTML subiacent

Etichetă <span>

Nasture <input type="submit"> sau <input type="button">

Casetă text <input type="text">, <input type="password"> sau <textarea>

Caseta de selectare <input type="caseta de selectare">

Buton radio <input type="radio">

Hyperlink <a>

LinkButton <a> cu o etichetă <img> conținută

ImageButton <input type="image">

Imagine <IMG>

Casetă listă <select size="X"> unde X este numărul de rânduri care sunt vizibile simultan

174
CAPITOLUL CONTROALE WEB
6 •

Clasa de control Element HTML subiacent

Listă verticală <selectați>

Lista casetelor de selectare O listă sau <tabel> cu mai multe etichete <input type="checkbox">

RadioButtonList O listă sau <tabel> cu mai multe etichete <input type="radio">

Lista cu marcatori O listă ordonată <ol> (numerotată) sau <ul> listă neordonată (cu marcatori)

Panou <div>

Table, TableRow și <tabel>, <tr> și <td> sau <th>


Celulă tabel

Acest tabel omite unele dintre controalele mai specializate utilizate pentru date, navigare, securitate și
portaluri web. Veți vedea aceste controale pe măsură ce aflați despre caracteristicile lor pe parcursul
acestei cărți.

Etichetele de control web


ASP.NET etichete au un format special. Ele încep întotdeauna cu prefixul asp: urmat de numele clasei. Dacă nu există
nicio etichetă de închidere, eticheta trebuie să se termine cu />. (Această convenție de sintaxă este împrumutată din XML,
despre care veți afla mult mai detaliat în capitolul 18.) Fiecare atribut din etichetă corespunde unei proprietăți de control,
cu excepția atributului runat=" server", care semnalează că controlul trebuie procesat pe server.

Următorul, de exemplu, este un ASP.NET TextBox:


<asp:TextBox ID="txt" runat="server" />
Când un client solicită această pagină .aspx, se returnează următorul cod HTML. Numele este un
atribut special pe care ASP.NET îl utilizează pentru a urmări controlul.
<input type="text" ID="txt" name="txt" />
Alternativ, puteți plasa text în caseta de text, îi puteți seta dimensiunea, îl puteți face doar în citire și puteți
schimba culoarea de fundal. Toate aceste acțiuni au proprietăți definite. De exemplu, proprietatea
TextBox.TextMode vă permite să specificați SingleLine (implicit), MultiLine (pentru un tip de control
<textarea>) sau Password (pentru un control de intrare care afișează marcatori pentru a ascunde valoarea
reală). Puteți ajusta culoarea utilizând proprietățile BackColor și ForeColor. Și puteți modifica dimensiunea
casetei de text într-unul din cele două moduri - fie utilizând proprietățile Rânduri și coloane (pentru abordarea
HTML pură), fie utilizând proprietățile Înălțime și Lățime (pentru abordarea bazată pe stil). Ambele au același
rezultat. Iată un exemplu de casetă de text particularizată:
<asp:TextBox ID="txt" BackColor="Galben" text="Hello World" ReadOnly="True"
TextMode="MultiLine" Rows="5" runat="server" />

HTML-ul rezultat utilizează elementul <textarea> și setează toate atributele necesare (cum ar fi rândurile și
numai în citire) și atributul de stil (cu culoarea de fundal). De asemenea, setează atributul cols cu lățimea
implicită de 20 de coloane, chiar dacă nu ați setat explicit proprietatea TextBox.Columns:

175
CAPITOLUL CONTROALE WEB
6 •

<textarea name="txt" rows="5" cols="20" readonly="readonly" id="txt"


style="background-color:Yellow;" >Hello World</textarea>

Figura 6-1 prezintă elementul <textarea> din browser.

Figura 6-1. O casetă text particularizată

În mod clar, este ușor să creați o etichetă de control web. Nu necesită nicio înțelegere a HTML. Cu toate
acestea, va trebui să înțelegeți clasa de control și proprietățile care vă sunt disponibile.

SENSIBILITATE LA MAJUSCULE ȘI MINUSCULE ÎN ASP.NET FORME

Porțiunea de aspect .aspx a unei pagini web tolerează scrierea cu majuscule diferite pentru numele
etichetelor, numele proprietăților și valorile enumerării. De exemplu, următoarele două etichete sunt
echivalente și ambele vor fi interpretate corect de motorul ASP.NET, chiar dacă cazul lor diferă:
<asp:Button ID="button1" runat="server" enabled="false"
text="button" font-size="XX-small" /> <asp:button ID="button2"
runat="server" enabled="false" tExT="button" font-size="xx-small"
/>
Acest design a fost adoptat pentru a face .aspx pagini să se comporte mai mult ca paginile web
HTML obișnuite, care ignoră complet majusculele. Cu toate acestea, nu puteți utiliza aceeași
slăbiciune în etichetele care aplică setările în fișierul web.config sau fișierul machine.config. Aici,
cazul trebuie să se potrivească exact.

Clase de control web


Clasele de control web sunt definite în spațiul de nume System.Web.UI.WebControls. Acestea urmează o
ierarhie a obiectelor puțin mai încurcată decât controalele serverului HTML. Figura 6-2 prezintă majoritatea,
dar nu toate, controalele web pe care le oferă ASP.NET.

176
CAPITOLUL CONTROALE WEB
6 •

Figura 6-2. Ierarhia controlului web

Această diagramă moștenire include unele controale pe care nu le veți studia în acest capitol, inclusiv
controalele de date, cum ar fi GridView, DetailsView și FormView și controalele de validare. Veți explora
aceste controale în capitolele următoare.

Clasa de bază WebControl


Majoritatea controalelor web încep prin moștenirea de la clasa de bază WebControl. Această clasă
definește funcționalitatea esențială pentru activități precum legarea datelor și include câteva proprietăți de
bază pe care le puteți utiliza cu aproape orice control web, așa cum este descris în tabelul 6-2.
Tabelul 6-2. Proprietăți WebControl

Proprietate Descriere

Cheie de acces Specifică scurtătura de la tastatură ca o singură literă. De exemplu, dacă setați această
opțiune la Y, combinația de tastatură Alt+Y își va schimba automat focalizarea pe acest
control web. Această caracteristică este acceptată numai pe Internet Explorer 4.0 și
versiuni ulterioare.
BackColor, ForeColor Setează culorile utilizate pentru fundalul, prim-planul și bordura controlului. În
și BorderColor majoritatea controalelor, culoarea planului frontal setează culoarea textului.

177
CAPITOLUL CONTROALE WEB
6 •

Proprietate Descriere

BorderWidth Specifică dimensiunea bordurii de control.

BorderStyle Una dintre valorile din enumerarea BorderStyle, inclusiv Dashed, Dotted,
Double, Groove, Ridge, Inset, Outset, Solid și None.

Controale Oferă o colecție a tuturor controalelor conținute în interiorul controlului curent.


Fiecare obiect este furnizat ca obiect generic System.Web.UI.Control, deci va
trebui să aruncați referința la proprietățile specifice controlului accesului.

Activat Când este setat la false, controlul va fi vizibil, dar nu va putea primi informații
de la utilizator sau focalizare.

EnableViewState Setați acest lucru la false pentru a dezactiva gestionarea automată a stării pentru
acest control. În acest caz, controlul va fi resetat la proprietățile și formatarea
specificate în eticheta de control (în pagina .aspx) de fiecare dată când pagina
este postată înapoi. Dacă aceasta este setată la true (implicit), controlul utilizează
Descărcați de la Wow! eBook <www.wowebook.com>

câmpul de introducere ascuns pentru a stoca informații despre proprietățile sale,


asigurându-se că toate modificările pe care le faceți în cod sunt memorate.
Font Specifică fontul utilizat pentru a reda orice text din control ca
obiect System.Web.UI.WebControls.FontInfo.

Înălțime și lățime Specifică lățimea și înălțimea comenzii. Pentru unele controale, aceste
proprietăți vor fi ignorate atunci când sunt utilizate cu browsere mai vechi.

ID Specifică numele pe care îl utilizați pentru a interacționa cu controlul din cod (și,
de asemenea, servește drept bază pentru ID-ul utilizat pentru a denumi
elementul de nivel superior din codul HTML redat).

Pagină Furnizează o referință la pagina web care conține acest control ca


obiect System.Web.UI.Page.

Părinte Furnizează o referință la controlul care conține acest control. Dacă controlul este
plasat direct pe pagină (nu în interiorul altui control), acesta va returna o
referință la obiectul paginii.

TabIndex Un număr care vă permite să controlați ordinea filelor. Controlul cu un TabIndex de 0 este
focalizat la prima încărcare a paginii. Apăsarea tastei Tab mută utilizatorul la controlul cu
următorul cel mai mic TabIndex, cu condiția ca acesta să fie activat. Această proprietate
este acceptată numai în Internet Explorer 4.0 și versiuni ulterioare.

Sfat ecran Afișează un mesaj text atunci când utilizatorul plasează mouse-ul deasupra
controlului. Multe browsere mai vechi nu acceptă această proprietate.

Vizibil Când este setat la false, controlul va fi ascuns și nu va fi redat la pagina HTML
finală care este trimisă clientului.

178
CAPITOLUL CONTROALE WEB
6 •

Următoarele câteva secțiuni descriu unele dintre conceptele comune pe care le veți utiliza cu aproape
orice control web, inclusiv modul de setare a proprietăților care utilizează unități și enumerări și modul
de utilizare a culorilor și fonturilor.

Unităţi
Toate proprietățile care utilizează măsurători, inclusiv BorderWidth, Height și Width, necesită structura Unității, care
combină o valoare numerică cu un tip de măsurare (pixeli, procentaj etc.). Aceasta înseamnă că atunci când setați aceste
proprietăți într-o etichetă de control, trebuie să vă asigurați că adăugați px (pixel) sau % (pentru procent) la număr pentru
a indica tipul de unitate.
Iată un exemplu cu un control al panoului care are o înălțime de 300 de pixeli și o lățime egală cu 50% din
fereastra curentă a browserului:
<asp:Înălțimea panoului = "300px" width = "50%" ID = "pnl" runat = "server" / >
Dacă atribuiți o proprietate bazată pe unitate prin cod, trebuie să utilizați una dintre metodele statice
ale tipului de unitate. Utilizați Pixel() pentru a furniza o valoare în pixeli și utilizați Percent() pentru a
furniza o valoare procentuală:
Convertiți numărul 300 într-un obiect unitate //
reprezentând pixeli și atribuiți-l. PNL. înălțime =
unitate.pixel(300);
Convertiți numărul 50 într-un obiect unitate //
reprezentând procentul și atribuiți-l. PNL. lățime
= unitate.procent(50);
De asemenea, puteți crea manual un obiect Unit și îl puteți inițializa utilizând unul dintre constructorii furnizați
și enumerarea UnitType. Acest lucru necesită câțiva pași suplimentari, dar vă permite să atribuiți cu ușurință
aceeași unitate mai multor controale:
Creați un obiect Unitate.
Unit myUnit = unitate nouă (300, UnitType.Pixel);

Atribuiți obiectul Unitate mai multor controale sau proprietăți. PNL.


Înălțime = unitatea mea; PNL. lățime = unitatea mea;

Enumerări
Enumerările sunt utilizate intens în biblioteca de clase .NET pentru a grupa un set de constante asociate. De
exemplu, când setați proprietatea BorderStyle a unui control, puteți alege una dintre mai multe valori
predefinite din enumerarea BorderStyle. În cod, setați o enumerare utilizând sintaxa punct:
Ctrl. BorderStyle = BorderStyle.Dashed;
În fișierul .aspx, setați o enumerare specificând una dintre valorile permise ca șir. Nu includeți numele
tipului de enumerare, care este presupus automat.

<asp:Label BorderStyle="Dashed " text="Test frontieră" ID="lbl"


runat="server" />

179
CAPITOLUL CONTROALE WEB
6 •

Figura 6-3 prezintă eticheta cu marginea modificată.

Figura 6-3. Modificarea stilului chenarului

Culori
Proprietatea Culoare se referă la un obiect Culoare din spațiul de nume System.Drawing. Puteți crea
obiecte color în mai multe moduri:

Utilizarea unei valori de culoare ARGB (alfa, roșu, verde, albastru): Specificați fiecare valoare ca
număr întreg de la 0 la 255. Componenta alfa reprezintă transparența unei culori și, de obicei, veți
utiliza 255 pentru a face culoarea complet opacă.
Utilizarea unui nume de culoare .NET predefinit: Alegeți proprietatea denumită corespunzător doar în
citire din structura de culori. Aceste proprietăți includ cele 140 de nume de culori HTML.
Utilizarea unui nume de culoare HTML: Specificați această valoare ca șir utilizând clasa ColorTranslator.

Pentru a utiliza oricare dintre aceste tehnici, probabil că veți dori să începeți prin importul spațiului de
nume System.Drawing, după cum urmează:
folosind System.Drawing;
Următorul cod prezintă mai multe moduri de a specifica o culoare în cod:

Creați o culoare dintr-o valoare ARGB int alfa = 255, roșu = 0,


verde = 255, albastru = 0; Ctrl. ForeColor = Color.FromArgb(alfa,
roșu, verde, albastru);
Creați o culoare utilizând un nume .NET
ctrl. ForeColor = Culoare.Purpuriu;

Creați o culoare dintr-un cod HTML ctrl. ForeColor =


ColorTranslator.FromHtml("Albastru");

180
CAPITOLUL CONTROALE WEB
6 •

Când definiți o culoare în fișierul .aspx, puteți utiliza oricare dintre numele de culori cunoscute:

<asp:TextBox Text="Test" ID="txt" runat="server" />


ForeColor="Rosu"
Numele culorilor HTML pe care le puteți utiliza sunt listate în ajutorul Visual Studio. Alternativ, puteți utiliza
un număr de culoare hexazecimal (în formatul #<roșu><verde><albastru>) așa cum se arată aici:

<asp:TextBox Text="Test"
ForeColor="#ff50ff"
ID="txt" runat="server" />

Fonturi
Proprietatea Font face referire de fapt la un obiect FontInfo complet, care este definit în spațiul de nume
System.Web.UI.WebControls. Fiecare obiect FontInfo are mai multe proprietăți care îi definesc numele,
dimensiunea și stilul (consultați tabelul 6-3).
Tabelul 6-3. Proprietăți FontInfo

Proprietate Descriere

Nume Un șir care indică numele fontului (cum ar fi Verdana).

Nume O matrice de șiruri cu nume de fonturi, în ordinea preferințelor. Cel


Browserul va utiliza primul font de potrivire instalat pe
computerul utilizatorului.

Mărime Dimensiunea fontului ca obiect FontUnit. Aceasta poate reprezenta un absolut


sau dimensiunea relativă.

aldine, cursive, tăiate, Proprietăți booleene care aplică atributul stil dat.
Subliniere și supraliniere

În cod, puteți atribui un font setând diferitele proprietăți ale fontului utilizând sintaxa familiară a punctelor:

ctrl.Font.Name = "Verdana"; Ctrl. Font.Bold = adevărat;

De asemenea, puteți seta dimensiunea utilizând tipul FontUnit:

Specifică o dimensiune relativă. Ctrl.


Font.Size = FontUnit.Small;
Specifică o dimensiune absolută de 14 pixeli. Ctrl.
Font.Size = FontUnit.Point (14);

În fișierul .aspx, trebuie să utilizați o sintaxă specială "obiect walker" pentru a specifica proprietățile obiectului,
cum ar fi Font. Sintaxa umblătorului de obiecte utilizează o cratimă (-) pentru a separa proprietățile. De
exemplu, puteți seta un control cu un anumit font (Tahoma) și dimensiunea fontului (40 de puncte) astfel:
<asp:TextBox font-name="Tahoma" font-size="40" text=" test de dimensiune"
ID="txt" runat="server" />

181
CAPITOLUL CONTROALE WEB
6 •

Sau puteți seta o dimensiune relativă ca aceasta:

<asp:TextBox Font-name = "Tahoma" text = "Test de dimensiune"


font-size = "mare"
ID="txt" runat="server" />

Figura 6-4 prezintă caseta de text modificată din acest exemplu.

Figura 6-4. Modificarea fontului unui control

O setare a fontului este într-adevăr doar o recomandare. În cazul în care computerul client nu are fontul solicitat, acesta
revine la un font standard. Pentru a rezolva această problemă, este obișnuit să specificați o listă de fonturi, în ordinea
preferințelor. Pentru aceasta, utilizați proprietatea Font.Names în loc de Font.Name, așa cum se arată aici:

<asp:TextBox
nume-fonturi="Verdana, Tahoma, Arial"
text="Test de dimensiune" ID="txt" runat="server" />
Aici, browserul va folosi fontul Verdana (dacă îl are). Dacă nu, va cădea înapoi pe Tahoma sau, dacă nu este
prezent, pe Arial.
Când specificați fonturi, este o idee bună să încheiați cu unul dintre următoarele fonturi, care sunt acceptate
în toate browserele:
• Ori
• Arial și Helvetica
• Curierat
Următoarele fonturi se găsesc pe aproape toate computerele Windows și Mac, dar nu neapărat pe alte
sisteme de operare, cum ar fi Unix:
• Verdana
• Georgia
• Tahoma
• Benzi desenate
• Negru Arial
• Impactul

182
CAPITOLUL CONTROALE WEB
6 •

Focar
Spre deosebire de controalele serverului HTML, fiecare control web oferă o metodă Focus(). Metoda Focus() afectează
numai controalele de intrare (controale care pot accepta apăsări de taste de la utilizator). Când pagina este redată în
browserul client, utilizatorul pornește în controlul focalizat.
De exemplu, dacă aveți un formular care permite utilizatorului să editeze informațiile despre clienți, puteți apela metoda
Focalizare() în prima casetă text din acel formular. În acest fel, cursorul apare în această casetă de text imediat când
pagina se încarcă pentru prima dată în browser. În cazul în care caseta text se află parțial în jos în formular, pagina chiar
defilează automat în jos. Utilizatorul poate trece apoi de la control la control folosind tasta Tab onorată de timp.

Dacă sunteți un dezvoltator HTML experimentat, știți că nu există nicio modalitate încorporată de a focaliza un control de
intrare. În schimb, trebuie să vă bazați pe JavaScript. Acesta este secretul implementării ASP.NET. Când codul este
procesat și pagina este redată, ASP.NET adaugă un bloc suplimentar de cod JavaScript la sfârșitul paginii. Acest cod
JavaScript setează pur și simplu focalizarea la ultimul control care a folosit metoda Focus(). Dacă nu ați apelat deloc
Focus(), acest cod nu este adăugat la pagină.
În loc să apelați metoda Focus() prin programare, puteți seta un control care ar trebui să fie întotdeauna
focalizat setând proprietatea DefaultFocus a etichetei <formular>:
<form DefaultFocus="TextBox2" runat="server">
Puteți înlocui focalizarea implicită apelând metoda Focalizare() din codul dvs.
O altă modalitate de a gestiona focalizarea este utilizarea tastelor de acces. De exemplu, dacă setați proprietatea
AccessKey a unei casete text la A, apăsarea focalizării Alt+A va comuta la TextBox. Etichetele pot intra, de asemenea, în
joc, chiar dacă nu pot accepta concentrarea. Trucul este să setați proprietatea Label.AssociatedControlID pentru a
specifica un control de intrare legat. În acest fel, eticheta transferă focalizarea asupra controlului asociat.
De exemplu, următoarea etichetă focalizează TextBox2 atunci când este apăsată combinația de
tastatură Alt+2:

<asp:Label AccessKey="2" AssociatedControlID="TextBox2" runat="server"


text="TextBox2:" /> <asp:TextBox runat="server" ID="TextBox2" />

Tastele de focalizare și acces sunt, de asemenea, acceptate în browserele non-Microsoft, inclusiv Firefox.

Butonul implicit
Împreună cu focalizarea controlului, ASP.NET vă permite, de asemenea, să desemnați un buton implicit pe o pagină
web. Butonul implicit este butonul pe care se face clic atunci când utilizatorul apasă tasta Enter. De exemplu, dacă
pagina web include un formular, poate doriți să transformați butonul de trimitere într-un buton implicit. În acest fel, dacă
utilizatorul apasă Enter în orice moment, pagina este postată înapoi și evenimentul Button.Click este declanșat pentru
acel buton.
Pentru a desemna un buton implicit, trebuie să setați proprietatea HtmlForm.DefaultButton cu ID-ul
controlului respectiv, așa cum se arată aici:
<form DefaultButton="cmdSubmit" runat="server">
Butonul implicit trebuie să fie un control care implementează interfața IButtonControl. Interfața este implementată
de controalele web Button, LinkButton și ImageButton, dar nu de niciunul dintre controalele serverului HTML.

În unele cazuri, este logic să aveți mai multe butoane implicite. De exemplu, puteți crea o pagină web cu două
grupuri de controale de intrare. Ambele grupuri pot avea nevoie de un buton implicit diferit. Puteți gestiona
acest lucru plasând grupurile în panouri separate. Controlul Panou expune, de asemenea, proprietatea
DefaultButton, care funcționează atunci când orice control de intrare pe care îl conține este focalizat.

183
CAPITOLUL CONTROALE WEB
6 •

PREFIXE DE CONTROL

Când lucrați cu controale web, este adesea util să utilizați un prefix de trei litere mici pentru a identifica tipul
de control. Exemplul precedent (și cele din restul acestei cărți) urmează această convenție pentru a face
codul interfeței cu utilizatorul cât mai clar posibil. Unele prefixe de control recomandate sunt următoarele:
• Buton: cmd
• Casetă de selectare: chk
• Imagine: img
• Etichetă: lbl
• Controlul listei: lst
• Panou: pnl
• RadioButton: optați
• Casetă text: txt
Dacă sunteți un programator veteran, veți observa, de asemenea, că această carte nu
folosește prefixe pentru a identifica tipurile de date. Acest lucru este în conformitate cu noua
filozofie a .NET, care recunoaște că tipurile de date se pot schimba adesea liber și fără
consecințe și că variabilele indică adesea obiecte cu funcții complete în loc de variabile
simple de date.

Controale listă
Controalele listei includ ListBox, DropDownList, CheckBoxList, RadioButtonList și BulletedList. Toate funcționează
în esență în același mod, dar sunt redate diferit în browser. ListBox, de exemplu, este o listă dreptunghiulară care
afișează mai multe intrări, în timp ce Lista verticală afișează numai elementul selectat. CheckBoxList și
RadioButtonList sunt similare cu ListBox, dar fiecare element este redat ca o casetă de selectare sau, respectiv,
un buton de opțiune. În cele din urmă, BulletedList este cel ciudat - este singurul control de listă care nu poate fi
selectat. În schimb, se redă ca o secvență de elemente numerotate sau marcate.
Toate controalele de listă selectabile furnizează o proprietate SelectedIndex care indică rândul selectat ca index
bazat pe zero (la fel ca controlul HtmlSelect pe care l-ați utilizat în capitolul anterior). De exemplu, dacă este
selectat primul element din listă, SelectedIndex va fi 0. Controalele de listă selectabile furnizează, de asemenea, o
proprietate SelectedItem suplimentară, care permite codului să regăsească obiectul ListItem care reprezintă
elementul selectat. Obiectul ListItem oferă trei proprietăți importante: Text (conținutul afișat), Valoare (valoarea
ascunsă din marcajul HTML) și Selectat (adevărat sau fals, în funcție de selectarea elementului).
În capitolul anterior, ați utilizat cod ca acesta pentru a prelua obiectul ListItem selectat dintr-un control
HtmlSelect numit Monedă, după cum urmează:

Element ListItem; item =


Currency.Items(Currency.SelectedIndex);

184
CAPITOLUL CONTROALE WEB
6 •

Dacă ați utilizat controlul web ListBox, puteți simplifica acest cod cu o sintaxă mai clară:

Element ListItem; item =


Currency.SelectedItem;

Controale listă cu selectare multiplă


Unele controale de listă pot permite selecții multiple. Acest lucru nu este permis pentru DropDownList sau
RadioButtonList, dar este acceptat pentru un ListBox, cu condiția să fi setat proprietatea SelectionMode la valoarea
enumerată ListSelectionMode.Multiple. Utilizatorul poate selecta apoi mai multe elemente ținând apăsată tasta Ctrl în
timp ce faceți clic pe elementele din listă. Cu CheckBoxList, sunt întotdeauna posibile mai multe selecții.

Proprietățile SelectedIndex și SelectedItem nu sunt de mare ajutor cu o listă care acceptă mai multe selecții,
deoarece vor returna pur și simplu primul element selectat. În schimb, puteți găsi toate elementele selectate
iterând prin colecția Elemente a controlului listă și verificând proprietatea ListItem.Selected a fiecărui
element. (Dacă este adevărat, acel element este unul dintre elementele selectate.) Figura 6-5 prezintă un
exemplu simplu de pagină web. Acesta oferă o listă de limbi de calculator și indică ce selecții a făcut
utilizatorul atunci când se face clic pe butonul OK.

Figura 6-5. Un test simplu CheckListBox

Fișierul .aspx pentru această pagină definește controalele CheckListBox, Button și Label, așa cum se arată aici:

185
CAPITOLUL 6 CONTROALE WEB

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="CheckListTest.aspx.cs"


mosteniri="CheckListTest" %> <html xmlns="http://www.w3.org/1999/xhtml"> <head
runat="server"> <title>CheckBoxTest</title> </head> <body> <form runat="server"> <div>
Alegeți limbajele de programare preferate:<br /><br /> <asp:CheckBoxList ID="chklst"
runat="server" /><br /><br /> <asp: Buton ID="cmdOK" Text="OK" OnClick
="cmdOK_Click" runat="server" /> <br /><br /> <asp:Label ID="lblResult" runat="server" />
</div> </form> </body> </html>

Codul adaugă elemente în CheckListBox la pornire și iterează prin colecție atunci când se face clic pe
buton:

clasa publică parțială CheckListTest : System.Web.UI.Page { protected void Page_Load(object sender,


EventArgs e) { if (!this. IsPostBack) { chklst. Items.Add ("C"); CHKLST. Items.Add ("C++"); CHKLST.
Items.Add ("C#"); CHKLST. Items.Add ("Visual Basic 6.0"); CHKLST. Items.Add ("VB.NET"); } } Chklst.
Items.Add ("Pascal");

protejat void cmdOK_Click(object sender, EventArgs e) {


lblResult.Text = "You chose:<b>";

foreach (ListItem lstItem in chklst. Items) { if


(lstItem.Selected == true) { // Adăugați text la
etichetă.

lblResult.Text += "<br />" + lstItem.Text; } } lblResult.Text


+= "</b>";

}
}

186
CAPITOLUL CONTROALE WEB
6 •

Controlul BulletedList
Controlul BulletedList este un echivalent pe partea de server a elementelor <ul> (listă neordonată) și <ol>
(listă ordonată). Ca și în cazul tuturor controalelor de listă, setați colecția de elemente care ar trebui să fie
afișate prin proprietatea Elemente. În plus, utilizați proprietățile din tabelul 6-4 pentru a configura modul de
afișare a elementelor.
Tabelul 6-4. S-au adăugat proprietăți BulletedList

Proprietate Descriere

Stil glonț Determină tipul de listă. Alegeți dintre Numerotate (1, 2, 3, . . .), LowerAlpha
(a, b, c, . . .) și UpperAlpha (A, B, C, . .), LowerRoman (i, ii, iii, ; . . .) și
Romana superioară (I, II, III, . .) și simbolurile marcatorilor Disc, Cerc, Pătrat sau
CustomImage (caz în care trebuie să setați proprietatea BulletImageUrl).

BulletImageUrl Dacă BulletStyle este setat la CustomImage, aceasta indică spre imaginea plasată
în partea stângă a fiecărui element sub forma unui marcator.

FirstBulletNumber Într-o listă ordonată (folosind Numerotate, LowerAlpha, UpperAlpha,


Stilurile Romane de Jos și Romane de Sus), aceasta stabilește prima valoare. De exemplu, dacă
setați FirstBulletNumber la 3, lista ar putea citi 3, 4, 5 (pentru numerotate) sau C,
D, E (pentru UpperAlpha).

Modul DisplayMode Stabilește dacă textul fiecărui element este redat ca text (utilizați Text, pictograma
implicit) sau un hyperlink (utilizați LinkButton sau HyperLink). Diferența dintre
LinkButton și HyperLink este modul în care tratează clicurile. Când utilizați LinkButton,
Lista cu marcatori declanșează un eveniment Click la care puteți reacționa pe server
Efectuați navigarea. Când utilizați HyperLink, BulletedList nu se declanșează
evenimentul Faceți clic - în schimb, tratează textul fiecărui element din listă ca relativ sau
URL absolut și le redă ca hyperlink-uri HTML obișnuite. Când utilizatorul
dă clic pe un element, browserul încearcă să navigheze la adresa URL respectivă.

Dacă setați DisplayMode la LinkButton, puteți reacționa la evenimentul Button.Click pentru a determina
pe ce element s-a făcut clic. Iată un exemplu:

protejat void BulletedList1_Click(expeditorul obiectului, BulletedListEventArgs e)


{
Găsiți elementul pe care ați făcut clic în listă.
(Nu puteți utiliza proprietatea SelectedIndex aici, deoarece listele statice // nu acceptă
selecția.)
string itemText = BulletedList1.Items[e.Index]. Text;
Label1.Text = "Tu alegi articolul" + itemText;
}

Figura 6-6 prezintă toate valorile BulletStyle acceptate de BulletList. Când faceți clic pe unul dintre
elemente, lista se modifică pentru a utiliza acel BulletStyle. Puteți încerca această pagină exemplu cu
exemplul de proiect WebControls pentru acest capitol.

187
CAPITOLUL CONTROALE WEB
6 •
Descărcați de la Wow! eBook <www.wowebook.com>

Figura 6-6. Diverse stiluri BulletedList

Controale tabel
În esență, controlul Tabel este construit dintr-o ierarhie de obiecte. Fiecare obiect Tabel conține unul sau
mai multe obiecte TableRow. La rândul său, fiecare obiect TableRow conține unul sau mai multe obiecte
TableCell. Fiecare obiect TableCell conține alte controale ASP.NET sau conținut HTML care afișează
informații. Dacă sunteți familiarizat cu etichetele tabelului HTML, această relație (prezentată în Figura 6-7)
va părea destul de logică.

188
CAPITOLUL CONTROALE WEB
6 •

Figura 6-7. Izolarea controlului tabelului

Pentru a crea un tabel dinamic, urmați aceeași filozofie ca și pentru orice alt control web. Mai întâi, creați și configurați
obiectele ASP.NET necesare. Apoi, ASP.NET convertește aceste obiecte la reprezentarea HTML finală înainte ca
pagina să fie trimisă clientului.
Luați în considerare exemplul prezentat în figura 6-8. Permite utilizatorului să specifice un număr de
rânduri și coloane, precum și dacă celulele ar trebui să aibă borduri.

189
CAPITOLUL CONTROALE WEB
6 •

Figura 6-8. Opțiunile de testare a tabelului

Când utilizatorul face clic pe butonul Creare, tabelul este completat dinamic cu date eșantion în conformitate cu
opțiunile selectate, așa cum se arată în figura 6-9.

Figura 6-9. A tabel generat dinamic

190
CAPITOLUL 6 CONTROALE WEB

Codul .aspx creează controalele TextBox, CheckBox, Button și Table:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="TableTest.aspx.cs"


mosteneste="TableTest" %> <html xmlns="http://www.w3.org/1999/xhtml"> <head
runat="server"> <title>Table Test</title> </head> <body> <form runat="server">
<div> Rânduri: <asp:TextBox ID="txtRows" runat="server" /> &nbsp; Cols:
<asp:TextBox ID="txtCols" runat="server" /> <br /><br /> <asp:CheckBox
ID="chkBorder" runat="server" text="put border around cells" /> <br /><br />
<asp:Button ID="cmdCreate" OnClick ="cmdCreate_Click" runat="server"
text="create" /> <br /><br /> <asp:Table ID="tbl" runat="server" /> </div> </form>
</body> </html>

Veți observa că controlul Tabel nu conține rânduri sau celule reale. Pentru a crea un tabel valid, va trebui
să imbricați mai multe straturi de etichete. Următorul exemplu creează un tabel cu o singură celulă care
conține textul Un rând de test:
<asp:Table ID="tbl" runat="server">
<asp:TableRow ID="row" runat="server">
A ID="cell" runat="server">A Sample Value</asp:TableCell> </asp:TableRow>
</asp:Table>

Pagina web de testare a tabelului nu are elemente imbricate. Aceasta înseamnă că tabelul va fi creat ca obiect de
control pe partea serverului, dar dacă codul nu adaugă rânduri și celule, tabelul nu va fi redat în pagina HTML finală.

Clasa TableTest utilizează două rutine de tratare a evenimentelor. Când pagina este încărcată pentru prima
dată, aceasta adaugă o bordură în jurul tabelului. Când se face clic pe buton, acesta creează dinamic
obiectele TableRow și TableCell necesare într-o buclă.
clasa publică parțială TableTest : System.Web.UI.Page
{
vid protejat Page_Load(expeditor obiect, EventArgs e)
{
Configurați aspectul tabelului.
Acest lucru poate fi efectuat și în fișierul .aspx // sau în
cmdCreate_Click event handler. TBL. BorderStyle =
BorderStyle.Inset; } tbl. BorderWidth = Unit.Pixel(1);
CAPITOLUL CONTROALE WEB
6 •

protejat void cmdCreate_Click(expeditor obiect, EventArgs e) {

} // Eliminați toate rândurile și celulele curente.


Acest lucru nu este necesar dacă EnableViewState este setat la
false. TBL. Controls.Clear();

int rows = Int32.Parse(txtRows.Text); int


cols = Int32.Parse(txtCols.Text);

pentru (int row = 0; rând < rânduri; row++)


{ // Creați un nou obiect TableRow.
TableRow rowNew = nou TableRow();

Puneți TableRow în tabel. TBL.


Controls.Add(rowNew);

pentru (int col = 0; col < cols; col++) { // Creați


un nou obiect TableCell. TableCell New = nou
TableCell();

cellNew.Text = "Exemplu de celulă (" + rând. ToString() + ",";


celulăNou.Text + = col. ToString() + ")";

if (chkBorder.Checked) { cellNew.BorderStyle =
BorderStyle.Inset; cellNew.BorderWidth =
Unit.Pixel(1); }

Puneți TableCell în TableRow.


rowNew.Controls.Add(cellNew); } }

Acest cod utilizează colecția Controale pentru a adăuga controale fiu. Fiecare control al containerului oferă
această proprietate. De asemenea, puteți utiliza colecția TableCell.Controls pentru a adăuga controale web la
fiecare TableCell. De exemplu, puteți plasa un control Imagine și un control Etichetă în fiecare celulă. În acest
caz, nu puteți seta proprietatea TableCell.Text. Următorul fragment de cod utilizează această tehnică, iar
Figura 6-10 afișează rezultatele:
Creați un nou obiect TableCell.
cellNew = nou TableCell();

Creați un nou obiect Etichetă.


Etichetă lblNew = etichetă nouă(); lblNew.Text = "Exemplu de celulă (" +
rând. ToString() + ","; lblNew.Text + = col. ToString() + ")<br />";

192
CAPITOLUL CONTROALE WEB
6 •

System.Web.UI.WebControls.Image imgNew = nou System.Web.UI.WebControls.Image();


imgNew.ImageUrl = "cellpic.png";

Puneți eticheta și imaginea în celulă.


cellNew.Controls.Add(lblNew);
cellNew.Controls.Add(imgNew);
Puneți TableCell în TableRow.
rowNew.Controls.Add(cellNew);

Flexibilitatea reală a paginii de test a tabelului este că fiecare tabel, rând de tabel și celulă de tabel este un
obiect cu caracteristici complete. Dacă doriți, puteți da fiecărei celule un stil diferit de bordură, culoare de
bordură și culoare de text, setând proprietățile corespunzătoare.

Figura 6-10. Un tabel cu controale conținute

Evenimente de control web și AutoPostBack


Capitolul anterior a explicat că una dintre principalele limitări ale controalelor serverului HTML este setul lor
limitat de evenimente utile - au exact două. Controalele HTML care declanșează o postback, cum ar fi
butoanele, ridică un eveniment ServerClick. Controalele de intrare oferă un eveniment ServerChange care nu
se declanșează până când pagina nu este postată înapoi.

193
CAPITOLUL CONTROALE WEB
6 •

ASP.NET controalele serverului sunt într-adevăr o iluzie ingenioasă. Vă veți aminti că codul dintr-o pagină
de ASP.NET este procesat pe server. Apoi este trimis utilizatorului ca HTML obișnuit. Figura 6-11
ilustrează ordinea evenimentelor în procesarea paginilor.

Figura 6-11. Secvența de procesare a paginii

Acest lucru este același în ASP.NET ca și în programarea tradițională ASP. Întrebarea este, cum puteți scrie cod
de server care să reacționeze imediat la un eveniment care apare pe client?
Unele evenimente, cum ar fi evenimentul Faceți clic pe un buton, apar imediat. Asta pentru că atunci când faceți clic,
butonul postează înapoi pagina. Aceasta este o convenție de bază a formularelor HTML. Cu toate acestea, alte acțiuni
provoacă evenimente, dar nu declanșează o postare. Un exemplu este atunci când utilizatorul modifică textul dintr-o
casetă text (care declanșează evenimentul TextChanged) sau alege un element nou dintr-o listă (evenimentul
SelectedIndexChanged). S-ar putea să doriți să răspundeți la aceste evenimente, dar fără o postback, codul dvs. nu are
cum să ruleze.
ASP.NET gestionează acest lucru oferindu-vă două opțiuni:
• Puteți aștepta până la următoarea postback pentru a reacționa la eveniment. De exemplu,
imaginați-vă că doriți să reacționați la evenimentul SelectedIndexChanged dintr-o listă. Dacă utilizatorul
selectează un element dintr-o listă, nu se întâmplă nimic imediat. Cu toate acestea, dacă utilizatorul
face clic apoi pe un buton pentru a posta înapoi pagina, se declanșează două evenimente: Button.Click
urmat de ListBox.SelectedIndexChanged. Și dacă aveți mai multe controale, este foarte posibil ca un

194
CAPITOLUL CONTROALE WEB
6 •

singur postback să ducă la mai multe evenimente de schimbare, care se declanșează unul după
altul, într-o ordine nedeterminată.

• Puteți utiliza funcția de postback automat pentru a forța un control să posteze înapoi
pagina imediat ce detectează o anumită acțiune a utilizatorului. În acest scenariu,
când utilizatorul face clic pe un element nou din listă, pagina este postat înapoi, codul
se execută și se returnează o nouă versiune a paginii.
Opțiunea pe care o alegeți depinde de rezultatul dorit. Dacă trebuie să reacționați imediat (de exemplu, doriți să
actualizați un alt control atunci când are loc o anumită acțiune), trebuie să utilizați postback-uri automate. Pe de altă parte,
postback-urile automate pot face uneori pagina mai puțin receptivă, deoarece fiecare postback și reîmprospătare a paginii
adaugă o întârziere scurtă, dar vizibilă, și o reîmprospătare a paginii. (Veți învăța cum să creați pagini care se
actualizează singure fără o reîmprospătare vizibilă a paginii atunci când luați în considerare ASP.NET AJAX în capitolul
25.)
Toate controalele web de intrare acceptă postback-uri automate. Tabelul 6-5 oferă o listă de bază a
controalelor web și a evenimentelor acestora.

Tabelul 6-5. Evenimente de control web

Eveniment Controale web care îl furnizează Postează întotdeauna înapoi

Clic Buton, ImageButton Adevărat

TextChanged TextBox (se declanșează numai după ce Fals


utilizatorul schimbă focalizarea la un alt
control)
VerificatModificat Casetă de selectare, RadioButton Fals

SelectedIndexChanged DropDownList, ListBox, CheckBoxList, Fals


RadioButtonList

Dacă doriți să capturați imediat un eveniment de modificare (cum ar fi TextChanged, CheckedChanged sau
SelectedIndexChanged), trebuie să setați proprietatea AutoPostBack a controlului la true. În acest fel, pagina va fi trimisă
automat atunci când utilizatorul interacționează cu controlul (de exemplu, alege o selecție din listă, face clic pe un buton
radio sau pe o casetă de selectare sau modifică textul dintr-o casetă de text și apoi trece la un control nou).

Când pagina este postată înapoi, ASP.NET va examina pagina, va încărca toate informațiile curente și apoi
va permite codului să efectueze o procesare suplimentară înainte de a returna pagina înapoi utilizatorului (a
se vedea figura 6-12). În funcție de rezultatul dorit, este posibil să aveți o pagină care are unele controale
care postează înapoi automat și altele care nu.

195
CAPITOLUL CONTROALE WEB
6 •

Figura 6-12. Secvența de procesare postback

Acest sistem postback nu este ideal pentru toate evenimentele. De exemplu, unele evenimente cu care este
posibil să fiți familiarizat din programele Windows, cum ar fi evenimentele de mișcare a mouse-ului sau
evenimentele de presă cheie, nu sunt practice într-o aplicație ASP.NET. Retrimiterea paginii de fiecare dată
când este apăsată o tastă sau mouse-ul este mutat ar face ca aplicația să fie insuportabil de lentă și să nu
răspundă.

196
CAPITOLUL CONTROALE WEB
6 •

Cum funcționează evenimentele postback


Capitolul 1 a explicat că nu toate tipurile de programare web utilizează cod pe partea serverului, cum ar fi ASP.NET. Un
exemplu comun de programare web pe partea clientului este JavaScript, care utilizează codul script executat de
browser. ASP.NET utilizează abilitățile JavaScript din partea clientului pentru a reduce decalajul dintre codul din partea
clientului și codul din partea serverului. (Un alt limbaj de scripting este VBScript, dar JavaScript este singurul care
funcționează pe toate browserele moderne, inclusiv Internet Explorer, Firefox, Opera, Safari și Netscape.) Iată cum
funcționează: În cazul în care creați o pagină Web care include unul sau mai multe controale Web care sunt configurate
să utilizeze AutoPostBack, ASP.NET adaugă o funcție JavaScript specială la pagina HTML redată. Această funcție se
numește __doPostBack(). Când este apelat, declanșează o postback, trimițând date înapoi la serverul web.

ASP.NET adaugă, de asemenea, două câmpuri de intrare ascunse suplimentare care sunt utilizate pentru a
transmite informații înapoi la server. Aceste informații constau în ID-ul controlului care a generat evenimentul și
orice informații suplimentare care ar putea fi relevante. Aceste câmpuri sunt inițial goale, după cum se arată aici:

<input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" /> <input


type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" />

Funcția __doPostBack() are responsabilitatea de a seta aceste valori cu informațiile corespunzătoare despre
eveniment și apoi de a trimite formularul. Funcția __doPostBack() este prezentată aici:

<script language="text/javascript"> funcția __doPostBack(eventTarget,


eventArgument) { if (!theForm.onsubmit || (theForm.onsubmit() != fals))
{ theForm.__EVENTTARGET.value = eventTarget;
theForm.__EVENTARGUMENT.value = eventArgument;
theForm.submit(); ...
}

}
</scenariu>
Rețineți, ASP.NET generează automat funcția __doPostBack(), cu condiția ca cel puțin un control de pe pagină să
utilizeze postback-uri automate.
În cele din urmă, orice control care are proprietatea AutoPostBack setată la true este conectat la funcția
__doPostBack() utilizând atributele onclick sau onchange. Aceste atribute indică ce acțiune trebuie să
întreprindă browserul ca răspuns la evenimentele JavaScript onclick și onchange din partea clientului.
Următorul exemplu afișează eticheta pentru un control listă numit lstBackColor, care postează înapoi
automat. Ori de câte ori utilizatorul modifică selecția din listă, evenimentul onchange din partea clientului se
declanșează. Browserul apelează apoi funcția __doPostBack(), care trimite pagina înapoi la server.
<select ID="lstBackColor" onchange="__doPostBack('lstBackColor','')" language="javascript">

Cu alte cuvinte, ASP.NET transformă automat un eveniment JavaScript pe partea client într-un eveniment
ASP.NET pe partea de server, utilizând funcția __doPostBack() ca intermediar. Figura 6-13 prezintă acest
proces.

197
CAPITOLUL CONTROALE WEB
6 •

Figura 6-13. Un postback automat

Ciclul de viață al paginii


Descărcați de la Wow! eBook <www.wowebook.com>

Pentru a înțelege cum funcționează evenimentele de control web, trebuie să aveți o înțelegere solidă a
ciclului de viață al paginii. Luați în considerare ce se întâmplă atunci când un utilizator modifică un control
care are proprietatea AutoPostBack setată la true:
1. Pe partea clientului, funcția JavaScript __doPostBack este invocată, iar pagina
este retrimisă către server.
2. ASP.NET recreează obiectul Page utilizând fișierul .aspx.
3. ASP.NET preia informațiile de stare din câmpul de stare a vizualizării
ascunse și actualizează controalele în consecință.
4. Evenimentul Page.Load este declanșat.
5. Evenimentul de schimbare corespunzător este declanșat pentru control. (Dacă au fost
modificate mai multe controale, ordinea evenimentelor de modificare este nedeterminată.)

6. Evenimentul Page.PreRender se declanșează și pagina este redată


(transformată dintr-un set de obiecte într-o pagină HTML).
7. În cele din urmă, evenimentul Page.Unload este declanșat.
8. Noua pagină este trimisă clientului.
Pentru a urmări aceste evenimente în acțiune, vă ajută să creați o aplicație simplă de urmărire a
evenimentelor. Tot ce face această aplicație este să scrie o nouă intrare într-un control de listă de fiecare
dată când are loc unul dintre evenimentele pe care le monitorizează. Acest lucru vă permite să vedeți
ordinea în care sunt declanșate evenimentele. Figura 6-14 arată cum arată fereastra după ce a fost încărcată
o dată, postată înapoi (când conținutul casetei text a fost modificată) și postată din nou (când starea casetei
de selectare a fost modificată).

198
CAPITOLUL 6 CONTROALE WEB

Figura 6-14. Instrumentul de urmărire a evenimentelor

Listarea 6-1 arată codul de marcare pentru trackerul de evenimente, iar Listarea 6-2 arată clasa din
spatele codului care îl face să funcționeze.

Lista 6-1. EventTracker.aspx

<%@ Page Language="C#" AutoEventWireup="true"


CodeFile="EventTracker.aspx.cs" mosteneste="EventTracker" %>
<html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server">
<title>Event Tracker</title> </head> <body> <form runat="server">
<div> <h1>Controale monitorizate pentru evenimente de
schimbare:</h1> <asp: TextBox ID="txt" runat="server"
AutoPostBack="true" OnTextChanged="CtrlChanged" /> <br /><br />
<asp:CheckBox ID="chk" runat="server" AutoPostBack="true"
OnCheckedChanged="CtrlChanged"/> <br /><br /> <asp:RadioButton
ID="opt1" runat="server" GroupName="Sample" AutoPostBack="true"
OnCheckedChanged="CtrlChanged"/> <asp:RadioButton ID="opt2"
CAPITOLUL CONTROALE WEB
6 •

runat="server" GroupName="Sample" AutoPostBack="true"


OnCheckedChanged="CtrlChanged"/>

<h1>Lista evenimentelor:</h1> <asp:ListBox ID="lstEvents"


runat="server" width="355px" height="150px" /><br /> <br /><br /><br />
</div> </form> </body> </html>

Lista 6-2. EventTracker.aspx.cs

public parțial clasa EventTracker : System.Web.UI.Page


{
vid protejat Page_Load(expeditor obiect, EventArgs e) } { Log("<< Page_Load >>");

protejat void Page_PreRender(expeditor obiect, EventArgs e) { // Când apare evenimentul Page.PreRender, este prea târziu
// pentru a schimba lista.

} Jurnal("Page_PreRender");

vid protejat CtrlChanged(Expeditor obiect, EventArgs e) { // Găsiți ID-ul de control al expeditorului.

Acest lucru necesită conversia tipului de obiect într-o clasă Control. șir ctrlName
= ((Control)sender).ID; } log(ctrlName + " schimbat");

Private void Log(intrare șir)


{
lstEvents.Items.Add (intrare);

Selectați ultimul element pentru a derula lista, astfel încât cele mai recente
intrări // să fie vizibile.
lstEvents.SelectedIndex = lstEvents.Items.Count - 1;
}
}

200
CAPITOLUL CONTROALE WEB
6 •

Disecarea codului . . .
Următoarele puncte sunt demne de remarcat despre acest cod:
• Codul scrie în ListBox folosind o metodă privată Log(). Metoda Log() adaugă
textul și derulează automat în partea de jos a listei de fiecare dată când se adaugă
o nouă intrare, asigurându-se astfel că cele mai recente intrări rămân vizibile.
• Toate evenimentele de modificare sunt gestionate prin aceeași metodă,
CtrlChanged(). Dacă vă uitați cu atenție la fișierul .aspx, veți observa că fiecare control
de intrare conectează evenimentul monitorizat la metoda CtrlChanged(). Codul de
tratare a evenimentelor din metoda CtrlChanged() utilizează parametrul sursă pentru a
afla ce control a trimis evenimentul și încorporează aceste informații în șirul jurnal.
• Pagina include rutine de tratare a evenimentelor pentru evenimentele Page.Load și
Page.PreRender. La fel ca în cazul tuturor evenimentelor de pagină, acești rutine de
tratare a evenimentelor sunt conectați prin numele metodei. Asta înseamnă că pentru
a adăuga rutina de tratare a evenimentelor pentru evenimentul Page.PreRender,
trebuie doar să adăugați o metodă numită Page_PreRender(), ca cea prezentată aici.

O pagină web simplă


Acum că ați avut un tur vârtej al modelului de control web de bază, este timpul să îl puneți la lucru cu al doilea utilitar cu o
singură pagină. În acest caz, este un exemplu simplu pentru un generator dinamic de cărți electronice. Puteți extinde acest
eșantion (de exemplu, permițând utilizatorilor să stocheze carduri electronice în baza de date), dar chiar și pe cont propriu,
acest exemplu demonstrează manipularea de control de bază cu ASP.NET.
Pagina web este împărțită în două regiuni. În stânga este o etichetă obișnuită <div> care conține un set de
controale web pentru specificarea opțiunilor cardului. În partea dreaptă este un control al panoului (numit
pnlCard), care conține alte două controale (lblGreeting și imgDefault) care sunt utilizate pentru a afișa text
configurabil de utilizator și o imagine. Acest text și această imagine reprezintă felicitarea. Când pagina se
încarcă pentru prima dată, cardul nu a fost încă generat, iar porțiunea din dreapta este goală (așa cum se
arată în Figura 6-15).

201
CAPITOLUL CONTROALE WEB
6 •

Figura 6-15. Generatorul de carduri electronice

Sfat: elementul <div> este util atunci când doriți să grupați textul și controalele și să le aplicați tuturor un set de proprietăți de formatare (cum ar fi o culoare sau un font). Elementul <div> este, de asemenea, un instrument esențial pentru poziționarea blocurilor de conținut într-o pagină. Din aceste motive, elementul <div> este folosit în multe dintre exemplele din această carte. Veți afla mai multe despre utilizarea <div> pentru aspect și formatare în capitolul

12.

Ori de câte ori utilizatorul face clic pe butonul Actualizare, pagina este postată înapoi și "cardul" este
actualizat (a se vedea figura 6-16).

202
CAPITOLUL• CONTROALE
6 WEB

Figura 6-16. O felicitare configurată de utilizator

Codul de aspect .aspx este simplu. Desigur, lungimea mare a acestuia face dificilă lucrul eficient. Iată
marcajul, cu detaliile de formatare reduse la elementele esențiale:

<%@ Page Language="C#" AutoEventWireup="true"


CodeFile="GreetingCardMaker.aspx.cs" mostenit="GreetingCardMaker" %> <html
xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>Greeting Card
Maker</title> </head> <body> <form runat="server"> <div>

<!-- Iată controalele: --> Alegeți o culoare de fundal: <br />


<asp:DropDownList ID="lstBackColor" runat="server" width="194px"
height="22px"/><br /><br /> Alegeți un font:<br /> <asp:DropDownList
CAPITOLUL CONTROALE WEB
6 •

ID="lstFontName" runat="server" width="194px" height="22px" /><br /><br />


Specificați o dimensiune numerică a fontului: <br /> <asp:TextBox ID="txtFontSize"
runat="server" /><br /><br /> Alegeți un stil de bordură:<br /> <asp:RadioButtonList
ID="lstBorder" runat="server" width="177px" height="59px" /><br /><br />
<asp:CheckBox ID="chkPicture" runat="server" text="Adăugați imaginea
implicită"></asp: CheckBox><br /><br /> Introduceți textul de salut de mai jos:<br />
<asp:TextBox ID="txtGreeting" runat="server" width="240px" height="85px"
TextMode="MultiLine" /><br /><br /> <asp:Button ID="cmdUpdate"
OnClick="cmdUpdate_Click" runat="server" width="71px" height="24px" text="update"
/> </div>

<!-- Iată cardul: --> <asp:Panel ID="pnlCard" runat="server"


width="339px" height="481px" HorizontalAlign="Center"
style="POSITION: absolut; SUS: 16px; STÂNGA: 313px;" > <br
/>&nbsp; <asp:Label ID="lblGreeting" runat="server" width="256px"
height="150px" /><br /><br /><br /> <asp:Image ID="imgDefault"
runat="server" width="212px" height="160px" /> </asp:Panel>
</form> </body> </html>

Pentru a obține aspectul cu două coloane în acest exemplu, aveți două opțiuni. Puteți utiliza tabele HTML
(care sunt o tehnică oarecum de modă veche) sau puteți utiliza poziționarea absolută cu stiluri CSS (ca în
acest exemplu). Esența poziționării absolute este ușor de înțeles. Uitați-vă doar la atributul de stil din controlul
Panou, care specifică o coordonată fixă de sus și o coordonată stângă pe pagina web. Când panoul este
randat în HTML, acest punct devine colțul din stânga sus.

Notă Poziționarea absolută este o caracteristică a CSS, standardul Cascading Style Sheets. Ca atare, funcționează în orice element XHTML, nu doar ASP.NET controale. Poziționarea absolută este descrisă în detaliu în capitolul 12.

Codul urmează modelul familiar, cu accent pe două evenimente: evenimentul Page.Load, unde sunt setate
valorile inițiale, și evenimentul Button.Click, unde este generat cardul.

clasa publică parțială GreetingCardMaker : System.Web.UI.Page


{
vid protejat Page_Load(expeditor obiect, EventArgs e)
{
dacă (!acest. IsPostBack) { //
Setați opțiunile de culoare.

204
CAPITOLUL CONTROALE WEB
6 •

} lstBackColor.Items.Add ("Alb");
lstBackColor.Items.Add ("Roșu");
lstBackColor.Items.Add ("Verde");
lstBackColor.Items.Add ("Albastru");
lstBackColor.Items.Add ("Galben");

Setați opțiunile fontului.


lstFontName.Items.Add ("Times New Roman");
lstFontName.Items.Add ("Arial");
lstFontName.Items.Add ("Verdana");
lstFontName.Items.Add ("Tahoma");
Setați opțiunile stilului de bordură adăugând o serie de obiecte
// ListItem.
ItemListItem = nou ListItem();

Textul elementului indică numele opțiunii. articol. Text =


BorderStyle.None.ToString();
Valoarea elementului înregistrează numărul întreg corespunzător
// din enumerare. Pentru a obține această valoare, trebuie să
aruncați valoarea de enumerare într-un întreg, // și apoi să
convertiți numărul într-un șir, astfel încât să poată fi plasat în
pagina HTML.
articol. Value = ((int)BorderStyle.None). ToString();

Adăugați elementul.
lstBorder.Items.Add(articol);

Acum repetați procesul pentru alte două stiluri de frontieră.


item = nou ListItem(); articol. Text =
BorderStyle.Double.ToString(); articol. Value =
((int)BorderStyle.Double). ToString();
lstBorder.Items.Add(articol);
item = nou ListItem(); articol. Text =
BorderStyle.Solid.ToString(); articol. Valoare =
((int)BorderStyle.Solid). ToString();
lstBorder.Items.Add(articol);
Selectați prima opțiune de bordură.
lstBorder.SelectedIndex = 0;

Setați imaginea.
imgDefault.ImageUrl = "defaultpic.png";

vid protejat cmdUpdate_Click(expeditor obiect, EventArgs e)


{
Actualizați culoarea.
pnlCard.BackColor = Color.FromName(lstBackColor.SelectedItem.Text);

Actualizați fontul.

205
CAPITOLUL CONTROALE WEB
6 •

lblGreeting.Font.Name = lstFontName.SelectedItem.Text;

dacă (Int32.Parse(txtFontSize.Text) > 0) {


lblGreeting.Font.Size = FontUnit.Point (Int32.Parse
(txtFontSize.Text)); }

Actualizați stilul de bordură. Acest lucru necesită doi pași de conversie. În primul
rând, valoarea elementului de listă este convertită dintr-un șir // într-un întreg. Apoi,
numărul întreg este convertit într-o valoare în // enumerarea BorderStyle.

int borderValue = Int32.Parse(lstBorder.SelectedItem.Value);


pnlCard.BorderStyle = (BorderStyle)borderValue;

Actualizați imaginea. dacă


(chkPicture.Checked) {
imgDefault.Visible = true; } else {
imgDefault.Visible = false; }

Setați textul.
lblGreeting.Text = txtGreeting.Text;
}
}

După cum puteți vedea, acest exemplu limitează utilizatorul la câteva opțiuni presetate de font și culoare.
Codul pentru opțiunea BorderStyle este deosebit de interesant. Controlul lstBorder are o listă care afișează
numele text al uneia dintre valorile enumerate BorderStyle. Vă veți aminti din capitolele introductive că fiecare
valoare enumerată este de fapt un număr întreg cu un nume atribuit. De asemenea, lstBorder stochează în
secret numărul corespunzător, astfel încât codul să poată prelua numărul și să seteze enumerarea cu
ușurință atunci când utilizatorul face o selecție și operatorul de evenimente cmdUpdate_Click se
declanșează.
Îmbunătățirea generatorului de felicitări
ASP.NET pagini au acces la biblioteca completă de clase .NET. Cu puțină explorare, veți găsi cursuri care ar putea ajuta
producătorul de felicitări, cum ar fi instrumente care vă permit să recuperați toate numele de culori cunoscute și toate
fonturile instalate pe serverul web.
De exemplu, puteți completa controlul lstFontName cu o listă de fonturi utilizând clasa InstalledFontCollection.
Pentru a-l accesa, trebuie să importați spațiul de nume System.Drawing.Text. De asemenea, trebuie să
importați spațiul de nume System.Drawing, deoarece definește clasa FontFamily care reprezintă fonturile
individuale instalate pe serverul web:
folosind System.Drawing;
folosind
System.Drawing.Text;

206
CAPITOLUL CONTROALE WEB
6 •

Iată codul care primește lista de fonturi și o folosește pentru a completa lista:

Obțineți lista fonturilor disponibile și adăugați-le la lista de fonturi. fonturi


InstalledFontCollection = nou InstalledFontCollection(); foreach (familia FontFamily în
fonturi. Familii)
{
lstFontName.Items.Add (familie. nume);
}

Figura 6-17 prezintă lista de fonturi rezultată.

Figura 6-17. Lista de fonturi

Pentru a obține o listă cu numele culorilor, trebuie să recurgeți la un truc mai avansat. Deși ați putea codifica greu o
listă de culori comune, .NET oferă de fapt o listă lungă de nume de culori în enumerarea
System.Drawing.KnownColor. Cu toate acestea, extragerea efectivă a numelor din această enumerare necesită
ceva muncă.
Trucul este de a utiliza o caracteristică de bază a tuturor enumerărilor: metoda statică Enum.GetNames(),
care inspectează o enumerare și oferă o serie de șiruri, cu un șir pentru fiecare valoare din enumerare.
Pagina web poate utiliza apoi legarea datelor pentru a completa automat controlul listei cu informațiile din
ColorArray. (Veți explora legarea datelor mult mai detaliat în capitolul 15.)

Notă: Nu vă faceți griji dacă acest exemplu introduce câteva caracteristici care arată complet extraterestru! Aceste caracteristici sunt mai avansate (și nu sunt legate în mod specific de ASP.NET). Cu toate acestea, vă arată o parte din aroma pe care biblioteca completă de clasă .NET o poate oferi pentru o aplicație matură.

207
CAPITOLUL CONTROALE WEB
6 •

Iată codul care copiază toate numele culorilor în caseta listă:

Obțineți lista de culori.


string[] colorArray = Enum.GetNames(typeof(KnownColor));
lstBackColor.DataSource = colorArray; lstBackColor.DataBind();

O problemă minoră cu această abordare este că include culori de mediu de sistem (de exemplu,
ActiveBorder) în listă. Este posibil să nu fie evident pentru utilizator ce culori reprezintă aceste valori.
Totuși, această abordare funcționează bine pentru această aplicație simplă. Puteți utiliza o tehnică
similară pentru a completa opțiunile BorderStyle:
Setați opțiunile pentru stilul de bordură.
string[] borderStyleArray = Enum.GetNames(typeof(BorderStyle));
lstBorder.DataSource = borderStyleArray; lstBorder.DataBind();

Acest cod ridică o nouă provocare: cum convertiți valoarea pe care utilizatorul o selectează în constanta
corespunzătoare pentru enumerare? Când utilizatorul alege un stil de bordură din listă, proprietatea SelectedItem va
avea un șir de text precum "Groove". Dar pentru a aplica acest stil de bordură controlului, aveți nevoie de o modalitate
Descărcați de la Wow! eBook <www.wowebook.com>

de a determina constanta enumerată care se potrivește cu acest text.


Puteți rezolva această problemă în câteva moduri. (Anterior, ați văzut un exemplu în care numărul întreg de enumerare a
fost stocat ca valoare în controlul listă.) În acest caz, abordarea cea mai directă implică utilizarea unei funcții avansate
numită TypeConverter. Un TypeConverter este o clasă specială care este capabilă să convertească de la un tip
specializat (în acest caz, enumerarea BorderStyle) la un tip mai simplu (cum ar fi un șir) și invers.

Pentru a accesa această clasă, trebuie să importați spațiul de nume System.ComponentModel:


folosind System.ComponentModel;
Apoi puteți adăuga următorul cod la rutina de tratare a evenimentelor cmdUpdate_Click:

Găsiți TypeConverter corespunzător pentru enumerarea BorderStyle. Convertor


TypeConverter = TypeDescriptor.GetConverter(typeof(BorderStyle));

Actualizați stilul de bordură utilizând valoarea din convertor.


pnlCard.BorderStyle = (BorderStyle)convertor. ConvertFromString(
lstBorder.SelectedItem.Text);
Acest cod primește TypeConverter corespunzător (în acest caz, unul care este proiectat în mod expres să
funcționeze cu enumerarea BorderStyle). Apoi convertește numele textului (cum ar fi Solid) la valoarea
corespunzătoare (BorderStyle.Solid).

Generarea automată a cardurilor


Ultimul pas este să utilizați evenimentele automate de postback ale ASP.NET pentru a face actualizarea dinamică a
cardului de fiecare dată când se schimbă o opțiune. Butonul Actualizare ar putea fi acum utilizat pentru a trimite felicitarea
finală, perfecționată, care ar putea fi apoi trimisă prin e-mail unui destinatar sau stocată într-o bază de date.
Pentru a configura controalele astfel încât să declanșeze automat o postback de pagină, pur și simplu setați
proprietatea AutoPostBack a fiecărui control de intrare la true. Un exemplu este prezentat aici:

208
CAPITOLUL CONTROALE WEB
6 •

Alegeți o culoare de fundal: <br /> <asp:DropDownList ID="lstBackColor" runat="server"

AutoPostBack="true"
Width="194px" Height="22px"/>

Apoi, modificați etichetele de control astfel încât evenimentul modificat al fiecărui control de intrare să fie
conectat la o rutină de tratare a evenimentelor numită ControlChanged. Iată un exemplu cu evenimentul
SelectedIndexChanged sau lista verticală:
Alegeți o culoare de fundal: <br /> <asp:DropDownList ID="lstBackColor"
AutoPostBack="true" runat="server" width="194px" height="22px"/>
OnSelectedIndexChanged="ControlChanged"
Veți observa că numele evenimentului de modificare depinde de control. De exemplu, TextBox furnizează un
eveniment TextChanged, ListBox furnizează un eveniment SelectedIndexChanged și așa mai departe. În cele din
urmă, trebuie să creați un manipulator de evenimente care să poată gestiona evenimentele de modificare. Pentru a
salva câțiva pași, puteți utiliza aceeași rutină de tratare a evenimentelor pentru toate controalele de intrare. Tot ce
trebuie să facă rutina de tratare a evenimentelor este să apeleze rutina de actualizare care regenerează felicitarea.

vid protejat ControlChanged(expeditor obiect, System.EventArgs e) { //


Reîmprospătați felicitarea (deoarece a fost modificat un control).
UpdateCard(); }

protejat void cmdUpdate_Click(expeditor obiect, EventArgs e) { //


Reîmprospătați felicitarea (deoarece s-a făcut clic pe buton). UpdateCard(); }

privat void UpdateCard() { // (Codul care desenează felicitarea


merge aici.) }

Cu aceste modificări, este ușor să perfecționați programul mai extins de generare a cardurilor
prezentat în Figura 6-18. Codul complet pentru această aplicație este furnizat împreună cu mostrele
online.

209
CAPITOLUL CONTROALE WEB
6 •

Figura 6-18. Un generator de carduri mai extins

Sfat
Postback-ul automat nu este întotdeauna cel mai bun. Uneori, un postback automat poate deranja un utilizator, mai ales atunci când utilizatorul lucrează printr-o conexiune lentă sau când serverul trebuie să efectueze o opțiune consumatoare de timp. Din acest motiv, uneori este mai bine să utilizați un buton explicit de trimitere și să nu activați AutoPostBack pentru majoritatea controalelor de intrare. Alternativ, vă puteți îmbunătăți pagina web cu cele ASP.NET caracteristici AJAX descrise în capitolul 25, care vă permit să creați interfețe de

utilizator care se simt mai receptive și se pot actualiza fără o reîmprospătare a paginii complete.

210
CAPITOLUL CONTROALE WEB
6 •

Ultimul cuvânt
Acest capitol v-a prezentat controalele web și interfața lor obiect. Pe măsură ce continuați prin această
carte, veți afla despre mai multe controale web. Următoarele momente importante urmează să apară:
• În capitolul 10, veți afla despre controalele avansate, cum ar fi AdRotator, Calendar și
comenzile de validare. Veți afla, de asemenea, despre controalele specializate ale
containerelor, cum ar fi MultiView și Wizard.
• În capitolul 13, veți afla despre comenzile de navigare, cum ar fi TreeView și
Menu.
• În capitolul 16, veți afla despre GridView, DetailsView și FormView - controale web
de nivel înalt care vă permit să manipulați un tabel complex de date din orice sursă de
date.
Pentru o referință bună care afișează fiecare control Web și listează proprietățile sale importante,
consultați Ajutorul Visual Studio.

211
CAPITOLUL7

• ■■

Tratarea erorilor,
înregistrarea în jurnal și
urmărirea
Niciun software nu poate rula fără erori, iar aplicațiile ASP.NET nu fac excepție. Mai devreme sau mai târziu, codul dvs. va fi
întrerupt de o greșeală de programare, date nevalide, circumstanțe neașteptate sau chiar defecțiuni hardware. Programatorii
începători petrec nopți nedormite îngrijorându-se de erori. Dezvoltatorii profesioniști recunosc că bug-urile sunt o parte
inerentă a aplicațiilor software și a codului defensiv, testând ipoteze, înregistrând probleme și scriind cod de manipulare a
erorilor pentru a face față neașteptatului. În acest capitol, veți afla practicile de gestionare și depanare a erorilor pe care le
puteți utiliza pentru a vă apăra aplicațiile ASP.NET împotriva erorilor comune, pentru a urmări problemele utilizatorilor și
pentru a rezolva probleme misterioase. Veți învăța cum să utilizați gestionarea structurată a excepțiilor, cum să păstrați o
evidență a erorilor nerecuperabile cu jurnalele și cum să configurați pagini web cu mesaje de eroare personalizate pentru
erorile HTTP comune. De asemenea, veți afla cum să utilizați urmărirea paginilor pentru a vedea informații de diagnosticare
despre ASP.NET pagini.

Erori comune
Erorile pot apărea într-o varietate de situații. Unele dintre cele mai frecvente cauze ale erorilor includ încercările de
împărțire la zero (cauzate de obicei de intrări nevalide sau de informații lipsă) și încercările de conectare la o resursă
limitată, cum ar fi un fișier sau o bază de date (care pot eșua dacă fișierul nu există, conexiunea bazei de date expiră
sau codul are acreditări de securitate insuficiente).
Un tip infam de eroare este excepția de referință nulă, care apare de obicei atunci când un program încearcă să
utilizeze un obiect neinițializat. Ca programator .NET, veți învăța rapid să recunoașteți și să rezolvați această greșeală
comună, dar enervantă. Următorul exemplu de cod arată problema în acțiune, cu două obiecte SqlConnection care
reprezintă conexiuni la baza de date:

Definiți o variabilă numită conOne și creați obiectul. privat


SqlConnection conOne = nou SqlConnection();

Definiți o variabilă numită conTwo, dar nu creați obiectul. privat


SqlConnection conTwo;

public void cmdDoSomething_Click(object sender, EventArgs e) { // Acest lucru funcționează, deoarece obiectul a fost creat // cu
noul cuvânt cheie.

...

213
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

conOne.ConnectionString = "..."; Următoarea afirmație nu va


reuși și va genera o excepție de referință nulă //.
Nu puteți modifica o proprietate (sau utiliza o metodă) a unui obiect
// care nu există!
conTwo.ConnectionString = "...";
...
}
Când apare o eroare în codul dvs., .NET verifică dacă apar rutine de tratare a erorilor în domeniul curent. Dacă
eroarea apare în interiorul unei metode, .NET caută rutine de tratare a erorilor locale și apoi verifică dacă există
rutine de tratare a erorilor active în codul de apelare. Dacă nu se găsesc rutine de tratare a erorilor, procesarea
paginii este întreruptă și Visual Studio intră în modul de depanare (așa cum ați aflat în capitolul 2). Dacă apăsați
butonul Redare pentru a continua, veți vedea o pagină de eroare detaliată care explică problema (așa cum se
arată în Figura 7-1). Aceste pagini de eroare sunt o comoditate de dezvoltare - odată ce implementați aplicația,
acestea sunt înlocuite cu pagini de eroare mai generale, pe care le puteți configura în software-ul serverului web
IIS (așa cum este descris în capitolul 26).

Figura 7-1. Un exemplu de pagină de eroare

214
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

Chiar dacă o eroare este rezultatul unei intrări nevalide sau al eșecului unei componente terțe, o pagină de
eroare poate distruge aspectul profesional al oricărei aplicații. Utilizatorii aplicației ajung să aibă sentimentul
că aplicația este instabilă, nesigură sau de slabă calitate - și sunt cel puțin parțial corecte.
Dacă o aplicație ASP.NET este proiectată și construită cu atenție, o pagină de eroare nu va apărea aproape
niciodată. Erorile pot apărea în continuare din cauza unor circumstanțe neprevăzute, dar acestea vor fi
prinse în cod și identificate. Dacă eroarea este una critică pe care aplicația nu o poate rezolva singură, va
raporta o pagină mai utilă (și mai ușor de utilizat) de informații care ar putea include un link către un e-mail
de asistență sau un număr de telefon unde clientul poate primi asistență suplimentară. Veți analiza aceste
tehnici în acest capitol.

Tratarea excepțiilor
Majoritatea limbajelor .NET acceptă tratarea excepțiilor structurate. În esență, atunci când apare o eroare în aplicația dvs.,
.NET Framework creează un obiect de excepție care reprezintă problema. Puteți prinde acest obiect folosind un
manipulator de excepții. Dacă nu reușiți să utilizați un rutină de tratare a excepțiilor, codul va fi anulat și utilizatorul va
vedea o pagină de eroare.
Gestionarea structurată a excepțiilor oferă mai multe caracteristici cheie:

Excepțiile sunt bazate pe obiecte: Fiecare excepție furnizează o cantitate semnificativă de informații de
diagnosticare înfășurate într-un obiect îngrijit, în locul unui mesaj simplu și a unui cod de eroare. Aceste obiecte de
excepție acceptă, de asemenea, o proprietate InnerException care vă permite să înfășurați o eroare generică peste
eroarea mai specifică care a provocat-o. Puteți chiar să creați și să aruncați propriile obiecte de excepție.
Excepțiile sunt prinse în funcție de tipul lor: Acest lucru vă permite să simplificați codul de gestionare a
erorilor fără a fi nevoie să treceți prin coduri de eroare obscure.
Gestionarii de excepții utilizează o structură modernă de blocuri: Acest lucru facilitează activarea și dezactivarea
diferitelor gestionare a erorilor pentru diferite secțiuni de cod și gestionarea individuală a erorilor.

Gestionarii de excepții sunt multistrat: Puteți suprapune cu ușurință gestionarii de excepții peste
alte rutine de tratare a excepțiilor, dintre care unele pot verifica doar un set specializat de erori.
Excepțiile sunt o parte generică a .NET Framework: Aceasta înseamnă că sunt complet compatibile
între limbi. Astfel, o componentă .NET scrisă în C# poate arunca o excepție pe care o puteți prinde într-o
pagină web scrisă în VB.

Notă
Gestionarii de excepții sunt o tehnică cheie de programare. Acestea vă permit să reacționați la problemele care apar în timpul rulării din cauza unor factori în afara controlului dvs. Cu toate acestea, evident că nu ar trebui să utilizați gestionari de excepții pentru a ascunde erorile care ar putea apărea în codul dvs.! În schimb, trebuie să urmăriți aceste greșeli ale programatorului la momentul dezvoltării și să le corectați. Caracteristicile de depanare ale Visual

Studio (care au fost descrise în capitolul 4) vă pot ajuta în această sarcină.

Clasa de excepție
Fiecare clasă de excepție derivă din clasa de bază System.Exception. .NET Framework este plin de clase
de excepții predefinite, cum ar fi NullReferenceException, IOException, SqlException și așa mai departe.

215
CAPITOLUL 7 GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR

Clasa Excepție include funcționalitatea esențială pentru identificarea oricărui tip de eroare. Tabelul 7-1
enumeră cei mai importanți membri ai săi.

Tabelul 7-1. Proprietăți de excepție

Membru Descriere

Link de ajutor Un link către un document de ajutor, care poate fi o uniformă relativă sau complet calificată
localizator de resurse (URL) sau nume uniform de resurse (URN), cum ar fi
file:///C:/ACME/MyApp/help.html#Err42. .NET Framework nu utilizează
această proprietate, dar o puteți seta în excepțiile personalizate dacă doriți să o utilizați
în codul paginii dvs.

InnerException O excepție imbricată. De exemplu, o metodă poate prinde un fișier simplu


eroare de intrare/ieșire (IO) și creați o eroare de nivel superior "operațiune eșuată". Cel
detalii despre eroarea inițială ar putea fi păstrate în InnerException
proprietatea erorii de nivel superior.

Mesaj O descriere text cu o cantitate semnificativă de informații care descrie


problemă.

Sursă Numele cererii sau al obiectului pentru care a fost ridicată excepția.

StackTrace Un șir care conține o listă cu toate apelurile metodei curente pe stivă, în
ordinea de la cele mai recente la cele mai puțin recente. Acest lucru este util pentru a determina unde
a apărut problema.

Site-ul țintă Un obiect de reflexie (o instanță a clasei System.Reflection.MethodBase)


Aceasta oferă câteva informații despre metoda în care a apărut eroarea.
Aceste informații includ detalii generice ale metodei, cum ar fi numele metodei
și tipurile de date pentru parametrii și valorile returnate. Nu conține niciun
informații despre valorile parametrilor reali care au fost utilizate atunci când
a apărut problema.

GetBaseException() O metodă utilă pentru excepțiile imbricate care pot avea mai mult de un strat. El
Recuperează excepția originală (cea mai adânc imbricată) deplasându-se la baza butonului
Lanțul InnerException.

Când prindeți o excepție într-o pagină ASP.NET, aceasta nu va fi o instanță a clasei generice System.Exception. În schimb,
va fi un obiect care reprezintă un anumit tip de eroare. Acest obiect se va baza pe una dintre multele clase care moștenesc
de la System.Exception. Acestea includ diverse clase, cum ar fi DivideByZeroException, ArithmeticException, IOException,
SecurityException și multe altele. Unele dintre aceste clase oferă detalii suplimentare despre eroarea din proprietățile
suplimentare. Visual Studio oferă un instrument util pentru a naviga prin excepțiile din biblioteca de clasă .NET. Pur și simplu
selectați Excepții de depanare din meniu (va trebui să aveți un proiect deschis pentru ca acest lucru să

→ de dialog Excepții. Extindeți grupul Excepții la rulare în limbaj comun, care


muncă). Va apărea caseta
afișează un arbore ierarhic de excepții .NET aranjate după spațiul de nume (consultați Figura 7-2).

216
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

Figura 7-2. Vizualizatorul de excepții al Visual Studio

Caseta de dialog Excepții vă permite să specificați ce excepții ar trebui să fie tratate de codul dvs. atunci când depanare
și ce excepții vor determina Visual Studio să intre imediat în modul de întrerupere. Aceasta înseamnă că nu trebuie să
dezactivați codul de tratare a erorilor pentru a depana o problemă. De exemplu, puteți alege să permiteți programului să
gestioneze un FileNotFoundException comun (care poate fi cauzat de o selecție de utilizator nevalidă), dar instruiți
Visual Studio să întrerupă executarea dacă apare o excepție neașteptată DivideByZero.

Pentru a configura acest lucru, adăugați un marcaj de selectare în coloana Aruncat de lângă intrarea pentru
excepția System.DivideByZero. În acest fel, veți fi avertizat imediat ce apare problema. Dacă nu adăugați o
bifă la coloana Aruncat, codul va continua, va rula orice rutine de tratare a excepțiilor pe care le-a definit și
va încerca să rezolve problema. Veți primi o notificare numai dacă apare o eroare și nu este disponibil un
handler de excepții adecvat.

Lanțul de excepții
Figura 7-3 arată cum funcționează proprietatea InnerException. În scenariul specific afișat aici, un
FileNotFoundException a condus la un NullReferenceException, care a condus la o particularizare
UpdateFailedException. Folosind un bloc de tratare a excepțiilor, aplicația poate prinde
UpdateFailedException. Apoi poate obține mai multe informații despre sursa problemei urmând proprietatea
InnerException la NullReferenceException, care la rândul său face referire la FileNotFoundException original.

217
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

Figura 7-3. Excepțiile pot fi înlănțuite împreună.

Proprietatea InnerException este un instrument extrem de util pentru programarea bazată pe componente. În general,
Descărcați de la Wow! eBook <www.wowebook.com>

nu este de mare ajutor dacă o componentă raportează o problemă de nivel scăzut, cum ar fi o referință nulă sau o
eroare de împărțire la zero. În schimb, trebuie să comunice un mesaj mai detaliat despre operațiunea care a eșuat și ce
intrare ar fi putut fi nevalidă. Codul de apelare poate corecta adesea problema și poate reîncerca operațiunea.

Pe de altă parte, uneori depanați un bug care se ascunde adânc în interiorul componentei în sine. În acest
caz, trebuie să știți exact ce a cauzat eroarea - nu doriți să o înlocuiți cu o excepție de nivel superior care ar
putea ascunde problema rădăcină. Utilizarea unui lanț de excepții gestionează ambele scenarii: primiți
oricâte obiecte de excepție legate sunt necesare, ceea ce poate specifica informații de la cea mai mică la
cea mai specifică condiție de eroare.

Excepții de manipulare
Prima linie de apărare într-o aplicație este de a verifica condițiile potențiale de eroare înainte de a efectua o
operație. De exemplu, un program poate verifica în mod explicit dacă divizorul este 0 înainte de a efectua un
calcul sau dacă există un fișier înainte de a încerca să îl deschidă:
dacă (divizor != 0)
{
Este sigur să împărțiți un anumit număr după divizor.
}

dacă (System.IO.File.Exists("myfile.txt")) { // Acum puteți deschide fișierul myfile.txt.

Cu toate acestea, ar trebui să utilizați în continuare gestionarea excepțiilor, deoarece pot interveni

o varietate de probleme } // (drepturi insuficiente, defecțiuni hardware etc.).

Chiar dacă efectuați acest nivel de bază de "asigurare a calității", aplicația dvs. este încă vulnerabilă. De exemplu, nu aveți nicio
modalitate de a vă proteja împotriva tuturor problemelor posibile de acces la fișiere care apar, inclusiv defecțiuni hardware sau
probleme de rețea care ar putea apărea spontan în mijlocul unei operații. În mod similar, nu aveți nicio modalitate de a valida un
ID de utilizator și o parolă pentru o bază de date înainte de a încerca să deschideți o conexiune - și chiar dacă ați face-o, acea

218
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

tehnică ar fi supusă propriului set de erori potențiale. În unele cazuri, este posibil să nu fie practic să efectuați întreaga gamă
de verificări defensive, deoarece acestea pot impune o rezistență notabilă asupra performanței aplicației dvs. Din toate
aceste motive, aveți nevoie de o modalitate de a detecta și de a face față erorilor atunci când apar.

Soluția este gestionarea structurată a excepțiilor. Pentru a utiliza tratarea structurată a excepțiilor,
încadrați codul potențial problematic în structura specială de blocuri prezentată aici:
încercați { // Codul riscant merge aici (deschiderea unui fișier, conectarea la o bază de date și
așa mai departe). } captură { // A fost detectată o eroare. Vă puteți ocupa de asta aici. } în
concluzie { // Este timpul să curățați, indiferent dacă a existat sau nu o eroare. }

Instrucțiunea de încercare permite tratarea erorilor. Orice excepții care apar în rândurile următoare pot fi "prinse"
automat. Codul din blocul de captură va fi executat atunci când este detectată o eroare. Și oricum, indiferent dacă
apare sau nu o eroare, blocul final al codului va fi executat ultimul. Acest lucru vă permite să efectuați o curățare de
bază, cum ar fi închiderea unei conexiuni la baza de date. În cele din urmă, codul este important deoarece se va
executa chiar dacă a apărut o eroare care va împiedica continuarea programului. Cu alte cuvinte, dacă o excepție
nerecuperabilă vă oprește cererea, veți avea în continuare șansa de a elibera resurse.
Actul de a prinde o excepție o neutralizează. Dacă tot ce doriți să faceți este să faceți inofensivă o anumită
eroare, nici măcar nu trebuie să adăugați niciun cod în blocul de captură al gestionarului de erori. De obicei,
totuși, această porțiune a codului va fi utilizată pentru a raporta eroarea utilizatorului sau pentru a o înregistra
pentru referințe viitoare. Într-o componentă separată (cum ar fi un obiect de afaceri), acest cod poate
gestiona excepția, poate efectua o curățare și apoi o poate rearunca în codul de apelare, care va fi în cea
mai bună poziție pentru a-l remedia sau pentru a alerta utilizatorul. Sau ar putea crea de fapt un nou obiect
de excepție cu informații suplimentare și să le arunce.

Capturarea unor excepții specifice


Gestionarea structurată a excepțiilor este deosebit de flexibilă, deoarece vă permite să prindeți anumite tipuri
de excepții. Pentru a face acest lucru, adăugați mai multe declarații privind captura, fiecare identificând tipul
de excepție (și furnizând o nouă variabilă pentru capturarea acesteia), după cum urmează:
încercați { // Codul bazei de date riscante
merge aici.

} captură (System.Data.SqlClient.SqlException err) { // Captează


probleme comune, cum ar fi erorile de conexiune. } captură
(System.NullReferenceException err) { // Prinde probleme rezultate
dintr-un obiect neinițializat. }

219
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

O excepție va fi prinsă atâta timp cât este o instanță a clasei indicate sau dacă este derivată din acea
clasă. Cu alte cuvinte, dacă utilizați această afirmație:
captură (excepție de eroare)
veți prinde orice excepție, deoarece fiecare obiect de excepție este derivat din clasa de bază
System.Exception.
Blocurile de excepție funcționează puțin ca un cod condiționat. De îndată ce se găsește un manipulator de
excepții corespunzător, se invocă codul de captură corespunzător. Prin urmare, trebuie să vă organizați
declarațiile de captură de la cele mai specifice la cele mai puțin specifice:
încercați { // Codul bazei de date riscante
merge aici.

} captură (System.Data.SqlClient.SqlException err) { // Prinde probleme


comune, cum ar fi erorile de conexiune. } captură
(System.NullReferenceException err) { // Prinde probleme rezultate
dintr-un obiect neinițializat. } captură (System.Exception err) { // Prinde
orice alte erori.

Încheind cu o declarație de captură pentru clasa de excepție de bază este adesea o idee bună pentru a vă
asigura că nu se strecoară erori. Cu toate acestea, în programarea bazată pe componente, ar trebui să vă
asigurați că interceptați numai acele excepții pe care le puteți trata sau recupera. În caz contrar, este mai
bine să lăsați codul de apelare să prindă eroarea inițială.

DETERMINAREA EXCEPȚIILOR PE CARE TREBUIE SĂ LE PRINDEȚI

Când utilizați clase din .NET Framework, este posibil să nu știți ce excepții trebuie să
prindeți. Din fericire, Ajutorul Visual Studio vă poate completa.
Trucul este să căutați metoda sau constructorul pe care îl utilizați în referința bibliotecii clasei. O modalitate
rapidă de a trece la o anumită metodă este să utilizați indexul Ajutor - trebuie doar să tastați numele clasei,
urmat de un punct, urmat de numele metodei, ca în File.Open (care este o metodă pe care o veți utiliza pentru
a deschide fișiere în capitolul 17). Dacă există mai multe versiuni supraîncărcate ale metodei, veți vedea o
pagină care le listează pe toate și va trebui să faceți clic pe cea care are parametrii doriți.
După ce găsiți metoda potrivită, parcurgeți documentația metodei până când găsiți o
secțiune numită Excepții. Această secțiune enumeră toate excepțiile posibile pe care
această metodă le poate arunca. De exemplu, dacă căutați metoda File.Open(), veți găsi
că excepțiile posibile includ DirectoryNotFoundException, FileNotFoundException,
UnauthorizedAccessException și așa mai departe. Probabil că nu veți scrie un bloc de
captură pentru fiecare excepție posibilă. Cu toate acestea, ar trebui să știți în continuare
despre toate, astfel încât să puteți decide ce excepții doriți să gestionați separat.

220
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

Rutine imbricate pentru tratarea excepțiilor


Când se aruncă o excepție, .NET încearcă să găsească o declarație de captură corespunzătoare în metoda
curentă. În cazul în care codul nu se află într-un bloc de excepții structurate local sau dacă niciuna dintre
declarațiile de captură nu se potrivește cu excepția, .NET va muta stiva de apeluri cu un nivel mai sus,
căutând rutine de tratare a excepțiilor active. Luați în considerare exemplul prezentat aici, unde rutina de
tratare a evenimentelor Page.Load apelează o metodă privată DivideNumbers():
vid protejat Page_Load(Object sender, EventArgs e) { try { DivideNumbers(5, 0); } captură (DivideByZeroException err) { //
Raportați eroarea aici. } }

zecimal privat DivideNumbers(număr zecimal, divizor zecimal) { return


number/divizor; }

În acest exemplu, metoda DivideNumbers() nu are niciun fel de rutină de tratare a excepțiilor. Cu toate
acestea, apelul metodei DivideNumbers() se face în interiorul unui bloc de încercare, ceea ce înseamnă că
problema va fi prinsă mai în amonte în codul de apelare. Aceasta este o abordare bună, deoarece rutina
DivideNumbers() ar putea fi utilizată într-o varietate de circumstanțe (sau, dacă face parte dintr-o
componentă, într-o varietate de tipuri diferite de aplicații). Chiar nu are acces la niciun fel de interfață cu
utilizatorul și nu poate raporta direct o eroare. Numai codul de apelare este în măsură să determine dacă
problema este una gravă sau minoră și numai codul de apelare poate solicita utilizatorului mai multe
informații sau poate raporta detaliile erorii în pagina web.

Notă În acest exemplu, se acordă o atenție deosebită utilizării tipului de date zecimale, mai degrabă decât dublului mai comun
• tip de date. Asta pentru că, spre deosebire de ceea ce v-ați putea aștepta, este acceptabil să
împărțiți un dublu la 0. Rezultatul este valoarea specială Double.PositiveInfinity (sau
Double.NegativeInfinity dacă împărțiți un număr negativ la 0).

De asemenea, puteți suprapune rutinele de tratare a excepțiilor în așa fel încât diferite rutine de tratare a
excepțiilor să filtreze diferite tipuri de probleme. Iată un astfel de exemplu:

vid protejat Page_Load(Object sender, EventArgs e) { try {


zecimal average = GetAverageCost(DateTime.Now); } captură
(DivideByZeroException err) } } { // Raportați eroarea aici.

221
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

zecimal privat GetAverageCost(DateTime saleDateDate) { încercați { // Utilizați codul de acces la baza de date aici pentru a prelua
toate înregistrările de vânzare // pentru această dată și pentru a calcula media.

} captură (System.Data.SqlClient.SqlException err) {


// Gestionați o problemă legată de baza de date.

} în concluzie { // Închideți conexiunea


bazei de date.

}}

Disecarea codului . . .
Ar trebui să fiți conștienți de următoarele puncte:
• Dacă apare un SqlException în timpul operației bazei de date, acesta va fi prins în
metoda GetAverageCost().
• Dacă apare o excepție DivideByZero(de exemplu, metoda nu primește înregistrări,
dar încearcă totuși să calculeze o medie), excepția va fi prinsă în rutina de tratare a
evenimentelor Page.Load.
• Dacă apare o altă problemă (cum ar fi o excepție de referință nulă), nu există un
handler de excepții activ pentru a o detecta. În acest caz, .NET va căuta prin întreaga
stivă de apeluri fără a găsi o declarație de captură corespunzătoare într-o rutină activă
de tratare a excepțiilor și va genera o eroare de execuție, va încheia programul și va
returna o pagină cu informații despre excepție.

Tratarea excepțiilor în acțiune


Puteți utiliza un program simplu pentru a testa excepțiile și pentru a vedea ce fel de informații sunt
preluate. Acest program permite unui utilizator să introducă două valori și încearcă să le împartă. Apoi
raportează toate informațiile privind excepțiile aferente în pagină (a se vedea figura 7-4).

222
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

Figura 7-4. Captarea și afișarea informațiilor despre excepții

Evident, puteți preveni cu ușurință apariția acestei excepții utilizând verificări suplimentare de siguranță a codului sau o
puteți rezolva elegant folosind controalele de validare. Cu toate acestea, acest cod oferă un bun exemplu despre modul în
care puteți face față proprietăților unui obiect de excepție. De asemenea, vă oferă o idee bună despre ce fel de informații
vor fi returnate.
Iată codul clasei de pagini pentru acest exemplu:

public clasa parțială ErrorHandlingTest : System.Web.UI.Page


{
protejat void cmdCompute_Click(Obiect expeditor, EventArgs e)
{
încercați { zecimal a, b, rezultat;

a = Decimal.Parse(txtA.Text);
b = Decimal.Parse (txtB.Text); rezultat = a/b; lblResult.Text =
rezultat. ToString(); } lblResult.ForeColor =
System.Drawing.Color.Black; catch (Exception err) {
lblResult.Text = "<b>Message:</b> " + err. Mesaj;
lblResult.Text += "<br /><br />"; lblResult.Text +=
"<b>Sursa:</b> " + eroare. Sursă; lblResult.Text += "<br /><br
/>"; } } lblResult.Text += "<b>Stack Trace:</b> " + err. Stivuire;
lblResult.ForeColor = System.Drawing.Color.Red;

223
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

}
Rețineți că, imediat ce apare eroarea, execuția este transferată unui rutină de tratare a excepțiilor. Codul din blocul de
încercare nu este finalizat. Din acest motiv, rezultatul etichetei este setat în blocul de încercare. Aceste linii vor fi
executate numai dacă codul de divizare rulează fără erori.
Veți vedea multe alte exemple de tratare a excepțiilor pe parcursul acestei cărți. Capitolele privind
accesul la date din partea 4 a acestei cărți prezintă cele mai bune practici pentru tratarea excepțiilor
la accesarea unei baze de date.

Excepții de stăpânire
Rețineți aceste puncte atunci când lucrați cu tratarea structurată a excepțiilor:

Împărțiți codul în mai multe blocuri de încercare/captură: dacă puneți tot codul într-un singur rutină de tratare a
excepțiilor, veți avea probleme la determinarea locului în care a apărut problema. Nu aveți nicio modalitate de
a "relua" codul într-un bloc de încercare. Aceasta înseamnă că, dacă apare o eroare la începutul unui bloc de
încercare lung, veți omite o cantitate mare de cod. Regula generală este să utilizați o rutină de tratare a
excepțiilor pentru o activitate asociată (cum ar fi deschiderea unui fișier și regăsirea informațiilor).
Raportați toate erorile: În timpul depanării, porțiuni din codul aplicației de gestionare a erorilor pot
masca greșelile ușor de corectat din aplicație. Pentru a preveni acest lucru, asigurați-vă că raportați
toate erorile și luați în considerare omiterea unei logici de gestionare a erorilor în compilările timpurii.
Nu utilizați rutine de tratare a excepțiilor pentru fiecare instrucțiune: Instrucțiunile simple de cod
(atribuirea unei valori constante unei variabile, interacțiunea cu un control etc.) pot provoca erori în timpul
testării dezvoltării, dar nu vor cauza probleme viitoare odată perfecționate. Gestionarea erorilor trebuie
utilizată atunci când accesați o resursă externă sau aveți de-a face cu date furnizate asupra cărora nu
aveți control (și, prin urmare, pot fi nevalide).

Aruncarea propriilor excepții


De asemenea, puteți defini propriile obiecte de excepție pentru a reprezenta condiții de eroare particularizate. Tot ce trebuie să
faceți este să creați o instanță a clasei de excepții corespunzătoare și apoi să utilizați instrucțiunea de aruncare.
Următorul exemplu introduce o metodă modificată DivideNumbers(). Acesta verifică în mod explicit dacă
divizorul specificat este 0 și apoi creează manual și aruncă o instanță a clasei DivideByZeroException pentru
a indica problema, mai degrabă decât să încerce operațiunea. În funcție de cod, acest model poate
economisi timp prin eliminarea unor pași inutili sau poate împiedica inițierea unei sarcini dacă nu poate fi
finalizată cu succes.
protejat void Page_Load(Obiect expeditor, EventArgs e)
{
încercați { DivideNumbers(5, 0); }
captură (DivideByZeroException err)
{ } } // Raportați eroarea aici.

224
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

zecimal privat DivideNumbers(număr zecimal, divizor zecimal) { if (divizor == 0) { DivideByZeroException err = nou
DivideByZeroException(); aruncă eroare; } else { număr returnat/divizor; } }

Alternativ, aveți posibilitatea să creați un obiect de excepție .NET și să specificați un mesaj de eroare
particularizat utilizând un alt constructor:

zecimal privat DivideNumbers(număr zecimal, divizor zecimal) { if (divizor == 0) { DivideByZeroException err = nou
DivideByZeroException( "Ați furnizat 0 pentru parametrul divizor. Trebuie să fii oprit."); aruncă eroare; } else { număr
returnat/divizor; } }

În acest caz, orice manipulator obișnuit de excepții va prinde în continuare DivideByZeroException.


Singura diferență este că obiectul de eroare are o proprietate Mesaj modificată care conține șirul
particularizat. Figura 7-5 prezintă excepția rezultată.

225
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

Figura 7-5. Excepție standard, mesaj personalizat

Aruncarea unei excepții este cea mai utilă în programarea bazată pe componente. În programarea bazată pe
componente, pagina dvs. ASP.NET creează obiecte și metode de apelare dintr-o clasă definită într-un ansamblu
compilat separat. În acest caz, clasa din componentă trebuie să poată notifica codul de apelare (aplicația web) cu
privire la orice erori. Componenta ar trebui să gestioneze erorile recuperabile în liniște și să nu le transmită codului
de apelare. Pe de altă parte, dacă apare o eroare nerecuperabilă, aceasta trebuie indicată întotdeauna cu o
excepție și niciodată printr-un alt mecanism (cum ar fi un cod de returnare). Pentru mai multe informații despre
programarea bazată pe componente, consultați capitolul 22. Dacă puteți găsi o excepție în biblioteca de clasă care
reflectă cu exactitate problema care a apărut, ar trebui să o aruncați. Dacă trebuie să returnați informații
suplimentare sau specializate, vă puteți crea propria clasă de excepții particularizată.
Clasele de excepții particularizate ar trebui să moștenească întotdeauna de la System.ApplicationException, care la
rândul său derivă din clasa de excepție de bază. Acest lucru permite .NET să facă distincția între două clase largi de
excepții - cele pe care le creați și cele care sunt native pentru .NET Framework.
Când creați o clasă de excepții, puteți adăuga proprietăți pentru a înregistra informații suplimentare. De
exemplu, iată o clasă specială care înregistrează informații despre încercarea eșuată de împărțire la zero:

clasa publică CustomDivideByZeroException : ApplicationException


{
Adăugați o variabilă pentru a specifica numărul "altele".
Acest lucru ar putea ajuta la diagnosticarea problemei.
DividingNumber zecimal public;
}

226
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

Notă Putețiorganizată
• Abordarea defini clasa
constă
de excepții
în utilizarea
particularizată
unui fișierdirect
de cod
în fișierul
separat.codului
Pentru.aspx.cs.
aceasta,Cu
faceți
toateclic
acestea, mai mult
dreapta pe aplicație în Exploratorul de soluții și alegeți Adăugare element nou. Apoi, alegeți Clasă
din lista de șabloane, introduceți un nume de fișier și faceți clic pe Adăugare. Visual Studio va
plasa un nou fișier de cod în subfolderul App_Code al site-ului web. (Depinde de dvs. dacă creați
un fișier de cod separat pentru fiecare clasă sau puneți mai multe clase în același fișier de cod.
Ambele abordări au același efect.)

Puteți arunca această excepție personalizată astfel:

private zecimal DivideNumbers(număr zecimal, divizor zecimal) { if (divizor == 0) { CustomDivideByZeroException err = new
CustomDivideByZeroException(); err. DividingNumber = număr; aruncă eroare; } else { număr returnat/divizor; } }

Pentru a perfecționa excepția personalizată, trebuie să o furnizați cu cei trei constructori standard. Acest
lucru permite crearea clasei de excepții în modurile standard pe care le acceptă fiecare excepție:
• Pe cont propriu, fără argumente
• Cu un mesaj personalizat
• Cu un mesaj personalizat și un obiect de excepție de utilizat ca excepție internă
Acești constructori nu trebuie să conțină niciun cod. Tot ce trebuie să facă acești constructori este să
transmită parametrii către clasa de bază (constructorii din clasa moștenită ApplicationException) folosind
cuvântul cheie de bază, așa cum se arată aici:
clasa publică CustomDivideByZeroException : ApplicationException { // Adăugați o variabilă pentru a specifica numărul "altele".
împărțirea zecimală privatăNumăr; public zecimal DividingNumber { get { return dividingNumber; } set { dividingNumber = value; } }

public CustomDivideByZeroException() : base() {}

227
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

public CustomDivideByZeroException(string message) : base(message) {}

public CustomDivideByZeroException(șir mesaj, excepție internă) : base(mesaj,


interior) {} }

Al treilea constructor este deosebit de util pentru programarea componentelor. Vă permite să setați
proprietatea InnerException cu obiectul excepției care a cauzat problema inițială. Următorul exemplu arată
cum puteți utiliza acest constructor cu o clasă de componente numită ArithmeticUtility:
clasa publică ArithmeticUtilityException : ApplicationException { public
ArithmeticUtilityException() : base() {}

public ArithmeticUtilityException(string message) : base(message) {}


Descărcați de la Wow! eBook <www.wowebook.com>

public ArithmeticUtilityException(string message, Exception inner) :


base(message, inner) {}

clasa publică ArithmeticUtility { private zecimal Divide (număr zecimal, divizor zecimal) { try { return number/divisor; } catch
(Exception err) {

Creați o instanță a clasei de excepții specializate, // și plasați obiectul de excepție original (de exemplu, un //
DivideByZeroException) în proprietatea InnerException. ArithmeticUtilityException errNew = new
ArithmeticUtilityException("Eroare de calcul", err);

Acum aruncați noua excepție. aruncă


errNou;
}
}
}

Amintiți-vă, clasele de excepții personalizate sunt într-adevăr doar o modalitate standardizată pentru o clasă
de a comunica o eroare unei alte porțiuni de cod. Dacă nu utilizați componente sau propriile clase de
utilitare, probabil că nu trebuie să creați clase de excepții particularizate.

228
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

Excepții de înregistrare în jurnal


În multe cazuri, cel mai bine este nu numai să detectați și să prindeți excepțiile, ci și să le înregistrați. De exemplu, unele
probleme pot apărea numai atunci când serverul dvs. web are de-a face cu o încărcare deosebit de mare. Alte probleme ar
putea reapărea intermitent, fără cauze evidente. Pentru a diagnostica aceste erori și a construi o imagine mai mare a
problemelor site-ului, trebuie să înregistrați excepțiile, astfel încât acestea să poată fi revizuite mai târziu.
.NET Framework oferă o gamă largă de instrumente de înregistrare în jurnal. Când apar anumite erori, aveți
posibilitatea să trimiteți un mesaj de poștă electronică, să adăugați o înregistrare a bazei de date sau să
scrieți într-un fișier. Multe dintre aceste tehnici sunt descrise în alte părți ale acestei cărți.

Sfat
În multe cazuri, este logic să se utilizeze mai mult de o tehnică de înregistrare. De exemplu, puteți decide să înregistrați majoritatea erorilor într-un tabel ErrorLog dintr-o bază de date, dar să înregistrați erorile bazei de date în alt loc, cum ar fi Jurnalul de evenimente Windows.

Unul dintre cele mai sigure instrumente de înregistrare este sistemul de înregistrare a evenimentelor
Windows , care este încorporat în sistemul de operare Windows și disponibil pentru orice aplicație. Utilizând
jurnalele de evenimente Windows, site-ul dvs. web poate scrie mesaje text care înregistrează erori sau
evenimente neobișnuite. Jurnalele de evenimente Windows stochează mesajele dvs., precum și diverse alte
detalii, cum ar fi tipul mesajului (informații, eroare etc.) și ora la care a fost lăsat mesajul.

Vizualizarea jurnalelor de evenimente Windows


Pentru a vizualiza jurnalele de evenimente Windows, utilizați instrumentul Vizualizator evenimente inclus în Windows. Pentru a-l
lansa, începeți prin a selecta Start Control Panel. Deschideți grupul Instrumente de administrare, apoi alegeți
→ Windows, veți vedea cele patru jurnale descrise în tabelul 7-2.
Vizualizator. În secțiunea Jurnale

Tabelul 7-2. Jurnale de evenimente Windows

Nume jurnal Descriere

Aplicație Folosit pentru a urmări erorile sau notificările din orice aplicație. În general, veți
utiliza acest jurnal atunci când efectuați înregistrarea în jurnal a evenimentelor
sau veți crea propriul jurnal personalizat.

Securitate Folosit pentru a urmări problemele legate de securitate, dar utilizat în general
exclusiv de sistemul de operare.

Sistem Folosit pentru a urmări evenimentele sistemului de operare.

Instalare Se utilizează pentru a urmări problemele care apar la instalarea actualizărilor


Windows sau a altor programe software.

229
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

Utilizând Vizualizatorul de evenimente, puteți efectua o varietate de activități de gestionare cu jurnalele. De exemplu,
dacă faceți clic dreapta pe unul dintre jurnalele din lista Vizualizator evenimente, veți vedea opțiuni care vă permit să
goliți evenimentele din jurnal, să salvați intrările de jurnal în alt fișier și să importați un fișier jurnal extern.
Fiecare înregistrare de eveniment dintr-un jurnal de evenimente identifică sursa (în general, aplicația sau
serviciul care a creat înregistrarea), tipul de notificare (eroare, informații, avertisment) și ora la care a fost
inserată intrarea în jurnal. Pentru a vedea aceste informații, trebuie doar să selectați o intrare în jurnal, iar
detaliile vor apărea într-o zonă de afișare sub lista de intrări (a se vedea figura 7-6).

Figura 7-6. Vizualizatorul de evenimente

De asemenea, aveți posibilitatea să revizuiți jurnalele de evenimente în Visual Studio, cu excepția cazului în care executați ediția
puțin mai puțin puternică Visual Studio Web Developer Express, care nu include această caracteristică. Mai întâi, afișați fereastra
Server Explorer (dacă nu este deja vizibilă) alegând Vizualizare Server Explorer. (Exploratorul serverului
→ fereastra apare de obicei în partea stângă a ferestrei Visual Studio, unde partajează spațiul cu Trusa de instrumente.) Utilizând Server Explorer, extindeți serverele

→ grupul [ComputerName] Jurnale de evenimente pentru a vedea o listă de jurnale de evenimente pe computer. Această
listă este puțin mai lungă decât ceea ce ați văzut în Vizualizatorul de evenimente, deoarece include atât jurnalele de evenimente
Windows pe care le-ați văzut, cât și jurnalele de evenimente particularizate pentru anumite aplicații (pe care veți învăța să le
creați mai târziu în acest capitol).
Dacă extindeți un jurnal de evenimente în fereastra Server Explorer, veți găsi toate intrările din jurnalul de
evenimente, grupate în funcție de sursa care a făcut intrarea în jurnal. Figura 7-7 prezintă unele dintre
jurnalele de evenimente lăsate în Jurnalul de aplicații pe computerul curent de sursa de evenimente .NET
Runtime Optimization Source. După ce selectați o intrare de jurnal, puteți vizualiza detaliile sale specifice
(cum ar fi mesajul jurnalului de evenimente și ora la care a fost lăsat) în fereastra Proprietăți.

230
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

Figura 7-7. Vizualizarea intrărilor jurnalului de evenimente în Visual Studio

Una dintre problemele potențiale cu jurnalele de evenimente este că intrările vechi sunt eliminate automat atunci când
jurnalul de evenimente atinge o dimensiune maximă (implicit, 20 MB). Cu toate acestea, veți descoperi că buștenii cresc
rapid. Aceasta înseamnă că, dacă nu utilizați un jurnal de evenimente personalizat care are mult spațiu, este posibil ca
intrările de jurnal să nu dureze o perioadă lungă de timp. În mod ideal, ar trebui să utilizați jurnalele de evenimente pentru a
înregistra informații care sunt revizuite și acționate într-o perioadă relativ scurtă de timp. De exemplu, jurnalele de
evenimente sunt o alegere bună dacă intenționați să înregistrați erorile aplicației și să le revizuiți pentru a diagnostica
comportamentul ciudat imediat după ce se întâmplă. Jurnalele de evenimente nu au la fel de mult sens dacă doriți să
obțineți o imagine detaliată a activității aplicației șase luni mai târziu, deoarece Windows (sau altcineva) poate șterge
intrările vechi de jurnal. În acest scenariu, o bază de date particularizată are mai mult sens.
Dacă doriți să adăugați puțin mai mult spațiu de respirație la un jurnal existent, puteți modifica dimensiunea
maximă. Pentru aceasta, faceți clic dreapta pe jurnal și alegeți Proprietăți. Veți vedea fereastra Proprietăți
aplicație prezentată în Figura 7-8, unde puteți modifica dimensiunea maximă.

231
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

Figura 7-8. Proprietăți jurnal

Sfat: Puteți mări dimensiunea jurnalului, dar chiar nu ar trebui să dezactivați complet ștergerea automată a jurnalului, deoarece ați putea ajunge să consumați o cantitate imensă de spațiu în timp dacă informațiile nu sunt eliminate în mod regulat.

Scrierea în jurnalul de evenimente


Aveți posibilitatea să interacționați cu jurnalele de evenimente într-o pagină ASP.NET utilizând clasele din spațiul
de nume System.Diagnostics. Mai întâi, importați spațiul de nume de la începutul fișierului din spatele codului:

folosind System.Diagnostics;
Următorul exemplu rescrie pagina simplă ErrorTest pentru a utiliza înregistrarea în jurnal a evenimentelor:

public parțial clasă ErrorTestLog : Pagina


{
protejat void cmdCompute_Click(Obiect expeditor, EventArgs e)
{
încercați { zecimal a, b,
rezultat; }

232
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

a = Decimal.Parse(txtA.Text);
b = Decimal.Parse (txtB.Text); rezultat = a / b;
lblResult.Text = rezultat. ToString(); lblResult.ForeColor
= System.Drawing.Color.Black;

catch (Exception err) { lblResult.Text = "<b>Message:</b> " + err. Mesaj + "<br /><br />"; lblResult.Text +=
"<b>Sursa:</b> " + eroare. Sursa + "<br /><br />"; lblResult.Text += "<b>Stack Trace:</b> " + err. Stivuire;
lblResult.ForeColor = System.Drawing.Color.Red;

Scrieți informațiile în jurnalul de evenimente.


Jurnal EventLog = nou EventLog(); jurnal. sursă = "DivisionPage";}
jurnal. WriteEntry(err. Mesaj, EventLogEntryType.Error);

}
}
Înregistrarea jurnalului de evenimente va apărea acum în utilitarul Vizualizator evenimente, așa cum se arată în
Figura 7-9. Rețineți că înregistrarea în jurnal este destinată administratorului sau dezvoltatorului de sistem. Nu
înlocuiește codul pe care îl utilizați pentru a notifica utilizatorul și a explica faptul că a apărut o problemă.

Figura 7-9. O înregistrare de eveniment

233
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

SECURITATEA JURNALULUI DE EVENIMENTE

Există o potențială problemă cu acest exemplu. În mod obișnuit, Windows nu vă va permite să accesați jurnalul
de evenimente și veți primi o excepție atunci când încercați să rulați acest exemplu. Modul corect de a rezolva
această problemă depinde dacă testați aplicația web sau o implementați pe un server web live.

Dacă doar testați codul de înregistrare în jurnal, cea mai simplă opțiune este să rulați Visual Studio ca
administrator. Pentru aceasta, faceți clic dreapta pe comanda rapidă Visual Studio și alegeți Executare ca
administrator. Acest pas este necesar din cauza sistemului de siguranță Control cont utilizator (UAC) din Windows.
CCU vă împiedică să utilizați privilegiile de administrator, cu excepția cazului în care le solicitați în mod specific,
totul în încercarea de a restricționa virușii, programele malware și hackerii care vă pot ataca computerul.

Când implementați aplicația pe un server web, procesul nu este la fel de simplu. În mod obișnuit, contul care
rulează aplicațiile dvs. web are un set atent limitat de permisiuni. Cu siguranță nu va fi un cont de
administrator. Dacă doriți ca aplicația web să poată utiliza jurnalul de evenimente, trebuie să acordați contului
respectiv permisiunile de care are nevoie pentru a crea intrări în jurnalul de evenimente. Iată pașii pe care dvs.
(sau un administrator) trebuie să îi urmați pe computerul server web:
1. Executați regedit.exe, fie utilizând un prompt de linie de comandă, fie
alegând Executare din meniul Start.
2. Navigați la secțiunea
HKEY_Local_Machine\SYSTEM\CurrentControlSet\Services\EventLog din registry.
3. Selectați folderul EventLog dacă doriți să acordați permisiunea ASP.NET tuturor
zonelor jurnalului de evenimente. Sau selectați un anumit folder care corespunde
jurnalului de evenimente pe care ASP.NET trebuie să îl acceseze.
4. Faceți clic dreapta pe folder și alegeți Permisiuni.
5. Adăugați contul pe care îl utilizează ASP.NET în listă (sau într-un grup din care face parte
acest cont). Dacă utilizați IIS în Windows 7, Windows Vista sau Windows Server 2008, trebuie
să adăugați permisiuni la grupul de IIS_IUSRS. (Pentru a afla mai multe despre modul în care
ASP.NET aplicații sunt mapate la conturile de utilizator, consultați capitolul 26.)

6. Acordați contului control complet pentru această secțiune a registrului


bifând caseta de selectare Permitere de lângă Control complet.

Jurnale personalizate
De asemenea, puteți înregistra erorile într-un jurnal particularizat. De exemplu, puteți crea un jurnal cu numele firmei și
puteți adăuga înregistrări la acesta pentru toate aplicațiile ASP.NET. Este posibil să doriți chiar să creați un jurnal
individual pentru o aplicație deosebit de mare și să utilizați proprietatea Sursă a fiecărei intrări pentru a indica pagina (sau
metoda serviciului web) care a cauzat problema.
Accesarea unui jurnal personalizat este ușoară - trebuie doar să utilizați un constructor diferit pentru clasa EventLog
pentru a specifica numele jurnalului particularizat. De asemenea, trebuie să înregistrați o sursă de evenimente pentru
jurnal. Acest pas inițial trebuie efectuat o singură dată - de fapt, veți primi o eroare dacă încercați să creați aceeași sursă
de evenimente. De obicei, veți utiliza numele aplicației ca sursă de evenimente.
Iată un exemplu care utilizează un jurnal personalizat numit ProseTech și înregistrează sursa
evenimentului DivideByZeroApp:

234
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

Înregistrați sursa evenimentului dacă este necesar.


dacă (! EventLog.SourceExists("ProseTech"))
{
Aceasta înregistrează sursa evenimentului și creează jurnalul personalizat,
// dacă este necesar.
EventLog.CreateEventSource ("DivideByZeroApp", "ProseTech");
}

Deschideți jurnalul. Dacă jurnalul nu există, // va fi


creat automat.
Jurnal EventLog = nou EventLog("ProseTech"); butuc. sursă
= "DivideByZeroApp"; butuc. WriteEntry(err. Mesaj,
EventLogEntryType.Error);
Dacă specificați numele unui jurnal care nu există atunci când utilizați metoda CreateEventSource(),
sistemul va crea un nou jurnal de evenimente particularizat pentru prima dată când scrieți o intrare. Pentru
a vedea un jurnal de evenimente nou creat în instrumentul Vizualizator evenimente, va trebui să ieșiți din
Vizualizator evenimente și să îl reporniți. Jurnalele particularizate apar într-un grup separat numit Jurnale
de aplicații și servicii, așa cum se arată în Figura 7-10.

Figura 7-10. Un jurnal personalizat

Puteți utiliza acest tip de cod oriunde în aplicația dvs. De obicei, veți utiliza codul de înregistrare atunci
când răspundeți la o excepție care ar putea fi un simptom al unei probleme mai profunde.

235
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

A clasă de înregistrare în jurnal personalizată


În loc să adăugați prea mult cod de înregistrare în blocul de captură, o abordare mai bună este să creați o clasă
separată care să se ocupe de munca grea de înregistrare a evenimentelor. Apoi puteți utiliza acea clasă din orice pagină
web, fără a duplica niciun cod.
Pentru a utiliza această abordare, începeți prin a crea un nou fișier de cod în subfolderul App_Code al site-ului web. Puteți face acest lucru în Visual Studio alegând Site web Adăugare element nou. În caseta de dialog Adăugare element nou

→, alegeți Clasă, alegeți un nume de fișier adecvat, apoi faceți clic pe Adăugare.

Iată un exemplu de clasă numită MyLogger care gestionează detaliile înregistrării evenimentelor:

clasa publică MyLogger { public void LogError(string pageInError,


Exception err)

{
RegistruLog();

Jurnal EventLog = nou EventLog("ProseTech"); butuc. Sursă =


pageInError; butuc. WriteEntry(err. Mesaj,
EventLogEntryType.Error);
}

private void RegisterLog() { // Înregistrați sursa evenimentului dacă este necesar.

dacă (! EventLog.SourceExists ("ProseTech") {


EventLog.CreateEventSource ("DivideByZeroApp", "ProseTech");

}}

Odată ce aveți o clasă în folderul App_Code, este ușor să o utilizați oriunde pe site-ul dvs. Iată cum puteți
utiliza clasa MyLogger într-o pagină web pentru a înregistra o excepție:

încercați { // Codul riscant merge


aici. } captură (Excepție err) {

Înregistrați eroarea utilizând clasa de înregistrare în jurnal. MyLogger logger = nou MyLogger(); Logger.
LogError(Request.Path, eroare);

Acum gestionați eroarea în funcție de această pagină.


lblResult.Text = "Ne pare rău. A apărut o eroare."; }

Dacă scrieți frecvent intrări de jurnal, este posibil să nu doriți să verificați dacă jurnalul există de fiecare dată
când doriți să scrieți o intrare. În schimb, puteți crea sursa de evenimente o singură dată - când aplicația
pornește pentru prima dată - utilizând o rutină de tratare a evenimentelor aplicației din fișierul global.asax.
Această tehnică este descrisă în capitolul 5.

236
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

Sfat: înregistrarea în jurnal a evenimentelor utilizează spațiu pe disc și ia timpul procesorului departe de aplicațiile web. Nu stocați informații neimportante, cantități mari de date sau informații care ar fi mai bine într-un alt tip de stocare (cum ar fi o bază de date relațională). În general, ar trebui să utilizați un jurnal de evenimente pentru a înregistra condiții sau erori neașteptate, nu acțiuni ale clienților sau informații de urmărire a performanței.

Recuperarea informațiilor de jurnal


Unul dintre dezavantajele jurnalelor de evenimente este că sunt legate de serverul web. Acest lucru poate face dificilă
revizuirea intrărilor de jurnal dacă nu aveți o modalitate de a accesa serverul (deși le puteți citi de pe alt computer din
aceeași rețea). Această problemă are mai multe soluții posibile. O tehnică interesantă implică utilizarea unei pagini
speciale de administrare. Această pagină ASP.NET poate utiliza clasa EventLog pentru a prelua și afișa toate informațiile
din jurnalul de evenimente.
Figura 7-11 prezintă într-o pagină web simplă toate intrările care au fost lăsate de pagina
ErrorTestCustomLog. Rezultatele sunt afișate utilizând o etichetă într-un panou derulabil (un control Panou
cu proprietatea Bare de defilare setată la Vertical). O abordare mai sofisticată ar utiliza un cod similar, dar cu
unul dintre controalele de date discutate în capitolul 16.

Figura 7-11. O pagină de vizualizare jurnal

237
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

Iată codul paginii web de care veți avea nevoie:

public clasa partiala EventReviewPage : System.Web.UI.Page


{
vid protejat cmdGet_Click(Obiect expeditor, EventArgs e)
{
lblResult.Text = "";

Verificați dacă jurnalul există.


dacă (! EventLog.Exists(txtLog.Text)) { lblResult.Text = "Jurnalul de evenimente " + txtLog.Text ; } lblResult.Text += " nu
există."; else { EventLog log = new EventLog(txtLog.Text); foreach (intrare EventLogEntry în log. Intrări) { // Scrieți
intrările evenimentului pe pagină. dacă (chkAll.Checked || intrare. Sursa == txtSource.Text) { lblResult.Text += "<b>Entry
Type:</b> "; lblResult.Text += intrare. EntryType.ToString(; lblResult.Text += "<br /><b>Message:</b> "; lblResult.Text
+= intrare. Mesaj; lblResult.Text += "<br /><b>Timp generat:</b> "; lblResult.Text += intrare. Generată în timp;
lblResult.Text += "<br /><br />"; } } }
Descărcați de la Wow! eBook <www.wowebook.com>

vid protejat chkAll_CheckedChanged(Object sender,


EventArgs e) { // Controlul chkAll are AutoPostback = true.
if (chkAll.Checked) { txtSource.Text = ""; txtSource.Enabled
= fals; } else {txtSource.Enabled = true;

}}

}
Dacă alegeți să afișați toate intrările din jurnalul de aplicații, pagina va funcționa lent. Doi factori sunt în acțiune aici. În primul
rând, este nevoie de timp pentru a prelua fiecare intrare în jurnalul de evenimente; Un jurnal tipic de aplicații poate conține cu

238
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

ușurință câteva mii de intrări. În al doilea rând, codul utilizat pentru a adăuga text la controlul Etichetă este ineficient. De
fiecare dată când adăugați o informație nouă la proprietatea Label.Text, .NET trebuie să genereze un nou obiect String. O
soluție mai bună este utilizarea clasei specializate System.Text.StringBuilder, care este concepută pentru a gestiona
procesarea intensivă a șirurilor cu o cheltuială mai mică prin gestionarea unui tampon intern sau a unei memorii.

Iată modul mai eficient în care puteți scrie codul de procesare a șirurilor:

Pentru performanțe maxime, alăturați toate informațiile


despre eveniment // într-un singur șir mare folosind //
StringBuilder.
System.Text.StringBuilder sb = nou System.Text.StringBuilder();

Jurnal EventLog = nou EventLog(txtLog.Text);


foreach (intrare EventLogEntry în jurnal. Intrări)
{
Scrieți intrările de eveniment în StringBuilder. dacă
(chkAll.Checked || intrare. Sursa == txtSource.Text)

{
Sb. Adăugare("<b>Tip intrare:</b>"); Sb.
Apend(intrare. EntryType.ToString()); Sb.
Adăugare("<br /><b>Message:</b> "); Sb.
Apend(intrare. Mesaj); Sb. Adăugare("<br
/><b>Timp generat:</b>"); Sb. Apend(intrare.
Generată în timp); Sb. Anexă("<br /><br />");
}
Copiați textul complet pe pagina web.
lblResult.Text = sb. ToString();
}

Sfat: Puteți ocoli unele dintre limitările jurnalului de evenimente utilizând propriul sistem de înregistrare personalizat. Toate ingredientele de care aveți nevoie sunt încorporate în biblioteca comună a clasei. De exemplu, puteți stoca informații despre erori într-o bază de date utilizând tehnicile de acces la date descrise în capitolul 14.

Urmărirea paginilor
Instrumentele de depanare ale Visual Studio și paginile de eroare detaliate ale ASP.NET sunt extrem de utile atunci
când testați o aplicație. Cu toate acestea, uneori aveți nevoie de o modalitate de a identifica problemele după ce ați
implementat o aplicație, atunci când nu aveți Visual Studio pe care să vă bazați.
Puteți încerca să identificați aceste erori înregistrând informații de diagnosticare într-un jurnal de
evenimente, dar acest lucru presupune că cineva va revizui jurnalul în mod regulat. Mai agresiv, puteți afișa
unele informații direct în pagina web. Problema cu această strategie este că trebuie să eliminați (sau cel
puțin să comentați) tot acest cod suplimentar înainte de a implementa aplicația web. În caz contrar,
utilizatorii site-ului dvs. web ar putea vedea mesaje ciudate de depanare atunci când se așteaptă cel mai
puțin.

239
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

Din fericire, există o modalitate mai ușoară de a rezolva problema fără a recurge la o soluție autohtonă.
ASP.NET oferă o caracteristică numită urmărire, care vă oferă o modalitate mult mai convenabilă și mai
flexibilă de a raporta informațiile de diagnosticare.

Activarea urmăririi
Pentru a utiliza urmărirea, trebuie să o activați în mod explicit. Există mai multe moduri de a activa urmărirea.
Una dintre cele mai simple modalități este adăugarea unui atribut la directiva Page în fișierul .aspx:
<%@ Pagina trace="true" ... %>
De asemenea, puteți activa vectorizarea utilizând obiectul Urmărire încorporat (care este o instanță a clasei
System.Web.TraceContext). Iată un exemplu de activare a urmăririi în rutina de tratare a evenimentelor
Page.Load:
protejat void Page_Load(Obiect expeditor, EventArgs e)
{
Trace.IsEnabled = adevărat;
}
Această tehnică este utilă deoarece vă permite să activați sau să dezactivați urmărirea pentru o pagină în
anumite circumstanțe pe care le testați în codul dvs.
Rețineți că, în mod prestabilit, după ce activați urmărirea, aceasta se va aplica numai solicitărilor locale. (Cu
alte cuvinte, dacă lucrați cu o aplicație web implementată, trebuie să faceți solicitările din browserul web de
pe computerul serverului web.) Această limitare împiedică utilizatorii finali să vadă informațiile de urmărire.
Dacă trebuie să urmăriți o pagină web dintr-o locație externă, va trebui să activați urmărirea la distanță
modificând unele setări din fișierul web.config. (Puteți găsi informații despre modificarea acestor setări în
secțiunea "Application-Level Tracing" din continuarea acestui capitol.) După ce activați urmărirea la distanță,
puteți utiliza codul pentru a activa selectiv urmărirea, de exemplu, pentru anumiți utilizatori.

Informații de urmărire
ASP.NET urmărire furnizează automat un set lung de informații standard, formatate. Figura 7-12 arată
cum arată aceste informații. Pentru a construi acest exemplu, puteți începe cu orice pagină de ASP.NET
de bază. Aici este prezentată o pagină rudimentară ASP.NET cu doar o etichetă și un buton.

240
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

Figura 7-12. O pagină ASP.NET simplă

Pe cont propriu, această pagină face foarte puțin, afișând o singură linie de text. Cu toate acestea, dacă faceți
clic pe buton, urmărirea este activată setând proprietatea Trace.IsEnabled la true (așa cum se arată în
fragmentul de cod anterior). Când pagina este redată, aceasta va include o cantitate semnificativă de
informații de diagnosticare, așa cum se arată în Figura 7-13.

241
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

Figura 7-13. Urmărirea paginii simple de ASP.NET

Informațiile de urmărire sunt furnizate în mai multe categorii diferite, care sunt descrise în secțiunile
următoare. În funcție de pagina dvs., este posibil să nu vedeți toate informațiile de urmărire. De exemplu,
dacă solicitarea de pagină nu furnizează niciun parametru șir de interogare, nu veți vedea colecția
QueryString. În mod similar, dacă nu există date păstrate în starea aplicației sau a sesiunii, nu veți vedea nici
acele secțiuni.

Sfat Dacă utilizați foi de stiluri, regulile pot afecta formatarea și aspectul informațiilor de urmărire, ceea ce le poate face dificil de citit. Dacă aceasta devine o problemă, puteți utiliza urmărirea la nivel de aplicație, așa cum este descris mai jos în acest capitol (consultați secțiunea "Urmărire la nivel de aplicație").

242
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

Solicitați detalii
Această secțiune include câteva informații de bază, cum ar fi ID-ul sesiunii curente, ora la care a fost efectuată
solicitarea web și tipul de solicitare web și codificare (a se vedea Figura 7-14). Cele mai multe dintre aceste detalii
sunt destul de neinteresante și nu veți petrece mult timp uitându-vă la ele. Excepția este ID-ul sesiunii. Urmărind
când se modifică ID-ul sesiunii, veți ști când este creată o nouă sesiune. (Sesiunile sunt utilizate pentru a stoca
informații pentru un anumit utilizator între solicitările de pagină. Veți afla despre ele în capitolul 8.)

Figura 7-14. Solicitați detalii

Informații despre urmărire


Informațiile de urmărire arată diferitele etape de procesare prin care a trecut pagina înainte de a fi trimisă
clientului (a se vedea figura 7-15). Fiecare secțiune conține informații suplimentare despre cât timp a durat
finalizarea, ca măsură de la începutul primei etape (De la prima) și ca măsură de la începutul etapei
anterioare (De la ultima). Dacă adăugați propriile mesaje de urmărire (o tehnică descrisă pe scurt), acestea
vor apărea, de asemenea, în această secțiune.

Figura 7-15. Informații de urmărire

Arborele de control
Arborele de control vă arată toate controalele de pe pagină, indentate pentru a afișa ierarhia lor (ce controale sunt
conținute în alte controale), așa cum se arată în Figura 7-16. În acest exemplu simplu de pagină, arborele de control
include butoane numite cmdWrite, cmdWrite_Category, cmdError și cmdSession, toate acestea fiind definite în mod
explicit în marcajul paginii web. ASP.NET adaugă, de asemenea, automat controale literale pentru a reprezenta spațierea
și orice alte elemente statice care nu sunt controale de server (cum ar fi text sau etichete HTML obișnuite). Aceste
controale apar între butoanele din acest exemplu și au generat automat nume precum ctl00, ctl01, ctl02 și așa mai
departe.
O caracteristică utilă a acestei secțiuni este coloana Stare vizualizare, care vă spune câți octeți de spațiu sunt necesari

243
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

pentru a persista informațiile curente în control. Acest lucru vă poate ajuta să evaluați dacă activarea stării de control
diminuează performanța, în special atunci când lucrați cu controale legate de date, cum ar fi GridView.

Figura 7-16. Arborele de control

Starea sesiunii și starea aplicației


Aceste secțiuni afișează fiecare element care se află în sesiunea curentă sau în starea aplicației. Fiecare element
din colecția de stare corespunzătoare este listat cu numele, tipul și valoarea sa. Dacă stocați fragmente simple de
informații despre șiruri, valoarea este simplă - este textul real din șir. Dacă stocați un obiect, .NET apelează metoda
ToString() a obiectului pentru a obține o reprezentare șir corespunzătoare. Pentru obiectele complexe care nu
suprascriu ToString() pentru a oferi ceva util, rezultatul poate fi doar numele clasei.
Figura 7-17 afișează secțiunea stare sesiune după ce ați adăugat două elemente la starea sesiunii (un șir
obișnuit și un obiect Set de date). Capitolul 8 conține mai multe despre utilizarea stării sesiunii.

Figura 7-17. Starea sesiunii

Solicitați cookie-uri și cookie-uri de răspuns


Aceste secțiuni afișează cookie-urile care au fost trimise de browserul web cu solicitarea pentru această pagină și
afișează cookie-urile care au fost returnate de serverul web cu răspunsul. ASP.NET arată conținutul și dimensiunea
fiecărui cookie în octeți.
Figura 7-18 prezintă un exemplu cu o pagină care utilizează un modul cookie numit Preferințe care
stochează o singură informație: un nume de utilizator. (Ați învățat să scrieți codul care creează acest cookie
în capitolul 8.) În plus, browserul web primește un cookie numit ASP.NET_SessionId, pe care ASP.NET
creează automat pentru a stoca ID-ul sesiunii curente.

244
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

Figura 7-18. Colecții de cookie-uri

Există o ciudățenie cu lista de cookie-uri din informațiile de urmărire. Dacă nu ați creat cel puțin un cookie
personalizat propriu, nu veți vedea niciun cookie, inclusiv cele pe care ASP.NET le creează automat (cum ar
fi cookie-ul de sesiune). ASP.NET presupune că, dacă nu utilizați cookie-uri, nu sunteți interesat să vedeți
aceste detalii.

Colecția de anteturi
Această secțiune listează toate anteturile HTTP (a se vedea figura 7-19). Din punct de vedere tehnic, anteturile
sunt biți de informații care sunt trimise serverului ca parte a unei solicitări. Acestea includ informații despre
browserul care face solicitarea, tipurile de conținut pe care le acceptă și limba pe care o utilizează. În plus, colecția
de anteturi de răspuns listează anteturile care sunt trimise clientului ca parte a unui răspuns (chiar înainte de codul
HTML real afișat în browser). Setul de anteturi de răspuns este mai mic și include detalii precum versiunea
ASP.NET și tipul de conținut trimis (text/html pentru paginile web). În general, nu este necesar să utilizați direct
informațiile antetului. În schimb, ASP.NET ia în considerare automat aceste informații.

Figura 7-19. Colecția de anteturi

245
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

Colecția de formulare
Această secțiune listează informațiile din formularul postat. Informațiile din formular includ toate valorile remise de
controalele web, cum ar fi textul dintr-o casetă text și selecția curentă dintr-o casetă listă. Controalele web ASP.NET
extrag automat informațiile de care au nevoie din colecția de formulare, astfel încât rareori trebuie să vă faceți griji.

Figura 7-20 prezintă valorile formularului pentru pagina simplă prezentată în figura 7-12. Acesta include
câmpul de stare a vizualizării ascunse, un alt câmp ascuns care este utilizat pentru validarea evenimentelor
(o caracteristică de ASP.NET de nivel scăzut care împiedică utilizatorii să vă manipuleze paginile web înainte
de a le posta înapoi) și un câmp pentru butonul cmdTrace, care este singurul control web de pe pagină.

Figura 7-20. Colectarea formularelor

Colecția de șiruri de interogare


Această secțiune listează variabilele și valorile remise în șirul de interogare. Puteți vedea aceste informații direct în
adresa URL a paginii web (în caseta de adrese din browser). Cu toate acestea, dacă șirul de interogare constă din mai
multe valori diferite și conține multe informații, poate fi mai ușor să revizuiți elementele individuale din afișarea urmăririi.

Figura 7-21 prezintă informațiile pentru o pagină care a fost solicitată cu două valori șir de interogare, una
numită căutare și cealaltă stil denumit. Puteți încerca acest lucru cu pagina SimpleTrace.aspx tastând
?search=cat&style=full la sfârșitul adresei URL din caseta de adrese a browserului web.

Figura 7-21. Colecție de șiruri de interogare

Variabile server
Această secțiune listează toate variabilele serverului și conținutul acestora. În general, nu este necesar să
examinați aceste informații. Rețineți, de asemenea, că, dacă doriți să examinați programatic o variabilă de
server, puteți face acest lucru după nume cu colecția încorporată Request.ServerVariables sau utilizând una
dintre cele mai utile proprietăți de nivel superior din obiectul Solicitare.

Scrierea informațiilor de urmărire


Jurnalul de urmărire implicit furnizează un set de informații importante care vă pot permite să monitorizați unele aspecte
importante ale aplicației, cum ar fi conținutul stării curente și timpul necesar pentru executarea porțiunilor de cod. În plus,
veți dori adesea să generați propriile mesaje de urmărire. De exemplu, ați putea dori să scoateți valoarea unei variabile în
diferite puncte ale execuției, astfel încât să o puteți compara cu o valoare așteptată. În mod similar, poate doriți să
scoateți mesaje atunci când codul atinge anumite puncte în execuție, astfel încât să puteți verifica dacă sunt utilizate

246
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

diferite proceduri (și sunt utilizate în ordinea pe care o așteptați). Încă o dată, acestea sunt sarcini pe care le puteți realiza
și utilizând depanarea Visual Studio, dar urmărirea este o tehnică neprețuită atunci când lucrați cu o aplicație web care a
fost implementată pe un server web. Pentru a scrie un mesaj de urmărire particularizat, utilizați metoda Write() sau
metoda Warn() a obiectului de urmărire încorporat. Aceste metode sunt echivalente. Singura diferență este că Warn()
afișează mesajul cu litere roșii, ceea ce face mai ușor să se distingă de alte mesaje din listă. Iată un fragment de cod care
scrie un mesaj de urmărire atunci când utilizatorul face clic pe un buton:

protejat void cmdWrite_Click(Obiect expeditor, EventArgs e)


{
Trace.Write("Pe cale să plaseze un element în starea sesiune.");
sesiune["test"] = "conținut"; Trace.Write("Element plasat în starea
sesiune.");
}

Aceste mesaje apar în secțiunea de informații de urmărire a paginii, împreună cu mesajele implicite pe care
ASP.NET le generează automat (vezi Figura 7-22).

Figura 7-22. Mesaje de urmărire particularizate

De asemenea, puteți utiliza o metodă supraîncărcată de Scriere() sau Avertizare() care vă permite să specificați categoria.
A utilizare obișnuită a acestui câmp este de a indica metoda curentă, așa cum se arată în figura 7-23.

247
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7
Descărcați de la Wow! eBook <www.wowebook.com>

Figura 7-23. Un mesaj de urmărire clasificat

protejat void cmdWriteCategory_Click(Obiect expeditor, EventArgs e)


{
Trace.Write("cmdWriteCategory_Click", "Pe cale să
plaseze un element în starea sesiune."); sesiune["test"]
= "conținut"; Trace.Write("cmdWriteCategory_Click",
"Element plasat în starea sesiune.");

Alternativ, puteți furniza informații despre categorii și mesaje cu un obiect de excepție care va fi descris
automat în jurnalul de urmărire, așa cum se arată în Figura 7-24.

248
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

Figura 7-24. Un mesaj de urmărire a excepțiilor

protejat void cmdError_Click(Object sender, EventArgs e) { try { DivideNumbers(5, 0); } captură (Excepție err) {
Trace.Warn("cmdError_Click", "Eroare prinsă", err);

}}

zecimal privat DivideNumbers(număr zecimal, divizor zecimal)


{
numărul de returnare/divizor;
}

În mod implicit, mesajele de urmărire sunt listate în ordinea în care au fost scrise de codul dvs. Alternativ,
puteți specifica ca mesajele să fie sortate după categorie utilizând atributul TraceMode din directiva Pagini:

249
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

<%@ Page Trace="true" TraceMode="SortByCategory" %>


sau proprietatea TraceMode a obiectului Trace din codul dvs.:

Trace.TraceMode = TraceMode.SortByCategory;

Urmărirea la nivel de aplicație


Urmărirea la nivel de aplicație vă permite să activați urmărirea pentru o întreagă aplicație. Cu toate acestea, informațiile
de urmărire nu vor fi afișate în pagină. În schimb, acesta va fi colectat și stocat în memorie pentru o perioadă scurtă de
timp. Puteți examina informațiile urmărite recent solicitând o adresă URL specială. Urmărirea la nivel de aplicație oferă
mai multe avantaje. Informațiile de urmărire nu vor fi gestionate de formatarea și aspectul din pagina web, puteți compara
informațiile de urmărire din solicitări diferite și puteți revizui informațiile de urmărire înregistrate pentru solicitarea altei
persoane.
Pentru a activa urmărirea la nivel de aplicație, trebuie să modificați setările din fișierul web.config,
așa cum se arată aici:

<configuration> <system.web> <trace enabled="true" requestLimit="10"


pageOutput="false" />

...
</system.web> </configuration>

Tabelul 7-4 enumeră opțiunile de urmărire.

Tabelul 7-4. Opțiuni de urmărire

Atribut Valorile Descriere

Activat adevărat, fals Activează sau dezactivează urmărirea la nivel de aplicație.

cerereLimită Orice număr Stochează informații de urmărire pentru un număr maxim de


întreg (de solicitări HTTP. Spre deosebire de urmărirea la nivel de pagină,
exemplu, 10) aceasta vă permite să colectați un lot de informații din mai multe
solicitări. Când se atinge valoarea maximă, ASP.NET pot renunța la
informațiile din cea mai veche solicitare (care este comportamentul
implicit) sau la informațiile din noua solicitare, în funcție de setarea
mostRecente.
pageOutput adevărat, fals Stabilește dacă informațiile de vectorizare vor fi afișate pe pagină
(așa cum se întâmplă în cazul vectorizării la nivel de pagină). Dacă
alegeți false, veți putea vedea în continuare informațiile colectate
solicitând trace.axd din directorul virtual în care rulează aplicația
dvs.

traceMode SortByTime, Stabilește ordinea de sortare a mesajelor de urmărire.


SortByCategory Valoarea implicită este SortByTime.

250
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

Atribut Valorile Descriere

localNumai adevărat, fals Determină dacă informațiile de urmărire vor fi afișate numai clienților
locali (clienți care utilizează același computer) sau pot fi afișate și
clienților la distanță. În mod implicit, acest lucru este adevărat și
clienții la distanță nu pot vedea informațiile de urmărire.

mostRecente adevărat, fals Păstrează numai cele mai recente mesaje de urmărire, dacă sunt adevărate.
Când se atinge requestLimit maximum, informațiile pentru cea mai veche
solicitare sunt abandonate de fiecare dată când se primește o nouă
solicitare. Dacă este false (valoarea implicită), ASP.NET oprește colectarea
de noi mesaje de urmărire atunci când se atinge limita.

Pentru a vizualiza informațiile de urmărire, solicitați fișierul trace.axd în directorul rădăcină al aplicației web. Acest
fișier nu există de fapt; În schimb, ASP.NET interceptează automat cererea și o interpretează ca
o solicitare de informații de urmărire. Apoi va lista cele mai recente solicitări colectate, cu condiția să faceți
solicitarea de la echipamentul local sau să fi activat urmărirea la distanță (a se vedea figura 7-25).

Figura 7-25. Solicitări de aplicații urmărite

251
CAPITOLUL GESTIONAREA, ÎNREGISTRAREA ÎN JURNAL ȘI URMĂRIREA ERORILOR
7

Notă Uneori, când solicitați trace.axd de la serverul web de testare Visual Studio, este posibil să nu vedeți cele mai recente solicitări urmărite. În această situație, faceți clic pe butonul Reîmprospătare al browserului pentru a obține o listă actualizată.

Puteți vedea informațiile detaliate pentru orice solicitare făcând clic pe linkul Vizualizare detalii. Aceasta oferă:
o modalitate utilă de stocare a informațiilor de urmărire pentru o perioadă scurtă de timp și vă permite să
le revizuiți fără a fi nevoie să vedeți paginile reale (a se vedea figura 7-26).

Figura 7-26. Una dintre cererile urmărite

Ultimul cuvânt
Una dintre cele mai semnificative diferențe dintre un site web obișnuit și o aplicație web profesională este
adesea în modul în care se ocupă de erori. În acest capitol, ați învățat diferitele linii de apărare pe care le
puteți utiliza în .NET, inclusiv gestionarea structurată a erorilor, înregistrarea în jurnal și urmărirea.

252
CAPITOLUL8

•■■

Managementul statului

Cea mai importantă diferență între programarea pentru Web și programarea pentru desktop este gestionarea stării -
modul în care stocați informațiile pe durata de viață a aplicației. Aceste informații pot fi la fel de simple ca numele unui
utilizator sau la fel de complexe ca un coș de cumpărături umplut pentru un magazin de comerț electronic.

Într-o aplicație tradițională Windows, nu este nevoie să vă gândiți la gestionarea stării. Memoria este
abundentă și întotdeauna disponibilă și trebuie să vă faceți griji doar pentru un singur utilizator. Într-o aplicație
web, este o poveste diferită. Mii de utilizatori pot rula simultan aceeași aplicație pe același computer (serverul
web), fiecare comunicând printr-o conexiune HTTP apatridă. Aceste condiții fac imposibilă proiectarea unei
aplicații web ca un program tradițional Windows. Înțelegerea acestor limitări de stare este cheia pentru
crearea de aplicații web eficiente. În acest capitol, veți vedea cum puteți utiliza caracteristicile de gestionare a
stării ASP.NET pentru a stoca informații cu atenție și în mod consecvent. Veți explora diferite opțiuni de
stocare, inclusiv starea vizualizării, starea sesiunii și cookie-urile personalizate. De asemenea, veți lua în
considerare modul de transfer al informațiilor de la o pagină la alta utilizând postarea pe mai multe pagini și
șirul de interogare.

Problema statului
Într-un program tradițional Windows, utilizatorii interacționează cu o aplicație care rulează continuu. O parte
din memoria de pe computerul desktop este alocată pentru a stoca setul curent de informații de lucru. Într-o
aplicație web, povestea este destul de diferită. Un site profesional ASP.NET ar putea arăta ca o aplicație care
rulează continuu, dar aceasta este într-adevăr doar o iluzie inteligentă. Într-o solicitare web tipică, clientul se
conectează la serverul web și solicită o pagină. Când pagina este livrată, conexiunea este întreruptă și
serverul web elimină toate obiectele paginii din memorie. În momentul în care utilizatorul primește o pagină,
codul paginii web a încetat deja să ruleze și nu mai există informații rămase în memoria serverului web.
Acest design apatrid are un avantaj semnificativ. Deoarece clienții trebuie să fie conectați doar pentru câteva
secunde cel mult, un server web poate gestiona un număr imens de solicitări aproape simultane fără o lovitură de
performanță. Cu toate acestea, dacă doriți să păstrați informațiile pentru o perioadă mai lungă de timp, astfel încât
acestea să poată fi utilizate pe mai multe postback-uri sau pe mai multe pagini, trebuie să luați măsuri
suplimentare.

Vizualizare stare
Una dintre cele mai comune modalități de stocare a informațiilor este starea de vizualizare. Starea
Vizualizare utilizează un câmp ascuns pe care ASP.NET inserează automat în codul HTML final, redat al
unei pagini Web. Este un loc perfect pentru a stoca informații care sunt utilizate pentru mai multe
postback-uri într-o singură pagină web.

253
CAPITOLUL MANAGEMENTUL DE STAT
8

În capitolele anterioare, ați aflat cum controalele web utilizează starea vizualizării pentru a urmări anumite detalii. De
exemplu, dacă modificați textul unei etichete, controlul Etichetă stochează automat noul text în starea de vizualizare. În
acest fel, textul rămâne la locul său data viitoare când pagina este postată înapoi. Controalele Web stochează
majoritatea valorilor proprietăților lor în starea vizualizare, cu condiția să nu fi dezactivat ViewState (de exemplu, setând
proprietatea EnableViewState a controlului sau pagina la false).
Starea vizualizării nu se limitează la controalele web. Codul paginii dvs. web poate adăuga biți de informații
direct la starea de vizualizare a paginii care conține și le poate prelua mai târziu după ce pagina este
postată înapoi. Tipul de informații pe care le puteți stoca include tipuri de date simple și propriile obiecte
particularizate.

Colecția ViewState
Proprietatea ViewState a paginii furnizează informațiile despre starea curentă a vizualizării. Această proprietate oferă o
instanță a clasei de colectare StateBag. StateBag este o colecție de dicționare, ceea ce înseamnă că fiecare articol este
stocat într-un "slot" separat, folosind un nume unic de șir.
De exemplu, luați în considerare acest cod:

Acest cuvânt cheie se referă la obiectul curent Pagină. Este opțional. acest.
ViewState["Contor"] = 1;
Aceasta plasează valoarea 1 (sau, mai degrabă, un număr întreg care conține valoarea 1) în colecția ViewState și îi dă
numele descriptiv Counter. Dacă în prezent niciun articol nu are numele Contor, un element nou va fi adăugat automat.
Dacă un articol este deja stocat sub numele de Contor, acesta va fi înlocuit. Când regăsiți o valoare, utilizați numele
cheii. De asemenea, trebuie să aruncați valoarea preluată la tipul de date corespunzător utilizând sintaxa de turnare pe
care ați văzut-o în capitolele 2 și 3. Acest pas suplimentar este necesar deoarece colecția ViewState stochează toate
elementele ca obiecte de bază, ceea ce îi permite să gestioneze multe tipuri de date diferite.

Iată codul care preia contorul din starea de vizualizare și îl convertește într-un întreg:

contor int; contor = (int)acest.


ViewState["Contor"];

Notă ASP.NET oferă multe colecții care utilizează aceeași sintaxă de dicționar. Aceasta include colecțiile pe care le veți utiliza pentru starea sesiunii și a aplicației, precum și cele utilizate pentru memorarea în cache și cookie-uri. Veți vedea câteva dintre aceste colecții în acest capitol.

Un exemplu de stare de vizualizare


Următorul exemplu este un program simplu de contorizare care înregistrează de câte ori se face clic pe un
buton. Fără niciun fel de gestionare a statului, contorul va fi blocat perpetuu la 1. Cu utilizarea atentă a
stării de vizualizare, contorul funcționează conform așteptărilor.
public parțial clasa SimpleCounter : System.Web.UI.Page
{
vid protejat cmdIncrement_Click(Obiect expeditor, EventArgs e)
{

254
CAPITOLUL MANAGEMENTUL DE STAT
8

contor int; dacă (ViewState["Counter"] == null) {


counter = 1; } else { counter =
(int)ViewState["Counter"] + 1; }

ViewState["Contor"] = contor; lblCount.Text = "Contor: " +


contor. ToString();
}
}
Codul verifică dacă elementul există în starea vizualizare înainte de a încerca să-l recupereze. În caz contrar, ați putea
întâmpina cu ușurință probleme, cum ar fi infama excepție de referință nulă (care este descrisă în capitolul 7).

Figura 8-1 prezintă rezultatul pentru această pagină.

Figura 8-1. Un contor simplu de stare de vizualizare

Securizarea stării vizualizării


Probabil vă amintiți din capitolul 5 că informațiile despre starea vizualizării sunt stocate într-un singur șir
amestecat care arată astfel:
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" />

value="dDw3NDg2NTI5MDg7Oz4="
Pe măsură ce adăugați mai multe informații la starea de vizualizare, această valoare poate deveni mult mai lungă. Deoarece
această valoare nu este formatată ca text clar, mulți programatori ASP.NET presupun că datele lor de stare de vizualizare sunt
criptate. Nu este. În schimb, informațiile despre starea vizualizării sunt pur și simplu patch-uri împreună în memorie și convertite
într-un șir Base64 (care este un tip special de șir care este întotdeauna acceptabil într-un document HTML, deoarece nu include

255
CAPITOLUL MANAGEMENTUL DE STAT
8

caractere extinse). Un hacker inteligent ar putea inversa acest șir și ar putea examina datele de stare a vizualizării în
câteva secunde.

Stare vizualizare inviolabilă


Dacă doriți să faceți starea de vizualizare mai sigură, aveți două opțiuni. Mai întâi, vă puteți asigura că informațiile
despre starea vizualizării sunt inviolabile, instruindu ASP.NET să utilizați un cod hash. Un cod hash este uneori descris
ca o sumă de control puternică din punct de vedere criptografic. Ideea este că ASP.NET examinează toate datele în
starea de vizualizare, chiar înainte de a reda pagina finală. Rulează aceste date printr-un algoritm de hashing (cu
ajutorul unei valori cheie secrete). Algoritmul de hashing creează un segment scurt de date, care este codul hash.
Acest cod este apoi adăugat la sfârșitul datelor de stare a vizualizării, în codul HTML final trimis browserului.

Când pagina este postată înapoi, ASP.NET examinează datele stării vizualizării și recalculează codul hash
utilizând același proces. Apoi verifică dacă suma de control pe care a calculat-o se potrivește cu codul hash
stocat în starea de vizualizare pentru pagină. Dacă un utilizator rău intenționat modifică o parte din datele
stării vizualizării, ASP.NET va ajunge la un nou cod hash care nu se potrivește. În acest moment, va respinge
complet postback-ul. (S-ar putea să credeți că un utilizator cu adevărat inteligent ar putea ocoli acest lucru
generând informații false despre starea vizualizării și un cod hash corespunzător. Cu toate acestea, utilizatorii
rău intenționați nu pot genera codul hash corect, deoarece nu au aceeași cheie criptografică ca ASP.NET.
Aceasta înseamnă că codurile hash pe care le creează nu se vor potrivi.) Codurile hash sunt de fapt activate
în mod implicit, deci dacă doriți această funcționalitate, nu trebuie să faceți pași suplimentari.
Starea de vizualizare privată
Chiar și atunci când utilizați coduri hash, datele privind starea vizualizării vor putea fi citite în continuare de utilizator. În
multe cazuri, acest lucru este complet acceptabil - la urma urmei, starea vizualizării urmărește informațiile care sunt
adesea furnizate direct prin alte controale. Cu toate acestea, dacă starea vizualizării conține unele informații pe care doriți
să le păstrați secrete, puteți activa criptarea stării de vizualizare.
Puteți activa criptarea pentru o pagină individuală utilizând proprietatea ViewStateEncryptionMode din
directiva Pagină:
<%@Page ViewStateEncryptionMode="Always" %>
Sau puteți seta același atribut într-un fișier de configurare pentru a configura criptarea stării vizualizării pentru
toate paginile din site-ul dvs.

<configuration> <system.web> <pages


viewStateEncryptionMo="Always" />

...
</system.web>
</configuration>
În orice caz, acest lucru impune criptarea. Aveți trei opțiuni pentru setarea de criptare a stării de vizualizare:
criptați întotdeauna (întotdeauna), nu criptați niciodată (niciodată) sau criptați numai dacă un control solicită în
mod specific acest lucru (automat). Valoarea implicită este Auto, ceea ce înseamnă că pagina nu își va cripta
starea de vizualizare decât dacă un control din pagina respectivă solicită în mod specific acest lucru. (Din
punct de vedere tehnic, un control face această solicitare apelând metoda
Page.RegisterRequiresViewStateEncryption().) Dacă niciun control apelează această metodă pentru a indica
faptul că are informații sensibile, starea vizualizării nu este criptată, salvând astfel criptarea deasupra capului.
Pe de altă parte, un control nu are putere absolută - dacă apelează
Page.RegisterRequiresViewStateEncryption() și modul de criptare este Niciodată, starea vizualizării nu va fi
criptată.

256
CAPITOLUL MANAGEMENTUL DE STAT
8

Sfat: Nu criptați datele de stare ale vizualizării dacă nu este necesar să faceți acest lucru. Criptarea va impune o penalizare de performanță, deoarece serverul web trebuie să efectueze criptarea și decriptarea cu fiecare postback.

Păstrarea variabilelor pentru membri


Probabil ați observat că orice informație pe care o setați într-o variabilă membru pentru o pagină ASP.NET este
abandonată automat atunci când procesarea paginii este finalizată și pagina este trimisă clientului. Interesant este că
puteți ocoli această limitare folosind starea de vizualizare.
Principiul de bază este de a salva toate variabilele membre pentru a vizualiza starea atunci când are loc evenimentul
Page.PreRender și de a le prelua atunci când are loc evenimentul Page.Load. Nu uitați, evenimentul Încărcare are loc de
fiecare dată când este creată pagina. În cazul unui postback, evenimentul Încărcare are loc primul, urmat de orice alte
evenimente de control.
Următorul exemplu utilizează această tehnică cu o singură variabilă membru (denumită Conținut). Pagina oferă o
casetă text și două butoane. Utilizatorul poate alege să salveze un șir de text și apoi să îl restaureze ulterior (a se
vedea figura 8-2). Rutinele de tratare a evenimentelor Button.Click stochează și preiau acest text utilizând variabila
pentru membri Conținut. Acești rutine de tratare a evenimentelor nu trebuie să salveze sau să restaureze aceste
informații utilizând starea de vizualizare, deoarece rutinele de tratare a evenimentelor PreRender și Load
efectuează aceste activități atunci când începe și se termină procesarea paginilor.

Figura 8-2. O pagină cu starea

257
CAPITOLUL MANAGEMENTUL DE STAT
8

clasa publică parțială PreserveMembers : Pagina


{
O variabilă de membru care va fi ștearsă cu fiecare postback. conținutul șirurilor private;

vid protejat Page_Load(Object sender, EventArgs e) { if


(this. IsPostBack) {

Restaurați variabilele.
} contents = (șir)ViewState["contents"];
}

protejat void Page_PreRender(Object sender, EventArgs e) {

Variabile persistente.
} ViewState["contents"] = conținut;
Descărcați de la Wow! eBook <www.wowebook.com>

protejat void cmdSave_Click(Expeditor obiect, EventArgs e) { // Transferați conținutul casetei de text la variabila membru.
contents = txtValue.Text; } txtValue.Text = "";

protejat void cmdLoad_Click(Object sender, EventArgs e) { //


Restaurați conținutul variabilei membru în caseta de text.
txtValue.Text = conținut; }

Logica din rutinele de tratare a evenimentelor Load și PreRender permite restului codului să funcționeze mai
mult sau mai puțin ca într-o aplicație desktop. Cu toate acestea, trebuie să aveți grijă să nu stocați cantități
inutile de informații atunci când utilizați această tehnică. Dacă stocați informații inutile în starea de vizualizare,
aceasta va mări dimensiunea ieșirii finale a paginii și poate încetini astfel timpii de transmitere a paginii. Un alt
dezavantaj al acestei abordări este că ascunde realitatea de nivel scăzut că fiecare informație trebuie salvată
și restaurată în mod explicit. Când ascunzi această realitate, este mult mai probabil să uiți să o respecți și să
proiectezi pentru ea. Dacă decideți să utilizați această abordare pentru a salva variabilele membru în starea
vizualizare, utilizați-o exclusiv. Cu alte cuvinte, abțineți-vă de la salvarea unor variabile de stare de vizualizare
în etapa PreRender și a altora în gestionarii de evenimente de control, deoarece acest lucru vă va confunda
cu siguranță pe dvs. și pe orice alt programator care se uită la codul dvs.

258
CAPITOLUL MANAGEMENTUL DE STAT
8

Sfat
Exemplul de cod anterior reacționează la evenimentul Page.PreRender, care apare imediat după finalizarea procesării paginii și chiar înainte ca pagina să fie redată în HTML. Acesta este un loc ideal pentru a stoca orice informații rămase necesare. Nu puteți stoca informații de stare de vizualizare într-o rutină de tratare a evenimentelor pentru evenimentul Page.Unload. Deși codul nu va cauza o eroare, informațiile nu vor fi stocate în starea de vizualizare, deoarece ieșirea finală a paginii HTML este deja

redată.

Stocarea obiectelor personalizate


Puteți stoca propriile obiecte în starea de vizualizare la fel de ușor cum stocați tipurile numerice și de șiruri. Cu toate
acestea, pentru a stoca un element în starea de vizualizare, ASP.NET trebuie să îl puteți converti într-un flux de octeți,
astfel încât să poată fi adăugat la câmpul de introducere ascuns din pagină. Acest proces se numește serializare. Dacă
obiectele nu sunt serializabile (și implicit nu sunt), veți primi un mesaj de eroare atunci când încercați să le plasați în
starea de vizualizare.
Pentru a face obiectele serializabile, trebuie să adăugați un atribut serializabil înainte de declarația
clasei. De exemplu, iată o clasă de clienți extrem de simplă:

[Serializabil] client de clasă publică


{
șir privat prenume; șir public
FirstName { get { return
firstName; } set { firstName =
value; } }

șir privat lastName; șir public


LastName { get { return
lastName; } set { lastName =
value; } }

public Customer(șir firstName, șir lastName) { FirstName


= firstName; NumeFamilie = Nume; }

Deoarece clasa Client este marcată ca serializabilă, aceasta poate fi stocată în starea de vizualizare:

Stocați un client în starea de vizualizare.


Client cust = Client nou ("Marsala", "Simons");
ViewState["CurrentCustomer"] = cust;

Rețineți că, atunci când utilizați obiecte personalizate, va trebui să proiectați datele atunci când le
regăsiți din starea de vizualizare.

259
CAPITOLUL 8 MANAGEMENTUL STATULUI

Regăsiți un client din starea de vizualizare. Custul


clientului; cust = (Client)ViewState["ClientCurent"];

Odată ce înțelegeți acest principiu, veți putea, de asemenea, să determinați ce obiecte .NET pot fi plasate
în starea de vizualizare. Trebuie doar să găsiți informațiile despre clasă în Ajutorul Visual Studio. Cea mai
ușoară abordare este să căutați clasa în index. De exemplu, pentru a afla despre clasa FileInfo (despre
care veți afla în capitolul 17), căutați intrarea index "Clasa FileInfo". În documentația clasei, veți vedea
declarația pentru clasa respectivă, care arată cam așa:
[Serializabil] [ComVisible(true)] clasă publică sigilată
FileInfo : FileSystemInfo

Dacă declarația de clasă este precedată de atributul serializabil (așa cum este aici), instanțele acestei clase
pot fi plasate în starea de vizualizare. Dacă atributul Serializable nu este prezent, clasa nu este serializabilă
și nu veți putea plasa instanțe ale acesteia în starea de vizualizare.

Transferul informațiilor între pagini


Una dintre cele mai semnificative limitări cu starea de vizualizare este că este strâns legată de o anumită pagină. Dacă
utilizatorul navighează la o altă pagină, aceste informații se pierd. Această problemă are mai multe soluții, iar cea mai
bună abordare depinde de cerințele dvs.
În secțiunile următoare, veți învăța două tehnici de bază pentru a transfera informații între pagini: postarea
pe mai multe pagini și șirul de interogare.

Postarea pe mai multe pagini


Un postback cross-page este o tehnică care extinde mecanismul postback despre care ați învățat deja, astfel
încât o pagină să poată trimite utilizatorul la o altă pagină, completată cu toate informațiile pentru acea pagină.
Această tehnică sună conceptual simplu, dar este un potențial câmp minat. Dacă nu sunteți atent, vă poate
determina să creați pagini care sunt strâns legate de altele și dificil de îmbunătățit și de depanat.

Infrastructura care acceptă postback-uri între pagini este o proprietate numită PostBackUrl, care este definită de
interfața IButtonControl și apare în controale de buton, cum ar fi ImageButton, LinkButton și Button. Pentru a
utiliza postarea încrucișată, pur și simplu setați PostBackUrl la numele unui alt formular web. Când utilizatorul
face clic pe buton, pagina va fi postată la acea nouă adresă URL cu valorile din toate controalele de introducere
de pe pagina curentă. (Aceste informații postate înapoi includ câmpul de stare a vizualizării ascunse. După cum
veți vedea în curând, ASP.NET permite să creați o instanță actualizată a paginii sursă în memorie.)

Iată un exemplu - o pagină numită CrossPage1.aspx care definește un formular cu două casete text și un
buton. Când se face clic pe buton, acesta este postat pe o pagină numită CrossPage2.aspx.

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="CrossPage1.aspx.cs"


inherits="CrossPage1" %> <html xmlns="http://www.w3.org/1999/xhtml"> <head
runat="server"> <title>CrossPage1</title> </head> <body>

260
CAPITOLUL MANAGEMENTUL DE STAT
8

<form id="form1" runat="server" > <div> Prenume: <asp:TextBox ID="txtFirstName"


runat="server"></asp:TextBox> <br /> Nume: <asp:TextBox ID="txtLastName"
runat="server"></asp:TextBox> <br /> <br /> <asp:Button runat="server" ID="cmdPost"
PostBackUrl="CrossPage2.aspx" text="Cross-Page Postback" /><br /> </div> </form>
</body> </html>

Pagina CrossPage1 nu include niciun cod. Figura 8-3 arată cum apare în browser.

Figura 8-3. Sursa unei postări pe mai multe pagini

Acum, dacă încărcați această pagină și faceți clic pe buton, pagina va fi postată înapoi la CrossPage2.aspx. În acest
moment, pagina CrossPage2.aspx poate interacționa cu CrossPage1.aspx utilizând proprietatea Page.PreviousPage.
Iată o rutină de tratare a evenimentelor care preia titlul de pe pagina anterioară și îl afișează:

public parțial clasa CrossPage2 : System.Web.UI.Page


{
vid protejat Page_Load(expeditor obiect, EventArgs e)
{
dacă (PreviousPage != null) { lblInfo.Text = "Ați venit dintr-o
pagină intitulată " + } } PreviousPage.Title;

261
CAPITOLUL MANAGEMENTUL DE STAT
8

}
Rețineți că această pagină verifică dacă există o referință nulă înainte de a încerca să accesați obiectul Pagina
anterioară. Dacă este o referință nulă, nu a avut loc nicio postare pe mai multe pagini. Aceasta înseamnă că
CrossPage2.aspx fost solicitat direct sau CrossPage2.aspx postat înapoi. Oricum ar fi, nu este disponibil niciun
obiect PreviousPage. Figura 8-4 arată ce veți vedea atunci când CrossPage1.aspx postați pe CrossPage2.aspx.

Figura 8-4. Ținta unei postări pe mai multe pagini

Obținerea mai multor informații din pagina sursă


Exemplul anterior arată un test inițial interesant, dar nu vă permite cu adevărat să transferați informații utile.
La urma urmei, probabil că sunteți interesat să preluați detalii specifice (cum ar fi textul din casetele de text
ale CrossPage1.aspx) din CrossPage2.aspx. Titlul în sine nu este foarte interesant. Pentru a obține detalii
mai specifice, cum ar fi valorile de control, trebuie să aruncați referința Pagina anterioară la clasa de pagini
corespunzătoare (în acest caz este clasa CrossPage1). Iată un exemplu care tratează corect această
situație, verificând mai întâi dacă obiectul Pagina anterioară este o instanță a clasei așteptate:

vid protejat Page_Load(expeditor obiect, EventArgs e) { CrossPage1 prevPage = PreviousPage as CrossPage1; if (prevPage !=
null) { // (Citiți câteva informații din pagina anterioară.) } }

262
CAPITOLUL MANAGEMENTUL DE STAT
8

Notă site Web fără proiect, Visual Studio poate semnaliza acest lucru ca o eroare, indicând
• Într-un
faptul că nu are informațiile de tip pentru clasa de pagini sursă (în acest exemplu, adică
CrossPage1). Cu toate acestea, odată ce compilați site-ul web, eroarea va dispărea.

De asemenea, puteți rezolva această problemă într-un alt mod. În loc să proiectați manual referința, puteți
adăuga directiva PreviousPageType la pagina .aspx care primește postback-ul pe mai multe pagini (în acest
exemplu, CrossPage2.aspx), imediat după directiva Pagină. Directiva PreviousPageType indică tipul estimat
de pagină care inițiază postback-ul între pagini. Iată un exemplu:
<%@ PreviousPageType VirtualPath="~/CrossPage1.aspx" %>
Acum, proprietatea PreviousPage va utiliza automat tipul CrossPage1. Acest lucru vă permite să săriți codul
de proiectare și să mergeți direct la lucru folosind obiectul paginii anterioare, astfel:

protejat void Page_Load(expeditor obiect, EventArgs e) { if (PreviousPage != null)

{
(Citiți câteva informații de pe pagina anterioară.)
}
}
Cu toate acestea, această abordare este mai fragilă, deoarece vă limitează la o singură clasă de pagini. Nu aveți
flexibilitatea de a face față situațiilor în care mai multe pagini ar putea declanșa o postback pe mai multe pagini. Din
acest motiv, este de obicei mai flexibil să folosiți abordarea turnării.
După ce ați proiectat pagina anterioară la tipul de pagină corespunzător, tot nu veți putea accesa direct obiectele de
control pe care le conține. Acest lucru se datorează faptului că controalele de pe pagina web nu sunt accesibile public
altor clase. Puteți rezolva acest lucru utilizând proprietăți.
De exemplu, dacă doriți să expuneți valorile din două casete text din pagina sursă, puteți adăuga proprietăți
care încadrează variabilele de control. Iată două proprietăți pe care le puteți adăuga la clasa CrossPage1
pentru a-i expune controalele TextBox:
public TextBox FirstNameTextBox {
get { return txtFirstName; } } public
TextBox LastNameTextBox { get {
return txtLastName; } }

Cu toate acestea, aceasta nu este de obicei cea mai bună abordare. Problema este că expune prea multe detalii, oferind
paginii țintă libertatea de a citi totul, de la textul din caseta de text până la fonturile și culorile sale. Dacă trebuie să
modificați pagina mai târziu pentru a utiliza diferite controale de intrare, va fi dificil să mențineți aceste proprietăți. În
schimb, probabil că veți fi forțat să rescrieți codul în ambele pagini.
O alegere mai bună este să definiți metode sau proprietăți specifice, limitate, care extrag doar informațiile de
care aveți nevoie. De exemplu, puteți decide să adăugați o proprietate Nume complet care regăsește doar
textul din cele două casete text. Iată codul paginii complete pentru CrossPage1.aspx cu această proprietate:

263
CAPITOLUL MANAGEMENTUL DE STAT
8

public clasa parțială CrossPage1 : System.Web.UI.Page { public string FullName { get { return txtFirstName.Text + " " +
txtLastName.Text; }

}}

În acest fel, relația dintre cele două pagini este clară, simplă și ușor de întreținut. Probabil că puteți modifica
controalele din pagina sursă (CrossPage1) fără a fi nevoie să modificați alte părți ale aplicației. De exemplu,
dacă decideți să utilizați controale diferite pentru introducerea numelui în CrossPage1.aspx, veți fi obligat să
revizuiți codul pentru proprietatea NumeComplet. Cu toate acestea, modificările dvs. ar fi limitate la
CrossPage1.aspx și nu ar trebui să CrossPage2.aspx modificați deloc.
Iată cum puteți rescrie codul în CrossPage2.aspx pentru a afișa informațiile din
CrossPage1.aspx:

vid protejat Page_Load(expeditor obiect, EventArgs e)


{
dacă (PreviousPage != null) { lblInfo.Text = "Ați venit dintr-o
pagină intitulată " + PreviousPage.Title + "<br />";

CrossPage1 prevPage = PreviousPage as CrossPage1; dacă


(prevPage != null) { lblInfo.Text += "Ați tastat aici: " +
prevPage.FullName; }

}
}
Observați că pagina țintă (CrossPage2.aspx) poate accesa proprietatea Title a paginii anterioare (CrossPage1.aspx) fără
a efectua nicio turnare. Acest lucru se datorează faptului că proprietatea Title este definită ca parte a clasei
System.Web.UI.Page de bază și, prin urmare, fiecare pagină web o include. Cu toate acestea, pentru a obține acces la
proprietatea FullName mai specializată, trebuie să aruncați pagina anterioară la clasa de pagini din dreapta (CrossPage1)
sau să utilizați directiva PreviousPageType discutată mai devreme.
Figura 8-5 prezintă noul rezultat.

264
CAPITOLUL 8
MANAGEMENTUL STATULUI

Figura 8-5. Preluarea informațiilor specifice din pagina sursă

Notă: Postback-urile pe mai multe pagini sunt cu adevărat utile, dar pot deschide calea către pagini mai complicate.

Dacă permiteți mai multor pagini sursă să posteze pe aceeași pagină de destinație, depinde de dvs. să codificați logica
care determină de la ce pagină a venit utilizatorul și apoi să acționați în consecință. Pentru a evita aceste dureri de cap,
este mai ușor să efectuați postări pe mai multe pagini numai între două pagini specifice.

ASP.NET folosește o sleight interesantă pentru a face să funcționeze postback-urile pe mai multe pagini. Prima dată
când a doua pagină accesează Page.PreviousPage, ASP.NET trebuie să creeze obiectul paginii anterioare. Pentru a
face acest lucru, începe de fapt procesarea paginii, dar o întrerupe chiar înainte de etapa PreRender și nu permite
paginii să redea nicio ieșire HTML.
Cu toate acestea, acest lucru are încă unele efecte secundare interesante. De exemplu, toate evenimentele
de pagină din pagina anterioară sunt declanșate, inclusiv Page.Load și Page.Init, iar evenimentul
Button.Click se declanșează și pentru butonul care a declanșat postback-ul pe mai multe pagini. ASP.NET
declanșează aceste evenimente, deoarece ar putea fi necesare pentru a readuce pagina sursă la starea în
care a fost ultima dată, chiar înainte de a declanșa postarea pe mai multe pagini.

Șirul de interogare
O altă abordare comună este de a transmite informații utilizând un șir de interogare în adresa URL. Această
abordare este frecvent întâlnită în motoarele de căutare. De exemplu, dacă efectuați o căutare pe site-ul Google,
veți fi redirecționat(ă) la o nouă adresă URL care încorporează parametrii de căutare. Iată un exemplu:

http://www.google.ca/search?q=organic+gardening
Șirul de interogare este porțiunea URL-ului după semnul întrebării. În acest caz, definește o singură variabilă
numită q, care conține șirul organic + grădinărit.
Avantajul șirului de interogare este că este ușor și nu exercită niciun fel de sarcină asupra serverului. Cu
toate acestea, are și câteva limitări:
CAPITOLUL MANAGEMENTUL DE STAT
8

• Informațiile sunt limitate la șiruri simple, care trebuie să conțină caractere juridice URL.
• Informațiile sunt vizibile în mod clar pentru utilizator și pentru oricine
altcineva care dorește să asculte pe Internet.
• Utilizatorul întreprinzător poate decide să modifice șirul de interogare și să furnizeze
noi valori, la care programul nu se va aștepta și împotriva cărora nu se poate proteja.
• Multe browsere impun o limită a lungimii unei adrese URL (de obicei de la 1KB la
2KB). Din acest motiv, nu puteți plasa o cantitate mare de informații în șirul de
interogare și totuși să fiți sigur de compatibilitatea cu majoritatea browserelor.
Adăugarea informațiilor la șirul de interogare este încă o tehnică utilă. Este deosebit de potrivit în aplicațiile de baze
de date, unde prezentați utilizatorului o listă de elemente care corespund înregistrărilor dintr-o bază de date, cum ar
fi produsele. Utilizatorul poate apoi să selecteze un element și să fie redirecționat către o altă pagină cu informații
detaliate despre elementul selectat. O modalitate ușoară de a implementa acest design este ca prima pagină să
trimită ID-ul elementului la a doua pagină. A doua pagină caută apoi acel element în baza de date și afișează
informațiile detaliate. Veți observa această tehnică pe site-urile de comerț electronic, cum ar fi Amazon. Pentru a
stoca informații în șirul de interogare, trebuie să le plasați acolo. Din păcate, nu aveți nicio modalitate bazată pe
colectare de a face acest lucru. În schimb, va trebui să îl inserați singur în adresa URL. Iată un exemplu care
utilizează această abordare cu metoda Response.Redirect():
Accesați newpage.aspx. Remiteți un singur argument șir de interogare //
numit recordID și setați la 10.
Response.Redirect("newpage.aspx?recordID=10");

Puteți trimite mai mulți parametri atâta timp cât sunt separați cu un ampersand (&):

Accesați newpage.aspx. Remiteți două argumente șir de interogare: //


recordID (10) și mod (complet).
Response.Redirect ("newpage.aspx?recordID=10&mode=full");

Pagina de primire lucrează mai ușor cu șirul de interogare. Poate primi valorile din colecția de dicționare
QueryString expuse de obiectul Solicitare încorporat:
string ID = Request.QueryString["recordID"];
Rețineți că informațiile sunt întotdeauna preluate ca un șir, care poate fi apoi convertit într-un alt tip de date
simplu. Valorile din colecția QueryString sunt indexate după numele variabilei. Dacă încercați să regăsiți o
valoare care nu este prezentă în șirul de interogare, veți obține o referință nulă.

Notă Spre deosebire de starea de vizualizare, informațiile transmise prin șirul de interogare sunt clar vizibile și necriptate. Nu utilizați șirul de interogare pentru informații care trebuie ascunse sau făcute inviolabile.

A exemplu de șir de interogare


Următorul program prezintă o listă de intrări. Când utilizatorul alege un element făcând clic pe elementul corespunzător din
listă, utilizatorul este redirecționat către o pagină nouă. Această pagină afișează numărul de identificare primit. Aceasta oferă
un test rapid și simplu de șir de interogare cu două pagini. Într-o aplicație sofisticată, ați dori să combinați unele dintre
caracteristicile de control al datelor care sunt descrise mai târziu în partea 3 a acestei cărți. Prima pagină oferă o listă de

266
CAPITOLUL MANAGEMENTUL DE STAT
8

elemente, o casetă de selectare și un buton de trimitere (a se vedea figura 8-6).

Figura 8-6. Un expeditor de șir de interogare

Iată codul pentru prima pagină:

public parțial clasă QueryStringSender : System.Web.UI.Page


{
protejat void Page_Load(Object sender, EventArgs e) { if (!this. IsPostBack) { // Adăugați valori eșantion.

lstItems.Items.Add ("Canapea Econo"); lstItems.Items.Add


("Draperie supremă din piele"); lstItems.Items.Add ("Covor
fără fir"); lstItems.Items.Add ("Lampă antică");
lstItems.Items.Add ("Jacuzzi cu finisaj retro");

}}

vid protejat cmdGo_Click(Obiect expeditor, EventArgs e)


{
dacă (lstItems.SelectedIndex == -1) { lblError.Text =
"Trebuie să selectați un element."; } else {

267
CAPITOLUL MANAGEMENTUL DE STAT
8

Redirecționați utilizatorul către pagina de informații, // cu datele șirului de interogare.

șir url = "QueryStringRecipient.aspx?"; url += "Item=" + lstItems.SelectedItem.Text + "&"; url += "Mode =" +
chkDetails.Checked.ToString();
} Response.Redirect(url);
}
}

Iată codul pentru pagina destinatarului (prezentată în Figura 8-7):

clasa publică parțială QueryStringRecipient : System.Web.UI.Page { protected void Page_Load(Object sender, EventArgs e) {
lblInfo.Text = "Item: " + Request.QueryString["Item"]; lblInfo.Text += "<br />Arată înregistrarea completă: "; } lblInfo.Text +=
Request.QueryString["Mode"]; }
Descărcați de la Wow! eBook <www.wowebook.com>

Figura 8-7. Un destinatar șir de interogare

Un aspect interesant al acestui exemplu este că plasează informații în șirul de interogare care nu sunt valide,
și anume, spațiul care apare în numele elementului. Când rulați aplicația, veți observa că ASP.NET codifică
șirul pentru dvs. automat, convertind spațiile la secvența de evacuare echivalentă %20 validă. Pagina
destinatar citește valorile originale din colecția QueryString fără probleme. Această codificare automată nu
este întotdeauna suficientă. Pentru a face față caracterelor speciale, ar trebui să utilizați tehnica de codificare
URL descrisă în secțiunea următoare.

268
CAPITOLUL MANAGEMENTUL DE STAT
8

Codificare URL
O problemă potențială cu șirul de interogare este că unele caractere nu sunt permise într-o adresă URL. De fapt,
lista de caractere permise într-o adresă URL este mult mai scurtă decât lista de caractere permise într-un
document HTML. Toate caracterele trebuie să fie alfanumerice sau dintr-un set mic de caractere speciale (inclusiv
$-_.+!*'(),). Unele browsere tolerează anumite caractere speciale suplimentare (Internet Explorer este notoriu lax),
dar multe nu. În plus, unele personaje au o semnificație specială. De exemplu, semnul ampersand (&) este utilizat
pentru a separa mai mulți parametri de șir de interogare, semnul plus (+) este un mod alternativ de a reprezenta un
spațiu, iar semnul numeric (#) este utilizat pentru a indica un anumit marcaj în document dintr-o pagină web. Dacă
încercați să trimiteți valori șir de interogare care includ oricare dintre aceste caractere, veți pierde unele date. Puteți
testa acest lucru cu exemplul anterior adăugând elemente cu caractere speciale în caseta listă.
Pentru a evita problemele potențiale, este o idee bună să efectuați codificarea URL pentru valorile text înainte de a le
plasa în șirul de interogare. Cu codificarea URL, caracterele speciale sunt înlocuite cu secvențe de caractere scăpate,
începând cu semnul procentual (%), urmat de o reprezentare hexazecimală din două cifre. De exemplu, caracterul &
devine %26. Singura excepție este caracterul spațiu, care poate fi reprezentat ca secvența de caractere %20 sau semnul
+.
Pentru a efectua codificarea URL, utilizați metodele UrlEncode() și UrlDecode() din clasa HttpServerUtility.
După cum ați învățat în capitolul 5, un obiect HttpServerUtility este pus la dispoziția codului dvs. în fiecare
formular web prin proprietatea Page.Server. Următorul cod utilizează metoda UrlEncode() pentru a rescrie
exemplul anterior, deci funcționează cu nume de produse care conțin caractere speciale:
șir url = "QueryStringRecipient.aspx?"; url += "Articol =" +
Server.UrlEncode(lstItems.SelectedItem.Text) + "&"; url += "Mode =" +
chkDetails.Checked.ToString(); Response.Redirect(url);

Observați că este important să nu codificați totul. În acest exemplu, nu puteți codifica caracterul & care
unește cele două valori ale șirului de interogare, deoarece este cu adevărat un caracter special.
Puteți utiliza metoda UrlDecode() pentru a readuce un șir codificat URL la valoarea inițială. Cu toate acestea,
nu trebuie să faceți acest pas cu șirul de interogare. Acest lucru se datorează faptului că ASP.NET
decodează automat valorile atunci când le accesați prin colecția Request.QueryString. (Mulți oameni încă
mai fac greșeala de a decoda valorile șirului de interogare a doua oară. De obicei, decodarea datelor deja
decodate nu va cauza o problemă. Singura excepție este dacă aveți o valoare care include semnul +. În
acest caz, utilizarea UrlDecode() va converti semnul + într-un spațiu, ceea ce nu doriți.)

Modulele cookie
Cookie-urile oferă un alt mod în care puteți stoca informații pentru utilizare ulterioară. Cookie-urile sunt fișiere mici care
sunt create în memoria browserului web (dacă sunt temporare) sau pe hard disk-ul clientului (dacă sunt permanente). Un
avantaj al cookie-urilor este că funcționează transparent, fără ca utilizatorul să fie conștient de faptul că informațiile trebuie
stocate. De asemenea, pot fi utilizate cu ușurință de orice pagină din aplicația dvs. și chiar pot fi păstrate între vizite, ceea
ce permite stocarea cu adevărat pe termen lung. Acestea suferă de unele dintre aceleași dezavantaje care afectează
șirurile de interogare - și anume, sunt limitate la informații simple despre șiruri și sunt ușor accesibile și lizibile dacă
utilizatorul găsește și deschide fișierul corespunzător. Acești factori le fac o alegere proastă pentru informații complexe sau
private sau cantități mari de date.
Unii utilizatori dezactivează cookie-urile în browserele lor, ceea ce va cauza probleme aplicațiilor web care le solicită. De
asemenea, utilizatorii pot șterge manual fișierele cookie stocate pe hard disk-urile lor. Dar, în cea mai mare parte,
cookie-urile sunt adoptate pe scară largă și utilizate pe scară largă pe multe site-uri web.
Înainte de a putea utiliza modulele cookie, ar trebui să importați spațiul de nume System.Net, astfel încât
să puteți lucra cu ușurință cu tipurile corespunzătoare:

269
CAPITOLUL MANAGEMENTUL DE STAT
8

folosind System.Net;
Cookie-urile sunt destul de ușor de utilizat. Atât obiectele Solicitare, cât și Răspuns (care sunt furnizate prin proprietățile
Paginii) oferă o colecție de cookie-uri. Trucul important de reținut este că preluați cookie-urile din obiectul Solicitare și
setați cookie-uri folosind obiectul Răspuns.
Pentru a seta un cookie, trebuie doar să creați un nou obiect HttpCookie. Apoi îl puteți completa cu
informații despre șir (utilizând modelul familiar de dicționar) și îl puteți atașa la răspunsul web curent:

Creați obiectul cookie.


HttpCookie cookie = nou HttpCookie ("Preferințe");

Setați o valoare în ea.


cookie["LanguagePref"] = "Engleză";

Adăugați altă valoare.


cookie["Țară"] = "SUA";

Adăugați-l la răspunsul web curent.


Response.Cookies.Add (cookie);

Un cookie adăugat în acest mod va persista până când utilizatorul închide browserul și va fi trimis cu
fiecare solicitare. Pentru a crea un cookie cu durată mai lungă de viață, puteți seta o dată de expirare:

Acest cookie durează un an.


prăjitură. Expiră = DateTime.Now.AddYears(1);

Preluați cookie-urile după numele cookie-ului folosind colectarea Request.Cookies:

Cookie HttpCookie = Request.Cookies["Preferințe"];

Verificați dacă a fost găsit un cookie cu acest nume. Aceasta este o


bună precauție de luat, // deoarece utilizatorul ar putea dezactiva
cookie-urile, // caz în care cookie-ul nu va exista. limbajul șirurilor;
dacă (cookie != nul)

{
language = cookie["LanguagePref"];
}

Singura modalitate de a elimina un cookie este înlocuirea acestuia cu un cookie care are o dată de expirare
care a trecut deja. Acest cod demonstrează tehnica:

HttpCookie cookie = nou HttpCookie ("Preferințe");


prăjitură. Expiră = DateTime.Now.AddDays(-1);
Response.Cookies.Add (cookie);

270
CAPITOLUL MANAGEMENTUL DE STAT
8

Un exemplu de cookie
Următorul exemplu arată o utilizare tipică a cookie-urilor pentru a stoca un nume de client (Figura 8-8).
Pentru a încerca acest exemplu, începeți prin a rula pagina, a introduce un nume și a face clic pe butonul
Creare modul cookie. Apoi, închideți browserul și solicitați din nou pagina. A doua oară, pagina va găsi
cookie-ul, va citi numele și va afișa un mesaj de întâmpinare.

Figura 8-8. Afișarea informațiilor dintr-un cookie personalizat

Iată codul pentru această pagină:

clasa publică parțială CookieExemplu : System.Web.UI.Page


{
protejat void Page_Load(Expeditor obiect, EventArgs e) { HttpCookie cookie = Request.Cookies["Preferințe"]; dacă (cookie
== null) { lblWelcome.Text = "<b>Client necunoscut</b>"; } else { lblWelcome.Text = "<b>Cookie Found.</b><br /><br />";
lblWelcome.Text += "Bun venit", + cookie["Nume"];

}}

protejat void cmdStore_Click(Object sender, EventArgs e) // Verificați dacă


{

271
CAPITOLUL MANAGEMENTUL DE STAT
8

există un cookie și creați unul nou numai dacă // nu există deja.

Cookie HttpCookie = Request.Cookies["Preferințe"]; if


(cookie == null) { cookie = new HttpCookie
("Preferences"); }

cookie["Name"] = txtName.Text; prăjitură. Expiră


= DateTime.Now.AddYears(1);
Response.Cookies.Add (cookie);
lblWelcome.Text = "<b>cookie creat.</b><br /><br />";
lblWelcome.Text += "Client nou: " + cookie["Nume"];
}
}

Notă: veți descoperi că alte funcții ASP.NET utilizează cookie-uri. Două exemple sunt starea sesiunii (care vă permite să stocați temporar informații specifice utilizatorului în memoria serverului) și securitatea formularelor (care vă permite să restricționați porțiuni ale unui site web și să forțați utilizatorii să îl acceseze printr-o pagină de conectare). Capitolul 19 discută despre securitatea formularelor, iar următoarea secțiune a acestui capitol discută despre starea

sesiunii.

Starea sesiunii
Vine un moment în viața majorității aplicațiilor când încep să aibă cerințe de stocare mai sofisticate. Este posibil ca o
aplicație să trebuiască să stocheze și să acceseze informații complexe, cum ar fi obiecte de date personalizate,
care nu pot fi ușor păstrate într-un modul cookie sau trimise printr-un șir de interogare. Sau aplicația ar putea avea
cerințe stricte de securitate care o împiedică să stocheze informații despre un client în starea de vizualizare sau
într-un cookie personalizat. În aceste situații, puteți utiliza facilitatea de stare de sesiune încorporată ASP.NET.
Gestionarea stării sesiunii este una dintre caracteristicile principale ale ASP.NET. Vă permite să stocați orice tip de
date în memoria de pe server. Informațiile sunt protejate, deoarece nu sunt transmise niciodată clientului și sunt
legate în mod unic de o anumită sesiune. Fiecare client care accesează aplicația are o sesiune diferită și o colecție
distinctă de informații. Starea sesiunii este ideală pentru stocarea informațiilor, cum ar fi articolele din coșul de
cumpărături al utilizatorului curent atunci când utilizatorul navighează de la o pagină la alta.

Urmărirea sesiunii
ASP.NET urmărește fiecare sesiune utilizând un identificator unic pe 120 de biți. ASP.NET folosește un algoritm
proprietar pentru a genera această valoare, garantând astfel (statistic vorbind) că numărul este unic și este suficient de
aleatoriu încât un utilizator rău intenționat să nu poată face inginerie inversă sau să "ghicească" ce ID de sesiune va
folosi un anumit client. Acest ID este singura informație legată de sesiune care este transmisă între serverul web și
client.
Când clientul prezintă ID-ul sesiunii, ASP.NET caută sesiunea corespunzătoare, recuperează obiectele pe
care le-ați stocat anterior și le plasează într-o colecție specială, astfel încât să poată fi accesate în codul dvs.
Acest proces are loc automat.

272
CAPITOLUL MANAGEMENTUL DE STAT
8

Pentru ca acest sistem să funcționeze, clientul trebuie să prezinte ID-ul de sesiune corespunzător cu
fiecare solicitare. Puteți realiza acest lucru în două moduri:

Utilizarea cookie-urilor: În acest caz, ID-ul sesiunii este transmis într-un cookie special (numit
ASP.NET_SessionId), pe care ASP.NET îl creează automat atunci când se utilizează colecția de sesiuni.
Aceasta este valoarea implicită.
Utilizarea adreselor URL modificate: În acest caz, ID-ul sesiunii este transmis într-o adresă URL special
modificată (sau modificată). Acest lucru vă permite să creați aplicații care utilizează starea sesiunii cu
clienți care nu acceptă cookie-uri.

Starea sesiunii nu vine gratuit. Deși rezolvă multe dintre problemele asociate cu alte forme de gestionare a stării,
forțează serverul să stocheze informații suplimentare în memorie. Această cerință suplimentară de memorie, chiar dacă
este mică, poate crește rapid la niveluri care distrug performanța, pe măsură ce sute sau mii de clienți accesează site-ul.

Cu alte cuvinte, trebuie să vă gândiți la orice utilizare a stării sesiunii. O utilizare neglijentă a stării sesiunii
este unul dintre cele mai frecvente motive pentru care o aplicație web nu poate scala pentru a servi un număr
mare de clienți. Uneori, o abordare mai bună este utilizarea memorării în cache, așa cum este descris în
capitolul 23.

Utilizarea stării sesiunii


Puteți interacționa cu starea sesiunii utilizând clasa System.Web.SessionState.HttpSessionState, care este
furnizată într-o pagină web ASP.NET ca obiect Sesiune încorporat. Sintaxa pentru adăugarea elementelor la
colecție și regăsirea acestora este practic aceeași ca și pentru adăugarea elementelor la starea de
vizualizare a unei pagini. De exemplu, puteți stoca un set de date în memoria sesiunii astfel:
Session["InfoDataSet"] = dsInfo;
Apoi îl puteți prelua cu o operațiune de conversie adecvată:
dsInfo = (DataSet)Session["InfoDataSet"];
Desigur, înainte de a încerca să utilizați obiectul dsInfo, va trebui să verificați dacă există cu adevărat - cu alte
cuvinte, că nu este o referință nulă. Dacă dsInfo este nul, depinde de tine să-l regenerezi. (De exemplu, puteți
decide să interogați o bază de date pentru a obține cele mai recente date.)

Notă Capitolul 14 explorează setul de date.


Starea sesiunii este globală pentru întreaga aplicație pentru utilizatorul curent. Cu toate acestea, starea
sesiunii poate fi pierdută în mai multe moduri:
• Dacă utilizatorul închide și repornește browserul.
• Dacă utilizatorul accesează aceeași pagină printr-o fereastră de browser diferită, deși
sesiunea va exista în continuare dacă o pagină web este accesată prin fereastra browserului
original. Browserele diferă în ceea ce privește modul în care gestionează această situație.
• Dacă sesiunea expiră din cauza inactivității. Mai multe informații despre expirarea
sesiunii pot fi găsite în secțiunea de configurare.

273
CAPITOLUL MANAGEMENTUL DE STAT
8

• Dacă codul paginii dvs. web încheie sesiunea apelând metoda Session.Abandon().
În primele două cazuri, sesiunea rămâne de fapt în memoria serverului web, deoarece ASP.NET nu are idee că clientul
a închis browserul sau a schimbat ferestrele. Sesiunea va rămâne în memorie, rămânând inaccesibilă, până când va
expira în cele din urmă.
Tabelul 8-1 descrie metodele și proprietățile cheie ale clasei HttpSessionState.

Tabelul 8-1. Membrii HttpSessionState

Membru Descriere

Număra Furnizează numărul de elemente din colecția curentă de sesiuni.

IsCookieless Identifică dacă sesiunea este urmărită cu un modul cookie sau cu adrese URL modificate.

IsNewSession Identifică dacă sesiunea a fost creată numai pentru solicitarea curentă. Dacă nicio
informație nu este în starea sesiunii, nu ASP.NET deranja să urmăriți sesiunea sau să
creați un cookie de sesiune. În schimb, sesiunea va fi recreată cu fiecare solicitare.

Chei Obține o colecție a tuturor cheilor de sesiune care sunt utilizate în prezent pentru a stoca
elemente în colecția de stări a sesiunii.

Mod Furnizează o valoare enumerată care explică modul în care ASP.NET stochează
informațiile despre starea sesiunii. Acest mod de stocare este determinat pe baza
setărilor web.config discutate în secțiunea "Configurare stare sesiune" din acest capitol.

ID-ul sesiunii Furnizează un șir cu identificatorul unic de sesiune pentru clientul curent.

Timeout Determină numărul de minute care vor trece înainte ca sesiunea curentă să fie
abandonată, cu condiția să nu se mai primească solicitări de la client. Această valoare
poate fi modificată programatic, permițându-vă să faceți colecția de sesiuni mai lungă
atunci când este necesar.

Abandon() Anulează imediat sesiunea curentă și eliberează toată memoria pe care a ocupat-o.
Aceasta este o tehnică utilă într-o pagină de deconectare pentru a vă asigura că
memoria serverului este recuperată cât mai repede posibil.

Clar() Elimină toate elementele sesiunii, dar nu modifică identificatorul curent al sesiunii.

Un exemplu de stare a sesiunii


Următorul exemplu utilizează starea sesiunii pentru a stoca mai multe obiecte de date Mobilier. Obiectul de date
combină câteva variabile înrudite și utilizează un constructor special, astfel încât să poată fi creat și inițializat într-o
singură linie ușoară. În loc să utilizeze proceduri complete de proprietate, clasa ia o comandă rapidă și folosește
variabile pentru membrii publici, astfel încât listarea codului să rămână scurtă și concisă. (Dacă faceți referire la
codul complet în exemplele descărcabile, veți vedea că utilizează proceduri de proprietate.)
Mobilier de clasă publică
{

274
CAPITOLUL MANAGEMENTUL DE STAT
8

șir public Nume; șir public


Descriere; costul zecimal public;

mobilier public (numele șirului, descrierea șirului, costul


zecimal) { Nume = nume; Descriere = descriere; Cost =
cost; }

}
Trei obiecte de mobilier sunt create prima dată când pagina este încărcată și sunt stocate în starea
sesiune. Utilizatorul poate alege apoi dintr-o listă de nume de piese de mobilier. Când se face o selecție,
obiectul corespunzător va fi recuperat și informațiile sale vor fi afișate, așa cum se arată în figura 8-9.

Figura 8-9. Un exemplu de stare de sesiune cu obiecte de date

clasă publică parțială SessionStateExemplu : System.Web.UI.Page


{
protejat void Page_Load(Object sender, EventArgs e) { if (!this. IsPostBack) { // Creați obiecte de mobilier.

Piesă de mobilier1 = mobilier nou ("Econo Sofa", "Acme


Inc.", 74.99M); Piesă de mobilier2 = mobilier nou ("Masa

275
CAPITOLUL MANAGEMENTUL DE STAT
8

pionierului", } "Unitatea de patrimoniu", 866,75M); Piesă de


mobilier3 = mobilier nou ("Dulap retro", "Sixties Ltd.", 300.11M);

Adăugați obiecte la starea sesiunii. Sesiune["Mobilier1"] = bucată1; Sesiune["Mobilier2"] = bucată2;


Sesiune["Mobilier3"] = bucată3;

Adăugați rânduri la controlul listă.


lstItems.Items.Add(bucată1. Nume);
lstItems.Items.Add(bucată2. Nume);
lstItems.Items.Add(bucată3. Nume); }

Afișați câteva informații de bază despre sesiune. Acest lucru este util
pentru testarea setărilor de configurare. lblSession.Text = "ID
sesiune: " + Session.SessionID; lblSession.Text += "<br />Număr de
obiecte: "; lblSession.Text += Session.Count.ToString();
lblSession.Text += "<br />Mode: " + Session.Mode.ToString();
lblSession.Text += "<br />este fără cookie: "; lblSession.Text +=
Session.IsCookieless.ToString(); lblSession.Text += "<br />este nou:
"; lblSession.Text += Session.IsNewSession.ToString();
lblSession.Text += "<br />Timeout (minute): "; lblSession.Text +=
Session.Timeout.ToString();

protejat void cmdMoreInfo_Click(Object sender, EventArgs e) {

} dacă (lstItems.SelectedIndex == -1) { lblRecord.Text = "Niciun


element selectat."; } else { // Construiți numele corect al cheii pe
baza indexului. string key = "Mobilier" + (lstItems.SelectedIndex
+ 1). ToString();

Regăsiți obiectul Mobilier din starea sesiunii. Piesă de mobilier = (Mobilier)Sesiune[cheie];

Afișați informațiile pentru acest obiect. lblRecord.Text = "Nume: " + bucată.


Nume; lblRecord.Text += "<br />Producător: "; lblRecord.Text + = bucată.
Descriere; lblRecord.Text + = "<br />Cost: " + bucată. Cost.ToString("c"); }

276
CAPITOLUL MANAGEMENTUL DE STAT
8

Este, de asemenea, o practică bună să adăugați câteva funcții prietenoase pentru sesiune în aplicația dvs.
De exemplu, puteți adăuga un buton de deconectare la pagină care anulează automat o sesiune utilizând
metoda Session.Abandon(). În acest fel, utilizatorul va fi încurajat să încheie sesiunea, mai degrabă decât să
închidă fereastra browserului, iar memoria serverului va fi recuperată mai repede.

STAREA SESIUNII DEVINE MAI SCALABILĂ

Atunci când dezvoltatorii web trebuie să stocheze o cantitate mare de informații despre stat, se confruntă cu o problemă
confuză. Acestea pot utiliza starea sesiunii și pot asigura performanțe excelente pentru un set mic de utilizatori, dar riscă
o scalabilitate slabă pentru numere mari. Alternativ, aceștia pot utiliza o bază de date pentru a stoca informații temporare
despre sesiune. Acest lucru le permite să stocheze o cantitate mare de informații despre sesiune pentru o perioadă lungă
de timp (potențial săptămâni sau luni în loc de doar câteva minute). Cu toate acestea, încetinește și performanța,
deoarece baza de date trebuie interogată pentru aproape fiecare solicitare de pagină.

Compromisul implică memorarea în cache. Abordarea de bază este de a crea o înregistrare temporară a bazei de date cu
informații despre sesiune și de a stoca ID-ul său unic în starea sesiunii. Acest lucru asigură faptul că informațiile sesiunii
din memorie sunt întotdeauna minime, dar codul paginii web poate găsi cu ușurință înregistrarea corespunzătoare a
sesiunii. Pentru a reduce numărul de interogări ale bazei de date, veți adăuga, de asemenea, informațiile despre sesiune
în memoria cache (indexate sub identificatorul sesiunii). La solicitările ulterioare, codul poate verifica mai întâi informațiile
sesiunii în memoria cache. Dacă informațiile nu mai sunt în memoria cache, codul le poate prelua din baza de date ca
ultimă soluție. Acest proces devine și mai transparent dacă creați o componentă particularizată care furnizează informațiile
despre sesiune și efectuează căutarea necesară în memoria cache pentru dvs.

Pentru mai multe informații, citiți despre componentele particularizate în capitolul 22 și memorarea în cache în
capitolul 23.

Configurarea stării sesiunii


Configurați starea sesiunii prin fișierul web.config pentru aplicația curentă (care se găsește în același director virtual ca
fișierele de pagină web .aspx). Fișierul de configurare vă permite să setați opțiuni avansate, cum ar fi expirarea și modul
de stare a sesiunii.
Următoarea listă afișează cele mai importante opțiuni pe care le puteți seta pentru elementul
<sessionState>. Reține că nu vei folosi toate aceste detalii în același timp. Unele setări se aplică numai
anumitor moduri de stare a sesiunii, după cum veți vedea în curând.
<?xml version="1.0" ?> <configuration>
<system.web> <!-- Alte setări omise. -->

<sessionState cookieless="UseCookies" cookieName="ASP.NET_SessionId"


regenerateExpiredSessionId="false" timeout="20" mode="InProc"
stateConnectionString="tcpip=127.0.0.1:42424" stateNetworkTimeout="10"
sqlConnectionString="data source=127.0.0.1; Securitate integrată = SSPI" / >
sqlCommandTimeout = "30" allowCustomSqlDatabase = "false" customProvider=""
compressionEnabled="false"

277
CAPITOLUL MANAGEMENTUL DE STAT
8

</system.web> </configuration>

Următoarele secțiuni descriu cele mai importante setări ale stării sesiunii.

Fără cookie-uri
Puteți seta setarea fără module cookie la una dintre valorile definite de enumerarea HttpCookieMode, așa
cum este descris în tabelul 8-2.

Tabelul 8-2. Valorile HttpCookieMode

Valoare Descriere
Descărcați de la Wow! eBook <www.wowebook.com>

UtilizareCookie-uri Cookie-urile sunt utilizate întotdeauna, chiar dacă browserul sau dispozitivul nu acceptă cookie-uri sau
sunt dezactivate. Aceasta este valoarea implicită. Dacă dispozitivul nu acceptă cookie-uri,
Informațiile despre sesiune se vor pierde din cauza solicitărilor ulterioare, deoarece fiecare solicitare
va primi un nou act de identitate.

UseUri Cookie-urile nu sunt utilizate niciodată, indiferent de capacitățile browserului sau dispozitivului.
În schimb, ID-ul sesiunii este stocat în adresa URL.

UseDeviceProfile ASP.NET alege dacă să utilizeze sesiuni fără cookie-uri examinând


Obiectul BrowserCapabilities. Dezavantajul este că acest obiect indică ce
Dispozitivul ar trebui să accepte - nu ia în considerare faptul că utilizatorul poate avea
Cookie-uri dezactivate într-un browser care le acceptă.

Detectare automată ASP.NET încearcă să determine dacă browserul acceptă cookie-uri prin:
încercarea de a seta și recupera un cookie (o tehnică utilizată în mod obișnuit pe Web).
Această tehnică poate determina corect dacă un browser acceptă cookie-uri, dar
le dezactivează, caz în care se utilizează modul fără cookie-uri.

Iată un exemplu care forțează modul fără cookie-uri (care este util pentru testare):

<sessionState cookieless="UseUri" ... />


În modul fără cookie-uri, ID-ul sesiunii va fi inserat automat în adresa URL. Când ASP.NET primește o
solicitare, va elimina ID-ul, va prelua colecția de sesiuni și va redirecționa solicitarea către directorul
corespunzător. Figura 8-10 prezintă o adresă URL modificată.

278
CAPITOLUL MANAGEMENTUL DE STAT
8

Figura 8-10. O adresă URL modificată cu ID-ul sesiunii

Deoarece ID-ul sesiunii este inserat în adresa URL curentă, linkurile relative obțin automat și ID-ul sesiunii.
Cu alte cuvinte, dacă utilizatorul este staționat în prezent pe Page1.aspx și dă clic pe un link relativ către
Page2.aspx, linkul relativ include ID-ul sesiunii curente ca parte a adresei URL. Același lucru este valabil
și dacă apelați Response.Redirect() cu o adresă URL relativă, așa cum se arată aici:
Răspuns.Redirecționare("Page2.aspx");
Figura 8-11 prezintă un exemplu de site web (inclus cu mostrele online din directorul CookielessSessions)
care testează sesiunile fără cookie-uri. Acesta conține două pagini și utilizează modul fără cookie-uri. Prima
pagină (Cookieless1.aspx) conține un control HyperLink și două butoane, toate ducându-vă la o a doua
pagină (Cookieless2.aspx). Trucul este că aceste controale au moduri diferite de a-și efectua navigarea. Doar
doi dintre ei lucrează cu sesiune fără cookie-uri - al treilea pierde sesiunea curentă.

Figura 8-11. Trei teste de sesiuni fără cookie-uri

Controlul HyperLink navighează la pagina specificată în proprietatea sa NavigateUrl, care este setată la calea
relativă Cookieless2.aspx. Dacă faceți clic pe acest link, ID-ul sesiunii este păstrat în adresa URL și noul

279
CAPITOLUL 8 MANAGEMENTUL STATULUI

Pagina poate prelua informațiile sesiunii. Acest lucru demonstrează că sesiunile fără cookie-uri funcționează cu
linkuri relative.
Cele două butoane de pe această pagină utilizează redirecționarea programatică apelând metoda
Response.Redirect(). Primul buton utilizează calea relativă Cookieless2.aspx, la fel ca controlul
HyperLink. Această abordare funcționează cu starea sesiunii fără cookie-uri și păstrează adresa URL
modificată, fără a fi necesari pași suplimentari.
protejat void cmdLink_Click(Object sender, EventArgs e) {
Response.Redirect("Cookieless2.aspx"); }

Singura limitare reală a stării fără cookie-uri este că nu puteți utiliza link-uri absolute (link-uri care includ
URL-ul complet, începând cu http://). Al doilea buton folosește o legătură absolută pentru a demonstra
această problemă. Deoarece ASP.NET nu se poate insera ID-ul sesiunii în URL, sesiunea se pierde.
vid protejat cmdLinkAbsolute_Click(Object sender, EventArgs e) {
Response.Redirect("http://localhost:56371/CookielessSessions/Cookieless2.aspx"); }

Acum, pagina țintă (Figura 8-12) verifică informațiile despre sesiune, dar nu le găsește.

Figura 8-12. O sesiune pierdută

Scrierea codului pentru a demonstra această problemă într-un mediu de testare este un pic dificilă. Problema este că
serverul web integrat al Visual Studio alege un port diferit pentru site-ul dvs. web de fiecare dată când îl porniți. Prin
urmare, va trebui să editați codul de fiecare dată când deschideți Visual Studio, astfel încât URL-ul să utilizeze numărul
de port corect (cum ar fi 56371 în exemplul anterior).
Există o altă soluție. Puteți utiliza un cod viclean care obține adresa URL curentă din pagină și modifică
doar ultima parte a acesteia (schimbând numele paginii din Cookieless1.aspx în Cookieless2.aspx). Iată
cum:

280
CAPITOLUL MANAGEMENTUL DE STAT
8

Creați o adresă URL nouă pe baza adresei URL actuale (dar care se
termină cu // pagina Cookieless2.aspx în loc de Cookieless1.aspx. șir
url = "http://" + Request.Url.Authority + Request.Url.Segments[0] +
Request.Url.Segments[1] + "Cookieless2.aspx";
Response.Redirect(url);

Desigur, dacă implementați site-ul dvs. web într-un director virtual real găzduit de IIS, nu veți mai utiliza un
număr de port ales aleatoriu și nu veți experimenta această ciudățenie. Capitolul 26 conține mai multe
despre directoarele virtuale și implementarea site-urilor web.

GESTIONAREA ID-URILOR DE SESIUNE EXPIRATE

În mod implicit, ASP.NET vă permite să reutilizați un identificator de sesiune. De exemplu, dacă efectuați o
solicitare și șirul de interogare conține o sesiune expirată, ASP.NET creează o sesiune nouă și utilizează acel ID de
sesiune. Problema este că un ID de sesiune poate apărea din greșeală într-un loc public, cum ar fi într-o pagină cu
rezultate dintr-un motor de căutare. Acest lucru ar putea duce la mai mulți utilizatori care accesează serverul cu
același identificator de sesiune și apoi se alătură aceleiași sesiuni cu aceleași date partajate.

Pentru a evita acest risc potențial de securitate, ar trebui să includeți atributul opțional
regenerateExpiredSessionId și să îl setați la true ori de câte ori utilizați sesiuni fără cookie-uri. În acest
fel, un nou ID de sesiune va fi emis dacă un utilizator se conectează cu un ID de sesiune expirat.
Singurul dezavantaj este că acest proces forțează, de asemenea, pagina curentă să piardă toate
datele de stare și formular de vizualizare, deoarece ASP.NET efectuează o redirecționare pentru a vă
asigura că browserul are un nou identificator de sesiune.

Timeout
O altă setare importantă a stării sesiunii în fișierul web.config este expirarea. Aceasta specifică numărul de minute
pe care ASP.NET le va aștepta, fără a primi o solicitare, înainte de a abandona sesiunea. Această setare reprezintă
unul dintre cele mai importante compromisuri ale stării sesiunii. O diferență de minute poate avea un efect dramatic
asupra încărcării serverului și a performanței aplicației. În mod ideal, veți alege un interval de timp suficient de scurt
pentru a permite serverului să recupereze memoria valoroasă după ce un client nu mai utilizează aplicația, dar
suficient de lung pentru a permite unui client să întrerupă și să continue o sesiune fără a o pierde.
De asemenea, puteți modifica programatic expirarea sesiunii în cod. De exemplu, dacă știți că o sesiune
conține o cantitate neobișnuit de mare de informații, poate fi necesar să limitați perioada de timp în care
sesiunea poate fi stocată. Apoi avertizați utilizatorul și modificați proprietatea Expirare. Iată un exemplu de
linie de cod care modifică durata de expirare la 10 minute:
Session.Timeout = 10;

Mod
Setările de stare a sesiunii rămase vă permit să configurați ASP.NET să utilizați diferite servicii de stare a
sesiunii, în funcție de modul pe care îl alegeți. Următoarele câteva secțiuni descriu modurile din care
puteți alege.

281
CAPITOLUL MANAGEMENTUL DE STAT
8

• Notă Schimbarea modului este o activitate de configurare complexă. Pentru a face acest lucru
cu succes, trebuie să înțelegeți mediul în care va fi implementată aplicația dvs. web. De exemplu,
dacă implementați aplicația la o gazdă web terță parte, trebuie să știți dacă gazda acceptă alte
moduri înainte de a încerca să le utilizați. Dacă implementați aplicația pe un server de rețea din
propria organizație, trebuie să faceți echipă cu administratorul de rețea prietenos din vecinătate.

InProc
InProc este modul implicit și are cel mai mult sens pentru site-urile mici. Acesta instruiește ca informațiile să fie stocate în
același proces ca și firele de lucru ASP.NET, ceea ce oferă cea mai bună performanță, dar cea mai mică durabilitate.
Dacă reporniți serverul, informațiile despre stare se vor pierde. (În ASP.NET, domeniile de aplicații pot fi repornite din mai
multe motive, inclusiv modificări de configurație și pagini actualizate, precum și atunci când sunt îndeplinite anumite
praguri. Dacă descoperiți că pierdeți sesiuni înainte de limita de expirare, vă recomandăm să experimentați cu un mod
mai durabil.)
Modul InProc nu va funcționa dacă utilizați o fermă web, care este un aranjament de echilibrare a încărcării
care utilizează mai multe servere web pentru a vă rula site-ul web. În această situație, diferite servere web
pot gestiona solicitări consecutive de la același utilizator. Dacă serverele web utilizează modul InProc, fiecare
va avea propria colecție privată de date de sesiune. Rezultatul final este că utilizatorii își vor pierde în mod
neașteptat sesiunile atunci când călătoresc pe o pagină nouă sau o postează înapoi pe cea curentă.

Notă utilizați modurile StateServer și SQLServer, obiectele pe care le stocați în starea sesiune
• Când
trebuie să fie serializabile. În caz contrar, ASP.NET nu va putea transmite obiectul serviciului de
stat sau nu îl va putea stoca în baza de date. Mai devreme în acest capitol, ați învățat cum să
creați o clasă de clienți serializabilă pentru stocarea în starea de vizualizare.

Off
Această setare dezactivează gestionarea stării sesiunii pentru fiecare pagină din aplicație. Acest lucru
poate oferi o ușoară îmbunătățire a performanței pentru site-urile web care nu utilizează starea sesiunii.

Server de stare
Cu această setare, ASP.NET va utiliza un serviciu Windows separat pentru gestionarea stării. Acest serviciu rulează pe
același server web, dar este în afara procesului principal de ASP.NET, ceea ce îi conferă un nivel de bază de protecție
dacă procesul de ASP.NET trebuie repornit. Costul este întârzierea crescută impusă atunci când informațiile de stat
sunt transferate între două procese. Dacă accesați și modificați frecvent informațiile de stare, acest lucru poate duce la
o încetinire destul de nedorită.
Când utilizați setarea StateServer, trebuie să specificați o valoare pentru setarea stateConnectionString.
Acest șir identifică adresa TCP/IP a computerului care execută serviciul StateServer și numărul său de port
(care este definit de ASP.NET și de obicei nu trebuie modificat). Acest lucru vă permite să găzduiți
StateServer pe un alt computer. Dacă nu modificați această setare, se va utiliza serverul local (setat ca
adresă 127.0.0.1).

282
CAPITOLUL MANAGEMENTUL DE STAT
8

Desigur, înainte ca aplicația dvs. să poată utiliza serviciul, trebuie să îl porniți. Cel mai simplu mod de a face
acest lucru este să utilizați Microsoft Management Console (MMC). Iată cum:
1. Selectați Start Control Panel.
2. Deschideți grupul Instrumente administrative, apoi alegeți Computer
Management.
3. În instrumentul Computer Management, accesați Servicii și aplicații
Nod de servicii. →
4. Găsiți serviciul numit Serviciul ASP.NET Stat în listă, așa cum se arată în
figura 8-13.

Figura 8-13. Serviciul de stat ASP.NET

5. După ce găsiți serviciul în listă, îl puteți porni și opri manual făcând clic dreapta
pe el. În general, veți dori să configurați Windows pentru a porni automat
serviciul. Faceți clic dreapta pe el, selectați Proprietăți și modificați Tipul de
pornire, setându-l la Automat, așa cum se arată în Figura 8-14.

283
CAPITOLUL MANAGEMENTUL DE STAT
8

Figura 8-14. Modificarea tipului de pornire

Notă
Când utilizați modul StateServer, puteți seta, de asemenea, un atribut opțional stateNetworkTimeout care specifică numărul maxim de secunde pentru a aștepta ca serviciul să răspundă înainte de a anula solicitarea. Valoarea implicită este 10 (secunde).

284
CAPITOLUL 8
MANAGEMENTUL STATULUI

SQLServer
Această setare instruiește ASP.NET să utilizați o bază de date SQL Server pentru a stoca informații despre sesiune,
așa cum sunt identificate prin atributul sqlConnectionString. Acesta este cel mai rezistent magazin de stat, dar și cel
mai lent de departe. Pentru a utiliza această metodă de gestionare a stării, va trebui să aveți un server cu SQL Server
instalat.
Când setați atributul sqlConnectionString, urmați același tip de model pe care îl utilizați cu ADO.NET acces la
date. În general, va trebui să specificați o sursă de date (adresa serverului) și un ID de utilizator și o parolă, cu
excepția cazului în care utilizați securitatea integrată SQL.
În plus, trebuie să instalați procedurile speciale stocate și bazele de date temporare de sesiune. Aceste proceduri stocate se
ocupă de stocarea și recuperarea informațiilor despre sesiune. ASP.NET include un instrument de linie de comandă care
face treaba automat pentru dvs., numit aspnet_regsql.exe. Se găsește în directorul
c:\Windows\Microsoft.NET\Framework\[Version] (unde [Version] este versiunea curentă de .NET, cum ar fi v4.0.30319). Cel
mai simplu mod de a rula aspnet_regsql.exe este să începeți prin lansarea promptului de comandă Visual Studio (deschideți
meniul Start și alegeți Programe → Visual Visual Studio 2010

Instrumente de studio Visual Studio linie de comandă). Apoi puteți introduce o comandă aspnet_regsql.exe,

indiferent în ce director vă aflați.
Puteți utiliza instrumentul aspnet_regsql.exe pentru a efectua mai multe activități legate de baza de date. Pe măsură
ce parcurgeți această carte, veți vedea cum să utilizați aspnet_regsql.exe cu funcții ASP.NET, cum ar fi calitatea de
membru (capitolul 20), profilurile (capitolul 21) și memorarea în cache (capitolul 23). Pentru a utiliza
aspnet_regsql.exe pentru a crea o bază de date de stocare a sesiunilor, furnizați parametrul –ssadd. În plus, utilizați
parametrul –S pentru a indica numele serverului bazei de date și parametrul –E pentru a vă conecta la baza de
date utilizând contul de utilizator Windows conectat în prezent.
Iată o comandă care creează baza de date de stocare a sesiunilor pe computerul curent, utilizând
numele implicit al bazei de date ASPState:
aspnet_regsql.exe -S localhost -E –ssadd
Această comandă utilizează aliasul localhost, care aspnet_regsql.exe spune să se conecteze la serverul
bazei de date de pe computerul curent.

Notă
Comanda aspnet_regsql.exe acceptă opțiuni suplimentare care vă permit să stocați informații despre sesiune într-o bază de date cu un nume diferit. Puteți afla despre aceste opțiuni consultând ajutorul Visual Studio (căutați aspnet_regsql în index) sau navigând la http://msdn2.microsoft.com/library/ms178586.aspx. Aceste informații descriu, de asemenea, pașii suplimentari pe care trebuie să îi efectuați pentru a utiliza stocarea sesiunii susținute de baza de date cu SQL Server Express.

După ce ați creat baza de date cu starea sesiunii, trebuie să ASP.NET spuneți să o utilizați modificând
secțiunea <sessionState> a fișierului web.config. Dacă utilizați o bază de date numită ASPState pentru a
stoca informațiile despre sesiune (care este implicit), nu este necesar să furnizați numele bazei de date. În
schimb, trebuie pur și simplu să indicați locația serverului și tipul de autentificare pe care ASP.NET trebui
să îl utilizați pentru a vă conecta la acesta, așa cum se arată aici:
<sessionState mode="SQLServer" sqlConnectionString="data
source=127.0.0.1; Securitate integrată = SSPI" ... />
CAPITOLUL MANAGEMENTUL DE STAT
8

Când utilizați modul SQLServer, puteți seta, de asemenea, un atribut opțional sqlCommandTimeout care
specifică numărul maxim de secunde pentru a aștepta ca baza de date să răspundă înainte de a anula
solicitarea. Valoarea implicită este de 30 de secunde.

Obicei
Când utilizați modul personalizat, trebuie să indicați ce furnizor de magazin de stare de sesiune să utilizați,
furnizând atributul customProvider. Atributul customProvider indică numele clasei. Clasa poate face parte din
aplicația dvs. web (caz în care codul sursă este plasat în subfolderul App_Code) sau poate fi într-un ansamblu pe
care îl utilizează aplicația dvs. web (caz în care ansamblul compilat este plasat în subfolderul Coș).
Crearea unui furnizor de stare particularizată este o activitate de nivel scăzut care trebuie gestionată cu atenție pentru a
asigura securitatea, stabilitatea și scalabilitatea. Furnizorii de stat vamal sunt, de asemenea, dincolo de domeniul de aplicare
al acestei cărți. Cu toate acestea, alți furnizori pot lansa furnizori de stare particularizați pe care doriți să îi utilizați. De exemplu,
Oracle ar putea furniza un furnizor de stare particularizat care vă permite să stocați informații despre stare într-o bază de date
Oracle.

Comprimare
Când setați enableCompression la true, datele sesiunii sunt comprimate înainte de a fi eliminate din proces. Setarea
enableCompression are un efect numai atunci când utilizați stocarea stării sesiunii în afara procesului, deoarece numai
în această situație datele sunt serializate.
Pentru a comprima și decomprima datele sesiunii, serverul web trebuie să efectueze lucrări suplimentare. Cu
toate acestea, aceasta nu este de obicei o problemă, deoarece compresia este utilizată în scenarii în care
serverele web au mult timp la CPU, dar sunt limitate de alți factori. Există două scenarii cheie în care
compresia stării sesiunii are sens:
Când stocați cantități uriașe de date despre starea sesiunii în memorie: Memoria serverului web este o resursă
prețioasă. În mod ideal, starea sesiunii este utilizată pentru bucăți relativ mici de informații, în timp ce o bază de date
se ocupă de stocarea pe termen lung a unor cantități mai mari de date. Dar dacă nu este cazul și dacă serverul de
stare în afara procesului consumă cantități uriașe de memorie, compresia este o soluție potențială.
Când stocați date despre starea sesiunii pe un alt computer: În unele aplicații web la scară largă, starea
sesiunii este stocată în afara procesului (de obicei în SQL Server) și pe un computer separat. Drept
urmare, ASP.NET trebuie să transmită informațiile sesiunii înainte și înapoi printr-o conexiune de rețea.
În mod clar, acest design reduce performanța față de vitezele pe care le veți vedea atunci când starea
sesiunii este stocată pe computerul serverului web. Cu toate acestea, este încă cel mai bun compromis
pentru unele aplicații web puternic traficate, cu nevoi uriașe de stocare a stării sesiunii.
Cantitatea reală de compresie variază foarte mult în funcție de tipul de date, dar în testarea Microsoft
clienții au obținut reduceri de dimensiune de 30% până la 60%, ceea ce este suficient pentru a
îmbunătăți performanța în aceste scenarii specializate.

Starea cererii
Starea aplicației vă permite să stocați obiecte globale care pot fi accesate de orice client. Starea aplicației se bazează
pe clasa System.Web.HttpApplicationState, care este furnizată în toate paginile web prin obiectul aplicației încorporat.

Starea aplicației este similară cu starea sesiunii. Suportă același tip de obiecte, păstrează informații pe server și

286
CAPITOLUL MANAGEMENTUL DE STAT
8

utilizează aceeași sintaxă bazată pe dicționar. Un exemplu comun cu starea aplicației este un contor
global care urmărește de câte ori a fost efectuată o operațiune de către toți clienții aplicației web.
De exemplu, puteți crea o rutină de tratare a evenimentelor global.asax care urmărește câte sesiuni au fost
create sau câte solicitări au fost primite în aplicație. Sau puteți utiliza o logică similară în rutina de tratare a
evenimentelor Page.Load pentru a urmări de câte ori o anumită pagină a fost solicitată de diverși clienți. Iată
un exemplu al acestuia din urmă:
vid protejat Page_Load(Object sender, EventArgs e) { // Regăsiți contravaloarea curentă.

Număr int = 0; dacă (Application["HitCounterForOrderPage"] !=


null) { count = (int)Application["HitCounterForOrderPage"]; }

Incrementați contorul.
număr++;

Stocați contravaloarea curentă.


Application["HitCounterForOrderPage"] = număr;
lblCounter.Text = număr. ToString(); }

Încă o dată, elementele de stare a aplicației sunt stocate ca obiecte, deci trebuie să le aruncați atunci când le recuperați
din colecție. Elementele în starea aplicației nu expiră niciodată. Acestea durează până când aplicația sau serverul este
repornit sau domeniul aplicației se reîmprospătează singur (din cauza setărilor automate de reciclare a proceselor sau a
unei actualizări a uneia dintre paginile sau componentele din aplicație). Starea aplicației nu este adesea utilizată,
deoarece este în general ineficientă. În exemplul anterior, contorul nu ar ține probabil o numărătoare exactă, în special în
perioadele de trafic intens. De exemplu, dacă doi clienți au solicitat pagina în același timp, ați putea avea o secvență de
evenimente ca aceasta:
1. Utilizatorul A recuperează numărul curent (432).
2. Utilizatorul B recuperează numărul curent (432).
3. Utilizatorul A setează numărul curent la 433.
4. Utilizatorul B setează numărul curent la 433.
Cu alte cuvinte, o cerere nu este contorizată, deoarece doi clienți accesează contorul în același timp.
Pentru a preveni această problemă, trebuie să utilizați metodele Lock() și Unlock(), care permit în mod
explicit unui singur client să acceseze colecția de stări a aplicației la un moment dat.
protejat void Page_Load(Object sender, EventArgs e) {

Obțineți acces exclusiv. Application.Lock();

Număr int = 0; dacă (Application["HitCounterForOrderPage"] !=


null) { count = (int)Application["HitCounterForOrderPage"]; }
număr++; Application["HitCounterForOrderPage"] = număr;

287
CAPITOLUL MANAGEMENTUL DE STAT
8

Eliberați accesul exclusiv. Aplicație.Deblocare();

lblCounter.Text = număr. ToString();


}

Din păcate, toți ceilalți clienți care solicită pagina vor fi blocați până la lansarea colecției de aplicații. Acest
lucru poate reduce drastic performanța. În general, valorile modificate frecvent sunt candidați slabi pentru
starea aplicației. De fapt, starea aplicației este rar utilizată în lumea .NET, deoarece cele două utilizări cele
mai frecvente au fost înlocuite cu metode mai ușoare și mai eficiente:
• În trecut, starea aplicației era utilizată pentru a stoca constante la nivel de aplicație, cum
ar fi un șir de conexiune la baza de date. După cum ați văzut în capitolul 5, acest tip de
constantă poate fi stocat în fișierul web.config, care este în general mai flexibil, deoarece îl
puteți schimba cu ușurință fără a fi nevoie să căutați prin codul paginii web sau să vă
recompilați aplicația.
• Starea aplicației poate fi, de asemenea, utilizată pentru a stoca informații utilizate
Descărcați de la Wow! eBook <www.wowebook.com>

frecvent, a căror creare necesită mult timp, cum ar fi un catalog complet de produse
care necesită o căutare în baza de date. Cu toate acestea, utilizarea stării aplicației
pentru a stoca acest tip de informații ridică tot felul de probleme cu privire la modul de
verificare a validității datelor și la modul de înlocuire a acestora atunci când este
necesar. De asemenea, poate împiedica performanța dacă catalogul de produse este
prea mare. Capitolul 23 introduce o abordare similară, dar mult mai sensibilă –
stocarea informațiilor utilizate frecvent în memoria cache a ASP.NET. Multe utilizări ale
stării aplicației pot fi înlocuite mai eficient cu memorarea în cache.
Sfat
Dacă decideți să utilizați starea aplicației, puteți inițializa conținutul acesteia la prima pornire a aplicației. Doar adăugați codul de inițializare la fișierul global.asax într-o metodă numită Application_OnStart(), așa cum este descris în capitolul 5.

O prezentare generală a opțiunilor de gestionare a statului


Fiecare alegere de management de stat are o durată de viață, un domeniu de aplicare, o performanță
generală și un nivel de sprijin diferit. Tabelele 8-3 și 8-4 prezintă o comparație dintr-o privire a opțiunilor de
gestionare a stării.

288
CAPITOLUL MANAGEMENTUL DE STAT
8

Tabelul 8-3. Compararea opțiunilor de gestionare a stării (partea 1)

Vizualizare stare Șir de interogare Cookie-uri personalizate

Tipuri de date Toate tipurile de date O cantitate limitată de date Date șir.
permise .NET serializabile. șir.

Locație Un câmp ascuns în Șirul URL al browserului. Computerul clientului (în


de pagina web curentă. memorie sau într-un fișier
stocare text mic, în funcție de
setările sale de viață).

Generație Păstrat permanent pentru Pierdut când utilizatorul intră Set de programator.
postback-uri pe o singură o nouă adresă URL sau Poate fi folosit în mai
pagină. închide browserul. Cu toate multe pagini și poate
acestea, acest lucru poate
persista între vizite.
fi stocat într-un marcaj.

Aplicare Limitat la pagina Limitat la pagina țintă. Întreaga aplicație


curentă. ASP.NET.

Securitate Inviolabil în mod implicit, Clar vizibil și ușor de Nesigur și poate fi


dar ușor de citit. Puteți modificat pentru utilizator. modificat de utilizator.
impune criptarea utilizând
proprietatea
ViewStateEncryptionMod e
din directiva Pagină.

Implicații Lent dacă este stocată o Niciuna, deoarece Niciuna, deoarece


asupra cantitate mare de cantitatea de date este cantitatea de date este
performanței informații, dar nu va afecta banală. banală.
performanța serverului.

Utilizare tipică Setări specifice paginii. Trimiterea unui ID de produs de la Preferințe de


de la o pagină de catalog personalizare pentru un
la o pagină de detalii. site web.

289
CAPITOLUL MANAGEMENTUL DE STAT
8

Tabelul 8-4. Compararea opțiunilor de gestionare a stării (partea 2)

Starea sesiunii Starea cererii

Tipuri de date Toate tipurile de date .NET pentru modul Toate tipurile de date .NET.
permise implicit de stocare în proces. Toate tipurile
de date .NET serializabile dacă utilizați un
mod de stocare în afara procesului.

Locație Memorie server, serviciu de stare sau Memorie server.


de SQL Server, în funcție de modul pe
stocare care îl alegeți.
Generație Expiră după o perioadă predefinită (de Durata de viață a aplicației (de obicei,
obicei 20 de minute, dar poate fi până când serverul este repornit).
modificată global sau programatic).
Aplicare Întreaga aplicație ASP.NET. Întreaga aplicație ASP.NET. Spre deosebire
de alte metode, datele aplicațiilor sunt
globale pentru toți utilizatorii.

Securitate Foarte sigur, deoarece datele nu Foarte sigur, deoarece datele nu


sunt niciodată transmise clientului. sunt niciodată transmise clientului.

Implicații Lent atunci când stocați o cantitate mare de Încetiniți atunci când stocați o cantitate
asupra informații, mai ales dacă există mai mulți mare de informații, deoarece aceste date
performanței utilizatori simultan, deoarece fiecare utilizator nu vor expira niciodată și nu vor fi
va avea propria copie a datelor sesiunii. eliminate.
Utilizare tipică Depozitarea articolelor într-un coș de cumpărături. Stocarea oricărui tip de date globale.

Rețineți ASP.NET are un alt tip mai specializat de management de stat numit profiluri. Profilurile vă permit să stocați și să preluați informații specifice utilizatorului dintr-o bază de date. Singura captură este că trebuie să autentificați utilizatorul pentru a obține informațiile corecte. Veți afla despre profiluri în capitolul 21.

Ultimul cuvânt
Managementul de stat este arta păstrării informațiilor între cereri. De obicei, aceste informații sunt specifice
utilizatorului (cum ar fi o listă de articole dintr-un coș de cumpărături, un nume de utilizator sau un nivel de
acces), dar uneori sunt globale pentru întreaga aplicație (cum ar fi statisticile de utilizare care urmăresc
activitatea site-ului). Deoarece ASP.NET utilizează o arhitectură deconectată, trebuie să stocați și să preluați
în mod explicit informații de stare cu fiecare solicitare. Abordarea pe care o alegeți pentru a stoca aceste date
poate afecta dramatic performanța, scalabilitatea și securitatea aplicației dvs. Nu uitați să consultați tabelul
8-3 și tabelul 8-4 pentru a vă ajuta să evaluați diferite tipuri de management de stat și să determinați ce este
mai bine pentru nevoile dvs.

290
PART3

• ■■

Crearea unor formulare web mai bune


CAPITOLUL9

• ■■

Validare

Acest capitol analizează unele dintre cele mai utile controale care sunt incluse în ASP.NET: controalele de validare.
Aceste controale preiau o sarcină complicată și consumatoare de timp, care necesita mult timp – verificarea erorilor
introduse și de raportare de către utilizatori – și o automatizează cu o colecție elegantă și ușor de utilizat de validatori.
Fiecare validator are propria logică încorporată. Unii verifică datele lipsă, alții verifică dacă numerele se încadrează
într-un interval predefinit și așa mai departe. În multe cazuri, controalele de validare vă permit să verificați datele
introduse de utilizator fără a scrie o linie de cod.
În acest capitol, veți învăța cum să utilizați controalele de validare într-o pagină web ASP.NET și cum să
profitați la maximum de ele cu expresii regulate sofisticate, funcții de validare personalizate și multe altele. Și,
ca de obicei, veți privi sub capotă pentru a vedea cum ASP.NET implementează aceste caracteristici.

Înțelegerea validării
În calitate de dezvoltator experimentat, probabil că vă dați seama că utilizatorii vor face greșeli. Ceea ce este
deosebit de descurajant este gama de posibile greșeli pe care utilizatorii le pot face. Iată câteva exemple comune:
• Utilizatorii pot ignora un câmp important și îl pot lăsa necompletat.
• Utilizatorii ar putea încerca să tasteze un șir scurt de prostii pentru a eluda o verificare
de teren necesară, creând astfel dureri de cap nesfârșite la sfârșitul dvs. De exemplu,
este posibil să rămâneți blocat cu o adresă de poștă electronică nevalidă care cauzează
probleme programului automat de poștă electronică.
• Utilizatorii ar putea face o greșeală onestă, cum ar fi introducerea unei erori de tastare,
introducerea unui caracter nenumeric într-un câmp numeric sau trimiterea unui tip greșit
de informații. Acestea ar putea chiar să introducă mai multe informații care sunt corecte
individual, dar atunci când sunt luate împreună sunt inconsecvente (de exemplu,
introducerea unui număr MasterCard după alegerea Visa ca tip de plată).
• Utilizatorii rău intenționați ar putea încerca să exploateze o slăbiciune a codului dvs.
introducând valori greșite atent structurate. De exemplu, ar putea încerca să provoace o
eroare specifică care va dezvălui informații sensibile. Un exemplu mai dramatic al acestei
tehnici este atacul de injecție SQL, în care valorile furnizate de utilizator modifică
funcționarea unei comenzi de bază de date construite dinamic. (Desigur, validarea nu este
o apărare pentru codificarea slabă. Când luați în considerare programarea bazelor de date
în capitolul 14, veți învăța cum să utilizați comenzi parametrizate, care evită cu totul
pericolul atacurilor de injecție SQL.)
O aplicație web este deosebit de susceptibilă la aceste probleme, deoarece se bazează pe controale de intrare HTML de
bază care nu au toate caracteristicile omologilor lor Windows. De exemplu, o tehnică obișnuită într-o aplicație Windows este

293
CAPITOLUL VALIDARE
9

de a gestiona evenimentul KeyPress al unei casete text, de a verifica dacă caracterul curent este valid și de a împiedica
apariția acestuia dacă nu este. Această tehnică facilitează crearea unei casete de text care acceptă numai intrare numerică.

Această strategie nu este la fel de ușoară într-o pagină web de pe server. Pentru a efectua validarea pe serverul web,
trebuie să postați pagina înapoi și pur și simplu nu este practic să postați pagina înapoi pe server de fiecare dată când
utilizatorul introduce o scrisoare. Pentru a evita acest tip de problemă, trebuie să efectuați toate validările simultan atunci
când este trimisă o pagină (care poate conține mai multe controale de intrare). Apoi, trebuie să creați interfața de
utilizator adecvată pentru a raporta greșelile. Unele site-uri web raportează doar primul câmp incorect, în timp ce altele
utilizează un tabel, o listă sau o fereastră pentru a le descrie pe toate. Până când vă perfecționați strategia de validare,
veți fi petrecut o cantitate considerabilă de efort scriind cod plictisitor.
ASP.NET își propune să vă scutească de această problemă și să vă ofere un cadru reutilizabil de controale
de validare care gestionează detaliile de validare verificând câmpurile și raportând automat erorile. Aceste
controale pot utiliza chiar JavaScript din partea clientului pentru a oferi o interfață mai dinamică și mai
receptivă, oferind în același timp validarea obișnuită pentru browserele mai vechi (adesea denumite
browsere de nivel inferior ).

Controalele de validare
ASP.NET oferă cinci controale validatoare, care sunt descrise în tabelul 9-1. Patru vizează anumite tipuri de
validare, în timp ce al cincilea vă permite să aplicați rutine de validare personalizate. De asemenea, veți
vedea un control ValidationSummary în Toolbox, care vă oferă o altă opțiune pentru afișarea unei liste de
mesaje de eroare de validare într-un singur loc. Veți afla despre ValidationSummary mai târziu în acest
capitol (consultați secțiunea "Alte opțiuni de afișare").
Tabelul 9-1. Controale validator

Clasa de control Descriere

RequiredFieldValidator Validarea reușește atâta timp cât controlul de intrare nu conține un


șir gol.

RangeValidator Validarea reușește dacă controlul de intrare conține o valoare într-un


specifice numerice, alfabetice sau interval de date.

CompareValidator Validarea reușește dacă controlul de intrare conține o valoare care se potrivește
valoarea dintr-un alt control de intrare sau o valoare fixă specificată.

RegularExpressionValidator Validarea reușește dacă valoarea dintr-un control de intrare se potrivește cu un


expresie regulată specificată.

CustomValidator Validarea este efectuată de o funcție definită de utilizator.

Fiecare control de validare poate fi legat la un singur control de intrare. În plus, puteți aplica mai multe controale de
validare aceluiași control de intrare pentru a furniza mai multe tipuri de validare.
Dacă utilizați RangeValidator, CompareValidator sau RegularExpressionValidator, validarea va reuși
automat dacă controlul de intrare este gol, deoarece nu există nicio valoare de validat. Dacă nu acesta este
comportamentul dorit, ar trebui să adăugați și un RequiredFieldValidator și să îl legați la același control de
intrare. Acest lucru asigură efectuarea a două tipuri de validare, restricționând în mod eficient valorile
necompletate.

294
CAPITOLUL VALIDARE
9

Validare pe partea de server


Puteți utiliza comenzile validatorului pentru a verifica automat o pagină atunci când utilizatorul o trimite sau manual
în codul dvs. Prima abordare este cea mai comună.
Când utilizați validarea automată, utilizatorul primește o pagină normală și începe să completeze comenzile de
intrare. Când terminați, utilizatorul face clic pe un buton pentru a trimite pagina. Fiecare buton are o
proprietate CausesValidation, care poate fi setată la true sau false. Ce se întâmplă când utilizatorul face clic
pe buton depinde de valoarea proprietății CausesValidation:
• Dacă CausesValidation este fals, ASP.NET vor ignora controalele de validare,
pagina va fi postată înapoi, iar codul de gestionare a evenimentelor va rula normal.
• Dacă CausesValidation este true (implicit), ASP.NET va valida automat pagina atunci
când utilizatorul face clic pe buton. Face acest lucru efectuând validarea pentru fiecare
control de pe pagină. Dacă vreun control nu reușește să valideze, ASP.NET va returna
pagina cu unele informații de eroare, în funcție de setările dvs. Codul de gestionare a
evenimentelor de clic poate fi executat sau nu, ceea ce înseamnă că va trebui să verificați
în mod specific la rutina de tratare a evenimentelor dacă pagina este validă.
Pe baza acestei descrieri, veți realiza că validarea are loc automat atunci când se face clic pe anumite
butoane. Nu se întâmplă atunci când pagina este postată înapoi din cauza unui eveniment de modificare (cum
ar fi alegerea unei valori noi într-o listă AutoPostBack) sau dacă utilizatorul face clic pe un buton care are
CausesValidation setat la false. Cu toate acestea, puteți valida manual unul sau mai multe controale și apoi
puteți lua o decizie în codul dvs. pe baza rezultatelor. Veți afla despre acest proces mai detaliat puțin mai
târziu (consultați secțiunea "Validare manuală").

Notă Multe alte controale asemănătoare butoanelor care pot fi utilizate pentru a remite pagina furnizează, de asemenea, proprietatea CausesValidation. Exemplele includ LinkButton, ImageButton și BulletedList. (Din punct de vedere tehnic, proprietatea CausesValidation este definită de interfața IButtonControl, pe care o implementează toate controalele asemănătoare butoanelor.)

Validare din partea clientului


În browserele moderne (inclusiv Internet Explorer, Firefox, Safari și Google Chrome), ASP.NET adaugă automat cod
JavaScript pentru validarea pe partea clientului. În acest caz, când utilizatorul face clic pe un buton CausesValidation,
aceleași mesaje de eroare vor apărea fără ca pagina să fie necesară pentru a fi trimisă și returnată de pe server. Acest
lucru crește capacitatea de reacție a paginii dvs.
Cu toate acestea, chiar dacă pagina se validează cu succes pe partea clientului, ASP.NET încă o revalidează
atunci când este primită la server. Acest lucru se datorează faptului că este ușor pentru un utilizator experimentat
să eludeze validarea din partea clientului. De exemplu, un utilizator rău intenționat poate șterge blocul codului de
validare JavaScript și poate continua să lucreze cu pagina. Prin efectuarea validării la ambele capete, ASP.NET
se asigură că aplicația dvs. poate fi cât mai receptivă posibil, rămânând în același timp sigură.

Controalele de validare
Controalele de validare se găsesc în spațiul de nume System.Web.UI.WebControls și moștenesc de la clasa
BaseValidator. Această clasă definește funcționalitatea de bază pentru un control de validare. Tabelul 9-2
descrie proprietățile sale cheie.

295
CAPITOLUL VALIDARE
9

Tabelul 9-2. Proprietățile clasei BaseValidator

Proprietate Descriere

ControlToValidate Identifică controlul pe care acest validator îl va verifica. Fiecare validator poate verifica
valoare într-un control de intrare. Cu toate acestea, este perfect rezonabil să "stivuiți"
validatori - cu alte cuvinte, atașați mai mulți validatori la un control de intrare la
Efectuați mai multe tipuri de verificare a erorilor.

ErrorMessage și Dacă validarea eșuează, controlul validatorului poate afișa un mesaj text (setat de butonul
ForeColor Proprietatea ErrorMessage). Prin schimbarea ForeColor, puteți face acest lucru
Mesajul iese în evidență cu litere roșii furioase. (În versiunile anterioare ale ASP.NET,
Controalele de validare utilizau în mod implicit litere roșii. Dar dacă creați un nou
Pentru ASP.NET 4, mesajele de validare vor folosi negru obișnuit
text, cu excepția cazului în care setați proprietatea ForeColor sau utilizați stiluri CSS în pagina dvs.)

Arăta Vă permite să configurați dacă acest mesaj de eroare va fi inserat în


pagina dinamic atunci când este necesară (dinamică) sau dacă este adecvată
spațiul va fi rezervat mesajului (Static). Dinamic este util atunci când
plasarea mai multor validatori unul lângă celălalt. În acest fel, spațiul se va extinde la
Potriviți indicatorii de eroare activi în prezent și nu veți rămâne cu niciunul
spațiu alb necuviincios. Static este util atunci când validatorul se află într-un tabel și dvs
Nu doriți ca lățimea celulei să se restrângă atunci când nu se afișează niciun mesaj.
În cele din urmă, puteți alege, de asemenea, Nici unul pentru a ascunde complet mesajul de eroare.

IsValid După efectuarea validării, aceasta returnează true sau false, în funcție de faptul dacă
a reușit sau a eșuat. În general, veți verifica starea întregii pagini
uitându-se în schimb la proprietatea IsValid pentru a afla dacă toate controalele de validare
Reuşit.

Activat Când este setat la fals, validarea automată nu va fi efectuată pentru acest control
când pagina este trimisă.

EnableClientScript Dacă este setat la true, ASP.NET va adăuga cod JavaScript și DHTML pentru a permite partea clientului
validarea pe browserele care o acceptă.

Când utilizați un control de validare, singurele proprietăți pe care trebuie să le implementați sunt
ControlToValidate și ErrorMessage. În plus, poate fi necesar să implementați proprietățile utilizate pentru
validatorul dvs. Tabelul 9-3 prezintă aceste proprietăți.

296
CAPITOLUL VALIDARE
9

Tabelul 9-3. Proprietăți specifice validatorului

Validator Control Membri adăugați

RequiredFieldValidator Nu este necesar niciunul

RangeValidator MaximumValue, MinimumValue, Type

CompareValidator ControlToCompare, Operator, Tip, ValueToCompare

RegularExpressionValidator ValidationExpression

CustomValidator ClientValidationFunction, ValidateEmptyText, eveniment ServerValidate

Mai târziu în acest capitol (în secțiunea "Un formular de client validat"), veți vedea un exemplu de
formular de client care demonstrează fiecare tip de validare.

Un exemplu simplu de validare


Pentru a înțelege cum funcționează validarea, puteți crea o pagină web simplă. Acest test utilizează un
singur control web cu buton, două controale TextBox și un control RangeValidator care validează prima
casetă text. Dacă validarea eșuează, controlul RangeValidator afișează un mesaj de eroare, deci ar trebui
să plasați acest control imediat lângă caseta de text pe care o validează. A doua casetă text nu utilizează
nicio validare. Figura 9-1 arată aspectul paginii după o încercare de validare eșuată.

Figura 9-1. Validare nereușită

297
CAPITOLUL VALIDARE
9

În plus, plasați un control Etichetă în partea de jos a formularului. Această etichetă va raporta când pagina a fost postată
înapoi și codul de gestionare a evenimentelor a fost executat. Dezactivați proprietatea EnableViewState pentru a vă
asigura că va fi ștearsă de fiecare dată când pagina este postată înapoi.
Marcajul pentru această pagină definește un control RangeValidator, setează mesajul de eroare, identifică
controlul care va fi validat și necesită un număr întreg de la 1 la 10. Aceste proprietăți sunt setate în fișierul
.aspx, dar pot fi configurate și în rutina de tratare a evenimentelor pentru evenimentul Page.Load. Butonul
are automat proprietatea CauseValidation setată la true, deoarece aceasta este valoarea implicită.
Un număr (de la 1 la 10): <asp:TextBox
id="txtValidated" runat="server" />
<asp:RangeValidator id="RangeValidator" runat="server" ErrorMessage="Acest număr nu este în interval"
ControlToValidate ="txtValidated" MaximumValue="10" MinimumValue="1" ForeColor="Red" font-bold="true" type="integer" />
<br /><br /> Nevalidat: <asp:TextBox id="txtNotValidated" runat="server" /><br /><br /> <asp:Button id="cmdOK" runat="server"
text="OK" OnClick="cmdOK_Click" /> <br /><br /> <asp: Label id="lblMessage" runat="server" EnableViewState="False" />
Descărcați de la Wow! eBook <www.wowebook.com>

În cele din urmă, iată codul care răspunde la clicul butonului:

vid protejat cmdOK_Click(Obiect expeditor, EventArgs e)


{
lblMessage.Text = "cmdOK_Click gestionar de evenimente executat.";
}
Dacă testați această pagină web într-un browser modern, veți observa un truc interesant. Când deschideți prima dată
pagina, mesajul de eroare este ascuns. Dar dacă tastați un număr nevalid (amintiți-vă, validarea va reuși pentru o valoare
goală) și apăsați tasta Tab pentru a trece la a doua casetă de text, un mesaj de eroare va apărea automat lângă controlul
ofensator. Acest lucru se datorează faptului că ASP.NET adaugă o funcție JavaScript specială care detectează când se
schimbă focalizarea. Implementarea efectivă a acestui cod JavaScript este oarecum complicată, dar ASP.NET
gestionează automat toate detaliile pentru dvs. Drept urmare, dacă încercați să faceți clic pe butonul OK cu o valoare
nevalidă în txtValidated, acțiunile dvs. vor fi ignorate, iar pagina nu va fi postată înapoi.

Nu toate browserele vor accepta validarea pe partea clientului. Pentru a vedea ce se va întâmpla într-un browser de
nivel inferior, setați proprietatea RangeValidator.EnableClientScript la false și executați din nou pagina. Acum,
mesajele de eroare nu vor apărea dinamic pe măsură ce schimbați focalizarea. Cu toate acestea, când faceți clic pe
butonul OK, pagina va fi returnată de pe server cu mesajul de eroare corespunzător afișat lângă controlul nevalid.

Problema potențială în acest scenariu este că codul de tratare a evenimentelor de clic se va executa în
continuare, chiar dacă pagina este nevalidă. Pentru a corecta această problemă și pentru a te asigura că
pagina ta se comportă la fel în browserele moderne și mai vechi, trebuie să anulezi codul evenimentului
dacă validarea nu a fost efectuată cu succes.

298
CAPITOLUL VALIDARE
9

protejat void cmdOK_Click(Object sender, EventArgs e) { //


Anulați evenimentul dacă controlul nu este valid. dacă (!
RangeValidator.IsValid) returnare;

lblMessage.Text = "cmdOK_Click gestionar de evenimente executat.";


}

Acest cod rezolvă problema curentă, dar nu este de mare ajutor dacă pagina conține mai multe controale de
validare. Din fericire, fiecare formular web oferă propria proprietate IsValid. Această proprietate va fi falsă
dacă vreun control de validare nu a reușit. Va fi adevărat dacă toate controalele de validare s-au finalizat cu
succes. Dacă validarea nu a fost efectuată (de exemplu, dacă controalele de validare sunt dezactivate sau
dacă butonul are CausesValidation setat la false), veți primi o excepție Http atunci când încercați să citiți
proprietatea IsValid.
protected void cmdOK_Click(Object sender, EventArgs e) { // Anulați
evenimentul dacă orice control de pe pagină este nevalid. dacă (!
Page.IsValid) returnare;

lblMessage.Text = "cmdOK_Click gestionar de evenimente executat."; }

Amintiți-vă, validarea pe partea clientului este doar o glazură frumoasă deasupra aplicației dvs.
Validarea pe partea de server va fi întotdeauna efectuată, asigurându-se că utilizatorii vicleni nu pot
"falsifica" paginile.

Alte opțiuni de afișare


În unele cazuri, este posibil să fi creat deja un formular proiectat cu atenție, care combină mai multe câmpuri de intrare.
Poate doriți să adăugați validare la această pagină, dar nu puteți reformata aspectul pentru a găzdui toate mesajele de
eroare pentru toate controalele de validare. În acest caz, aveți posibilitatea să salvați ceva lucru utilizând controlul
ValidationSummary.
Pentru a încerca acest lucru, setați proprietatea Afișare a controlului RangeValidator la Fără. Acest lucru
asigură că mesajul de eroare nu va fi afișat niciodată. Cu toate acestea, validarea va fi efectuată în continuare și
utilizatorul va fi în continuare împiedicat să facă clic cu succes pe butonul OK dacă există unele informații
nevalide pe pagină. Apoi, adăugați ValidationSummary într-o locație adecvată (cum ar fi partea de jos a paginii):
<asp:ValidationSummary id="Erori" runat="server" />
Când rulați pagina, nu veți vedea niciun mesaj dinamic atunci când introduceți informații nevalide și filați
într-un câmp nou. Cu toate acestea, când faceți clic pe butonul OK, ValidationSummary va apărea cu o listă a
tuturor mesajelor de eroare, așa cum se arată în Figura 9-2. În acest caz, regăsește un mesaj de eroare (din
controlul RangeValidator). Cu toate acestea, dacă ați avea o duzină de validatori, acesta ar prelua toate
mesajele lor de eroare și ar crea o listă.

299
CAPITOLUL VALIDARE
9

Figura 9-2. Rezumatul validării

Când ValidationSummary afișează lista de erori, regăsește automat valoarea proprietății ErrorMessage de la
fiecare validator. În unele cazuri, veți dori să afișați un mesaj complet în rezumat și un fel de indicator vizual
lângă controlul contravenient. De exemplu, multe site-uri web utilizează o pictogramă de eroare sau un
asterisc pentru a evidenția casetele de text cu intrare nevalidă. Puteți utiliza această tehnică cu ajutorul
proprietății Text a validatorilor. De obicei, textul este lăsat gol, iar validatorul nu afișează niciun conținut în
pagina web. Cu toate acestea, dacă setați atât Text cât și ErrorMessage, valoarea ErrorMessage va fi utilizată
pentru rezumat în timp ce valoarea Text este afișată în validator. (Desigur, va trebui să vă asigurați că nu
setați și proprietatea Afișare a validatorului la Fără, care ascunde complet validatorul - și conținutul acestuia.)
Iată un exemplu de validator care include un mesaj de eroare detaliat (care va apărea în
ValidationSummary) și un indicator asterisc (care va apărea în validator, lângă controlul care are
problema):
<asp:RangeValidator id="RangeValidator" runat="server"
text="*" errormessage="primul număr nu este în interval" ControlToValidate="txtValidated"
MaximumValue="10" minimumValue="1" type="integer" />

Dacă aveți mult text, este posibil să preferați să îl imbricați în interiorul elementului
<asp:RangeValidator>. De exemplu, puteți rescrie marcajul afișat mai devreme cu aceasta:

<asp:RangeValidator id="RangeValidator" runat="server"


ControlToValidate="txtValidated" MaximumValue="10" MinimumValue="1"
ErrorMessage="Primul număr nu este în interval" type="Întreg"><b> ***
Eroare</b></asp:RangeValidator>

300
CAPITOLUL VALIDARE
9

Aici, ASP.NET extrage automat codul HTML din interiorul elementului <asp:RangeValidator> și îl utilizează pentru
a seta proprietatea RangeValidator.Text.
Puteți obține chiar și un pic mai fantezist înlocuind asteriscul simplu cu un fragment de HTML mai
interesant. Iată un exemplu care utilizează eticheta <img> pentru a adăuga o mică imagine pictogramă
de eroare atunci când validarea nu reușește:
<asp:RangeValidator id="RangeValidator" runat="server"> <img
src="ErrorIcon.gif" alt="Error"> </asp:RangeValidator>

Figura 9-3 prezintă acest validator în acțiune.

Figura 9-3. Un rezumat de validare și un indicator de eroare

Controlul ValidationSummary oferă câteva proprietăți utile pe care le puteți utiliza pentru a regla fin afișarea erorilor.
Puteți seta proprietatea HeaderText să afișeze un titlu special în partea de sus a listei (cum ar fi Pagina dvs. conține
următoarele erori:). De asemenea, puteți schimba ForeColor și puteți alege un DisplayMode. Modurile posibile sunt
BulletList (implicit), List și SingleParagraf.
În cele din urmă, puteți alege ca rezumatul validării să fie afișat într-o casetă de dialog pop-up în loc de pagină (a
se vedea Figura 9-3). Această abordare are avantajul de a lăsa interfața cu utilizatorul a paginii neatinsă, dar obligă
utilizatorul să respingă mesajele de eroare închizând fereastra înainte de a putea modifica controalele de intrare.
Dacă utilizatorii vor trebui să se refere la aceste mesaje în timp ce repară pagina, afișarea în linie este mai bună.

Pentru a afișa rezumatul într-o casetă de dialog, setați proprietatea ShowMessageBox a ValidationSummary
la true. Rețineți că, dacă nu setați proprietatea ShowSummary la false, veți vedea atât caseta de mesaj, cât
și rezumatul din pagină (ca în Figura 9-4).

301
CAPITOLUL VALIDARE
9

Figura 9-4. Un rezumat al casetei de mesaje

Validare manuală
Opțiunea finală este să dezactivați validarea și să efectuați lucrările pe cont propriu, cu ajutorul controalelor de validare.
Acest lucru vă permite să luați în considerare alte informații sau să creați un mesaj de eroare specializat care implică
alte controale (cum ar fi imagini sau butoane).
Puteți crea validarea manuală într-unul din următoarele trei moduri:
• Utilizați propriul cod pentru a verifica valorile. În acest caz, nu veți utiliza niciunul
dintre controalele de validare ASP.NET.
• Dezactivați proprietatea EnableClientScript pentru fiecare control de validare. Acest
lucru permite trimiterea unei pagini nevalide, după care puteți decide ce să faceți cu ea
în funcție de problemele care pot exista.
• Adăugați un buton cu CausesValidation setat la false. Când faceți clic pe acest
buton, validați manual pagina apelând metoda Page.Validate(). Apoi examinați
proprietatea IsValid și decideți ce să faceți.
Următorul exemplu folosește a doua abordare. Odată ce pagina este trimisă, examinează toate controalele
de validare de pe pagină prin buclă prin colecția Page.Validators. De fiecare dată când găsește un control
care nu a fost validat cu succes, preia valoarea nevalidă din controlul de intrare și o adaugă la un șir. La
sfârșitul acestei rutine, afișează un mesaj care descrie ce valori au fost incorecte, așa cum se arată în Figura
9-5.

302
CAPITOLUL VALIDARE
9

Figura 9-5. Validare manuală

Această tehnică adaugă o caracteristică care nu ar fi disponibilă cu validarea automată, care utilizează
proprietatea ErrorMessage. În acest caz, nu este posibil să includeți valorile incorecte reale în mesaj.

Pentru a încerca acest exemplu, setați proprietatea EnableCientScript a fiecărui validator la false. Apoi,
puteți utiliza codul din această rutină de tratare a evenimentelor pentru a verifica dacă există valori nevalide.

vid protejat cmdOK_Click(Obiect expeditor, EventArgs e)


{
string errorMessage = "<b>Greșeli găsite:</b><br />";

Căutați prin controalele de validare. foreach


(BaseValidator ctrl în aceasta. Validatori)
{
dacă (!ctrl. IsValid) { errorMessage += ctrl. ErrorMessage
+ "<br />";

Găsiți controlul de intrare corespunzător și schimbați variabila


generică Control // într-o variabilă TextBox. Acest lucru permite
accesul la proprietatea Text. TextBox ctrlInput =
(TextBox)acest. FindControl(ctrl. ControlToValidate);
errorMessage += " * Problema este cu această intrare: ";
errorMessage += ctrlInput.Text + "<br />";
}
}
lblMessage.Text = errorMessage;
}

303
CAPITOLUL VALIDARE
9

Acest exemplu utilizează o tehnică avansată: metoda Page.FindControl(). Este necesar deoarece proprietatea
ControlToValidate a fiecărui validator furnizează pur și simplu un șir cu numele unui control, nu o referință la
obiectul de control real. Pentru a găsi controlul care se potrivește cu acest nume (și a-i regăsi proprietatea
Text), trebuie să utilizați metoda FindControl(). Odată ce codul a recuperat caseta de text potrivită, acesta
poate efectua alte sarcini, cum ar fi ștergerea valorii curente, modificarea unei proprietăți sau chiar
schimbarea culorii casetei de text. Rețineți că metoda FindControl() returnează o referință generică Control,
deoarece este posibil să căutați în orice tip de control. Pentru a accesa toate proprietățile controlului dvs.,
trebuie să îl proiectați la tipul corespunzător (cum ar fi TextBox în acest exemplu).

Sfat În acest exemplu, codul găsește fiecare validator și citește proprietatea ErrorMessage. Cu toate acestea, puteți seta, de asemenea, proprietatea ErrorMessage în acest moment, care vă permite să creați mesaje de eroare particularizate care încorporează informații despre valorile nevalide în textul lor.

Validarea cu expresii regulate


Unul dintre cele mai puternice controale de validare ale ASP.NET este RegularExpressionValidator, care
validează textul determinând dacă se potrivește cu un anumit model.
De exemplu, adresele de poștă electronică, numerele de telefon și numele fișierelor sunt exemple de text care are
restricții specifice. Un număr de telefon trebuie să fie un număr stabilit de cifre, o adresă de e-mail trebuie să includă
exact un caracter @ (cu text pe fiecare parte), iar un nume de fișier nu poate include anumite caractere speciale, cum ar
fi \ și ?. O modalitate de a defini modele ca acestea este cu expresii regulate.
Expresiile regulate au apărut în nenumărate alte limbi și au câștigat popularitate ca o modalitate extrem de
puternică de a lucra cu corzi. De fapt, Visual Studio permite chiar programatorilor să efectueze o operație de
căutare și înlocuire în codul lor folosind o expresie regulată (care poate reprezenta o nouă înălțime a geekdom-ului
computerului). Expresiile regulate pot fi considerate aproape o întreagă limbă proprie. Modul de stăpânire a tuturor
modurilor în care puteți utiliza expresii regulate - inclusiv potrivirea modelelor, referințele din spate și grupurile
numite - ar putea ocupa o carte întreagă (și mai multe cărți sunt dedicate doar acestui subiect). Din fericire, puteți
înțelege elementele de bază ale expresiilor regulate fără prea multă muncă.

Literale și metacaractere
Toate expresiile regulate constau din două tipuri de caractere: literale și metacaractere. Literalele nu sunt
diferite de literalele șir pe care le tastați în cod. Ele reprezintă un caracter specific definit. De exemplu,
dacă căutați șirul literal "l", veți găsi caracterul l și nimic altceva. Metacaracterele oferă adevăratul secret
pentru deblocarea întregii puteri a expresiilor regulate. Probabil că sunteți deja familiarizați cu două
metapersonaje din lumea DOS (? și *). Luați în considerare expresia de linie de comandă afișată aici:

Del*.*
Expresia *.* conține un literal (punctul) și două metacaractere (asteriscurile). Acest lucru se traduce prin
"ștergeți fiecare fișier care începe cu orice număr de caractere și se termină cu o extensie de orice număr
de caractere (sau nu are nicio extensie)". Deoarece toate fișierele din DOS au implicit extensii, acest lucru
are efectul bine documentat de a șterge totul din directorul curent. Un alt metacaracter DOS este semnul
întrebării, ceea ce înseamnă "orice caracter unic". De exemplu, următoarea declarație șterge orice fișier
numit salut care are o extensie de exact un caracter.

304
CAPITOLUL VALIDARE
9

Bună ziua.?
Limbajul de expresie regulat oferă multe metacaractere flexibile - mult mai multe decât linia de comandă
DOS. De exemplu, \s reprezintă orice caracter spațiu alb (cum ar fi un spațiu sau o filă). \d reprezintă orice
cifră. Astfel, următoarea expresie s-ar potrivi cu orice șir care a început cu numerele 333, urmat de un
singur caracter spațiu alb și oricare trei numere. Meciurile valide ar include 333, 333 și 333 945, dar nu
334, 333 sau 3334 945.
333\s\d\d\d
Un aspect care poate face expresiile regulate mai puțin lizibile este că folosesc metacaractere speciale care au mai mult
de un caracter. În exemplul anterior, \s reprezintă un singur caracter, la fel ca \d, chiar dacă ambele ocupă două
caractere în expresie.
Puteți utiliza semnul plus (+) pentru a reprezenta un caracter repetat. De exemplu, 5+7 înseamnă "una sau mai
multe apariții ale caracterului 5, urmate de un singur 7". Numărul 57 s-ar potrivi, la fel și 555557. De asemenea,
puteți utiliza paranteze pentru a grupa o subexpresie. De exemplu, (52)+7 s-ar potrivi cu orice șir care începe cu o
secvență de 52. Meciurile ar include 527, 52527, 5252527 și așa mai departe. De asemenea, puteți delimita un
interval de caractere utilizând paranteze pătrate. [a-f] s-ar potrivi cu orice caracter de la A la F (numai minuscule).
Următoarea expresie s-ar potrivi cu orice cuvânt care începe cu o literă de la a la f, conține unul sau mai multe
caractere "cuvânt" (litere) și se termină cu ing - potrivirile posibile includ actoria și dezvoltarea.

[a-f]\w+ing
Următoarea este o expresie regulată mai utilă care se poate potrivi cu orice adresă de e-mail, verificând dacă
conține simbolul @. Punctul este un metacaracter folosit pentru a indica orice caracter, cu excepția liniei noi.
Totuși, unele adrese de poștă electronică nevalide vor fi permise în continuare, inclusiv cele care conțin spații și
cele care nu includ un punct (.). Veți vedea un exemplu mai bun puțin mai târziu în exemplul formularului de client.
.+@.+

Găsirea unei expresii regulate


În mod clar, alegerea expresiei regulate perfecte poate necesita unele teste. De fapt, numeroase materiale
de referință (pe internet și pe suport de hârtie) includ expresii regulate utile pentru validarea valorilor
comune, cum ar fi codurile poștale. Pentru a experimenta, puteți utiliza pagina simplă
RegularExpressionTest inclusă în probele online, care este prezentată în Figura 9-6. Vă permite să setați o
expresie regulată care va fi utilizată pentru a valida un control. Apoi puteți introduce câteva valori eșantion și
puteți vedea dacă validatorul de expresii regulate reușește sau nu reușește.

305
CAPITOLUL VALIDARE
9

Figura 9-6. O pagină de test de expresie regulată

Codul este destul de simplu. Butonul Setare expresie atribuie o nouă expresie regulată controlului
RegularExpressionValidator (utilizând orice text pe care l-ați tastat). Butonul Validare declanșează pur și
simplu o postback, ceea ce face ca ASP.NET să efectueze automat validarea. Dacă apare un mesaj de
eroare, validarea nu a reușit. În caz contrar, are succes.
public parțial clasa RegularExpressionTest : System.Web.UI.Page
{
protejat void cmdSetExpression_Click(Object sender, EventArgs e) { TestValidator.ValidationExpression =
txtExpression.Text; lblExpression.Text = "Expresia curentă: "; } lblExpression.Text += txtExpression.Text;

Tabelul 9-4 prezintă câteva dintre blocurile fundamentale de expresie regulată. Dacă trebuie să potriviți un
caracter literal cu același nume ca un caracter special, îl precedați în general cu un caracter \. De exemplu,
\*hello\* se potrivește cu *hello* într-un șir, deoarece caracterul asterisc special (*) este precedat de o bară
oblică (\).

306
CAPITOLUL VALIDARE
9

Tabelul 9-4. Caractere expresie regulată

Caracter Descriere

* Zero sau mai multe apariții ale caracterului sau subexpresiei anterioare. De exemplu, 7*8
se potrivește cu 7778 sau doar 8.

+ Una sau mai multe apariții ale caracterului sau subexpresiei anterioare. De exemplu, 7+8
se potrivește cu 7778, dar nu cu 8.

() Grupează o subexpresie care va fi tratată ca un singur element. De exemplu, (78)+ se


potrivește cu 78 și 787878.

{m,n} Caracterul anterior (sau subexpresia) poate apărea de la m la n ori. De exemplu, A{1,3}
se potrivește cu A, AA sau AAA.

| Oricare dintre cele două meciuri. De exemplu, 8|6 se potrivește cu 8 sau 6.

[] Potrivește un caracter dintr-un interval de caractere valide. De exemplu, [A-C] se potrivește cu A, B sau
C.

[^ ] Se potrivește cu un caracter care nu se află în intervalul dat. De exemplu, [^A-B] se


potrivește cu orice caracter, cu excepția lui A și B.

. Orice caracter, cu excepția liniei noi. De exemplu, .here se potrivește unde și acolo.

\s Orice caracter spațiu alb (cum ar fi o filă sau un spațiu).

\S Orice caracter non-spațiu alb.

\d Orice caracter cifră.

\D Orice caracter care nu este o cifră.

\w Orice caracter "cuvânt" (literă, număr sau subliniere).

\W Orice caracter care nu este un caracter "cuvânt" (literă, număr sau caracter de subliniere).

Tabelul 9-5 prezintă câteva expresii regulate comune (și utile).

307
CAPITOLUL 9 VALIDAREA

Tabelul 9-5. Expresii regulate utilizate în mod obișnuit

Conținut Exprimare regulată Descriere

Adresa de e-mail* \S+@\S+\.\S+ Verificați dacă există un semn at (@) și un punct (.)
și permiteți numai caractere care nu sunt spații
albe.
Parolă \w+ Orice secvență de unul sau mai multe caractere de
cuvinte (literă, spațiu sau subliniere).

Parolă cu \w{4,10} O parolă care trebuie să aibă cel puțin patru caractere, dar
lungime specifică nu mai mult de zece caractere.

Parolă [a-zA-Z]\w{3,9} Ca și în cazul parolei cu lungime specifică, această expresie regulată


avansată va permite patru până la zece caractere totale. Răsucirea este că
primul caracter trebuie să se încadreze în intervalul a-z sau A-
Z (adică trebuie să înceapă cu o literă obișnuită
neaccentuată).

O altă parolă [a-zA-Z]\w*\d+\w* Această parolă începe cu un caracter de literă, urmat de zero
avansată sau mai multe caractere de cuvinte, una sau mai multe cifre,
apoi zero sau mai multe caractere de cuvinte. Pe scurt,
forțează o parolă să conțină unul sau mai multe numere
undeva în interiorul acesteia. Puteți utiliza un model similar
pentru a solicita două numere sau orice alt caracter special.

Câmp cu \S{4,10} Ca și exemplul cu parola, aceasta permite patru până


lungime limitată la zece caractere, dar permite caractere speciale
(asteriscuri, ampersande și așa mai departe).

Numărul de \d{3}-\d{2}-\d{4} O secvență de trei, două, apoi patru cifre, fiecare grup fiind
securitate socială separat printr-o liniuță. Puteți utiliza un model similar atunci
din SUA când solicitați un număr de telefon.
* Aveți multe modalități diferite de a valida adresele de e-mail cu expresii regulate de complexitate variabilă. Vezi
http://www.4guysfromrolla.com/webtech/validateemail.shtml pentru o discuție despre subiect și numeroase exemple.

Unele logici sunt mult mai dificil de modelat într-o expresie regulată. Un exemplu este algoritmul Luhn, care
verifică numerele cardurilor de credit dublând mai întâi fiecare a doua cifră, apoi adăugând aceste cifre
dublate împreună și, în final, împărțind suma la zece. Numărul este valid (deși nu este neapărat conectat la
un cont real) dacă nu există rest după împărțirea sumei. Pentru a utiliza algoritmul Luhn, aveți nevoie de un
control CustomValidator care rulează această logică pe valoarea furnizată. (Puteți găsi o descriere detaliată
a algoritmului Luhn la http://en.wikipedia.org/wiki/Luhn_formula.)

A formular de client validat


Pentru a reuni aceste subiecte diferite, veți vedea acum un formular web complet, care combină o varietate
de informații care ar putea fi necesare pentru a adăuga o înregistrare de utilizator (de exemplu, un
cumpărător de site-uri de comerț electronic sau un abonat la site-ul de conținut). Figura 9-7 prezintă acest
formular.

308
CAPITOLUL VALIDARE
9

Figura 9-7. Un exemplu de formular de client

Mai multe tipuri de validare au loc pe formularul clientului:


• Trei controale RequiredFieldValidator asigură că utilizatorul introduce un nume de
utilizator, o parolă și o confirmare a parolei.
• Un CompareValidator asigură că cele două versiuni ale parolei mascate se potrivesc.
• Un RegularExpressionValidator verifică dacă adresa de e-mail conține un
simbol at (@).
• Un RangeValidator asigură că vârsta este un număr de la 0 la 120.
• Un CustomValidator efectuează o validare specială pe server a unui "cod de
referință". Acest cod verifică dacă primele trei caractere alcătuiesc un număr divizibil
cu 7.
Etichetele pentru controalele validatorului sunt următoarele:

<asp:RequiredFieldValidator id="vldUserName" runat="server"


ErrorMessage="Trebuie să introduceți un nume de utilizator."
ControlToValidate="txtUserName" />
<asp:RequiredFieldValidator id="vldPassword" runat="server"
ErrorMessage="Trebuie să introduceți o parolă."
ControlToValidate="txtPassword" />
<asp:CompareValidator id="vldRetype" runat="server"

309
CAPITOLUL VALIDARE
9

ErrorMessage="Parola ta nu se potrivește." ControlToCompare="txtPassword"


ControlToValidate="txtRetype" />

<asp:RequiredFieldValidator id="vldRetypeRequired" runat="server"


ErrorMessage="Trebuie să confirmați parola."
ControlToValidate="txtRetype" />
<asp:RegularExpressionValidator id="vldEmail" runat="server"
ErrorMessage="Acestui e-mail îi lipsește simbolul @."
ValidationExpression=".+@.+" ControlToValidate="txtEmail" />
<asp:RangeValidator id="vldAge" runat="server" ErrorMessage="Această vârstă
nu este între 0 și 120." type="Integer" MinimumValue="0" MaximumValue="120"
ControlToValidate="txtAge" />

<asp:CustomValidator id="vldCode" runat="server"


ErrorMessage="Încercați un șir care începe cu 014."
ValidateEmptyText = "fals" OnServerValidate =
"vldCode_ServerValidate" ControlToValidate = "txtCode" / >

Formularul oferă două butoane de validare - unul care necesită validare și unul care permite utilizatorului să
anuleze activitatea cu grație:

<asp:Button id="cmdSubmit" runat="server" OnClick="cmdSubmit_Click"


text="Submit"></asp:Button> <asp:Button id="cmdCancel" runat="server"
CausesValidation="False" OnClick = "cmdCancel_Click" text = "anulare"> < /
asp: buton>

Iată codul de gestionare a evenimentelor pentru butoane:

protejat void cmdSubmit_Click(Object sender, EventArgs e) { if (Page.IsValid)

{
lblMessage.Text = "Acesta este un formular valid.";
}}

protejat void cmdCancel_Click(Obiect expeditor, EventArgs e)


{
lblMessage.Text = "Nu s-a făcut nicio încercare de validare a acestui formular.";
}

Singurul cod la nivel de formular necesar pentru validare este codul de validare particularizat. Validarea are
loc în rutina de tratare a evenimentelor pentru evenimentul CustomValidator.ServerValidate. Această metodă
primește valoarea de care are nevoie pentru validare (e.Value) și setează rezultatul validării la true sau false
(e.IsValid).

310
CAPITOLUL VALIDARE
9

protejat void vldCode_ServerValidate(Sursă obiect, ServerValidateEventArgs e)


{
încerca
{ // Verificați dacă primele trei cifre sunt divizibile cu șapte. int val =
Int32.Parse(e.Value.Substring(0, 3)); dacă (val % 7 == 0) {

e.IsValid = adevărat; }
altceva {

e.IsValid = fals;
}}

captură { // A apărut o eroare în conversie. // Valoarea nu este validă.

e. = fals;

Acest exemplu introduce, de asemenea, un detaliu nou: tratarea erorilor. Acest cod de tratare a erorilor
asigură că problemele potențiale sunt detectate și tratate în mod corespunzător. Fără gestionarea erorilor,
codul dvs. poate eșua, lăsând utilizatorul cu nimic mai mult decât o pagină de eroare criptică. Motivul pentru
care acest exemplu necesită cod de gestionare a erorilor se datorează faptului că efectuează doi pași care
nu sunt garantați să reușească. Mai întâi, metoda Int32.Parse() încearcă să convertească datele din caseta
text într-un întreg. În timpul acestui pas va apărea o eroare dacă informațiile din caseta de text nu sunt
numerice (de exemplu, dacă utilizatorul a introdus caracterele 4G). În mod similar, metoda String.Substring(),
care extrage primele trei caractere, va eșua dacă apar mai puțin de trei caractere în caseta text. Pentru a vă
proteja împotriva acestor probleme, aveți posibilitatea să verificați în mod specific aceste detalii înainte de a
încerca să utilizați metodele Parse() și Substring() sau puteți utiliza tratarea erorilor pentru a răspunde la
probleme după ce apar. (O altă opțiune este să utilizați metoda TryParse(), care returnează o valoare
booleană care vă spune dacă conversia a reușit. Ați văzut TryParse () la lucru în capitolul 5.)

Sfat
În unele cazuri, este posibil să puteți înlocui validarea personalizată cu o utilizare deosebit de ingenioasă a unei expresii regulate. Cu toate acestea, puteți utiliza validarea particularizată pentru a vă asigura că codul de validare este executat numai la server. Acest lucru împiedică utilizatorii să vadă șablonul dvs. de expresie obișnuită (în codul JavaScript redat) și să îl folosească pentru a determina modul în care vă pot depăși rutina de validare. De exemplu, este posibil ca un utilizator să nu aibă un număr valid de card de credit, dar dacă cunoaște

algoritmul pe care îl utilizați pentru a testa numerele cărților de credit, poate crea unul fals mai ușor.

CustomValidator are o altă ciudățenie. Veți observa că validarea personalizată pe partea de server nu este efectuată
până când pagina nu este postată înapoi. Aceasta înseamnă că, dacă activați codul scriptului client (implicit), vor apărea
mesaje dinamice care informează utilizatorul atunci când celelalte valori sunt incorecte, dar nu vor indica nicio problemă

311
CAPITOLUL VALIDARE
9

cu codul de recomandare până când pagina nu este postată înapoi pe server. Aceasta nu este într-adevăr o problemă,
dar dacă vă deranjează, puteți utiliza proprietatea CustomValidator.ClientValidationFunction. Adăugați o funcție
JavaScript pe partea client la porțiunea .aspx a paginii web. Amintiți-vă, nu puteți utiliza codul ASP.NET pe partea
clientului, deoarece C# și VB nu sunt recunoscute de browserul client.
Funcția JavaScript va accepta doi parametri (în stil adevărat .NET), care identifică sursa evenimentului și parametrii
suplimentari de validare. De fapt, evenimentul de partea client este modelat pe evenimentul .NET ServerValidate. La fel
cum ați făcut în rutina de tratare a evenimentelor ServerValidate, în funcția de validare client, regăsiți valoarea de validat
din proprietatea Value a obiectului argument eveniment. Apoi setați proprietatea IsValid pentru a indica dacă validarea
reușește sau nu reușește.
Următorul este echivalentul pe partea client pentru codul din rutina de tratare a evenimentelor
ServerValide. Codul JavaScript seamănă superficial cu C#.

<script type="text/javascript">
<!--
funcția MyCustomValidation(objSource, objArgs)
{
Obțineți valoare.
numărul var = objArgs.Value;

Verificați valoarea și rezultatul


returnat. număr = număr.substr(0, 3);
if (număr % 7 == 0) { objArgs.IsValid =
true; } else { objArgs.IsValid = false; }

}
--> </scenariu>

După ce ați adăugat funcția script de validare, trebuie să setați proprietatea ClientValidationFunction a
controlului CustomValidator la numele funcției. Aveți posibilitatea să editați manual eticheta CustomValidator
sau să utilizați fereastra Proprietăți din Visual Studio.
<asp:CustomValidator id="vldCode" runat="server"
ErrorMessage="Încercați un șir care începe cu 014."
ControlToValidate="txtCode"
OnServerValidate="vldCode_ServerValidate"
ClientValidationFunction="MyCustomValidation" />
ASP.NET va apela acum această funcție în numele dvs. atunci când este necesar.

Sfat: Chiar și atunci când utilizați validarea pe partea client, trebuie să includeți în continuare rutina de tratare a evenimentelor ServerValide, atât pentru a oferi validare pe partea de server pentru clienții care nu acceptă caracteristicile JavaScript și DHTML necesare, cât și pentru a împiedica clienții să vă eludeze validarea modificând pagina HTML pe care o primesc.

312
CAPITOLUL VALIDARE
9

În mod implicit, validarea personalizată nu se efectuează pentru valori goale. Cu toate acestea, aveți
posibilitatea să modificați acest comportament setând proprietatea CustomValidator.ValidateEmptyText la
true. Aceasta este o abordare utilă dacă creați o funcție JavaScript mai detaliată (de exemplu, una care se
actualizează cu informații suplimentare) și doriți ca aceasta să ruleze atunci când textul este șters.

Grupuri de validare
În paginile mai complexe, este posibil să aveți mai multe grupuri distincte de controale, posibil în panouri separate. În
aceste situații, poate doriți să efectuați validarea separat. De exemplu, puteți crea un formular care include o casetă cu
controale de conectare și o casetă dedesubt cu comenzile pentru înregistrarea unui utilizator nou. Fiecare casetă include
propriul buton de trimitere și, în funcție de butonul pe care faceți clic, doriți să efectuați validarea doar pentru acea
secțiune a paginii.
Acest scenariu este posibil datorită unei caracteristici numite grupuri de validare. Pentru a crea un grup de validare,
trebuie să puneți controalele de intrare, validatorii și controalele butonului CausesValidation în același grup logic. Faceți
acest lucru setând proprietatea ValidationGroup a fiecărui control cu același șir descriptiv (cum ar fi "LoginGroup" sau
"NewUserGroup"). Fiecare control care furnizează o proprietate CausesValidation include și proprietatea
ValidationGroup.
De exemplu, pagina următoare definește două grupuri de validare, denumite Grup1 și Grup2. Controalele
pentru fiecare grup sunt plasate în controale separate ale panoului.

<form id="form1" runat="server"> <asp:Panel ID="Panel1" runat="server">


<asp:TextBox ID="TextBox1" ValidationGroup="Group1" runat="server" />
<asp:RequiredFieldValidator ID="RequiredFieldValidator1"
ErrorMessage="*Required" ValidationGroup="Group1" runat="server"
ControlToValidate="TextBox1" /> <asp:Button ID="Button1" text="Validare grup1"
ValidationGroup="Group1" runat="server" /> </asp:Panel> <br /> <asp: Panel
ID="Panel2" runat="server"> <asp:TextBox ID="TextBox2"
ValidationGroup="Group2" runat="server" /> <asp:RequiredFieldValidator
ID="RequiredFieldValidator2" ErrorMessage="*Required" ValidationGroup="Group2"
ControlToValidate="TextBox2" runat="server" /> <asp:Button ID="Button2"
text="Validare grup2" ValidationGroup="Group2" runat="server" /> </asp:Panel>
</form>

Dacă faceți clic pe butonul din panoul cel mai de sus, numai prima casetă de text este validată. Dacă
faceți clic pe butonul din al doilea panou, numai a doua casetă de text este validată (așa cum se arată
în Figura 9-8).

313
CAPITOLUL VALIDARE
9

Figura 9-8. Gruparea controalelor pentru validare

Ce se întâmplă dacă adăugați un buton nou care nu specifică niciun grup de validare? În acest caz, butonul
validează fiecare control care nu este atribuit în mod explicit unui grup de validare numit. În exemplul curent,
niciun control nu corespunde cerinței, astfel încât pagina este postată înapoi cu succes și considerată validă.
Dacă doriți să vă asigurați că un control este întotdeauna validat, indiferent de grupul de validare al butonului
pe care se face clic, va trebui să creați mai mulți validatori pentru control, câte unul pentru fiecare grup (și
unul fără grup de validare).

Ultimul cuvânt
În acest capitol, ați învățat cum să utilizați una dintre cele mai practice caracteristici ale ASP.NET: validarea.
Ați văzut cum ASP.NET combină validarea pe partea serverului și pe partea clientului pentru a asigura
securitatea antiglonț fără a sacrifica gradul de utilizare a paginilor dvs. web. De asemenea, ați analizat tipurile
de validare furnizate de diferitele controale de validare și chiar ați perfecționat sintaxa puternică de potrivire a
modelelor utilizată pentru expresiile regulate. În cele din urmă, v-ați gândit cum să personalizați și să extindeți
procesul de validare pentru a gestiona câteva scenarii diferite.

314
C A P I T O L U L 10

•■■

Controale bogate

Controalele îmbogățite sunt controale web care modelează elemente complexe de interfață cu utilizatorul. Deși nu există
o definiție strictă pentru ceea ce este și ceea ce nu este un control bogat, termenul descrie în mod obișnuit un control
web care are un model de obiect distinct separat de HTML-ul pe care îl generează. Un control bogat tipic poate fi
programat ca un singur obiect (și adăugat la o pagină web cu o singură etichetă de control), dar se redă folosind o
secvență complexă de elemente HTML. Controalele îmbogățite pot, de asemenea, să reacționeze la acțiunile
utilizatorului (cum ar fi un clic de mouse pe o anumită regiune a controlului) și să genereze evenimente mai
semnificative la care codul dvs. poate răspunde pe serverul web. Cu alte cuvinte, controalele bogate vă oferă o
modalitate de a crea interfețe de utilizator avansate în paginile dvs. web fără a scrie linii de HTML complicat.
În acest capitol, veți arunca o privire asupra mai multor controale web care nu au echivalent direct în lumea
HTML obișnuit. Veți începe cu Calendarul, care oferă funcționalitate elegantă de selectare a datei. Apoi, veți
lua în considerare AdRotator, care vă oferă o modalitate ușoară de a insera o imagine selectată aleatoriu
într-o pagină web. În cele din urmă, veți învăța cum să creați pagini sofisticate cu mai multe vizualizări
utilizând două controale avansate ale containerului: MultiView și Wizard. Aceste controale vă permit să
împachetați o aplicație miniaturală într-o singură pagină. Folosindu-le, puteți gestiona o sarcină în mai mulți
pași fără a redirecționa utilizatorul de la o pagină la alta.

Notă
ASP.NET include numeroase controale bogate care sunt discutate în altă parte în această carte, inclusiv controale de listă bazate pe date, controale de securitate și controale adaptate pentru portalurile web. În acest capitol, vă veți concentra pe câteva controale web utile care nu se încadrează perfect în niciuna dintre aceste categorii. Toate aceste controale apar în fila Standard din Visual Studio Toolbox.

Calendarul
Controlul Calendar prezintă un calendar miniatural pe care îl puteți plasa în orice pagină web. La fel ca
majoritatea controalelor bogate, calendarul poate fi programat ca un singur obiect (și definit într-o singură
etichetă simplă), dar se redă cu zeci de linii de ieșire HTML.
<asp:Calendar id="MyCalendar" runat="server" />
Controlul Calendar prezintă o vizualizare pentru o singură lună, așa cum se arată în Figura 10-1. Utilizatorul poate naviga
de la o lună la alta folosind săgețile de navigare, moment în care pagina este postată înapoi și ASP.NET furnizează

315
CAPITOLUL CONTROALE BOGATE
10

automat o pagină nouă cu valorile corecte ale lunii. Nu trebuie să scrieți niciun cod suplimentar de gestionare a
evenimentelor pentru a gestiona acest proces. Când utilizatorul face clic pe o dată, data devine evidențiată într-o
casetă gri (implicit). Puteți regăsi ziua selectată în cod ca obiect DateTime din proprietatea Calendar.SelectedDate.

Figura 10-1. Calendarul implicit

Acest set de caracteristici de bază vă poate oferi tot ce aveți nevoie în aplicația dvs. Alternativ, puteți configura diferite
moduri de selecție pentru a permite utilizatorilor să selecteze săptămâni sau luni întregi sau să redea controlul ca un
calendar static care nu permite selecția. Singurul fapt pe care trebuie să-l amintiți este că, dacă permiteți selectarea lunii,
utilizatorul poate selecta și o singură săptămână sau o zi. În mod similar, dacă permiteți selectarea săptămânii,
utilizatorul poate selecta și o singură zi.
Setați tipul de selecție prin proprietatea Calendar.SelectionMode. De asemenea, poate fi necesar să setați
proprietatea Calendar.FirstDayOfWeek pentru a configura modul în care este selectată o săptămână. (De exemplu,
setați FirstDayOfWeek la valoarea enumerată duminică, iar săptămânile vor fi selectate de duminică până sâmbătă.)

Când permiteți selectarea mai multor date, trebuie să examinați proprietatea SelectedDates, care oferă o
colecție a tuturor datelor selectate. Puteți parcurge în buclă această colecție folosind sintaxa foreach.
Următorul cod demonstrează această tehnică:
lblDates.Text = "Ați selectat aceste date:<br />";

foreach (DateTime dt în MyCalendar.SelectedDates)


{
lblDates.Text += dt. ToLongDateString() + "<br />";
}

Figura 10-2 prezintă pagina rezultată după executarea acestui cod.

316
CAPITOLUL CONTROALE BOGATE
10

Figura 10-2. Selectarea mai multor date

Formatarea calendarului
Controlul Calendar furnizează o serie întreagă de proprietăți legate de formatare. Puteți seta diverse părți ale
calendarului, cum ar fi antetul, selectorul și diverse tipuri de zile, utilizând una dintre proprietățile stilului (de
exemplu, WeekendDayStyle). Fiecare dintre aceste proprietăți de stil face referire la un obiect TableItemStyle
cu caracteristici complete, care furnizează proprietăți pentru colorare, stil bordură, font și aliniere. Luate
împreună, acestea vă permit să modificați aproape orice parte a aspectului calendarului. Tabelul 10-1
listează proprietățile de stil furnizate de controlul Calendar.
Tabelul 10-1. Proprietăți pentru stilurile de calendar

Membru Descriere

DayHeaderStyle Stilul pentru secțiunea din Calendar care afișează zilele săptămânii (ca
anteturi de coloană).
Stil de zi Stilul implicit pentru datele din luna curentă.

NextPrevStyle Stilul pentru controalele de navigare din secțiunea titlu care se mută de la o
lună la alta.

317
CAPITOLUL CONTROALE BOGATE
10

Membru Descriere

OtherMonthDayStyle Stilul pentru datele care nu se află în luna afișată curent. Acești
Datele sunt folosite pentru a "completa" grila calendarului. De exemplu, primele câteva celule din
Rândul cel mai de sus poate afișa ultimele zile din luna anterioară.

SelectedDayStyle Stilul pentru datele selectate din calendar.

SelectorStil Stilul pentru controalele de selectare a datei săptămânii și lunii.

TitluStil Stilul pentru secțiunea titlu.

TodayDayStyle Stilul pentru data desemnată ca astăzi (reprezentat de TodaysDate


proprietatea controlului Calendar).

WeekendDayStyle Stilul pentru datele care cad în weekend.

Puteți ajusta fiecare stil utilizând fereastra Proprietăți. Pentru o comandă rapidă rapidă, puteți seta o întreagă schemă de
Descărcați de la Wow! eBook <www.wowebook.com>

culori asociată utilizând funcția Format automat a calendarului. Pentru a face acest lucru, începeți prin selectarea
Calendarului pe suprafața de proiectare a unui formular web. Apoi, faceți clic pe pictograma săgeată care apare lângă
colțul din dreapta sus pentru a afișa eticheta inteligentă a calendarului și faceți clic pe linkul Formatare automată. Vi se
va prezenta o listă de formate predefinite care setează proprietățile stilului, așa cum se arată în Figura 10-3.

Figura 10-3. Stiluri de calendar

318
CAPITOLUL CONTROALE BOGATE
10

De asemenea, puteți utiliza proprietăți suplimentare pentru a ascunde unele elemente sau pentru a configura
textul afișat de acestea. De exemplu, proprietățile care încep cu "Afișare" (cum ar fi ShowDayHeader,
ShowTitle și ShowGridLines) pot fi utilizate pentru a ascunde sau a afișa un anumit element vizual.
Proprietățile care se termină în "Text" (cum ar fi PrevMonthText, NextMonthText și SelectWeekText) vă
permit să setați textul afișat într-o parte a calendarului.

Restricționarea datelor
În majoritatea situațiilor în care trebuie să utilizați un calendar pentru selecție, nu doriți să permiteți utilizatorului să
selecteze nicio dată din calendar. De exemplu, utilizatorul poate rezerva o rezervare sau poate alege o dată de livrare -
două servicii care sunt furnizate în general numai în zile stabilite. Controlul Calendar face surprinzător de ușoară
implementarea acestei logici. De fapt, dacă ați lucrat cu controalele de dată și oră de pe platforma Windows, veți
recunoaște rapid că versiunile ASP.NET sunt mult superioare. Abordarea de bază pentru restricționarea datelor este de a
scrie un handler de evenimente pentru evenimentul Calendar.DayRender. Acest eveniment apare atunci când controlul
Calendar este pe cale să creeze o lună pentru a se afișa utilizatorului. Acest eveniment vă oferă șansa de a examina
data care este adăugată la luna curentă (prin proprietatea e.Day) și de a decide dacă ar trebui să fie selectabilă sau
restricționată.
Următorul cod face imposibilă selectarea zilelor de weekend sau a zilelor din anii mai mari decât 2012:

protejat void MyCalendar_DayRender(Sursă obiect, DayRenderEventArgs e)


{
Restricționați datele de după anul 2012 și cele din weekend. dacă
(e.Day.IsWeekend || e.Day.Date.Year > 2012)
{
e.Day.IsSelectable = false;
}
}

Obiectul e.Day este o instanță a clasei CalendarDay, care oferă diverse proprietăți. Tabelul 10-2 descrie
unele dintre cele mai utile.

Tabelul 10-2. Proprietățile CalendarDay

Proprietate Descriere

Dată Obiectul DateTime care reprezintă această dată.

IsWeekend Adevărat dacă această dată cade într-o sâmbătă sau duminică.

IsToday True dacă această valoare se potrivește cu proprietatea Calendar.TodaysDate, care


este setată implicit la ziua curentă.

IsOtherMonth True dacă această dată nu aparține lunii curente, dar este afișată pentru a completa
primul sau ultimul rând. De exemplu, aceasta poate fi ultima zi a lunii anterioare sau
următoarea zi a lunii următoare.

IsSelectable Vă permite să configurați dacă utilizatorul poate selecta această zi.

319
CAPITOLUL CONTROALE BOGATE
10

Evenimentul DayRender este extrem de puternic. Pe lângă faptul că vă permite să personalizați ce date sunt selectabile,
vă permite, de asemenea, să configurați celula în care se află data prin proprietatea e.Cell. (Calendarul este afișat
utilizând un tabel HTML.) De exemplu, puteți evidenția o dată importantă sau chiar adăuga informații. Iată un exemplu
care evidențiază o singură zi – a cincea mai – adăugând
a nou control Etichetă în celula de tabel pentru ziua respectivă:

protejat void MyCalendar_DayRender(Sursă obiect, DayRenderEventArgs e)


{
Verificați pentru 5 mai în orice an și formatați-l. dacă
(e.Day.Date.Day == 5 &&; e.Day.Date.Month == 5)
{
e.Cell.BackColor = System.Drawing.Color.Yellow;

Adăugați text static la celulă. Etichetă lbl =


etichetă nouă(); Lbl. Text = "<br />Ziua mea!";

e.Cell.Controls.Add(lbl);
}
}

Figura 10-4 prezintă afișarea calendarului rezultat.

Figura 10-4. Evidențierea unei zile

Controlul Calendar furnizează alte două evenimente utile: SelectionChanged și VisibleMonthChanged.


Acestea apar imediat după ce utilizatorul selectează o nouă zi sau navighează la o nouă lună (folosind
linkurile lunii următoare și lunii anterioare). Puteți reacționa la aceste evenimente și puteți actualiza alte
porțiuni ale paginii web pentru a corespunde lunii calendaristice curente. De exemplu, puteți proiecta o
pagină care vă permite să programați o întâlnire în doi pași. În primul rând, alegeți ziua potrivită. Apoi,
alegeți una dintre orele disponibile în acea zi.

320
CAPITOLUL CONTROALE BOGATE
10

Următorul cod demonstrează această abordare, utilizând un set diferit de valori de timp dacă este selectată
o zi de luni în calendar decât pentru alte zile:

protejat void MyCalendar_SelectionChanged(Sursă obiect, EventArgs e)


{
lstTimes.Items.Clear();

comutator (MyCalendar.SelectedDate.DayOfWeek) { case DayOfWeek.Monday: // Aplicați programul special de luni.


lstTimes.Items.Add ("10:00"); lstTimes.Items.Add ("10:30"); lstTimes.Items.Add ("11:00"); pauză; implicit: lstTimes.Items.Add
("10:00"); lstTimes.Items.Add ("10:30"); lstTimes.Items.Add ("11:00"); lstTimes.Items.Add ("11:30"); lstTimes.Items.Add
("12:00"); lstTimes.Items.Add ("12:30"); lstTimes.Items.Add ("12:30"); } pauză;

}
Pentru a încerca aceste caracteristici ale controlului Calendar, rulați pagina Appointment.aspx din eșantioanele
online. Această pagină oferă un control Calendar formatat care restricționează unele date, formatează altele în
mod special și actualizează un control de listă corespunzător atunci când se modifică selecția.
Tabelul 10-3 vă oferă o privire dintr-o privire asupra aproape tuturor membrilor clasei de control Calendar.

Tabelul 10-3. Membrii calendarului

Membru Descriere

Subtitrare și Vă oferă o modalitate ușoară de a adăuga un titlu la calendar. În mod prestabilit, legenda
legendăAliniere apare în partea de sus a zonei de titlu, chiar deasupra titlului lunii. Cu toate acestea,
puteți controla acest lucru într-o oarecare măsură cu proprietatea CaptionAlign. Utilizați
Stânga sau Dreapta pentru a păstra legenda în partea de sus, dar mutați-o într-o parte
sau alta și utilizați Partea de jos pentru a plasa legenda sub calendar.

Căptușeală celulară ASP.NET creează o dată într-o celulă separată a unui tabel invizibil. CellPadding
este spațiul, în pixeli, dintre marginea fiecărei celule și conținutul acesteia.

Spațiere celule Spațiul, în pixeli, dintre celulele din același tabel.

DayNameFormat Stabilește modul de afișare a zilelor în antetul calendarului. Valorile valide sunt
Full (ca în Sunday), FirstLetter (S), FirstTwoLetters (Su) și Short (Sun), care este
implicit.

321
CAPITOLUL CONTROALE BOGATE
10

Membru Descriere

Prima zia săptămânii Stabilește ce zi este afișată în prima coloană a calendarului. Valorile sunt orice
nume de zi din enumerarea FirstDayOfWeek (cum ar fi Sunday). În mod
implicit, aceasta este duminică.

NextMonthText și Setează textul pe care utilizatorul face clic pentru a trece la luna următoare sau
PrevMonthText anterioară. Aceste linkuri de navigare apar în partea de sus a calendarului și sunt
semnele mai mari decât (>) și mai mici (<) în mod implicit. Această setare se
aplică numai dacă NextPrevFormat este setat la CustomText.
NextPrevFormat Setează textul pe care utilizatorul face clic pentru a trece la luna următoare sau
anterioară. Aceasta poate fi FullMonth (de exemplu, decembrie), ShortMonth
(decembrie) sau CustomText, caz în care sunt utilizate proprietățile
NextMonthText și PrevMonthText. CustomText este implicit.

SelectedDate și Setează sau obține data selectată curent ca obiect DateTime. Puteți specifica
SelectedDates acest lucru în eticheta de control într-un format ca acesta: "12:00:00 AM,
12/31/2010" (în funcție de setările regionale ale computerului). Dacă permiteți
selectarea mai multor date, proprietatea SelectedDates va returna o colecție de
obiecte DateTime, câte unul pentru fiecare dată selectată. Puteți utiliza metode
de colectare, cum ar fi Adăugare, Eliminare și Golire pentru a modifica selecția.
Modul de selecție Stabilește câte date pot fi selectate simultan. Valoarea implicită este Zi, care
permite selectarea unei date. Alte opțiuni includ DayWeek (o singură dată sau o
săptămână întreagă) sau DayWeekMonth (o singură dată, o săptămână întreagă
sau o lună întreagă). Nu aveți nicio modalitate de a permite utilizatorului să
selecteze mai multe date necontigue. De asemenea, nu aveți nicio modalitate
de a permite selecții mai mari fără a include și selecții mai mici. (De exemplu,
dacă permiteți selectarea lunilor întregi, trebuie să permiteți, de asemenea,
selectarea săptămânii și selectarea individuală a zilei.)
SelectMonthText și Textul afișat pentru linkul care permite utilizatorului să selecteze o lună sau o
SelectWeekText săptămână întreagă. Aceste proprietăți nu se aplică dacă SelectionMode este Day.

ShowDayHeader, Aceste proprietăți booleene vă permit să configurați dacă sunt afișate diferite
ShowGridLines, părți ale calendarului, inclusiv titlurile zilelor, liniile de grilă dintre fiecare zi,
ShowNextPrevMonth linkurile de navigare anterioare/luna următoare și secțiunea titlu. Rețineți că
și ShowTitle ascunderea secțiunii titlu ascunde, de asemenea, comenzile de navigare din
luna următoare și din luna anterioară.
TitleFormat Configurează modul în care este afișată luna în zona de titlu. Valorile valide
includ Month și MonthYear (valoarea implicită).

TodaysDate Setează ziua care trebuie recunoscută ca dată curentă și formatată cu


TodayDayStyle. Aceasta este implicită la ziua curentă pe serverul web.

VisibleDate Obține sau setează data care specifică luna care va fi afișată în calendar. Acest
lucru vă permite să modificați afișarea calendarului fără a modifica selecția
curentă a datei.

322
CAPITOLUL 10
CONTROALE COMPLEXE

Membru Descriere

Evenimentul DayRender Apare o dată pentru fiecare zi creată și adăugată la luna vizibilă în prezent
înainte de redarea paginii. Acest eveniment vă oferă posibilitatea de a aplica
formatare specială, de a adăuga conținut sau de a restricționa selecția pentru o
celulă de dată individuală. Reține că zilele pot apărea în calendar chiar și atunci
când nu cad în luna curentă, cu condiția să cadă aproape de sfârșitul lunii
anterioare sau aproape de începutul lunii următoare.
SelecțieEveniment Apare atunci când utilizatorul selectează o zi, o săptămână sau o lună întreagă
modificat făcând clic pe controalele selectorului de dată.

Eveniment Apare atunci când utilizatorul face clic pe controalele de navigare din luna
VisibleMonthChanged următoare sau din luna anterioară pentru a trece la altă lună.

AdRotatorul
Scopul de bază al AdRotator este de a oferi un grafic pe o pagină care este aleasă aleatoriu dintr-un grup
de imagini posibile. Cu alte cuvinte, de fiecare dată când pagina este solicitată, o imagine este selectată la
întâmplare și afișată, care este "rotația" indicată de numele AdRotator. O utilizare a AdRotator este de a
afișa reclame în stil banner pe o pagină, dar îl puteți utiliza oricând doriți să variați o imagine aleatoriu.
Folosind ASP.NET, nu ar fi prea dificil să implementați un tip de design AdRotator pe cont propriu. Puteți
reacționa la evenimentul Page.Load, puteți genera un număr aleatoriu și apoi puteți utiliza acel număr
pentru a alege dintr-o listă de fișiere imagine predeterminate. Puteți chiar să stocați lista în fișierul
web.config, astfel încât să poată fi ușor modificată separat ca parte a configurației aplicației. Desigur, dacă
doriți să activați mai multe pagini cu o imagine aleatorie, va trebui fie să repetați codul, fie să creați propriul
control personalizat. AdRotator oferă aceste funcții gratuit.

Fișierul publicitar
AdRotator stochează lista sa de fișiere imagine într-un fișier XML. Acest fișier utilizează formatul afișat aici:

<Reclame> <Ad> <ImageUrl>prosetech.jpg</ImageUrl>


<NavigateUrl>http://www.prosetech.com</NavigateUrl>
<AlternateText>ProseTech Site</AlternateText>

1 <Cuvânt-cheie>Computer</Cuvânt-cheie>
</Anunț> </Reclame>
CAPITOLUL CONTROALE BOGATE
10

Sfat: După cum veți vedea în capitolul 18, un fișier XML este doar un fișier text cu etichete specifice (așa cum se arată anterior). Puteți crea un fișier XML utilizând nimic mai mult decât un editor de text, cum ar fi Notepad, dar puteți utiliza și editorul de text Visual Studio. Doar selectați site-ul web


→ Adăugare element nou din meniu și selectați Fișier XML. Depinde de dvs. să completați
etichetele și conținutul potrivit. Puteți plasa fișierul de anunțuri oriunde doriți, fie în folderul
principal al site-ului web, fie într-un subfolder pe care l-ați creat.

Acest exemplu arată o singură reclamă posibilă, pe care controlul AdRotator o alege la întâmplare din lista de
reclame. Pentru a adăuga mai multe reclame, trebuie să creați mai multe elemente <Ad> și să le plasați pe
toate în interiorul elementului rădăcină <Reclame>:
<Reclame> <Anunț> <!--
Primul anunț aici. --> </Ad>

<Ad> <!-- Al doilea anunț aici.


--> </Ad> </Reclame>

Fiecare element <Ad> are o serie de alte proprietăți importante care configurează legătura, imaginea și
frecvența, așa cum este descris în tabelul 10-4.

Tabelul 10-4. Elemente de fișier publicitar

Element Descriere

ImageUrl Imaginea care va fi afișată. Acesta poate fi un link relativ (un fișier din directorul
curent) sau o adresă URL de internet complet calificată.

NavigateUrl Linkul care va fi urmat dacă utilizatorul face clic pe banner. Aceasta poate fi o adresă
URL relativă sau complet calificată.

Text alternativ Textul care va fi afișat în locul imaginii, dacă nu poate fi afișat. Acest text va fi, de
asemenea, utilizat ca sfat ecran în unele browsere mai noi.

Impresii Un număr care stabilește cât de des va apărea o reclamă. Acest număr este relativ la
numerele specificate pentru alte anunțuri. De exemplu, un banner cu valoarea 10 va fi
afișat de două ori mai des (în medie) decât bannerul cu valoarea 5.

Cuvânt cheie Un cuvânt cheie care identifică un grup de anunțuri. Puteți utiliza acest lucru pentru
filtrare. De exemplu, puteți crea zece anunțuri și puteți da jumătate dintre ele cuvântul
cheie Retail și cealaltă jumătate cuvântul cheie Computer. Pagina web poate alege
apoi să filtreze anunțurile posibile pentru a include doar unul dintre aceste grupuri.

324
CAPITOLUL CONTROALE BOGATE
10

Clasa AdRotator
Clasa AdRotator actuală oferă un set limitat de proprietăți. Specificați atât fișierul de publicitate
corespunzător în proprietatea AdvertisementFile, cât și tipul de fereastră pe care linkul ar trebui să o
urmeze (fereastra țintă). Ținta poate denumi un anumit cadru sau poate utiliza una dintre valorile
definite în tabelul 10-5.
Tabelul 10-5. Obiective cadru special

Scop Descriere

_necompletat Linkul deschide o nouă fereastră neîncadrată.

_părinte Legătura se deschide în părintele cadrului curent.

_Sine Linkul se deschide în cadrul curent.

_culme Legătura se deschide în cadrul cel mai de sus al ferestrei curente (astfel încât legătura apare în
fereastră completă).

Opțional, puteți seta proprietatea KeywordFilter astfel încât bannerul să fie ales dintr-un anumit grup
de cuvinte cheie. Aceasta este o etichetă AdRotator complet configurată:

<asp:AdRotator id="Ads" runat="server" AdvertisementFile="MainAds.xml"


Target="_blank" KeywordFilter="Computer" />

Notă
Atributul target nu este permis în XHTML strict. Dacă decideți să-l utilizați, asigurați-vă că utilizați XHTML 1.0 doctype de tranziție, așa cum este descris în capitolul 4. (Acesta este doctype implicit pentru paginile web noi pe care le creați în Visual Studio.)

În plus, puteți reacționa la evenimentul AdRotator.AdCreated. Acest lucru se întâmplă atunci când pagina
este creată și o imagine este aleasă aleatoriu din fișierul de anunțuri. Acest eveniment vă oferă informații
despre imaginea pe care o puteți utiliza pentru a personaliza restul paginii. De exemplu, puteți afișa un
conținut similar sau un link, așa cum se arată în Figura 10-5.

325
CAPITOLUL CONTROALE BOGATE
10

Figura 10-5. Un AdRotator cu conținut sincronizat

Codul de gestionare a evenimentelor pentru acest exemplu configurează pur și simplu un control
HyperLink numit lnkBanner pe baza reclamei selectate aleatoriu:

protejat void Ads_AdCreated(Expeditor obiect, AdCreatedEventArgs e)


{
Sincronizați controlul Hyperlink.
lnkBanner.NavigateUrl = e.NavigateUrl;

Sincronizați textul linkului.


lnkBanner.Text = "Click aici pentru informații despre sponsorul nostru: ";
lnkBanner.Text += e.AlternateText;
}

După cum puteți vedea, controalele bogate, cum ar fi Calendar și AdRotator, nu adaugă doar o ieșire
HTML sofisticată; Acestea includ, de asemenea, un cadru de evenimente care vă permite să vă ocupați
de comportamentul controlului și să îl integrați în aplicația dvs.

Pagini cu mai multe vizualizări


Într-un site web tipic, veți naviga prin mai multe pagini separate. De exemplu, dacă doriți să adăugați un articol în coșul
de cumpărături și să îl duceți la checkout pe un site de comerț electronic, va trebui să treceți de la o pagină la alta. Acest
design are avantajele sale - și anume, vă permite să separați cu atenție diferite sarcini în diferite fișiere de cod. De
asemenea, prezintă unele provocări; de exemplu, trebuie să veniți cu o modalitate de a transfera informații de la o pagină
la alta (un subiect care este tratat în detaliu în capitolul 8). Cu toate acestea, în unele cazuri este mai logic să creați o
singură pagină care să poată gestiona mai multe sarcini diferite. De exemplu, poate doriți să furnizați mai multe
vizualizări ale acelorași date (cum ar fi o vizualizare bazată pe grilă și o vizualizare bazată pe diagrame) și să permiteți

326
CAPITOLUL CONTROALE BOGATE
10

utilizatorului să comute de la o vizualizare la alta fără a părăsi pagina. Sau, poate doriți să gestionați o mică activitate în
mai mulți pași într-un singur loc (cum ar fi furnizarea de informații despre utilizator pentru un proces de înregistrare a
contului). În aceste exemple, aveți nevoie de o modalitate de a crea pagini dinamice care oferă mai multe vizualizări
posibile. În esență, pagina ascunde și afișează diferite controale în funcție de vizualizarea pe care doriți să o prezentați.
Cel mai simplu mod de a înțelege această tehnică este de a crea o pagină cu mai multe controale ale panoului. Fiecare
panou poate conține un grup de controale ASP.NET. De exemplu, imaginați-vă că creați un expert simplu în trei pași. Veți
începe prin a adăuga trei panouri la pagina dvs., câte unul pentru fiecare pas - să zicem, panouPasul 1, panouPasul 2 și
panouPasul 3. Puteți plasa panourile unul după altul, deoarece veți afișa doar unul câte unul. După ce ați adăugat
panourile, puteți plasa controalele corespunzătoare în interiorul fiecărui panou. Pentru a începe, proprietatea Vizibil a
fiecărui panou ar trebui să fie falsă, cu excepția panouluiPasul 1, care apare prima dată când utilizatorul solicită pagina.

Iată un exemplu care arată modul în care vă puteți aranja panourile:

<asp:Panel ID="panelStep1" runat="server">... </asp:Panou>


<asp:Panel ID="panelStep2" vizibil="fals" runat="server">... </asp:Panel>
<asp:Panel ID="panelStep3" vizibil="False" runat="server">... </asp:Panou>

Notă
Când setați proprietatea Vizibil a unui control la false, controlul nu va apărea în pagină la momentul rulării. Orice controale din interiorul unui panou invizibil sunt, de asemenea, ascunse vederii și nu vor fi prezente în codul HTML redat pentru pagină. Cu toate acestea, aceste controale vor apărea în continuare în suprafața de proiectare Visual Studio, astfel încât să le puteți selecta și configura în continuare.

În cele din urmă, veți adăuga unul sau mai multe butoane de navigare în afara panourilor. De exemplu,
următorul cod gestionează clicul unui buton Următorul, care este plasat imediat după panouPasul 3 (deci
apare întotdeauna în partea de jos a paginii). Codul verifică pasul în care se află utilizatorul, ascunde panoul
curent și afișează următorul panou. În acest fel, utilizatorul este mutat la pasul următor.
vid protejat cmdNext_Click(expeditor obiect, EventArgs e)
{
dacă (panelStep1.Visible)
{ // Treceți la pasul 2.

panouPasul 1.Vizibil = fals;


panouPasul 2.Vizibil = adevărat; }
else if (panelStep2.Visible) { //
Treceți la pasul 3.

panouPasul 2.Vizibil = fals;


panouPasul 3.Vizibil = adevărat;

Schimbați textul butonului de la Următorul la


Terminare. cmdNext.Text = "Finalizare";
}
altfel dacă (panelStep3.Visible) { //
Expertul este terminat.

327
CAPITOLUL CONTROALE BOGATE
10

panelStep3.Visible = false;

Adăugați codul aici pentru a efectua sarcina


corespunzătoare // cu informațiile pe care le-ați colectat.
}
}
Această abordare funcționează relativ bine. Chiar și atunci când panourile sunt ascunse, puteți interacționa în
continuare cu toate comenzile de pe fiecare panou și puteți prelua informațiile pe care le conțin. Problema este că
trebuie să scrieți tot codul pentru a controla ce panou este vizibil. Dacă faceți expertul mult mai complex - de
exemplu, doriți să adăugați un buton pentru revenirea la un pas anterior - devine mai dificil să urmăriți ce se
întâmplă. În cel mai bun caz, această abordare vă aglomerează pagina cu codul pentru gestionarea panourilor. În
cel mai rău caz, veți face o greșeală minoră și veți ajunge la două panouri afișate în același timp.
Din fericire, ASP.NET vă oferă o opțiune mai robustă. Puteți utiliza două controale proiectate pentru
activitate - MultiView și Expertul. În secțiunile următoare, veți vedea cum puteți utiliza ambele controale cu
exemplul GreetingCardMaker dezvoltat în capitolul 6.

Controlul MultiView
Descărcați de la Wow! eBook <www.wowebook.com>

MultiView este cel mai simplu dintre cele două controale cu vizualizare multiplă. În esență, MultiView vă oferă o
modalitate de a declara mai multe vizualizări și de a afișa doar una la un moment dat. Nu are o interfață de utilizator
implicită - obțineți doar orice HTML și controale adăugați. MultiView este echivalent cu abordarea personalizată a
panoului explicată anterior.
Crearea unui MultiView este destul de simplă. Adăugați eticheta <asp:MultiView> la fișierul de pagină
.aspx și apoi adăugați o etichetă <asp:View> în interiorul acesteia pentru fiecare vizualizare separată:

<asp:MultiView ID="MultiView1" runat="server"> <asp:View


ID="View1" runat="server">... </asp:View> <asp:View
ID="View2" runat="server">... </asp:View> <asp:View
ID="View3" runat="server">... </asp:Vizualizare>
</asp:MultiView>
În Visual Studio, creați aceste etichete fixând mai întâi un control MultiView în formular, apoi utilizând caseta de instrumente
pentru a adăuga câte controale de vizualizare doriți în interiorul acestuia. Acest proces drag-and-drop poate fi un pic dificil.
Când adăugați primul control Vizualizare, trebuie să vă asigurați că îl fixați în zona goală din MultiView (nu lângă MultiView sau
pe bara de titlu MultiView). Atunci când adăugați mai multe controale Vizualizare, trebuie să le fixați pe fiecare pe una dintre
barele de antet gri ale uneia dintre vizualizările existente. Antetul gri are titlul Vizualizare (cum ar fi "Vizualizare1" sau
"Vizualizare2").
Controlul Vizualizare joacă același rol ca și controlul Panou din exemplul anterior, iar MultiView se ocupă de
coordonarea tuturor vizualizărilor, astfel încât doar una să fie vizibilă la un moment dat. În fiecare vizualizare,
puteți adăuga controale HTML sau web. De exemplu, luați în considerare exemplul GreetingCardMaker
demonstrat în capitolul 6, care permite utilizatorului să creeze o felicitare furnizând text și alegând culori, un
font și un fundal. Pe măsură ce GreetingCardMaker devine mai complex, necesită mai multe controale și
devine din ce în ce mai dificil să încadrați toate aceste controale pe aceeași pagină. O soluție posibilă este
împărțirea acestor controale în grupuri logice și plasarea fiecărui grup într-o vizualizare separată.

328
CAPITOLUL CONTROALE BOGATE
10

Crearea vizualizărilor
Iată marcajul complet pentru o vizualizare multiplă care împarte controalele felicitării în trei vizualizări
denumite Vizualizare1, Vizualizare2 și Vizualizare3:

<asp:MultiView id="MultiView1" runat="server" >

<asp:View ID="View1" runat="server"> Alegeți un plan frontal (text) culoare:<br />


<asp:DropDownList ID="lstForeColor" runat="server" AutoPostBack="True"
OnSelectedIndexChanged="ControlChanged" /> <br /><br /> Alegeți o culoare de
fundal: <br /> <asp:DropDownList ID="lstBackColor" runat="server"
AutoPostBack="True" OnSelectedIndexChanged="ControlChanged" /> </asp:View>

<asp:View ID="View2" runat="server"> Alegeți un stil de bordură:<br />


<asp:RadioButtonList ID="lstBorder" runat="server" AutoPostBack="True"
OnSelectedIndexChanged="ControlChanged" RepeatColumns="2" /> <br />
<asp:CheckBox ID="chkPicture" runat="server" AutoPostBack="True"
OnCheckedChanged="ControlChanged" text="Adăugați imaginea implicită" />
</asp:View>

<asp:View ID="View3" runat="server"> Alegeți un nume de font:<br />


<asp:DropDownList ID="lstFontName" runat="server" AutoPostBack="True"
OnSelectedIndexChanged="ControlChanged" /> <br /><br /> Specificați o
dimensiune font: <br /> <asp:TextBox ID="txtFontSize" runat="server"
AutoPostBack="True" OnTextChanged="ControlChanged" /> <br /><br />
Introduceți textul de salut de mai jos: <br /> <asp:TextBox ID="txtGreeting"
runat="server" AutoPostBack="True" OnTextChanged="ControlChanged"
TextMode="MultiLine" /> </asp:View>

</asp:MultiView>

Visual Studio afișează toate vizualizările la momentul proiectării, una după alta (consultați Figura 10-6).
Puteți edita aceste regiuni în același mod în care proiectați orice altă parte a paginii.

329
CAPITOLUL CONTROALE BOGATE
10

Figura 10-6. Proiectarea mai multor vizualizări

Afișarea unei vizualizări


Dacă rulați acest exemplu, nu veți vedea la ce vă așteptați. MultiView va apărea gol pe pagină și toate controalele din
toate vizualizările vor fi ascunse.
Motivul pentru care se întâmplă acest lucru este că proprietatea MultiView.ActiveViewIndex este, în mod implicit,
setată la –1. Proprietatea ActiveViewIndex determină ce vizualizare va fi afișată. Totuși, dacă setați
ActiveViewIndex la 0, veți vedea prima vizualizare. În mod similar, îl puteți seta la 1 pentru a afișa a doua
vizualizare și așa mai departe. Puteți seta această proprietate utilizând fereastra Proprietăți sau utilizând codul:

Afișați prima vizualizare.


MultiView1.ActiveViewIndex = 0;

Acest exemplu arată prima vizualizare (Vizualizare1) și ascunde orice vizualizare afișată în prezent, dacă
există.

330
CAPITOLUL CONTROALE BOGATE
10

Sfat Pentru a face codul mai lizibil, puteți crea o enumerare care definește un nume pentru fiecare vizualizare. În acest fel, aveți posibilitatea să setați ActiveViewIndex utilizând numele descriptiv din enumerare, nu un număr obișnuit. Consultați capitolul 3 pentru o recapitulare a enumerărilor.

De asemenea, puteți utiliza metoda SetActiveView(), care acceptă oricare dintre obiectele de vizualizare pe care
le-ați creat. Acest lucru poate avea ca rezultat un cod mai lizibil (dacă ați ales coduri descriptive pentru comenzile de
vizualizare) și asigură detectarea erorilor mai devreme (la momentul compilării în loc de timpul de execuție).

MultiView1.SetActiveView(Vizualizare1);
Acest lucru vă oferă suficientă funcționalitate încât să puteți crea butoanele de navigare anterioare și următoare. Cu
toate acestea, depinde în continuare de dvs. să scrieți codul care verifică ce vizualizare este vizibilă și schimbă
vizualizarea. Acest cod este puțin mai simplu, deoarece nu mai trebuie să vă faceți griji cu privire la ascunderea
vizualizărilor, dar este încă mai puțin decât ideal.
Din fericire, MultiView include câteva inteligente încorporate care vă pot salva multe probleme. Iată cum funcționează:
MultiView recunoaște comenzile butoanelor cu nume de comenzi specifice. (Din punct de vedere tehnic, un control cu
buton este orice control care implementează interfața IButtonControl, inclusiv Button, ImageButton și LinkButton.) Dacă
adăugați un control buton la vizualizarea care utilizează unul dintre aceste nume de comenzi recunoscute, butonul
primește unele funcționalități automate. Folosind această tehnică, puteți crea butoane de navigare fără a scrie niciun cod.

Tabelul 10-6 listează toate numele de comenzi recunoscute. Fiecare nume de comandă are, de asemenea,
un câmp static corespunzător în clasa MultiView, astfel încât să puteți obține cu ușurință numele corect al
comenzii dacă alegeți să îl setați programatic.
Tabelul 10-6. Nume de comenzi recunoscute pentru MultiView

Nume comandă Câmp MultiView Descriere

Vizualizare anterioară AnteriorViewCommandName Se deplasează la vizualizarea anterioară.

Vizualizarea următoare NextViewCommandName Se deplasează la vizualizarea următoare.

SwitchViewByID SwitchViewByIDCommandName Se deplasează la vizualizarea cu un


anumit ID (nume șir). ID-ul este preluat
din proprietatea CommandArgument a
controlului buton.

SwitchViewByIndex SwitchViewByIndexCommandName Se deplasează la vizualizarea cu un


anumit index numeric. Indexul este
preluat din proprietatea
CommandArgument a controlului buton.

Pentru a încerca acest lucru, adăugați acest buton la prima vizualizare:

<asp:Button ID="Button1" runat="server" CommandArgument="View2"


CommandName="SwitchViewByID" text="Du-te la View2" />

331
CAPITOLUL CONTROALE BOGATE
10

Când faceți clic, acest buton setează MultiView pentru a afișa vizualizarea specificată de CommandArgument
(View2).
În loc să creați butoane care duc utilizatorul la o anumită vizualizare, este posibil să doriți un buton care să se
deplaseze înainte sau înapoi cu o vizualizare. Pentru aceasta, utilizați numele de comandă PrevView și
NextView. Iată un exemplu care definește butoanele anterior și următor din a doua vizualizare:
<asp:Button ID="Button1" runat="server" text="< prev" CommandName="PrevView" />
<asp:Button ID="Button2" runat="server" text="Next >" CommandName="NextView" />

După ce adăugați aceste butoane la vizualizarea dvs., puteți trece cu ușurință de la vizualizare la
vizualizare. Figura 10-7 prezintă exemplul anterior, a doua vizualizare fiind vizibilă în prezent.

Figura 10-7. Deplasarea de la o vizualizare la alta

332
CAPITOLUL CONTROALE BOGATE
10

Sfat: Aveți grijă câte vizualizări înghesuiți într-o singură pagină. Atunci când utilizați controlul MultiView, întregul model de control - inclusiv controalele din fiecare vizualizare - este creat în fiecare postback și persistă în starea de vizualizare. În cele mai multe situații, acest lucru nu va fi un factor semnificativ. Cu toate acestea, crește dimensiunea generală a paginii, mai ales dacă modificați controalele programatic (ceea ce crește cantitatea de informații pe care trebuie să le stocheze în starea de

vizualizare).

Controlul expertului
Controlul Expert este o versiune mai plină de farmec a controlului MultiView. De asemenea, acceptă afișarea uneia dintre
mai multe vizualizări simultan, dar include un comportament destul de încorporat, dar personalizabil, inclusiv butoane de
navigare, o bară laterală cu linkuri de pași, stiluri și șabloane.
De obicei, vrăjitorii reprezintă o singură sarcină, iar utilizatorul se mișcă liniar prin ele, trecând de la pasul curent la cel
imediat următor (sau cel imediat precedent în cazul unei corecții). Controlul ASP.NET Wizard acceptă, de asemenea,
navigarea neliniară, ceea ce înseamnă că vă permite să decideți să ignorați un pas pe baza informațiilor furnizate de
utilizator.
În mod implicit, controlul Expert furnizează butoane de navigare și o bară laterală cu linkuri pentru fiecare
pas din stânga. Puteți ascunde bara laterală setând proprietatea Wizard.DisplaySideBar la false. De obicei,
veți face acest pas dacă doriți să impuneți o navigare strictă pas cu pas și să împiedicați utilizatorul să sară
din secvență. Furnizați conținutul pentru fiecare pas utilizând orice controale HTML sau ASP.NET. Figura
10-8 arată regiunea în care puteți adăuga conținut la o instanță predefinită a expertului.

Figura 10-8. Regiunea pentru conținutul pasului

Pași de vrăjitor
Pentru a crea un expert în ASP.NET, pur și simplu definiți pașii și conținutul acestora utilizând etichetele
<asp:WizardStep>. Iată structura de bază pe care o veți utiliza:

333
CAPITOLUL CONTROALE BOGATE
10

<asp:Wizard ID="Wizard1" runat="server" ... >


<WizardSteps>

<asp:WizardStep runat="server" title="Pasul 1"> ...

</asp:WizardStep>

<asp:WizardStep runat="server" title="Pasul 2"> ...

</asp:WizardStep>

...
<WizardSteps> </asp:Wizard>

Puteți adăuga oricâte controale WizardStep doriți în interiorul expertului. Conceptual, WizardStep joacă același rol ca
vizualizarea într-o vizualizare multiplă (sau panoul de bază din primul exemplu pe care l-ați luat în considerare). Plasați
conținutul pentru fiecare pas în interiorul controlului WizardStep.
Înainte de a începe să adăugați conținutul la expert, merită să consultați tabelul 10-7, care prezintă câteva
informații de bază pe care le puteți defini pentru fiecare pas.

Tabelul 10-7. Proprietățile WizardStep

Proprietate Descriere

Titlu Numele descriptiv al pasului. Acest nume este utilizat pentru textul linkurilor din
Bara laterală.

StepType Tipul de pas, ca valoare din enumerarea WizardStepType. Această valoare


Stabilește tipul de butoane de navigare care vor fi afișate pentru acest pas. Alegeri
includ Start (afișează un buton Următorul), Pasul (afișează butoanele Următorul și Anterior), Terminare
(afișează butoanele Terminare și Anterior), Finalizat (nu afișează niciun buton și ascunde butoanele
bara laterală, dacă este activată) și Auto (tipul pasului este dedus din poziția din
colectare). Valoarea implicită este Auto, ceea ce înseamnă că primul pas este Start, ultimul pas este
Terminați și toți ceilalți pași sunt Pas.

AllowReturn Indică dacă utilizatorul poate reveni la acest pas. Dacă este fals, odată ce utilizatorul a trecut
Acest pas, utilizatorul nu va putea reveni. Linkul barei laterale pentru acest pas va avea
fără efect, iar butonul Anterior al pasului următor fie va sări peste acest pas, fie va fi
ascuns complet (în funcție de valoarea AllowReturn a pașilor precedenți).

Pentru a vedea cum funcționează acest lucru, luați în considerare un expert care utilizează din nou
exemplul GreetingCardMaker. Acesta ghidează utilizatorul prin patru pași. Primii trei pași permit
utilizatorului să configureze felicitarea, iar ultimul pas arată felicitarea generată.
<asp:Wizard ID="Wizard1" runat="server" ActiveStepIndex="0" BackColor="LemonChiffon"
BorderStyle="Groove" BorderWidth="2px" CellPadding="10">

<WizardSteps> <asp:WizardStep runat="server" title="Pasul 1 -


Culori"> Alegeți un prim-plan (text) culoare:<br />

334
CAPITOLUL CONTROALE BOGATE
10

<asp:DropDownList ID="lstForeColor" runat="server" /> <br />


Alegeți o culoare de fundal: <br /> <asp:DropDownList
ID="lstBackColor" runat="server" /> </asp:WizardStep>

<asp:WizardStep runat="server" title="Pasul 2 - fundal"> Alegeți un stil de


bordură:<br /> <asp:RadioButtonList ID="lstBorder" runat="server"
RepeatColumns="2" /> <br /><br /> <asp:CheckBox ID="chkPicture" runat="server"
text="Adăugați imaginea implicită" /> </asp:WizardStep>

<asp:WizardStep runat="server" title="Pasul 3 - text"> Alegeți


un nume de font:<br /> <asp:DropDownList ID="lstFontName"
runat="server" /> <br /><br /> Specificați o dimensiune font:
<br /> <asp:TextBox ID="txtFontSize" runat="server" /> <br
/><br /> Introduceți textul de salut de mai jos: <br />
<asp:TextBox ID="txtGreeting" runat="server"
TextMode="MultiLine" /> </asp:WizardStep>

<asp:WizardStep runat="server" StepType="Complete" title="Felicitare"> <asp:Panel


ID="pnlCard" runat="server" HorizontalAlign="Center"> <br /> <asp:Label
ID="lblGreeting" runat="server" /> <asp:Image ID="imgDefault" runat="server"
Visible="False" /> </asp:Panel> </asp:WizardStep> </WizardSteps>

</asp:Expert>

Dacă vă uitați cu atenție, veți găsi câteva diferențe față de pagina originală și exemplul bazat pe MultiView. În
primul rând, comenzile nu sunt setate să posteze automat înapoi. Acest lucru se datorează faptului că
felicitarea nu este redată până la pasul final, la încheierea vrăjitorului. (Veți afla mai multe despre cum să
gestionați acest eveniment în secțiunea următoare.) O altă modificare este că nu există butoane de navigare.
Acest lucru se datorează faptului că expertul adaugă automat aceste detalii pe baza tipului de pas. De
exemplu, veți primi un buton Următorul pentru primii doi pași, un buton Anteriorul pentru pașii 2 și 3 și un
buton Terminare pentru pasul 3. Pasul final, care afișează fișa completă, nu furnizează linkuri de navigare,
deoarece StepType este setat la Finalizare. Figura 10-9 prezintă pașii expertului.

335
CAPITOLUL CONTROALE BOGATE
10

Figura 10-9. Un vrăjitor cu patru pași

Spre deosebire de controlul MultiView, puteți vedea doar câte un pas la un moment dat în Visual Studio.
Pentru a alege pasul pe care îl proiectați în prezent, selectați-l din eticheta inteligentă, așa cum se arată în
Figura 10-10. Dar fiți avertizat - de fiecare dată când faceți acest lucru, Visual Studio modifică proprietatea
Wizard.ActiveStepIndex la pasul pe care îl alegeți. Asigurați-vă că setați acest lucru înapoi la 0 înainte de a
rula aplicația, astfel încât să înceapă la primul pas.

336
CAPITOLUL CONTROALE BOGATE
10

Figura 10-10. Proiectarea unui pas

Notă Rețineți că, atunci când adăugați controale la pași separați pe un expert, controalele sunt toate instanțiate și persistate în starea de vizualizare, indiferent de pasul care este afișat în prezent. Dacă trebuie să reduceți un expert complex, va trebui să îl împărțiți în pagini separate, să utilizați metoda Server.Transfer () pentru a trece de la o pagină la alta și să tolerați un model de programare mai puțin elegant.

Evenimente Wizard
Puteți scrie codul care stă la baza expertului răspunzând la mai multe evenimente (așa cum sunt
enumerate în tabelul 10-8).

Tabelul 10-8. Evenimente Wizard

Eveniment Descriere

ActiveStepChanged Apare atunci când controlul comută la un pas nou (fie pentru că utilizatorul a
făcut clic pe un buton de navigare, fie pentru că codul a modificat proprietatea
ActiveStepIndex).

AnulareButonFaceți Apare când se face clic pe butonul Anulare. Butonul Anulare nu este afișat în
clic pe mod implicit, dar îl puteți adăuga la fiecare pas setând proprietatea
Wizard.DisplayCancelButton. De obicei, un buton Anulare iese din expert. Dacă
nu aveți niciun cod de curățare de efectuat, trebuie doar să setați proprietatea
CancelDestinationPageUrl, iar expertul se va ocupa automat de redirecționare.

337
CAPITOLUL CONTROALE BOGATE
10

Eveniment Descriere

FinishButtonClick Apare când se face clic pe butonul Terminare.

NextButtonFaceți clic și Apare atunci când se face clic pe butonul Următor sau Anterior în orice pas. Însă
AnteriorButonFaceți clic pe Deoarece există mai multe moduri de a trece de la un pas la altul, este
adesea mai ușor de gestionat evenimentul ActiveStepChanged.

Bară lateralăButonFaceți clic pe Apare atunci când se face clic pe un buton din zona barei laterale.

În ansamblu, există două modele de programare wizard:

Committ-as-you-go: Acest lucru are sens dacă fiecare pas al vrăjitorului încheie o operație atomică care nu poate fi
inversată. De exemplu, dacă procesați o comandă care implică o autorizare a cardului de credit urmată de o
achiziție finală, nu puteți permite utilizatorului să facă un pas înapoi și să editeze numărul cardului de credit. Pentru
a accepta acest model, setați proprietatea AllowReturn la fals pe unii sau toți pașii. De asemenea, se recomandă
să răspundeți la evenimentul ActiveStepChanged pentru a efectua modificări pentru fiecare pas.
Descărcați de la Wow! eBook <www.wowebook.com>

Committ-at-the-end: Acest lucru are sens dacă fiecare pas al expertului colectează informații pentru o
operațiune care se efectuează numai la sfârșit. De exemplu, dacă colectați informații despre utilizator și
intenționați să generați un cont nou după ce aveți toate informațiile, probabil că veți permite unui
utilizator să facă modificări la jumătatea procesului. Executați codul pentru generarea noului cont atunci
când expertul se termină reacționând la evenimentul FinishButtonClick.
Pentru a implementa commit-at-the-end cu exemplul curent, trebuie doar să răspundeți la evenimentul
FinishButtonClick. De exemplu, pentru a implementa expertul pentru felicitări, trebuie doar să răspundeți la
acest eveniment și să apelați UpdateCard(), metoda privată care reîmprospătează felicitarea:
protejat void Wizard1_FinishButtonClick(expeditor obiect,
WizardNavigationEventArgs e)
{
UpdateCard();
}

Pentru codul complet pentru metoda UpdateCard(), care generează felicitarea, consultați capitolul 6 (sau
consultați exemplul de cod descărcabil).
Dacă decideți să utilizați modelul commit-as-you go, răspundeți la evenimentul ActiveStepChanged și
apelați UpdateCard() în acel moment pentru a reîmprospăta cardul de fiecare dată când utilizatorul trece de
la un pas la altul. Acest lucru presupune că felicitarea este întotdeauna vizibilă. (Cu alte cuvinte, nu este
conținut în pasul final al vrăjitorului.) Modelul commit-as-you-go este similar cu exemplul anterior care a
utilizat MultiView.

Formatarea expertului
Fără îndoială, cel mai mare punct forte al controlului vrăjitorului este modul în care vă permite să-i personalizați
aspectul. Aceasta înseamnă că dacă doriți modelul de bază (un proces în mai mulți pași cu butoane de navigare și
diverse evenimente), nu sunteți blocat în interfața de utilizator implicită.
În funcție de cât de radical doriți să schimbați expertul, aveți mai multe opțiuni. Pentru modificări mai puțin dramatice,
puteți seta diferite proprietăți de nivel superior ale controlului expertului. De exemplu, puteți controla culorile, fonturile,

338
CAPITOLUL CONTROALE BOGATE
10

spațierea și stilul bordurii, așa cum puteți face cu orice control ASP.NET. De asemenea, puteți modifica aspectul fiecărui
buton. De exemplu, pentru a modifica butonul Următorul, puteți utiliza următoarele proprietăți: StepNextButtonType
(utilizați un buton, un link sau o imagine pe care se poate face clic), StepNextButtonText (particularizați textul pentru un
buton sau un link), StepNextButtonImageUrl (setați imaginea pentru un buton imagine) și StepNextButtonStyle (utilizați un
stil dintr-o foaie de stiluri). De asemenea, puteți adăuga un antet utilizând proprietatea HeaderText.
Mai mult control este disponibil prin stiluri. Puteți utiliza stiluri pentru a aplica opțiuni de formatare la diverse porțiuni ale
controlului expert, la fel cum puteți utiliza stiluri pentru a formata părți ale controalelor de date îmbogățite, cum ar fi
GridView. Tabelul 10-9 listează toate stilurile pe care le puteți utiliza. Ca și în cazul altor controale bazate pe stil, setările
de stil mai specifice (cum ar fi SideBarStyle) suprascriu setările de stil mai generale (cum ar fi ControlStyle) atunci când
intră în conflict. În mod similar, StartNextButtonStyle suprascrie NavigationButtonStyle la primul pas.

Tabelul 10-9. Stiluri de vrăjitori

Stil Descriere

ControlStil Se aplică tuturor secțiunilor controlului expert.

Stil antet Se aplică secțiunii antet a expertului, care este vizibilă numai dacă setați
un text în proprietatea HeaderText.

BorderStyle Se aplică bordurii din jurul controlului expert. Îl puteți utiliza împreună
cu proprietățile BorderColor și BorderWidth.

SideBarStyle Se aplică în zona barei laterale a expertului.

Bară lateralăButtonStil Se aplică doar butoanelor din bara laterală.

StepStyle Se aplică secțiunii controlului în care definiți conținutul pasului.

Stil de navigare Se aplică în zona de jos a comenzii unde sunt afișate butoanele de
navigare.

NavigationButtonStyle Se aplică doar butoanelor de navigare din zona de navigare.

StartNextButtonStyle Se aplică butonului de navigare Next la primul pas (când StepType este
Start).

StepNextButtonStyle Se aplică butonului de navigare Next pe pașii intermediari (când


StepType este Step).

StepPreviousButtonStyle Se aplică butonului de navigare anterior la pașii intermediari (când


StepType este Step).

FinishPreviousButtonStyle Se aplică butonului de navigare Anterior la ultimul pas (când


StepType este Terminare).

FinishCompleteButtonStyle Se aplică butonului de navigare Finalizare la ultimul pas (când


StepType este Terminare).

CancelButtonStyle Se aplică butonului Revocare, dacă aveți Wizard.DisplayCancelButton


setat la true.

339
CAPITOLUL CONTROALE BOGATE
10

• Notă Controlul Expert acceptă, de asemenea, șabloane, care vă oferă o abordare mai radicală a
formatării. Dacă nu puteți obține nivelul de particularizare dorit prin proprietăți și stiluri, puteți utiliza
șabloane pentru a defini complet aspectul fiecărei secțiuni a controlului expert, inclusiv anteturile și
linkurile de navigare. Modelele necesită expresii de legare a datelor și sunt discutate în capitolele
15 și 16.

Validarea cu expertul
Evenimentele FinishButtonClick, NextButtonClick, PreviousButtonClic și SideBarButtonClick pot fi
anulate. Aceasta înseamnă că puteți utiliza un astfel de cod pentru a împiedica desfășurarea acțiunii de
navigare solicitate:
protejat void Wizard1_NextButtonClick(expeditorul obiectului,
WizardNavigationEventArgs e) { // Efectuați un fel de
verificare.
dacă (e.NextStepIndex == 1 &&; txtName.Text == "")
{
Anulați navigarea și afișați un mesaj în altă parte a paginii. e.Cancel = true;
lblInfo.Text = "Nu puteți trece la pasul următor până când nu furnizați
numele dvs.";
}
}

Aici codul verifică dacă utilizatorul încearcă să treacă la pasul 1 utilizând proprietatea NextStepIndex.
(Alternativ, puteți examina pasul curent utilizând proprietatea CurrentStepIndex.) Dacă da, codul verifică apoi
o casetă de text și anulează navigarea dacă nu conține niciun text, menținând utilizatorul la pasul curent.
Scrierea acestui tip de logică devine puțin dificilă, deoarece trebuie să rețineți că navigarea pas cu pas poate
fi efectuată în mai multe moduri. Pentru a vă simplifica viața, puteți scrie o rutină de tratare a evenimentelor
care se ocupă de evenimentele NextButtonClick, PreviousButtonClick și SideBarButtonClick și efectuează
aceeași verificare. Ați văzut această tehnică în capitolul 6 cu GreetingCardMaker.

• Notă De asemenea, aveți posibilitatea să utilizați fără nicio problemă controalele de validare
ASP.NET dintr-un expert. În cazul în care controalele de validare detectează date nevalide,
acestea vor împiedica utilizatorul să facă clic pe oricare dintre linkurile din bara laterală (pentru a
trece la un alt pas) și vor împiedica utilizatorul să continue făcând clic pe butonul Următorul. Cu
toate acestea, în mod implicit, butonul Anterior are proprietatea CausesValidation setată la false,
ceea ce înseamnă că utilizatorului i se va permite să revină la pasul anterior.

340
CAPITOLUL 10
CONTROALE COMPLEXE

Ultimul cuvânt
Acest capitol v-a arătat cum controalele îmbogățite Calendar, AdRotator, MultiView și Wizard pot depăși cu
mult limitările elementelor HTML obișnuite. Când lucrați cu aceste controale, nu trebuie să vă gândiți deloc
la HTML. În schimb, vă puteți concentra asupra modelului de obiect definit de control.
De-a lungul acestei cărți, veți lua în considerare mai multe exemple de controale bogate și veți învăța cum
să le utilizați pentru a crea aplicații web bogate care sunt o lume diferită de elementele de bază HTML.
Unele dintre cele mai interesante controale bogate care sunt încă în față includ controalele de navigație
(capitolul 13), controalele de date (capitolul 16) și controalele de securitate (capitolul 20).

Sfat
Ați putea fi, de asemenea, interesat să adăugați controale terță parte pe site-urile dvs. Internetul conține multe hub-uri pentru partajarea controlului. O astfel de locație este propriul http://www.asp.net al Microsoft, care oferă o galerie de control unde dezvoltatorii își pot trimite propriile controale web ASP.NET. Unele dintre aceste controale sunt gratuite (cel puțin într-o versiune limitată), iar altele necesită o achiziție.


C A P I T O L U L 11

•■■

Controale utilizator și grafică

În acest capitol, veți lua în considerare două modalități de a vă extinde paginile web cu o altă crestătură.
În primul rând, veți aborda controalele utilizatorului, care vă oferă o modalitate eficientă de a reutiliza un bloc de marcaj al
interfeței cu utilizatorul - și codul care îl însoțește. Controalele utilizatorilor sunt un instrument cheie pentru construirea de
aplicații web modulare. De asemenea, vă pot ajuta să creați modele consecvente de site-uri web și să vă reutilizați munca
grea. Apoi, veți explora desenul personalizat cu GDI +. Veți vedea cum puteți picta exact imaginea de care aveți nevoie la
cerere. Veți învăța, de asemenea, cel mai bun mod de a încorpora aceste imagini în paginile dvs.

Controale utilizator
O aplicație web bine construită își împarte activitatea în blocuri discrete, independente. Cu cât aplicația web este mai
modulară, cu atât este mai ușor să vă mențineți codul, să depanați problemele și să reutilizați biții cheie de
funcționalitate.
Deși este destul de ușor să reutilizați codul (trebuie pur și simplu să îl scoateți din paginile dvs. și să îl puneți în clase
separate), nu este la fel de simplu să reutilizați marcarea paginilor web. Puteți tăia și lipi blocuri de HTML și ASP.NET
etichete de control, dar acest lucru provoacă dureri de cap nesfârșite dacă doriți să vă schimbați marcajul mai târziu. În
schimb, aveți nevoie de o modalitate de a înfășura marcarea paginii web într-un pachet reutilizabil, la fel cum puteți
înfășura codul obișnuit C #. Trucul este de a crea un control al utilizatorului.
Controalele utilizatorului arată aproape la fel ca ASP.NET formularele web. La fel ca formularele web, acestea sunt
compuse dintr-o porțiune de marcare cu etichete HTML și de control (fișierul .ascx) și pot utiliza opțional un fișier de cod în
spatele cu logică de gestionare a evenimentelor. De asemenea, pot include aceeași gamă de conținut HTML și controale
ASP.NET și se confruntă cu aceleași evenimente ca obiectul Pagină (cum ar fi Încărcare și Prerandare). Singurele
diferențe dintre controalele utilizatorului și paginile web sunt următoarele:
• Controalele utilizatorului utilizează extensia de fișier .ascx în loc de .aspx, iar fișierele lor din
spatele codului moștenesc de la clasa System.Web.UI.UserControl. De fapt, clasa UserControl
și clasa Page moștenesc ambele de la aceleași clase de bază, motiv pentru care împărtășesc
atât de multe dintre aceleași metode și evenimente, așa cum se arată în diagrama moștenirii din
Figura 11-1.
• Fișierul .ascx pentru un control utilizator începe cu o directivă <%@ Control %> în loc de
o directivă <%@ Page %>.
• Comenzile utilizatorului nu pot fi solicitate direct de un browser web. În schimb, acestea
trebuie să fie încorporate în alte pagini web.

343
CAPITOLUL CONTROALE UTILIZATOR ȘI GRAFICĂ
11

Figura 11-1. Lanțul de moștenire Page și UserControl

Crearea unui control simplu al utilizatorului


Aveți posibilitatea să creați un control de utilizator în Visual Studio în același mod în care adăugați o pagină web. Doar selectați site-ul web
→ Adăugare element nou și alegeți Control utilizator web din listă.
Următorul control utilizator conține un singur control etichetă:

<%@ Control Language="C#" AutoEventWireup="true"


CodeFile="Footer.ascx.cs" mosteneste="footer" %>

<asp:Label id="lblFooter" runat="server" />


Rețineți că directiva Control utilizează aceleași atribute utilizate în directiva Pagină pentru o pagină web, inclusiv
Limbă, AutoEventWireup și Moștenire.
Clasa din spatele codului pentru acest exemplu de control al utilizatorului este la fel de simplă.
Utilizează evenimentul UserControl.Load pentru a adăuga text la etichetă:

public clasa parțială subsol : System.Web.UI.UserControl


{
protejat void Page_Load(Obiect expeditor, EventArgs e)
{
lblFooter.Text = "Aceasta pagina a fost servita la ";
lblFooter.Text += DateTime.Now.ToString();
}
}

344
CAPITOLUL 11 CONTROALE
UTILIZATOR ȘI GRAFICĂ

Pentru a testa acest control al utilizatorului, trebuie să îl inserați într-o pagină web. Acesta este un proces în
două etape. În primul rând, trebuie să adăugați o directivă Înregistrare la pagina care va conține controlul
utilizatorului. Plasați directiva Înregistrare imediat după directiva Pagină. Directiva Registru identifică
controlul pe care doriți să îl utilizați și îl asociază cu un prefix unic de control, după cum se arată aici:
<%@ Register TagPrefix="apress" TagName="Footer" src="Footer.ascx" %>
Directiva privind registrul specifică un prefix și un nume de etichetă. Prefixele etichetelor grupează seturi de controale asociate
(de exemplu, toate controalele web ASP.NET utilizează prefixul etichetei asp). Prefixele etichetelor sunt de obicei minuscule -
din punct de vedere tehnic, nu sunt sensibile la litere mari și mici - și ar trebui să fie unice pentru compania sau organizația
dvs. Directiva Src identifică locația fișierului șablon de control al utilizatorului, nu fișierul din spatele codului.
În al doilea rând, acum puteți adăuga controlul utilizatorului oricând doriți (și de câte ori doriți) în pagină,
introducând eticheta de control. Luați în considerare acest exemplu de pagină:

<%@ Page Language="C#" AutoEventWireup="true"


CodeFile="FooterHost.aspx.cs" mosteneste="footerHost"%> <%@ Register
TagPrefix="apress" TagName="footer" src="footer.ascx" %>
<! DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML
1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"> <head


runat="server"> <title>footer host</title> </head> <body>
<form id="form1" runat="server"> <div> <h1>o pagină cu
subsol</h1><hr /> pagină statică text<br /><br /> <apress:
Footer id="Footer1" runat="server" /> </div> </form>
</body> </html>

Acest exemplu (prezentat în Figura 11-2) demonstrează o modalitate simplă prin care puteți crea un antet
sau un subsol și îl puteți reutiliza în toate paginile de pe site-ul dvs. web doar adăugând un control de
utilizator. În cazul subsolului simplu, nu veți salva mult cod. Cu toate acestea, această abordare va deveni
mult mai utilă pentru un control complex cu formatare extinsă sau mai multe controale conținute.
CAPITOLUL CONTROALE UTILIZATOR ȘI GRAFICĂ
11

Figura 11-2. O pagină cu un subsol de control utilizator

Desigur, acest lucru zgârie doar suprafața a ceea ce puteți face cu un control al utilizatorului. În secțiunile
următoare, veți învăța cum să îmbunătățiți un control cu proprietăți, metode și evenimente -
transformându-l dintr-un simplu "includeți fișierul" într-un obiect cu drepturi depline.

Notă Clasa Page furnizează o metodă LoadControl() care vă permite să creați un control de utilizator dinamic la momentul rulării dintr-un fișier .ascx. Controlul utilizatorului vă este returnat ca obiect de control, pe care îl puteți adăuga apoi la colecția Controale a unui control container de pe pagina web (cum ar fi Substituent sau Panou) pentru a-l afișa pe pagină. Această tehnică nu este un substitut bun pentru utilizarea declarativă a unui control al utilizatorului, deoarece este mai complexă. Cu toate acestea, are

câteva aplicații interesante dacă doriți să generați dinamic o interfață cu utilizatorul.

În Visual Studio, aveți o comandă rapidă utilă pentru adăugarea unui control de utilizator la o pagină fără a tasta manual
directiva Înregistrare. Începeți prin a deschide pagina web pe care doriți să o utilizați. Apoi, găsiți fișierul .ascx pentru
controlul utilizatorului în Exploratorul de soluții. Glisați-l din Exploratorul de soluții și fixați-l în zona de design vizual a
formularului web (nu în zona de vizualizare sursă). Visual Studio va adăuga automat directiva Înregistrare pentru controlul
utilizatorului, precum și o instanță a etichetei de control utilizator.
O altă opțiune este să configurați controlul utilizatorului în fișierul web.config pentru aplicația dvs. Iată un
exemplu care înregistrează controlul subsol în acest fel:

<configuration> <system.web> <pages> <controls> <add tagPrefix="apress"


src="~Footer.ascx" tagName="Footer" /> </controls> </pages> </system.web>
</configuration>

346
CAPITOLUL CONTROALE UTILIZATOR ȘI GRAFICĂ
11

Puteți adăuga mai multe elemente <adăugați> la secțiunea <controale> pentru a înregistra câte controale de
utilizator doriți.
Când faceți acest pas, controlul utilizatorului este disponibil în fiecare pagină, fără a fi necesare directive de
înregistrare. Visual Studio va respecta, de asemenea, configurația dvs., astfel încât, dacă plasați un control
de utilizator pe o pagină, acesta va utiliza prefixul etichetei definite și nu va adăuga o directivă Înregistrare.

Controale independente ale utilizatorului


Conceptual, există două tipuri de controale ale utilizatorilor: independente și integrate. Controalele
independente ale utilizatorului nu interacționează cu restul codului din formular. Controlul utilizatorului subsol
este un astfel de exemplu. Un alt exemplu ar putea fi un control LinkMenu care conține o listă de butoane
care oferă linkuri către alte pagini. Acest control al utilizatorului LinkMenu poate gestiona evenimentele
pentru toate butoanele și apoi poate rula codul Response.Redirect () corespunzător pentru a trece la o altă
pagină web. Sau poate fi doar un control obișnuit HyperLink care nu are niciun cod asociat pe partea de
server. Fiecare pagină de pe site poate include apoi același control al utilizatorului LinkMenu, permițând
navigarea nedureroasă pe site, fără a fi nevoie să vă faceți griji cu privire la cadre.

Notă Puteți utiliza controalele de navigare mai bogate în funcții pentru a furniza navigarea pe site-ul web. Crearea propriilor controale personalizate vă oferă o abordare mai flexibilă (și mai obositoare) pentru furnizarea navigației. Este foarte probabil să utilizați controale personalizate, mai degrabă decât o hartă întreagă a site-ului, pentru o navigare simplă între câteva pagini.

Următorul exemplu definește un control simplu care prezintă o listă de linkuri formatată atractiv. Rețineți că
atributul stil al etichetei <div> (care definește fonturile și formatarea) a fost omis pentru claritate.

<%@ Control Language="C#" AutoEventWireup="true"


CodeFile="LinkMenu.ascx.cs" mosteneste="LinkMenu" %> <div>
Produse:<br /> <asp:HyperLink id="lnkBooks" runat="server"
NavigateUrl="MenuHost.aspx?product=Books">Books
</asp:HyperLink><br /> <asp:HyperLink id="lnkToys" runat="server"
NavigateUrl="MenuHost.aspx?product=Toys">Toys
</asp:HyperLink><br /> <asp: HyperLink id="lnkSports"
runat="server"
NavigateUrl="MenuHost.aspx?product=Sports">Sports
</asp:HyperLink><br /> <asp:HyperLink id="lnkFurniture"
runat="server"
NavigateUrl="MenuHost.aspx?product=Furniture">Furniture
</asp:HyperLink> </div>

347
CAPITOLUL 11 CONTROALE UTILIZATOR ȘI GRAFICĂ

Linkurile nu declanșează de fapt niciun cod pe partea de server - în schimb, se redau ca etichete ancoră HTML
obișnuite cu o adresă URL codificată hard.
Pentru a testa acest meniu, puteți utiliza următoarea pagină web MenuHost.aspx. Acesta include două
controale: controlul Meniu și un control Etichetă care afișează parametrul șir de interogare produs. Ambele
sunt poziționate folosind o masă.
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="MenuHost.aspx.cs"
mostenit="MenuHost"%> <%@ Register TagPrefix="apress" TagName="LinkMenu"
src="LinkMenu.ascx" %>
<! DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML
1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server">


<title>Menu Host</title> </head> <body> <form id="form1" runat="server">
<div> <table> <tr> <td><apress:LinkMenu id="Menu1" runat="server"
/></td> <td><asp: Label id="lblSelection" runat="server" /></td> </tr>
</table> </div> </form> </body> </html>

Când pagina MenuHost.aspx se încarcă, adaugă informațiile corespunzătoare la


controlul lblSelection:

protejat void Page_Load(Object sender, EventArgs e) { if (Request.Params["product"] != null) {


lblSelection.Text = "You chose: "; lblSelection.Text += Request.Params["produs"]; } }

Figura 11-3 prezintă rezultatul final. Ori de câte ori faceți clic pe un buton, pagina este postată înapoi și
textul este actualizat.

348
CAPITOLUL CONTROALE UTILIZATOR ȘI GRAFICĂ
11

Figura 11-3. Controlul utilizatorului LinkMenu

Puteți utiliza controlul LinkMenu pentru a repeta același meniu pe mai multe pagini. Acest lucru este deosebit
de util într-o situație în care nu puteți utiliza paginile coordonatoare pentru a standardiza aspectul (posibil
pentru că paginile sunt prea diferite).

Controale integrate ale utilizatorului


Controalele integrate ale utilizatorului interacționează într-un fel sau altul cu pagina web care le găzduiește. Când proiectați
aceste controale, sfaturile de proiectare bazate pe clasă pe care le-ați învățat în capitolul 4 devin cu adevărat utile. Un
exemplu tipic este un control al utilizatorului care permite un anumit nivel de configurare prin proprietăți. De exemplu, puteți
crea un subsol care acceptă două formate de afișare diferite: dată lungă și timp scurt. Pentru a adăuga un nivel suplimentar de
rafinament, controlul utilizatorului Subsol permite paginii web să specifice formatul de afișare corespunzător utilizând o
enumerare.
Primul pas este să creați o enumerare în clasa particularizată Footer. Rețineți că o enumerare este pur și
simplu un tip de constantă care este stocată intern ca întreg, dar este setată în cod utilizând unul dintre
numele permise pe care le specificați. Variabilele care utilizează enumerarea FooterFormat pot lua valoarea
FooterFormat.LongDate sau FooterFormat.ShortTime:
enum public FooterFormat
{
LongDate,
ShortTime
}

Următorul pas este să adăugați o proprietate la clasa Subsol care permite paginii web să regăsească sau
să seteze formatul curent aplicat subsolului. Formatul real este stocat într-o variabilă privată numită format,
care este setată implicit la formatul de dată lungă atunci când controlul este creat pentru prima dată. (Dacă
sunteți neclar cu privire la modul în care funcționează procedurile de proprietate, nu ezitați să consultați
explicația din capitolul 3.)

349
CAPITOLUL CONTROALE UTILIZATOR ȘI GRAFICĂ
11

format privat FooterFormat = FooterFormat.LongDate;

public FooterFormat Format {


get { return format; } set {
format = valoare; } }

În cele din urmă, rutina de tratare a evenimentelor UserControl.Load trebuie să țină cont de starea curentă
a subsolului și să formateze ieșirea în consecință. Următorul este codul complet al clasei Footer:

clasa publică parțială Footer : System.Web.UI.UserControl {


public enum FooterFormat { LongDate, ShortTime }

format privat FooterFormat = FooterFormat.LongDate; public


FooterFormat Format { get { return format; } set { format =
value; } }

protejat void Page_Load(Object sender, EventArgs e) {


lblFooter.Text = "Aceasta pagina a fost servita la ";

if (format == FooterFormat.LongDate) { lblFooter.Text +=


DateTime.Now.ToLongDateString(); } else if (format ==
FooterFormat.ShortTime) { lblFooter.Text +=
DateTime.Now.ToShortTimeString(); }

}
}

Pentru a testa acest subsol, trebuie să creați o pagină care modifică proprietatea Format a controlului
utilizator Subsol. Figura 11-4 prezintă un exemplu de pagină, care setează automat proprietatea Format
pentru controlul utilizatorului pentru a se potrivi cu o selecție a butonului radio ori de câte ori pagina este
postată înapoi.

350
CAPITOLUL CONTROALE UTILIZATOR ȘI GRAFICĂ
11

Figura 11-4. Subsolul modificat

Rețineți că proprietatea de control utilizator este modificată în rutina de tratare a evenimentelor Page.Load, nu
în rutina de tratare a evenimentelor cmdRefresh.Click. Motivul este că evenimentul Încărcare are loc înainte ca
controlul utilizatorului să fie redat de fiecare dată când este creată pagina. Evenimentul Click are loc după ce
controlul utilizatorului a fost redat și, deși modificarea proprietății este vizibilă în codul dvs., aceasta nu
afectează ieșirea HTML a controlului utilizatorului, care a fost deja adăugată la pagină.

public clasa partiala FooterHost : System.Web.UI.Page { protejat void Page_Load(Object sender, EventArgs e)

{
dacă (optLong.Checked) { Footer1.Format =
Footer.FooterFormat.LongDate; } else if (optShort.Checked) {
Footer1.Format = Footer.FooterFormat.ShortTime; } else { // Se
va aplica valoarea implicită din clasa Footer. }

}}

351
CAPITOLUL CONTROALE UTILIZATOR ȘI GRAFICĂ
11

De asemenea, puteți seta aspectul inițial al subsolului în eticheta de control:

<apress:Footer Format="ShortTime" id="Footer1" runat="server" />


Ca și în cazul tuturor marcajelor de control web, ASP.NET convertește șiruri de caractere (cum ar fi
"ShortTime") la valoarea de enumerare corespunzătoare fără probleme.

Evenimente de control al utilizatorului


Un alt mod în care comunicarea poate avea loc între un control al utilizatorului și o pagină web este prin evenimente.
Cu metode și proprietăți, controlul utilizatorului reacționează la o modificare făcută de codul paginii web. Cu
evenimente, povestea este inversată: controlul utilizatorului notifică pagina web despre o acțiune, iar codul paginii web
răspunde.
Crearea unui control web care utilizează evenimente este destul de ușoară. În exemplul următor, veți vedea o versiune a
controlului LinkMenu care utilizează evenimente. În loc să navigheze direct la pagina corespunzătoare atunci când
utilizatorul face clic pe un buton, controlul ridică un eveniment, pe care pagina web poate alege să îl gestioneze.

Primul pas pentru a crea acest control este definirea evenimentelor. Amintiți-vă, pentru a defini un eveniment, trebuie mai
întâi să alegeți o semnătură de eveniment. Standardul .NET pentru evenimente specifică faptul că fiecare eveniment
trebuie să utilizeze doi parametri. Primul oferă o referință la controlul care a trimis evenimentul, în timp ce al doilea
încorporează orice informații suplimentare. Aceste informații suplimentare sunt împachetate într-un obiect EventArgs
particularizat, care moștenește de la clasa System.EventArgs. (Dacă evenimentul dvs. nu necesită informații suplimentare,
puteți utiliza doar clasa EventArgs predefinită, care nu conține date suplimentare. Multe evenimente din ASP.NET, cum ar fi
Page.Load sau Button.Click, urmează acest model.) Puteți consulta capitolul 4 pentru o prezentare generală rapidă a
modului de utilizare a evenimentelor în .NET.
Controlul LinkMenu2 utilizează un singur eveniment, care indică când se face clic pe un link:

clasa publică parțială LinkMenu2 : System.Web.UI.UserControl


{
eveniment public EventHandler LinkClicked;
...
}

Acest cod definește un eveniment numit LinkClicked. Evenimentul LinkClicked are semnătura specificată de
delegatul System.EventHandler, care include doi parametri - expeditorul evenimentului și un obiect EventArgs
obișnuit. Aceasta înseamnă că orice rutină de tratare a evenimentelor pe care o creați pentru a gestiona
evenimentul LinkClicked trebuie să arate astfel:
vid protejat LinkMenu_LinkClicked(expeditor obiect, EventArgs e) { ... }

Acest lucru are grijă de definirea evenimentului, dar cum rămâne cu ridicarea acestuia? Această parte este ușoară. Pentru a
declanșa evenimentul, controlul LinkMenu2 pur și simplu apelează evenimentul după nume și trece în cei doi parametri,
astfel:
Ridicați evenimentul LinkClicked, trecând o referință la // obiectul curent
(expeditorul) și un obiect EventArgs gol. LinkClicked(acest,
EventArgs.Empty);
Controlul LinkMenu2 are nevoie de fapt de câteva modificări. Versiunea originală a folosit controlul HyperLink. Acest lucru nu va
funcționa, deoarece controlul HyperLink nu declanșează un eveniment atunci când se face clic pe link. În schimb, va trebui să

352
CAPITOLUL CONTROALE UTILIZATOR ȘI GRAFICĂ
11

utilizați LinkButton. LinkButton declanșează evenimentul Clic, pe care controlul LinkMenu2 îl poate intercepta, apoi ridică
evenimentul LinkClicked la pagina web. Următorul este codul complet de control al utilizatorului:

clasa publică parțială LinkMenu2 : System.Web.UI.UserControl {


eveniment public EventHandler LinkClicked;

protejat void lnk_Click(expeditor obiect, EventArgs e) { // S-a făcut clic pe unul dintre controalele LinkButton. // Ridicați un
eveniment pe pagină.

if (LinkClicked != null) { LinkClicked(this,


EventArgs.Empty);

}}

}
Observați că înainte de a ridica evenimentul LinkClicked, controlul LinkMenu2 trebuie să testeze evenimentul
LickedClick pentru o referință nulă. Există o referință nulă dacă nu sunt atașate rutine de tratare a evenimentelor. În
acest caz, nu ar trebui să încercați să ridicați evenimentul, deoarece ar provoca doar o eroare. Puteți crea o pagină
care utilizează controlul LinkMenu2 și puteți adăuga o rutină de tratare a evenimentelor. Din păcate, nu veți putea
conecta aceste rutine de tratare a evenimentelor utilizând fereastra Visual Studio Properties, deoarece fereastra
Proprietăți nu va afișa evenimentele particularizate furnizate de controlul utilizatorului. În schimb, va trebui să
modificați direct eticheta LinkMenu2, după cum se arată aici:
<apress:LinkMenu2 id="Menu1" runat="server" OnLinkClicked="LinkClicked" />
Iată rutina de tratare a evenimentelor care răspunde în pagina web:

vid protejat LinkClicked(expeditor obiect, EventArgs e) {


lblClick.Text = "Clic detectat."; }

Notă
• Nu veți putea crea rutine de tratare a evenimentelor de control al utilizatorului prin fereastra
Proprietăți Visual Studio. În schimb, va trebui să introduceți manual rutina de tratare a
evenimentelor.

Figura 11-5 prezintă rezultatul.

353
CAPITOLUL CONTROALE UTILIZATOR ȘI GRAFICĂ
11

Figura 11-5. Utilizarea controlului utilizatorului LinkMenu2

Conceptual, această abordare ar trebui să ofere paginii dvs. web mai multă putere pentru a personaliza
modul în care funcționează controlul utilizatorului. Din păcate, nu este cazul în acest moment, deoarece
lipsește o informație cheie. Când are loc evenimentul LinkClicked, pagina web nu are nicio modalitate de a ști
pe ce link s-a făcut clic, ceea ce o împiedică să ia orice fel de acțiune rezonabilă. Singura modalitate de a
rezolva această problemă este de a crea un eveniment mai inteligent care poate transmite unele informații
prin argumente de eveniment. Veți vedea cum în secțiunea următoare.

Transmiterea informațiilor cu evenimente


În exemplul curent LinkMenu2, nu sunt transmise informații personalizate împreună cu evenimentul. În multe cazuri,
totuși, doriți să transmiteți informații suplimentare care se referă la eveniment. Pentru a face acest lucru, trebuie să
creați o clasă personalizată care derivă din EventArgs.
Clasa LinkClickedEventArgs care urmează permite controlului utilizatorului LinkMenu2 să transmită adresa URL
selectată de utilizator printr-o proprietate URL. De asemenea, oferă o proprietate Anulare. Dacă este setat la true,
controlul utilizatorului va opri imediat procesarea. Dar dacă opțiunea Anulare rămâne falsă (valoarea implicită),
controlul utilizatorului va trimite utilizatorul la noua pagină. În acest fel, controlul utilizatorului se ocupă în continuare
de sarcina de redirecționare a utilizatorului, dar permite paginii web să se conecteze la acest proces și să îl schimbe
sau să îl oprească (de exemplu, dacă a rămas o lucrare neterminată pe pagina curentă).

clasa publica LinkClickedEventArgs : EventArgs


{
șir public URL {get; set;} bool
public Anulați {obțineți; setați;}

link publicClickedEventArgs(url șir)


{
URL = url;
}
}

354
CAPITOLUL CONTROALE UTILIZATOR ȘI GRAFICĂ
11

Pentru a utiliza această clasă particularizată EventArgs, trebuie să modificați definiția evenimentului
LinkClicked astfel încât să utilizeze obiectul LinkClickedEventArgs:
eveniment public EventHandler<LinkClickedEventArgs> LinkClicked;
Magia genericelor este cea care face ca acest lucru să funcționeze. În esență, configurați clasa generică
EventHandler pentru a utiliza clasa EventArgs dorită - în acest caz, LinkClickedEventArgs. Apoi, codul de control al
utilizatorului pentru ridicarea evenimentului trebuie să trimită informațiile necesare atunci când apelați evenimentul.
Dar cum determină controlul utilizatorului pe ce link s-a făcut clic? Trucul este să comutați de la evenimentul
LinkButton.Click la evenimentul LinkButton.Command. Evenimentul Comandă primește automat argumentul
CommandArgument definit în etichetă. Deci, dacă definiți comenzile LinkButton astfel:
<asp:LinkButton ID="lnkBooks" runat="server"
CommandArgument="Menu2Host.aspx?product=Books"
OnCommand="lnk_Command">Books </asp:LinkButton><br /> <asp:LinkButton
ID="lnkToys" runat="server" CommandArgument="Menu2Host.aspx?product=Toys"
OnCommand="lnk_Command">Toys </asp:LinkButton><br /> <asp:LinkButton
ID="lnkSports" runat="server" CommandArgument="Menu2Host.aspx?product=Sports"
OnCommand="lnk_Command">Sports </asp: LinkButton><br /> <asp:LinkButton
ID="lnkFurniture" runat="server" CommandArgument="Menu2Host.aspx?product=Furniture"
OnCommand="lnk_Command"> Furniture</asp:LinkButton>

Puteți transmite linkul către pagina web astfel:

LinkClickedEventArgs args = nou LinkClickedEventArgs((șir) );


e.CommandArgument
LinkClicked(acest, args);

Iată codul complet de control al utilizatorului. Implementează încă o caracteristică. După ce evenimentul a
fost ridicat și gestionat de pagina web, LinkMenu2 verifică proprietatea Anulare. Dacă este fals, merge
mai departe și efectuează redirecționarea folosind Reponse.Redirect().
clasa publică parțială LinkMenu2 : System.Web.UI.UserControl
{
eveniment public LinkClickedEventHandler LinkClicked;

protejat void lnk_Command(expeditor obiect, CommandEventArgs e)


{
S-a făcut clic pe unul dintre comenzile LinkButton. Ridicați un
eveniment în pagină.
dacă (LinkClicked != null) { // Transmiteți
informațiile linkului.

LinkClickedEventArgs args = nou


LinkClickedEventArgs((string)e.CommandArgument);
LinkClicked(acest, args);
Efectuați redirecționarea.
dacă (!args. Anulare) { // Observați că folosim URL-ul din obiectul
LinkClickedEventArgs //, nu linkul original. Asta înseamnă că pagina
web } } } } // poate schimba linkul, dacă se dorește, înainte de

355
CAPITOLUL CONTROALE UTILIZATOR ȘI GRAFICĂ
11

redirecționare. Response.Redirect(args. URL);

În cele din urmă, trebuie să actualizați codul din pagina web (unde este plasat controlul utilizatorului), astfel
încât rutina de tratare a evenimentelor să utilizeze noua semnătură. În următorul cod, rutina de tratare a
evenimentelor LinkClicked verifică adresa URL și o permite în toate cazurile, cu excepția unuia:
vid protejat LinkClicked(expeditor obiect, LinkClickedEventArgs e) { if (e.Url == "Menu2Host.aspx?product=Furniture") {
lblClick.Text = "Acest link nu este permis."; e.Cancel = true; } else { // Permiteți redirecționarea și nu modificați adresa URL. } }

Dacă faceți clic pe linkul Mobilier, veți vedea mesajul afișat în Figura 11-6.

Figura 11-6. Gestionarea unui eveniment de control al utilizatorului în pagină

356
CAPITOLUL CONTROALE UTILIZATOR ȘI GRAFICĂ
11

Grafică dinamică
Una dintre caracteristicile .NET Framework este GDI +, un set de clase concepute pentru desenarea imaginilor. Puteți
utiliza GDI + într-o aplicație Windows sau ASP.NET pentru a crea grafică dinamică. Într-o aplicație Windows, grafica pe
care o desenați ar fi copiată într-o fereastră pentru afișare. În ASP.NET, codul dvs. poate reda grafica dorită și le poate
trimite direct în browserul clientului.
În general, utilizarea codului GDI + pentru a desena un grafic este mai lentă decât utilizarea unui fișier
imagine gata făcut. Cu toate acestea, GDI + vă oferă mult mai multă libertate. De exemplu, vă puteți
adapta imaginea pentru a se potrivi unui anumit scop, încorporând informații precum data sau numele
de utilizator curent. De asemenea, puteți amesteca text, forme și alte hărți de biți pentru a crea o
imagine completă.

Desen de bază
Trebuie să urmați patru pași de bază atunci când utilizați GDI +. În primul rând, trebuie să creați un bitmap în
memorie. Acesta este spațiul de desen în care vă veți crea capodopera. Pentru a crea bitmap-ul, declarați o
nouă instanță a clasei System.Drawing.Bitmap. Trebuie să specificați înălțimea și lățimea imaginii în pixeli. Aveți
grijă - nu faceți bitmap-ul mai mare decât este necesar, altfel veți pierde inutil memoria.

Creați un bitmap în memorie în care veți desena imaginea. Bitmap-ul


are 300 de pixeli lățime și 50 de pixeli înălțime. Imagine Bitmap =
Bitmap nou (300, 50);
Următorul pas este crearea unui context grafic GDI + pentru imagine, care este reprezentat de un obiect
System.Drawing.Graphics. Acest obiect furnizează metodele care vă permit să redați conținut în bitmap-ul
din memorie. Pentru a crea un obiect grafic dintr-un obiect Bitmap existent, utilizați doar metoda statică
Graphics.FromImage(), așa cum se arată aici:
Grafică g = Graphics.FromImage(imagine);

Notă
Metoda Graphics.FromImage() funcționează cu orice obiect Image. Clase precum Bitmap derivă din Image, deci funcționează bine.

Acum vine partea interesantă. Folosind metodele clasei Grafică, puteți desena text, forme și imagini pe bitmap. Tabelul 11-1 enumeră unele dintre cele mai
fundamentale metode ale clasei Grafică. Metodele care încep cu cuvântul Desenează contururi, în timp ce metodele care încep cu cuvântul desenează regiuni solide.
Singurele excepții sunt metoda DrawString(), care desenează text completat folosind un font specificat de dvs. și metodele de copiere a imaginilor bitmap, cum ar fi
DrawIcon() și DrawImage().
Umple

357
CAPITOLUL CONTROALE UTILIZATOR ȘI GRAFICĂ
11

Tabelul 11-1. Metode de desen ale clasei grafice

Metodă Descriere

DrawArc() Desenează un arc de cerc reprezentând o porțiune a unei elipse specificată


printr-o pereche de coordonate, o lățime și o înălțime (sau o altă combinație de
informații, dacă utilizați una dintre versiunile supraîncărcate ale acestei metode).

DrawBezier() și Desenează infama și atractiva curbă Bezier, care este definită de patru
DrawBeziers() puncte de control.

DrawClosedCurve() Desenează o curbă și apoi o închide conectând punctele finale.

DrawCurve() Desenează o curbă (tehnic, o splină cardinală).

DrawEllipse() Desenează o elipsă definită de un dreptunghi de încadrare specificat de o


pereche de coordonate, o înălțime și o lățime.

DrawIcon() și Desenează pictograma reprezentată de un obiect Pictogramă și (opțional) o


Descărcați de la Wow! eBook <www.wowebook.com>

DrawIconUnstretched() întinde pentru a se potrivi unui dreptunghi dat.

DrawImage() și Desenează imaginea reprezentată de un obiect derivat din imagine (cum ar fi


DrawImageUnscaled() un obiect Bitmap) și (opțional) o întinde pentru a se potrivi unui dreptunghi dat.

DrawLine()și Desenează una sau mai multe linii. Fiecare linie conectează cele două
DrawLines() puncte specificate de o pereche de coordonate.

DrawPie() Desenează o formă de "bucată de plăcintă" definită printr-o elipsă specificată


de o pereche de coordonate, o lățime, o înălțime și două linii radiale.

DrawPolygon() Desenează un poligon cu mai multe laturi definit de o matrice de puncte.

DrawRectangle() și Desenează unul sau mai multe dreptunghiuri obișnuite. Fiecare dreptunghi
DrawRectangles() este definit de o pereche de coordonate de pornire, o lățime și o înălțime.

DrawString() Desenează un șir de text într-un font dat.

DrawPath() Desenează o formă mai complexă, definită de obiectul Traseu.

FillClosedCurve() Desenează o curbă, o închide conectând punctele finale și o umple.

FillEllipse() Umple interiorul unei elipse.

FillPie() Umple interiorul unei forme de "bucată de plăcintă".

FillPolygon() Umple interiorul unui poligon.

FillRectangle() și Umple interiorul unuia sau mai multor dreptunghiuri.


FillRectangles()

FillPath() Umple interiorul unei forme complexe definite de obiectul Traseu.

358
CAPITOLUL CONTROALE UTILIZATOR ȘI GRAFICĂ
11

Când apelați metodele clasei Grafică, trebuie să specificați mai mulți parametri pentru a indica coordonatele
pixelilor pentru ceea ce doriți să desenați. De exemplu, atunci când desenați un dreptunghi, trebuie să
specificați locația colțului din stânga sus și lățimea și înălțimea acestuia. Iată un exemplu despre cum ați putea
desena un dreptunghi solid cu galben:
Desenați un dreptunghi începând de la locație (0, 0) //
care are 300 pixeli lățime și 50 pixeli înălțime.
g.FillRectangle(Pensule.Galben, 0, 0, 300, 50);
Când măsurați pixeli, punctul (0, 0) este colțul din stânga sus al imaginii în coordonate (x, y). Coordonata x
crește pe măsură ce mergeți mai departe spre dreapta, iar coordonata y crește pe măsură ce mergeți mai
departe. În exemplul curent, imaginea are 300 de pixeli lățime și 50 de pixeli înălțime, ceea ce înseamnă că
punctul (299, 49) este colțul din dreapta jos.

Notă cod desenează pe obiectul Bitmap din memorie creat anterior. Până când această
• Acest
imagine nu este redată (o abilitate pe care o veți prelua în curând), nu veți vedea nimic pe
pagina web.

Veți observa, de asemenea, că trebuie să specificați fie un obiect Pensulă, fie un obiect Peniță atunci când desenați majoritatea
conținutului. (Ambele clase sunt definite în spațiul de nume System.Drawing, alături de clasa Grafică.) Metodele care desenează
contururi de forme necesită un creion, în timp ce metodele care desenează forme umplute necesită o pensulă. Puteți crea
propriile obiecte personalizate pentru Pen și Pensulă, dar .NET oferă o soluție mai ușoară cu clasele Pensule și Stilouri. Aceste
clase expun proprietăți statice care oferă diferite pensule și stilouri pentru diferite culori. De exemplu, Pensule.Galben
returnează un obiect Pensulă care umple forme utilizând o culoare galbenă continuă, iar Stilouri.Galben returnează un obiect
Peniță care desenează contururi de formă utilizând aceeași culoare galbenă solidă.

Odată ce imaginea este completă, o puteți trimite browserului folosind metoda Image.Save (). Conceptual,
"salvați" imaginea în fluxul de răspuns al browserului. Apoi este trimis clientului și afișat în browser.

Redați imaginea în fluxul de ieșire HTML. imagine.


Salvare(Response.OutputStream,
System.Drawing.Imaging.ImageFormat.Gif);

Sfat salva o imagine în orice flux valid, inclusiv în clasa FileStream descrisă în Capitolul 17.
• Puteți
Această tehnică vă permite să salvați imaginile generate dinamic pe disc, astfel încât să le puteți
utiliza ulterior în alte pagini web.

În cele din urmă, ar trebui să eliberați în mod explicit contextul imaginii și graficii când ați terminat, deoarece
ambele păstrează unele resurse negestionate care ar putea să nu fie lansate imediat dacă:

g.Dispune(); imagine. dispune();

Utilizarea GDI + este o tehnică specializată, iar caracteristicile sale mai avansate depășesc scopul acestei
cărți. Cu toate acestea, puteți învăța multe luând în considerare câteva exemple simple.

359
CAPITOLUL CONTROALE UTILIZATOR ȘI GRAFICĂ
11

Desenarea unei imagini particularizate


Folosind tehnicile pe care le-ați învățat, este ușor să creați o pagină web simplă care utilizează GDI +.
Următorul exemplu utilizează GDI+ pentru a reda un text într-un dreptunghi mărginit, cu un grafic cu față
fericită alături. Iată codul de care veți avea nevoie:
protejat void Page_Load(Obiect expeditor, EventArgs e)
{
Creați un bitmap în memorie în care veți desena imaginea. Bitmap-ul
are 300 de pixeli lățime și 50 de pixeli înălțime. Imagine Bitmap =
Bitmap nou (300, 50);
Obțineți contextul grafic pentru bitmap. Grafică g
= Graphics.FromImage(imagine);

Desenați un dreptunghi galben solid cu o margine roșie.


g. deschis, 0, 0, 300, 50);
g. 0, 0, 299, 49);

Desenați un text folosind un font fantezist.


Font font = font nou ("Alba Super", 20, FontStyle.Regular);
g. este un test.", font, Brushes.Blue, 10, 0);

Copiați un gif mai mic în imagine dintr-un fișier.


pictograma System.Drawing.Image = Image.FromFile(Server.MapPath("smiley.gif"));
g. 240, 0);

Redați întregul bitmap în fluxul de ieșire HTML. imagine.


Salvare(Response.OutputStream,
System.Drawing.Imaging.ImageFormat.Gif);
Curățați.
g. imagine.
dispune();
}

Acest cod este ușor de înțeles. Urmează modelul de bază stabilit mai devreme - creează Bitmap-ul în
memorie, obține obiectul grafic corespunzător, efectuează pictura și apoi salvează imaginea în fluxul de
răspuns. Acest exemplu utilizează metodele FillRectangle(), DrawRectangle(), DrawString() și
DrawImageUnscaled() pentru a crea desenul complet prezentat în Figura 11-7.

Sfat: Deoarece această imagine este generată pe serverul web, aveți posibilitatea să utilizați orice font instalat pe server. Clientul nu trebuie să aibă același font, deoarece clientul primește textul ca imagine redată.

360
CAPITOLUL CONTROALE UTILIZATOR ȘI GRAFICĂ
11

Figura 11-7. Desenarea unei imagini particularizate

Plasarea imaginilor personalizate în paginile web


Abordarea Image.Save() demonstrată până acum are o problemă. Când salvați o imagine în fluxul de răspunsuri,
suprascrieți orice informații ASP.NET ar folosi altfel. Dacă aveți o pagină web care include alt conținut și
controale, acest conținut nu va apărea deloc în pagina web finală. În schimb, grafica redată dinamic îl înlocuiește.

Din fericire, aceasta are o soluție simplă: vă puteți conecta la o imagine generată dinamic utilizând eticheta HTML <img>
sau controlul web Imagine. Dar, în loc să legați imaginea la un fișier imagine fix, legați-o la fișierul .aspx care generează
imaginea.
De exemplu, puteți crea un fișier numit GraphicalText.aspx care scrie o imagine generată dinamic în fluxul de
răspunsuri. Într-o altă pagină, puteți afișa imaginea dinamică adăugând un control web Imagine și setând proprietatea
ImageUrl la GraphicalText.aspx. De fapt, veți vedea chiar și imaginea apărând în mediul de proiectare al Visual Studio
înainte de a rula pagina web!
Când utilizați această tehnică pentru a încorpora grafică dinamică în paginile web, trebuie să vă gândiți și la modul
în care pagina web poate trimite informații graficului dinamic. De exemplu, ce se întâmplă dacă nu doriți să afișați
un fragment fix de text, ci doriți să generați o etichetă dinamică care să includă numele utilizatorului curent? (De
fapt, dacă doriți să afișați o bucată fixă de text, este probabil mai bine să creați graficul din timp și să îl stocați
într-un fișier, decât să îl generați folosind codul GDI + de fiecare dată când utilizatorul solicită pagina.) O soluție
este să transmiteți informațiile utilizând șirul de interogare, așa cum este descris în capitolul 8. Pagina care redă
elementul grafic poate verifica apoi informațiile despre șirul de interogare de care are nevoie.
Iată cum ați rescrie generatorul grafic dinamic având în vedere acest lucru:

Obțineți numele de utilizator.


dacă (Request.QueryString["Nume"] == null) {
// Nu a fost furnizat niciun nume. Nu afișați
nimic. } else { string name =
Request.QueryString["Name"];

361
CAPITOLUL CONTROALE UTILIZATOR ȘI GRAFICĂ
11

Creați un bitmap în memorie în care veți desena imaginea. Imagine


Bitmap = Bitmap nou (300, 50);

Obțineți contextul grafic pentru bitmap. Grafică g


= Graphics.FromImage(imagine);

g. deschis, 0, 0, 300, 50);


g. 0, 0, 299, 49);

Desenați text pe baza șirului de interogare.


Font font = font nou ("Alba Super", 20, FontStyle.Regular);
g. font, Pensule.Blue, 10, 0);

Redați întregul bitmap în fluxul de ieșire HTML. imagine.


Salvare(Response.OutputStream,
System.Drawing.Imaging.ImageFormat.Gif);
g. imagine.
dispune();
}

Conceptual, acest cod nu este mult diferit de exemplele pe care le-ați văzut înainte. Singura modificare este
că o informație - șirul utilizat cu metoda DrawString() - este regăsită din șirul de interogare.

Figura 11-8 prezintă o pagină care utilizează această pagină grafică dinamică, împreună cu două
controale Etichetă. Pagina transmite argumentul șir de interogare Joe Brown la pagină. Image.ImageUrl
complet devine astfel GraphicalText.aspx? Name=Joe%20Brown, așa cum se arată aici:
<asp:Label id="Label1" runat="server">Iată un conținut.</asp:Label> <br /><br />

<asp:Image id="Image1" runat="server"


ImageUrl="GraphicalText.aspx? Nume=Joe%20Brown"></asp:Imagine>
<br /><br /> <asp:Label id="Label2" runat="server">Iată mai mult conținut.</asp:Label>

362
CAPITOLUL CONTROALE UTILIZATOR ȘI GRAFICĂ
11

Figura 11-8. Amestecarea imaginilor și controalelor personalizate pe aceeași pagină

Este posibil să fie necesar să trimiteți mai multe informații sau informații mai complexe la pagina care
desenează imaginea. De exemplu, poate doriți să transmiteți un obiect de date unei pagini care desenează
o diagramă radială. În acest caz, șirul de interogare nu este suficient de bun și va trebui să utilizați un alt tip
de gestionare a stării. O opțiune este starea sesiunii, așa cum este descris în capitolul 8.

Formatul și calitatea imaginii


Când randați o imagine, puteți alege și formatul pe care doriți să îl utilizați. JPEG oferă cel mai bun suport pentru culori
și grafică, deși folosește compresie care poate pierde detalii și poate face textul să pară neclar. GIF (standardul folosit
în exemple până acum) este adesea o alegere mai bună pentru grafica care conține text, dar nu oferă un suport bun
pentru culoare. În .NET, fiecare GIF folosește o paletă fixă cu 256 de culori generice. Dacă utilizați o culoare care nu
se mapează la una dintre aceste presetări, culoarea va fi cuantizată, rezultând o grafică mai puțin optimă.

Cu toate acestea, cea mai bună alegere a formatului este PNG. PNG este un format universal care oferă întotdeauna
o calitate înaltă prin combinarea compresiei fără pierderi a GIF-urilor cu suportul bogat de culoare al JPEG-urilor.
(Singurul sughiț este că versiunile de Internet Explorer anterioare versiunii 7 nu tratează corect conținutul PNG
returnat de pe o pagină web. Dacă utilizați una dintre aceste versiuni vechi de IE, nu veți vedea conținutul imaginii, în
schimb, veți primi un mesaj care vă solicită să descărcați imaginea și să o deschideți în alt program. Pentru a evita
această problemă, trebuie să utilizați abordarea etichetei <img> prezentată în exemplul anterior.)

De asemenea, trebuie să fiți conștienți de încă o ciudățenie atunci când utilizați PNG - nu puteți utiliza metoda
Bitmap.Save() prezentată în exemplele anterioare. Dacă faceți acest lucru, va apărea o eroare. Din punct de vedere
tehnic, problema este că metoda Save() necesită un flux care poate fi căutat- un flux în care poziția poate fi schimbată
după bunul plac. Acest lucru se datorează faptului că .NET trebuie să poată trece înainte și înapoi prin conținutul imaginii
în timp ce este generat. Soluția este ușor de implementat, dacă este puțin ciudată. În loc să salvați direct în
Response.OutputStream, puteți crea un obiect System.IO.MemoryStream, care reprezintă un tampon de date în
memorie. MemoryStream este întotdeauna căutabil, astfel încât să puteți salva imaginea pe acest obiect. După ce ați
efectuat acest pas, puteți copia cu ușurință datele din MemoryStream în Response.OutputStream. Singurul dezavantaj
este că această tehnică necesită mai multă memorie, deoarece conținutul complet redat al graficului trebuie păstrat

363
CAPITOLUL CONTROALE UTILIZATOR ȘI GRAFICĂ
11

simultan în memorie. Cu toate acestea, grafica pe care o utilizați în paginile web, în general, nu este atât de mare, deci
probabil că nu veți observa nicio reducere a performanței.
Pentru a implementa această soluție, începeți prin a importa spațiul de nume System.IO:

folosind System.IO;
Acum puteți înlocui exemplul anterior cu acest cod modificat care salvează imaginea în format PNG.
Liniile modificate sunt evidențiate.

Obțineți numele de utilizator.


dacă (Request.QueryString["Nume"] == null) {
// Nu a fost furnizat niciun nume.

Nu afișați nimic.
} else { string name = Request.QueryString["Name"];

Creați un bitmap în memorie în care veți desena imaginea. Imagine


Bitmap = Bitmap nou (300, 50);

Obțineți contextul grafic pentru bitmap. Grafică g


= Graphics.FromImage(imagine);

g. deschis, 0, 0, 300, 50);


g. 0, 0, 299, 49);

Desenați text pe baza șirului de interogare.


Font font = font nou ("Alba Super", 20, FontStyle.Regular);
g. font, Pensule.Blue, 10, 0);

Response.ContentType = "imagine/png";

Creați PNG în memorie.


MemoryStream mem = nou MemoryStream(); Imagine. Salvare(mem, System.Drawing.Imaging.ImageFormat.Png);

Scrieți datele MemoryStream în fluxul de ieșire. Mem. WriteTo(Response.OutputStream);

g. imagine.
dispune(); }

• Notă Veți afla mai multe despre fluxuri atunci când abordați accesul
la fișiere în capitolul 17.

Calitatea nu este determinată doar de formatul imaginii. De asemenea, depinde de modul în care desenați conținutul imaginii
pe bitmap-ul din memorie. GDI + vă permite să alegeți între optimizarea codului de desen pentru aspect sau viteză. Când

364
CAPITOLUL CONTROALE UTILIZATOR ȘI GRAFICĂ
11

alegeți să optimizați pentru cel mai bun aspect, .NET utilizează tehnici suplimentare de redare, cum ar fi antialiasing,
pentru a îmbunătăți desenul.
Antialiasul netezește marginile zimțate din forme și text. Funcționează prin adăugarea de umbrire la marginea
unei muchii. De exemplu, umbrirea gri poate fi adăugată la marginea unei curbe negre pentru a face un colț
să pară mai neted. Din punct de vedere tehnic, antialiasing amestecă o curbă cu fundalul său. Figura 11-9
prezintă un prim-plan al unei elipse antialias.

Figura 11-9. Antialiasing cu o elipsă

Pentru a utiliza netezirea în aplicațiile dvs., setați proprietatea SmoothingMode a obiectului Grafică. Puteți
alege între None, HighSpeed (implicit), AntiAlias și HighQuality (care este similar cu Antialias, dar utilizează
alte optimizări mai lente care îmbunătățesc afișarea pe ecranele LCD). Proprietatea Graphics.SmoothingMode
este unul dintre puținii membri ai clasei de grafică. Aceasta înseamnă că îl setați înainte de a începe să
desenați și se aplică oricărui text sau forme pe care le desenați în restul sesiunii de pictare (până când
obiectul Grafică este eliberat).
g.SmoothingMode = Drawing.Drawing2D.SmoothingMode.AntiAlias;

Sfat
Antialiasing face cea mai mare diferență atunci când afișați curbe. Asta înseamnă că va îmbunătăți dramatic aspectul elipselor, cercurilor și arcelor, dar nu va face nicio diferență cu liniile drepte, pătratele și dreptunghiurile.

De asemenea, puteți utiliza antialiasing cu fonturi pentru a înmuia marginile zimțate ale textului. Puteți seta
proprietatea Graphics.TextRenderingHint pentru a asigura un text optimizat. Puteți alege între
SingleBitPerPixelGridFit (cea mai rapidă performanță și cea mai slabă calitate), AntiAliasGridFit (calitate mai
bună, dar performanță mai lentă) și ClearTypeGridFit (cea mai bună calitate pe un ecran LCD). Sau puteți
utiliza valoarea SystemDefault pentru a aplica orice setări de netezire a fonturilor configurate de utilizator.
SystemDefault este setarea implicită, iar setările implicite de sistem pentru majoritatea computerelor
activează antialiasing text. Chiar dacă nu setați acest lucru, textul redat dinamic va fi probabil desenat de
înaltă calitate. Cu toate acestea, deoarece nu puteți controla neapărat setările de sistem ale serverului web,
este o practică bună să specificați această setare în mod explicit dacă trebuie să desenați text într-o imagine.

365
CAPITOLUL CONTROALE UTILIZATOR ȘI GRAFICĂ
11

Ultimul cuvânt
În acest capitol, puneți încă două instrumente în setul de instrumente ASP.NET. În primul rând, ați văzut cum
controalele utilizatorului vă permit să reutilizați un bloc de interfață cu utilizatorul în mai multe pagini web. Apoi, ați luat
în considerare modul în care desenul personalizat vă permite să creați grafică pe măsură.
În capitolul următor, veți afla despre teme și pagini coordonatoare - două caracteristici care completează
controalele utilizatorului și vă oferă și mai multe modalități de a standardiza aspectul paginilor dvs. Temele
sunt mai detaliate decât controalele utilizatorului - acestea grupează presetările de formatare pe care le
puteți aplica controalelor individuale pentru a asigura un stil elegant și consecvent în întreaga aplicație.
Paginile coordonatoare sunt mai largi decât controalele utilizatorului - acestea vă permit să definiți un
șablon de pagină standardizat pe care îl puteți aplica pentru a bloca aspectul și aspectul mai multor pagini,
oferindu-vă o consecvență completă. Învățarea modului de amestecare a tuturor acestor ingrediente face
parte din arta plastică a programării ASP.NET.

366

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