[Risolto] Controllo ActiveX TreeView. Errore run-time '35603': Invalid key

di il
5 risposte

[Risolto] Controllo ActiveX TreeView. Errore run-time '35603': Invalid key

Vorrei creare, in un db access 2010, una struttura ad albero per la tabella delle funzioni di un'applicazione che sono organizzate a più livelli (ne ho previsti fino a 10, hai visto mai).

Alex mi ha suggerito il controllo treeview e in effetti ho potuto constatare che è molto interessante.

Allora mi sono impegnato, giuro, a studiare, ma niente da fare.

Ho visto sul tubo un esempio fatto su Nordwind da Emanuele Lana e l'ho ricopiato passo passo. Questo l'indirizzo:



Dice che lui lo fa su access 2013 ma va bene anche su 2010. Nell'esempio citato a lui funziona.

Su una maschera vuota ha creato un controllo TreeVeiw versione 6.0 con nome "tv" e un pulsante con nome "CaricaTreeView".

All'evento click ha scritto il codice.

Però, tanto per cominciare nel mio NW i nomi delle tabelle sono diversi e quelli dei campi oltre ad essere diversi a volte anche inframezzati da spazi (avrà una versione diversa di Northwind). Poco male perché ho sostituito i nomi, usando parentesi quadre ove necessitava, sperando di aver fatto bene.
Mi dà Errore run-time '35601': Element not found
Eseguendo il debug si posiziona sulla riga evidenziata con "!!!!!!!!!!!!!!!ERRORE!!!!!!!!!!!!!!!".
Questo il codice da lui usato e da me modificato con i nomi di tabelle e campi del MIO Northwind:

Private Sub cmdCaricaTreeView_Click()

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")
' il primo argomento rappresenta il primo livello dei nodi e si può lasciare vuoto
' il secondo argomento è il tipo di relazione e si può lasciare vuoto

Set rsC = CurrentDb.OpenRecordset("SELECT ID,Società FROM Clienti ORDER BY Società", , dbReadOnly)
Do While Not rsC.EOF
    Set tempNode = tv.Nodes.Add("C", tvwChild, "CL" & rsC.Fields("ID"), rsC.Fields("Società")) "!!!!!!!!!!!!!!!ERRORE!!!!!!!!!!!!!!!"
  ' carico gli ordini del cliente
  Set rsO = CurrentDb.OpenRecordset("SELECT [ID Ordine] as ChiaveOrdine,[Data Ordine] FROM Ordini WHERE [ID Cliente]=""" & rsC.Fields("ID") & """ ORDER BY [Data Ordine] DESC", , dbReadOnly)
    Do While Not rsO.EOF
        Set tempNode = tv.Nodes.Add("CL" & rsC.Fields("ID"), tvwChild, "O" & rsO.Fields("ChiaveOrdine"), rsO.Fields("[Data Ordine]"))
        rsO.MoveNext
    Loop
    rsO.Close
    rsC.MoveNext
Loop

rsC.Close

tv.Nodes.Item(1).Expanded = True
' per espandere automaticamente il primo livello

End Sub
Vabbè. Non ho risolto l'errore però non mi sono perso d'animo e ho riprodotto il codice sul mio db, caso mai il problema sia la versione di Northwind e/o di Access.

Ho creato una maschera nuova, il controllo treeview versione 6.0 e il pulsante con gli stessi nomi dell'esempio e all'evento click ho scritto questo codice inserendo anche stringhe SQL un po' più complesse, volendo visualizzare più campi e selezionare il livello, per i primi due livelli delle funzioni, tanto per provare e iniziare, e mi dà Errore run-time '35603': Invalid key alla riga evidenziata con "!!!!!!!!!!!!!!!ERRORE!!!!!!!!!!!!!!!".

Questa è la struttura della mia tabella "Accesso_Funzioni_Applicazione":
Nome Tipo dati Lunghezza
CODICE_FUNZIONE Testo 20
DESCRIZIONE_FUNZIONE Testo 50
LIVELLO Numerico Intero lungo-Generico-Decimali 0
TIPO_FUNZIONE Testo 1 - Valido se: "M" Or "P" - MsgErrore: Il valore deve essere "M"=Menù "P"=Programma
CODICE_FUNZIONE_PADRE Testo 20

Private Sub cmdCaricaTreeView_Click()

Dim tempNode As MSComctlLib.Node

Dim rs0 As DAO.Recordset 'contiene i record del livello 0
Dim rs1 As DAO.Recordset 'contiene i record del livello 1


tv.Nodes.Clear  'svuota il controllo treeview

Set tempNode = tv.Nodes.Add(, , "0", "Livello0") "!!!!!!!!!!!!!!!ERRORE!!!!!!!!!!!!!!!"
' il primo argomento rappresenta il primo livello dei nodi e si può lasciare vuoto
' il secondo argomento è il tipo di relazione e si può lasciare vuoto

Set rs0 = CurrentDb.OpenRecordset("SELECT LIVELLO,CODICE_FUNZIONE,DESCRIZIONE_FUNZIONE,TIPO_FUNZIONE FROM Accesso_Funzioni_Applicazione WHERE LIVELLO=0 ORDER BY CODICE_FUNZIONE", , dbReadOnly)
Do While Not rs0.EOF
    Set tempNode = tv.Nodes.Add("0", tvwChild, "00" & rs0.Fields("CODICE_FUNZIONE"), rs0.Fields("DESCRIZIONE_FUNZIONE"))
  ' carico il livello 1
  Set rs1 = CurrentDb.OpenRecordset("SELECT LIVELLO,CODICE_FUNZIONE,DESCRIZIONE_FUNZIONE,TIPO_FUNZIONE FROM Accesso_Funzioni_Applicazione WHERE LIVELLO=1 ORDER BY CODICE_FUNZIONE", , dbReadOnly)
    Do While Not rs1.EOF
        Set tempNode = tv.Nodes.Add("00" & rs0.Fields("CODICE_FUNZIONE"), tvwChild, "01" & rs1.Fields("CODICE_FUNZIONE"), rs1.Fields("[DESCRIZIONE_FUNZIONE]"))
        rs1.MoveNext
    Loop
    rs1.Close
    
    rs0.MoveNext
Loop

rs0.Close


tv.Nodes.Item(1).Expanded = True
' per espandere automaticamente il primo livello
End Sub
Dove sbaglio?

Grazie.


Alex66

5 Risposte

  • Re: [Risolto] Controllo ActiveX TreeView. Errore run-time '35603': Invalid key

    Ti renderai conto che se hai 10 LIVELLI non puoi scrivere una funzione che sprofonda in serie ed aprire 10 Recordset in quel modo...?

    Giusto per farti capire cosa si intende per ricorsività della Funzione...

    Solo a titolo di ESEMPIO BANALE, usando solo 1 Tabella in AutoReferenza.

    Ipotizzo di avere una Tabella chiamata [T1] con 3 Campi:
    
    [IdPK]			Chiave Primaria tipo Counter
    [IdParent] 		Indice tipo Intero Lungo
    [Descrizione] 		Testo
    Quindi prendiamo in considerazione i FILMS, così per fare un esempio banale, ed ipotizzo contengano questo
    
    IdParent		IdPK		Descrizione
    			1		Cartoons
    	1		2		Tom&Jerry
    	1		3		Mazinga
    	3		4		Mazinga Z
    	3		5		Super Mazinga
    	5		6		Figlio di Super Mazinga
    	6		7		Pronipote di super Mazinga
    	2		8		Gatto Tom&Jerry
    	2		9		Topo Jerry
    			10		Films
    	10		11		War
    	11		12		Platoon
    	10		13		Horror
    	13		14		Non aprite quella porta
    Ora ipotizzando N livelli che a priori non conosco... metto il TreeView nella Maschera e lo chiamo [TreeCtrl]
    Quindi su Load Gestisco il precaricamento del NODO ROOT chiamato "FILMS", e dei primi Livelli o dei Livelli a Gerarchia 0 che non hanno Parent, lo si vede dal Fatto che il Campo IdParent è NULL.
    Option Compare Database
    Option Explicit
    
    
    Private mT                      As MSComctlLib.TreeView
    Private mND                     As MSComctlLib.node
    Private mItem                   As String
    
    Private Sub Form_Load()
        
        Set mT = Me.TreeCtrl.Object   
        Call LoadTree
        Me.TreeCtrl.visible = True
        
    End Sub
    
    Private Sub Form_Unload(Cancel As Integer)
    
        On Error Resume Next
        Set mND = Nothing
        Set mT = Nothing
    
    End Sub
    
    Public Sub LoadTree()
        Dim ndRoot      As MSComctlLib.node
            
        mT.Nodes.Clear
        
        Set ndRoot = mT.Nodes.Add(, , "FILMS", "FILMS", 1)
        AddBranch ndRoot
    
        On Error Resume Next
        ndRoot.Expanded = True
    
        Set ndRoot = Nothing
    End Sub
    
    Sub AddBranch(mNode As MSComctlLib.node, Optional Parent_Key)
    
        Dim rsC             As DAO.Recordset
        Dim sSQL            As String
        Dim sKey            As String
        Dim nd              As MSComctlLib.node
        
        If IsMissing(Parent_Key) then
    	    sSQL = "SELECT IdPK, Descrizione FROM TI WHERE IdParent Is Null ORDER BY Descrizione;"
        Else
    	    sSQL = "SELECT IdPK, Descrizione FROM TI WHERE IdParent=" & Parent_Key & " ORDER BY Descrizione;"
        end if
    	
        Set rsC = DBEngine(0)(0).OpenRecordset(sSQL, dbOpenDynaset, dbReadOnly)
        If rsC.RecordCount > 0 Then    
            With rsC
                .MoveFirst
                Do While .EOF = False
                    sKey = "N" & rs!IdPK
                  
                    Set nd = TreeCtrl.Nodes.Add(mNode, tvwChild)
                    nd.Key = sKey
                    nd.Text = rs.Fields("Descrizione").Value
                    AddBranch nd,rs!IdPK
                    .MoveNext
                Loop
            End With
        End If
        
        rsC.Close
        Set rsC = Nothing
    
    End Sub
    Il codice non l'ho provato, l'ho estratto semplificandolo da un mio codice più complesso, quindi magari devi controllarlo... ma, sperando tu possa ragionare più sulla LOGICA di come funziona, ho provato a concentrare un esempio per dimostrarti l'aspetto tecnico di SOSTANZA.
  • Re: [Risolto] Controllo ActiveX TreeView. Errore run-time '35603': Invalid key

    Ho fatto una correzione alla Funzione AddBranch, che erroneamente conteneva una chiamata alla Funzione AddChildren ma in realtà deve richiamare se stessa...
  • Re: [Risolto] Controllo ActiveX TreeView. Errore run-time '35603': Invalid key

    @Alex ha scritto:


    Ho fatto una correzione alla Funzione AddBranch, che erroneamente conteneva una chiamata alla Funzione AddChildren ma in realtà deve richiamare se stessa...
    Ho applicato il tuo esempio alla mia maschere e, incredibilmente, ce l'ho fatta!!!

    Però ho dovuto aggiungere alla mia tabella funzioni i campi id contatore e idparent numerico perché nella sSQL mi dava errore di tipo di dati nella clausola where. Infatti non ho capito come viene valorizzata la Parent_Key utilizzata nell'SQL. Evidentemente deve essere per forza di tipo numerico.
    Poco male perchè tanto il contatore si valorizza da solo e l'idparent, nell'inserimento della funzione da maschera, lo valorizzo automaticamente con dlookup ricercando il valore tramite il mio codice padre di tipo testo.
    Inoltre ho inserito l'espansione automatica dei nodi anche nel Do While in maniera di avere tutto visibile all'apertura della maschera.

    Questo è il codice mio:
    Option Compare Database
    
        Private mT As MSComctlLib.TreeView
        Private mND As MSComctlLib.Node
        Private mItem As String
        
    Option Explicit
    
    
    Private Sub Form_Load()
    
        Set mT = Me.TreeCtrl.Object
        Call LoadTree
        Me.TreeCtrl.Visible = True
      
    
    End Sub
    
    Private Sub Form_Unload(Cancel As Integer)
    
        On Error Resume Next
        Set mND = Nothing
        Set mT = Nothing
    
    End Sub
    
    Public Sub LoadTree()
    
        Dim ndRoot As MSComctlLib.Node
            
        mT.Nodes.Clear
        
        Set ndRoot = mT.Nodes.Add(, , "Funzioni", "Albero delle Funzioni")
        AddBranch ndRoot
    
        On Error Resume Next
        ndRoot.Expanded = True
        Set ndRoot = Nothing
        
    End Sub
    
    Sub AddBranch(mNode As MSComctlLib.Node, Optional Parent_Key)
    
        Dim rsC             As DAO.Recordset
        Dim sSQL            As String
        Dim sKey            As String
        Dim nd              As MSComctlLib.Node
        
        If IsMissing(Parent_Key) Then
            sSQL = "SELECT IdFK, LIVELLO, CODICE_FUNZIONE, DESCRIZIONE_FUNZIONE, IIf(TIPO_FUNZIONE='M', 'Menù', 'Programma') As 
                       DESCR_TIPO_FUNZIONE  FROM Accesso_Funzioni_Applicazione WHERE IdParent Is Null ORDER BY CODICE_FUNZIONE;"
        Else
            sSQL = "SELECT IdFK, LIVELLO, CODICE_FUNZIONE, DESCRIZIONE_FUNZIONE, IIf(TIPO_FUNZIONE='M', 'Menù', 'Programma') As 
                       DESCR_TIPO_FUNZIONE FROM Accesso_Funzioni_Applicazione WHERE IdParent= " & Parent_Key & " ORDER BY CODICE_FUNZIONE;"
        End If
        
        Set rsC = DBEngine(0)(0).OpenRecordset(sSQL, dbOpenDynaset, dbReadOnly)
        If rsC.RecordCount > 0 Then
            With rsC
                .MoveFirst
                Do While .EOF = False
                    sKey = "N-" & rsC!IdFK
                  
                    Set nd = TreeCtrl.Nodes.Add(mNode, tvwChild)
                    nd.Key = sKey
                    nd.Text = rsC.Fields("LIVELLO").value & " - " & rsC.Fields("CODICE_FUNZIONE").value & " - " & rsC.Fields("DESCR_TIPO_FUNZIONE").value & 
                                   " - " & rsC.Fields("DESCRIZIONE_FUNZIONE").value
                    AddBranch nd, rsC!IdFK
                    nd.Expanded = True
                    .MoveNext
                Loop
            End With
        End If
        
        rsC.Close
        Set rsC = Nothing
    
    End Sub
    
    
    Un'ultima cosa. Chiedo troppo se volessi usare questo controllo anche per assegnare i diritti di accesso, ossia in modalità modifica?

    Grazie.

    ALex66
  • Re: [Risolto] Controllo ActiveX TreeView. Errore run-time '35603': Invalid key

    alex66 ha scritto:


    ...
    Ho applicato il tuo esempio alla mia maschere e, incredibilmente, ce l'ho fatta!!!
    Quando il Codice è scritto tecnicamente bene è sempre più facile...

    alex66 ha scritto:


    Però ho dovuto aggiungere alla mia tabella funzioni i campi id contatore e idparent numerico perché nella sSQL mi dava errore di tipo di dati nella clausola where. Infatti non ho capito come viene valorizzata la Parent_Key utilizzata nell'SQL. Evidentemente deve essere per forza di tipo numerico.
    Poco male perchè tanto il contatore si valorizza da solo e l'idparent, nell'inserimento della funzione da maschera, lo valorizzo automaticamente con dlookup ricercando il valore tramite il mio codice padre di tipo testo.
    Non ho capito la questione del Dlookup, che io non uso mai... e secondo me nemmeno tu ne hai bisogno... l'IDPARENT lo ricavi dalla KEY del NodeParent... senza scomodare le funzioni di Aggregazione...

    alex66 ha scritto:


    ...
    Un'ultima cosa. Chiedo troppo se volessi usare questo controllo anche per assegnare i diritti di accesso, ossia in modalità modifica?
    Grazie.
    ALex66
    Questo controllo di fatto non fa nulla... solo VISUALIZZAZIONE, quindi qualsiasi cosa tu voglia Editare, poi lo devi gestire da Codice con azioni di Edit nel Recordset o con Query Update.
    Personalmente mi muovo, nei miei casi, in modo diverso... ossia, alla base la Maschera che ospita il TreeView è una Form Associata alla Stessa origine con cui popolo il TreeView, inserisco i Controlli TextBox/Combo associate ai Campi, e, quando voglio Editare, attivo/Visualizzo i Controlli Bound direttamente, ovvio che poi serve rigenerare il TreeView... ma solo il Nodo editato che poi è il Nodo Corrente.
  • Re: [Risolto] Controllo ActiveX TreeView. Errore run-time '35603': Invalid key

    Per il dlookup intendevo nella maschera di popolamento della tabella funzioni, non in quella di visualizzazione dell'albero.
    Credo sia l'unico sistema, dopo aggiornamento del codice funzione, per ricavare automaticamente il dato ed evitare errori, tanto che ho reso il campo disabilitato.
    In questa maschera ho inserito un pulsante che mi apre la maschera contenete il treeview per verificare l'esattezza dell'inserimento della funzione nella gerarchia.

    Comunque, per l'edit, grazie lo stesso. Immaginavo di chiedere troppo.

    Procederò come da tuo consiglio.



    Alex66
Devi accedere o registrarti per scrivere nel forum
5 risposte