Databinding e templating con ASP.NET

di Cristian Civera, in ASP.NET,

DataBinding e Templating sono due funzionalità importanti in ASP.NET ed anche se questi termini sono a voi sconosciuti vi accorgerete come in realtà li avete sfruttati già molte volte.
In questo articolo non vedremo come usarli (si presuppone lo sappiate già), ma come questi meccanismi vengano implementati dietro le quinte, per poterli sfruttare nei migliori dei modi e per avere le conoscenze necessarie al fine di creare controlli custom molto potenti.

Il databinding

Per DataBinding si intende la possibilità di poter caricare delle informazioni in funzione di una sorgente dati proveniente da database, file o quant'altro. L'idea proviene già dalla programmazione WinForm con Visual Basic, volta a rendere più facile e immediato il popolamento di un controllo, data una sorgente. Questo approccio è stato portato anche in applicazioni ASP.NET poiché anche qua, tutto è stato organizzato in controlli, ognuno dei quali avente un compito specifico.

Ciascuno di questi eredita da Control e dispone quindi dei seguenti membri relativi al databinding:

  • DataBind : metodo da implementare per caricare le informazioni di un controllo in funzione di una eventuale sorgente;
  • DataBinding : evento alla quale è possibile abbonarsi per intercettare l'operazione di binding;
  • DataSource: proprietà definita solo in alcuni controlli (BaseDataList, ListControl, HtmlSelect, Repeater) per impostare la sorgente dati.

In ASP.NET è stato introdotto un nuovo costrutto <%# %> che permette di caricare informazioni in fase di binding,  cioè quando viene richiamato il metodo DataBind del controllo che lo contiene.

Proviamo l'esempio più semplice di binding. In una pagina ASP.NET scriviamo:

Questo è un <%#"Test"%>

Se lanciamo la pagina non vedremo la parola Test, ma se nel Page_Load (o comunque prima del rendering) chiamano il metodo Page.DataBind() vedremo restituita la stringa inserita tra virgolette.

Ora andiamo ad aprire il codice della pagina parserizzata che troviamo in %SystemRoot%\Microsoft.Net\Framework\(versione)\ Temporary ASP.NET Files\(virtual directory e guardiamo il codice prodotto (quello significativo):

// Ricevo __ctrl che come controllo contenitore base
// in questo caso, la pagina
private void __BuildControlTree(System.Web.UI.Control __ctrl) {
  // Costruisco l'unico controllo nella pagina
  this.__BuildControl__control2();
  
  // Aggiungo il controllo alla collezione Controls della pagina
  System.Web.UI.IParserAccessor __parser = ((System.Web.UI.IParserAccessor)(__ctrl));
  __parser.AddParsedSubObject(this.__control2);
}
 
private System.Web.UI.Control __BuildControl__control2() {
  // Creo un DataBoundLiteralControl indicando
  // il numero di stringe statiche e il numero di quelle dinamiche (1, 1)
  this.__control2 = new System.Web.UI.DataBoundLiteralControl(1, 1);
  // Imposto la stringa fissa
  __ctrl.SetStaticString(0, "\r\n\r\nQuesto è un ");
  // Intercetto l'evento di binding con un mio delegate
  this.__control2.DataBinding += new System.EventHandler(this.__DataBind__control2);
  return this.__control2;
}
 
public void __DataBind__control2(object sender, System.EventArgs e) {
  // Recupero il controllo che è in fase di binding
  System.Web.UI.DataBoundLiteralControl target = ((System.Web.UI.DataBoundLiteralControl)(sender));
  // Recupero il controllo contenitore: la pagina
  System.Web.UI.Control Container = ((System.Web.UI.Control)(target.BindingContainer));
 
  // Imposto la stringa dinamica
  target.SetDataBoundString(0, System.Convert.ToString("ciao"));
}

Ho modificato e commentato un po' il codice per renderlo più leggibile. In una pagina ASP.NET ogni cosa, perfino del testo, è un controllo perciò il parser ne crea uno, in questo caso di tipo DataBoundLiteralControl, per rappresentare testo statico e dinamico. La differenza in un normale Literal sta nel fatto che concatena in fase di rendering le varie stringhe.

Leggendo il codice dall'alto in basso, potrete vedere che una volta creato, viene impostato da subito il testo fisso "Questo è un ", mentre viene intercettato l'evento DataBinding per impostare in un successivo momento il testo dinamico.
Il compito è delegato al metodo __DataBind__control2. Il parser non farà altro che inserire il contenuto del tag <%# %> nella Convert.ToString.

Il numero dei costrutti di cui viene fatto il binding può essere anche maggiore di uno. Se non vi sono controlli intermedi in tutto il contenitore (in questo caso la pagina) il parser chiamerà SetDataBoundString con indici progressivi quanti sono i tag.
Questa chiamata e la Convert.ToString sono in funzione del target. Se avessimo scritto questo:

<asp:Repeater runat="server" DataSource='<%#mioDataSet%>'></asp:Repeater>

Il codice prodotto dal parser per la fase di binding sarebbe stato:

System.Web.UI.WebControls.Repeater target = ((System.Web.UI.WebControls.Repeater)(sender));
System.Web.UI.Control Container = ((System.Web.UI.Control)(target.BindingContainer));
target.DataSource = ((object)(mioDataSet));

Questo è il codice più generico che potrebbe produrre il parser, poiché ogni attributo di un tag server control è una proprietà di un controllo.

L'ultimo aspetto da considerare è la variabile Container dichiarata e mai usata. La proprietà BindingContainer della classe Control restituisce il controllo padre, o meglio, contenitore e depositario di tutti gli ID.

Ritornando alla variabile, sicuramente ha un nome conosciuto: l'abbiamo usata molteplici volte in controlli come Repeater, DataList, DataGrid.

Visto che il parser copia esattamente quanto inserito tra tag <%# %>, ci è stata data la possibilità di accedere al controllo contenitore in modo semplice dichiarando questo membro.

Per capire meglio, facciamo un altro esempio:

<asp:Repeater runat="server">
  <ItemTemplate>
      <%#DataBinder.Eval(Container.DataItem, "campo")%>
  </ItemTemplate>
</asp:Repeater> 

Il codice prodotto dal parser sarà:

public void __DataBind__control5(object sender, System.EventArgs e) {
  System.Web.UI.WebControls.RepeaterItem Container = ((System.Web.UI.WebControls.RepeaterItem)(target.BindingContainer));;
  System.Web.UI.DataBoundLiteralControl target = ((System.Web.UI.DataBoundLiteralControl)(sender));
 
  target.SetDataBoundString(0, System.Convert.ToString(DataBinder.Eval(Container.DataItem, "campo")));
}

Ecco svelato il trucchetto: il contenuto del template è una stringa dinamica, perciò è stato usato un DataBoundLiteralControl. Inoltre Container sarà un riferimento alla riga corrente ed il tag copiato potrà essere inserito tra la Convert.ToString, così che il codice risultante possa essere compilabile.

2 pagine in totale: 1 2
Contenuti dell'articolo

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Approfondimenti