Vai al contenuto

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
  • βœ… 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)
  1. 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;
        }
    }
}
  1. 🎬 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
  1. 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! 🎯

πŸ“š Teoria collegata (percorso C# – il linguaggio, esempi in console): Gestione delle eccezioni e Stringhe, date e input utente.