Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Access TreeView

311 views
Skip to first unread message

Bruno Campanini

unread,
May 17, 2019, 1:10:29 PM5/17/19
to
Mi interessava la costruzione in VBA.
Ce n'è molte in Google ma poi ognuno dà i nomi più diversi
a Parent e Child e nessun (meglio: io non ho trovato nessuno)
che dia un'indicazione precisa sul layout delle tabelle e
sulla rappresentazione dell'albero genealogico finale.
Qualcuno ha una cosetta piccola-piccola su cui rifarsi
l'occhio senza eccessivo impegno mentale?

Bruno

@Alex

unread,
May 17, 2019, 2:03:04 PM5/17/19
to
Non ho capito nulla.
Cosa ti serve... di preciso...?
Spiega semplice sai che io fatico se inizi ad andare troppo sul tecnico...

@Alex

Bruno Campanini

unread,
May 17, 2019, 7:03:43 PM5/17/19
to
After serious thinking @Alex wrote :
> Non ho capito nulla.
> Cosa ti serve... di preciso...?
> Spiega semplice sai che io fatico se inizi ad andare troppo sul tecnico...

Un esempio piccolo-piccolo di applicazione TreeView in VBA
che mostri come partendo da una tabellina corta-corta si
giunga all'alberello finale.

Mi sembrava di esser stato chiaro... debbo convincermi ad
inviare post la mattina presto, prima che inizi a bere.

Bruno

@Alex

unread,
May 18, 2019, 4:52:52 AM5/18/19
to
Intanto la tabella devono essere 2... semplifica sulla struttura più flessibile, potrebbe esserne usata anche solo 1 con il campo Padre, ma in quel caso un ITEM può essere figlio solo nei 1 padre... risulta molto limitata, ma fattibile.

Io uso sempre la.soluzione con 2 tabelle.

tblAnagrafica
IdAnagrafica(Pk)
IdTipo
Desxrizione

tblRelazione
IdAnagrafica(Pk)
IdAnagraficaParent(Pk)
Quantita

Va da sé che nella tabella tblRelazione serve una SELFREFERENCE TABLE alla tabella tblAnagrafica_1

La tabella tblRelazione rappresenta la ricetta ottenuta per gerarchia.

Questa struttura consente di poter creare ITEM con Figli che possono essere a loro volta ITEM di Items.

Questa è la.struttura più flessibile in assoluto.

Pensa a ricette di cucina in cui hai(idTipo):
RICETTE FINITE
RICETTE SEMILAVORATE
ELEMENTI BASE

Questi 3(n) tipi di Anagrafica finiscono sempre nella tblAnagrafica mentre la ricetta sarà indicata dalla tblRelazione.
È evidente che ogni oggetto della tabella anagrafica può essere relazionato come sottooggetto di un'altro oggetto.

La proprietà IdTipo però è quella che si usa per estrarre le RICETTE FINITE.
Da lì si cerca nella tabella tblRelazione quali items ci sono con IdAnagraficaParent=RICETTA FINITA.... e qui inizia la logica ricorsiva in qua to ogni IDANAGRAFICA figlio di IDANAGRAFICAPARENT potrebbe a sua volta avere figli... che saranno sempre contenuti nella tblRelazione...

Il Treeview di fatto è una classe ricorsiva sull'oggetto Nodo a sua volta elemento della collection Nodes...

Quindi se si costruisce la funzione VBA che esplora in modo gerarchico la tblRelazione in automatico si va a popolare l'oggetto gerarchico del Treeview... nel senso che se un item ha figli si inseriscono i nodi figlio...

Oggi non ho pc comodo... ma se riesco domani ti mostro le 10 righe di codice ricorsiva per popolare il treeview...

Intanto questi 2 link danno qualche spunto utilr:
https://access-programmers.co.uk/forums/showthread.php?t=277829

https://www.experts-exchange.com/questions/20988029/Recursive-treeview-function.html

Saluti
@Alex

@Alex

unread,
May 18, 2019, 2:20:24 PM5/18/19
to
Ti mostro un esempio veloce e semplice della struttura semplificata, ovvero con una sola Tabella che consente la gerarchia a N livelli.

Quindi la struttura delle Tabelle prevede 1 Tabella Principale de una Tabella GErarchica:

Tabella Princilape che identifica un PROGETTO:
[tProgetti]
CAMPI:
IdProgetto (PK Counter)
Titolo (Testo Breve)
... altri campi non utili

Tabella Gerarchica che identifica FASI di LAVORO del PROGETTO, ongni Fase può avere SOTTOFASI(n) non finite.

[tPhases]
CAMPI:
IdProgetto (FK Intero Lungo)
IdPhaseParent (FK Intero Lungo)
IdPhase (PK Counter)
NomeFase (Testo)

La tabella [tProgetti] è in relazione 1-M con la tabella [tPhases].
La tabella [tPhases] ha una SELFREFERENCE_TABLE [tPhases_1] sul campo

[tPhases_1](IdPhase)[1]<----->[M](IdPhaseParent)[tPhases]

Il Legame(JOIN) è di tipo [3]

La Maschera principale è basata sulla Tabella [tProgetti], sicchè ho i Controlli associati ai campi [IdProgetto] e [Titolo]
Sono 2 TextBox con il NomeControllo=NomeCampo
Il Controllo TreeView si chiama [cTW]

Nella Form ho questo codice sotto...
La funzione LoadTree() carica il NOME del PROGETTO come ROOT, poi inizia a caricare i Records della Tabella [tPhases] che NON HANNO Padre, ovvero tutti i Records a LIVELLO=0.
La funzione come vedi nel LOOP, chiama la Funzione(Ricorsiva), passando il NODO che contiene la PK nella proprietà TAG del Nodo.

AddChildren sKey, ndP

Questa seconda Funzione carica i Records con IdPhaseParent=Nodo.tag, ovvero tutt i Records che hanno come Padre il nodo di LIVELLO(N-1)
Qusta funzione a sua volta nel LOOP esplora se il Record ha dei SottoRecords richiamando se setssa..., questa è la RICORSIONE.

Questo il codice(ho tolto dei pezzi non necessari, spero di non aver tolto troppo):
---------------------------------------------------------------------------------
Option Compare Database
Option Explicit

Private Const mcProcParent As String = "Form_Progetto"
Private Const mMnuName As String = "mPopupRuntime"

Private mT As MSComctlLib.TreeView
Private mND As MSComctlLib.node
Private mItem As String

Private Sub Form_Load()

Set mT = Me.cTW.Object
mT.Font.Name = "Calibri"

Call LoadTree
Me.cTW.Visible = True

End Sub


Public Sub LoadTree()
Const mcProcName = "LoadTree"
On Error GoTo Err_Handler

Dim rs As DAO.Recordset
Dim rsI As DAO.Recordset
Dim sKey As String
Dim ssKEY As String
Dim sSQL As String
Dim ndP As MSComctlLib.node
Dim ndC As MSComctlLib.node
Dim ndRoot As MSComctlLib.node

Set mT = Me.cTW.Object
mT.Nodes.Clear

Set ndRoot = mT.Nodes.Add(, , "P-" & Me.IdProgetto, Me.Titolo, 1)

sSQL = "SELECT * FROM tPhases WHERE IdProgetto=" & Me.IdProgetto & " AND IdPhaseParent Is Null ORDER BY IdPhase;"

Set rs = DBEngine(0)(0).OpenRecordset(sSQL, dbOpenDynaset, dbReadOnly)
If rs.EOF = False Then
rs.MoveFirst
Do Until rs.EOF
sKey = UniqueKey

Set ndP = mT.Nodes.Add("P-" & Me.IdProgetto, tvwChild)

ndP.Key = sKey
ndP.Text = rs.Fields("NomeFase").Value
ndP.Image = 2
ndP.Tag = rs.Fields("IdPhase").Value
'add sub levels
AddChildren sKey, ndP

rs.MoveNext
Loop

End If

Exit_Here:

On Error Resume Next
ndRoot.Expanded = True
If Not rs Is Nothing Then rs.Close: Set rs = Nothing
Set ndP = Nothing
Set ndRoot = Nothing

Err.Clear
Exit Sub

Err_Handler:
Select Case Err.Number
Case Else
MsgBox Err.Number & " in " & mcProcParent & vbNewLine & Err.Description, vbCritical, mcProcName
End Select
Resume Exit_Here

End Sub

Sub AddChildren(ByVal Parent_Key As String, Optional mNode As MSComctlLib.node)
Const mcProcName = "AddChildren"
On Error GoTo Err_Handler

Dim rsC As DAO.Recordset
Dim sSQL As String
Dim sKey As String
Dim nd As MSComctlLib.node

sSQL = "SELECT * FROM tPhases WHERE IdProgetto=" & Me.IdProgetto & " AND IdPhaseParent = " & mNode.Tag & " ORDER BY IdPhase"
Set rsC = DBEngine(0)(0).OpenRecordset(sSQL, dbOpenDynaset, dbReadOnly)
If rsC.RecordCount > 0 Then
With rsC
.MoveFirst
Do While .EOF = False
sKey = UniqueKey

Set nd = cTW.Nodes.Add(Parent_Key, tvwChild)
nd.Key = sKey
nd.Text = .Fields("NomeFase").Value
nd.Image = 4
nd.Tag = .Fields("IdPhase").Value

AddChildren sKey, nd

Set nd = Nothing

.MoveNext
Loop
End With
End If

Exit_Here:

On Error Resume Next
If Not rsC Is Nothing Then rsC.Close: Set rsC = Nothing

Err.Clear
Exit Sub

Err_Handler:
Select Case Err.Number
Case Else
MsgBox Err.Number & " in " & mcProcParent & vbNewLine & Err.Description, vbCritical, mcProcName
End Select
Resume Exit_Here

End Sub

Public Function UniqueKey() As String
'***************************************************************************
'Purpose: Generate a unique key for a Treeview. Actually there is a one in
' 10 million chance of the key *not* being unique, but the error
' handling code in the calling Sub takes care of that.
'Inputs : None
'Outputs: The key for the Treeview
'***************************************************************************

UniqueKey = "K" & 1 + Int(Rnd() * 10000000)

End Function

Secondo me questo è un Esempio abbastanza banale e semplice.
Se vuoi applicare il concetto alla evoluzione con le 2 Tabelle, la logica è esattamente la stessa...

Ciao
@Alex

Bruno Campanini

unread,
May 18, 2019, 7:22:13 PM5/18/19
to
on 18-05-19, @Alex supposed :
> [...]
Per me hai tolto troppo poco.
Per il momento grazie, ci guarderò con calma e riferirò.

Ti mando il completamento di quanto già inviato ad Alfio
(Ricostruzione Albero Categorie del 11-05-19).
https://1drv.ms/u/s!AvTaMfd5-b2oxB0LAwhKiz-oOonD

Puoi cambiare StartParent in sub CFG().
Non mi piace per niente il ricorso a CatTree() e NumCat
definite in Root del Modulo, per la compilazione della
tabella destinazione (Categorie_Tree)... ma non ho trovato
nulla di meglio.
Hai qualche idea migliore?

Bruno

@Alex

unread,
May 20, 2019, 2:03:27 AM5/20/19
to
...
> Puoi cambiare StartParent in sub CFG().
> Non mi piace per niente il ricorso a CatTree() e NumCat
> definite in Root del Modulo, per la compilazione della
> tabella destinazione (Categorie_Tree)... ma non ho trovato
> nulla di meglio.

Ho già espresso come di norma si gestiscono le gerarchie.
Quello che hai proposto, ancorchè funzionante, richiede che sia creata una Tabella a RUNTIME... cosa che io nemmeno prendo in considerazione come tecnica.
Quella Tabella peraltro non servirebbe per popolare un TREEVIEW, anzi di fatto non servirebbe a nulla in quanto anche per usarla non puoi associarla a Report o a Maschere non sapendo il Livello di profondità.

Oltretutto, nella tabella Categorie, commetti un errore sostanziale... nel senso che quella tabella non verrà mai usata in quel modo.

La tabella che chiami Categorie in realtà a me pare CategorieParent...

In sostanza dovrebbe essere tabella Categorie:

IdCategoria
NomeCategoria
Parent


> Hai qualche idea migliore?

MIGLIORE per ottenere cosa...?
Se vuoi ottenere la Tabellina che hai ricavato tu... va bene quello che hai fatto, poi che secondo me non serva a nulla a livello pratico è un dettaglio.

Se devi gestire delle GERARCHIE anche a livello grafico, il codice che ti ho esposto prima è l'unica soluzione.

Tieni presente poi, sempre nel concreto, che quando si realizzano GERARCHIE(Multilivello), che poi vanno stamnpate, si deve serializzare e per farlo si usa sempre il codice ricorsivo che ho mostrato per popolare una Tabella di SERVIZIO dedicata alla reportistica, all'incirca strutturata così:

IdProgress
IdProgetto
IdCategoria
Livello
Descrizione

Nel Report quindi una Rappresentazione GERARCHIA MULTILIVELLO è in realtà una stampa semplice a cui si delega la gestione GRAFICA dell LIVELLO GERARCHICO al cmapo LIVELLO che con 1 riga di codice, dopo aver impostato il LAYOUT dei controlli nella sezione si sposta a DX in base al livello stesso:

Me.IdProgress.Left = iLMargin + Me.Livello * (Me.IdProgress.Width + iLMargin)

Quindi in sostanza la tua soluzione a mio avviso non serve a nulla.

> Bruno

@Alex


Bruno Campanini

unread,
May 20, 2019, 4:38:50 AM5/20/19
to
@Alex brought next idea :

> [...]
> Nel Report quindi una Rappresentazione GERARCHIA MULTILIVELLO è in realtà una
> stampa semplice a cui si delega la gestione GRAFICA dell LIVELLO GERARCHICO
> al cmapo LIVELLO che con 1 riga di codice, dopo aver impostato il LAYOUT dei
> controlli nella sezione si sposta a DX in base al livello stesso:

Meno chiacchiere, fammi vedere un fior del tuo giardino, fammi
vedere un report.
Ho bisogno di vedere un risultato, te l'ho già chiesto alla prima
domanda che m'hai fatto.
Diversamente non riesco a comprendere l'utilià delle tue
considerazioni.

Mandamelo stampato, non dirmi come si deve fare per crearlo.

Bruno

@Alex

unread,
May 20, 2019, 5:35:32 AM5/20/19
to
Il giorno lunedì 20 maggio 2019 10:38:50 UTC+2, Bruno Campanini ha scritto:
> @Alex brought next idea :
>
> > [...]
> > Nel Report quindi una Rappresentazione GERARCHIA MULTILIVELLO è in realtà una
> > stampa semplice a cui si delega la gestione GRAFICA dell LIVELLO GERARCHICO
> > al cmapo LIVELLO che con 1 riga di codice, dopo aver impostato il LAYOUT dei
> > controlli nella sezione si sposta a DX in base al livello stesso:
>
> Meno chiacchiere, fammi vedere un fior del tuo giardino, fammi
> vedere un report.

Le chiacchiere si fanno quando non si sa di cosa si parla.
L'argomento è TECNICO ma chiarissimo.

> Ho bisogno di vedere un risultato, te l'ho già chiesto alla prima
> domanda che m'hai fatto.

Ti ho descritto le Tabelle, dato il codice... credo che tu possa fare il resto.

> Diversamente non riesco a comprendere l'utilià delle tue
> considerazioni.

Capisco...!

> Mandamelo stampato, non dirmi come si deve fare per crearlo.

Aspetta tranquillo.

> Bruno

@Alex

RobertoA

unread,
May 20, 2019, 9:13:47 AM5/20/19
to
"..Mandamelo stampato, .."


Anvedi, sei un ardito
Che hai fatto servizio nelli Alpini ?

@Alex

unread,
May 20, 2019, 10:41:04 AM5/20/19
to
Io si...! Battaglione Tridentina.

@Alex

Bruno Campanini

unread,
May 20, 2019, 2:21:44 PM5/20/19
to
RobertoA formulated on Monday :
> "..Mandamelo stampato, .."
>
>
> Anvedi, sei un ardito
> Che hai fatto servizio nelli Alpini ?

No, figlio maggiore che ha perso anche l'ultimo genitore.
Se rinasco un'altra volta vado volontario in fanteria.

Bruno

Gabry69

unread,
May 23, 2019, 5:25:30 AM5/23/19
to
Non so se ti può essere utile, io mi sono trovata molto bene seguendo la guida pubblicata da karl su http://www.donkarl.com/it/CISA/Download.htm

Bruno Campanini

unread,
May 23, 2019, 10:12:22 PM5/23/19
to
Gabry69 formulated the question :

>> Qualcuno ha una cosetta piccola-piccola su cui rifarsi
>> l'occhio senza eccessivo impegno mentale?
>>
>> Bruno
>
> Non so se ti può essere utile, io mi sono trovata molto bene seguendo la
> guida pubblicata da karl su http://www.donkarl.com/it/CISA/Download.htm

Ti ringrazio.
Vi ho dato una scorsa veloce e mi sembra ben fatto.
È un bel giochino non c'è dubbio anche se mancano
(dovrei dire non ho visto?) alcune cosette tipo la
costruzione di un un report decente e, dato un child,
la possibilità di evidenziare la sua ascendenza/discendenza.
Correggimi se sbaglio.

Tu lo usi per qualche utilità pratica?

Bruno

Gabry69

unread,
May 24, 2019, 2:39:27 AM5/24/19
to
Di solito lo utilizzo per facilitare i filtri durante le interrogazioni; cerco di essere più chiara con un esempio... Interrogazioni anagrafe articoli prendiamo ad esempio categoria e sottocategoria
Nel Treeview a livello 1 carico le categorie, a livello 2 le sottocategorie
Quando l'utente clicca su un nodo applico il filtro.
Normalmente aggiungo anche un pulsante che permette di ricaricare l'albero con le caratteristiche scelte dall'utente da un elenco preimpostato.
Come metodo è molto semplice e anche di facile utilizzo

Gabriella

Massimiliano Amendola

unread,
May 27, 2019, 11:22:23 AM5/27/19
to
www.accessgroup.it esempio di treeview ricorsivo
MA
0 new messages