🎮 Programmazione ad Eventi: Esercizi Guidati

Da semplici operazioni a validazioni avanzate: impara facendo

🎯 Il Tuo Percorso Finora

Hai completato un percorso incredibile! Vediamo cosa hai già imparato e cosa farai in questa lezione:

✅ Competenze Già Acquisite (Lezioni 1-6)

  • Lezione 1: Installazione Visual Studio e primo “Hello World”
  • Lezione 2: TextBox, Label, Button ed eventi base (Click)
  • Lezione 3: ListBox, ComboBox, gestione liste
  • Lezione 4: List<string> e operazioni CRUD
  • Lezione 5: Classi personalizzate (Automobile), List<Automobile>, File I/O
  • Lezione 6: Eventi multipli, parametro sender, validazione avanzata

🎯 Focus di Questa Lezione

Nella Lezione 6 hai imparato le tecniche avanzate di gestione eventi
(sender, casting, eventi multipli). Ora è tempo di consolidare queste
conoscenze con esercizi pratici progressivi che combinano:

  • 🔢 Operazioni matematiche con validazione input
  • Strutture di controllo (if/else, switch) applicate a interfacce grafiche
  • 🎨 Feedback visivo dinamico (colori, messaggi, abilitazione controlli)
  • 🔄 Conversioni di tipo sicure (TryParse vs Convert)
  • 📊 Validazione complessa con più condizioni
💡 Perché Questa Lezione è Importante

Gli esercizi di questa lezione ti permettono di praticare intensivamente
i concetti fondamentali della programmazione (operatori, condizioni, conversioni)
attraverso interfacce grafiche. Non stai solo scrivendo codice astratto:
stai creando applicazioni reali che gli utenti possono usare!

Ogni esercizio introduce nuovi concetti teorici nel contesto pratico,
preparandoti gradualmente per le lezioni teoriche approfondite che seguiranno.

📚 Concetti Fondamentali Praticati

TextBox

Controllo per l’inserimento di testo e numeri

Label

Controllo per visualizzare testo e risultati

Button

Controllo che genera evento Click per azioni

Event Handler

Metodo eseguito quando si verifica un evento

🔢 Esercizio 1: Calcolatrice – Somma di Due Numeri

▶ Obiettivo

Creare la tua prima calcolatrice con interfaccia grafica! L’applicazione permette
di inserire due numeri, calcolare la somma e visualizzare il risultato.
Include validazione per evitare errori quando l’utente inserisce testo al posto di numeri.

🎯 Cosa Imparerai:

  • ✅ Raccogliere input numerici da TextBox
  • ✅ Convertire stringhe in numeri con Convert.ToDouble()
  • ✅ Eseguire operazioni matematiche (somma)
  • ✅ Gestire errori con try-catch
  • ✅ Validare input con string.IsNullOrEmpty()
  • ✅ Abilitare/disabilitare controlli dinamicamente

1

Progettazione dell’Interfaccia Grafica

Crea un nuovo Windows Forms Project:

  1. File → Nuovo → Progetto
  2. Seleziona “Applicazione Windows Forms (.NET Framework)”
  3. Nome: CalcolatoriceSomma
  4. Click su “Crea”

📐 Controlli da Aggiungere al Form:

Tipo Controllo Name (Nome) Proprietà Text Note
Label lblNumero1 “Primo Numero:” Etichetta descrittiva
TextBox txtNumero1 vuoto Input primo numero
Label lblNumero2 “Secondo Numero:” Etichetta descrittiva
TextBox txtNumero2 vuoto Input secondo numero
Button btnCalcola “Calcola Somma” Inizialmente disabilitato (Enabled = false)
Label lblRisultato vuoto Mostra risultato (Font: 14pt, Bold)

2

Gestione dell’Evento Click del Pulsante

Quando l’utente clicca sul pulsante “Calcola Somma”, dobbiamo:

  1. Verificare che entrambi i campi siano compilati
  2. Convertire i testi in numeri
  3. Calcolare la somma
  4. Mostrare il risultato
  5. Gestire eventuali errori
Form1.cs – Evento Click
using System;
using System.Windows.Forms;

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

        // ═══════════════════════════════════════════════════════
        // EVENTO: Click del pulsante "Calcola Somma"
        // ═══════════════════════════════════════════════════════
        private void btnCalcola_Click(object sender, EventArgs e)
        {
            // ─────────────────────────────────────────────────
            // STEP 1: Validazione - Controllo campi vuoti
            // ─────────────────────────────────────────────────
            
            // string.IsNullOrEmpty() verifica se la stringa è null o vuota ("")
            if (string.IsNullOrEmpty(txtNumero1.Text) || 
                string.IsNullOrEmpty(txtNumero2.Text))
            {
                lblRisultato.Text = "⚠️ Inserisci entrambi i numeri!";
                lblRisultato.ForeColor = System.Drawing.Color.Red;
                return;  // Esce dal metodo senza eseguire altro codice
            }

            // ─────────────────────────────────────────────────
            // STEP 2: Conversione e Calcolo (con gestione errori)
            // ─────────────────────────────────────────────────
            
            try
            {
                // Converti le stringhe in numeri decimali (double)
                // Convert.ToDouble() può generare eccezione se il testo non è un numero
                double numero1 = Convert.ToDouble(txtNumero1.Text);
                double numero2 = Convert.ToDouble(txtNumero2.Text);

                // Calcola la somma
                double somma = numero1 + numero2;

                // ─────────────────────────────────────────────
                // STEP 3: Visualizzazione Risultato
                // ─────────────────────────────────────────────
                
                // ToString() converte il numero in stringa per visualizzarlo
                lblRisultato.Text = "Risultato: " + somma.ToString();
                lblRisultato.ForeColor = System.Drawing.Color.Green;
            }
            catch (FormatException)
            {
                // Eccezione catturata se l'utente inserisce testo non numerico
                lblRisultato.Text = "❌ Inserisci valori numerici validi!";
                lblRisultato.ForeColor = System.Drawing.Color.Red;
            }
            catch (Exception ex)
            {
                // Cattura qualsiasi altra eccezione non prevista
                lblRisultato.Text = "❌ Errore: " + ex.Message;
                lblRisultato.ForeColor = System.Drawing.Color.Red;
            }
        }
    }
}

🔍 Analisi Dettagliata del Codice:

1️⃣ Validazione Input:

if (string.IsNullOrEmpty(txtNumero1.Text) || 
    string.IsNullOrEmpty(txtNumero2.Text))
{
    // Almeno uno dei due campi è vuoto
    lblRisultato.Text = "⚠️ Inserisci entrambi i numeri!";
    return;  // IMPORTANTE: interrompe l'esecuzione del metodo
}

L’operatore || (OR logico) restituisce true se
almeno una delle condizioni è vera. Se uno dei campi è vuoto,
mostra errore e return esce dal metodo.

2️⃣ Try-Catch per Gestione Errori:

try
{
    // Codice che potrebbe generare errori
    double numero1 = Convert.ToDouble(txtNumero1.Text);
}
catch (FormatException)
{
    // Gestisce l'errore se Convert fallisce
    lblRisultato.Text = "❌ Inserisci valori numerici!";
}

try-catch permette di “catturare” errori senza far crashare il programma.
Se Convert.ToDouble("abc") fallisce, invece di chiudere l’app,
il controllo passa al blocco catch.

3️⃣ Conversione con ToString():

double somma = 15.5;
lblRisultato.Text = "Risultato: " + somma.ToString();
// Output: "Risultato: 15.5"

.Text richiede una stringa. ToString() converte
il numero in stringa per poterlo concatenare con testo.

3

Validazione Dinamica con TextChanged

Miglioriamo l’app abilitando il pulsante “Calcola” solo quando entrambi i campi
contengono numeri validi. Questo offre feedback immediato all’utente!

Form1.cs – Eventi TextChanged
// ═══════════════════════════════════════════════════════
// EVENTI: TextChanged per validazione dinamica
// ═══════════════════════════════════════════════════════

private void txtNumero1_TextChanged(object sender, EventArgs e)
{
    // Abilita il pulsante solo se entrambi i campi sono validi
    btnCalcola.Enabled = ValidaInput();
}

private void txtNumero2_TextChanged(object sender, EventArgs e)
{
    // Stesso controllo per il secondo campo
    btnCalcola.Enabled = ValidaInput();
}

// ═══════════════════════════════════════════════════════
// METODO HELPER: Valida che entrambi i campi siano numeri
// ═══════════════════════════════════════════════════════
private bool ValidaInput()
{
    try
    {
        // Prova a convertire il primo numero
        if (!string.IsNullOrEmpty(txtNumero1.Text))
            Convert.ToDouble(txtNumero1.Text);

        // Prova a convertire il secondo numero
        if (!string.IsNullOrEmpty(txtNumero2.Text))
            Convert.ToDouble(txtNumero2.Text);

        // Se arriviamo qui, entrambi sono numeri validi
        return true;
    }
    catch
    {
        // Se Convert fallisce, ritorna false
        return false;
    }
}

⚡ Come Collegare gli Eventi TextChanged:

  1. Seleziona txtNumero1 nel Designer
  2. Premi F4 per aprire le Proprietà
  3. Clicca sull’icona del fulmine ⚡ (Eventi)
  4. Trova TextChanged e fai doppio click
  5. Visual Studio crea automaticamente il metodo txtNumero1_TextChanged
  6. Ripeti per txtNumero2

🎬 Flusso di Esecuzione:

  1. Utente apre l’app → btnCalcola.Enabled = false (disabilitato)
  2. Utente scrive “12” in txtNumero1 → Evento txtNumero1_TextChanged
  3. Chiama ValidaInput() → txtNumero2 è vuoto → return false
  4. btnCalcola.Enabled = false → Pulsante ancora disabilitato
  5. Utente scrive “8” in txtNumero2 → Evento txtNumero2_TextChanged
  6. Chiama ValidaInput() → Entrambi sono numeri validi → return true
  7. btnCalcola.Enabled = true → ✅ Pulsante si abilita!
  8. Utente clicca “Calcola Somma” → Evento btnCalcola_Click
  9. Calcola 12 + 8 = 20 → Mostra “Risultato: 20” (verde)

📚 Metodi e Concetti – Esercizio 1

string.IsNullOrEmpty(string)

Metodo statico che verifica se una stringa è null (non esiste) o vuota (""). Essenziale per validare campi obbligatori.

// Sintassi:
bool risultato = string.IsNullOrEmpty(testo);

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

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

💡 Perché Preferibile a txt.Text == “”:

  • txt.Text == "" → Crash se txt.Text è null
  • string.IsNullOrEmpty(txt.Text) → Gestisce sia null che “”
Convert.ToDouble(string)

Converte una stringa in numero decimale (double). Genera eccezione FormatException se la stringa non è numerica.

// Sintassi:
double numero = Convert.ToDouble(stringa);

// Esempi:
Convert.ToDouble("123.45")    // 123.45
Convert.ToDouble("100")       // 100.0
Convert.ToDouble("abc")       // ❌ FormatException (errore!)
Convert.ToDouble("")          // ❌ FormatException (errore!)

// Uso con try-catch:
try
{
    double num = Convert.ToDouble(txtInput.Text);
    // Usa "num" sapendo che è valido
}
catch (FormatException)
{
    MessageBox.Show("Inserisci un numero valido!");
}

⚠️ Quando Usare Convert vs TryParse:

  • Convert.ToDouble() → Usa con try-catch (solleva eccezioni)
  • double.TryParse() → Usa senza try-catch (ritorna true/false)
  • TryParse è generalmente preferibile per input utente
.ToString()

Converte qualsiasi valore nel suo equivalente rappresentazione stringa. Tutti i tipi ereditano questo metodo.

// Sintassi:
string testo = valore.ToString();

// Esempi con numeri:
int numero = 42;
numero.ToString()          // "42"

double decimale = 3.14;
decimale.ToString()        // "3.14"
decimale.ToString("F2")    // "3.14" (2 decimali)
decimale.ToString("F0")    // "3" (nessun decimale)

// Concatenazione con stringhe:
int eta = 25;
string messaggio = "Hai " + eta.ToString() + " anni";
// oppure (più moderno):
string messaggio2 = $"Hai {eta} anni";  // string interpolation
try-catch (Gestione Eccezioni)

Costrutto per gestire errori (eccezioni) a runtime senza far crashare il programma.

// Struttura base:
try
{
    // Codice che potrebbe generare errori
    double num = Convert.ToDouble(txtInput.Text);
}
catch (FormatException ex)
{
    // Gestisce errori di formato (testo non numerico)
    MessageBox.Show("Inserisci un numero valido!");
}
catch (Exception ex)
{
    // Cattura tutte le altre eccezioni
    MessageBox.Show("Errore: " + ex.Message);
}

// Best Practice: gestisci eccezioni specifiche prima
try
{
    // ...
}
catch (FormatException)      // Specifica
{
    // Errore formato
}
catch (OverflowException)    // Specifica
{
    // Numero troppo grande
}
catch (Exception ex)         // Generica (ultima)
{
    // Altri errori
}
Control.Enabled (proprietà)

Proprietà booleana che indica se un controllo è abilitato (true) o disabilitato (false). Controlli disabilitati appaiono in grigio e non rispondono a eventi.

// Abilitare/disabilitare:
btnCalcola.Enabled = true;   // Abilita (cliccabile)
btnCalcola.Enabled = false;  // Disabilita (grigio, non cliccabile)

// Uso dinamico:
btnCalcola.Enabled = ValidaInput();  // Abilita se ValidaInput() ritorna true

// Controlli multipli:
bool tuttiValidi = VerificaCampi();
btnSalva.Enabled = tuttiValidi;
btnInvia.Enabled = tuttiValidi;
Operatore || (OR Logico)

Restituisce true se almeno una delle condizioni è vera.

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

// Uso pratico - almeno uno deve essere vero:
if (string.IsNullOrEmpty(txtNome.Text) || 
    string.IsNullOrEmpty(txtCognome.Text))
{
    // Se Nome è vuoto OR Cognome è vuoto (o entrambi)
    MessageBox.Show("Compila tutti i campi!");
}

// Differenza con && (AND):
// && → TUTTI devono essere veri
// || → ALMENO UNO deve essere vero

🔀 Esercizio 2: Verifica Numero Pari o Dispari

▶ Obiettivo

Creare un’applicazione che determina se un numero inserito dall’utente è pari o dispari.
Introduce l’operatore modulo (%) e l’uso di strutture condizionali (if-else)
con feedback visivo colorato.

🎯 Nuovi Concetti:

  • ✅ Operatore modulo (%) per calcolare il resto della divisione
  • if-else per eseguire codice condizionalmente
  • Convert.ToInt32() per numeri interi
  • ✅ Cambiare colore del testo con ForeColor
  • ✅ Evento KeyPress per filtrare input in tempo reale

📖 Teoria: Numeri Pari e Dispari

Un numero è pari se è divisibile per 2 senza resto (resto = 0).
Un numero è dispari se diviso per 2 lascia resto 1.

// Esempi:
10 diviso 2 = 5 resto 0   → PARI
7 diviso 2 = 3 resto 1    → DISPARI
-4 diviso 2 = -2 resto 0  → PARI
0 diviso 2 = 0 resto 0    → PARI

// Operatore modulo (%):
10 % 2 = 0   // resto della divisione 10/2
7 % 2 = 1    // resto della divisione 7/2

// Regola generale:
if (numero % 2 == 0)
    // PARI
else
    // DISPARI

1

Progettazione dell’Interfaccia

Tipo Name Text Note
Label lblTitolo “Inserisci un Numero:”
TextBox txtNumero vuoto Input numero intero
Button btnVerifica “Verifica”
Label lblRisultato vuoto Font: 14pt, Bold

2

Codice Completo con Spiegazione

Form1.cs – Verifica Pari/Dispari
using System;
using System.Drawing;
using System.Windows.Forms;

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

        // ═══════════════════════════════════════════════════════
        // EVENTO: Click del pulsante "Verifica"
        // ═══════════════════════════════════════════════════════
        private void btnVerifica_Click(object sender, EventArgs e)
        {
            // ─────────────────────────────────────────────────
            // STEP 1: Validazione campo vuoto
            // ─────────────────────────────────────────────────
            if (string.IsNullOrEmpty(txtNumero.Text))
            {
                lblRisultato.Text = "⚠️ Inserisci un numero!";
                lblRisultato.ForeColor = Color.Gray;
                return;
            }

            try
            {
                // ─────────────────────────────────────────────
                // STEP 2: Conversione in intero
                // ─────────────────────────────────────────────
                
                // Convert.ToInt32() converte in numero intero (int)
                // a differenza di ToDouble(), ToInt32() non accetta decimali
                int numero = Convert.ToInt32(txtNumero.Text);

                // ─────────────────────────────────────────────
                // STEP 3: Verifica con operatore modulo (%)
                // ─────────────────────────────────────────────
                
                if (numero % 2 == 0)
                {
                    // Resto = 0 → PARI
                    lblRisultato.Text = $"{numero} è un numero PARI ✅";
                    lblRisultato.ForeColor = Color.Blue;
                }
                else
                {
                    // Resto = 1 → DISPARI
                    lblRisultato.Text = $"{numero} è un numero DISPARI ⚡";
                    lblRisultato.ForeColor = Color.Red;
                }
            }
            catch (FormatException)
            {
                // L'utente ha inserito testo non numerico
                lblRisultato.Text = "❌ Inserisci un numero intero valido!";
                lblRisultato.ForeColor = Color.Black;
            }
            catch (OverflowException)
            {
                // Il numero è troppo grande per un int
                lblRisultato.Text = "❌ Numero troppo grande!";
                lblRisultato.ForeColor = Color.Black;
            }
            catch (Exception ex)
            {
                lblRisultato.Text = "❌ Errore: " + ex.Message;
                lblRisultato.ForeColor = Color.Black;
            }
        }
    }
}

🔍 Analisi Operatore Modulo (%):

// L'operatore % restituisce il RESTO della divisione

int numero = 10;
int resto = numero % 2;  // 10 diviso 2 = 5 con resto 0
// resto = 0

int numero2 = 7;
int resto2 = numero2 % 2;  // 7 diviso 2 = 3 con resto 1
// resto2 = 1

// Altri esempi:
10 % 3 = 1    // 10 / 3 = 3 resto 1
15 % 4 = 3    // 15 / 4 = 3 resto 3
20 % 5 = 0    // 20 / 5 = 4 resto 0 (divisibile perfettamente)

Il modulo è utile per verificare divisibilità: se numero % divisore == 0,
il numero è divisibile perfettamente.

3

Miglioramento: Filtrare Input con KeyPress

Aggiungiamo l’evento KeyPress per permettere solo numeri e il segno meno
(per numeri negativi). Questo previene errori prima ancora che l’utente clicchi “Verifica”.

Form1.cs – Evento KeyPress
// ═══════════════════════════════════════════════════════
// EVENTO: KeyPress - Filtra caratteri non numerici
// ═══════════════════════════════════════════════════════
private void txtNumero_KeyPress(object sender, KeyPressEventArgs e)
{
    // char.IsControl() verifica se è un tasto di controllo (Backspace, Tab, ecc.)
    // char.IsDigit() verifica se è una cifra (0-9)
    
    bool isNumero = char.IsDigit(e.KeyChar);
    bool isControllo = char.IsControl(e.KeyChar);
    bool isMeno = e.KeyChar == '-';
    
    // Permetti SOLO:
    // - Numeri (0-9)
    // - Tasti di controllo (Backspace, Delete, ecc.)
    // - Segno meno (-)
    if (!isNumero && !isControllo && !isMeno)
    {
        e.Handled = true;  // ❌ Blocca il tasto
        return;
    }
    
    // Permetti il segno meno SOLO all'inizio
    if (isMeno)
    {
        TextBox txt = (TextBox)sender;
        
        // Se il segno meno non è all'inizio o c'è già un meno
        if (txt.Text.IndexOf('-') > -1 || txt.SelectionStart != 0)
        {
            e.Handled = true;  // ❌ Blocca
        }
    }
}

⚡ Come Collegare l’Evento KeyPress:

  1. Seleziona txtNumero nel Designer
  2. F4 → Fulmine ⚡ → Trova KeyPress
  3. Fai doppio click su KeyPress
  4. Scrivi il codice sopra nel metodo generato

🎬 Cosa Succede con KeyPress:

  1. Utente prova a scrivere “a” → KeyPress si attiva
  2. char.IsDigit('a') = false
  3. char.IsControl('a') = false
  4. 'a' == '-' = false
  5. e.Handled = true → ❌ “a” viene bloccato!
  6. Utente prova a scrivere “5” → char.IsDigit('5') = true
  7. Tasto permesso → ✅ “5” viene inserito in txtNumero

📚 Nuovi Metodi e Concetti – Esercizio 2

Convert.ToInt32(string)

Converte una stringa in numero intero a 32 bit (int). Non accetta decimali.

// Sintassi:
int numero = Convert.ToInt32(stringa);

// Esempi:
Convert.ToInt32("123")      // 123
Convert.ToInt32("-45")      // -45
Convert.ToInt32("0")        // 0
Convert.ToInt32("12.5")     // ❌ FormatException (ha decimali!)
Convert.ToInt32("abc")      // ❌ FormatException

// Differenza con ToDouble():
Convert.ToDouble("12.5")    // ✅ 12.5 (accetta decimali)
Convert.ToInt32("12.5")     // ❌ Errore (non accetta decimali)

// Quando usare ToInt32 vs ToDouble:
// ToInt32 → Età, quantità, contatori (numeri interi)
// ToDouble → Peso, altezza, prezzi (numeri decimali)
Operatore % (Modulo)

Restituisce il resto della divisione tra due numeri. Fondamentale per verificare divisibilità.

// Sintassi:
int resto = dividendo % divisore;

// Esempi:
10 % 2 = 0    // 10 / 2 = 5 resto 0
10 % 3 = 1    // 10 / 3 = 3 resto 1
15 % 4 = 3    // 15 / 4 = 3 resto 3
20 % 5 = 0    // 20 / 5 = 4 resto 0

// Applicazioni pratiche:

// 1. Verificare se pari o dispari:
if (numero % 2 == 0)
    Console.WriteLine("Pari");

// 2. Verificare se multiplo di 3:
if (numero % 3 == 0)
    Console.WriteLine("Multiplo di 3");

// 3. Alternare colori in una lista (pari=blu, dispari=rosso):
if (indice % 2 == 0)
    colore = Color.Blue;
else
    colore = Color.Red;
if-else (Struttura Condizionale)

Esegue blocchi di codice diversi in base a una condizione booleana (true/false).

// Struttura base:
if (condizione)
{
    // Eseguito se condizione è true
}
else
{
    // Eseguito se condizione è false
}

// Esempio pratico:
int eta = 18;

if (eta >= 18)
{
    MessageBox.Show("Maggiorenne");
}
else
{
    MessageBox.Show("Minorenne");
}

// if-else if-else (condizioni multiple):
int voto = 85;

if (voto >= 90)
{
    lblGiudizio.Text = "Eccellente";
}
else if (voto >= 80)
{
    lblGiudizio.Text = "Ottimo";
}
else if (voto >= 70)
{
    lblGiudizio.Text = "Buono";
}
else
{
    lblGiudizio.Text = "Sufficiente";
}
Control.ForeColor (proprietà)

Proprietà che imposta il colore del testo di un controllo.

// Sintassi:
controllo.ForeColor = Color.NomeColore;

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

// Colore personalizzato RGB:
lblRisultato.ForeColor = Color.FromArgb(255, 100, 50);
// FromArgb(rosso, verde, blu) - valori da 0 a 255

// Uso pratico con feedback:
if (valido)
{
    lblMessaggio.Text = "✅ Corretto!";
    lblMessaggio.ForeColor = Color.Green;
}
else
{
    lblMessaggio.Text = "❌ Errore!";
    lblMessaggio.ForeColor = Color.Red;
}
char.IsDigit() / char.IsControl()

Metodi statici per verificare il tipo di carattere.

// char.IsDigit(char) - verifica se è una cifra (0-9)
char.IsDigit('5')      // true
char.IsDigit('a')      // false
char.IsDigit(' ')      // false

// char.IsControl(char) - verifica se è un carattere di controllo
char.IsControl('\b')   // true (backspace)
char.IsControl('\t')   // true (tab)
char.IsControl('a')    // false

// char.IsLetter(char) - verifica se è una lettera
char.IsLetter('M')     // true
char.IsLetter('5')     // false

// Uso in KeyPress:
private void txt_KeyPress(object sender, KeyPressEventArgs e)
{
    // Permetti solo numeri e backspace
    if (!char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar))
    {
        e.Handled = true;  // Blocca
    }
}
string.IndexOf(char)

Restituisce la posizione (indice) della prima occorrenza di un carattere nella stringa. Ritorna -1 se non trovato.

// Sintassi:
int posizione = stringa.IndexOf(carattere);

// Esempi:
"Hello".IndexOf('e')      // 1 (posizione 0=H, 1=e)
"Hello".IndexOf('o')      // 4
"Hello".IndexOf('x')      // -1 (non trovato)

"mario@email.com".IndexOf('@')   // 5

// Uso pratico - verificare se contiene un carattere:
if (txtNumero.Text.IndexOf('-') > -1)
{
    // La stringa contiene il segno meno
}

// Alternativa più leggibile:
if (txtNumero.Text.Contains("-"))
{
    // Stesso risultato, sintassi più chiara
}
TextBox.SelectionStart (proprietà)

Proprietà che indica la posizione del cursore nella TextBox (indice del carattere).

// Posizione cursore:
txtNome.SelectionStart      // 0 = inizio, 5 = dopo 5 caratteri

// Esempio - permetti '-' solo all'inizio:
if (e.KeyChar == '-' && txtNumero.SelectionStart != 0)
{
    e.Handled = true;  // Blocca se non è all'inizio
}

// Spostare il cursore programmaticamente:
txtNome.SelectionStart = 0;     // Cursore all'inizio
txtNome.SelectionStart = txtNome.Text.Length;  // Cursore alla fine

📊 Esercizio 3: Valutazione del Voto con Switch

▶ Obiettivo

Creare un’applicazione che converte un voto numerico (0-100) in una valutazione letterale
(Eccellente, Molto Buono, Buono, Sufficiente, Insufficiente). Introduce la struttura
switch come alternativa a lunghe catene di if-else.

🎯 Nuovi Concetti:

  • ✅ Struttura switch per valutare condizioni multiple
  • ✅ Pattern matching con range (>= e <)
  • ✅ Combinare validazione e logica complessa
  • ✅ Feedback colorato dinamico basato su valutazione

📖 Scala di Valutazione:

Voto Numerico Valutazione Colore
90 – 100 ECCELLENTE Verde
80 – 89 MOLTO BUONO Verde Scuro
70 – 79 BUONO Blu
60 – 69 SUFFICIENTE Arancione
0 – 59 INSUFFICIENTE Rosso

1

Progettazione dell’Interfaccia

Tipo Name Text Note
Label lblTitolo “Inserisci il Voto (0-100):”
TextBox txtVoto vuoto Input voto numerico
Button btnValuta “Valuta”
Label lblValutazione vuoto Font: 16pt, Bold

2

Codice con Switch Statement

Form1.cs – Valutazione con Switch
using System;
using System.Drawing;
using System.Windows.Forms;

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

        // ═══════════════════════════════════════════════════════
        // EVENTO: Click del pulsante "Valuta"
        // ═══════════════════════════════════════════════════════
        private void btnValuta_Click(object sender, EventArgs e)
        {
            // ─────────────────────────────────────────────────
            // STEP 1: Validazione campo vuoto
            // ─────────────────────────────────────────────────
            if (string.IsNullOrEmpty(txtVoto.Text))
            {
                lblValutazione.Text = "⚠️ Inserisci un voto!";
                lblValutazione.ForeColor = Color.Gray;
                return;
            }

            try
            {
                // ─────────────────────────────────────────────
                // STEP 2: Conversione e validazione range
                // ─────────────────────────────────────────────
                int voto = Convert.ToInt32(txtVoto.Text);

                // Validazione: voto deve essere tra 0 e 100
                if (voto < 0 || voto > 100)
                {
                    lblValutazione.Text = "❌ Il voto deve essere tra 0 e 100!";
                    lblValutazione.ForeColor = Color.Black;
                    return;
                }

                // ─────────────────────────────────────────────
                // STEP 3: Valutazione con SWITCH (C# 9.0+)
                // ─────────────────────────────────────────────
                
                string valutazione;
                Color colore;

                // Switch con pattern matching (range)
                switch (voto)
                {
                    case >= 90 and <= 100: valutazione = "ECCELLENTE 🏆"; colore = Color.Green; break; case >= 80 and < 90: valutazione = "MOLTO BUONO ⭐"; colore = Color.DarkGreen; break; case >= 70 and < 80: valutazione = "BUONO ✅"; colore = Color.Blue; break; case >= 60 and < 70: valutazione = "SUFFICIENTE ✔️"; colore = Color.Orange; break; case >= 0 and < 60:
                        valutazione = "INSUFFICIENTE ❌";
                        colore = Color.Red;
                        break;

                    default:
                        // Questo non dovrebbe mai verificarsi grazie alla validazione sopra
                        valutazione = "Voto non valido";
                        colore = Color.Black;
                        break;
                }

                // ─────────────────────────────────────────────
                // STEP 4: Visualizzazione risultato
                // ─────────────────────────────────────────────
                lblValutazione.Text = $"Voto {voto}: {valutazione}";
                lblValutazione.ForeColor = colore;
            }
            catch (FormatException)
            {
                lblValutazione.Text = "❌ Inserisci un voto numerico valido!";
                lblValutazione.ForeColor = Color.Black;
            }
            catch (Exception ex)
            {
                lblValutazione.Text = "❌ Errore: " + ex.Message;
                lblValutazione.ForeColor = Color.Black;
            }
        }
    }
}

📝 NOTA: Versione Compatibile per Visual Studio Vecchio

Il codice sopra usa il pattern matching di C# 9.0+
(case >= 90 and <= 100). Se il tuo Visual Studio non lo supporta,
usa questa versione alternativa con if-else:

Versione Compatibile (if-else)
// Versione con if-else (funziona su tutte le versioni C#)
string valutazione;
Color colore;

if (voto >= 90 && voto <= 100) { valutazione = "ECCELLENTE 🏆"; colore = Color.Green; } else if (voto >= 80 && voto < 90) { valutazione = "MOLTO BUONO ⭐"; colore = Color.DarkGreen; } else if (voto >= 70 && voto < 80) { valutazione = "BUONO ✅"; colore = Color.Blue; } else if (voto >= 60 && voto < 70)
{
    valutazione = "SUFFICIENTE ✔️";
    colore = Color.Orange;
}
else  // voto < 60
{
    valutazione = "INSUFFICIENTE ❌";
    colore = Color.Red;
}

lblValutazione.Text = $"Voto {voto}: {valutazione}";
lblValutazione.ForeColor = colore;

🔍 Confronto: Switch vs If-Else

✅ Quando usare SWITCH:

  • Quando hai molte condizioni basate sullo stesso valore
  • Quando le condizioni sono discrete (valori specifici o range)
  • Codice più leggibile con molti casi

✅ Quando usare IF-ELSE:

  • Quando le condizioni sono complesse (più variabili)
  • Quando hai poche condizioni (2-3)
  • Quando usi il pattern matching moderno (>= and <) non supportato
// Switch moderno (C# 9.0+) - Elegante per range
switch (voto)
{
    case >= 90 and <= 100: // ... break; } // If-else - Più verboso ma universale if (voto >= 90 && voto <= 100)
{
    // ...
}

// Switch classico - Solo per valori esatti
switch (giorno)
{
    case 1:
        nome = "Lunedì";
        break;
    case 2:
        nome = "Martedì";
        break;
}

3

Validazione Avanzata con KeyPress

Form1.cs – KeyPress per Voto
// ═══════════════════════════════════════════════════════
// EVENTO: KeyPress - Permetti solo numeri 0-9
// ═══════════════════════════════════════════════════════
private void txtVoto_KeyPress(object sender, KeyPressEventArgs e)
{
    // Permetti solo numeri e backspace (NO segno meno)
    if (!char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar))
    {
        e.Handled = true;  // Blocca il tasto
    }
}

📚 Nuovi Concetti – Esercizio 3

switch Statement

Struttura di controllo per eseguire codice diverso in base al valore di un’espressione.

// Sintassi base:
switch (variabile)
{
    case valore1:
        // Codice se variabile == valore1
        break;
    
    case valore2:
        // Codice se variabile == valore2
        break;
    
    default:
        // Codice se nessun case corrisponde
        break;
}

// Esempio: giorni della settimana
int giorno = 3;
string nomeGiorno;

switch (giorno)
{
    case 1:
        nomeGiorno = "Lunedì";
        break;
    case 2:
        nomeGiorno = "Martedì";
        break;
    case 3:
        nomeGiorno = "Mercoledì";
        break;
    default:
        nomeGiorno = "Giorno non valido";
        break;
}

// IMPORTANTE: break è obbligatorio!
// Senza break, il codice continua nei case successivi
Pattern Matching con Range (C# 9.0+)

Sintassi moderna per verificare se un valore rientra in un intervallo usando and.

// Sintassi moderna:
switch (numero)
{
    case >= 90 and <= 100: // numero è tra 90 e 100 (inclusi) break; case >= 80 and < 90: // numero è tra 80 e 89 break; } // Equivalente con if-else: if (numero >= 90 && numero <= 100) { // ... } else if (numero >= 80 && numero < 90) { // ... } // Altri pattern: switch (valore) { case > 0:           // maggiore di 0
        break;
    case < 0:           // minore di 0
        break;
    case 0:             // uguale a 0
        break;
}
⚠️ Compatibilità: Questa sintassi richiede C# 9.0+ (Visual Studio 2019 16.8 o successivo).
Se non disponibile, usa if-else.
Operatore && (AND Logico)

Restituisce true solo se TUTTE le condizioni sono vere.

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

// Uso per validare range:
if (voto >= 0 && voto <= 100) { // voto è valido solo se >= 0 E <= 100 } // Differenza con || (OR): // && → TUTTE devono essere vere // || → ALMENO UNA deve essere vera // Esempio pratico: if (eta >= 18 && patente == true)
{
    // Può guidare SOLO se ha 18 anni E ha la patente
}

// Condizioni multiple:
if (nome != "" && cognome != "" && email.Contains("@"))
{
    // Tutti e 3 devono essere veri
}
break Statement

Interrompe l’esecuzione di un blocco switch o di un ciclo.

// In switch - OBBLIGATORIO dopo ogni case:
switch (valore)
{
    case 1:
        Console.WriteLine("Uno");
        break;  // Esce dallo switch
    
    case 2:
        Console.WriteLine("Due");
        break;  // Senza break, errore di compilazione!
}

// In cicli (vedremo nelle prossime lezioni):
for (int i = 0; i < 10; i++)
{
    if (i == 5)
        break;  // Esce dal ciclo quando i è 5
}

// Cosa succede SENZA break?
switch (valore)
{
    case 1:
        Console.WriteLine("Uno");
        // ❌ ERRORE: manca break!
        // Il codice "cade" nel case successivo
    case 2:
        Console.WriteLine("Due");
        break;
}

🧮 Esercizio 4: Calcolatrice con 4 Operazioni

▶ Obiettivo

Creare una calcolatrice completa che esegue le quattro operazioni fondamentali
(somma, sottrazione, moltiplicazione, divisione) con gestione completa degli errori,
inclusa la divisione per zero.

🎯 Cosa Imparerai:

  • ✅ Gestire più operazioni con un unico metodo helper
  • ✅ Validazione avanzata (divisione per zero)
  • ✅ Abilitare/disabilitare pulsanti dinamicamente
  • ✅ Organizzare codice con metodi riutilizzabili
  • ✅ Operatori aritmetici: +, -, *, /

1

Progettazione dell’Interfaccia

Tipo Name Text Note
Label lblNumero1 “Primo Numero:”
TextBox txtNumero1 vuoto
Label lblNumero2 “Secondo Numero:”
TextBox txtNumero2 vuoto
Button btnSomma “+” Enabled = false
Button btnSottrai “-“ Enabled = false
Button btnMoltiplica “×” Enabled = false
Button btnDividi “÷” Enabled = false
Label lblOperazione “Seleziona operazione” Font: 10pt
Label lblRisultato “” Font: 16pt, Bold

2

Codice Completo della Calcolatrice

Form1.cs – Calcolatrice Completa
using System;
using System.Drawing;
using System.Windows.Forms;

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

        // ═══════════════════════════════════════════════════════
        // EVENTO FORM LOAD: Inizializzazione
        // ═══════════════════════════════════════════════════════
        private void Form1_Load(object sender, EventArgs e)
        {
            // Inizializza l'interfaccia all'avvio
            lblOperazione.Text = "Seleziona un'operazione";
            lblRisultato.Text = "";
            
            // Disabilita tutti i pulsanti (abilitati quando i numeri sono validi)
            AbilitaPulsanti(false);
        }

        // ═══════════════════════════════════════════════════════
        // EVENTI: Click dei pulsanti operazione
        // ═══════════════════════════════════════════════════════
        
        private void btnSomma_Click(object sender, EventArgs e)
        {
            CalcolaOperazione('+');
        }

        private void btnSottrai_Click(object sender, EventArgs e)
        {
            CalcolaOperazione('-');
        }

        private void btnMoltiplica_Click(object sender, EventArgs e)
        {
            CalcolaOperazione('*');
        }

        private void btnDividi_Click(object sender, EventArgs e)
        {
            CalcolaOperazione('/');
        }

        // ═══════════════════════════════════════════════════════
        // METODO PRINCIPALE: Calcola operazione
        // ═══════════════════════════════════════════════════════
        private void CalcolaOperazione(char operatore)
        {
            // Validazione campi
            if (string.IsNullOrEmpty(txtNumero1.Text) || 
                string.IsNullOrEmpty(txtNumero2.Text))
            {
                lblRisultato.Text = "⚠️ Inserisci entrambi i numeri!";
                lblRisultato.ForeColor = Color.Red;
                return;
            }

            try
            {
                // Conversione numeri
                double num1 = Convert.ToDouble(txtNumero1.Text);
                double num2 = Convert.ToDouble(txtNumero2.Text);

                double risultato = 0;
                bool operazioneValida = true;
                string nomeOperazione = "";

                // ─────────────────────────────────────────────
                // Esegui operazione in base al carattere
                // ─────────────────────────────────────────────
                switch (operatore)
                {
                    case '+':
                        risultato = num1 + num2;
                        nomeOperazione = "Somma";
                        break;

                    case '-':
                        risultato = num1 - num2;
                        nomeOperazione = "Sottrazione";
                        break;

                    case '*':
                        risultato = num1 * num2;
                        nomeOperazione = "Moltiplicazione";
                        break;

                    case '/':
                        // IMPORTANTE: Gestione divisione per zero
                        if (num2 != 0)
                        {
                            risultato = num1 / num2;
                            nomeOperazione = "Divisione";
                        }
                        else
                        {
                            lblRisultato.Text = "❌ Errore: divisione per zero!";
                            lblRisultato.ForeColor = Color.Red;
                            lblOperazione.Text = "Non puoi dividere per zero";
                            operazioneValida = false;
                        }
                        break;
                }

                // Mostra risultato se operazione valida
                if (operazioneValida)
                {
                    lblOperazione.Text = $"Operazione: {nomeOperazione}";
                    lblRisultato.Text = $"Risultato: {risultato:F2}";
                    lblRisultato.ForeColor = Color.Green;
                }
            }
            catch (FormatException)
            {
                lblRisultato.Text = "❌ Inserisci valori numerici validi!";
                lblRisultato.ForeColor = Color.Red;
            }
            catch (Exception ex)
            {
                lblRisultato.Text = $"❌ Errore: {ex.Message}";
                lblRisultato.ForeColor = Color.Red;
            }
        }

        // ═══════════════════════════════════════════════════════
        // EVENTI: TextChanged per validazione dinamica
        // ═══════════════════════════════════════════════════════
        
        private void txtNumero1_TextChanged(object sender, EventArgs e)
        {
            AbilitaPulsanti(ValidaInput());
        }

        private void txtNumero2_TextChanged(object sender, EventArgs e)
        {
            AbilitaPulsanti(ValidaInput());
        }

        // ═══════════════════════════════════════════════════════
        // METODO HELPER: Valida che entrambi i campi siano numeri
        // ═══════════════════════════════════════════════════════
        private bool ValidaInput()
        {
            try
            {
                if (!string.IsNullOrEmpty(txtNumero1.Text))
                    Convert.ToDouble(txtNumero1.Text);

                if (!string.IsNullOrEmpty(txtNumero2.Text))
                    Convert.ToDouble(txtNumero2.Text);

                return true;
            }
            catch
            {
                return false;
            }
        }

        // ═══════════════════════════════════════════════════════
        // METODO HELPER: Abilita/disabilita pulsanti operazione
        // ═══════════════════════════════════════════════════════
        private void AbilitaPulsanti(bool abilita)
        {
            btnSomma.Enabled = abilita;
            btnSottrai.Enabled = abilita;
            btnMoltiplica.Enabled = abilita;
            btnDividi.Enabled = abilita;
        }
    }
}

🔍 Analisi del Design Pattern:

Nota come il codice è organizzato in metodi riutilizzabili:

  • CalcolaOperazione(char operatore):
    Metodo generico che funziona per tutte le 4 operazioni.
    Riceve il simbolo dell’operazione come parametro.
  • ValidaInput():
    Metodo separato per validazione, riutilizzato in 2 punti diversi.
  • AbilitaPulsanti(bool abilita):
    Metodo che abilita/disabilita tutti i 4 pulsanti con una sola chiamata.

Questo è un esempio di codice DRY (Don’t Repeat Yourself):
scriviamo la logica una volta e la riusiamo dove serve!

⚠️ Gestione Divisione per Zero:

case '/':
    if (num2 != 0)
    {
        risultato = num1 / num2;
    }
    else
    {
        // Divisione per zero = ERRORE MATEMATICO
        lblRisultato.Text = "❌ Divisione per zero!";
        operazioneValida = false;
    }
    break;

La divisione per zero è un errore matematico
(risultato = infinito, indefinito). Dobbiamo sempre controllare
che il divisore sia diverso da zero prima di eseguire la divisione.

3

🎬 Flusso di Esecuzione Completo

Scenario: Calcolare 10 + 5

  1. Avvio applicazione:
    • Form1_Load si attiva
    • Tutti i pulsanti vengono disabilitati (Enabled = false)
  2. Utente scrive “10” in txtNumero1:
    • Evento txtNumero1_TextChanged
    • Chiama ValidaInput() → txtNumero2 è vuoto → return false
    • AbilitaPulsanti(false) → Pulsanti ancora disabilitati
  3. Utente scrive “5” in txtNumero2:
    • Evento txtNumero2_TextChanged
    • Chiama ValidaInput() → Entrambi sono numeri → return true
    • AbilitaPulsanti(true) → ✅ Pulsanti si abilitano!
  4. Utente clicca sul pulsante “+”:
    • Evento btnSomma_Click
    • Chiama CalcolaOperazione('+')
    • Converte: num1 = 10, num2 = 5
    • Switch: case '+'risultato = 10 + 5 = 15
    • Mostra: “Risultato: 15.00” (verde)
    • lblOperazione: “Operazione: Somma”

📚 Operatori Aritmetici in C#

Operatore + (Somma)

Somma due numeri. Con stringhe, concatena (unisce) le stringhe.

// Con numeri:
int risultato = 10 + 5;        // 15
double somma = 3.5 + 2.1;      // 5.6

// Con stringhe (concatenazione):
string nome = "Mario" + " " + "Rossi";  // "Mario Rossi"
string testo = "Voto: " + 85;           // "Voto: 85"
Operatore – (Sottrazione)

Sottrae il secondo numero dal primo.

int risultato = 10 - 5;        // 5
double differenza = 7.8 - 2.3;   // 5.5

// Con numeri negativi:
int x = 10 - 15;                 // -5
Operatore * (Moltiplicazione)

Moltiplica due numeri.

int risultato = 10 * 5;        // 50
double prodotto = 2.5 * 4;       // 10.0

// Calcolo area rettangolo:
double larghezza = 5.0;
double altezza = 3.0;
double area = larghezza * altezza;  // 15.0
Operatore / (Divisione)

Divide il primo numero per il secondo. ATTENZIONE: La divisione per zero genera errore!

double risultato = 10.0 / 2.0;    // 5.0
double quoziente = 7.0 / 2.0;     // 3.5

// IMPORTANTE: Divisione interi vs decimali
int a = 7 / 2;                    // 3 (divisione intera, perde decimali!)
double b = 7.0 / 2.0;             // 3.5 (divisione decimale)
double c = 7 / 2;                 // 3.0 (converti almeno uno in double!)

// Gestione divisione per zero:
double divisore = 0;
if (divisore != 0)
{
    double risultato = 10 / divisore;
}
else
{
    // Errore: divisione per zero!
}
Precedenza Operatori

Gli operatori hanno un ordine di esecuzione (come in matematica).

// Ordine di precedenza (dal più alto al più basso):
// 1. Parentesi ()
// 2. Moltiplicazione * e Divisione /
// 3. Somma + e Sottrazione -

int risultato1 = 10 + 5 * 2;        // 20 (prima 5*2=10, poi 10+10)
int risultato2 = (10 + 5) * 2;      // 30 (prima 10+5=15, poi 15*2)

double calc = 10 + 20 / 2 - 3;      // 17
// Esecuzione: 20/2=10 → 10+10=20 → 20-3=17

// Best Practice: Usa parentesi per chiarezza!
double media = (voto1 + voto2 + voto3) / 3;  // ✅ Chiaro
double media2 = voto1 + voto2 + voto3 / 3;   // ❌ Confuso!

🌡️ Esercizio 5: Convertitore di Temperatura

▶ Obiettivo

Creare un convertitore che trasforma temperature da Celsius a Fahrenheit e viceversa.
Introduce l’uso di RadioButton per selezionare opzioni mutuamente esclusive
e formule matematiche più complesse.

🎯 Nuovi Concetti:

  • RadioButton: Controlli per scelte esclusive (o questo o quello)
  • RadioButton.Checked: Verificare quale opzione è selezionata
  • Formule matematiche: Applicare conversioni con precedenza operatori
  • CheckedChanged: Evento quando cambia selezione RadioButton
  • ✅ Aggiornamento dinamico dell’interfaccia

📖 Formule di Conversione:

🔥 Da Celsius a Fahrenheit:

F = C × 9/5 + 32

// Esempi:
0°C  = 0 × 9/5 + 32 = 32°F   (punto di congelamento acqua)
100°C = 100 × 9/5 + 32 = 212°F (punto di ebollizione acqua)
37°C = 37 × 9/5 + 32 = 98.6°F (temperatura corporea)

❄️ Da Fahrenheit a Celsius:

C = (F - 32) × 5/9

// Esempi:
32°F  = (32 - 32) × 5/9 = 0°C
212°F = (212 - 32) × 5/9 = 100°C
98.6°F = (98.6 - 32) × 5/9 = 37°C

1

Progettazione dell’Interfaccia

Tipo Name Text Note
Label lblTitolo “Convertitore di Temperatura” Font: 14pt, Bold
Label lblTemperatura “Temperatura:”
TextBox txtTemperatura vuoto Input temperatura
Label lblTipoConversione “Tipo di Conversione:” Font: Bold
RadioButton rbCelsius “Celsius → Fahrenheit” Checked = true
RadioButton rbFahrenheit “Fahrenheit → Celsius” Checked = false
Button btnConverti “Converti”
Label lblRisultato vuoto Font: 14pt, Bold

💡 Suggerimento Layout:

Posiziona i due RadioButton uno sotto l’altro, vicini tra loro.
Visual Studio raggruppa automaticamente i RadioButton sullo stesso Form/Panel:
quando ne selezioni uno, l’altro si deseleziona automaticamente.

2

Codice Completo del Convertitore

Form1.cs – Convertitore Temperatura
using System;
using System.Drawing;
using System.Windows.Forms;

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

        // ═══════════════════════════════════════════════════════
        // EVENTO: Click del pulsante "Converti"
        // ═══════════════════════════════════════════════════════
        private void btnConverti_Click(object sender, EventArgs e)
        {
            // ─────────────────────────────────────────────────
            // STEP 1: Validazione campo vuoto
            // ─────────────────────────────────────────────────
            if (string.IsNullOrEmpty(txtTemperatura.Text))
            {
                lblRisultato.Text = "⚠️ Inserisci una temperatura!";
                lblRisultato.ForeColor = Color.Gray;
                return;
            }

            try
            {
                // ─────────────────────────────────────────────
                // STEP 2: Conversione valore temperatura
                // ─────────────────────────────────────────────
                double temperatura = Convert.ToDouble(txtTemperatura.Text);

                double risultato;
                string unitaPartenza;
                string unitaArrivo;

                // ─────────────────────────────────────────────
                // STEP 3: Determina tipo conversione con RadioButton
                // ─────────────────────────────────────────────
                
                if (rbCelsius.Checked)
                {
                    // Converti da Celsius a Fahrenheit
                    // Formula: F = C × 9/5 + 32
                    
                    risultato = temperatura * 9.0 / 5.0 + 32;
                    unitaPartenza = "°C";
                    unitaArrivo = "°F";
                }
                else  // rbFahrenheit.Checked
                {
                    // Converti da Fahrenheit a Celsius
                    // Formula: C = (F - 32) × 5/9
                    
                    risultato = (temperatura - 32) * 5.0 / 9.0;
                    unitaPartenza = "°F";
                    unitaArrivo = "°C";
                }

                // ─────────────────────────────────────────────
                // STEP 4: Visualizzazione risultato formattato
                // ─────────────────────────────────────────────
                
                // Formato: "25°C = 77°F"
                lblRisultato.Text = $"{temperatura:F1}{unitaPartenza} = {risultato:F1}{unitaArrivo}";
                lblRisultato.ForeColor = Color.Blue;
            }
            catch (FormatException)
            {
                lblRisultato.Text = "❌ Inserisci un valore numerico valido!";
                lblRisultato.ForeColor = Color.Red;
            }
            catch (Exception ex)
            {
                lblRisultato.Text = $"❌ Errore: {ex.Message}";
                lblRisultato.ForeColor = Color.Red;
            }
        }

        // ═══════════════════════════════════════════════════════
        // EVENTI: CheckedChanged - Quando cambia selezione RadioButton
        // ═══════════════════════════════════════════════════════
        
        private void rbCelsius_CheckedChanged(object sender, EventArgs e)
        {
            // Aggiorna l'interfaccia quando cambia la selezione
            AggiornaInterfaccia();
        }

        private void rbFahrenheit_CheckedChanged(object sender, EventArgs e)
        {
            AggiornaInterfaccia();
        }

        // ═══════════════════════════════════════════════════════
        // METODO HELPER: Aggiorna testi interfaccia
        // ═══════════════════════════════════════════════════════
        private void AggiornaInterfaccia()
        {
            if (rbCelsius.Checked)
            {
                lblTitolo.Text = "🔥 Converti da Celsius a Fahrenheit";
                lblTemperatura.Text = "Temperatura (°C):";
            }
            else
            {
                lblTitolo.Text = "❄️ Converti da Fahrenheit a Celsius";
                lblTemperatura.Text = "Temperatura (°F):";
            }

            // Pulisce il risultato precedente
            lblRisultato.Text = "";
        }
    }
}

🔍 Analisi delle Formule:

1️⃣ Celsius → Fahrenheit:

// Formula: F = C × 9/5 + 32
risultato = temperatura * 9.0 / 5.0 + 32;

// IMPORTANTE: Usa 9.0 e 5.0 (con decimali)!
// Sbagliato: 9/5 = 1 (divisione intera)
// Corretto:  9.0/5.0 = 1.8 (divisione decimale)

// Esempio: 25°C
risultato = 25 * 9.0 / 5.0 + 32
         = 25 * 1.8 + 32
         = 45 + 32
         = 77°F

2️⃣ Fahrenheit → Celsius:

// Formula: C = (F - 32) × 5/9
risultato = (temperatura - 32) * 5.0 / 9.0;

// IMPORTANTE: Parentesi sono fondamentali!
// Sbagliato: temperatura - 32 * 5.0 / 9.0
//            (moltiplicazione prima della sottrazione)
// Corretto:  (temperatura - 32) * 5.0 / 9.0
//            (sottrazione prima, poi moltiplicazione)

// Esempio: 77°F
risultato = (77 - 32) * 5.0 / 9.0
         = 45 * 5.0 / 9.0
         = 225.0 / 9.0
         = 25°C

💡 Perché 9.0 e 5.0 invece di 9 e 5?

In C#, la divisione tra numeri interi produce un risultato intero (perde i decimali):

// Divisione INTERA (entrambi int):
int a = 9 / 5;        // 1 (perde .8)
int b = 7 / 2;        // 3 (perde .5)

// Divisione DECIMALE (almeno uno è double):
double c = 9.0 / 5.0; // 1.8 ✅
double d = 9.0 / 5;   // 1.8 ✅ (basta uno double)
double e = 9 / 5.0;   // 1.8 ✅

// Nel convertitore:
// ❌ SBAGLIATO (perde decimali):
risultato = temperatura * 9 / 5 + 32;  
// 25 * 9 / 5 + 32 = 25 * 1 + 32 = 57 (ERRORE!)

// ✅ CORRETTO (mantiene decimali):
risultato = temperatura * 9.0 / 5.0 + 32;
// 25 * 9.0 / 5.0 + 32 = 25 * 1.8 + 32 = 77

3

🎬 Flusso di Esecuzione: Convertire 0°C

  1. Avvio applicazione:
    • rbCelsius è selezionato di default (Checked = true)
    • lblTitolo: “🔥 Converti da Celsius a Fahrenheit”
  2. Utente scrive “0” in txtTemperatura
  3. Utente clicca “Converti”:
    • Evento btnConverti_Click
    • Validazione: campo non vuoto ✅
    • Conversione: temperatura = 0
    • Verifica: rbCelsius.Checked = true
    • Calcolo: risultato = 0 * 9.0 / 5.0 + 32 = 32
    • Mostra: “0.0°C = 32.0°F” (blu)
  4. Utente clicca su rbFahrenheit:
    • Evento rbFahrenheit_CheckedChanged
    • rbCelsius automaticamente diventa Checked = false
    • Chiama AggiornaInterfaccia()
    • lblTitolo: “❄️ Converti da Fahrenheit a Celsius”
    • lblRisultato: “” (pulito)
  5. Utente scrive “32” e clicca “Converti”:
    • temperatura = 32
    • Verifica: rbFahrenheit.Checked = true
    • Calcolo: risultato = (32 - 32) * 5.0 / 9.0 = 0
    • Mostra: “32.0°F = 0.0°C” (blu)

📚 Nuovi Controlli e Concetti – Esercizio 5

RadioButton

Controllo per selezionare una sola opzione tra un gruppo di scelte mutuamente esclusive.

// Caratteristiche RadioButton:
// 1. Solo UNO può essere selezionato alla volta
// 2. Selezionarne uno deseleziona automaticamente gli altri
// 3. Devono essere nello stesso contenitore (Form, Panel, GroupBox)

// Creazione:
RadioButton rbOpzione1 = new RadioButton();
rbOpzione1.Text = "Opzione 1";
rbOpzione1.Checked = true;  // Selezionato

RadioButton rbOpzione2 = new RadioButton();
rbOpzione2.Text = "Opzione 2";
rbOpzione2.Checked = false; // Non selezionato

// Verifica quale è selezionato:
if (rbOpzione1.Checked)
{
    // L'utente ha selezionato Opzione 1
}
else if (rbOpzione2.Checked)
{
    // L'utente ha selezionato Opzione 2
}
RadioButton.Checked (proprietà)

Proprietà booleana che indica se il RadioButton è selezionato (true) o no (false).

// Leggere lo stato:
bool selezionato = rbCelsius.Checked;

// Impostare lo stato programmaticamente:
rbCelsius.Checked = true;   // Seleziona questo
// Automaticamente rbFahrenheit diventa Checked = false

// Uso pratico:
if (rbCelsius.Checked)
{
    // Esegui conversione Celsius → Fahrenheit
}
else
{
    // Esegui conversione Fahrenheit → Celsius
}
Evento CheckedChanged

Si attiva quando cambia lo stato Checked del RadioButton (da true a false o viceversa).

// Gestione evento:
private void rbCelsius_CheckedChanged(object sender, EventArgs e)
{
    // Questo si attiva quando:
    // - L'utente clicca su rbCelsius (diventa Checked = true)
    // - L'utente clicca su rbFahrenheit (rbCelsius diventa Checked = false)
    
    if (rbCelsius.Checked)
    {
        lblTitolo.Text = "Converti da Celsius";
    }
}

// ATTENZIONE: Si attiva due volte quando cambi selezione:
// 1. Sul RadioButton che si DESELEZIONA (Checked passa da true a false)
// 2. Sul RadioButton che si SELEZIONA (Checked passa da false a true)

// Best Practice: Controlla sempre rbX.Checked prima di agire
private void RadioButton_CheckedChanged(object sender, EventArgs e)
{
    RadioButton rb = (RadioButton)sender;
    
    if (rb.Checked)  // Esegui solo se è quello selezionato
    {
        // Logica qui
    }
}
Formattazione :F1

Formatta un numero con 1 cifra decimale. Variante di :F2 visto negli esercizi precedenti.

double temperatura = 25.6789;

// Vari formati decimali:
$"{temperatura:F0}"    // "26" (nessun decimale, arrotondato)
$"{temperatura:F1}"    // "25.7" (1 decimale)
$"{temperatura:F2}"    // "25.68" (2 decimali)
$"{temperatura:F3}"    // "25.679" (3 decimali)

// Nel convertitore:
lblRisultato.Text = $"{temperatura:F1}°C = {risultato:F1}°F";
// Output: "25.7°C = 78.3°F"

// Alternativa senza formattazione:
lblRisultato.Text = temperatura + "°C = " + risultato + "°F";
// Output: "25.6789°C = 78.22202°F" (troppi decimali!)
String Interpolation $”…”

Sintassi moderna per inserire variabili direttamente in una stringa usando ${variabile}.

// Metodo MODERNO (C# 6.0+) - String Interpolation:
string nome = "Mario";
int eta = 25;
string messaggio = $"Ciao {nome}, hai {eta} anni";
// Output: "Ciao Mario, hai 25 anni"

// Con formattazione:
double prezzo = 19.99;
string testo = $"Prezzo: €{prezzo:F2}";
// Output: "Prezzo: €19.99"

// Metodo VECCHIO - Concatenazione:
string messaggio2 = "Ciao " + nome + ", hai " + eta + " anni";
// Stesso risultato ma meno leggibile

// Metodo VECCHIO - String.Format:
string messaggio3 = String.Format("Ciao {0}, hai {1} anni", nome, eta);

// Best Practice: Usa String Interpolation $"..." quando possibile!
lblRisultato.Text = $"{temperatura:F1}°C = {risultato:F1}°F";
Precedenza con Parentesi

Le parentesi forzano l’ordine di esecuzione delle operazioni matematiche.

// CRUCIALE nelle formule di conversione!

// ❌ SBAGLIATO (senza parentesi):
risultato = temperatura - 32 * 5.0 / 9.0;
// Esecuzione: 32 * 5.0 = 160.0
//             160.0 / 9.0 = 17.78
//             temperatura - 17.78
// Per 77°F: 77 - 17.78 = 59.22 (ERRORE! Dovrebbe essere 25)

// ✅ CORRETTO (con parentesi):
risultato = (temperatura - 32) * 5.0 / 9.0;
// Esecuzione: (77 - 32) = 45
//             45 * 5.0 = 225.0
//             225.0 / 9.0 = 25.0 (CORRETTO!)

// Regola: Le parentesi hanno MASSIMA precedenza
// Ordine senza parentesi: * / % → + -
// Con parentesi: () → * / % → + -

// Altri esempi:
int x = 10 + 5 * 2;        // 20 (prima 5*2=10, poi +10)
int y = (10 + 5) * 2;      // 30 (prima 10+5=15, poi 15*2)

double z = 10 + 20 / 2;    // 20 (prima 20/2=10, poi 10+10)
double w = (10 + 20) / 2;  // 15 (prima 10+20=30, poi 30/2)

🎓 Riepilogo Completo: Cosa Hai Imparato

✨ Competenze Acquisite in Questa Lezione

🔢 Operazioni Matematiche

  • Somma (+), sottrazione (-)
  • Moltiplicazione (*), divisione (/)
  • Modulo (%) per resto divisione
  • Precedenza operatori e uso parentesi

✅ Strutture Condizionali

  • if-else per decisioni binarie
  • if-else if-else per condizioni multiple
  • switch per valutazioni discrete
  • Pattern matching con range (C# 9.0+)

🔄 Conversioni di Tipo

  • Convert.ToDouble() per decimali
  • Convert.ToInt32() per interi
  • .ToString() per stringhe
  • Formattazione con :F0, :F1, :F2

🛡️ Validazione Input

  • string.IsNullOrEmpty()
  • Gestione errori con try-catch
  • Validazione range (es: 0-100)
  • Filtro caratteri con KeyPress

🎨 Feedback Visivo

  • Cambiare colori con ForeColor
  • Abilitare/disabilitare con Enabled
  • Messaggi dinamici informativi
  • Icone emoji per UX migliore

🎛️ Nuovi Controlli

  • RadioButton: Scelte mutuamente esclusive
  • .Checked per verificare selezione
  • Evento CheckedChanged
  • Aggiornamento dinamico interfaccia

🏆 Tabella Riepilogativa Esercizi

Esercizio Concetti Chiave Applicazione Pratica
1. Somma Numeri Conversioni, validazione, try-catch Base per qualsiasi calcolatrice
2. Pari/Dispari Operatore %, if-else, KeyPress Validazione numeri, filtri input
3. Valutazione Voto Switch, pattern matching, range Sistemi di rating, classificazione
4. Calcolatrice 4 operatori, metodi helper, divisione zero App calcolatrice completa
5. Convertitore Temp RadioButton, formule complesse, precedenza Convertitori unità di misura

💎 Best Practices: Lezioni Apprese

✅ Regole d’Oro della Programmazione

1️⃣ Valida SEMPRE l’Input Utente

Non fidarti mai di ciò che l’utente inserisce. Controlla sempre:

  • ✅ Il campo è compilato? (string.IsNullOrEmpty())
  • ✅ Il valore è numerico? (try-catch o TryParse)
  • ✅ È nel range corretto? (if (voto >= 0 && voto <= 100))
  • ✅ Casi speciali? (divisione per zero, valori negativi, ecc.)

2️⃣ Dai Feedback Visivo Immediato

L’utente deve sempre sapere cosa sta succedendo:

  • 🟢 Verde: Operazione riuscita, valore valido
  • 🔴 Rosso: Errore, valore non valido
  • 🟠 Arancione: Avviso, attenzione richiesta
  • 🔵 Blu: Informazione neutra, risultato
  • Grigio: Disabilitato, non disponibile

3️⃣ Scrivi Codice Leggibile

// ✅ BUONO - Leggibile
if (eta >= 18)
{
    MessageBox.Show("Maggiorenne");
}
else
{
    MessageBox.Show("Minorenne");
}

// ❌ CATTIVO - Compresso, illeggibile
if(eta>=18)MessageBox.Show("Maggiorenne");else MessageBox.Show("Minorenne");

// ✅ BUONO - Nomi significativi
double temperaturaFahrenheit = (celsius * 9.0 / 5.0) + 32;

// ❌ CATTIVO - Nomi vaghi
double x = (y * 9.0 / 5.0) + 32;

4️⃣ Usa try-catch per Conversioni

// ✅ BUONO - Gestisce errori
try
{
    int numero = Convert.ToInt32(txtInput.Text);
    // Usa numero sapendo che è valido
}
catch (FormatException)
{
    lblErrore.Text = "Inserisci un numero valido!";
}

// ❌ CATTIVO - Può crashare
int numero = Convert.ToInt32(txtInput.Text);  // 💥 Se non è numero, crash!

5️⃣ Attenzione ai Decimali nelle Divisioni

// ❌ SBAGLIATO - Divisione intera perde decimali
double media = (voto1 + voto2 + voto3) / 3;
// Se voto1=7, voto2=8, voto3=9 → (7+8+9)/3 = 24/3 = 8 (CORRETTO)
// Se voto1=7, voto2=8, voto3=8 → (7+8+8)/3 = 23/3 = 7 (ERRORE! Dovrebbe essere 7.67)

// ✅ CORRETTO - Almeno un operando deve essere double
double media = (voto1 + voto2 + voto3) / 3.0;
// 23 / 3.0 = 7.666... ✅

// Oppure converti esplicitamente:
double media = (double)(voto1 + voto2 + voto3) / 3;

6️⃣ Usa Parentesi nelle Formule Complesse

Anche se conosci la precedenza degli operatori, le parentesi rendono il codice più chiaro:

// ✅ CHIARO - Parentesi esplicite
double fahrenheit = (celsius * 9.0 / 5.0) + 32;
double media = (voto1 + voto2 + voto3) / 3.0;

// ❌ AMBIGUO - Precedenza implicita
double fahrenheit = celsius * 9.0 / 5.0 + 32;  // Funziona ma meno chiaro
double media = voto1 + voto2 + voto3 / 3.0;    // 💥 ERRORE! Divide solo voto3

❌ Errori Comuni da Evitare

1️⃣ Dimenticare la Gestione Errori

// ❌ PERICOLOSO
private void btnCalcola_Click(object sender, EventArgs e)
{
    double num = Convert.ToDouble(txtNumero.Text);  // 💥 Crash se non è numero!
    lblRisultato.Text = (num * 2).ToString();
}

// ✅ SICURO
private void btnCalcola_Click(object sender, EventArgs e)
{
    try
    {
        double num = Convert.ToDouble(txtNumero.Text);
        lblRisultato.Text = (num * 2).ToString();
    }
    catch (FormatException)
    {
        lblRisultato.Text = "Inserisci un numero valido!";
    }
}

2️⃣ Non Validare la Divisione per Zero

// ❌ ERRORE - Divisione per zero
double risultato = num1 / num2;  // 💥 Se num2 = 0, errore!

// ✅ CORRETTO - Controllo prima di dividere
if (num2 != 0)
{
    double risultato = num1 / num2;
    lblRisultato.Text = risultato.ToString();
}
else
{
    lblRisultato.Text = "Errore: divisione per zero!";
}

3️⃣ Confondere = con ==

// ❌ ERRORE - Assegnazione invece di confronto
if (voto = 10)  // 💥 ERRORE! Sta assegnando, non confrontando!
{
    // Questo codice non compila
}

// ✅ CORRETTO - Confronto
if (voto == 10)  // ✅ Confronta se voto è uguale a 10
{
    lblGiudizio.Text = "Perfetto!";
}

// Ricorda:
// = → Assegnazione (voto = 10 → assegna 10 a voto)
// == → Confronto (voto == 10 → verifica se voto è 10)

4️⃣ Dimenticare break in switch

// ❌ ERRORE - Manca break
switch (voto)
{
    case >= 90:
        valutazione = "Eccellente";
        // ❌ ERRORE: manca break!
    case >= 80:
        valutazione = "Molto Buono";
        break;
}

// ✅ CORRETTO - break dopo ogni case
switch (voto)
{
    case >= 90:
        valutazione = "Eccellente";
        break;  // ✅
    case >= 80:
        valutazione = "Molto Buono";
        break;  // ✅
}

🚀 Prossimi Passi: Il Tuo Percorso di Apprendimento

📖 Cosa Farai Nelle Prossime Lezioni

Lezione 8 – Cicli e Iterazioni (TEORIA + GUI)

  • 🔄 Ciclo for: Ripetere codice un numero preciso di volte
  • ♾️ Ciclo while: Ripetere fino a condizione vera
  • Ciclo do-while: Eseguire almeno una volta
  • 📋 Ciclo foreach: Iterare su collezioni (liste, array)
  • 🎯 Applicazioni pratiche: Generare tabelle, popolare liste, calcoli ripetuti

Lezione 9 – Array e Liste (TEORIA APPROFONDITA)

  • 📊 Array: Collezioni a dimensione fissa
  • 📈 List<T>: Collezioni dinamiche (già usate!)
  • 🔍 Metodi delle liste: Add, Remove, Contains, Find, Sort
  • 🎨 LINQ base: Query su collezioni
  • 💡 Quando usare array vs liste

Lezione 10 – Metodi e Funzioni (TEORIA FONDAMENTALE)

  • ⚙️ Creare metodi personalizzati: Riutilizzare codice
  • 📥 Parametri: Passare dati ai metodi
  • 📤 Return: Restituire risultati
  • 🔀 Overloading: Metodi con stesso nome, parametri diversi
  • 🎯 Refactoring: Migliorare codice esistente

🎯 Il Piano Completo del Corso

Stai seguendo un percorso strutturato che alterna pratica (esercizi con GUI)
e teoria (concetti fondamentali). Dopo le prossime 3 lezioni teoriche,
tornerai a progetti pratici complessi come Gestionale Biblioteca,
Sistema Prenotazioni e altro ancora!

💪 Esercitazione Prima della Prossima Lezione

Per consolidare quanto appreso, prova a completare questi esercizi:

  1. Calcolatrice IMC Migliorata:
    Riprendi l’esercizio della Lezione 6 e aggiungi classificazione BMI con colori
    (come nell’Esercizio 3 – Valutazione Voto)
  2. Convertitore Multi-Unità:
    Estendi il convertitore temperatura aggiungendo conversioni per:
    km ↔ miglia, kg ↔ libbre, litri ↔ galloni
  3. Calcolatrice Scientifica Semplice:
    Aggiungi alla calcolatrice base (Esercizio 4): potenza (x²), radice quadrata (√),
    percentuale (%)
  4. Sistema di Voti:
    Crea un’app che permette di inserire più voti in una ListBox, calcola la media
    e mostra la valutazione finale (usa Esercizio 3 + Lezione 4)

Lezione 7 – Programmazione ad Eventi: Esercizi Pratici con Windows Forms

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

© 2025 Academy PC – Tutti i diritti riservati

Torna in alto