Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Data Bound Controls personalizzati con ASP.NET 2.0

Creare controlli personalizzati collegabili a diversi datasource
Creare controlli personalizzati collegabili a diversi datasource
Link copiato negli appunti

I controlli Data-Bound sono preziosi elementi della grande famiglia dei controlli lato server di ASP.NET, sin dalla prima versione del framework. A differenza degli altri controlli, offrono una funzionalità a dir poco fondamentale: il collegamento ad una fonte di dati, quindi la gestione del legame tra lo strato contenente le informazioni e lo strato di presentazione del sito.

Attraverso questi controlli infatti, siamo in grado di rendere dinamica la nostra applicazione, caratteristica ormai fondamentale, seguendo una logica affine alla programmazione ad oggetti (Modello MVC).

È abbastanza comune l'uso di controlli come Repeater, DataList, DataGrid o GridView; questi sono tutti Data-Bound Controls, controlli che permettono di rappresentare dati prelevati da una qualsiasi fonte (DB, XML, etc.) con funzioni e modelli predefiniti o personalizzati, grazie a template di visualizzazione.

Possiamo classificare i controlli Data-Bound in 3 tipi:

  • Controlli di tipo lista - Sono quei controlli che presentano i dati in una struttura grafica predefinita, con cui l'utente finale non ha alcuna possibilità di interazione (e quindi di modifica visiva). Per capirci meglio, sono dei controlli di tipo lista i controlli RadioButtonList, CheckBoxList e BulletedList.
  • Controlli data-bound semplici - Sono tutti quei controlli che danno la possibilità allo sviluppatore di effettuare il databind su una o più delle loro proprietà. Il semplice controllo Label, ad esempio, permette di effettuare il binding di dati sulla sua proprietà Text. Allo stesso modo si comportano, tutti i controlli con delle proprietà decorate con l'attributo Bindable impostato su true.
  • Controlli data-bound complessi - Sono quei controlli che offrono funzionalità di rendering dei dati avanzate, senza alcun limite sul numero dei dati prelevati o sul relativo schema. Questi sono dei controlli compositi con aggiunte le funzionalità di binding dei dati. I più famosi sono GridView, DetailsView , FormView (i 3 nuovi controlli aggiunti con la versione 2.0 di ASP.NET), DataGrid, DataList e Repeater.

L'utilizzo così diffuso di Data-Bound Controls nasce da concrete necessità di controlli avanzati per semplificare il lavoro del team di sviluppo.

La possibilità di estensione, propria del framework, si applica anche a questa famiglia di controlli, perciò abbiamo la possibilità di sviluppare controlli custom per soddisfare le più svariate esigenze.

Prima di passare alla parte pratica, esaminiamo in dettaglio come funziona il meccanismo di binding dei dati in ASP.NET 2.

Anzitutto bisogna specificare che esistono due tipi di data-bind, il primo viene effettuato attraverso l'utilizzo della proprietà DataSource:

GridView1.DataSource = dataset;
GridView1.DataBind();

tale meccanismo, controlla che la fonte di dati implementi l'interfaccia IEnumerable (propria delle collezione di oggetti) o IListSource (che sarebbe l'interfaccia implementata dalle classi DataSet e DataTable) e necessita della chiamata manuale al metodo DataBind() del controllo.

Il secondo modo è invece quello di valorizzare la proprietà DataSourceID con l'ID di un controllo di tipo DataSource (per una panoramica su tali controlli, rimandiamo a questo articolo).

<asp:GridView ID="GridView1" DataSourceID="ds" runat="server">
</asp:GridView>

In questo modo, è il template engine di ASP.NET che si preoccupa di collegare la fonte di dati prelevata dal controllo DataSource all'omonima proprietà del controllo e di chiamare il metodo DataBind(), il tutto senza scrivere alcuna riga di codice all'interno del file di code-behind.

Se scegliamo uno dei due metodi appena descritti, siamo impossibilitati ad utilizzare l'altro, poiché le proprietà DataSource e DataSourceID non possono convivere assieme allo stesso tempo.

Creare un controllo Data Bound

Le proprietà DataSource e DataSourceID, sono definite all'interno della classe BaseDataBoundControl, che si occupa di ridefinire il metodo DataBind() ereditato dalla classe Control e di controllare che il binding dei dati venga effettuato correttamente anche a fronte di eventuali PostBack della pagina.

Se decidessimo però di ereditare da questa classe per lo sviluppo del nostro controllo Data-Bound, ci dovremmo mettere a carico lo sviluppo dei meccanismi di validazione della fonte di dati (ridefinendo il metodo ValidateDataSource) e di esecuzione dell'operazione di selezione delle informazioni (ridefinendo il metodo PerformSelect).

Come al solito, ASP.NET ci viene in aiuto con la definizione di una seconda classe posta ad un livello logico più alto, la classe DataBoundControl, che ha già implementati i metodi appena descritti. Ereditando da tale classe infatti, è solamente necessario:

  • definire le proprietà di mappatura dei campi da selezionare,
  • implementare il metodo PerformDataBinding(), per elaborare i dati restituiti dall'operazione di selezione,
  • implementare il metodo Render(), per fornire la presentazione scelta dei dati.

Nell'esempio che proponiamo, realizziamo un controllo Data-Bound in grado di visualizzare un grafico a barre orizzontali; come al solito l'esempio deve essere visto come un punto di partenza per sviluppi futuri.

Per prima cosa dobbiamo ereditare dalla classe DataBoundControl:

public class ChartControl : DataBoundControl

Tipicamente possiamo pensare al datasource in ingresso come a una tabella (il risultato di una query) con diversi campi. Avremo bisogno quindi, di sapere quale dei campi è il campo numerico da rappresentare con il grafico. Inoltre possiamo prevedere che a ciascun numero sia associata un'etichetta. Lasciamo che l'utente del controllo imposti questi due parametri fornendogli due proprietà: DataTextField e DataValueField.

public BarCollection Bars
{
  get {
    if (bars == null) bars = new BarCollection();
    return bars;
  }
}

public string DataTextField
{
  get {
    object o = ViewState["DataTextField"];
    return (o == null) ? String.Empty : (string)o;
  }
  set { ViewState["DataTextField"] = value; }
}

public string DataValueField
{
  get {
    object o = ViewState["DataValueField"];
    return (o == null) ? String.Empty : (string)o;
  }
  set { ViewState["DataValueField"] = value; }
}

La proprietà Bars ci permette, secondo una struttura ben definita (data dalle classi di supporto Bar e BarCollection, di cui abbiamo omesso l'implementazione, presente nell'esempio da scaricare), di salvare la collezione di righe restituite dall'operazione di selezione delle informazioni.

Dobbiamo ora implementare il metodo PerformDataBinding e popolare le strutture dati appena definite leggendo i vari campi dal DataSource legato.

protected override void PerformDataBinding(System.Collections.IEnumerable data)
{
  if (DataTextField == null)
    throw new ArgumentNullException("DataTextField", "La proprietà DataTextField è necessaria");
    
  if (DataValueField == null)
    throw new ArgumentException("DataValueField", "La proprietà DataValueField è necessaria");

  if (data != null)
  {
    foreach (object item in data)
    {
      Bar b = new Bar();
      b.Text = DataBinder.GetPropertyValue(item, DataTextField, null);
      int result = 0;
      Int32.TryParse(DataBinder.GetPropertyValue(item, DataValueField, null), out result);
      b.Value = result;

      Bars.Add(b);
    }
  }
}

Per prelevare il singolo campo da ogni singola riga abbiamo utilizzato il metodo GetPropertyValue della classe DataBinder e gli abbiamo passato la singola riga (item) e il nome del campo scelto (prelevato dalle proprietà DataTextField e DataValueField).

Fatto questo, abbiamo popolato le nostre strutture dati (la collezione Bars) e siamo pronti per presentarle a video. La fase di rappresentazione delle informazioni è chiaramente legata alla definizione del metodo Render del controllo. Tale operazione risulta possibile in quanto, nel ciclo di vita di un controllo Data-Bound, il metodo PerformDataBinding viene richiamato sempre prima del metodo Render.

All'interno del metodo per la rappresentazione, scorriamo la collezione e che abbiamo a disposizione e avvalendoci del markup HTML, realizziamo la grafica prescelta.

protected override void Render(HtmlTextWriter writer)
{
  if (Bars.Count <= 0) return;

  writer.RenderBeginTag(HtmlTextWriterTag.Table); // apro table

  foreach (Bar b in Bars)
  {
    // calcolo il valore in scala
    int valoreInScala = ((int)Width.Value * b.Value) / 100;

    writer.RenderBeginTag(HtmlTextWriterTag.Tr); // apro tr

    writer.RenderBeginTag(HtmlTextWriterTag.Td); // apro td barra

    writer.AddAttribute(HtmlTextWriterAttribute.Style,
        String.Format("width: {0}px; background-color: {1}; " +
        "float: left; margin-right: 5px",
        valoreInScala,
        ColorTranslator.ToHtml(BarColor)), false);

    writer.RenderBeginTag(HtmlTextWriterTag.Div); //apro div barra
    writer.RenderEndTag(); //chiudo div

    writer.RenderBeginTag(HtmlTextWriterTag.Div); //apro div testo
    writer.Write(b.Text);
    writer.RenderEndTag(); //chiudo div
    writer.RenderEndTag(); //chiudo td

    writer.RenderEndTag(); //chiudo tr
  }

  writer.RenderEndTag(); //chiudo table
}

A questo punto il nostro controllo Data-Bound è pronto per essere utilizzato a pieno regime all'interno di un'applicazione ASP.NET. Come sempre possiamo decidere se salvare la classe all'interno della directory App_Code dell'applicazione o se inserirla all'interno di un'assembly .NET specifico, in ogni caso dobbiamo ricordarci di registrare il controllo: all'interno del web.config o direttamente nella pagina che lo contiene.

Possiamo collegare il nostro controllo Data-Bound ad un controllo di tipo DataSource:

<peppe:ChartControl ID="ChartControl1" runat="server"
      DataSourceID="AccessDataSource1"
      DataTextField="Squadra" DataValueField="g22" />

<asp:AccessDataSource ID="AccessDataSource1" runat="server"
      DataFile="~/App_Data/classifica.mdb"
      SelectCommand="SELECT * FROM [Classifica]" />

Oppure, possiamo scrivere il codice per la connessione alla fonte di dati a mano ed attaccarla al nostro grafico a barre attraverso la proprietà DataSource e la chiamata al metodo DataBind().

<peppe:ChartControl ID="ChartControl2" runat="server"
      DataValueField="g22" DataTextField="Squadra"/>

protected void Page_Load(object sender, EventArgs e)
{
  string strConn = @"...";
  string sql = @"SELECT * FROM [Classifica]";
  OleDbConnection conn = new OleDbConnection(strConn);
  using (conn)
  {
    OleDbCommand cmd = new OleDbCommand(sql, conn);
    using (cmd)
    {
      OleDbDataAdapter adapter = new OleDbDataAdapter(cmd);
      DataTable dt = new DataTable();
      adapter.Fill(dt);

      ChartControl2.DataSource = dt;
      ChartControl2.DataBind();
    }
  }
}

In entrambi i casi, il risultato è il medesimo e, nonostante la semplicità, l'impatto visivo è già gradevole.

Figura 1. Risultato del rendering
Risultato del rendering

Conclusioni

Il data-binding è fondamentale nello sviluppo di applicazioni Web con ASP.NET 2.0: avere a disposizione oggetti predefiniti in grado di rappresentare informazioni da database, XML o oggetti di business è fondamentale. Con i Data-Bound controls, siamo in grado di progettare oggetti riusabili ed adattabili alle esigenze grafiche dell'applicazione indipendentemente da quale sia lo schema delle informazioni che abbiamo intenzione di rappresentare.

Giuseppe Marchi è consulente informatico in ambito Microsoft .NET e dottore in Comunicazione Digitale; co-autore del libro "Pocket C#", editore Apogeo, collabora con community on-line di sviluppatori fornendo articoli e materiale. Dal 2006 è certificato Microsoft su ASP.NET 2.0 e Microsoft Certified Technology Specialist su Windows Sharepoint Services 3.0. Il suo sito Web personale www.peppedotnet.it contiene ulteriori informazioni ed esempi di codice.

Ti consigliamo anche