Metti insieme TUTTE le competenze in un'app professionale completa!.
Il Grande Progetto Finale
Hai imparato tutti i controlli di Windows Forms! Ora Γ¨ il momento di creare un'applicazione completa e professionale che integra tutto quello che hai studiato. Questo progetto andrΓ nel tuo portfolio! πΌβ¨
π Il Progetto: Gestionale Biblioteca
Creerai un sistema completo per gestire una biblioteca: catalogo libri, gestione prestiti, statistiche, ricerca avanzata, salvataggio dati. Un'applicazione che potresti usare DAVVERO!
π― Requisiti del Progetto
β FunzionalitΓ Base
- Login con credenziali
- CRUD libri (Create, Read, Update, Delete)
- Gestione prestiti
- Ricerca e filtri
- Salvataggio su file CSV
β Controlli da Usare
- TabControl (organizzazione)
- DataGridView (visualizza dati)
- MenuStrip (menu professionale)
- Timer (controlla scadenze)
- ErrorProvider (validazione)
- StatusStrip (info stato)
β Requisiti Tecnici
- Minimo 3 form separati
- Classi custom (Libro, Prestito)
- Validazione input completa
- Gestione errori (try-catch)
- Commenti nel codice
β±οΈ Tempo Stimato
Questo progetto richiede 2-3 settimane di lavoro. Prenditi il tempo necessario per farlo bene! Puoi procedere a step incrementali.
Architettura dell'Applicazione
π Struttura Form
π FormLogin
Scopo: Autenticazione utente Controlli: TextBox username/password, Button login FunzionalitΓ : Verifica credenziali, apre FormPrincipale se OK
π FormPrincipale (con TabControl)
Scopo: Dashboard principale con 4 schede Schede:
- π Catalogo: DataGridView con tutti i libri, pulsanti Aggiungi/Modifica/Elimina
- π€ Prestiti: Gestisci prestiti attivi, controllo scadenze
- π Ricerca: TextBox ricerca, filtri per categoria/autore
- π Statistiche: Grafici e numeri (libri totali, prestiti attivi, top autori)
Menu: File (Salva, Carica, Esci) | Modifica (Aggiungi Libro) | Aiuto (Info)
π FormDettagliLibro
Scopo: Aggiungi/Modifica libro Controlli: TextBox (titolo, autore), NumericUpDown (anno, pagine), ComboBox (categoria), CheckBox (disponibile) Validazione: ErrorProvider per tutti i campi
Classi per i Dati
Libro.cs
using System;
namespace GestionaleBiblioteca
{
// Classe che rappresenta un libro
public class Libro
{
public int Id { get; set; }
public string Titolo { get; set; }
public string Autore { get; set; }
public int Anno { get; set; }
public int Pagine { get; set; }
public string Categoria { get; set; } // Es: Fantasy, Thriller, Saggio
public bool Disponibile { get; set; }
public DateTime? DataAggiunta { get; set; }
// ProprietΓ calcolata per CSV
public string CsvFormat =>
$"{Id};{Titolo};{Autore};{Anno};{Pagine};{Categoria};{Disponibile};{DataAggiunta:yyyy-MM-dd}";
// Costruttore vuoto
public Libro()
{
Disponibile = true;
DataAggiunta = DateTime.Now;
}
// Costruttore con parametri
public Libro(int id, string titolo, string autore, int anno, int pagine, string categoria)
{
Id = id;
Titolo = titolo;
Autore = autore;
Anno = anno;
Pagine = pagine;
Categoria = categoria;
Disponibile = true;
DataAggiunta = DateTime.Now;
}
// Parsing da riga CSV
public static Libro FromCsv(string csvLine)
{
string[] parts = csvLine.Split(';');
return new Libro
{
Id = int.Parse(parts[0]),
Titolo = parts[1],
Autore = parts[2],
Anno = int.Parse(parts[3]),
Pagine = int.Parse(parts[4]),
Categoria = parts[5],
Disponibile = bool.Parse(parts[6]),
DataAggiunta = DateTime.Parse(parts[7])
};
}
public override string ToString()
{
return $"{Titolo} - {Autore} ({Anno})";
}
}
}
Prestito.cs
using System;
namespace GestionaleBiblioteca
{
public class Prestito
{
public int Id { get; set; }
public int IdLibro { get; set; }
public string NomeUtente { get; set; }
public DateTime DataPrestito { get; set; }
public DateTime DataScadenza { get; set; }
public DateTime? DataRestituzione { get; set; }
// ProprietΓ calcolate
public bool Attivo => DataRestituzione == null;
public bool Scaduto => Attivo && DateTime.Now > DataScadenza;
public int GiorniRimanenti => (DataScadenza - DateTime.Now).Days;
public string CsvFormat =>
$"{Id};{IdLibro};{NomeUtente};{DataPrestito:yyyy-MM-dd};{DataScadenza:yyyy-MM-dd};{DataRestituzione?.ToString("yyyy-MM-dd") ?? ""}";
// Costruttore standard: prestito di 14 giorni
public Prestito(int id, int idLibro, string nomeUtente)
{
Id = id;
IdLibro = idLibro;
NomeUtente = nomeUtente;
DataPrestito = DateTime.Now;
DataScadenza = DateTime.Now.AddDays(14); // 2 settimane
DataRestituzione = null;
}
// Costruttore vuoto
public Prestito() { }
public static Prestito FromCsv(string csvLine)
{
string[] parts = csvLine.Split(';');
return new Prestito
{
Id = int.Parse(parts[0]),
IdLibro = int.Parse(parts[1]),
NomeUtente = parts[2],
DataPrestito = DateTime.Parse(parts[3]),
DataScadenza = DateTime.Parse(parts[4]),
DataRestituzione = string.IsNullOrEmpty(parts[5]) ? null : (DateTime?)DateTime.Parse(parts[5])
};
}
}
}
Step 1: FormLogin
FormLogin.cs (Completo)
using System;
using System.Windows.Forms;
namespace GestionaleBiblioteca
{
public partial class FormLogin : Form
{
// Credenziali (in app reale: database!)
private const string USERNAME = "admin";
private const string PASSWORD = "biblioteca";
public FormLogin()
{
InitializeComponent();
txt_password.PasswordChar = 'β';
this.AcceptButton = btn_login; // INVIO = click login
// Centra il form
this.StartPosition = FormStartPosition.CenterScreen;
}
private void btn_login_Click(object sender, EventArgs e)
{
string user = txt_username.Text.Trim();
string pass = txt_password.Text;
if (string.IsNullOrEmpty(user) || string.IsNullOrEmpty(pass))
{
lbl_errore.Text = "β Compila tutti i campi!";
lbl_errore.Visible = true;
return;
}
if (user == USERNAME && pass == PASSWORD)
{
// β
Login OK!
lbl_errore.Visible = false;
// Apri form principale
FormPrincipale principale = new FormPrincipale(user);
principale.Show();
// Nascondi login
this.Hide();
// Quando chiude il principale, chiudi anche questo
principale.FormClosed += (s, args) => this.Close();
}
else
{
// β Errore
lbl_errore.Text = "β Credenziali errate!";
lbl_errore.Visible = true;
txt_password.Clear();
txt_password.Focus();
// Animazione shake
AnimazioneErrore();
}
}
private void AnimazioneErrore()
{
int posOriginale = this.Left;
this.Left -= 10;
System.Threading.Thread.Sleep(50);
this.Left = posOriginale + 10;
System.Threading.Thread.Sleep(50);
this.Left = posOriginale;
}
}
}
Step 2: FormPrincipale (Struttura)
FormPrincipale.cs (Parte 1 - Setup)
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows.Forms;
namespace GestionaleBiblioteca
{
public partial class FormPrincipale : Form
{
// βββββββββββββββββββββββββββββββββββββββββββ
// VARIABILI GLOBALI
// βββββββββββββββββββββββββββββββββββββββββββ
private List<Libro> libri = new List<Libro>();
private List<Prestito> prestiti = new List<Prestito>();
private string nomeUtente;
private const string FILE_LIBRI = "libri.csv";
private const string FILE_PRESTITI = "prestiti.csv";
// βββββββββββββββββββββββββββββββββββββββββββ
// COSTRUTTORE
// βββββββββββββββββββββββββββββββββββββββββββ
public FormPrincipale(string utente)
{
InitializeComponent();
this.nomeUtente = utente;
this.Text = $"Gestionale Biblioteca - {utente}";
this.WindowState = FormWindowState.Maximized;
// Setup iniziale
ImpostaDataGridView();
CaricaDati();
AggiornaStatistiche();
// Timer per controllare scadenze ogni minuto
timer_scadenze.Interval = 60000; // 1 minuto
timer_scadenze.Tick += timer_scadenze_Tick;
timer_scadenze.Start();
// StatusBar
toolStripStatusLabel.Text = $"Benvenuto, {utente}! | {libri.Count} libri nel catalogo";
}
// βββββββββββββββββββββββββββββββββββββββββββ
// SETUP DATAGRIDVIEW
// βββββββββββββββββββββββββββββββββββββββββββ
private void ImpostaDataGridView()
{
dgv_catalogo.Columns.Clear();
dgv_catalogo.Columns.Add("Id", "ID");
dgv_catalogo.Columns.Add("Titolo", "Titolo");
dgv_catalogo.Columns.Add("Autore", "Autore");
dgv_catalogo.Columns.Add("Anno", "Anno");
dgv_catalogo.Columns.Add("Pagine", "Pagine");
dgv_catalogo.Columns.Add("Categoria", "Categoria");
dgv_catalogo.Columns.Add("Disponibile", "Disponibile");
dgv_catalogo.Columns["Id"].Width = 50;
dgv_catalogo.Columns["Titolo"].Width = 250;
dgv_catalogo.Columns["Autore"].Width = 200;
dgv_catalogo.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
dgv_catalogo.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
dgv_catalogo.MultiSelect = false;
dgv_catalogo.AllowUserToAddRows = false;
// Stesso setup per dgv_prestiti
dgv_prestiti.Columns.Clear();
dgv_prestiti.Columns.Add("Id", "ID");
dgv_prestiti.Columns.Add("Libro", "Libro");
dgv_prestiti.Columns.Add("Utente", "Utente");
dgv_prestiti.Columns.Add("DataPrestito", "Data Prestito");
dgv_prestiti.Columns.Add("Scadenza", "Scadenza");
dgv_prestiti.Columns.Add("GiorniRim", "Giorni Rim.");
dgv_prestiti.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
}
// βββββββββββββββββββββββββββββββββββββββββββ
// CARICA DATI DA FILE
// βββββββββββββββββββββββββββββββββββββββββββ
private void CaricaDati()
{
try
{
// Carica libri
if (File.Exists(FILE_LIBRI))
{
string[] righe = File.ReadAllLines(FILE_LIBRI);
foreach (string riga in righe)
{
if (!string.IsNullOrWhiteSpace(riga))
{
libri.Add(Libro.FromCsv(riga));
}
}
}
else
{
// Prima volta: aggiungi libri di esempio
AggiungiLibriDiEsempio();
}
// Carica prestiti
if (File.Exists(FILE_PRESTITI))
{
string[] righe = File.ReadAllLines(FILE_PRESTITI);
foreach (string riga in righe)
{
if (!string.IsNullOrWhiteSpace(riga))
{
prestiti.Add(Prestito.FromCsv(riga));
}
}
}
AggiornaDataGridView();
AggiornaPrestiti();
}
catch (Exception ex)
{
MessageBox.Show($"Errore caricamento dati: {ex.Message}", "Errore",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
// βββββββββββββββββββββββββββββββββββββββββββ
// SALVA DATI SU FILE
// βββββββββββββββββββββββββββββββββββββββββββ
private void SalvaDati()
{
try
{
// Salva libri
List<string> righeLibri = new List<string>();
foreach (Libro libro in libri)
{
righeLibri.Add(libro.CsvFormat);
}
File.WriteAllLines(FILE_LIBRI, righeLibri);
// Salva prestiti
List<string> righe Prestiti = new List<string>();
foreach (Prestito p in prestiti)
{
righe Prestiti.Add(p.CsvFormat);
}
File.WriteAllLines(FILE_PRESTITI, righe Prestiti);
toolStripStatusLabel.Text = $"Dati salvati alle {DateTime.Now:HH:mm:ss}";
}
catch (Exception ex)
{
MessageBox.Show($"Errore salvataggio: {ex.Message}", "Errore",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
// βββββββββββββββββββββββββββββββββββββββββββ
// AGGIORNA DATAGRIDVIEW CATALOGO
// βββββββββββββββββββββββββββββββββββββββββββ
private void AggiornaDataGridView()
{
dgv_catalogo.Rows.Clear();
foreach (Libro libro in libri)
{
int rowIndex = dgv_catalogo.Rows.Add(
libro.Id,
libro.Titolo,
libro.Autore,
libro.Anno,
libro.Pagine,
libro.Categoria,
libro.Disponibile ? "β
SΓ¬" : "β No"
);
// Colora riga se non disponibile
if (!libro.Disponibile)
{
dgv_catalogo.Rows[rowIndex].DefaultCellStyle.BackColor = Color.LightCoral;
}
}
}
// βββββββββββββββββββββββββββββββββββββββββββ
// LIBRI DI ESEMPIO (prima volta)
// βββββββββββββββββββββββββββββββββββββββββββ
private void AggiungiLibriDiEsempio()
{
libri.Add(new Libro(1, "Il Signore degli Anelli", "J.R.R. Tolkien", 1954, 1178, "Fantasy"));
libri.Add(new Libro(2, "1984", "George Orwell", 1949, 328, "Distopico"));
libri.Add(new Libro(3, "Harry Potter", "J.K. Rowling", 1997, 309, "Fantasy"));
libri.Add(new Libro(4, "Dune", "Frank Herbert", 1965, 688, "Fantascienza"));
libri.Add(new Libro(5, "Il Nome della Rosa", "Umberto Eco", 1980, 503, "Thriller"));
}
}
}
Step 3: CRUD Libri
FormPrincipale.cs (Parte 2 - CRUD)
// βββββββββββββββββββββββββββββββββββββββββββ
// PULSANTE: Aggiungi Libro
// βββββββββββββββββββββββββββββββββββββββββββ
private void btn_aggiungi_libro_Click(object sender, EventArgs e)
{
FormDettagliLibro formDettagli = new FormDettagliLibro();
if (formDettagli.ShowDialog() == DialogResult.OK)
{
// Nuovo libro da aggiungere
Libro nuovoLibro = formDettagli.LibroCorrente;
// Assegna ID automatico
nuovoLibro.Id = libri.Count > 0 ? libri.Max(l => l.Id) + 1 : 1;
// Aggiungi alla lista
libri.Add(nuovoLibro);
// Aggiorna visualizzazione
AggiornaDataGridView();
AggiornaStatistiche();
// Salva
SalvaDati();
MessageBox.Show($"Libro '{nuovoLibro.Titolo}' aggiunto!", "Successo",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
// βββββββββββββββββββββββββββββββββββββββββββ
// PULSANTE: Modifica Libro
// βββββββββββββββββββββββββββββββββββββββββββ
private void btn_modifica_libro_Click(object sender, EventArgs e)
{
if (dgv_catalogo.SelectedRows.Count == 0)
{
MessageBox.Show("Seleziona un libro da modificare!", "Attenzione",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
int indice = dgv_catalogo.SelectedRows[0].Index;
Libro libroSelezionato = libri[indice];
FormDettagliLibro formDettagli = new FormDettagliLibro(libroSelezionato);
if (formDettagli.ShowDialog() == DialogResult.OK)
{
// Aggiorna il libro
libri[indice] = formDettagli.LibroCorrente;
// Aggiorna visualizzazione
AggiornaDataGridView();
SalvaDati();
MessageBox.Show("Libro aggiornato!", "Successo",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
// βββββββββββββββββββββββββββββββββββββββββββ
// PULSANTE: Elimina Libro
// βββββββββββββββββββββββββββββββββββββββββββ
private void btn_elimina_libro_Click(object sender, EventArgs e)
{
if (dgv_catalogo.SelectedRows.Count == 0)
{
MessageBox.Show("Seleziona un libro da eliminare!", "Attenzione",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
int indice = dgv_catalogo.SelectedRows[0].Index;
Libro libro = libri[indice];
// Controlla se Γ¨ in prestito
bool inPrestito = prestiti.Any(p => p.IdLibro == libro.Id && p.Attivo);
if (inPrestito)
{
MessageBox.Show("Impossibile eliminare: libro in prestito!", "Attenzione",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
DialogResult ris = MessageBox.Show(
$"Eliminare '{libro.Titolo}'?",
"Conferma",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question);
if (ris == DialogResult.Yes)
{
libri.RemoveAt(indice);
AggiornaDataGridView();
AggiornaStatistiche();
SalvaDati();
}
}
// βββββββββββββββββββββββββββββββββββββββββββ
// RICERCA LIBRI
// βββββββββββββββββββββββββββββββββββββββββββ
private void btn_cerca_Click(object sender, EventArgs e)
{
string termine = txt_ricerca.Text.Trim().ToLower();
if (string.IsNullOrWhiteSpace(termine))
{
// Mostra tutti
AggiornaDataGridView();
return;
}
// Filtra libri
var risultati = libri.Where(l =>
l.Titolo.ToLower().Contains(termine) ||
l.Autore.ToLower().Contains(termine) ||
l.Categoria.ToLower().Contains(termine)
).ToList();
// Mostra risultati
dgv_catalogo.Rows.Clear();
foreach (Libro libro in risultati)
{
dgv_catalogo.Rows.Add(
libro.Id, libro.Titolo, libro.Autore,
libro.Anno, libro.Pagine, libro.Categoria,
libro.Disponibile ? "β
SΓ¬" : "β No"
);
}
toolStripStatusLabel.Text = $"Trovati {risultati.Count} risultati per '{termine}'";
}
Step 4: Gestione Prestiti
FormPrincipale.cs (Parte 3 - Prestiti)
// βββββββββββββββββββββββββββββββββββββββββββ
// PULSANTE: Nuovo Prestito
// βββββββββββββββββββββββββββββββββββββββββββ
private void btn_nuovo_prestito_Click(object sender, EventArgs e)
{
if (dgv_catalogo.SelectedRows.Count == 0)
{
MessageBox.Show("Seleziona un libro!", "Attenzione",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
int indice = dgv_catalogo.SelectedRows[0].Index;
Libro libro = libri[indice];
if (!libro.Disponibile)
{
MessageBox.Show("Libro giΓ in prestito!", "Attenzione",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
// Chiedi nome utente
string nomeUtentePrestito = Microsoft.VisualBasic.Interaction.InputBox(
"Inserisci il nome della persona che prende in prestito il libro:",
"Nuovo Prestito",
""
);
if (string.IsNullOrWhiteSpace(nomeUtentePrestito))
return;
// Crea prestito
int idPrestito = prestiti.Count > 0 ? prestiti.Max(p => p.Id) + 1 : 1;
Prestito prestito = new Prestito(idPrestito, libro.Id, nomeUtentePrestito);
prestiti.Add(prestito);
// Segna libro come non disponibile
libro.Disponibile = false;
// Aggiorna
AggiornaDataGridView();
AggiornaPrestiti();
AggiornaStatistiche();
SalvaDati();
MessageBox.Show(
$"Prestito registrato!\n\n" +
$"Libro: {libro.Titolo}\n" +
$"Utente: {nomeUtentePrestito}\n" +
$"Scadenza: {prestito.DataScadenza:dd/MM/yyyy}",
"Prestito Creato",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
// βββββββββββββββββββββββββββββββββββββββββββ
// PULSANTE: Restituisci
// βββββββββββββββββββββββββββββββββββββββββββ
private void btn_restituisci_Click(object sender, EventArgs e)
{
if (dgv_prestiti.SelectedRows.Count == 0)
{
MessageBox.Show("Seleziona un prestito!", "Attenzione",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
int indicePrestito = dgv_prestiti.SelectedRows[0].Index;
var prestitiAttivi = prestiti.Where(p => p.Attivo).ToList();
Prestito prestito = prestitiAttivi[indicePrestito];
// Registra restituzione
prestito.DataRestituzione = DateTime.Now;
// Trova il libro e rendilo disponibile
Libro libro = libri.FirstOrDefault(l => l.Id == prestito.IdLibro);
if (libro != null)
{
libro.Disponibile = true;
}
// Aggiorna
AggiornaDataGridView();
AggiornaPrestiti();
AggiornaStatistiche();
SalvaDati();
// Verifica se in ritardo
if (prestito.Scaduto)
{
int giorniRitardo = (DateTime.Now - prestito.DataScadenza).Days;
MessageBox.Show(
$"Libro restituito in RITARDO di {giorniRitardo} giorni!",
"Restituzione",
MessageBoxButtons.OK,
MessageBoxIcon.Warning);
}
else
{
MessageBox.Show("Libro restituito correttamente!", "Restituzione",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
// βββββββββββββββββββββββββββββββββββββββββββ
// AGGIORNA TABELLA PRESTITI
// βββββββββββββββββββββββββββββββββββββββββββ
private void AggiornaPrestiti()
{
dgv_prestiti.Rows.Clear();
var prestitiAttivi = prestiti.Where(p => p.Attivo).OrderBy(p => p.DataScadenza);
foreach (Prestito p in prestitiAttivi)
{
Libro libro = libri.FirstOrDefault(l => l.Id == p.IdLibro);
string titoloLibro = libro != null ? libro.Titolo : "Sconosciuto";
int rowIndex = dgv_prestiti.Rows.Add(
p.Id,
titoloLibro,
p.NomeUtente,
p.DataPrestito.ToString("dd/MM/yyyy"),
p.DataScadenza.ToString("dd/MM/yyyy"),
p.GiorniRimanenti
);
// Colora se scaduto
if (p.Scaduto)
{
dgv_prestiti.Rows[rowIndex].DefaultCellStyle.BackColor = Color.LightCoral;
}
else if (p.GiorniRimanenti <= 3)
{
dgv_prestiti.Rows[rowIndex].DefaultCellStyle.BackColor = Color.LightYellow;
}
}
}
// βββββββββββββββββββββββββββββββββββββββββββ
// TIMER: Controlla Scadenze
// βββββββββββββββββββββββββββββββββββββββββββ
private void timer_scadenze_Tick(object sender, EventArgs e)
{
int prestitiScaduti = prestiti.Count(p => p.Scaduto);
if (prestitiScaduti > 0)
{
toolStripStatusLabel.Text = $"β οΈ {prestitiScaduti} prestiti scaduti!";
toolStripStatusLabel.ForeColor = Color.Red;
}
}
Step 5: Statistiche
FormPrincipale.cs (Parte 4 - Statistiche)
// βββββββββββββββββββββββββββββββββββββββββββ
// AGGIORNA STATISTICHE
// βββββββββββββββββββββββββββββββββββββββββββ
private void AggiornaStatistiche()
{
// Totale libri
lbl_totale_libri.Text = $"Totale Libri: {libri.Count}";
// Libri disponibili
int disponibili = libri.Count(l => l.Disponibile);
lbl_disponibili.Text = $"Disponibili: {disponibili}";
// Prestiti attivi
int prestitiAttivi = prestiti.Count(p => p.Attivo);
lbl_prestiti_attivi.Text = $"Prestiti Attivi: {prestitiAttivi}";
// Autore piΓΉ presente
if (libri.Count > 0)
{
var autoreTop = libri.GroupBy(l => l.Autore)
.OrderByDescending(g => g.Count())
.First();
lbl_autore_top.Text = $"Autore Top: {autoreTop.Key} ({autoreTop.Count()} libri)";
}
// Categoria piΓΉ presente
if (libri.Count > 0)
{
var categoriaTop = libri.GroupBy(l => l.Categoria)
.OrderByDescending(g => g.Count())
.First();
lbl_categoria_top.Text = $"Categoria Top: {categoriaTop.Key} ({categoriaTop.Count()} libri)";
}
// Anno medio
if (libri.Count > 0)
{
int annoMedio = (int)libri.Average(l => l.Anno);
lbl_anno_medio.Text = $"Anno Medio: {annoMedio}";
}
}
Step 6: FormDettagliLibro
FormDettagliLibro.cs (Form Modale)
using System;
using System.Windows.Forms;
namespace GestionaleBiblioteca
{
public partial class FormDettagliLibro : Form
{
public Libro LibroCorrente { get; private set; }
private bool modalitaModifica = false;
// Costruttore per NUOVO libro
public FormDettagliLibro()
{
InitializeComponent();
this.Text = "Nuovo Libro";
LibroCorrente = new Libro();
modalitaModifica = false;
// Popola categorie
cmb_categoria.Items.AddRange(new string[]
{
"Fantasy", "Fantascienza", "Thriller",
"Saggio", "Biografia", "Storia", "Romanzo"
});
cmb_categoria.SelectedIndex = 0;
}
// Costruttore per MODIFICA libro esistente
public FormDettagliLibro(Libro libro) : this()
{
this.Text = "Modifica Libro";
LibroCorrente = libro;
modalitaModifica = true;
// Carica dati esistenti
txt_titolo.Text = libro.Titolo;
txt_autore.Text = libro.Autore;
num_anno.Value = libro.Anno;
num_pagine.Value = libro.Pagine;
cmb_categoria.SelectedItem = libro.Categoria;
}
// βββββββββββββββββββββββββββββββββββββββββββ
// PULSANTE: Salva
// βββββββββββββββββββββββββββββββββββββββββββ
private void btn_salva_Click(object sender, EventArgs e)
{
// Validazione
bool valido = true;
errorProvider.Clear();
if (string.IsNullOrWhiteSpace(txt_titolo.Text))
{
errorProvider.SetError(txt_titolo, "Titolo obbligatorio");
valido = false;
}
if (string.IsNullOrWhiteSpace(txt_autore.Text))
{
errorProvider.SetError(txt_autore, "Autore obbligatorio");
valido = false;
}
if (num_anno.Value < 1000 || num_anno.Value > DateTime.Now.Year)
{
errorProvider.SetError(num_anno, "Anno non valido");
valido = false;
}
if (num_pagine.Value < 1)
{
errorProvider.SetError(num_pagine, "Pagine devono essere > 0");
valido = false;
}
if (!valido)
{
MessageBox.Show("Correggi gli errori!", "Validazione",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
// Salva dati nel libro
LibroCorrente.Titolo = txt_titolo.Text.Trim();
LibroCorrente.Autore = txt_autore.Text.Trim();
LibroCorrente.Anno = (int)num_anno.Value;
LibroCorrente.Pagine = (int)num_pagine.Value;
LibroCorrente.Categoria = cmb_categoria.SelectedItem.ToString();
// Se non in modifica, Γ¨ disponibile di default
if (!modalitaModifica)
{
LibroCorrente.Disponibile = true;
LibroCorrente.DataAggiunta = DateTime.Now;
}
// Chiudi con OK
this.DialogResult = DialogResult.OK;
this.Close();
}
// βββββββββββββββββββββββββββββββββββββββββββ
// PULSANTE: Annulla
// βββββββββββββββββββββββββββββββββββββββββββ
private void btn_annulla_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
this.Close();
}
}
}
Checklist Finale
π Verifica di Aver Implementato Tutto
π Login:
- β Form login con validazione
- β Apre form principale se credenziali OK
- β Animazione errore
π Catalogo Libri:
- β DataGridView con tutti i libri
- β Aggiungi libro (form modale)
- β Modifica libro
- β Elimina libro (con conferma)
- β Colore diverso per libri non disponibili
π€ Prestiti:
- β Crea nuovo prestito
- β Visualizza prestiti attivi
- β Restituisci libro
- β Evidenzia prestiti scaduti
- β Timer controllo scadenze
π Ricerca:
- β Ricerca per titolo/autore/categoria
- β Mostra risultati filtrati
π Statistiche:
- β Totale libri
- β Libri disponibili
- β Prestiti attivi
- β Autore/categoria top
πΎ Persistenza Dati:
- β Salvataggio su file CSV
- β Caricamento all'avvio
- β Gestione errori (try-catch)
π¨ UI/UX:
- β StatusStrip con info
- β ErrorProvider validazione
- β MenuStrip (File, Modifica, Aiuto)
- β ToolTip su controlli
- β Icone/emoji per rendere accattivante
Possibili Migliorie (Bonus)
π‘ Se Vuoi Andare Oltre:
- Stampa Report: Genera PDF o documento Word con statistiche
- Grafici: Usa Chart per mostrare grafici a barre/torta
- Export/Import Excel: Esporta catalogo in Excel
- Foto Copertine: Aggiungi PictureBox per mostrare copertine
- Sistema Voti: Gli utenti possono votare i libri (1-5 stelle)
- Notifiche Email: Invia promemoria scadenze via email
- Multi-Utente: Login diversi con permessi diversi (admin/utente)
- Database Vero: Passa da CSV a SQLite o SQL Server
- Temi: ModalitΓ chiara/scura
- Storico: Salva cronologia di tutti i prestiti passati
π CONGRATULAZIONI!
Hai completato il percorso Windows Forms! Ora sai creare applicazioni desktop professionali con interfacce grafiche complete. Questo progetto dimostra che hai padronanza di:
β Tutti i controlli principali β Gestione eventi β Architettura multi-finestra β Persistenza dati β Validazione professionale β LINQ e collezioni β Problem solving
Sei pronto per il prossimo livello: la programmazione teorica! π
π Teoria collegata (percorso C# β il linguaggio, esempi in console): Programmazione a oggetti: i fondamenti, Lettura e scrittura dei file e Progetti ed esercizi.