Technology Experience

Contenuti gestiti da Igor Damiani
posts - 949, comments - 2741, trackbacks - 15120

My Links

News

  • Questo blog si propone di raccogliere riflessioni, teoriche e pratiche, su tutto quello che riguarda il world-computing che mi sta attorno: programmazione in .NET, software attuale e futuro, notizie provenienti dal web, tecnologia in generale, open-source.

    L'idea è quella di lasciare una sorta di patrimonio personale, una raccolta di idee che un giorno potrebbe farmi sorridere, al pensiero di dov'ero e cosa stavo facendo.

    10/05/2005,
    Milano

Archives

Post Categories

Generale

[MCAD.8] Completare l'evento Validating con il controllo ErrorProvider ed altre chicce

Nel post precedente abbiamo realizzato una piccola applicazione che ci dice quanti anni abbiamo. Ho usato l'evento Validating della TextBox per controllare che l'input dell'utente sia valido (in realtà l'ho fatto in modo orrendo, come giustamente Marco ho sottolineato). In questo post vado avanti con il discorso:

  • usiamo il controllo ErrorProvider per segnalare all'utente l'errore
  • miglioriamo la validazione con la classe Convert
  • diamo una prima occhiata al blocco Try...Catch

Nel codice di ieri, se la validazione falliva, il codice prevedeva un semplice e.Cancel = true ; senza dare alcuna comunicazione all'utente. Semplicemente il focus rimane sulla TextBox e il click sul button btnCalcola non fa nulla. La Study Guide di Lorenzo dice di usare il control ErrorProvider. Di cosa si tratta? In breve: è un controllo invisibile a run-time che dispone di un metodo SetError (dettagli su MSDN) che permette di mostrare un'icona vicino al controllo che ha fallito la validazione e un tooltip sopra l'icona con il messaggio che vogliamo noi. Ok, vediamo di usarlo. Dalla toolbox di VS, trasciniamo il controllo ErrorProvider, lo chiamiamo errMsg. Io ho impostato la property BlinkStyle = NeverBlink .
A questo punto ho scritto il codice dell'evento Validating, migliore rispetto a quello di ieri:

private void txtBirthYear_Validating(object sender, System.ComponentModel.CancelEventArgs e)
{
    
int anno;
    
string msg;

    
// controllo la lunghezza della property Text della TextBox
    
if (txtBirthYear.Text.Length != 4)
        msg = "L'anno deve essere lungo 4 caratteri!";

    
try
    
{    
        
// assegnamento che potrebbe sollevare un'exception
        
anno = Convert.ToInt32(txtBirthYear.Text);
    }
    
catch
    
{
        
// in caso di errore, la validazione fallisce
        // e setto il messaggio di errore
        
msg = "L'anno non è valido!";
    }

    
if (anno < 1880 || anno > System.DateTime.Now.Year)
        msg = "L'anno non è valido!";

    errMsg.SetError(txtBirthYear, msg);
    
// attivo l'errore e il focus
    // rimane sulla TextBox
    
if (msg.Length > 0) e.Cancel = true;
}

Qui c'è già qualcosa che non mi quadra. Ho letto il post di Marco sull'uso della libreria FxCop per la scrittura del buon codice. Una delle regole, la n°4, dice di "evitare di inizializzare le variabili ai loro valori di default. E' quello che ho fatto io qui sopra, ma il compilatore mi riporta: "Use of unassigned local variable 'anno'" (stesso errore per msg), quindi ho dovuto assegnare dei valori iniziali alle variabili (anno = 0, msg = ""). Mah!

Ho dichiarato una stringa msg che conterrà l'eventuale messaggio di errore da mostrare. A questo punto faccio tutti i controlli che voglio: se c'è qualcosa che non va, in msg memorizzo la stringa che voglio far vedere all'utente finale. In breve: controllo la lunghezza, come ieri, controllo che l'anno non sia superiore all'anno in corso e che non sia minore di 1880. Attenzione: qui non siamo più in VB6, le conversioni bisogna farsele a mano. txtBirthYear.Text contiene una stringa, per cui:

try
{    
    
// assegnamento che potrebbe sollevare un'exception
    
anno = Convert.ToInt32(txtBirthYear.Text);
}
catch
{
    
// in caso di errore, la validazione fallisce
    // e setto il messaggio di errore
    
msg = "L'anno non è valido!";
}

Attenzione: il codice è stato incluso in un blocco Try...Catch per prevenire eventuali assegnamenti che genererebbero errori (se l'utente scrive "196a", per esempio, o altre stringhe che non possono essere convertite in intero).

In fondo alla function Validating, infine, chiamo sempre il metodo SetError così:

errMsg.SetError(txtBirthYear, msg);

Questa linea di codice serve anche a rimuovere icona+messaggio nel caso in cui la validazione abbia successo. MSDN dice che: "If the string length is greater than zero, then the error icon is displayed, and the ToolTip for the error icon is the error description text. If the string length is zero, the error icon is hidden.". Quindi se msg.Length == 0, non ho alcun errore, l'icona e il messaggio spariscono. L'ErrorProvider ha diverse cose utili:

Se msg.Length > 0, ho un errore, appaiono l'icona e il tooltip.

Il click del btnCalcola invece mi piace di più così:

private void btnCalcola_Click(object sender, System.EventArgs e)
{
    
int anno = System.DateTime.Now.Year - Convert.ToInt32(txtBirthYear.Text);
    
string msg = String.Format("Hai {0} anni!!!", anno.ToString());
    MessageBox.Show(msg);
}

Ieri usavamo la concatenazione delle stringhe, oggi usiamo la classe String. Mi vengono in mente le buone "vecchie" printf del C-ANSI. Il metodo Format sostituisce {0} con anno.ToString(). Questo sarà molto utile per la gestione della localizzazione delle applicazioni: anni fa creai un'applicazione VB6 in italiano, inglese e spagnolo e la mia gestione della localizzazione fu orribile (meglio non dirvelo!). Con .NET sarà tutto molto più semplice!

Come ci tengo a dire, anche io sto studiando, perciò più correzioni avete da farmi, più son contento!

powered by IMHO 1.2

Print | posted on venerdì 8 luglio 2005 16:23 | Filed Under [ MCAD ]

Feedback

Gravatar

# re: [MCAD.8] Completare l'evento Validating con il controllo ErrorProvider ed altre chicce

Grande Igor,
prima cosa fammi dire che seguo con grandissimo interesse questa tua serie di articoli.

Poi vorrei addentrarmi con qualche idea relativamente al codice di validazione, non possiedo le tavole della legge e quindi sono pareri personali... che potrebbero anche essere sbagliati ;-)

- Il controllo dell'anno if (anno < 1880 || anno > System.DateTime.Now.Year)
lo sposterei nella try catch. In questo modo il controllo sull'anno in formato intero lo fai solo se la conversione avviene con successo.

- Quando richiami la SetError mandi una stringa che potrebbe non essere mai stata inizializzata, la cosa immagino funzioni ugualmente perchè il metodo SetError farà il controllo sulla null ma io eviterei di passare un null.

- Una finezza... il controllo sul corretto formato numerico lo effettui con un tentativo di conversione tra try and catch. Io cerco sempre di evitare le eccezioni quando posso. In questo caso userei la classe char con (vado a memoria) isNumeric su ogni carattere della stringa per verificarne la validità.

Comunque ancora complimenti e continua così.

Bye, Gabriele
08/07/2005 17:22 | Gabriele Gaggi
Gravatar

# re: [MCAD.8] Completare l'evento Validating con il controllo ErrorProvider ed altre chicce

grazie dei compimenti, Gabriele!
:-)))
ottimi comment! unica precisazione sul 2° punto. quando dichiaro...
string msg;
ho precisato che poi ho dovuto correggere in
string msg = "";
quindi quando arrivo alla SetError, msg in ogni caso contiene qualcosa: o "" oppure il messaggio di errore.

non sapevo che ci fosse un metodo isNumeric: se c'è, è l'ideale...controllo!!! :-)
08/07/2005 17:42 | Igor Damiani
Gravatar

# re: [MCAD.8] Completare l'evento Validating con il controllo ErrorProvider ed altre chicce

Ricordavo male, il metodo è IsNumber e non IsNumeric (http://msdn.microsoft.com/library/en-us/cpref/html/frlrfsystemcharclassisnumbertopic.asp)

Per quanto riguarda l'assegnazione di "" a msg ti rimando ad un interessante commento di Adrian ad un vecchio mio post:
http://blogs.ugidotnet.org/netart/archive/2005/01/05/8624.aspx#8725
08/07/2005 17:50 | Gabriele Gaggi
Gravatar

# re: [MCAD.8] Completare l'evento Validating con il controllo ErrorProvider ed altre chicce

IsNumeric esiste in Microsoft.VisualBasic (io uso VB) non so se in C# esista qualcosa del genere.
08/07/2005 17:51 | Alex
Gravatar

# re: [MCAD.8] Completare l'evento Validating con il controllo ErrorProvider ed altre chicce

Ciao, non voglio fare il pignolo di turno ;-) visto che tu sei certamente tecnicamente più preparato di me, però vorrei dirti come implementerei io il codice, premettendo che l'obbiettivo di spiegare il funzionamento degli oggetti validazione li hai spiegati in maniera superlativa.
1 - per inizializzare o fare confronti con stringhe usa «string.Empty» e non «""», in questo modo eviti di creare un oggeto visto che «.Empty» è statico.
2 - credo che il controllo sulla lunghezza sia inutile, visto che fai il confronto sui valori interi tra 1880 e l'anno corrente. (infatti se scrivi 45 o 999 viene cmq escluso).
Ps. ;-) Complimenti per gli articoli, li seguo sempre!
08/07/2005 17:54 | Marco Santoni
Gravatar

# re: [MCAD.8] Completare l'evento Validating con il controllo ErrorProvider ed altre chicce

Per Alex. No IsNumeric non esiste in C# (anche se puoi referenziare il namespace di VB.NET).
E' corretto come ha fatto Igor nell'esempio. In alternativa con .Net 2.0 si può fare int.TryParse(n) facendosi restituire true/false se la conversione è andata a buon fine.
08/07/2005 17:58 | Marco Santoni
Gravatar

# re: [MCAD.8] Completare l'evento Validating con il controllo ErrorProvider ed altre chicce

tutti questi commenti meriterebbero quasi un post dedicato ;-)
rispondo un po' a tutti.
1) a me piace testare la .Length delle stringhe, quindi piuttosto che usare msg != String.Empty, preferisco msg.Length > 0

2) il controllo .Length != 4 lo faccio perchè così escludo a priori qualsiasi inserimento. Per dire, se togliessi quell'if e scrivessi "194", entro nel try...catch, non ho errori, assegno anno, e poi uscirei dalla function perchè non rispetta dalla condizione. Invece prima di tutto, IMHO, se scrivo qualcosa con .Length != 4, esco direttamente, senza andare oltre.
08/07/2005 18:53 | Igor Damiani
Gravatar

# re: [MCAD.8] Completare l'evento Validating con il controllo ErrorProvider ed altre chicce

ho detto una sciocchezza!
se scrivo "194", la condizione
if (anno < 1880 || anno > System.DateTime.Now.Year)

è rispettata, quindi compare il msg di errore!!
chissà perchè ho detto che esce dalla funzione..mah!! la fretta...
:-D
grazie!
08/07/2005 21:01 | Igor Damiani
Gravatar

# re: [MCAD.8] Completare l'evento Validating con il controllo ErrorProvider ed altre chicce

Capita pure a me spesso ;-) non ci fare caso
08/07/2005 23:26 | Marco Santoni
Gravatar

# re: [MCAD.8] Completare l'evento Validating con il controllo ErrorProvider ed altre chicce

Innanzitutto mi complimento con te Igor, per essere uno che sta imparando sembri conoscere già molto bene le cose di cui parli, e soprattutto le spieghi benissimo. Sei un buon professore di MCAD.
Io sinceramente non conoscevo nemmeno l'esistenza dell'evento validating e tutto l'ambaradan che gli gira intorno (errorprovider incluso), quindi... grazie :)
Posto il codice 2.0 che ho messo io, partendo dalla tua base:

private void textBox1_Validating(object sender, CancelEventArgs e)
{
int anno = 0;
string msg = String.Empty;
if(!Int32.TryParse(textBox1.Text, out anno))
msg = "L'anno non è valido!";
if (anno < 1880 || anno > DateTime.Now.Year)
msg = "L'anno non è valido!";
if (msg != String.Empty)
{
errorProvider1.SetError(textBox1, msg);
e.Cancel = true;
}
}

Ho usato il TryParse per la validazione dell'inserimento e setto l'errorprovider solo in caso di effettivo errore (come fai tu a quanto ho capito viene effettuato due volte il controllo sulla lunghezza della stringa, sia da te nel tuo codice che dal controllo).

Ciao :)
04/09/2005 14:50 | Diego
Gravatar

# re: [MCAD.8] Completare l'evento Validating con il controllo ErrorProvider ed altre chicce

Riposto il codice corretto e modificato ancora un pò:
int anno = 0;
bool error = false;
string errorMessage = String.Intern("L'anno non è valido!");
error = !Int32.TryParse(textBox1.Text, out anno);
if (anno < 1880 || anno > DateTime.Now.Year) error = true;
if (error)
{
errorProvider1.SetError(textBox1, errorMessage);
e.Cancel = true;
}
else errorProvider1.SetError(textBox1, String.Empty);

A parte l'internizzazione della stringa di errore che è una stupidaggine fatta solo per provare a vedere se funziona (!!!), la stupidaggine l'ho messa io prima dicendo che il tuo codice faceva due controlli sulla stringa, nel senso che è NECESSARIO passare comunque una stringa vuota all'error provider per ripristinare lo stato di non errore!
04/09/2005 14:58 | Diego
Gravatar

# [70-526, #6] Carrellata: ToolTip, ProgressBar ed HelpProvider

15/01/2007 13:14 | Technology Experience
Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET