3.5.07

Tips para el TreeView

Estoy empezando a trabajar con una vieja aplicación hecha en Visual Basic 6, para convertirla en Visual Basic .Net y uno de los primeros problemas con los que me he topado es con el uso del control TreeView, que difiere un poco con el viejo VB6, en cuanto a su llenado y en cuanto a encontrar un nodo dentro del "arbol" con el sólo dato de su clave de acceso.

Voy a dejar aquí dos ejemplos, el primero de como llenar un TreeView a partir de un objeto DataTable, que contiene la lectura de una tabla de mi base de datos. Y el segundo ejemplo es como encontrar un nodo conociendo su clave (ahora NAME).

Para el primer ejemplo, suponemos que tienes una conexión establecida a una base de datos, que puede ser mySQL, y que en ella tenemos una tabla que contiene todas las opciones de menú y con ella llenaremos el TreeView. De esta tabla menuKey es el "nombre del menú" (su clave), menuDescription es el texo que aparecerá a la vista del usuario del TreeView, y menuKeyParent es el "nombre del menú" del cual "cuelga" el menú descripto en esta fila.

Para esta tarea, también, me valgo del objeto DataView que permite filtrar los datos del DataTable. Además, observese que la función InsertTVMenuItems es llamada recursivamente. Sobre esto sólo hay que tener en cuenta la cantidad de llamadas recursivas que se van a hacer sin haber retornado (return) de ellas, puesto que en cada llamada el programa creará nuevas variables locales, independientes de las ya creadas, con el consecuente consumo de memoria. Obviamente, cada retorno provoca la liberación de recursos.

'rellena el tree view
Private Sub RellenaTreeView1()
   Dim Itm As New TreeNode()
   Dim adapter As New MySqlDataAdapter()
   Dim data As New DataTable()
   Dim Sql As String

   System.Windows.Forms.Cursor.Current = Cursors.WaitCursor

   TreeView1.Nodes.Clear()
   TreeView1.Refresh()

   Sql = "SELECT menuKey,"
   Sql = Sql & "menuDescription,"
   Sql = Sql & "menuKeyParent"
   Sql = Sql & " FROM Menu"
   Sql = Sql & " ORDER BY menuDescription"
   adapter.SelectCommand = New MySqlCommand(Sql, dbConn)
   adapter.Fill(data)

   If data.Rows.Count = 0 Then
      data.Dispose()
      adapter.Dispose()
      System.Windows.Forms.Cursor.Current = Cursors.Default
      Exit Sub
   End If

   'comienzo con "0" que es el menuKeyParent que cuelga del root del treeview
   InsertTVMenuItems("0", Nothing, data)

   'libero recursos
   data.Dispose()
   adapter.Dispose()
   System.Windows.Forms.Cursor.Current = Cursors.Default
End Sub
Private Sub InsertTVMenuItems(ByVal keyParent As String, _
                              ByVal nodoPadre As TreeNode, _
                               ByVal data As DataTable)

   Dim dataFilter As DataView

   'Crear un DataView con los Nodos que dependen del Nodo padre pasado como parámetro.
   dataFilter = New DataView(data)

   dataFilter.RowFilter = String.Format("menuKeyParent ='{0}'", keyParent.ToString())

   'Agregar al TreeView los nodos Hijos que se han obtenido en el DataView.
   For Each dataRowCurrent As DataRowView In dataFilter

      Dim nuevoNodo As New TreeNode
      With nuevoNodo
         .Text = dataRowCurrent("menuDescription").ToString().Trim()
         .Name = dataRowCurrent("menuKey").ToString().Trim()
      End With

      'si el parámetro nodoPadre es nulo es porque es la primera llamada, son los Nodos
      'del primer nivel que no dependen de otro nodo.
      If nodoPadre Is Nothing Then
         TreeView1.Nodes.Add(nuevoNodo)
      Else
         'se añade el nuevo nodo al nodo padre.
         nodoPadre.Nodes.Add(nuevoNodo)
      End If

      'Llamada recurrente al mismo método para agregar los Hijos del Nodo recién agregado.
      InsertTVMenuItems(dataRowCurrent("menuKey").ToString(), nuevoNodo, data)
   Next dataRowCurrent
End Sub

Sencillo, ¿no?

Bueno, ahora que tenemos nuestro TreeView llenito de datos, se nos viene en ganas de encontrar uno cuya clave (Name) sea X. Para ello definimos una variable privada a nivel de formulario donde almacenaremos el nodo encontrado durante la búsqueda.

Public Class myForm
   Private myNode As TreeNode

Luego escribimos el procedimiento que hará la búsqueda, que también opera recursivamente

'busca un nodo en el treeview
   Private Sub SearchNodeRecursive(ByVal nodes As TreeNodeCollection, ByVal key As String)
      For Each n As TreeNode In nodes
         If n.Name.Trim() = key.Trim() Then
            myNode = n
            Exit Sub
         End If
         SearchNodeRecursive(n.Nodes, key)
      Next
   End Sub

Y por último hacemos la llamada al procedimiento SearchNodeRecursive

Private Sub myProc()
      myNode = Nothing
      SearchNodeRecursive(TreeView1.Nodes, "clave_a_buscar")
      If IsNothing(myNode) Then
         MsgBox("No encontrado")
      Else
         MsgBox(myNode.Text.Trim())
      End If
End Sub

De nuevo... tengan en cuenta la cantidad de llamadas recursivas, sobre todo si el programa se instalará en PC's con escasa memoria.

3 Comentarios:

Anónimo dice...

Muy bueno su articulo, ahora necesito saber como puedo llenar el datatable desde una base de datos de una tabla de solo dos columnas por ej, con campo_hijo y campo_padre. Como seria la FUNCION que llenaria el datatable para poder enviar ese datatable al trevieew?
Urge.
Muchas Gracias de antemano.
Jorge

Julio González Seara dice...

No se si llego a entender tu pregunta, pero en el ejemplo se usan tres campos pues yo agrego la descripción al treeview, pero si obvias la descripción, el ejemplo usa dos campos: un hijo y un padre.
La descripción puede ser lo que tu necesites, esté o no en la tabla. Lo común es que la descripción acompañe a los códigos.

Salu2

Julio Ponce dice...

Excelente, muy excelente las funciones.

ahora bien para poder seleccionar multiples nodos padres e hijos tendras alguna función ???

mi correo es julioponce@hotmail.com

saludos y gracias de antemano,