Ad essere onesto, per quanto sia stato per gli scorsi anni un utente passivo del .net framework, non significa che non ci ho fatto nulla.
Ci ho giocato, poco, ma l'ho fatto. E una delle cose che ho provato è stata quella dei validator. Beh che dire. La versione 1.1 sicuramente è stata deludente, nel senso che Microsoft non si è smentita, ed è quindi andata sull'onda della sua filosofia "il mondo non mi basta" (The World is not enough) che non c'entra un fico secco con lo 007, ma il concetto è proprio quello, ovvero fare tutto come se gli altri non esistono.
Tant'è che i validator su browser alternativi di tutto rispetto (che sul mio pc regnano sovrani come browser di default) come Firefox e compagni ovviamente avevano dei problemi di comprensione legati ovviamente al fatto che la sintassi JavaScript adottata dai validator era tutta "IEizzata".
Tentativi tipo modificare il browsercap.ini piuttosto che la WebUIValidation.js ... vi ometto i risultati, con la logica conseguenza che fare ancora tutto con il vecchio sistema javascript client-side per tutti era sicuramente la via più spicciativa per ottenere qualcosa.
Fortunatamente le cose son cambiate. Quando andai ad una delle preview del 2.0, Daniele (Bochicchio), illustrò le novità e la retrocompatibilità con altri browser sicuramente era un punto a favore del nuovo framework.
All'epoca della preview, ovviamente, era ancora in beta e certo mi sarei aspettato un pò più di impegno da parte del team di Microsoft per implementare qualche custom control in più.
Beh non l'hanno fatto. Forse avevano altre priorità. Come biasimarli.
Insomma, morale della favola, fare una validazione sul checkbox, è praticamente impossibile, non per lo meno senza scrivere del codice. Ed è quello che ho fatto io per ottenere uno stupid CheckBoxValidator.
Certo il controllo non è che sia commercialmente valido, nel senso che fa lo stretto indispensabile, e non può certo essere paragonato a tool belli e pronti all'acquisto tipo quello di Peter Blum, però per quello che occorreva a me.
Da dove iniziamo. Dalla classe BaseValidator innazitutto. Implementando la classe, si ha praticamente una struttura di un validatore pronta all'uso. Chiaramente così come è non farebbe nulla. Dobbiamo quindi implementare la nostra logica di validazione.
Il tutto si riconduce essenzialmente a due override: il primo per il metodo EvaluateIsValid, che permette di determinare quando il valore del controllo oggetto della verifica è valido, il secondo è il metodo ControlPropertiesValid che consente di stabilire se il controllo associato al validatore è del tipo corretto.
Quindi nel mio caso, ecco un pò di codice:
public class CheckBoxValidator : BaseValidator
{
private CheckBox _checkbox;
public CheckBoxValidator()
{
}
protected override bool ControlPropertiesValid()
{
Control ctrl = FindControl(ControlToValidate);
if (ctrl != null)
{
_checkbox = (CheckBox)ctrl;
return (_checkbox != null);
}
else
return false; // raise exception }
protected override bool EvaluateIsValid()
{
return _checkbox.Checked == true;
}
}
Ovviamente, messo così, il validatore non ha alcuna funzionalità di cliend-side. Bisogna quindi dirgli:
1) Guarda che voglio abilitare la verifica lato client;
2) Guarda che quando l'utente fa il submit devi lanciare la mia funzione di validazione;
3) Ultimo, ma non ultimo, iniettare il codice javascript che si preoccupa della validazione.
Nella sintassi:
base.EnableClientScript = true;
Questa riga qui sopra la mettiamo nel costruttore. Il puntatore base, che si riferisce appunto alla classe implementata BaseValidator, andrà a modificare la proprietà per far scrivere durante il rendering in HTML tutta quella serie di elementi necessari per far funzionare la validazione lato client-side. Un esempio aggiungere il Control-ID all'array Javascript di funzioni da richiamare e via dicendo.
Questo codice qui sotto, semplicemente lo aggiungiamo al resto:
protected override void AddAttributesToRender(HtmlTextWriter writer)
{
base.AddAttributesToRender(writer);
if (this.RenderUplevel)
{
// Add the custom property that will be rendered
// in the javascript code to start the client-side validation;
Page.ClientScript.RegisterExpandoAttribute(this.ClientID, "evaluationfunction", "IsCheckBoxRequired");
}
}
Stiamo in sostanza aggiungendo, qualora il browser utilizzato sia capace di utilizzare la cosa (RenderUplevel), una proprietà per il nostro futuro controllo HTML. Per nostra fortuna tutto il lavoro "sporco" lo fa il framework e ci penserà lui ad inserirlo nell'array di funzioni/metodi javascript che saranno utilizzate dal controllo HTML che non verrà toccato (sporcato) con tag non conformi al W3C (questa è veramente una gran bella cosa).
Questa proprietà è la "evaluationfunction" che avrà come valore il nome della funzione Javascript da richiamare, nel mio caso IsCheckBoxRequired.
Non mettere questa proprietà equivale a non avere una validazione lato client.
Per tutti coloro che vengono dal framework 1.1, sappiate che la writer.AddProperties non funziona. Ho provato a cercare di capire il perchè guardando cosa succede con il reflector, ma onestamente mi sono perso. Quindi prendo anche io per buono che bisogna fare così e stop.
Ultimo step, il codice javascript. Qua non mi sono inventato nulla di eccezionalmente complesso. Se il controllo è spuntato, allora ritorna true, altrimenti ritorna false.
Che tradotto in Javascript "mimetizzato" dentro al C# significa questo codice:
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
if (RenderUplevel)
{
string script = "\r\n<script language=\"javascript\">\r\n" +
" function IsCheckBoxRequired(val){\r\n" +
" if (val.controltovalidate != \"\") {\r\n" +
" if (document.getElementById(val.controltovalidate).checked == true) {\r\n" +
" return true;\r\n" +
" } else {\r\n" +
" return false;\r\n" +
" }\r\n" +
" } else {\r\n" +
" return false;\r\n" +
" }\r\n" +
" }\r\n" +
"</script>\r\n";
if (!Page.ClientScript.IsClientScriptBlockRegistered("IsCheckBoxRequired"))
{
Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "IsCheckBoxRequired", script);
}
}
}
Ovviamente il tutto l'ho messo dentro l'evento OnPreRender che viene scatenato non appena il controllo è stato caricato, ma prima che qualsiasi parte HTML sia stata inviata al nostro client (altrimenti non avremmo modo di moficare lo stream da passare al browser).
Mi sincero ovviamente di chiamare il metodo base OnPreRender, altrimenti tutte le altre cosette nascoste (necessarie) che fa il BaseValidator, verrebbero ovviamente saltate.
E il gioco è praticamente fatto.
Mettete tutto dentro una vostra classe, compilate e refenziate, quindi semplicemente dentro la pagina aspx fate una cosa di questo tipo.
CheckBoxValidator cbv = new CheckBoxValidator();
cbv.ID = "CBV" + ID;
cbv.ControlToValidate = ControlToValidate;
cbv.Text = " *";
Certo, mancano tutti gli attributi per trasformare la classe in un custom control da poter utilizzare dentro al Visual Studio, ma questa è un'altra storia che forse racconterò in un altro articolo.