⚡ Gestione Avanzata di Eventi Multipli

Scrivi meno codice, ottieni più funzionalità: il segreto del parametro sender

🎯 Il Salto dalla Lezione 5: Da Oggetti a Eventi Intelligenti

Nella Lezione 5 hai imparato a gestire oggetti complessi come Automobile
con liste List<Automobile>. Hai creato CRUD completo, ordinamenti e statistiche.
Ma c’è un problema che probabilmente hai notato…

❌ Il Problema della Duplicazione del Codice

Parco Auto – Codice Ripetitivo (Da Evitare)
// Nel Parco Auto avevi 4 TextBox: Modello, Cilindrata, Prezzo, Anno
// Probabilmente hai scritto qualcosa del genere:

private void txtModello_TextChanged(object sender, EventArgs e)
{
    if (string.IsNullOrEmpty(txtModello.Text))
    {
        lblErrore.Text = "Il modello è obbligatorio!";
        btnAggiungi.Enabled = false;
    }
    else
    {
        lblErrore.Text = "";
        VerificaCampiCompleti();
    }
}

private void txtCilindrata_TextChanged(object sender, EventArgs e)
{
    if (string.IsNullOrEmpty(txtCilindrata.Text))
    {
        lblErrore.Text = "La cilindrata è obbligatoria!";
        btnAggiungi.Enabled = false;
    }
    else
    {
        lblErrore.Text = "";
        VerificaCampiCompleti();
    }
}

// 😱 E devi ripetere questo per txtPrezzo e txtAnno!
// Codice quasi identico copiato 4 volte!

⚠️ Problemi di questo Approccio:

  • 📝 Codice duplicato: 4 metodi quasi identici
  • 🐛 Difficile manutenzione: Se cambi la logica, devi modificare 4 posti
  • ⏱️ Tempo sprecato: Scrivi e debugghi lo stesso codice più volte
  • 💥 Errori facili: Dimentichi di aggiornare un metodo = bug

✅ La Soluzione: UN Metodo per TUTTE le TextBox

Approccio Professionale con sender
// UN SOLO metodo gestisce TUTTE le TextBox!
private void TextBox_TextChanged(object sender, EventArgs e)
{
    // 1️⃣ Scopri QUALE TextBox ha generato l'evento
    TextBox textBoxCorrente = (TextBox)sender;
    
    // 2️⃣ Controlla se è vuota
    if (string.IsNullOrEmpty(textBoxCorrente.Text))
    {
        lblErrore.Text = $"Il campo {textBoxCorrente.Name} è obbligatorio!";
        btnAggiungi.Enabled = false;
    }
    else
    {
        lblErrore.Text = "";
        VerificaCampiCompleti();
    }
}

// 🎉 Questo metodo funziona per txtModello, txtCilindrata, txtPrezzo, txtAnno
// e qualsiasi altra TextBox che aggiungerai in futuro!

✅ Vantaggi Immediati:

  • ✂️ Meno codice: 1 metodo invece di 4 (75% in meno!)
  • 🔧 Manutenzione facile: Modifichi un solo punto
  • 🚀 Scalabile: Aggiungi nuove TextBox senza scrivere nuovo codice
  • 🎯 Professionale: Codice pulito e DRY (Don’t Repeat Yourself)

📚 Cosa Imparerai in Questa Lezione
  • Il Parametro sender: Scoprire quale controllo ha generato l’evento
  • Casting di Controlli: Convertire sender nel tipo specifico (TextBox, Button, etc.)
  • Un Metodo, Più Controlli: Condividere la logica tra controlli simili
  • Validazione Dinamica: Feedback in tempo reale mentre l’utente digita
  • Eventi Multipli sullo Stesso Controllo: Enter, Leave, KeyPress insieme
  • Best Practices: Scrivere codice pulito e manutenibile
🔗 Collegamento con le Lezioni Precedenti

Lezione 2 (Eventi Base): Hai usato btnCalcola_Click per un solo pulsante
Lezione 5 (Parco Auto): Hai gestito eventi separati per ogni TextBox
Lezione 6 (Questa): Imparerai a unificare la gestione eventi risparmiando codice

🔑 Il Segreto: Il Parametro “sender”

🧐 Cos’è questo Misterioso “sender”?

Ogni volta che crei un evento in C# (come Button_Click o TextBox_TextChanged),
Visual Studio genera automaticamente un metodo con questa firma:

private void NomeMetodo(object sender, EventArgs e)
{
    // Il tuo codice qui
}

Ma cosa sono sender e EventArgs e? Vediamoli uno per uno.

📦 object sender

sender è un riferimento all’oggetto (controllo) che ha generato l’evento.
In pratica, ti dice CHI ha chiamato questo metodo.

🎭 Analogia della Vita Reale:

Immagina di essere un professore con 30 studenti. Quando uno studente alza la mano,
tu devi sapere CHI ha alzato la mano per dargli la parola.
Il parametro sender è proprio questo: ti dice quale studente
(quale controllo) ha “alzato la mano” (generato l’evento).

Senza sender, sapresti solo che “qualcuno ha alzato la mano”, ma non chi.
Con sender, sai esattamente: “È stato Mario!” (È stata txtNome!).

📋 EventArgs e

EventArgs e contiene informazioni aggiuntive sull’evento.
Per eventi semplici come Click o TextChanged, di solito è vuoto.
Per eventi più complessi (come KeyPress), contiene dati utili come
“quale tasto è stato premuto”.

💡 Nota: In questa lezione ci concentreremo principalmente su sender,
che è il più importante per gestire eventi multipli. Vedremo EventArgs negli esempi avanzati.

🔍 Esempio Pratico: Capire sender

Esperimento con sender
// Crea 3 pulsanti: btnPulsante1, btnPulsante2, btnPulsante3
// Collega tutti e 3 allo STESSO metodo Button_Click

private void Button_Click(object sender, EventArgs e)
{
    // sender è un "object" generico
    // Dobbiamo dire a C# che in realtà è un Button
    Button pulsantePremuto = (Button)sender;
    
    // Ora possiamo usare tutte le proprietà di Button!
    MessageBox.Show($"Hai premuto: {pulsantePremuto.Text}");
    
    // Esempi di cosa puoi fare con sender:
    string nome = pulsantePremuto.Name;           // "btnPulsante1"
    string testo = pulsantePremuto.Text;          // "Clicca qui"
    pulsantePremuto.BackColor = Color.Green;      // Cambia colore
    pulsantePremuto.Enabled = false;              // Disabilita il pulsante
}

🧪

Esperimento da Provare Subito!

  1. Crea un nuovo Windows Form
  2. Aggiungi 3 Button: btnRosso, btnVerde, btnBlu
  3. Imposta la proprietà Text rispettivamente a “Rosso”, “Verde”, “Blu”
  4. Seleziona tutti e 3 i pulsanti, vai in Proprietà → Eventi → Click
  5. Seleziona lo stesso metodo per tutti e 3 (es: Button_Click)
  6. Scrivi questo codice:
private void Button_Click(object sender, EventArgs e)
{
    Button btn = (Button)sender;
    
    // Cambia il colore del form in base al pulsante premuto
    if (btn.Text == "Rosso")
        this.BackColor = Color.Red;
    else if (btn.Text == "Verde")
        this.BackColor = Color.Green;
    else if (btn.Text == "Blu")
        this.BackColor = Color.Blue;
}

🎉 Risultato: Un solo metodo controlla 3 pulsanti diversi!
Premi ciascun pulsante e guarda il form cambiare colore.

🔄 Casting: Trasformare sender nel Tipo Giusto

❓ Perché sender è “object” e non direttamente “TextBox” o “Button”?

Perché sender può essere qualsiasi tipo di controllo!
Può essere un Button, una TextBox, una ComboBox, un CheckBox, ecc.
Per questo motivo, C# usa il tipo più generico: object.

Ma se è un object, come facciamo ad accedere alle proprietà specifiche di una TextBox
(come .Text) o di un Button (come .Text)?

📦 Analogia del Pacco Generico:

  • object sender = Hai ricevuto un pacco generico
  • Non sai cosa c’è dentro finché non lo apri
  • (TextBox)sender = Apri il pacco e scopri che è una scarpa Nike
  • Ora puoi usarla come scarpa (allacciarla, indossarla, ecc.)

🎯 Come Funziona il Casting

Sintassi del Casting
// PRIMA del casting: sender è un object generico
object sender = ...;  // Non puoi usare .Text, .Name, ecc.

// CASTING: Trasforma object in TextBox
TextBox textBoxCorrente = (TextBox)sender;

// DOPO il casting: puoi usare tutte le proprietà di TextBox!
string testo = textBoxCorrente.Text;
string nome = textBoxCorrente.Name;
textBoxCorrente.BackColor = Color.Yellow;
int lunghezza = textBoxCorrente.Text.Length;

📖 Tipi di Casting più Comuni

(TextBox)sender
Converte sender in una TextBox. Usa questo quando gestisci eventi di TextBox come TextChanged, Enter, Leave.

private void TextBox_TextChanged(object sender, EventArgs e)
{
    TextBox txt = (TextBox)sender;
    
    // Ora puoi usare:
    string contenuto = txt.Text;
    bool vuoto = string.IsNullOrEmpty(txt.Text);
    txt.MaxLength = 50;
}

(Button)sender
Converte sender in un Button. Usa questo quando gestisci Click di più pulsanti con un solo metodo.

private void Button_Click(object sender, EventArgs e)
{
    Button btn = (Button)sender;
    
    // Ora puoi usare:
    string testoBtn = btn.Text;
    btn.Enabled = false;
    btn.BackColor = Color.Gray;
}

(ComboBox)sender / (ListBox)sender
Converte sender in ComboBox o ListBox. Utile per gestire SelectedIndexChanged di più controlli.

private void ComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
    ComboBox cmb = (ComboBox)sender;
    
    // Ora puoi usare:
    string selezione = cmb.SelectedItem.ToString();
    int indice = cmb.SelectedIndex;
}

⚠️ Cosa Succede se Sbagli il Casting?

Se provi a fare un casting sbagliato (es: convertire un Button in TextBox),
il programma va in errore (InvalidCastException).

// ❌ SBAGLIATO: Button_Click prova a convertire in TextBox
private void Button_Click(object sender, EventArgs e)
{
    TextBox txt = (TextBox)sender;  // 💥 ERRORE! sender è un Button, non una TextBox!
}

💡 Regola d’Oro: Fai il casting nel tipo corretto per l’evento:

  • TextBox_TextChanged → (TextBox)sender
  • Button_Click → (Button)sender
  • ComboBox_SelectedIndexChanged → (ComboBox)sender

💪 Esercizio 1: Form Contatto con Validazione Dinamica

▶ Obiettivo

Creare un form di contatto (Nome, Cognome, Email) che valida i campi in tempo reale
mentre l’utente digita. Il pulsante “Invia” si abilita automaticamente solo quando
tutti i campi sono validi.

🎯 Cosa Imparerai:

  • ✅ Usare sender per gestire più TextBox con un metodo
  • ✅ Validazione in tempo reale (feedback istantaneo)
  • ✅ Abilitare/disabilitare controlli dinamicamente
  • ✅ Feedback visivo con colori e messaggi
1

Crea l’Interfaccia Grafica

Controlli Necessari:

Tipo Name Proprietà Text Note
Label lblNome “Nome:”
TextBox txtNome vuoto Input nome
Label lblCognome “Cognome:”
TextBox txtCognome vuoto Input cognome
Label lblEmail “Email:”
TextBox txtEmail vuoto Input email
Label lblMessaggio vuoto ForeColor: Red, Font: Bold
Button btnInvia “Invia” Enabled: false

2

Il Codice Completo Spiegato

Form1.cs – Form Contatto
using System;
using System.Drawing;
using System.Windows.Forms;

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

        // ═══════════════════════════════════════════════════════
        // METODO PRINCIPALE: Gestisce TextChanged di TUTTE le TextBox
        // ═══════════════════════════════════════════════════════
        private void TextBox_TextChanged(object sender, EventArgs e)
        {
            // 🎯 STEP 1: Scopri QUALE TextBox ha generato l'evento
            TextBox textBoxCorrente = (TextBox)sender;
            
            // 🎨 STEP 2: Feedback visivo - mostra quale campo stai compilando
            lblMessaggio.Text = $"✏️ Stai scrivendo in: {textBoxCorrente.Name}";
            lblMessaggio.ForeColor = Color.Blue;
            
            // ✅ STEP 3: Verifica se TUTTI i campi sono compilati correttamente
            bool tuttiCampiValidi = VerificaFormCompleto();
            
            // 🔘 STEP 4: Abilita/disabilita il pulsante in base alla validazione
            btnInvia.Enabled = tuttiCampiValidi;
            
            // 💬 STEP 5: Aggiorna il messaggio di stato
            if (tuttiCampiValidi)
            {
                lblMessaggio.Text = "✅ Puoi inviare il form!";
                lblMessaggio.ForeColor = Color.Green;
            }
            else
            {
                lblMessaggio.Text = "❌ Compila tutti i campi correttamente";
                lblMessaggio.ForeColor = Color.Red;
            }
        }
        
        // ═══════════════════════════════════════════════════════
        // METODO HELPER: Controlla se il form è completo e valido
        // ═══════════════════════════════════════════════════════
        private bool VerificaFormCompleto()
        {
            // ✅ Validazione Nome: non vuoto e almeno 2 caratteri
            bool nomeValido = !string.IsNullOrEmpty(txtNome.Text) && 
                             txtNome.Text.Trim().Length >= 2;
            
            // ✅ Validazione Cognome: non vuoto e almeno 2 caratteri
            bool cognomeValido = !string.IsNullOrEmpty(txtCognome.Text) && 
                                txtCognome.Text.Trim().Length >= 2;
            
            // ✅ Validazione Email: non vuota E contiene @ E contiene .
            bool emailValida = !string.IsNullOrEmpty(txtEmail.Text) && 
                              txtEmail.Text.Contains("@") && 
                              txtEmail.Text.Contains(".");
            
            // Restituisci true SOLO se TUTTI e 3 sono validi
            return nomeValido && cognomeValido && emailValida;
        }
        
        // ═══════════════════════════════════════════════════════
        // EVENTO: Click del pulsante Invia
        // ═══════════════════════════════════════════════════════
        private void btnInvia_Click(object sender, EventArgs e)
        {
            // Crea il messaggio con i dati inseriti
            string messaggio = $"📧 DATI INVIATI CON SUCCESSO!\n\n" +
                             $"Nome: {txtNome.Text}\n" +
                             $"Cognome: {txtCognome.Text}\n" +
                             $"Email: {txtEmail.Text}";
            
            MessageBox.Show(messaggio, 
                          "✅ Invio Completato", 
                          MessageBoxButtons.OK, 
                          MessageBoxIcon.Information);
            
            // Pulisci il form dopo l'invio
            PulisciForm();
        }
        
        // ═══════════════════════════════════════════════════════
        // METODO HELPER: Pulisce tutti i campi
        // ═══════════════════════════════════════════════════════
        private void PulisciForm()
        {
            txtNome.Text = "";
            txtCognome.Text = "";
            txtEmail.Text = "";
            lblMessaggio.Text = "";
            btnInvia.Enabled = false;
            txtNome.Focus();  // Riporta il cursore sul primo campo
        }
    }
}

3

⚡ IMPORTANTISSIMO: Come Collegare l’Evento

Ora devi dire a Visual Studio che il metodo TextBox_TextChanged
deve essere chiamato quando scrivi nelle TextBox. Segui questa procedura:

📝 Procedura Passo-Passo:

  1. Per txtNome:

    • Clicca su txtNome nel Designer
    • Premi F4 per aprire la finestra Proprietà
    • Clicca sull’icona del fulmine ⚡ (Eventi)
    • Scorri fino a trovare TextChanged
    • Nel menu a tendina, seleziona TextBox_TextChanged
  2. Per txtCognome:

    • Ripeti la stessa procedura
    • Seleziona lo stesso metodo: TextBox_TextChanged
  3. Per txtEmail:

    • Ripeti la stessa procedura
    • Seleziona lo stesso metodo: TextBox_TextChanged
✨ Risultato: Tutte e 3 le TextBox useranno lo STESSO metodo!
Quando scrivi in una qualsiasi di esse, il metodo TextBox_TextChanged
verrà chiamato automaticamente, e sender conterrà il riferimento
alla TextBox specifica che ha generato l’evento.

🔍 Verifica Visiva nel Codice:

Dopo aver collegato gli eventi, apri il file Form1.Designer.cs
(espandi Form1.cs in Solution Explorer). Dovresti vedere qualcosa del genere:

Form1.Designer.cs (generato automaticamente)
// Collegamento eventi generato da Visual Studio
this.txtNome.TextChanged += new System.EventHandler(this.TextBox_TextChanged);
this.txtCognome.TextChanged += new System.EventHandler(this.TextBox_TextChanged);
this.txtEmail.TextChanged += new System.EventHandler(this.TextBox_TextChanged);

✅ Se vedi questo, gli eventi sono collegati correttamente!

4

🎬 Flusso di Esecuzione: Cosa Succede Passo-Passo

📋 Scenario Completo:

1️⃣ Utente scrive “Mario” in txtNome

  1. Viene chiamato TextBox_TextChanged
  2. sender contiene il riferimento a txtNome
  3. Casting: TextBox textBoxCorrente = (TextBox)sender;
  4. textBoxCorrente.Name = “txtNome”
  5. lblMessaggio mostra: “✏️ Stai scrivendo in: txtNome”
  6. Chiama VerificaFormCompleto():
    • nomeValido = true ✅ (Mario ha 5 caratteri)
    • cognomeValido = false ❌ (vuoto)
    • emailValida = false ❌ (vuoto)
    • Risultato: false
  7. btnInvia.Enabled = false; (pulsante disabilitato)
  8. lblMessaggio mostra: “❌ Compila tutti i campi correttamente” (rosso)
2️⃣ Utente scrive “Rossi” in txtCognome

  1. Viene chiamato TextBox_TextChanged
  2. sender contiene il riferimento a txtCognome
  3. Casting: textBoxCorrente ora punta a txtCognome
  4. textBoxCorrente.Name = “txtCognome”
  5. lblMessaggio mostra: “✏️ Stai scrivendo in: txtCognome”
  6. Chiama VerificaFormCompleto():
    • nomeValido = true ✅
    • cognomeValido = true ✅ (Rossi ha 5 caratteri)
    • emailValida = false ❌ (ancora vuoto)
    • Risultato: false
  7. btnInvia.Enabled = false; (ancora disabilitato)
  8. lblMessaggio: “❌ Compila tutti i campi correttamente” (rosso)
3️⃣ Utente scrive “mario@gmail.com” in txtEmail

  1. Viene chiamato TextBox_TextChanged
  2. sender contiene il riferimento a txtEmail
  3. Casting: textBoxCorrente ora punta a txtEmail
  4. textBoxCorrente.Name = “txtEmail”
  5. lblMessaggio mostra: “✏️ Stai scrivendo in: txtEmail”
  6. Chiama VerificaFormCompleto():
    • nomeValido = true ✅
    • cognomeValido = true ✅
    • emailValida = true ✅ (contiene @ e .)
    • Risultato: TRUE! 🎉
  7. btnInvia.Enabled = true;Pulsante si abilita!
  8. lblMessaggio mostra: “✅ Puoi inviare il form!” (verde)
4️⃣ Utente clicca “Invia”

  1. Viene chiamato btnInvia_Click
  2. MessageBox mostra i dati inseriti
  3. Chiama PulisciForm():
    • Tutti i campi tornano vuoti
    • btnInvia torna disabilitato
    • Il cursore torna su txtNome

📚 Concetti Chiave dell’Esercizio 1

string.IsNullOrEmpty()
Verifica se una stringa è null (non esiste) o vuota (“”). Fondamentale per validare campi obbligatori.

// Esempi:
string.IsNullOrEmpty("")           // true (stringa vuota)
string.IsNullOrEmpty("Mario")      // false (ha contenuto)
string.IsNullOrEmpty(null)         // true (non esiste)

// Uso tipico:
if (string.IsNullOrEmpty(txtNome.Text))
{
    MessageBox.Show("Il nome è obbligatorio!");
}

.Trim()
Rimuove spazi bianchi all’inizio e alla fine di una stringa. Utile per evitare che l’utente “inganni” la validazione con solo spazi.

// Esempi:
"  Mario  ".Trim()       // "Mario"
"   ".Trim()             // "" (stringa vuota)
"Mario Rossi".Trim()     // "Mario Rossi" (spazi interni rimangono)

// Uso con validazione:
string nome = txtNome.Text.Trim();
if (nome.Length >= 2)  // Ignora spazi iniziali/finali
{
    // Nome valido
}

.Contains()
Verifica se una stringa contiene un’altra stringa o carattere. Perfetto per validazioni semplici come email.

// Esempi:
"mario@gmail.com".Contains("@")     // true
"ciao mondo".Contains("mondo")      // true
"TESTO".Contains("test")            // false (case-sensitive!)

// Validazione email semplice:
bool emailValida = txtEmail.Text.Contains("@") && 
                  txtEmail.Text.Contains(".");

Operatore && (AND logico)
Restituisce true solo se TUTTE le condizioni sono vere. Usato per combinare validazioni multiple.

// Tabella di verità:
true && true        // true
true && false       // false
false && false      // false

// Uso pratico - tutte devono essere vere:
bool formValido = !string.IsNullOrEmpty(txtNome.Text) && 
                  !string.IsNullOrEmpty(txtCognome.Text) &&
                  txtEmail.Text.Contains("@");
// formValido è true SOLO se Nome OK E Cognome OK E Email OK

.Focus()
Sposta il cursore su un controllo specifico. Utile per guidare l’utente o riportarlo a un campo dopo la pulizia.

// Esempi:
txtNome.Focus();        // Cursore va su txtNome
txtEmail.Focus();       // Cursore va su txtEmail

// Uso dopo la pulizia del form:
private void PulisciForm()
{
    txtNome.Text = "";
    txtCognome.Text = "";
    txtEmail.Text = "";
    txtNome.Focus();  // Riporta il cursore sul primo campo
}

🧮 Esercizio 2: Calcolatrice IMC con Feedback Visivo Avanzato

▶ Obiettivo

Creare una calcolatrice dell’Indice di Massa Corporea (IMC) che calcola automaticamente
il risultato mentre l’utente digita e cambia colore e messaggio in base
alla categoria di peso.

📖 Cos’è l’IMC (Indice di Massa Corporea)?

L’IMC è un indicatore numerico che valuta se il tuo peso è proporzionato alla tua altezza.
Si calcola con questa formula matematica:

IMC = Peso (kg) / (Altezza (m) × Altezza (m))
💡 Esempio Pratico:

  • Peso: 70 kg
  • Altezza: 1.75 m
  • Calcolo: 70 / (1.75 × 1.75) = 70 / 3.0625 = 22.86
  • Categoria: Normopeso ✅ (18.5 – 24.9)
📊 Categorie IMC (OMS):

  • IMC < 18.5: Sottopeso
  • IMC 18.5 – 24.9: Normopeso (ideale) ✅
  • IMC 25.0 – 29.9: Sovrappeso
  • IMC ≥ 30.0: Obesità

1

Crea l’Interfaccia

Tipo Name Text Proprietà Speciali
Label lblPeso “Peso (kg):”
TextBox txtPeso vuoto
Label lblAltezza “Altezza (m):”
TextBox txtAltezza vuoto
Label lblRisultato “IMC: –“ Font: 16pt, Bold
Label lblCategoria vuoto Font: 12pt

2

Il Codice Completo

CalcolatriceIMC.cs
using System;
using System.Drawing;
using System.Windows.Forms;

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

        // ═══════════════════════════════════════════════════════
        // METODO UNICO: Calcola IMC quando scrivi in txtPeso o txtAltezza
        // ═══════════════════════════════════════════════════════
        private void TextBox_TextChanged(object sender, EventArgs e)
        {
            // 🎯 sender non serve qui perché controlliamo entrambi i campi
            // Ma lo usiamo comunque per mostrare il concetto
            TextBox txtCorrente = (TextBox)sender;
            
            // Calcola solo se ENTRAMBI i campi contengono valori numerici validi
            if (ValidaNumeri())
            {
                CalcolaEMostraIMC();
            }
            else
            {
                // Se i dati non sono validi, resetta la visualizzazione
                ResetVisualizzazione();
            }
        }

        // ═══════════════════════════════════════════════════════
        // METODO: Valida che entrambi i campi contengano numeri
        // ═══════════════════════════════════════════════════════
        private bool ValidaNumeri()
        {
            // TryParse prova a convertire, restituisce true se riesce
            
            // Valida peso: deve essere un numero > 0
            bool pesoValido = double.TryParse(txtPeso.Text, out double peso) && 
                             peso > 0 && 
                             peso <= 300;  // Limite ragionevole
            
            // Valida altezza: deve essere un numero > 0 e < 3 metri
            bool altezzaValida = double.TryParse(txtAltezza.Text, out double altezza) && 
                                altezza > 0 && 
                                altezza < 3.0;
            
            // Restituisci true solo se entrambi sono validi
            return pesoValido && altezzaValida;
        }

        // ═══════════════════════════════════════════════════════
        // METODO: Calcola l'IMC e mostra il risultato con colori
        // ═══════════════════════════════════════════════════════
        private void CalcolaEMostraIMC()
        {
            // Converti i testi in numeri (sappiamo già che sono validi)
            double peso = Convert.ToDouble(txtPeso.Text);
            double altezza = Convert.ToDouble(txtAltezza.Text);
            
            // 📐 FORMULA IMC: peso / (altezza × altezza)
            double imc = peso / (altezza * altezza);
            
            // Mostra il risultato arrotondato a 2 decimali
            lblRisultato.Text = $"IMC: {imc:F2}";
            
            // 🎨 Determina categoria e colore in base al valore IMC
            if (imc < 18.5)
            {
                // Sottopeso
                lblCategoria.Text = "📉 Sottopeso";
                lblCategoria.ForeColor = Color.Blue;
                lblRisultato.ForeColor = Color.Blue;
            }
            else if (imc >= 18.5 && imc < 25)
            {
                // Normopeso (ideale)
                lblCategoria.Text = "✅ Normopeso - Peso Ideale!";
                lblCategoria.ForeColor = Color.Green;
                lblRisultato.ForeColor = Color.Green;
            }
            else if (imc >= 25 && imc < 30)
            {
                // Sovrappeso
                lblCategoria.Text = "⚠️ Sovrappeso";
                lblCategoria.ForeColor = Color.Orange;
                lblRisultato.ForeColor = Color.Orange;
            }
            else
            {
                // Obesità (IMC >= 30)
                lblCategoria.Text = "❌ Obesità";
                lblCategoria.ForeColor = Color.Red;
                lblRisultato.ForeColor = Color.Red;
            }
        }

        // ═══════════════════════════════════════════════════════
        // METODO: Resetta la visualizzazione
        // ═══════════════════════════════════════════════════════
        private void ResetVisualizzazione()
        {
            lblRisultato.Text = "IMC: --";
            lblRisultato.ForeColor = Color.Black;
            lblCategoria.Text = "Inserisci peso e altezza validi";
            lblCategoria.ForeColor = Color.Gray;
        }
    }
}

3

Collegamento Eventi

Per txtPeso:

  1. Seleziona txtPeso nel Designer
  2. F4 → Fulmine ⚡ → TextChanged
  3. Seleziona TextBox_TextChanged

Per txtAltezza:

  1. Seleziona txtAltezza nel Designer
  2. F4 → Fulmine ⚡ → TextChanged
  3. Seleziona lo stesso metodo: TextBox_TextChanged
✅ Risultato: Entrambe le TextBox condividono lo stesso metodo.
Ogni volta che scrivi in una di esse, il calcolo dell’IMC viene eseguito automaticamente!

4

🎨 Cosa Succede Visivamente: Scenari d’Uso

📝 Scenario 1: Inserimento Peso

  • Utente digita “70” in txtPeso
  • Viene chiamato TextBox_TextChanged
  • ValidaNumeri() controlla:
    • pesoValido = true ✅ (70 è valido)
    • altezzaValida = false ❌ (campo vuoto)
    • Risultato: false
  • Chiama ResetVisualizzazione()
  • Mostra: “IMC: –” / “Inserisci peso e altezza validi” (grigio)
📝 Scenario 2: Inserimento Altezza

  • Utente digita “1.75” in txtAltezza
  • Viene chiamato TextBox_TextChanged
  • ValidaNumeri() controlla:
    • pesoValido = true
    • altezzaValida = true
    • Risultato: TRUE! 🎉
  • Chiama CalcolaEMostraIMC()
  • Calcola: 70 / (1.75 × 1.75) = 22.86
  • 22.86 è tra 18.5 e 25 → Normopeso
  • Mostra: “IMC: 22.86” (VERDE)
  • Mostra: “✅ Normopeso – Peso Ideale!” (VERDE)
📝 Scenario 3: Modifica Peso (Obesità)

  • Utente cambia peso da “70” a “95”
  • Viene chiamato TextBox_TextChanged
  • ValidaNumeri() → true ✅
  • Ricalcola automaticamente: 95 / (1.75 × 1.75) = 31.02
  • 31.02 è ≥ 30 → Obesità
  • Mostra: “IMC: 31.02” (ROSSO)
  • Mostra: “❌ Obesità” (ROSSO)
📝 Scenario 4: Modifica Altezza (Sovrappeso)

  • Peso rimane “95”, altezza cambia da “1.75” a “1.85”
  • Ricalcola: 95 / (1.85 × 1.85) = 27.76
  • 27.76 è tra 25 e 30 → Sovrappeso
  • Mostra: “IMC: 27.76” (ARANCIONE)
  • Mostra: “⚠️ Sovrappeso” (ARANCIONE)

📚 Concetti Chiave dell’Esercizio 2

double.TryParse()
Prova a convertire una stringa in numero decimale (double). Restituisce true se la conversione riesce, false altrimenti. MOLTO più sicuro di Convert.ToDouble() perché non genera errori.

// Sintassi:
double.TryParse(stringa, out variabileDestinazione)

// Esempi:
bool ok1 = double.TryParse("12.5", out double numero1);
// ok1 = true, numero1 = 12.5

bool ok2 = double.TryParse("abc", out double numero2);
// ok2 = false, numero2 = 0 (valore di default)

// Uso pratico con validazione:
if (double.TryParse(txtPeso.Text, out double peso) && peso > 0)
{
    // Conversione riuscita E peso positivo
    Console.WriteLine($"Peso valido: {peso}");
}
else
{
    // Conversione fallita O peso <= 0
    MessageBox.Show("Inserisci un peso valido!");
}

💡 Perché usare TryParse invece di Convert?

  • Convert.ToDouble("abc") → 💥 ERRORE (eccezione)
  • double.TryParse("abc", out _) → ✅ false (nessun errore)
  • TryParse è ideale per validazione input utente

Formattazione :F2
Formatta un numero decimale con un numero fisso di cifre dopo la virgola. :F2 = 2 decimali.

// Esempi di formattazione:
double numero = 22.8571428;

// Vari formati:
numero.ToString()          // "22.8571428" (tutti i decimali)
numero.ToString("F2")      // "22.86" (2 decimali, arrotondato)
numero.ToString("F0")      // "23" (nessun decimale)
numero.ToString("F4")      // "22.8571" (4 decimali)

// Con string interpolation:
string risultato = $"IMC: {numero:F2}";  // "IMC: 22.86"

// Altri formati utili:
numero.ToString("N2")      // "22,86" (separatore migliaia)
numero.ToString("C2")      // "€ 22,86" (valuta con 2 decimali)

Color (cambiare colori dinamicamente)
Cambia il colore del testo (ForeColor) o dello sfondo (BackColor) di un controllo per dare feedback visivo all'utente.

// Colori predefiniti:
label.ForeColor = Color.Red;
label.ForeColor = Color.Green;
label.ForeColor = Color.Blue;
label.ForeColor = Color.Orange;
label.ForeColor = Color.Gray;
label.ForeColor = Color.Black;

// Colore personalizzato RGB (rosso, verde, blu) da 0 a 255:
label.ForeColor = Color.FromArgb(255, 100, 50);  // Arancione custom

// Colore di sfondo:
textBox.BackColor = Color.Yellow;
textBox.BackColor = Color.LightGray;

// Combinazione testo + sfondo:
label.ForeColor = Color.White;
label.BackColor = Color.DarkGreen;  // Testo bianco su sfondo verde scuro

🎨 Best Practice per Feedback Visivo:

  • Verde: Tutto OK, valore ideale
  • ⚠️ Arancione: Attenzione, valore border-line
  • Rosso: Errore, valore critico
  • ℹ️ Blu: Informazione neutra
  • Grigio: Disabilitato o non applicabile

Operatori di Confronto e Logici
Usati per confrontare valori e combinare condizioni nelle istruzioni if.

// Operatori di confronto:
imc < 18.5         // minore di
imc <= 18.5        // minore o uguale a
imc > 30           // maggiore di
imc >= 30          // maggiore o uguale a
imc == 25          // uguale a
imc != 25          // diverso da

// Operatori logici:
imc >= 18.5 && imc < 25    // AND: entrambe devono essere vere
imc < 18.5 || imc >= 30    // OR: almeno una deve essere vera
!(imc >= 25)               // NOT: inverte il valore (true diventa false)

// Esempi pratici nell'IMC:
if (imc >= 18.5 && imc < 25)
{
    // Normopeso: imc è maggiore o uguale a 18.5 E minore di 25
}

if (peso > 0 && altezza > 0)
{
    // Entrambi i valori sono positivi
}

🎯 Esercizio 3: Gestire Più Eventi sullo Stesso Controllo

🤔 Nuovo Concetto: Un Controllo, Più Eventi

Finora abbiamo visto come UN evento (es: TextChanged) può essere gestito da PIÙ controlli (più TextBox).
Ora facciamo il contrario: UN controllo può generare DIVERSI TIPI di eventi che vogliamo gestire contemporaneamente!

📋 Eventi Disponibili su una TextBox:

Evento Quando Si Attiva Uso Tipico
TextChanged Quando il testo cambia Validazione in tempo reale
Enter Quando il controllo riceve il focus (ci clicchi dentro o arrivi con Tab) Evidenziare campo attivo, mostrare suggerimenti
Leave Quando il controllo perde il focus (esci con Tab o click altrove) Validazione finale, formattazione dati
KeyPress Quando premi un tasto mentre la TextBox ha focus Bloccare caratteri non voluti (solo numeri, solo lettere)
💡 Perché Usare Più Eventi?

  • Enter: Cambia colore sfondo per evidenziare il campo attivo
  • Leave: Valida i dati quando l'utente finisce di scrivere
  • KeyPress: Blocca caratteri non validi (es: lettere in un campo numerico)
  • TextChanged: Feedback in tempo reale durante la digitazione

▶ Obiettivo

Creare TextBox intelligenti che:

  1. Cambiano colore quando ci clicchi dentro (Enter)
  2. Tornano al colore normale quando esci (Leave)
  3. Validano i dati quando esci dal campo (Leave)
  4. Bloccano caratteri non validi mentre scrivi (KeyPress)
1

Crea l'Interfaccia

Tipo Name Text/Descrizione
Label lblNome "Nome:"
TextBox txtNome Campo testo - validazione min 2 caratteri
Label lblEta "Età:"
TextBox txtEta Solo numeri - validazione 0-120
Label lblEmail "Email:"
TextBox txtEmail Validazione @ e .
Label lblMessaggio Feedback eventi (vuota inizialmente)

2

Il Codice Completo con Tutti gli Eventi

EventiMultipli.cs
using System;
using System.Drawing;
using System.Windows.Forms;

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

        // ═══════════════════════════════════════════════════════
        // EVENTO ENTER: Quando il cursore ENTRA nella TextBox
        // ═══════════════════════════════════════════════════════
        private void TextBox_Enter(object sender, EventArgs e)
        {
            // Converti sender in TextBox
            TextBox txt = (TextBox)sender;
            
            // 🎨 Cambia il colore di sfondo in giallo chiaro
            txt.BackColor = Color.LightYellow;
            
            // 💬 Mostra messaggio di feedback
            lblMessaggio.Text = $"✏️ Stai scrivendo in: {txt.Name}";
            lblMessaggio.ForeColor = Color.Blue;
        }

        // ═══════════════════════════════════════════════════════
        // EVENTO LEAVE: Quando il cursore ESCE dalla TextBox
        // ═══════════════════════════════════════════════════════
        private void TextBox_Leave(object sender, EventArgs e)
        {
            // Converti sender in TextBox
            TextBox txt = (TextBox)sender;
            
            // 🎨 Riporta il colore di sfondo a bianco
            txt.BackColor = Color.White;
            
            // ✅ VALIDAZIONE in base al campo specifico
            bool valido = true;
            string messaggioErrore = "";
            
            if (txt == txtNome)
            {
                // Valida NOME: min 2 caratteri
                if (string.IsNullOrEmpty(txt.Text) || txt.Text.Trim().Length < 2)
                {
                    valido = false;
                    messaggioErrore = "Il nome deve avere almeno 2 caratteri!";
                }
            }
            else if (txt == txtEta)
            {
                // Valida ETÀ: deve essere numero tra 0 e 120
                if (!int.TryParse(txt.Text, out int eta) || eta < 0 || eta > 120)
                {
                    valido = false;
                    messaggioErrore = "Inserisci un'età valida (0-120)!";
                }
            }
            else if (txt == txtEmail)
            {
                // Valida EMAIL: deve contenere @ e .
                if (!txt.Text.Contains("@") || !txt.Text.Contains("."))
                {
                    valido = false;
messaggioErrore = "Inserisci un'email valida (deve contenere @ e .)!";
}
}
        // Mostra errore se non valido
        if (!valido)
        {
            MessageBox.Show(messaggioErrore, 
                          "⚠️ Errore di Validazione", 
                          MessageBoxButtons.OK, 
                          MessageBoxIcon.Warning);
            
            // Rimetti il focus sulla TextBox per correggere l'errore
            txt.Focus();
            txt.SelectAll();  // Seleziona tutto il testo per sovrascrivere facilmente
            
            lblMessaggio.Text = "❌ Correggi il campo evidenziato";
            lblMessaggio.ForeColor = Color.Red;
        }
        else
        {
            lblMessaggio.Text = "✅ Campo validato correttamente";
            lblMessaggio.ForeColor = Color.Green;
        }
    }

    // ═══════════════════════════════════════════════════════
    // EVENTO KEYPRESS: Quando premi un TASTO (solo per txtEta)
    // ═══════════════════════════════════════════════════════
    private void txtEta_KeyPress(object sender, KeyPressEventArgs e)
    {
        // 🔒 Permetti SOLO numeri e backspace (tasto cancella) in txtEta
        
        // char.IsDigit() verifica se è una cifra (0-9)
        // Keys.Back è il codice del tasto backspace
        
        bool isNumero = char.IsDigit(e.KeyChar);
        bool isBackspace = e.KeyChar == (char)Keys.Back;
        
        if (!isNumero && !isBackspace)
        {
            // ❌ Blocca il tasto (non lo fa inserire)
            e.Handled = true;
            
            // Mostra feedback
            lblMessaggio.Text = "⚠️ Solo numeri in questo campo!";
            lblMessaggio.ForeColor = Color.Red;
        }
    }
}
}
3

⚡ Come Collegare gli Eventi Multipli (CRUCIALE!)

📝 Collegamento per txtNome:

  1. Seleziona txtNome nel Designer
  2. Premi F4 → Clicca sul fulmine ⚡
  3. Trova Enter → seleziona TextBox_Enter
  4. Trova Leave → seleziona TextBox_Leave

📝 Collegamento per txtEta:

  1. Seleziona txtEta nel Designer
  2. F4 → Fulmine ⚡
  3. Trova Enter → seleziona TextBox_Enter
  4. Trova Leave → seleziona TextBox_Leave
  5. Trova KeyPress → seleziona txtEta_KeyPress ⚠️ (metodo specifico!)

📝 Collegamento per txtEmail:

  1. Seleziona txtEmail nel Designer
  2. F4 → Fulmine ⚡
  3. Trova Enter → seleziona TextBox_Enter
  4. Trova Leave → seleziona TextBox_Leave
✨ Riepilogo Collegamenti:

TextBox Enter Leave KeyPress
txtNome TextBox_Enter TextBox_Leave -
txtEta TextBox_Enter TextBox_Leave txtEta_KeyPress
txtEmail TextBox_Enter TextBox_Leave -

4

🎬 Sequenza Completa degli Eventi: Cosa Succede

1️⃣ Utente clicca su txtNome

  • Evento: Enter
  • Viene chiamato TextBox_Enter
  • sender = txtNome
  • Sfondo diventa giallo chiaro
  • lblMessaggio mostra: "✏️ Stai scrivendo in: txtNome" (blu)
2️⃣ Utente scrive "M" e preme Tab

  • Evento: Leave
  • Viene chiamato TextBox_Leave
  • sender = txtNome
  • Sfondo torna bianco
  • Validazione: txt == txtNome → true
  • "M".Trim().Length = 1 < 2 → ❌ Non valido!
  • MessageBox: "Il nome deve avere almeno 2 caratteri!"
  • txt.Focus() → Il cursore torna su txtNome
  • txt.SelectAll() → "M" viene selezionato (pronto per sovrascrivere)
  • lblMessaggio: "❌ Correggi il campo evidenziato" (rosso)
3️⃣ Utente sovrascrive con "Mario" e preme Tab

  • Evento: Leave
  • Sfondo torna bianco
  • Validazione: "Mario".Trim().Length = 5 ≥ 2 → ✅ Valido!
  • lblMessaggio: "✅ Campo validato correttamente" (verde)
  • Il cursore passa automaticamente a txtEta (prossimo controllo in ordine)
4️⃣ Il cursore entra in txtEta

  • Evento: Enter
  • Sfondo diventa giallo chiaro
  • lblMessaggio: "✏️ Stai scrivendo in: txtEta" (blu)
5️⃣ Utente prova a scrivere "abc" in txtEta

  • Preme "a":
    • Evento: KeyPress
    • txtEta_KeyPress viene chiamato
    • char.IsDigit('a') = false (non è un numero)
    • e.KeyChar == Keys.Back = false (non è backspace)
    • e.Handled = true; → ❌ Blocca il tasto!
    • "a" NON viene inserito nella TextBox
    • lblMessaggio: "⚠️ Solo numeri in questo campo!" (rosso)
  • Stesso comportamento per "b" e "c" → bloccati
  • Risultato: txtEta rimane vuota
6️⃣ Utente scrive "25" in txtEta

  • Preme "2":
    • Evento: KeyPress
    • char.IsDigit('2') = true ✅
    • e.Handled = false; (default) → Tasto permesso
    • "2" viene inserito in txtEta
  • Preme "5": stesso processo → "5" inserito
  • txtEta ora contiene: "25"
7️⃣ Utente preme Tab per uscire da txtEta

  • Evento: Leave
  • Sfondo torna bianco
  • Validazione: int.TryParse("25", out int eta) = true, eta = 25
  • 25 ≥ 0 && 25 ≤ 120 → ✅ Valido!
  • lblMessaggio: "✅ Campo validato correttamente" (verde)
  • Il cursore passa a txtEmail

📚 Nuovi Concetti - Eventi e Validazione

Evento Enter (riceve focus)
Si attiva quando un controllo riceve il focus (l'utente ci clicca dentro o ci arriva con Tab/Shift+Tab). Ideale per evidenziare il campo attivo o mostrare suggerimenti.

private void TextBox_Enter(object sender, EventArgs e)
{
TextBox txt = (TextBox)sender;
// Evidenzia il campo attivo
txt.BackColor = Color.LightYellow;

// Mostra aiuto contestuale
lblAiuto.Text = "Inserisci il tuo nome completo";
}
Evento Leave (perde focus)
Si attiva quando un controllo perde il focus (l'utente esce cliccando altrove o premendo Tab). Perfetto per validare i dati inseriti e formattarli.

private void TextBox_Leave(object sender, EventArgs e)
{
TextBox txt = (TextBox)sender;
// Rimuovi evidenziazione
txt.BackColor = Color.White;

// Valida i dati
if (string.IsNullOrEmpty(txt.Text))
{
    MessageBox.Show("Campo obbligatorio!");
    txt.Focus();  // Rimetti il cursore qui
}
}
Evento KeyPress (tasto premuto)
Si attiva ogni volta che l'utente preme un tasto mentre il controllo ha focus. Permette di filtrare caratteri non voluti in tempo reale.

private void TextBox_KeyPress(object sender, KeyPressEventArgs e)
{
// Esempio 1: Solo numeri
if (!char.IsDigit(e.KeyChar) && e.KeyChar != (char)Keys.Back)
{
e.Handled = true;  // Blocca il tasto
}
// Esempio 2: Solo lettere
if (!char.IsLetter(e.KeyChar) && e.KeyChar != (char)Keys.Back)
{
    e.Handled = true;
}

// Esempio 3: Solo numeri, punto, virgola
if (!char.IsDigit(e.KeyChar) && 
    e.KeyChar != '.' && 
    e.KeyChar != ',' && 
    e.KeyChar != (char)Keys.Back)
{
    e.Handled = true;
}
}
char.IsDigit() / char.IsLetter()
Metodi statici della classe char per verificare il tipo di carattere.

// char.IsDigit() - verifica se è una cifra (0-9)
char.IsDigit('5')      // true
char.IsDigit('a')      // false
// char.IsLetter() - verifica se è una lettera (a-z, A-Z)
char.IsLetter('M')     // true
char.IsLetter('5')     // false
// char.IsControl() - verifica se è un carattere di controllo
char.IsControl('\t')   // true (tab)
char.IsControl('\b')   // true (backspace)
// char.IsWhiteSpace() - verifica se è uno spazio
char.IsWhiteSpace(' ')  // true
char.IsWhiteSpace('\t') // true (tab)
KeyPressEventArgs.Handled
Proprietà che indica se l'evento è stato "gestito". Se impostata a true, il tasto premuto viene bloccato e non inserito nel controllo.

private void txtNumerico_KeyPress(object sender, KeyPressEventArgs e)
{
// Se NON è un numero E NON è backspace
if (!char.IsDigit(e.KeyChar) && e.KeyChar != (char)Keys.Back)
{
e.Handled = true;  // BLOCCA il tasto
    // Il carattere NON verrà inserito nella TextBox
    // È come se l'utente non avesse premuto nulla
}
}
.Focus() e .SelectAll()
Metodi per controllare il focus e la selezione del testo in un controllo.

// .Focus() - sposta il cursore su un controllo
txtNome.Focus();  // Il cursore va in txtNome
// .SelectAll() - seleziona tutto il testo
txtNome.SelectAll();  // Tutto il testo viene evidenziato
// Uso combinato per correggere errori:
private void TextBox_Leave(object sender, EventArgs e)
{
TextBox txt = (TextBox)sender;
if (string.IsNullOrEmpty(txt.Text))
{
    MessageBox.Show("Campo obbligatorio!");
    txt.Focus();      // Torna sul campo
    txt.SelectAll();  // Seleziona tutto per sovrascrivere
}
}
int.TryParse() vs double.TryParse()
Due varianti di TryParse per numeri interi e decimali.

// int.TryParse() - converte in numero intero
int.TryParse("25", out int numero)       // true, numero = 25
int.TryParse("25.5", out int numero)     // false (ha decimali)
int.TryParse("abc", out int numero)      // false
// double.TryParse() - converte in numero decimale
double.TryParse("25.5", out double num)  // true, num = 25.5
double.TryParse("25", out double num)    // true, num = 25.0
double.TryParse("abc", out double num)   // false
// Scelta del tipo giusto:
// - Età, quantità, contatori → int.TryParse()
// - Peso, altezza, prezzi, percentuali → double.TryParse()

💎 Best Practices: Buone Abitudini di Programmazione

✅ DA FARE (Good Practices)

1️⃣ Usa Nomi Descrittivi

// ✅ BUONO - si capisce subito cosa fa
private void TextBox_TextChanged(object sender, EventArgs e)
private void ValidaCampoEmail()
private void CalcolaEMostraIMC()
// ❌ CATTIVO - nomi vaghi
private void Metodo1(object sender, EventArgs e)
private void Func()
private void DoStuff()
2️⃣ Valida SEMPRE i Dati Utente

// ✅ BUONO - valida prima di usare
if (double.TryParse(txtPeso.Text, out double peso) && peso > 0)
{
// Usa "peso" sapendo che è valido
}
// ❌ CATTIVO - assume che l'input sia corretto
double peso = Convert.ToDouble(txtPeso.Text);  // 💥 Può crashare!
3️⃣ Usa TryParse invece di Convert

// ✅ BUONO - sicuro, non va mai in errore
if (int.TryParse(txtEta.Text, out int eta))
{
// Conversione riuscita
}
// ❌ CATTIVO - può generare eccezione
int eta = Convert.ToInt32(txtEta.Text);  // 💥 Se non è un numero, crash!
4️⃣ Dai Feedback Visivo Chiaro

// ✅ BUONO - l'utente capisce subito cosa succede
if (valido)
{
lblMessaggio.Text = "✅ Dati validi";
lblMessaggio.ForeColor = Color.Green;
}
else
{
lblMessaggio.Text = "❌ Errore: " + dettaglioErrore;
lblMessaggio.ForeColor = Color.Red;
}
// ❌ CATTIVO - nessun feedback
if (valido)
{
// L'utente non sa se ha funzionato o no
}
5️⃣ Commenta il Codice Complesso

// ✅ BUONO - spiega il PERCHÉ
// Blocca caratteri non numerici, ma permette backspace per correggere
if (!char.IsDigit(e.KeyChar) && e.KeyChar != (char)Keys.Back)
{
e.Handled = true;
}
// ❌ CATTIVO - nessuna spiegazione
if (!char.IsDigit(e.KeyChar) && e.KeyChar != (char)Keys.Back)
{
e.Handled = true;
}
💡 Regola: Commenta il PERCHÉ fai qualcosa, non il COSA.
Il codice dice già cosa fa, i commenti devono spiegare il motivo.
6️⃣ Separa la Logica in Metodi

// ✅ BUONO - logica separata e riutilizzabile
private void btnCalcola_Click(object sender, EventArgs e)
{
if (!ValidaInput())
return;
double risultato = CalcolaIMC();
MostraRisultato(risultato);
}
// ❌ CATTIVO - tutto in un metodo gigante
private void btnCalcola_Click(object sender, EventArgs e)
{
// 200 righe di codice tutte qui...
}

❌ DA EVITARE (Bad Practices)

1️⃣ Codice Duplicato

Se copi-incolli lo stesso codice più volte, stai sbagliando approccio.
Usa eventi condivisi e metodi helper!

// ❌ CATTIVO - codice ripetuto 4 volte
private void txtNome_TextChanged(...) { /* validazione / }
private void txtCognome_TextChanged(...) { / stessa validazione / }
private void txtEmail_TextChanged(...) { / stessa validazione / }
private void txtTelefono_TextChanged(...) { / stessa validazione */ }
// ✅ BUONO - un solo metodo per tutti
private void TextBox_TextChanged(object sender, EventArgs e)
{
TextBox txt = (TextBox)sender;
// Validazione unica
}
2️⃣ Nessuna Gestione Errori

// ❌ CATTIVO - assume sempre input corretto
double numero = Convert.ToDouble(txtInput.Text);
double risultato = 100 / numero;  // 💥 Cosa succede se numero = 0?
// ✅ BUONO - gestisce casi anomali
if (double.TryParse(txtInput.Text, out double numero) && numero != 0)
{
double risultato = 100 / numero;
}
else
{
MessageBox.Show("Inserisci un numero valido diverso da zero");
}
3️⃣ Troppi Eventi sullo Stesso Controllo

Non esagerare! Usa solo gli eventi necessari.
Avere 5-6 eventi diversi su una TextBox rende il codice confuso.

// ❌ ESAGERATO - troppi eventi
txtNome.TextChanged += ...
txtNome.Enter += ...
txtNome.Leave += ...
txtNome.KeyPress += ...
txtNome.KeyDown += ...
txtNome.KeyUp += ...
txtNome.Click += ...
// ✅ GIUSTO - solo ciò che serve
txtNome.TextChanged += ...  // Validazione in tempo reale
txtNome.Leave += ...        // Validazione finale
4️⃣ Nomi di Controlli Generici

// ❌ CATTIVO - nomi autogenerati
button1_Click
textBox1_TextChanged
label1.Text = ...
// ✅ BUONO - nomi descrittivi
btnCalcola_Click
txtNome_TextChanged
lblRisultato.Text = ...

🎯 Regola d'Oro della Programmazione ad Eventi

"Se stai scrivendo lo stesso codice più di due volte,
c'è un modo migliore per farlo."

Questa regola si chiama DRY (Don't Repeat Yourself).
Ogni volta che ti ritrovi a copiare codice, fermati e pensa:
"Posso usare un metodo condiviso? Un evento multiplo? Una funzione helper?"

📋 Tabella Riferimento Rapido: Eventi Comuni

Evento Quando Si Attiva Uso Comune Esempio Pratico
TextChanged Quando il testo cambia (ogni carattere digitato) Validazione in tempo reale, calcoli automatici Calcolatrice IMC, contatore caratteri
Click Quando clicchi il controllo Eseguire azioni, salvare dati Pulsante "Salva", "Calcola", "Invia"
Enter Quando il controllo riceve il focus Evidenziare campo attivo, mostrare aiuto Cambiare colore sfondo, tooltip
Leave Quando il controllo perde il focus Validare dati inseriti, formattare Controllo email valida, formatta data
KeyPress Quando premi un tasto (con focus sul controllo) Bloccare caratteri non voluti Solo numeri in campo età, solo lettere in nome
SelectedIndexChanged Quando cambi selezione in ListBox/ComboBox Caricare dati correlati Selezionare auto per modificarla (Parco Auto)
Load Quando il form si carica (una sola volta all'avvio) Inizializzare controlli, caricare dati Popolare ComboBox carburanti all'avvio
FormClosing Prima che il form si chiuda Confermare uscita, salvare dati non salvati "Vuoi salvare prima di uscire?"
DoubleClick Quando fai doppio click Aprire finestre dettaglio, modificare item Doppio click su auto in ListBox per modificarla

🏋️ Esercizi per Allenarsi

⭐ Livello Facile

1️⃣ Contatore Caratteri Globale

  • Crea 3 TextBox: txtMessaggio1, txtMessaggio2, txtMessaggio3
  • Aggiungi una Label: lblTotaleCaratteri
  • Quando scrivi in una qualsiasi TextBox, mostra il totale di caratteri di TUTTE e 3
  • Usa UN SOLO metodo TextChanged per tutte le TextBox
💡 Suggerimento: Nel metodo TextChanged, calcola
txtMessaggio1.Text.Length + txtMessaggio2.Text.Length + txtMessaggio3.Text.Length

2️⃣ Pulsante Reset Intelligente

  • Riprendi l'esercizio 1 (Contatore Caratteri)
  • Aggiungi un Button: btnReset
  • Quando clicchi il pulsante, cancella TUTTE le TextBox
  • Mostra un MessageBox: "Campi azzerati!"

⭐⭐ Livello Medio

3️⃣ Calcolatrice Semplice con Validazione

  • Crea 2 TextBox: txtNumero1, txtNumero2
  • Crea 4 Button: btnSomma, btnSottrazione, btnMoltiplicazione, btnDivisione
  • Aggiungi Label: lblRisultato
  • Il risultato si aggiorna automaticamente mentre scrivi i numeri
  • Le TextBox diventano rosse se inserisci valori non numerici
  • I pulsanti si disabilitano se i campi non sono validi
💡 Suggerimenti:

  • Usa double.TryParse() per validare
  • Usa TextBox_TextChanged condiviso per entrambe le TextBox
  • Cambia txtNumero1.BackColor in base alla validità

4️⃣ Form Registrazione con Feedback Colorato

  • Crea TextBox: txtNome, txtCognome, txtEmail, txtPassword, txtConfermaPassword
  • Validazione:
    • Nome e Cognome: min 2 caratteri
    • Email: deve contenere @ e .
    • Password: min 6 caratteri
    • Conferma Password: deve coincidere con Password
  • Quando un campo è valido, mostra ✅ verde accanto
  • Quando non valido, mostra ❌ rosso con motivo
  • Il pulsante "Registrati" si abilita solo se TUTTO è valido
💡 Suggerimento: Usa Label dedicate (lblErroreNome, lblErroreCognome, etc.)
per mostrare icone e messaggi accanto a ogni campo.

⭐⭐⭐ Livello Difficile

5️⃣ Form Multi-Step con Validazione Progressiva

  • Step 1 - Dati Personali: Nome, Cognome, Data di Nascita
  • Step 2 - Contatti: Email, Telefono
  • Step 3 - Credenziali: Username, Password, Conferma Password
  • Validazioni specifiche per ogni campo
  • Pulsanti "Avanti" e "Indietro" per navigare tra gli step
  • "Avanti" si abilita solo se lo step corrente è valido
  • Feedback visivo con colori e icone ✅❌
  • Barra di progresso: "Step 1 di 3", "Step 2 di 3", ecc.
💡 Challenge Extra: Aggiungi un riepilogo finale con tutti i dati inseriti
prima della "Conferma Registrazione".

6️⃣ Calcolatrice Scientifica Mini

  • 2 TextBox numeriche con validazione
  • 8 operazioni: +, -, ×, ÷, x², √, %, 1/x
  • Risultato si aggiorna in tempo reale
  • Gestione errori: divisione per zero, radice di numero negativo
  • Cronologia operazioni in una ListBox
  • Pulsante "Pulisci Tutto" per reset completo

🎓 Riepilogo: Cosa Hai Imparato

✨ Competenze Acquisite

🎯 Il Parametro sender

Hai capito come identificare QUALE controllo ha generato un evento usando
sender, il segreto per scrivere meno codice.

🔄 Casting di Controlli

Sai trasformare sender nel tipo giusto usando casting:
(TextBox)sender, (Button)sender, ecc.

⚡ Un Metodo, Più Controlli

Hai imparato a condividere la logica tra controlli simili,
riducendo la duplicazione del codice del 75% o più!

✅ Validazione Dinamica

Feedback in tempo reale mentre l'utente digita, con validazione
automatica e controlli che si abilitano/disabilitano intelligentemente.

🎨 Eventi Multipli

Gestione combinata di Enter, Leave, KeyPress sullo stesso controllo
per creare TextBox intelligenti e user-friendly.

💎 Best Practices

Codice pulito, manutenibile e professionale seguendo le buone
abitudini della programmazione (DRY, validazione, feedback visivo).

🏆 Da Principiante a Professionista

Con questa lezione hai fatto un salto di qualità enorme nella gestione degli eventi.
Non scrivi più codice ripetitivo e inefficiente: ora programmi come un professionista,
con codice DRY (Don't Repeat Yourself), riutilizzabile e facile da manutenere.
Queste tecniche sono usate quotidianamente dagli sviluppatori professionisti in tutto il mondo!

🚀 Prossimi Passi: Verso la Teoria Fondamentale

📖 Nella Prossima Lezione Imparerai:

Lezione 7 - Variabili, Tipi di Dato e Operatori (TEORIA)

  • 🔢 Variabili: int, double, string, bool - cosa sono e come usarle
  • 📊 Tipi di Dato: Quando usare int vs double? string vs char?
  • Operatori Aritmetici: +, -, *, /, % con esempi pratici GUI
  • 🔀 Operatori di Confronto: ==, !=, <, >, <=, >= nelle validazioni
  • 🔗 Operatori Logici: &&, ||, ! per condizioni complesse
  • 💡 Casting e Conversioni: Convert vs Parse vs TryParse - la guida definitiva
  • 🎯 Esercizi Pratici: Calcolatrici, convertitori, validatori

🎯 Finora hai usato variabili e operatori "per intuito". Nella prossima lezione
capirai VERAMENTE cosa succede dietro le quinte: come funziona la memoria,
perché usiamo tipi diversi, come evitare errori comuni. Sarà la base teorica
che ti renderà un programmatore consapevole!

📚 Perché Teoria ADESSO?

Hai già praticato con TextBox, Button, eventi, oggetti (Automobile), liste.
Ora è il momento perfetto per consolidare le basi teoriche.
Capirai PERCHÉ hai fatto certe scelte fino ad ora e sarai pronto per progetti ancora più complessi!

💪 Cosa Fare Prima della Prossima Lezione
  1. Completa gli Esercizi 1 e 2 di questa lezione (Form Contatto e Calcolatrice IMC)
  2. Prova l'Esercizio 3 (Eventi Multipli) per familiarizzare con Enter/Leave/KeyPress
  3. Riprendi il Parco Auto (Lezione 5) e applica la validazione con eventi multipli
  4. Sfida: Migliora il Form Contatto aggiungendo un campo Telefono che accetta solo numeri

Lezione 6 - Gestione Avanzata di Eventi Multipli in C#

Corso C# Completo con Interfaccia Grafica - Da Zero a Esperto

© 2025 Academy PC - Tutti i diritti riservati

Torna in alto