PDA

View Full Version : [C#/VS2005]Validazione input utente


RaouL_BennetH
26-05-2009, 12:45
Ciao a tutti :)

Fino ad ora, nei miei piccoli progetti winforms, gestivo la validazione dell'input da parte dell'utente gestendo gli eventi per ogni singolo controllo presente sul form, per esempio per controllare che una textbox non venisse lasciata vuota o accettasse solo valori numerici et similia..

Adesso invece vorrei capire come fare per creare una classe apposita che possa gestirmi queste cose indipendentemente dal tipo di oggetto, soprattutto in considerazione del fatto che normalmente, un'applicazione che sia già poco più di un esempio, può contenere decine di forms.

L'ideuzza che ho io sarebbe in pratica:

Input utente -> Validazione

Validazione = ok -> passa i dati sul db x esempio
Validazione = no -> evidenzia il controllo che richiede un input corretto.


Sapreste mettermi sulla buona strada?

grazie mille :)

RaouL.

gugoXX
26-05-2009, 13:16
La strategia che considero migliore e' quella di "impedire" il piu' possibile a priori all'utente di digitare qualcosa di sbagliato.
Per ASP.net e per WPF ci sono dei template anche estendibili per scrivere in fase dichiarativa cosa conterra' l'elemento di input.

Se per esempio nel WebForm di una pagina ASP.net si istanzia una TextBox e si vuole che contenga solo formule, allora potrai usare il filtro dichiarativo per imporre tale comportamento.

<asp:TextBox runat="server" ID="TextBox3" />
<ajaxToolkit:FilteredTextBoxExtender ID="ftbe" runat="server"
TargetControlID="TextBox3"
FilterType="Custom, Numbers"
ValidChars="+-=/*()." />

Analogamente e anche piu' facilmente con WPF, dove il criterio di validazione e' customizzabile in modo decisamente semplice.

Relativamente a WinForm mi ricordo di avere usato una libreria che permetteva un comportamento simile.

Talvolta la fase di validazione dovra comunque essere fatta. Ma direi piu' leggera.
In Winform puoi dichiarare per una form/controllo quello che e' il pulsante di Conferma.
Al premere del pulsante il Framework scatenera' l'evento
Validating
su ciascuno dei componenti visuali della form/controllo
(Quindi tutte le textbox, etc.)

Potrai quindi sottoscriverai questo evento su qualcuno di questi componenti, e il corpo del metodo potra' elevare il fallimento della validazione, cosa che elevera' una Eccezione invece di chiamare il codice del metodo del pulsante di Conferma.

Cosa fare con l'eccezione sta poi a te, come per esempio visualizzare una dialog di errore.

Per quanto riguarda la strategia da adottare per descrivere il codice di validazione dipende dalla natura delle tue validazioni.
Se sono tante e sono tutte simili/uguali ti consiglierei di usare un subclassing dei controlli standard, ovvero di creare dei tuoi controlli con quel paio di validazioni che andrai a pilotare in fase dichiarativa e che potrai estendere con il passare del tempo quando dovessi trovare l'esigenza di nuove validazioni non ancora coperte.
Se invece i controlli da validare sono pochi e le validazioni sono tutte diverse e magari cambiano anche a runtime, ti consiglieri di dare un'occhiata al Decorator Pattern.

RaouL_BennetH
26-05-2009, 14:31
Ciao gugoXX :)

In generale io faccio così:

Parto anche io dal presupposto di cercare di impedire a priori l'inserimento di input non valido anzichè controllarlo successivamente.

per fare questo, solitamente uso l'evento KeyPressEventArgs in modo tale che se l'utente si trova su una casella che accetti solo numeri, non possa neanche per sbaglio inserire una lettera (e quindi se ad esempio ho 20 textbox devo gestire 20 eventi KeyPressEventArgs).

Quello che a me non piace però, è dover gestire appunto un evento per ogni singolo controllo.

Pensavo di generalizzare la validazione almeno per due aspetti:

1) Non lasciare un campo obbligatorio vuoto
2) Avere il focus sull'oggetto dove c'è l'eccezione

Stavo ragionando in questi termini:



public class ProvaValidazione
{

private const string emptyFieldMessage = "CAMPO OBBLIGATORIO";

public static bool IsValid(TextBox t)
{
if(t.Text.Length != 0)
{
SomeErrorProvider.Clear();
return true;
}
else
{
SomeErrorProvider.SetError(t, emptyFieldMessage);
return false;
}
}
}



Logicamente però... fa schifo :(

Infatti se devo validare i dati alla pressione del tasto conferma, se per esempio mi trovassi ad avere 20 textbox, dovrei fare:



(if(IsValid(txtCognome) && IsValid(txtNome) && blablabla...



ho vergogna di me stesso.....

banryu79
26-05-2009, 14:49
Se i 20 componenti fossero raccolti in una collezione, basterebbe codificare il controllo di validazione in un ciclo.

RaouL_BennetH
26-05-2009, 15:20
ciao banryu79 :)

Mmm...

alla fine, diciamo che devo controllare che la lunghezza di alcune stringhe non sia pari a zero.

usando una collezione, potrei fare:



List<string> lista = new List<string>();

lista.Add(txtCognome.Text);
lista.Add(txtNome.Text);
lista.Add(comboBoxQualcosa.Text);

...

foreach(string s in lista)
{
if(s.Length == 0)
{

blabla
}
else
{
....
}
}



?


....

però così poi non potrei capire su quale controllo mettere il focus perchè è vuoto.

RaouL_BennetH
26-05-2009, 15:39
Forse ho trovato:



List<Control> controlList = new List<Control>();

controlList.Add(txtCognome);
controlList.Add(comboBoxQualcosa);
controlList.Add(txtNome);

for(int i = 0; i < controlList.Count; ++i)
{
if(controlList[i].Text.Length == 0)
{
SomeErrorProvider.SetError(controlList[i], "errore");
}
}

RaouL_BennetH
26-05-2009, 16:59
mmm.. ho notato una cosa però...

mi basta che uno solo dei campi sia vero ed il controllo si blocca.

Cioè, se sono tutti vuoti, me lo segnala correttamente... se invece ne ho solo uno vuoto, non me lo segnala..

banryu79
27-05-2009, 12:46
mmm.. ho notato una cosa però...

mi basta che uno solo dei campi sia vero ed il controllo si blocca.

Cioè, se sono tutti vuoti, me lo segnala correttamente... se invece ne ho solo uno vuoto, non me lo segnala..
Non è che dipende da come funziona la classe SomeErrorProvider?
Nel codice che hai postato in precedenza si vede che nel caso un controllo sia valido, chiami il metodo Clear; magari dipende da questo.

MarcoGG
28-05-2009, 14:13
Forse ho trovato:



List<Control> controlList = new List<Control>();

controlList.Add(txtCognome);
controlList.Add(comboBoxQualcosa);
controlList.Add(txtNome);

for(int i = 0; i < controlList.Count; ++i)
{
if(controlList[i].Text.Length == 0)
{
SomeErrorProvider.SetError(controlList[i], "errore");
}
}



Scusa, ma non mi è chiaro lo scopo...
Perchè creare una List ? Non è più semplice un foreach a livello di container ?
Se vuoi restringere i controlli sull'input utente solo ad alcuni controls specifici ci sono anche altre vie, come ad esempio l'uso dei Tag...

RaouL_BennetH
29-05-2009, 12:20
Scusa, ma non mi è chiaro lo scopo...
Perchè creare una List ? Non è più semplice un foreach a livello di container ?
Se vuoi restringere i controlli sull'input utente solo ad alcuni controls specifici ci sono anche altre vie, come ad esempio l'uso dei Tag...

Ciao :)

Ho pensato ad una lista perchè ho fatto questa considerazione:

In un container potrei avere anche controlli sui quali non devo verificarne il contenuto, perchè magari non sono campi obbligatori. Con una lista a disposizione invece posso decidere appunto quali devono essere verificati.

Di conseguenza, se usassi un foreach sul container, inevitabilmente mi fa il check 'per ogni' controllo presente.

Oltre a questo, dovrei anche andare a specificare cose del tipo:

"se il controllo è un textbox"
"se il controllo è un combobox"

Credo di aver fatto un altro piccolo passo in avanti (spero);

Sia ben chiaro che sono ben accette tutte le critiche e i suggerimenti :)



public class ValidatoreElementare()
{

public static bool CampiObbligatoriOk(List<Control> controlList, ErrorProvider helper)
{
bool fieldsOk = true;
helper.Clear();
for (int i = 0; i < controlList.Count; ++i)
{
controlList[i].BackColor = Color.White;
if (controlList[i].Text.Length == 0)
{
helper.SetError(controlList[i], "CAMPO OBBLIGATORIO");
controlList[i].BackColor = Color.LightYellow;
fieldsOk = false;
}
}
return fieldsOk;
}

//da un form

List<Control> ls = new List<Control>();
ls.Add(txtCognome);
ls.Add(txtNome);
ls.Add(cmbProfessione);

return ValidatoreElementare.CampiObbligatoriOk(ls, formErrorProvider);

MarcoGG
29-05-2009, 12:50
Ciao :)

Ho pensato ad una lista perchè ho fatto questa considerazione:

In un container potrei avere anche controlli sui quali non devo verificarne il contenuto, perchè magari non sono campi obbligatori. Con una lista a disposizione invece posso decidere appunto quali devono essere verificati.

Di conseguenza, se usassi un foreach sul container, inevitabilmente mi fa il check 'per ogni' controllo presente.

Oltre a questo, dovrei anche andare a specificare cose del tipo:

"se il controllo è un textbox"
"se il controllo è un combobox"



No, puoi discriminare i controls su cui eseguire il check NotNull da quelli che possono anche essere Null, ad esempio utilizzando la Proprietà Tag dei controlli stessi, senza scomodare List aggiuntive e quant'altro, anche perchè una "List" ce l'hai già, ossia il container Form, inoltre con Control.GetType() sapere che tipo di controllo è.
Esempio : Ho vari controlli, ma solo in quelli che prevedo debbano essere Not Null vado a scrivere nella proprietà Tag = NotNull.

foreach (Control C in this.Controls) {
if ((string)C.Tag == "NotNull") {
if (C.Text == "") {
C.BackColor = Color.Red;
MessageBox.Show(C.GetType() + " - " + C.Name + " Non può essere vuoto !");
//... ecc ...
//... ecc ...
}
}
}

Cioè, secondo me è molto più agile come soluzione... ;)

RaouL_BennetH
29-05-2009, 13:00
Capito :)

Ragionerò anche sul tuo suggerimento.

Per il momento grazie :)