Il controllo datagrid è certamente uno dei più utilizzati, ma, senza opportune tecniche, risulta difficile usarlo per visualizzare dati provenienti da tabelle distinte. In un altro
articolo, è stata presa in esame la possibilità di creare una vista doppia su più tabelle usando le proprietà intrinseche dell?oggetto dataset. In questa sede, si descrive un?altra tecnica che sfrutta un controllo freeware, creato da Denis Bauer, uno sviluppatore tedesco che ringrazio per la disponibilità dimostrata, e disponibile (con documentazione in lingua inglese) all'url:
http://www.denisbauer.com/ASPNETControls.aspx ">Denis Bauer.
Il controllo, che eredita la maggior parte delle sue proprietà e dei suoi metodi dal datagrid del .NET Framework, utilizza un Dataset in cui siano state, ovviamente, create più tabelle con relative relazioni: durante la fase di caricamento dei dati della tabella principale, il controllo cerca nelle tabelle relazionate eventuali righe "figlie" e, se ne trova, le carica dinamicamente in un modello; questo non viene però direttamente visualizzato, ma nascosto all?interno di una colonna nascosta che visualizza soltanto, tramite immagini, la presenza di dati. Ciccando su tale colonna, viene attivata, attraverso JavaScript, la copia dei dati nascosti in una nuova riga del grid.
Nell'esempio allegato, il controllo verrà usato per visualizzare dati di tre distinte tabelle, come nella figura sopra, ma, com?è ovvio non c'è limite al grado di gerarchizzazione.
Prima di usare il controllo per visualizzare i dati, è necessario creare la base dati e riversarla all'interno del DataSet: nell'esempio, viene usata una base dati Access, ma con un procedimento analogo è facile usare XML o base dati SQL. Il database, assolutamente semplice, consta di tre tabelle tra loro collegate da relazioni uno a molti.
Il database di esempio è costituito da tre semplici tabelle, messe logicamente in realazione uno a molti tra di loro come dalla figura:
Il primo passo da compiere è quello di ricreare un oggetto DataSet con analoghe caratteristiche.
All'interno del primo codebehind,
index.vb, si trova la routine
binda() che provvede a creare un ogetto
OleDbConnection
Dim conn as OleDbConnection = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source="& server.mappath("miodb.mdb"))
e, successivamente, tre
OleDbDataAdapter, uno per ogni tabella:
Dim Adapter As New OledbDataAdapter("Select * from Categoria",conn)
Dim Adapter2 as new OledbDataAdapter("Select * from Autori",conn)
Dim Adapter3 as new OledbDataAdapter("Select * from Opere",conn)
Viene quindi creato il dataset:
Dim MioDataSet As New DataSet()
e al suo interno vengono riversati i dati dei tre
DataAdapter creati in precedenza: si usa il metodo
Fill per creare le generare tabelle nel DataSet, tabelle che vengono chiamate
table1, table2 table3
Adapter.Fill(ds,"table1")
Adapter2.Fill(ds,"table2")
Adapter3.fill(ds,"table3")
Adesso è necessario impostare la relazione anche tra le tabelle del dataset: le istruzioni sono semplici e richiedono la definizione degli oggetti datacolumn a carico dei quali creare le relazioni, e la successiva dichiarazione delle stesse:
Dim Parent As DataColumn
Dim Child As DataColumn
parent = ds.Tables("table1").Columns("idcategoria ")
child=ds.Tables("table2").Columns("idcategoria ")
La relazione viene quindi agganciata, in termini figurati, alle due tabelle del DataSet:
Dim dr As DataRelation = New DataRelation("newrelation", Parent, Child, False)
ds.Relations.Add(dr)
e quindi, in maniera analoga, per la seconda relazione:
Parent= ds.Tables("table2").Columns("idautore ")
Child = ds.Tables("table3").Columns("idautore")
dr = New DataRelation("newrelation2", Parent, Child, False)
ds.Relations.Add(dr)
e, infine, il DataSet, con la sua tabella principale, denominata
table1, viene usata quale fonte di dati per il nostro controllo chiamato HG1.
Per iniziare ad usare il controllo, è ovviamente necessario creare una directory bin dove inserire la dll; creata quindi la nuova pagina aspx, si andrà a referenziare il controllo con la direttiva
<%@ Register TagPrefix="DBWC" Namespace="DBauer.Web.UI.WebControls" Assembly="DBauer.Web.UI.WebControls.HierarGrid" %>A questo punto, si tratta di inserire il controllo nella pagina, così come si farebbe con un normale DataGrid:
<asp:DataGrid id="DG1" runat="server"> cambiato in <dbwc:HierarGrid id="HG1" runat="server">Unica accortezza da usare è quella di dichiarare il controllo nel codebehind:
Protected WithEvents HG1 As DBauer.Web.UI.WebControls.HierarGridA questo punto, nel file aspx, si tratterà di impostare il grid come si desidera: nel caso in esame, esso viene programmato per visualizzare una sola colonna; essenziale è impostare ora i valori delle proprietà che consentono al controllo di funzionare, e cioè il
LoadControlMode ed il
TemplateDataMode. Il primo, LoadControlMode, viene impostato come
LoadControlMode="UserControl"Mentre il secondo parametro,
TemplateDataMode, può essere impostato o su
SingleRow o su
Table: nel primo caso, ad ogni riga sarà associato il caricamento di un user control, mentre nel secondo caso lo stesso tipo di controllo verrà usato per l?intera griglia. Lo svantaggio, nel nostro caso di usare
TemplateDataMode="SingleRow" sarebbe che ogni riga figlia del grid verrebbe visualizzata grazie ad un file ascx suo proprio, impedendo invece di riunire tutte le righe figlie nello stesso controllo, uguale, nell?esempio, ad un datagrid. Nel caso di esempio, perciò, si imposta:
TemplateDataMode="Table"Infine, per velocizzare quanto più possibile il rendering del controllo, si può utilizzare la proprietà :
TemplateCachingBase="Tablename"A questo punto, la griglia è pronta per essere programmata a visualizzare i dati: questo viene fatto grazie all'evento
TemplateSelection(...), dichiarato come:
Private Sub HG1_TemplateSelection(ByVal sender As Object, ByVal e As DBauer.Web.UI.WebControls.HierarGridTemplateSelectionEventArgs) Handles HG1.TemplateSelection
In questo evento si deve semplicemente indicare il file ascx a usare come visualizzatore dei dati figli, in questo modo:
e.TemplateFilename = e.Row.Table.TableName + ".ascx"
che, nell'esempio, è ridotto più semplicemente, ad un
Private Sub HG1_TemplateSelection(ByVal sender As Object, ByVal e As DBauer.Web.UI.WebControls.HierarGridTemplateSelectionEventArgs) Handles HG1.TemplateSelection
Select Case (e.Row.Table.TableName)
Case "table2"
e.TemplateFilename = "file2.ascx"
End Select
End Sub
Dove cioè si indica che, se la tabella dipendente ? nel dataset ? è table2, il controllo deve agganciare il file (controllo utente) file2.ascx.
I controlli utenti che vengono usati sono dei file che vengono salvati come ascx, all?interno dei quali si inseriscono tutti i controlli che servono per visualizzare i dati che servono, con l?accortezza di usare una particolare espressione di databinding che è:
DataBinder.Eval(CType(Container, DataGridItem).DataItem, "nome_campo_da_visualizzare")e che diventa, nel caso ? come quello di esempio ? di uso di un controllo valido per l?intera tabella:
Private Sub prova_DataBind(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.DataBinding
Dim dgi As DataGridItem = CType(Me.BindingContainer, DataGridItem)
Dim ds As DataSet = CType(dgi.DataItem, DataSet)
DG1.DataSource = ds
DG1.DataMember = "table3"
DG1.DataBind()
End Sub
che crea un DataSet sulla base della proprietà
BindingContainer del controllo principale, datast che viene usato per alimentare il controllo che rappresenta i dati gerarchici.
Nell?esempio, cui si rimanda per chiarezza, vengono quindi creati due controlli utente (denominati file2.ascx e file3.ascx) in cui lo stessa routine del listato 9 viene riferita alle due tabelle dipendenti.