Vai al contenuto

TabControl e DataGridView

Crea interfacce multi-scheda e visualizza dati in tabelle professionali.

Cosa Imparerai in Questa Lezione

Hai imparato i controlli base di Windows Forms. Ora Γ¨ il momento di creare interfacce professionali con piΓΉ pagine e tabelle di dati! Pensa a programmi come Excel con le sue schede in basso, o a gestionali con sezioni diverse. Oggi imparerai a crearli! πŸš€

✨ I Nuovi Super-Controlli

πŸ“‘ TabControl

Crea schede multiple come Excel! Ogni scheda puΓ² contenere controlli diversi. Perfetto per organizzare app complesse.

Es: Scheda "Anagrafica" | Scheda "Voti" | Scheda "Statistiche"

πŸ“Š DataGridView

Mostra dati in formato tabella come Excel! Colonne, righe, ordinamento. L'utente puΓ² modificare, aggiungere, eliminare.

Es: Tabella studenti con Nome | Cognome | Voto | Classe

πŸ“¦ Panel e FlowLayoutPanel

Contenitori per organizzare altri controlli. Panel Γ¨ fisso, FlowLayoutPanel dispone automaticamente i controlli in fila.

Es: Raggruppare tutti i pulsanti in un Panel in fondo al form

πŸ“š Prerequisiti

Prima di iniziare, dovresti aver completato:

  • βœ… Tutti i controlli base (TextBox, ListBox, ComboBox)
  • βœ… Gestione eventi multipli
  • βœ… RadioButton e CheckBox

Cos'Γ¨ il TabControl?

Analogia della Vita Reale

Hai presente i quaderni con i separatori colorati per dividere le materie? O un'agenda con schede per mesi diversi? Il TabControl Γ¨ esattamente questo: ti permette di creare piΓΉ "pagine" nella stessa finestra!

πŸ’‘ Esempi di TabControl nel Mondo Reale

  • Excel: Foglio1 | Foglio2 | Foglio3 (in basso)
  • Browser: Schede multiple per siti diversi
  • Impostazioni Windows: Sistema | Rete | Privacy
  • Gestionale Studenti: Anagrafica | Voti | Presenze

🎯 Quando Usare TabControl

βœ… USA quando:

  • Hai molti controlli da organizzare
  • L'app ha sezioni logiche separate
  • Vuoi un'interfaccia pulita e ordinata

❌ NON usare quando:

  • Hai solo 2-3 controlli semplici
  • Le sezioni sono troppo collegate
  • L'utente deve vedere tutto insieme

Esercizio 1: Gestionale Studenti con TabControl

Obiettivo

Creare un'applicazione per gestire studenti con 3 schede diverse: una per l'anagrafica, una per i voti, e una per le statistiche. Ogni scheda avrΓ  controlli specifici!

🎯 Cosa Imparerai:

  • Creare un TabControl con piΓΉ schede
  • Aggiungere controlli diversi in ogni scheda
  • Navigare tra le schede via codice
  • Passare dati tra schede diverse
  1. Crea l'Interfaccia con TabControl

πŸ“ Procedura Step-by-Step:

  1. Trascina un TabControl dalla Toolbox sul form
  2. Ridimensionalo per occupare quasi tutto il form
  3. Vedrai 2 schede di default: tabPage1 e tabPage2
  4. Per aggiungere una terza scheda:
    • Clicca con il tasto destro sul TabControl
    • Seleziona "Add Tab"
    • Oppure: vai in Properties β†’ TabPages β†’ clicca ... β†’ Add
  5. Rinomina le schede:
    • Seleziona tabPage1 β†’ Properties β†’ Text = "Anagrafica"
    • Seleziona tabPage2 β†’ Properties β†’ Text = "Voti"
    • Seleziona tabPage3 β†’ Properties β†’ Text = "Statistiche"

🎨 Aggiungi Controlli in Ogni Scheda:

πŸ“„ SCHEDA 1 - Anagrafica:

  • Label: "Nome:"
  • TextBox: txt_nome
  • Label: "Cognome:"
  • TextBox: txt_cognome
  • Label: "Classe:"
  • ComboBox: cmb_classe (1A, 2A, 3A, 4A, 5A)
  • Button: btn_salva_studente (testo: "Salva Studente")

πŸ“Š SCHEDA 2 - Voti:

  • Label: "Materia:"
  • TextBox: txt_materia
  • Label: "Voto:"
  • NumericUpDown: num_voto (Minimum=1, Maximum=10)
  • Button: btn_aggiungi_voto (testo: "Aggiungi Voto")
  • ListBox: lst_voti (per vedere i voti inseriti)

πŸ“ˆ SCHEDA 3 - Statistiche:

  • Label: lbl_media (testo: "Media: --")
  • Label: lbl_voto_max (testo: "Voto Max: --")
  • Label: lbl_voto_min (testo: "Voto Min: --")
  • Button: btn_calcola_statistiche (testo: "Calcola")
  1. Il Codice Completo

GestionaleStudenti.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace GestionaleStudenti
{
    public partial class Form1 : Form
    {
        // Lista per memorizzare i voti
        private List<double> voti = new List<double>();

        // Variabili per i dati studente
        private string nomeStudente = "";
        private string cognomeStudente = "";
        private string classeStudente = "";

        public Form1()
        {
            InitializeComponent();

            // Popola la ComboBox delle classi
            cmb_classe.Items.AddRange(new string[] { "1A", "2A", "3A", "4A", "5A" });
            cmb_classe.SelectedIndex = 0; // Seleziona il primo
        }

        // ═══════════════════════════════════════════
        // SCHEDA 1: SALVA DATI STUDENTE
        // ═══════════════════════════════════════════
        private void btn_salva_studente_Click(object sender, EventArgs e)
        {
            // Valida che i campi siano compilati
            if (string.IsNullOrWhiteSpace(txt_nome.Text))
            {
                MessageBox.Show("Inserisci il nome!", "Attenzione",
                              MessageBoxButtons.OK, MessageBoxIcon.Warning);
                txt_nome.Focus();
                return;
            }

            if (string.IsNullOrWhiteSpace(txt_cognome.Text))
            {
                MessageBox.Show("Inserisci il cognome!", "Attenzione",
                              MessageBoxButtons.OK, MessageBoxIcon.Warning);
                txt_cognome.Focus();
                return;
            }

            // Salva i dati
            nomeStudente = txt_nome.Text.Trim();
            cognomeStudente = txt_cognome.Text.Trim();
            classeStudente = cmb_classe.SelectedItem.ToString();

            // Conferma
            MessageBox.Show($"Studente salvato:\n{cognomeStudente} {nomeStudente}\nClasse: {classeStudente}",
                          "Successo",
                          MessageBoxButtons.OK,
                          MessageBoxIcon.Information);

            // NAVIGA AUTOMATICAMENTE ALLA SCHEDA VOTI
            tabControl1.SelectedIndex = 1; // 0=Anagrafica, 1=Voti, 2=Statistiche
        }

        // ═══════════════════════════════════════════
        // SCHEDA 2: AGGIUNGI VOTO
        // ═══════════════════════════════════════════
        private void btn_aggiungi_voto_Click(object sender, EventArgs e)
        {
            // Controlla che ci sia uno studente salvato
            if (string.IsNullOrEmpty(nomeStudente))
            {
                MessageBox.Show("Prima salva i dati dello studente nella scheda Anagrafica!",
                              "Attenzione",
                              MessageBoxButtons.OK,
                              MessageBoxIcon.Warning);

                // Torna alla scheda Anagrafica
                tabControl1.SelectedIndex = 0;
                return;
            }

            // Valida materia
            if (string.IsNullOrWhiteSpace(txt_materia.Text))
            {
                MessageBox.Show("Inserisci la materia!", "Attenzione",
                              MessageBoxButtons.OK, MessageBoxIcon.Warning);
                txt_materia.Focus();
                return;
            }

            // Prendi il voto
            double voto = (double)num_voto.Value;

            // Aggiungi alla lista
            voti.Add(voto);

            // Mostra nella ListBox
            lst_voti.Items.Add($"{txt_materia.Text}: {voto}");

            // Pulisci i campi per il prossimo inserimento
            txt_materia.Clear();
            num_voto.Value = 6; // Reset a 6
            txt_materia.Focus();
        }

        // ═══════════════════════════════════════════
        // SCHEDA 3: CALCOLA STATISTICHE
        // ═══════════════════════════════════════════
        private void btn_calcola_statistiche_Click(object sender, EventArgs e)
        {
            // Controlla che ci siano voti
            if (voti.Count == 0)
            {
                MessageBox.Show("Non ci sono voti da calcolare!\nAggiungi dei voti nella scheda Voti.",
                              "Attenzione",
                              MessageBoxButtons.OK,
                              MessageBoxIcon.Warning);

                // Vai alla scheda Voti
                tabControl1.SelectedIndex = 1;
                return;
            }

            // Calcola statistiche usando LINQ
            double media = voti.Average();
            double votoMax = voti.Max();
            double votoMin = voti.Min();

            // Mostra risultati
            lbl_media.Text = $"Media: {media:F2}"; // F2 = 2 decimali
            lbl_voto_max.Text = $"Voto Max: {votoMax}";
            lbl_voto_min.Text = $"Voto Min: {votoMin}";

            // Cambia colore in base alla media
            if (media >= 6)
            {
                lbl_media.ForeColor = Color.Green;
            }
            else
            {
                lbl_media.ForeColor = Color.Red;
            }
        }
    }
}
  1. 🎬 Come Funziona l'App

πŸ“‹ Flusso Completo:

1️⃣ Scheda Anagrafica:

  • L'utente inserisce: Nome = "Mario", Cognome = "Rossi", Classe = "3A"
  • Clicca "Salva Studente"
  • L'app passa automaticamente alla scheda Voti

2️⃣ Scheda Voti:

  • Inserisce: Materia = "Matematica", Voto = 8
  • Clicca "Aggiungi Voto" β†’ appare "Matematica: 8" nella ListBox
  • Inserisce: Materia = "Italiano", Voto = 7
  • Inserisce: Materia = "Inglese", Voto = 9
  • Totale: 3 voti inseriti

3️⃣ Scheda Statistiche:

  • Clicca "Calcola"
  • Media: 8.00 (verde perchΓ© β‰₯ 6)
  • Voto Max: 9
  • Voto Min: 7

Concetti Chiave - TabControl

tabControl.SelectedIndex

Cambia scheda via codice. Gli indici partono da 0.

tabControl1.SelectedIndex = 0;  // Prima scheda
tabControl1.SelectedIndex = 1;  // Seconda scheda
tabControl1.SelectedIndex = 2;  // Terza scheda

// Ottieni l'indice della scheda corrente
int schemaCorrente = tabControl1.SelectedIndex;

LINQ: .Average(), .Max(), .Min()

Metodi per calcolare statistiche su liste di numeri

List<double> numeri = new List<double> { 5, 8, 3, 9, 7 };

double media = numeri.Average();      // 6.4
double massimo = numeri.Max();        // 9
double minimo = numeri.Min();         // 3
int conteggio = numeri.Count;         // 5
double somma = numeri.Sum();          // 32

string.IsNullOrWhiteSpace()

Controlla se una stringa Γ¨ vuota, null, o contiene solo spazi

string.IsNullOrWhiteSpace("")          // true
string.IsNullOrWhiteSpace("   ")       // true (solo spazi)
string.IsNullOrWhiteSpace("Mario")     // false
string.IsNullOrWhiteSpace(null)        // true

Cos'Γ¨ il DataGridView?

Pensa a Excel!

Il DataGridView Γ¨ praticamente una tabella Excel dentro la tua applicazione! Ha colonne, righe, e puoi aggiungere/modificare/eliminare dati direttamente come in un foglio di calcolo.

πŸ’‘ Esempi di DataGridView

  • Registro Studenti: Nome | Cognome | Classe | Email | Telefono
  • Catalogo Prodotti: Codice | Nome | Prezzo | QuantitΓ  | Categoria
  • Lista Film: Titolo | Anno | Regista | Durata | Genere
  • Elenco Auto: Marca | Modello | Anno | Prezzo | Carburante

🎯 Vantaggi del DataGridView

  • βœ… Visualizza molti dati in poco spazio
  • βœ… L'utente puΓ² ordinare cliccando sulle colonne
  • βœ… Supporta modifica diretta delle celle
  • βœ… Puoi aggiungere/rimuovere righe facilmente
  • βœ… Selezione multipla di righe
  • βœ… Scrollbar automatici quando ci sono molti dati

Esercizio 2: Registro Libri con DataGridView

Obiettivo

Creare un'applicazione per gestire una biblioteca personale. I libri vengono mostrati in una tabella dove puoi aggiungere, rimuovere e modificare i libri direttamente!

  1. Crea l'Interfaccia

🎨 Controlli da Aggiungere:

  • DataGridView: dgv_libri
    • Ridimensionalo per occupare la maggior parte del form
    • Properties β†’ Dock = Fill (riempie tutto lo spazio)
  • Panel (in fondo con Dock = Bottom) contenente:
    • TextBox: txt_titolo (placeholder: "Titolo")
    • TextBox: txt_autore (placeholder: "Autore")
    • NumericUpDown: num_anno (Minimum=1900, Maximum=2025)
    • TextBox: txt_genere (placeholder: "Genere")
    • Button: btn_aggiungi (testo: "Aggiungi Libro")
    • Button: btn_rimuovi (testo: "Rimuovi Selezionato")
  1. Codice Completo

RegistroLibri.cs

using System;
using System.Windows.Forms;

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

            // Inizializza il DataGridView
            ImpostaDataGridView();
        }

        // ═══════════════════════════════════════════
        // SETUP INIZIALE: Crea le colonne
        // ═══════════════════════════════════════════
        private void ImpostaDataGridView()
        {
            // Pulisci colonne esistenti (se ci sono)
            dgv_libri.Columns.Clear();

            // Aggiungi colonne manualmente
            dgv_libri.Columns.Add("Titolo", "Titolo");
            dgv_libri.Columns.Add("Autore", "Autore");
            dgv_libri.Columns.Add("Anno", "Anno");
            dgv_libri.Columns.Add("Genere", "Genere");

            // Imposta larghezza colonne
            dgv_libri.Columns["Titolo"].Width = 250;
            dgv_libri.Columns["Autore"].Width = 200;
            dgv_libri.Columns["Anno"].Width = 80;
            dgv_libri.Columns["Genere"].Width = 150;

            // Impostazioni visualizzazione
            dgv_libri.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
            dgv_libri.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
            dgv_libri.MultiSelect = false; // Seleziona una riga alla volta
            dgv_libri.AllowUserToAddRows = false; // Nascondi riga vuota in fondo

            // Aggiungi alcuni libri di esempio
            AggiungiLibriDiEsempio();
        }

        // ═══════════════════════════════════════════
        // AGGIUNGI LIBRI DI ESEMPIO ALL'AVVIO
        // ═══════════════════════════════════════════
        private void AggiungiLibriDiEsempio()
        {
            dgv_libri.Rows.Add("Il Signore degli Anelli", "J.R.R. Tolkien", 1954, "Fantasy");
            dgv_libri.Rows.Add("1984", "George Orwell", 1949, "Distopico");
            dgv_libri.Rows.Add("Harry Potter e la Pietra Filosofale", "J.K. Rowling", 1997, "Fantasy");
        }

        // ═══════════════════════════════════════════
        // PULSANTE: Aggiungi Nuovo Libro
        // ═══════════════════════════════════════════
        private void btn_aggiungi_Click(object sender, EventArgs e)
        {
            // Validazione campi obbligatori
            if (string.IsNullOrWhiteSpace(txt_titolo.Text))
            {
                MessageBox.Show("Inserisci il titolo del libro!", "Attenzione",
                              MessageBoxButtons.OK, MessageBoxIcon.Warning);
                txt_titolo.Focus();
                return;
            }

            if (string.IsNullOrWhiteSpace(txt_autore.Text))
            {
                MessageBox.Show("Inserisci l'autore!", "Attenzione",
                              MessageBoxButtons.OK, MessageBoxIcon.Warning);
                txt_autore.Focus();
                return;
            }

            // Prendi i dati dai controlli
            string titolo = txt_titolo.Text.Trim();
            string autore = txt_autore.Text.Trim();
            int anno = (int)num_anno.Value;
            string genere = txt_genere.Text.Trim();

            // Se genere Γ¨ vuoto, metti "Non specificato"
            if (string.IsNullOrWhiteSpace(genere))
            {
                genere = "Non specificato";
            }

            // AGGIUNGI LA RIGA AL DATAGRIDVIEW
            dgv_libri.Rows.Add(titolo, autore, anno, genere);

            // Pulisci i campi per il prossimo inserimento
            txt_titolo.Clear();
            txt_autore.Clear();
            num_anno.Value = DateTime.Now.Year; // Anno corrente
            txt_genere.Clear();
            txt_titolo.Focus(); // Rimetti focus sul titolo

            // Messaggio di conferma
            MessageBox.Show($"Libro '{titolo}' aggiunto con successo!",
                          "Successo",
                          MessageBoxButtons.OK,
                          MessageBoxIcon.Information);
        }

        // ═══════════════════════════════════════════
        // PULSANTE: Rimuovi Libro Selezionato
        // ═══════════════════════════════════════════
        private void btn_rimuovi_Click(object sender, EventArgs e)
        {
            // Controlla che ci sia una riga selezionata
            if (dgv_libri.SelectedRows.Count == 0)
            {
                MessageBox.Show("Seleziona un libro da rimuovere!", "Attenzione",
                              MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            // Prendi la riga selezionata
            DataGridViewRow rigaSelezionata = dgv_libri.SelectedRows[0];

            // Prendi il titolo per la conferma
            string titolo = rigaSelezionata.Cells["Titolo"].Value.ToString();

            // Chiedi conferma
            DialogResult risultato = MessageBox.Show(
                $"Sei sicuro di voler eliminare '{titolo}'?",
                "Conferma Eliminazione",
                MessageBoxButtons.YesNo,
                MessageBoxIcon.Question);

            if (risultato == DialogResult.Yes)
            {
                // RIMUOVI LA RIGA
                dgv_libri.Rows.Remove(rigaSelezionata);

                MessageBox.Show("Libro rimosso!", "Successo",
                              MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }

        // ═══════════════════════════════════════════
        // EVENTO: Doppio Click su una Riga
        // ═══════════════════════════════════════════
        private void dgv_libri_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
        {
            // Ignora se clicchi sull'header
            if (e.RowIndex < 0) return;

            // Prendi la riga cliccata
            DataGridViewRow riga = dgv_libri.Rows[e.RowIndex];

            // Mostra i dettagli del libro
            string dettagli = $"DETTAGLI LIBRO:\n\n" +
                            $"Titolo: {riga.Cells["Titolo"].Value}\n" +
                            $"Autore: {riga.Cells["Autore"].Value}\n" +
                            $"Anno: {riga.Cells["Anno"].Value}\n" +
                            $"Genere: {riga.Cells["Genere"].Value}";

            MessageBox.Show(dettagli, "Dettagli Libro",
                          MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
    }
}
  1. πŸ”— Collegare gli Eventi

πŸ“ Eventi da Collegare:

  1. btn_aggiungi:
    • Event: Click
    • Handler: btn_aggiungi_Click
  2. btn_rimuovi:
    • Event: Click
    • Handler: btn_rimuovi_Click
  3. dgv_libri:
    • Event: CellDoubleClick
    • Handler: dgv_libri_CellDoubleClick

Concetti Chiave - DataGridView

dgv.Columns.Add()

Aggiunge una nuova colonna al DataGridView

// Sintassi: .Add(name, headerText)
dgv.Columns.Add("Nome", "Nome Completo");
dgv.Columns.Add("Eta", "EtΓ ");

// Imposta larghezza
dgv.Columns["Nome"].Width = 200;

dgv.Rows.Add()

Aggiunge una nuova riga con i valori specificati

// I valori devono essere nello stesso ordine delle colonne
dgv.Rows.Add("Mario Rossi", 25);
dgv.Rows.Add("Luigi Verdi", 30);

// Numero di righe totali
int totaleRighe = dgv.Rows.Count;

dgv.SelectedRows

Ottiene le righe selezionate dall'utente

// Controlla se c'Γ¨ una selezione
if (dgv.SelectedRows.Count > 0)
{
    // Prendi la prima riga selezionata
    DataGridViewRow riga = dgv.SelectedRows[0];

    // Leggi valore di una cella
    string nome = riga.Cells["Nome"].Value.ToString();
}

dgv.Rows.Remove()

Rimuove una riga specifica dal DataGridView

// Rimuovi riga selezionata
DataGridViewRow riga = dgv.SelectedRows[0];
dgv.Rows.Remove(riga);

// Rimuovi per indice
dgv.Rows.RemoveAt(0); // Rimuove la prima riga

Esercizio 3: Dashboard Completa

Obiettivo

Crea un'applicazione professionale che combina TabControl + DataGridView! SarΓ  un gestionale prodotti con 3 schede: Catalogo (con tabella), Aggiungi Prodotto, Statistiche.

🎯 Sfida Completa:

Questo esercizio integra tutto quello che hai imparato finora! È un'applicazione di livello professionale.

  1. Struttura dell'Applicazione

πŸ“‘ SCHEDA 1 - Catalogo:

  • DataGridView con colonne: Nome | Prezzo | QuantitΓ  | Categoria
  • TextBox per ricerca
  • Button "Cerca"
  • Button "Rimuovi Selezionato"

πŸ“ SCHEDA 2 - Aggiungi Prodotto:

  • TextBox: Nome Prodotto
  • NumericUpDown: Prezzo (0-10000)
  • NumericUpDown: QuantitΓ  (0-1000)
  • ComboBox: Categoria (Elettronica, Abbigliamento, Alimentari, Sport)
  • Button "Aggiungi al Catalogo"

πŸ“Š SCHEDA 3 - Statistiche:

  • Label: Totale Prodotti
  • Label: Valore Totale Magazzino
  • Label: Prodotto PiΓΉ Costoso
  • Label: Categoria con PiΓΉ Prodotti
  • Button "Aggiorna Statistiche"
  1. Codice Completo (Versione Pro!)

DashboardProdotti.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace DashboardProdotti
{
    public partial class Form1 : Form
    {
        // Classe per rappresentare un prodotto
        public class Prodotto
        {
            public string Nome { get; set; }
            public decimal Prezzo { get; set; }
            public int Quantita { get; set; }
            public string Categoria { get; set; }

            public decimal ValoreTotale => Prezzo * Quantita;
        }

        // Lista prodotti
        private List<Prodotto> prodotti = new List<Prodotto>();

        public Form1()
        {
            InitializeComponent();

            // Setup iniziale
            ImpostaDataGridView();
            ImpostaComboBoxCategorie();
            AggiungiProdottiDiEsempio();
        }

        // ═══════════════════════════════════════════
        // SETUP: DataGridView
        // ═══════════════════════════════════════════
        private void ImpostaDataGridView()
        {
            dgv_catalogo.Columns.Clear();
            dgv_catalogo.Columns.Add("Nome", "Nome Prodotto");
            dgv_catalogo.Columns.Add("Prezzo", "Prezzo (€)");
            dgv_catalogo.Columns.Add("Quantita", "QuantitΓ ");
            dgv_catalogo.Columns.Add("Categoria", "Categoria");

            dgv_catalogo.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
            dgv_catalogo.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
            dgv_catalogo.MultiSelect = false;
            dgv_catalogo.AllowUserToAddRows = false;
        }

        // ═══════════════════════════════════════════
        // SETUP: ComboBox Categorie
        // ═══════════════════════════════════════════
        private void ImpostaComboBoxCategorie()
        {
            cmb_categoria.Items.AddRange(new string[]
            {
                "Elettronica",
                "Abbigliamento",
                "Alimentari",
                "Sport"
            });
            cmb_categoria.SelectedIndex = 0;
        }

        // ═══════════════════════════════════════════
        // AGGIUNGI PRODOTTI DI ESEMPIO
        // ═══════════════════════════════════════════
        private void AggiungiProdottiDiEsempio()
        {
            prodotti.Add(new Prodotto
            {
                Nome = "Smartphone",
                Prezzo = 599.99m,
                Quantita = 15,
                Categoria = "Elettronica"
            });

            prodotti.Add(new Prodotto
            {
                Nome = "T-Shirt",
                Prezzo = 19.99m,
                Quantita = 50,
                Categoria = "Abbigliamento"
            });

            prodotti.Add(new Prodotto
            {
                Nome = "Pasta",
                Prezzo = 1.50m,
                Quantita = 200,
                Categoria = "Alimentari"
            });

            AggiornaDataGridView();
        }

        // ═══════════════════════════════════════════
        // AGGIORNA IL DATAGRIDVIEW CON I PRODOTTI
        // ═══════════════════════════════════════════
        private void AggiornaDataGridView()
        {
            dgv_catalogo.Rows.Clear();

            foreach (Prodotto p in prodotti)
            {
                dgv_catalogo.Rows.Add(
                    p.Nome,
                    p.Prezzo.ToString("F2"),
                    p.Quantita,
                    p.Categoria
                );
            }
        }

        // ═══════════════════════════════════════════
        // SCHEDA 2: AGGIUNGI PRODOTTO
        // ═══════════════════════════════════════════
        private void btn_aggiungi_prodotto_Click(object sender, EventArgs e)
        {
            // Validazione
            if (string.IsNullOrWhiteSpace(txt_nome_prodotto.Text))
            {
                MessageBox.Show("Inserisci il nome del prodotto!", "Attenzione",
                              MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            // Crea nuovo prodotto
            Prodotto nuovo = new Prodotto
            {
                Nome = txt_nome_prodotto.Text.Trim(),
                Prezzo = num_prezzo.Value,
                Quantita = (int)num_quantita.Value,
                Categoria = cmb_categoria.SelectedItem.ToString()
            };

            // Aggiungi alla lista
            prodotti.Add(nuovo);

            // Aggiorna DataGridView
            AggiornaDataGridView();

            // Pulisci campi
            txt_nome_prodotto.Clear();
            num_prezzo.Value = 0;
            num_quantita.Value = 1;
            cmb_categoria.SelectedIndex = 0;

            // Vai alla scheda Catalogo per vedere il prodotto
            tabControl1.SelectedIndex = 0;

            MessageBox.Show($"Prodotto '{nuovo.Nome}' aggiunto!", "Successo",
                          MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        // ═══════════════════════════════════════════
        // SCHEDA 1: CERCA PRODOTTO
        // ═══════════════════════════════════════════
        private void btn_cerca_Click(object sender, EventArgs e)
        {
            string termineRicerca = txt_ricerca.Text.Trim().ToLower();

            if (string.IsNullOrWhiteSpace(termineRicerca))
            {
                // Se vuoto, mostra tutti
                AggiornaDataGridView();
                return;
            }

            // Filtra prodotti
            var prodottiFiltrati = prodotti.Where(p =>
                p.Nome.ToLower().Contains(termineRicerca) ||
                p.Categoria.ToLower().Contains(termineRicerca)
            ).ToList();

            // Aggiorna DataGridView con i risultati filtrati
            dgv_catalogo.Rows.Clear();

            foreach (Prodotto p in prodottiFiltrati)
            {
                dgv_catalogo.Rows.Add(p.Nome, p.Prezzo.ToString("F2"),
                                     p.Quantita, p.Categoria);
            }

            if (prodottiFiltrati.Count == 0)
            {
                MessageBox.Show("Nessun prodotto trovato!", "Ricerca",
                              MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }

        // ═══════════════════════════════════════════
        // SCHEDA 1: RIMUOVI PRODOTTO
        // ═══════════════════════════════════════════
        private void btn_rimuovi_Click(object sender, EventArgs e)
        {
            if (dgv_catalogo.SelectedRows.Count == 0)
            {
                MessageBox.Show("Seleziona un prodotto!", "Attenzione",
                              MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            // Prendi la riga selezionata
            int indice = dgv_catalogo.SelectedRows[0].Index;
            string nome = dgv_catalogo.SelectedRows[0].Cells["Nome"].Value.ToString();

            // Conferma
            DialogResult ris = MessageBox.Show(
                $"Eliminare '{nome}'?",
                "Conferma",
                MessageBoxButtons.YesNo,
                MessageBoxIcon.Question);

            if (ris == DialogResult.Yes)
            {
                // Rimuovi dalla lista
                prodotti.RemoveAt(indice);

                // Aggiorna DataGridView
                AggiornaDataGridView();
            }
        }

        // ═══════════════════════════════════════════
        // SCHEDA 3: CALCOLA STATISTICHE
        // ═══════════════════════════════════════════
        private void btn_aggiorna_statistiche_Click(object sender, EventArgs e)
        {
            if (prodotti.Count == 0)
            {
                MessageBox.Show("Nessun prodotto nel catalogo!", "Attenzione",
                              MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            // Totale prodotti
            int totale = prodotti.Count;
            lbl_totale_prodotti.Text = $"Totale Prodotti: {totale}";

            // Valore totale magazzino
            decimal valoreTotale = prodotti.Sum(p => p.ValoreTotale);
            lbl_valore_totale.Text = $"Valore Magazzino: {valoreTotale:C}";

            // Prodotto piΓΉ costoso
            Prodotto piuCostoso = prodotti.OrderByDescending(p => p.Prezzo).First();
            lbl_prodotto_costoso.Text = $"PiΓΉ Costoso: {piuCostoso.Nome} (€{piuCostoso.Prezzo:F2})";

            // Categoria con piΓΉ prodotti
            var gruppi = prodotti.GroupBy(p => p.Categoria)
                                .OrderByDescending(g => g.Count())
                                .First();
            lbl_categoria_top.Text = $"Categoria Top: {gruppi.Key} ({gruppi.Count()} prodotti)";
        }
    }
}

Concetti Avanzati Usati

LINQ: .Where()

Filtra una lista in base a una condizione

List<int> numeri = new List<int> { 1, 5, 10, 15, 20 };

// Filtra numeri maggiori di 10
var maggioriDi10 = numeri.Where(n => n > 10).ToList();
// Risultato: { 15, 20 }

// Filtra stringhe che contengono "a"
List<string> nomi = new List<string> { "Mario", "Anna", "Luigi" };
var conA = nomi.Where(n => n.Contains("a")).ToList();
// Risultato: { "Mario", "Anna" }

LINQ: .GroupBy()

Raggruppa elementi in base a una proprietΓ 

// Raggruppa prodotti per categoria
var gruppi = prodotti.GroupBy(p => p.Categoria);

foreach (var gruppo in gruppi)
{
    Console.WriteLine($"{gruppo.Key}: {gruppo.Count()} prodotti");
}

Classi Custom (Prodotto)

Creare classi personalizzate per rappresentare i dati

public class Prodotto
{
    public string Nome { get; set; }
    public decimal Prezzo { get; set; }

    // ProprietΓ  calcolata
    public decimal ValoreTotale => Prezzo * Quantita;
}

// Uso
Prodotto p = new Prodotto { Nome = "Telefono", Prezzo = 599 };
Console.WriteLine(p.Nome); // Telefono

Bonus: Panel e Layout

A Cosa Servono Panel e FlowLayoutPanel?

Sono contenitori per organizzare altri controlli. Invece di posizionare manualmente 10 pulsanti, li metti in un Panel e sposti solo quello!

πŸ“¦ Panel

Contenitore fisso. I controlli stanno dove li metti.

πŸ’‘ Quando usarlo:

  • Raggruppare pulsanti in fondo al form
  • Creare sezioni visive separate
  • Con Dock per posizionamento automatico

🌊 FlowLayoutPanel

Dispone i controlli automaticamente in fila, come il testo.

πŸ’‘ Quando usarlo:

  • Toolbar con molti pulsanti
  • Gallery di immagini
  • Liste di tag/bottoni dinamici

🎯 Proprietà Importante: Dock

La proprietΓ  Dock attacca un controllo ai bordi del form:

  • Dock = Top: Si attacca in alto, occupa tutta la larghezza
  • Dock = Bottom: Si attacca in fondo
  • Dock = Left/Right: Si attacca a sinistra/destra
  • Dock = Fill: Riempie tutto lo spazio rimanente

πŸ’‘ Esempio pratico: Panel con pulsanti in fondo (Dock=Bottom) + DataGridView che riempie il resto (Dock=Fill)

Riepilogo: Cosa Hai Imparato

✨ Nuove Super-Competenze

1️⃣ TabControl

  • Creare schede multiple
  • Navigare tra schede via codice
  • Organizzare app complesse

2️⃣ DataGridView

  • Mostrare dati in tabelle
  • Aggiungere/rimuovere righe
  • Accedere a celle specifiche

3️⃣ LINQ Base

  • .Where() per filtrare
  • .Average(), .Max(), .Min()
  • .GroupBy() per raggruppare

4️⃣ Organizzazione

  • Panel e FlowLayoutPanel
  • ProprietΓ  Dock
  • Layout professionali

Esercizi per Allenarsi

Sfide Progressive

⭐ Livello Facile:

  1. Crea un TabControl con 2 schede: "Lista Spesa" e "Totale". Nella prima aggiungi prodotti, nella seconda mostra il totale.
  2. Crea un DataGridView che mostra una lista di film (Titolo, Anno, Genere) con pulsante per aggiungerne di nuovi.

⭐⭐ Livello Medio:

  1. Gestionale Contatti con TabControl (Anagrafica | Lista | Ricerca) e DataGridView per visualizzare tutti i contatti.
  2. Registro Presenze: DataGridView con Nome Studente, Data, Presente/Assente. Aggiungi filtro per cercare assenze di uno studente specifico.

⭐⭐⭐ Livello Difficile:

  1. Crea un Gestionale Videonoleggio completo con: TabControl (Catalogo Film | Prestiti | Statistiche), DataGridView per i film, salvataggio su file CSV.
  2. Dashboard Aziendale: Dipendenti, Progetti, Assegnazioni. Usa TabControl, DataGridView, e LINQ per statistiche avanzate (chi lavora su cosa, progetti piΓΉ impegnativi, ecc.).

πŸš€ Prossima Lezione

Hai imparato a organizzare interfacce complesse! Nella prossima lezione scoprirai Timer e ProgressBar per creare applicazioni che si aggiornano nel tempo: orologi, cronometri, quiz a tempo e molto altro! ⏱️

πŸ“š Teoria collegata (percorso C# – il linguaggio, esempi in console): Array e collezioni.