✅ Validazione Avanzata e User Experience

Crea interfacce user-friendly con validazione professionale!

🎯 Cosa Imparerai in Questa Lezione

Hai imparato a creare interfacce e gestire dati. Ora è il momento di renderle
professionali con validazione real-time, messaggi d’errore chiari,
input formattati e feedback visivo immediato! 🚀

✨ I Nuovi Controlli UX

❗ ErrorProvider

Mostra icone di errore accanto ai controlli con problemi.
Feedback visivo professionale!

🎭 MaskedTextBox

TextBox con maschera per input formattati: telefono, codice fiscale,
date, codici.

💡 ToolTip

Suggerimenti che appaiono quando passi il mouse sopra un controllo.
Aiuta l’utente!

📊 StatusStrip

Barra in fondo alla finestra per mostrare informazioni di stato,
conteggi, messaggi.

📚 Prerequisiti
  • ✅ Dialog e Multi-finestre (Lezione 10)
  • ✅ Validazione base (if, string.IsNullOrWhiteSpace)
  • ✅ Eventi e metodi

❗ ErrorProvider: Mostra Errori Visivamente

🤔 Cos’è l’ErrorProvider?

Hai presente quando compili un form online e appare un’icona rossa
con un punto esclamativo accanto al campo sbagliato? Quello è un ErrorProvider!
Molto più chiaro di un semplice MessageBox!

💡 Vantaggi dell’ErrorProvider

  • ✅ L’utente vede SUBITO dove ha sbagliato (icona rossa vicino al campo)
  • ✅ Può correggere senza chiudere MessageBox fastidiosi
  • ✅ Più errori visibili contemporaneamente
  • ✅ Passando il mouse sull’icona, appare il messaggio d’errore
  • ✅ Look professionale come le app vere!
⚙️ Come Funziona

  1. Aggiungi un ErrorProvider dalla Toolbox (appare in basso, non è visibile)
  2. Quando c’è un errore: errorProvider.SetError(txt_campo, "Messaggio errore")
  3. Quando l’errore è corretto: errorProvider.SetError(txt_campo, "")

📝 Esercizio 1: Form Registrazione con ErrorProvider

▶ Obiettivo

Crea un form di registrazione utente con validazione real-time.
Ogni campo mostra immediatamente se c’è un errore mentre l’utente digita!

1

Interfaccia

🎨 Controlli:

  • Label + TextBox: txt_username (min 5 caratteri)
  • Label + TextBox: txt_email (deve contenere @)
  • Label + TextBox: txt_password (min 8 caratteri, PasswordChar = ‘●’)
  • Label + TextBox: txt_conferma_password (deve essere uguale)
  • Label + NumericUpDown: num_eta (Minimum = 13, Maximum = 120)
  • CheckBox: chk_termini (Text = “Accetto i termini e condizioni”)
  • Button: btn_registrati (Text = “✅ Registrati”)
  • ErrorProvider: errorProvider (dalla Toolbox)
  • ToolTip: toolTip (dalla Toolbox)

2

Codice Completo

FormRegistrazione.cs
using System;
using System.Text.RegularExpressions;
using System.Windows.Forms;

namespace FormRegistrazione
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            
            // Imposta ToolTip per aiutare l'utente
            toolTip.SetToolTip(txt_username, "Minimo 5 caratteri, solo lettere e numeri");
            toolTip.SetToolTip(txt_email, "Inserisci un'email valida (es: nome@esempio.com)");
            toolTip.SetToolTip(txt_password, "Minimo 8 caratteri");
            toolTip.SetToolTip(txt_conferma_password, "Deve essere uguale alla password");
            toolTip.SetToolTip(num_eta, "Devi avere almeno 13 anni");
            
            // Collega eventi per validazione real-time
            txt_username.TextChanged += txt_username_TextChanged;
            txt_email.TextChanged += txt_email_TextChanged;
            txt_password.TextChanged += txt_password_TextChanged;
            txt_conferma_password.TextChanged += txt_conferma_password_TextChanged;
        }

        // ═══════════════════════════════════════════
        // VALIDAZIONE REAL-TIME: Username
        // ═══════════════════════════════════════════
        private void txt_username_TextChanged(object sender, EventArgs e)
        {
            string username = txt_username.Text.Trim();
            
            // Valida lunghezza minima
            if (username.Length < 5)
            {
                errorProvider.SetError(txt_username, "Lo username deve essere di almeno 5 caratteri");
                txt_username.BackColor = Color.LightPink;
            }
            // Valida che contenga solo lettere/numeri
            else if (!Regex.IsMatch(username, @"^[a-zA-Z0-9]+$"))
            {
                errorProvider.SetError(txt_username, "Solo lettere e numeri, niente spazi o caratteri speciali");
                txt_username.BackColor = Color.LightPink;
            }
            else
            {
                // ✅ OK!
                errorProvider.SetError(txt_username, "");
                txt_username.BackColor = Color.LightGreen;
            }
        }

        // ═══════════════════════════════════════════
        // VALIDAZIONE REAL-TIME: Email
        // ═══════════════════════════════════════════
        private void txt_email_TextChanged(object sender, EventArgs e)
        {
            string email = txt_email.Text.Trim();
            
            // Regex per validare email (pattern semplice)
            string pattern = @"^[^@\s]+@[^@\s]+\.[^@\s]+$";
            
            if (string.IsNullOrWhiteSpace(email))
            {
                errorProvider.SetError(txt_email, "L'email è obbligatoria");
                txt_email.BackColor = Color.LightPink;
            }
            else if (!Regex.IsMatch(email, pattern))
            {
                errorProvider.SetError(txt_email, "Email non valida (manca @ o dominio)");
                txt_email.BackColor = Color.LightPink;
            }
            else
            {
                // ✅ OK!
                errorProvider.SetError(txt_email, "");
                txt_email.BackColor = Color.LightGreen;
            }
        }

        // ═══════════════════════════════════════════
        // VALIDAZIONE REAL-TIME: Password
        // ═══════════════════════════════════════════
        private void txt_password_TextChanged(object sender, EventArgs e)
        {
            string password = txt_password.Text;
            
            if (password.Length < 8)
            {
                errorProvider.SetError(txt_password, "La password deve essere di almeno 8 caratteri");
                txt_password.BackColor = Color.LightPink;
            }
            else
            {
                // ✅ OK!
                errorProvider.SetError(txt_password, "");
                txt_password.BackColor = Color.LightGreen;
            }
            
            // Ricontrolla anche la conferma password
            txt_conferma_password_TextChanged(null, null);
        }

        // ═══════════════════════════════════════════
        // VALIDAZIONE REAL-TIME: Conferma Password
        // ═══════════════════════════════════════════
        private void txt_conferma_password_TextChanged(object sender, EventArgs e)
        {
            if (txt_conferma_password.Text != txt_password.Text)
            {
                errorProvider.SetError(txt_conferma_password, "Le password non coincidono");
                txt_conferma_password.BackColor = Color.LightPink;
            }
            else if (!string.IsNullOrEmpty(txt_conferma_password.Text))
            {
                // ✅ OK!
                errorProvider.SetError(txt_conferma_password, "");
                txt_conferma_password.BackColor = Color.LightGreen;
            }
        }

        // ═══════════════════════════════════════════
        // PULSANTE: Registrati
        // ═══════════════════════════════════════════
        private void btn_registrati_Click(object sender, EventArgs e)
        {
            // Controlla che tutti i campi siano validi
            bool tuttoValido = true;
            
            // Username
            if (txt_username.Text.Trim().Length < 5)
            {
                tuttoValido = false;
                errorProvider.SetError(txt_username, "Username troppo corto");
            }
            
            // Email
            string emailPattern = @"^[^@\s]+@[^@\s]+\.[^@\s]+$";
            if (!Regex.IsMatch(txt_email.Text.Trim(), emailPattern))
            {
                tuttoValido = false;
                errorProvider.SetError(txt_email, "Email non valida");
            }
            
            // Password
            if (txt_password.Text.Length < 8)
            {
                tuttoValido = false;
                errorProvider.SetError(txt_password, "Password troppo corta");
            }
            
            // Conferma password
            if (txt_conferma_password.Text != txt_password.Text)
            {
                tuttoValido = false;
                errorProvider.SetError(txt_conferma_password, "Le password non coincidono");
            }
            
            // Età
            if (num_eta.Value < 13)
            {
                tuttoValido = false;
                MessageBox.Show("Devi avere almeno 13 anni!", "Attenzione", 
                              MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
            
            // Termini
            if (!chk_termini.Checked)
            {
                tuttoValido = false;
                MessageBox.Show("Devi accettare i termini e condizioni!", "Attenzione", 
                              MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
            
            // Se tutto OK, procedi!
            if (tuttoValido)
            {
                MessageBox.Show(
                    $"✅ Registrazione completata!\n\n" +
                    $"Username: {txt_username.Text}\n" +
                    $"Email: {txt_email.Text}\n" +
                    $"Età: {num_eta.Value}", 
                    "Successo", 
                    MessageBoxButtons.OK, 
                    MessageBoxIcon.Information);
                
                // Pulisci il form
                PulisciForm();
            }
            else
            {
                MessageBox.Show("Correggi gli errori evidenziati!", "Errori di validazione", 
                              MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
        }

        // ═══════════════════════════════════════════
        // HELPER: Pulisci il Form
        // ═══════════════════════════════════════════
        private void PulisciForm()
        {
            txt_username.Clear();
            txt_email.Clear();
            txt_password.Clear();
            txt_conferma_password.Clear();
            num_eta.Value = 18;
            chk_termini.Checked = false;
            
            // Pulisci ErrorProvider
            errorProvider.Clear();
            
            // Reset colori
            txt_username.BackColor = Color.White;
            txt_email.BackColor = Color.White;
            txt_password.BackColor = Color.White;
            txt_conferma_password.BackColor = Color.White;
        }
    }
}

3

🎬 Come Funziona

📋 Esperienza Utente:

  1. Utente inizia a digitare username: "Mar" → campo rosso + icona errore ❗
  2. Continua: "Mario" → campo verde ✅ errore sparisce
  3. Email: "mario" → rosso (manca @)
  4. Email: "mario@email.it" → verde ✅
  5. Password: "1234" → rosso (troppo corta)
  6. Password: "12345678" → verde ✅
  7. Conferma password: "1234" → rosso (non coincide)
  8. Conferma password: "12345678" → verde ✅
  9. Clicca Registrati → se tutto OK: messaggio di successo!

📚 Nuovi Concetti

Regex (Regular Expressions)
Pattern per validare formato stringhe

using System.Text.RegularExpressions;

// Valida email
string pattern = @"^[^@\s]+@[^@\s]+\.[^@\s]+$";
bool valida = Regex.IsMatch("mario@email.it", pattern); // true

// Solo lettere e numeri
bool soloAlfanumerico = Regex.IsMatch("Mario123", @"^[a-zA-Z0-9]+$"); // true

// Numero di telefono italiano (10 cifre)
bool telefono = Regex.IsMatch("3331234567", @"^\d{10}$"); // true

ErrorProvider.SetError()
Mostra/nasconde icona errore su un controllo

// Mostra errore
errorProvider.SetError(txt_campo, "Questo campo è obbligatorio");

// Rimuovi errore
errorProvider.SetError(txt_campo, "");

// Pulisci tutti gli errori
errorProvider.Clear();

ToolTip.SetToolTip()
Imposta suggerimento che appare al passaggio del mouse

toolTip.SetToolTip(txt_username, "Inserisci il tuo nome utente");
toolTip.SetToolTip(btn_salva, "Clicca per salvare i dati");

🎭 MaskedTextBox: Input Formattati

🤔 Cos'è il MaskedTextBox?

TextBox "intelligente" che guida l'utente nel formato corretto!
Telefono: ___-_______. Data: __/__/____. Codice: ____-____-____.
L'utente può solo digitare nei posti giusti!

🎯 Maschere Comuni

// Telefono italiano (10 cifre)
Mask = "000-0000000"          // 333-1234567

// Data italiana
Mask = "00/00/0000"           // 15/01/2025

// Codice fiscale italiano
Mask = "LLLLLL00L00L000L"    // RSSMRA85M15H501Z

// CAP (5 cifre)
Mask = "00000"                // 00100

// Carta di credito
Mask = "0000-0000-0000-0000"  // 1234-5678-9012-3456

// Ora (HH:MM)
Mask = "00:00"                // 14:30

📖 Legenda Caratteri Maschera:

  • 0 = Cifra obbligatoria (0-9)
  • 9 = Cifra opzionale
  • L = Lettera obbligatoria (a-z, A-Z)
  • ? = Lettera opzionale
  • A = Lettera o cifra obbligatoria
  • a = Lettera o cifra opzionale

📇 Esercizio 2: Anagrafica Clienti Professionale

▶ Obiettivo

Form anagrafica con MaskedTextBox per telefono, codice fiscale e data di nascita!

1

Interfaccia con MaskedTextBox

  • TextBox: txt_nome
  • TextBox: txt_cognome
  • MaskedTextBox: mtxt_telefono (Mask = "000-0000000")
  • TextBox: txt_email
  • MaskedTextBox: mtxt_codice_fiscale (Mask = "LLLLLL00L00L000L")
  • MaskedTextBox: mtxt_data_nascita (Mask = "00/00/0000")
  • Button: btn_salva
  • StatusStrip con Label: toolStripStatusLabel

2

Codice

private void Form1_Load(object sender, EventArgs e)
{
    // Imposta maschere
    mtxt_telefono.Mask = "000-0000000";
    mtxt_codice_fiscale.Mask = "LLLLLL00L00L000L";
    mtxt_data_nascita.Mask = "00/00/0000";
    
    // Imposta ToolTip
    toolTip.SetToolTip(mtxt_telefono, "Formato: 333-1234567");
    toolTip.SetToolTip(mtxt_codice_fiscale, "Es: RSSMRA85M15H501Z");
    toolTip.SetToolTip(mtxt_data_nascita, "Formato: GG/MM/AAAA");
    
    // Messaggio iniziale nella StatusBar
    toolStripStatusLabel.Text = "Pronto";
}

private void btn_salva_Click(object sender, EventArgs e)
{
    // Valida che tutti i campi siano compilati
    bool valido = true;
    errorProvider.Clear();
    
    if (string.IsNullOrWhiteSpace(txt_nome.Text))
    {
        errorProvider.SetError(txt_nome, "Nome obbligatorio");
        valido = false;
    }
    
    if (string.IsNullOrWhiteSpace(txt_cognome.Text))
    {
        errorProvider.SetError(txt_cognome, "Cognome obbligatorio");
        valido = false;
    }
    
    // MaskedTextBox: usa MaskCompleted per verificare se è tutto compilato
    if (!mtxt_telefono.MaskCompleted)
    {
        errorProvider.SetError(mtxt_telefono, "Telefono incompleto");
        valido = false;
    }
    
    if (!mtxt_codice_fiscale.MaskCompleted)
    {
        errorProvider.SetError(mtxt_codice_fiscale, "Codice fiscale incompleto");
        valido = false;
    }
    
    if (!mtxt_data_nascita.MaskCompleted)
    {
        errorProvider.SetError(mtxt_data_nascita, "Data incompleta");
        valido = false;
    }
    
    // Valida email
    if (!txt_email.Text.Contains("@"))
    {
        errorProvider.SetError(txt_email, "Email non valida");
        valido = false;
    }
    
    if (valido)
    {
        // Salva i dati (qui in realtà salveresti su database)
        string dati = $"Nome: {txt_nome.Text}\n" +
                     $"Cognome: {txt_cognome.Text}\n" +
                     $"Telefono: {mtxt_telefono.Text}\n" +
                     $"Email: {txt_email.Text}\n" +
                     $"Codice Fiscale: {mtxt_codice_fiscale.Text}\n" +
                     $"Data di Nascita: {mtxt_data_nascita.Text}";
        
        MessageBox.Show(dati, "Dati Cliente", 
                      MessageBoxButtons.OK, MessageBoxIcon.Information);
        
        // Aggiorna StatusBar
        toolStripStatusLabel.Text = $"Cliente {txt_nome.Text} {txt_cognome.Text} salvato alle {DateTime.Now:HH:mm}";
        
        // Pulisci form
        txt_nome.Clear();
        txt_cognome.Clear();
        txt_email.Clear();
        mtxt_telefono.Clear();
        mtxt_codice_fiscale.Clear();
        mtxt_data_nascita.Clear();
    }
    else
    {
        toolStripStatusLabel.Text = "Errore: correggi i campi evidenziati";
    }
}

📚 MaskedTextBox - Proprietà

MaskCompleted
Verifica se tutti i caratteri obbligatori sono stati inseriti

if (mtxt_telefono.MaskCompleted)
{
    // Telefono completo!
}
else
{
    MessageBox.Show("Completa il numero di telefono!");
}

MaskFull
Verifica se TUTTI i caratteri (anche opzionali) sono inseriti

if (mtxt_codice.MaskFull)
{
    // Ogni singolo carattere è stato inserito
}

📊 StatusStrip: Barra di Stato

La StatusStrip è la barra grigia in fondo alle finestre
che mostra info utili: conteggi, stato operazioni, ora ultimo salvataggio, ecc.

💡 Esempi di Utilizzo:

  • 📄 Editor: "Riga 15, Colonna 23 | 1.245 parole | 7.890 caratteri"
  • 💾 App Dati: "Ultimo salvataggio: 14:30 | 47 record"
  • 📊 Gestionale: "Totale vendite: €12.450,00 | 25 clienti"
  • Validazione: "Tutti i campi OK" / "⚠️ 3 errori da correggere"
Come Aggiungere StatusStrip:

1. Toolbox → StatusStrip → trascina sul form (va automaticamente in fondo)
2. Clicca sulla freccina a destra della StatusStrip
3. Scegli "StatusLabel" per aggiungere una label
4. Properties → (Name) = toolStripStatusLabel
5. Properties → Text = "Pronto"

// Nel codice:
toolStripStatusLabel.Text = "Operazione completata!";
toolStripStatusLabel.Text = $"Record: {conteggio}";
toolStripStatusLabel.Text = $"Salvato: {DateTime.Now:HH:mm:ss}";

📝 Esercizio 3: Mini Editor con Statistiche

▶ Obiettivo

Editor di testo che mostra in tempo reale: parole, caratteri, righe nella StatusBar!

private void txt_editor_TextChanged(object sender, EventArgs e)
{
    string testo = txt_editor.Text;
    
    // Conta caratteri
    int caratteri = testo.Length;
    
    // Conta parole (split per spazi)
    string[] parole = testo.Split(new[] { ' ', '\n', '\r' }, 
                                  StringSplitOptions.RemoveEmptyEntries);
    int conteggioParole = parole.Length;
    
    // Conta righe
    int righe = txt_editor.Lines.Length;
    
    // Aggiorna StatusBar
    toolStripStatusLabel.Text = $"Caratteri: {caratteri} | Parole: {conteggioParole} | Righe: {righe}";
}

// Bonus: Mostra riga corrente
private void txt_editor_SelectionChanged(object sender, EventArgs e)
{
    int riga = txt_editor.GetLineFromCharIndex(txt_editor.SelectionStart) + 1;
    int colonna = txt_editor.SelectionStart - 
                 txt_editor.GetFirstCharIndexOfCurrentLine() + 1;
    
    toolStripStatusLabel2.Text = $"Riga {riga}, Colonna {colonna}";
}

🎓 Riepilogo

✅ ErrorProvider

  • Icone errore vicino ai campi
  • Validazione real-time
  • Feedback visivo professionale
✅ MaskedTextBox

  • Input formattati (telefono, CF, data)
  • Guida l'utente
  • MaskCompleted per validare
✅ ToolTip + StatusStrip

  • Suggerimenti al passaggio mouse
  • Barra stato con info utili
  • UX migliorata
✅ Regex

  • Validazione pattern (email, ecc)
  • Regex.IsMatch()
  • Pattern comuni pronti

🚀 Prossimo Step

Hai imparato TUTTI i controlli essenziali! È il momento del
PROGETTO FINALE: un gestionale completo che integra tutto! 🎯

Torna in alto