Come usare un controllo treeview in Access

Oggi impariamo come usare un controllo treeview in Access.

treeview in access

Non è così semplice, perché il treeview è un controllo particolare. Non ha una origine controllo facilmente impostabile, come la casella di testo, o la casella combinata. Per popolare di valori il controllo treeview devo usare dei recordset.

Il recordset è un oggetto che mi consente di “mettere” in memoria i record presenti in una tabella o in una query (quindi un sottoinsieme di righe e forse anche un sottoinsieme di colonne della tabella originale, o anche campi provenienti da più tabelle).

I dati visibili su un controllo treeview sono chiaramente dati che hanno una relazione gerarchica tra loro, un po’ come la struttura ad albero delle cartelle nel nostro computer. Nel nostro esempio lavoreremo con i dati dei clienti e dei relativi ordini, che infatti sono in relazione uno-a-molti.

Partiamo con una maschera vuota in cui inseriamo un controllo treeview. Lo troviamo andando su controlli ActiveX

controlli ActiveX

Nel lungo elenco selezioniamo Microsoft Treeview Control e poi Ok. Diamo nome al controllo “tv”.

Aggiungiamo un pulsante di comando “cmdCaricaTreeView”

Ora dobbiamo scrivere il codice che popolerà il controllo. Per farlo andiamo sull’evento Su clic del pulsante di comando, tasto destro, Genera, generatore di codice e arriviamo nella finestra dove scrivere il nostro VBA.

cmdCaricaTreeView

 

Iniziamo dichiarando una variabile di tipo MSComctlLib.Node. Sarà il nostro nodo, l’oggetto che di volta in volta conterrà i valori da visualizzare nel treeview.

Dim tempNode As MSComctlLib.Node

Ora le due variabili per i recordset dei clienti e degli ordini

Dim rsC As DAO.Recordset ' contiene i record dei clienti
Dim rsO As DAO.Recordset ' contiene i record degli ordini

A questo punto una istruzione che svuota il controllo TreeView:

tv.Nodes.Clear ' svuota il controllo treeview

E ora creiamo il livello iniziale del nostro treeview, un po’ come se fosse il nostro C:\ in una struttura ad albero vecchia maniera :-)

Set tempNode = tv.Nodes.Add(, , "C", "Clienti")

Uso il metodo Add per la collezione Nodes del controllo Treeview.

Questo metodo ha 4 argomenti:

  • relative: serve per fare riferimento a un nodo già esistente
  • relationship: specifica il tipo di relazione con il nodo già esistente
  • key: chiave univoca che identifica il nodo
  • text: il testo che l’utente visualizza nel treeview (obbligatorio)

Sarà più chiaro il loro utilizzo mano mano che scriveremo il codice.

Carico il recordset con l’elenco dei clienti

Set rsC = CurrentDb.OpenRecordset("SELECT IDCliente,NomeSocietà FROM tblClienti ORDER BY NomeSocietà", , dbReadOnly)

Qui ho preso tutti i clienti. Avrei potuto filtrare i clienti scegliendo solo quelli che hanno fatto ordini quest’anno o altro. L’istruzione SELECT appartiene al linguaggio SQL, quello utilizzato dalle query.

dbReadOnly ottimizza l’esecuzione dell’istruzione, perché sto dicendo ad Access che non farò modifiche in quei record e così il recordset sarà imn sola lettura.

Ora eseguo un loop e passerò in rassegna tutti i record che esistono nel recordset:

Do While Not rsC.EOF
Loop

Ossia: Fai finché non ti trovi alla fine (EOF) del recordset rsC e ripeti (loop)

Carico un nodo per ogni cliente trovato nel recordset:

 Set tempNode = tv.Nodes.Add("C", tvwChild, "CL" & rsC.Fields("IDCliente"), rsC.Fields("NomeSocietà"))
  • “C”:relative. Infatti questo record è relativo al nodo superiore, che prima abbiamo identificato con “C”
  • tvwChild: relationship. questo nodo sarà figlio del precedente (il cliente appartiene ai Clienti)
  • “CL” & rsC.Fields(“IDCliente”): key. chiave univoca del nodo (non può esistere un altro nodo con la stessa chiave)
  • rsC.Fields(“NomeSocietà”): text. Ciò che vedrà l’utente

Ora per ciascuno cliente, se esistono, carico gli ordini.

Come prima cosa carico il recordset con l’elenco degli ordini

 ' carico gli ordini del cliente
 Set rsO = CurrentDb.OpenRecordset("SELECT IDOrdine as ChiaveOrdine,DataOrdine FROM tblOrdini WHERE IDCliente=""" & rsC.Fields("IDCliente") & """ ORDER BY DataOrdine DESC", , dbReadOnly)

Qui è necessario mettere una WHERe, ossia un filtro, e selezionare solo gli ordini del cliente su cui sono. Per questo motivo faccio riferimento a rsC.Fields(“IDCliente”).

E ora è tutto come prima: faccio un ciclo e creo un nodo con l’ordine:

 Do While Not rsO.EOF
 Set tempNode = tv.Nodes.Add("CL" & rsC.Fields("IDCliente"), tvwChild, "O" & rsO.Fields("ChiaveOrdine"), rsO.Fields("DataOrdine"))
 rsO.MoveNext
 Loop
  • “CL”:relative. Infatti questo record è relativo al nodo superiore, che prima abbiamo identificato con “CL”
  • tvwChild: relationship. questo nodo sarà figlio del precedente (l’ordine appartiene al cliente)
  • “O” & rsC.Fields(“IDOrdine”): key. chiave univoca del nodo (non può esistere un altro nodo con la stessa chiave)
  • rsO.Fields(“DataOrdine”): text. Ciò che vedrà l’utente,. Ho scelto la data, poteva naturalmente essere qualunque altra informazione.

Ora chiudo il recordset

 rsO.Close

Dopo la chiusura del primo Do (quello tra i clienti), chiudo il recordset rsC:

rsC.Close

Ultima istruzione: imposto il treeview con il primo nodo sempre espanso:

tv.Nodes.Item(1).Expanded = True

Riepilogando, il codice è il seguente:

Dim tempNode As MSComctlLib.Node
Dim rsC As DAO.Recordset ' contiene i record dei clienti
Dim rsO As DAO.Recordset ' contiene i record degli ordini
tv.Nodes.Clear ' svuota il controllo treeview
Set tempNode = tv.Nodes.Add(, , "C", "Clienti")
Set rsC = CurrentDb.OpenRecordset("SELECT IDCliente,NomeSocietà FROM tblClienti ORDER BY NomeSocietà", , dbReadOnly)
Do While Not rsC.EOF
 Set tempNode = tv.Nodes.Add("C", tvwChild, "CL" & rsC.Fields("IDCliente"), rsC.Fields("NomeSocietà"))
 ' carico gli ordini del cliente
 Set rsO = CurrentDb.OpenRecordset("SELECT IDOrdine as ChiaveOrdine,DataOrdine FROM tblOrdini WHERE IDCliente=""" & rsC.Fields("IDcliente") & """ ORDER BY DataOrdine DESC", , dbReadOnly)
 Do While Not rsO.EOF
 Set tempNode = tv.Nodes.Add("CL" & rsC.Fields("IDCliente"), tvwChild, "O" & rsO.Fields("ChiaveOrdine"), rsO.Fields("DataOrdine"))
 rsO.MoveNext
 Loop
 rsO.Close
 rsC.MoveNext
Loop
rsC.Close
tv.Nodes.Item(1).Expanded = True