none
Buscar nombre de nodo en Treeview igual a Item de ListBox RRS feed

  • Pregunta

  • Hola a todos:

    No entiendo por qué cuando estoy buscando un nodo que existe siempre, la búsqueda del mismo es "Nothing", como ejemplo tengo un ListBox1 con 2 ítems, por ejemplo: Balance en Masas Patrimoniales y Fondo de Maniobra. Lo que hago es que cuando recorro los nodos de 4 treeviews me busque el mismo nombre del nodo y entonces lo marque en el treeview correspondiente.

    Por ejemplo os muestro el código. Antes, mencionar que todos los procesos funcionan correctamente menos el valor a "Nothing"  que lleva la variable: Dim nodo as TreeNode = ObtenerTreenode(item). En este caso cuando sitúo el cursor en "item" haciendo un breakpoint, me marca correctamente, por ejemplo: Balance en Masas Patrimoniales en la primera pasada del bucle y Fondo de Maniobra en la segunda pasada, ya que el listBox lleva esos 2 ítems. Si me sitúo en el valor que tendría que llevar "nodo" siempre me indica Nothing, aunque los valores existen en el Treview correspondiente.

    Aquí como doy de alta el nodo, en el treeview por ejemplo.

    Private Sub CrearNodosTreeView2() Dim a As Assembly = Assembly.GetExecutingAssembly Dim rm As New ResourceManager(a.GetName.Name & ".Textos", a) ' Nodo Treeview2 '//ANALITICA Dim BalMasasPatrim As TreeNode = New TreeNode BalMasasPatrim.Name = rm.GetString("BalMasasPatrimoniales") ' BalMasasPatrim.Text = rm.GetString("BalMasasPatrimoniales") 'Balance en Masas Patrimoniales BalMasasPatrim.ImageIndex = 18 BalMasasPatrim.SelectedImageIndex = 18 .../.... end sub

    Cuando me posiciono con el cursor en el "Name" del Nodo, lleva perfectamente el nombre "Balance en Masas Patrimoniales"

    que obtiene del archivo de recursos.

    Ahora paso a buscar dicho nodo en la colección Nodes.

     Private Function ObtenerTreeNode(name As String) As TreeNode
    
            ' Referenciar el objeto TreeNode del control TreeView1 cuya
            ' propiedad Name se corresponda con el valor del elemento
            ' actual del control ListBox.
            '
            Dim nodo As TreeNode = TreeView1.Nodes(name)
            If (nodo Is Nothing) Then
                ' Referenciar el objeto TreeNode del control TreeView2
                nodo = TreeView2.Nodes(name)
                If (nodo Is Nothing) Then
                    ' Referenciar el objeto TreeNode del control TreeView3
                    nodo = TreeView3.Nodes(name)
                    If (nodo Is Nothing) Then
                        ' Referenciar el objeto TreeNode del control TreeView4
                        nodo = TreeView4.Nodes(name)
                    End If
                End If
            End If
    
            Return nodo
    
        End Function
    
        Private Sub RecuperarElementosListBox()
    
            Dim RutaAccesoArchivoLectura As String = String.Empty
    
    
            If (VarGlobal.strTipoPapel = "A4") Then
                RutaAccesoArchivoLectura = IO.Path.Combine(Application.StartupPath & "\Informes\Informes_A4\" & VarGlobal.StrCodEmpresa & ".txt")
            Else
                RutaAccesoArchivoLectura = IO.Path.Combine(Application.StartupPath & "\Informes\Informes_Carta\" & VarGlobal.StrCodEmpresa & ".txt")
            End If
    
            If RutaAccesoArchivoLectura = "" Then
                Exit Sub
            End If
    
            Using objReader As New StreamReader(RutaAccesoArchivoLectura)
    
                Dim sLine As String = String.Empty
    
                Do
                    sLine = objReader.ReadLine()
    
                    If (Not sLine Is Nothing) Then
                        ListBox1.Items.Add(sLine)
                    End If
    
                Loop Until sLine Is Nothing
    
            End Using
    
            'Recorrer los elementos existentes en el control ListBox.
            For Each item As String In ListBox1.Items
    
                ' Referenciar el objeto TreeNode del control TreeView1 cuya
                ' propiedad Name se corresponda con el valor del elemento
                ' actual del control ListBox.
                '
                Dim nodo As TreeNode = ObtenerTreeNode(item)
                If (Not nodo Is Nothing) Then
                    ' Activar el objeto TreeNode
                    nodo.Checked = True
                End If
            Next
    
        End Sub

    Y es ahí (marcado en negrita) donde me da Nothing y no encuentra el nodo que ya lo tiene

    creado: Dim nodo As TreeNode = ObtenerTreeNode(item), indicando nuevamente que item lleva

    un valor igual al nombre del nodo, pero no lo encuentra.

    He comprobado que el "Name" fuera igual tanto en la creación del nodo como en el Listbox

    del cual toma dicho nombre. Si que hago mención que esos nodos son hijos, no son padres.

    Bueno si alguien ve el error o que puede faltar o porqué falla, le rugo me lo indique.

    Un saludo.

    Gemma

    sábado, 5 de marzo de 2016 7:31

Respuestas

  • "gemma_campillo" escribió:

    > Dim nodo As TreeNode = ObtenerTreeNode(item)
    >
    > Y es ahí (marcado en negrita) donde me da Nothing y no encuentra el nodo que ya lo tiene

    Si la función ObtenerTreeNode te devuelve un valor Nothing, no me queda más que pensar en que NO EXISTE el elemento cuyo valor le has pasado a dicha función.

    Establece un punto de interrupción en la llamada a la función ObtenerTreeNode, y verifica que la variable "item" tenga un valor adecuado.

    >  Private Sub RecuperarElementosListBox()
    >
    >      Dim RutaAccesoArchivoLectura As String = String.Empty
    >
    >      If (VarGlobal.strTipoPapel = "A4") Then
    >          RutaAccesoArchivoLectura = IO.Path.Combine(Application.StartupPath & "\Informes\Informes_A4\" & VarGlobal.StrCodEmpresa & ".txt")
    >      Else
    >          RutaAccesoArchivoLectura = IO.Path.Combine(Application.StartupPath & "\Informes\Informes_Carta\" & VarGlobal.StrCodEmpresa & ".txt")
    >      End If
    >
    > Bueno si alguien ve el error o que puede faltar o porqué falla, le rugo me lo indique.

    Por cierto, ¿has revisado si el archivo de texto se ha creado satisfactoriamente en la ruta existente en la propiedad RutaAccesoArchivoLectura? Te lo pregunto porque no he leído nada al respecto en tu pregunta. Como ahí estás ejecutando

    >        If RutaAccesoArchivoLectura = "" Then
    >            Exit Sub
    >        End If

    estoy pensando que igual ni se ha creado el archivo de texto. ;-)


    Enrique Martínez Montejo
    [MS MVP - Visual Studio y Tecnologías de Desarrollo]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.


    sábado, 5 de marzo de 2016 11:54
    Moderador
  • Aunque no lo he probado, porque no dispongo en estos momentos de un formulario con cuatro TreeView, mira a ver si con el siguiente código que he hecho de prisa y corriendo, tienes suerte y resuelves el problema.

    Sustituye la función ObtenerTreeNode por ésta versión nueva:

       Private Function ObtenerTreeNode(name As String) As TreeNode
    
            Dim tvs As TreeView() = {TreeView1}
    
            ' Recorrer los cuatro controles TreeView
            '
            For Each tv As TreeView In tvs
    
                ' Recorrer todos los nodos (incluidos los hijos) existentes en el control TreeView
    
                For Each nodo As TreeNode In tv.Nodes
    
                    If (nodo.Nodes.Count > 0) Then
                        ' Existen nodos hijos.
                        For Each n As TreeNode In nodo.Nodes
                            If (n.Name.Equals(name, StringComparison.CurrentCultureIgnoreCase)) Then
                                Return n
                            End If
                        Next
    
                    Else
                        ' No existen nodos hijos.
                        If (nodo.Name.Equals(name, StringComparison.CurrentCultureIgnoreCase)) Then
                            Return nodo
                        End If
    
                    End If
    
                Next
    
            Next
    
            Return Nothing
    
        End Function

    Te advierto que esta función está diseñada específicamente para el control TreeView cuya imagen has publicado, por lo que si los otros controles TreeView tienen muchos más nodos hijos, lo más seguro es que no funcione.

    Asimismo te comento que ésta función puede que demore la ejecución del código, sobre todo si los controles TreeView están repletos de nodos, porque como habrás observado, hay que recorrer, uno a uno, cada nodo de cada control TreeView, hasta buscar uno cuya propiedad Name coincida exactamente con el valor pasado a la función.


    Enrique Martínez Montejo
    [MS MVP - Visual Studio y Tecnologías de Desarrollo]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.


    sábado, 5 de marzo de 2016 20:06
    Moderador
  • "gemma_campillo" escribió:

    > Funciona perfectamente para los nodos que nada mas tienen dos niveles ...

    Así lo comenté en mi respuesta, que la función estaba diseñada específicamente para el control TreeView cuya imagen habías publicado. Pero aunque la función la adaptemos a un control TreeView con 20.000 nodos hijos, y funcione correctamente, a mi entender NO ES LA MANERA ÓPTIMA de recorrer la multitud de nodos que puedan existir en 4 controles TreeView, ni tampoco en único control TreeView, porque lo CORRECTO es implementar una función que se llame a sí misma de manera recursiva.

    Anoche, de prisa y corriendo, cometí el error de muchos de vosotros: implementar una función con lo primero que a uno se le pasa por la mente, sin pensar en el rendimiento óptimo de la aplicación, porque recorrer 4 TreeView con multitud de nodos, eso es ralentizar la ejecución del código hasta tal punto que un cliente potencial de tu aplicación puede que "pase" totalmente de ella al ver lo que se demora la presentación del formulario Informes, o como se llame el formulario que contiene los 4 TreeView, y eso para mí, es inadmisible como bien creo que podrás comprender.

    Así que olvídate de la función ObtenerTreeNode que te indique anoche, y vamos con una versión nueva de la misma, que entiendo está mucho más optimizada que la anterior pensando en tu necesidades: que tienes un control ListBox solamente con los nombres de los nodos hijos, sin su ruta completa.

    Inserta donde proceda estos tres procedimientos:

        Private Function ObtenerTreeNode(text As String) As TreeNode
    
            ' Referenciar el objeto TreeNode del control TreeView1
            Dim nodo As TreeNode = ObtenerTreeNode(TreeView1, text)
    
            If (nodo Is Nothing) Then
                ' Referenciar el objeto TreeNode del control TreeView2
                nodo = ObtenerTreeNode(TreeView2, text)
                If (nodo Is Nothing) Then
                    ' Referenciar el objeto TreeNode del control TreeView3
                    ObtenerTreeNode(TreeView3, text)
                    If (nodo Is Nothing) Then
                        ' Referenciar el objeto TreeNode del control TreeView4
                        ObtenerTreeNode(TreeView4, text)
                    End If
                End If
            End If
    
            Return nodo
    
        End Function
    
        Private Function ObtenerTreeNode(tv As TreeView, text As String) As TreeNode
    
            If ((tv Is Nothing) OrElse String.IsNullOrWhiteSpace(text)) Then
                Return Nothing
            End If
    
            Dim lst As New List(Of TreeNode)()
    
            For Each item As TreeNode In tv.Nodes
    
                If (item.Text.Equals(text, StringComparison.CurrentCultureIgnoreCase)) Then
                    lst.Add(item)
    
                ElseIf (item.Nodes.Count > 0) Then
                    lst.AddRange(FindFirstNodeByText(item.Nodes, text))
    
                End If
    
                If (lst.Count = 1) Then
                    ' Si la lista contiene un elemento, es porque existe al
                    ' menos un nodo con el texto especificado. Abandonar el
                    ' bucle para agilizar la ejecución del código evitando
                    ' llamadas recursivas al método FindFirstNodeByText.
                    Exit For
                End If
    
            Next
    
            Return If(lst.Count = 1, lst(0), Nothing)
    
        End Function
    
        Private Function FindFirstNodeByText(nodes As TreeNodeCollection, text As String) As List(Of TreeNode)
    
            If ((nodes Is Nothing) OrElse (nodes.Count = 0) OrElse (String.IsNullOrWhiteSpace(text))) Then
                Return Nothing
            End If
    
            Dim lst As New List(Of TreeNode)()
    
            For Each item As TreeNode In nodes
    
                If (item.Text.Equals(text, StringComparison.CurrentCultureIgnoreCase)) Then
                    lst.Add(item)
    
                ElseIf (item.Nodes.Count > 0) Then
                    lst.AddRange(FindFirstNodeByText(item.Nodes, text))
    
                End If
    
                If (lst.Count = 1) Then
                    ' NOTA IMPORTANTE: Si la lista contiene un elemento, es porque existe al
                    ' menos un nodo con el texto especificado. Como solamente nos interesa
                    ' encontrar el primer nodo, no tiene sentido continuar ejecutando llamadas
                    ' recursivas al método FindFirstNodeByText, por lo que abandonamos el bucle
                    ' sin más.
                    Exit For
                End If
    
            Next
    
            Return lst
    
        End Function

    Observa que la búsqueda ya no la vamos a realizar por el valor de la propiedad Name del objeto TreeNode correspondiente, si no que la vamos a realizar por el valor de su propiedad Text.

    Cuando desees activar la casilla de verificación de aquellos nodos donde el valor de su propiedad TEXT figura en el control ListBox, a la función ObtenerTreeNode la llamarías de la misma manera que la estabas llamado hasta ahora:

            ' Recorrer los elementos existentes en el control ListBox.
            For Each item As String In ListBox1.Items
    
                ' Referenciar el objeto TreeNode donde el valor de su
                ' propiedad TEXT se corresponda con el valor del
                ' elemento actual del control ListBox.
                '
                Dim nodo As TreeNode = ObtenerTreeNode(item)
    
                If (Not nodo Is Nothing) Then
                    ' Activar el objeto TreeNode
                    nodo.Checked = True
                End If
    
            Next

    Quiero dejar bien claro que el código fuente de los procedimientos descritos más arriba están diseñados exclusivamente para las necesidades de la aplicación de Gemma Campillo, por lo que cualquier otro usuario que esté buscando una manera de recorrer recursivamente los nodos de un control TreeView, tendría que adaptar los procedimientos a sus propias necesidades (no sirve cortar/pegar), ya que como indico en los comentarios de los mismos, no tiene sentido continuar ejecutando llamadas recursivas cuando ya se ha encontrado un nodo donde el valor de su propiedad Text se corresponde con el valor que estamos buscando, lo que quiere decir que los valores de las propiedades Text de los nodos de todos los 4 controles TreeView SON ÚNICOS, como si se tratase de la clave principal de una tabla cualquiera de una base de datos, porque digo yo que serán VALORES ÚNICOS. ¿O se repiten?


    Enrique Martínez Montejo
    [MS MVP - Visual Studio y Tecnologías de Desarrollo]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.


    domingo, 6 de marzo de 2016 9:45
    Moderador
  • "gemma_campillo" escribió:

    > Por ello creo que lo que hace es no leer correctamente el valor de los nodos hijo,
    > en este caso "Activo No Corriente" y siempre me va a dar nothing.

    Observando el TreeView del formulario Informes que has publicado (que digo yo, también lo podrías haber publicado ayer), te diré que crees bien, porque para que se referencie el nodo correspondiente al valor "Activo No Corriente", tendrás que referenciar primero su nodo padre (Movimientos Activo), que es el nodo primario del control TreeViewXXX, que es el que busca la función ObtenerTreeNode que te comenté ayer, ya que ignoraba por completo que un nodo podría tener a su vez varios nodos hijos.

    Lo siento, pero en estos momentos no te puedo ofrecer una solución, porque habría que estar buscando también entre los subnodos que puede tener un nodo primario del control TreeView, y si dices que tan sólo conoces el nombre del nodo final (Activo No Corriente), pues ahora mismo no doy con ninguna solución. :-(


    Enrique Martínez Montejo
    [MS MVP - Visual Studio y Tecnologías de Desarrollo]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.

    • Marcado como respuesta gemma_campillo sábado, 5 de marzo de 2016 19:33
    sábado, 5 de marzo de 2016 19:17
    Moderador

Todas las respuestas

  • "gemma_campillo" escribió:

    > Dim nodo As TreeNode = ObtenerTreeNode(item)
    >
    > Y es ahí (marcado en negrita) donde me da Nothing y no encuentra el nodo que ya lo tiene

    Si la función ObtenerTreeNode te devuelve un valor Nothing, no me queda más que pensar en que NO EXISTE el elemento cuyo valor le has pasado a dicha función.

    Establece un punto de interrupción en la llamada a la función ObtenerTreeNode, y verifica que la variable "item" tenga un valor adecuado.

    >  Private Sub RecuperarElementosListBox()
    >
    >      Dim RutaAccesoArchivoLectura As String = String.Empty
    >
    >      If (VarGlobal.strTipoPapel = "A4") Then
    >          RutaAccesoArchivoLectura = IO.Path.Combine(Application.StartupPath & "\Informes\Informes_A4\" & VarGlobal.StrCodEmpresa & ".txt")
    >      Else
    >          RutaAccesoArchivoLectura = IO.Path.Combine(Application.StartupPath & "\Informes\Informes_Carta\" & VarGlobal.StrCodEmpresa & ".txt")
    >      End If
    >
    > Bueno si alguien ve el error o que puede faltar o porqué falla, le rugo me lo indique.

    Por cierto, ¿has revisado si el archivo de texto se ha creado satisfactoriamente en la ruta existente en la propiedad RutaAccesoArchivoLectura? Te lo pregunto porque no he leído nada al respecto en tu pregunta. Como ahí estás ejecutando

    >        If RutaAccesoArchivoLectura = "" Then
    >            Exit Sub
    >        End If

    estoy pensando que igual ni se ha creado el archivo de texto. ;-)


    Enrique Martínez Montejo
    [MS MVP - Visual Studio y Tecnologías de Desarrollo]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.


    sábado, 5 de marzo de 2016 11:54
    Moderador
  • Hola Enrique:

    Crea perfectamente el archivo de texto en la carpeta indicada y también lo lee perfectamente cuando cargo el método: RecuperarElementosListBox.

    Después cuando me indicas que establezca un punto de interrupción en la función "ObtenerTreeNode" para ver si la variable "item" o sea "name" lleva algún valor, efectivamente lo lleva, aunque continúe poniendo que el valor de "nodo" es nothing. (no lo entiendo porqué es así).

    Mira en la imagen en lo remarcado se ve que lleva el valor name es "Archivo No Corriente", pues bien, nodo tendría que ser igual al valor Name y es nothing.

    Pero mira, si me posiciono sobre Treeview1, me da como resultado: {System.Windows.Forms.TreeView, Nodes.Count: 2, Nodes[0]: TreeNode: Balances        }, es decir, está cogiendo el rango de los nodos padre del treeview1, en este caso "Balances" y eso es lo mismo en las dos veces que se llama a dicha función como se ve en la siguiente imagen:

    Por ello creo que lo que hace es no leer correctamente el valor de los nodos hijo, en este caso "Activo No Corriente" y siempre me va a dar nothing.

    Voy a ir viendo si como se podría hacer para que lea el valor del nodo hijo y no el del padre.

    Bueno maestro, muchas gracias por tu ayuda como siempre.

    Recibe un fuerte abrazo.

    Gemma

    sábado, 5 de marzo de 2016 16:02
  • "gemma_campillo" escribió:

    > Por ello creo que lo que hace es no leer correctamente el valor de los nodos hijo,
    > en este caso "Activo No Corriente" y siempre me va a dar nothing.

    Observando el TreeView del formulario Informes que has publicado (que digo yo, también lo podrías haber publicado ayer), te diré que crees bien, porque para que se referencie el nodo correspondiente al valor "Activo No Corriente", tendrás que referenciar primero su nodo padre (Movimientos Activo), que es el nodo primario del control TreeViewXXX, que es el que busca la función ObtenerTreeNode que te comenté ayer, ya que ignoraba por completo que un nodo podría tener a su vez varios nodos hijos.

    Lo siento, pero en estos momentos no te puedo ofrecer una solución, porque habría que estar buscando también entre los subnodos que puede tener un nodo primario del control TreeView, y si dices que tan sólo conoces el nombre del nodo final (Activo No Corriente), pues ahora mismo no doy con ninguna solución. :-(


    Enrique Martínez Montejo
    [MS MVP - Visual Studio y Tecnologías de Desarrollo]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.

    • Marcado como respuesta gemma_campillo sábado, 5 de marzo de 2016 19:33
    sábado, 5 de marzo de 2016 19:17
    Moderador
  • Hola maestro:

    Es que a veces vengo con cosas raras, ya lo sé yo, ya.

    Bueno, no te preocupes que al final algo haré.

    Enrique, muchas gracias por todo como siempre. Eres mi genio.

    Un fuerte abrazo.

    Gemma


    sábado, 5 de marzo de 2016 19:35
  • Aunque no lo he probado, porque no dispongo en estos momentos de un formulario con cuatro TreeView, mira a ver si con el siguiente código que he hecho de prisa y corriendo, tienes suerte y resuelves el problema.

    Sustituye la función ObtenerTreeNode por ésta versión nueva:

       Private Function ObtenerTreeNode(name As String) As TreeNode
    
            Dim tvs As TreeView() = {TreeView1}
    
            ' Recorrer los cuatro controles TreeView
            '
            For Each tv As TreeView In tvs
    
                ' Recorrer todos los nodos (incluidos los hijos) existentes en el control TreeView
    
                For Each nodo As TreeNode In tv.Nodes
    
                    If (nodo.Nodes.Count > 0) Then
                        ' Existen nodos hijos.
                        For Each n As TreeNode In nodo.Nodes
                            If (n.Name.Equals(name, StringComparison.CurrentCultureIgnoreCase)) Then
                                Return n
                            End If
                        Next
    
                    Else
                        ' No existen nodos hijos.
                        If (nodo.Name.Equals(name, StringComparison.CurrentCultureIgnoreCase)) Then
                            Return nodo
                        End If
    
                    End If
    
                Next
    
            Next
    
            Return Nothing
    
        End Function

    Te advierto que esta función está diseñada específicamente para el control TreeView cuya imagen has publicado, por lo que si los otros controles TreeView tienen muchos más nodos hijos, lo más seguro es que no funcione.

    Asimismo te comento que ésta función puede que demore la ejecución del código, sobre todo si los controles TreeView están repletos de nodos, porque como habrás observado, hay que recorrer, uno a uno, cada nodo de cada control TreeView, hasta buscar uno cuya propiedad Name coincida exactamente con el valor pasado a la función.


    Enrique Martínez Montejo
    [MS MVP - Visual Studio y Tecnologías de Desarrollo]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.


    sábado, 5 de marzo de 2016 20:06
    Moderador
  • Hola Enrique:

    Funciona perfectamente para los nodos que nada mas tienen dos niveles, hablamos del Treeview1, donde no los coge es cuando el nodo tiene 3 niveles que el máximo que pueden tener, hay nodos padre con 2 niveles y nodos padre con 3 niveles: Balances > Balances y Resultados- Vista "Normal" > Balance de Situación. Siempre el que estará marcado en este caso de 3 niveles es el último hijo: Balance de Situación.

    Ahora por ejemplo lo he probado con 2 niveles: Ratios y Comparativa Sectorial > Ratios de Liquidez y Endeudamiento y en este caso va perfecto, marca correctamente el checkbox de Ratios de Liquidez y Endeudamiento. O sea, para 2 niveles para entendernos funciona perfectamente.

    Bueno querido amigo, no entiendo como tienes esa paciencia en ayudarme  en cosas raras, pero si ts te profeso mi más profundo agradecimiento por tu infinito interés.

    Gracias maestro.

    Un fuerte abrazo.

    Gemma


    sábado, 5 de marzo de 2016 20:24
  • "gemma_campillo" escribió:

    > Funciona perfectamente para los nodos que nada mas tienen dos niveles ...

    Así lo comenté en mi respuesta, que la función estaba diseñada específicamente para el control TreeView cuya imagen habías publicado. Pero aunque la función la adaptemos a un control TreeView con 20.000 nodos hijos, y funcione correctamente, a mi entender NO ES LA MANERA ÓPTIMA de recorrer la multitud de nodos que puedan existir en 4 controles TreeView, ni tampoco en único control TreeView, porque lo CORRECTO es implementar una función que se llame a sí misma de manera recursiva.

    Anoche, de prisa y corriendo, cometí el error de muchos de vosotros: implementar una función con lo primero que a uno se le pasa por la mente, sin pensar en el rendimiento óptimo de la aplicación, porque recorrer 4 TreeView con multitud de nodos, eso es ralentizar la ejecución del código hasta tal punto que un cliente potencial de tu aplicación puede que "pase" totalmente de ella al ver lo que se demora la presentación del formulario Informes, o como se llame el formulario que contiene los 4 TreeView, y eso para mí, es inadmisible como bien creo que podrás comprender.

    Así que olvídate de la función ObtenerTreeNode que te indique anoche, y vamos con una versión nueva de la misma, que entiendo está mucho más optimizada que la anterior pensando en tu necesidades: que tienes un control ListBox solamente con los nombres de los nodos hijos, sin su ruta completa.

    Inserta donde proceda estos tres procedimientos:

        Private Function ObtenerTreeNode(text As String) As TreeNode
    
            ' Referenciar el objeto TreeNode del control TreeView1
            Dim nodo As TreeNode = ObtenerTreeNode(TreeView1, text)
    
            If (nodo Is Nothing) Then
                ' Referenciar el objeto TreeNode del control TreeView2
                nodo = ObtenerTreeNode(TreeView2, text)
                If (nodo Is Nothing) Then
                    ' Referenciar el objeto TreeNode del control TreeView3
                    ObtenerTreeNode(TreeView3, text)
                    If (nodo Is Nothing) Then
                        ' Referenciar el objeto TreeNode del control TreeView4
                        ObtenerTreeNode(TreeView4, text)
                    End If
                End If
            End If
    
            Return nodo
    
        End Function
    
        Private Function ObtenerTreeNode(tv As TreeView, text As String) As TreeNode
    
            If ((tv Is Nothing) OrElse String.IsNullOrWhiteSpace(text)) Then
                Return Nothing
            End If
    
            Dim lst As New List(Of TreeNode)()
    
            For Each item As TreeNode In tv.Nodes
    
                If (item.Text.Equals(text, StringComparison.CurrentCultureIgnoreCase)) Then
                    lst.Add(item)
    
                ElseIf (item.Nodes.Count > 0) Then
                    lst.AddRange(FindFirstNodeByText(item.Nodes, text))
    
                End If
    
                If (lst.Count = 1) Then
                    ' Si la lista contiene un elemento, es porque existe al
                    ' menos un nodo con el texto especificado. Abandonar el
                    ' bucle para agilizar la ejecución del código evitando
                    ' llamadas recursivas al método FindFirstNodeByText.
                    Exit For
                End If
    
            Next
    
            Return If(lst.Count = 1, lst(0), Nothing)
    
        End Function
    
        Private Function FindFirstNodeByText(nodes As TreeNodeCollection, text As String) As List(Of TreeNode)
    
            If ((nodes Is Nothing) OrElse (nodes.Count = 0) OrElse (String.IsNullOrWhiteSpace(text))) Then
                Return Nothing
            End If
    
            Dim lst As New List(Of TreeNode)()
    
            For Each item As TreeNode In nodes
    
                If (item.Text.Equals(text, StringComparison.CurrentCultureIgnoreCase)) Then
                    lst.Add(item)
    
                ElseIf (item.Nodes.Count > 0) Then
                    lst.AddRange(FindFirstNodeByText(item.Nodes, text))
    
                End If
    
                If (lst.Count = 1) Then
                    ' NOTA IMPORTANTE: Si la lista contiene un elemento, es porque existe al
                    ' menos un nodo con el texto especificado. Como solamente nos interesa
                    ' encontrar el primer nodo, no tiene sentido continuar ejecutando llamadas
                    ' recursivas al método FindFirstNodeByText, por lo que abandonamos el bucle
                    ' sin más.
                    Exit For
                End If
    
            Next
    
            Return lst
    
        End Function

    Observa que la búsqueda ya no la vamos a realizar por el valor de la propiedad Name del objeto TreeNode correspondiente, si no que la vamos a realizar por el valor de su propiedad Text.

    Cuando desees activar la casilla de verificación de aquellos nodos donde el valor de su propiedad TEXT figura en el control ListBox, a la función ObtenerTreeNode la llamarías de la misma manera que la estabas llamado hasta ahora:

            ' Recorrer los elementos existentes en el control ListBox.
            For Each item As String In ListBox1.Items
    
                ' Referenciar el objeto TreeNode donde el valor de su
                ' propiedad TEXT se corresponda con el valor del
                ' elemento actual del control ListBox.
                '
                Dim nodo As TreeNode = ObtenerTreeNode(item)
    
                If (Not nodo Is Nothing) Then
                    ' Activar el objeto TreeNode
                    nodo.Checked = True
                End If
    
            Next

    Quiero dejar bien claro que el código fuente de los procedimientos descritos más arriba están diseñados exclusivamente para las necesidades de la aplicación de Gemma Campillo, por lo que cualquier otro usuario que esté buscando una manera de recorrer recursivamente los nodos de un control TreeView, tendría que adaptar los procedimientos a sus propias necesidades (no sirve cortar/pegar), ya que como indico en los comentarios de los mismos, no tiene sentido continuar ejecutando llamadas recursivas cuando ya se ha encontrado un nodo donde el valor de su propiedad Text se corresponde con el valor que estamos buscando, lo que quiere decir que los valores de las propiedades Text de los nodos de todos los 4 controles TreeView SON ÚNICOS, como si se tratase de la clave principal de una tabla cualquiera de una base de datos, porque digo yo que serán VALORES ÚNICOS. ¿O se repiten?


    Enrique Martínez Montejo
    [MS MVP - Visual Studio y Tecnologías de Desarrollo]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.


    domingo, 6 de marzo de 2016 9:45
    Moderador
  • Querido maestro, buenos días:

    Funciona a la perfección, tanto en nodos de 3 niveles como en dos niveles, treviews diferentes, etc, etc. Lo coge todo a la primera y de una forma rapidísima, no demora nada, se carga el form y ya aparecen marcados los checkbox de los informes que lleva el listbox desde el archivo de texto.

    !Ah! querido Enrique, lo mismo me daba buscarlos por la propiedad Name o por la propiedad Text, tengo puesto lo mismo en cada una y lo hace sin problemas en el idioma que lo ponga.

    No entiendo como se pueden lograr esas cosas, pero para eso están los puñeteros genios.

    Cada vez tengo más claro que lo que se pasa por la cabeza con los conocimientos suficientes se puede hacer, eso para mi ha sido una demostración de ello.

    Bueno maestro, infinitas gracias como te las he dado siempre, me siento orgullosa de tenerte como maestro. Son unos cuantos años ya.

    Querido amigo y maestro, un fuertísimo abrazo de la pesada de tu amiga.

    Gemma


    domingo, 6 de marzo de 2016 10:32