book-openListView in modul Details

ListView este un control care poate afișa o colecție de elemente în mai multe moduri vizuale, controlate prin proprietatea View: LargeIcon, SmallIcon, List, Tile și Details. Modul Details afișează datele ca un tabel cu rânduri și coloane — similar cu panoul din Windows Explorer în vizualizarea detalii.

Față de controalele pe care le-am văzut anterior, fiecare are locul lui:

  • ListBox afișează o singură coloană de text, generată automat din ToString(). Potrivit când ai un singur atribut de afișat sau când ToString() rezumă bine obiectul și nu ai nevoie de coloane separate.

  • DataGridView se leagă direct la o sursă de date prin DataSource și generează coloane automat din proprietățile tipului. Potrivit când vrei binding automat și actualizări fără cod suplimentar, și nu ai nevoie de control fin asupra formatului afișat.

  • ListView în modul Details se populează manual, rând cu rând, din cod. Nu există binding automat — tu construiești fiecare rând explicit. Această abordare îți oferă control complet: alegi exact ce coloane apar, cum sunt formatate valorile și ce stocat în fiecare celulă. Este o alegere bună când prezentarea datelor contează — de exemplu, când vrei să afișezi prețul cu simbol monetar, data formatată sau un câmp calculat care nu există ca proprietate pe obiect.

Configurarea de bază

Modul Details se setează prin proprietatea View, iar câteva alte proprietăți îl fac utilizabil pentru o aplicație de gestiune:

lvCarti.View = View.Details;
// Modul tabel cu coloane — cel mai util pentru liste de obiecte cu mai multe atribute

lvCarti.FullRowSelect = true;
// Un click pe orice celula selecteaza intregul rand, nu doar celula respectiva.
// Fara aceasta, utilizatorul poate selecta o singura celula din mijlocul unui rand,
// ceea ce este confuz si face recuperarea obiectului la selectare mai complicata.

lvCarti.MultiSelect = false;
// Permite selectarea unui singur rand la un moment dat.
// Simplifica logica de editare si stergere — nu trebuie sa gestionezi selectii multiple.

lvCarti.GridLines = true;
// Afiseaza linii de separare intre randuri — mai usor de citit pentru liste lungi.

lvCarti.AllowColumnReorder = true;
// Optional: permite utilizatorului sa reordoneze coloanele prin drag and drop.

Adăugarea coloanelor

Coloanele se adaugă în cod, de obicei în Form1_Load, înainte de popularea cu date. Metoda Columns.Add primește textul antetului, lățimea în pixeli și alinierea textului din coloană:

Alinierea textului din coloană este un detaliu vizual care comunică tipul datei: numerele și prețurile se aliniează natural la dreapta (exact cum scriem cifrele în tabele financiare), textele la stânga, codurile sau anii scurți se centrează.

ListViewItem și sub-items

Un rând în ListView este reprezentat de un obiect ListViewItem. Prima coloană a rândului este textul principal al item-ului — transmis direct în constructor. Coloanele următoare se adaugă ca SubItems, în ordinea în care apar coloanele:

Ordinea SubItems.Add trebuie să corespundă exact ordinii coloanelor definite anterior. Dacă adaugi coloanele în ordinea Titlu, Autor, An, Gen, Preț, atunci sub-items-urile trebuie adăugate în aceeași ordine — altfel datele vor apărea în coloanele greșite fără niciun mesaj de eroare.

Proprietatea Tag

Aceasta este tehnica esențială care face ListView-ul util în aplicații CRUD, și merită înțeleasă bine.

Când utilizatorul selectează un rând și apasă Editează sau Șterge, ai nevoie să știi exact ce obiect Carte corespunde rândului selectat. Ai putea căuta cartea în repository după titlu, dar aceasta este fragil — pot exista două cărți cu același titlu. Ai putea reține indexul rândului, dar indexul se schimbă dacă lista este filtrată sau sortată.

Soluția elegantă este proprietatea Tag de tip object, disponibilă pe orice ListViewItem. Poți stoca în ea orice referință — în cazul nostru, obiectul Carte corespunzător rândului:

Când utilizatorul selectează rândul, recuperezi obiectul direct din Tag, fără căutări suplimentare:

Tag nu afectează nimic vizual — este un spațiu de stocare invizibil atașat rândului, exclusiv pentru uz intern în cod. Utilizatorul nu știe că există.

Metoda RefreshLista()

Patternul de populare este același ca la ListBox din laboratorul anterior — o metodă dedicată care golește și repopulează de fiecare dată:

Această metodă se apelează după orice operație care modifică datele: adăugare, editare, ștergere. Nu există binding automat ca la DataGridView cu BindingList — actualizarea este manuală, dar și explicită.

Obținerea elementului selectat și activarea butoanelor

SelectedItems este o colecție de ListViewItem. Chiar dacă MultiSelect = false, verificăm Count > 0 înainte să accesăm [0] — accesul la indexul 0 pe o colecție goală aruncă excepție.

O îmbunătățire importantă de UX: dezactivează butoanele de editare și ștergere când nimic nu este selectat și activează-le la selecție, prin evenimentul SelectedIndexChanged. Utilizatorul primește feedback vizual clar despre ce poate face în starea curentă:

Apelează această logică și în Form1_Load pentru starea inițială — când lista este goală sau nimic nu este selectat, butoanele trebuie să fie dezactivate de la pornire:

Evenimentul DoubleClick

O îmbunătățire frecventă și apreciată de utilizatori: dublu-click pe un rând deschide direct formularul de editare, fără să mai fie nevoie să apese butonul „Editează". Se implementează reutilizând logica existentă din buton:

Last updated