Buen tema, por lo menos interesante. El archiconocido control TreeView puede sernos de mucha ayuda en nuestros proyectos, aunque primero debamos "renegar" un poco para comprenderlo y utilizarlo. Mal traducido, desde ya, sería el control "Vista de Arbol", lo cual desde el comienzo nos habla de la fuerte jerarquización que emplea este objeto con sus elementos. Esta Vista de Arbol, y para que todos nos pongamos en la misma línea, no es otro control que el observado en la parte izquierda del Explorador de Windows.
Dentro del mismo VBA, en la ventana Explorador de Proyectos, también tenemos uno:
[+/-] Ver el resto / OcultarYa sabemos entonces de que hablamos al mencionar al control TreeView. Este control acepta la incorporación de nodos (nodes), que son los íconos que se expanden (o no) dentro del él. Cabe aclarar que, a su vez, para VBA cada nodo es un objeto en si mismo, con sus propiedades, métodos y eventos. Al trabajar con TreeView deberemos tener en cuenta que arrojaremos mano sobre un objeto (el TreeView) que a su vez es contenedor de muchos otros objetos: sus nodos.
Si miramos la imagen superior observaremos una típica estructura jerárquica del TreeView: el nodo "padre" (vbaproyect), tiene un "hijo": microsoft excel objectos, el que a su vez contiene 4 nodos "hijos": hoja1, hoja2, hoja3 y thisworkbook. Toda una generación: hijos, padres y nietos. Y este arbol genealógico podría continuar hasta el infinito, como practicamente sucede con los directorios del Explorador de Windows.
Será cuestión entonces de ir viendo como programamos este control. Abrimos un nuevo libro y nos vamos al Editor de VBA (Alt + F11), hacemos click derecho sobre el "Cuadro de herramientas" y luego en "Controles adicionales", ya que por defecto este objeto no se muestra. Del formulario emergente buscamos a TreeView y seleccionamos la casilla respectiva:
Al colocar el control sobre el formulario (o también podríamos usarlo directamente en una hoja) este será el aspecto que presentará:
un nodo con dos hijos y un nodo solo. cabe destacar que la leyenda (text) presente en cada uno de ellos, "Sample node" (nodo de ejemplo) no será visible en tiempo de ejecución.
Para darle un sentido práctico al ejemplo e intentar que sea de fácil lectura y aprendizaje, haré de cuenta que una concesionaria de autos me pide un formulario para visualizar todas las marcas y sus respectivos modelos, con las características de cada uno de ellos (tipo de motor, frenos, confort, etc, etc).
Cada hoja del libro tendrá el nombre de una marca:
cada hoja se corresponde a una determinada marca de autos
Y en cada hoja, para estructurar correctamente el proyecto, existirá una tabla con los modelos y características:
una pequeña tabla, cuyo formato se repetirá en todas las hojas. los modelos están en la columna A.
Podremos ir deduciendo la estructura que tendrá nuestro TreeView:
jerarquía de nuestro TreeView.
Iremos por partes, como siempre. Un nodo se agrega al TreeView mediante el método Add, el cual posee 5 argumentos que debemos manejar bien. Ejemplo:
TreeView1.Nodes.Add(Relative, Relationship , Key, Text, Image)
Relative: quien es el padre del nodo que estoy agregando.
Relationship: tipo de relación existente con el nodo padre
Key: clave. una cadena string que nos servirá para almacenar información adicional. La Key debe ser única y ningún otro nodo puede tener una igual.
Text: el texto que se mostrará
Image: icono del nodo. lo veremos sobre el final del post.
Teniendo en cuenta los argumentos arriba citados (los cuales no son obligatorios), puedo dar de alta todas las marcas de autos de esta forma:
Private Sub UserForm_Initialize() 'recorro las hojas del libro: For Each Hoja In ActiveWorkbook.Sheets 'y voy creando un nodo padre con cada una de ellas. 'de Key le pongo la marca del auto y como text también: Set Nodo1 = TreeView1.Nodes.Add(, , Hoja.Name, Hoja.Name) Next Hoja Set Hoja = Nothing Set Nodo1 = Nothing End Sub
ya tenemos todas las marcas dentro del TreeView.
Continuaré mostrando la forma de incorporar a cada marca sus modelos:
Private Sub UserForm_Initialize() 'recorro las hojas del libro: For Each Hoja In ActiveWorkbook.Sheets 'y voy creando un nodo padre con cada una de ellas. 'de Key le pongo la marca del auto y como text también: Set Nodo1 = TreeView1.Nodes.Add(, , Hoja.Name, Hoja.Name) 'las marcas de autos comienzan en la segunda fila de cada hoja. Fila = 2 'recorro con un bucle, dando de alta en el control solo las 'filas que contienen datos: While Sheets(Hoja.Name).Cells(Fila, 1).Value <> "" 'almaceno el modelo: Modelo = Sheets(Hoja.Name).Cells(Fila, 1).Value 'y lo doy de alta en el treeview. noten que aquí paso 'el primer argumento "Relative", indicando que el Nodo1 'es el padre del nodo que estoy creando. El segundo ar 'gumento (relationship) es tvwChild, es decir, "hijo" Set Nodo2 = TreeView1.Nodes.Add(Nodo1, tvwChild, , Modelo) Fila = Fila + 1 Wend Next Hoja Set Hoja = Nothing Set Nodo1 = Nothing Set Nodo2 = Nothing End Sub
va queriendo.....
Me voy a tomar 10 mintuos para calentar el agua y cebarme unos buenos mates, en un día lluvioso. Luego de eso terminaré de completar las tablas de cada maca de auto y correré de nuevo la macro, levantando la imagen resultante.
definitivamente va tomando forma.
Deberemos ahora incorporar a cada modelo sus caracteristicas:
Private Sub UserForm_Initialize() 'recorro las hojas del libro: For Each Hoja In ActiveWorkbook.Sheets 'y voy creando un nodo padre con cada una de ellas. 'de Key le pongo la marca del auto y como text también: Set Nodo1 = TreeView1.Nodes.Add(, , Hoja.Name, Hoja.Name) 'las marcas de autos comienzan en la segunda fila de cada hoja. Fila = 2 'recorro con un bucle, dando de alta en el control solo las 'filas que contienen datos: While Sheets(Hoja.Name).Cells(Fila, 1).Value <> "" 'almaceno el modelo: Modelo = Sheets(Hoja.Name).Cells(Fila, 1).Value 'y lo doy de alta en el treeview. noten que aquí paso 'el primer argumento "Relative", indicando que el Nodo1 'es el padre del nodo que estoy creando. El segundo ar 'gumento (relationship) es tvwChild, es decir, "hijo" Set Nodo2 = TreeView1.Nodes.Add(Nodo1, tvwChild, , Modelo) 'recorro la tabla, agregando las caracteristicas colu = 2 While Sheets(Hoja.Name).Cells(Fila, colu) <> "" 'armo el texto a mostrar: Cadena = Sheets(Hoja.Name).Cells(1, colu) & ": " _ & Sheets(Hoja.Name).Cells(Fila, colu) 'seteo el nuevo nodo, indicando que su padre es el nodo2 Set Nodo3 = TreeView1.Nodes.Add(Nodo2, tvwChild, , Cadena) colu = colu + 1 Wend Fila = Fila + 1 Wend Next Hoja Set Hoja = Nothing Set Nodo1 = Nothing Set Nodo2 = Nothing End Sub
falta aún, pero vamos avanzando.
Puede que la forma en la que recorro las hojas y tablas no queden muy en claro, pero realmente no importa. Aquí el punto a destacar es: doy de alta un nodo, por ejemplo Nodo1. Si quiero agregarle un "hijo" no debo omitir, al dar de alta al nuevo nodo, indicarle quien es el padre (en este caso Nodo1) y especificar el segundo argumento en tvwChild, aclarando así que se trata de un "hijo directo".
Voy a colocar el mismo código de arriba, pero incorporando una propiedad muy útil que presentan los nodos: el Tag. Esta nos servirá para almacenar información de cualquier tipo y será de gran utilidad en el manejo posterior de los nodos. En este ejemplo especificaré que jerarquía ocupa cada nodo en el TreeView:
Private Sub UserForm_Initialize() 'recorro las hojas del libro: For Each Hoja In ActiveWorkbook.Sheets 'y voy creando un nodo padre con cada una de ellas. 'de Key le pongo la marca del auto y como text también: Set Nodo1 = TreeView1.Nodes.Add(, , Hoja.Name, Hoja.Name) 'indico que el nodo refleja la marca del auto Nodo1.Tag = "marca" 'las marcas de autos comienzan en la segunda fila de cada hoja. Fila = 2 'recorro con un bucle, dando de alta en el control solo las 'filas que contienen datos: While Sheets(Hoja.Name).Cells(Fila, 1).Value <> "" 'almaceno el modelo: Modelo = Sheets(Hoja.Name).Cells(Fila, 1).Value 'y lo doy de alta en el treeview. noten que aquí paso 'el primer argumento "Relative", indicando que el Nodo1 'es el padre del nodo que estoy creando. El segundo ar 'gumento (relationship) es tvwChild, es decir, "hijo" Set Nodo2 = TreeView1.Nodes.Add(Nodo1, tvwChild, , Modelo) 'indico que el nodo hace referencia al modelo del auto Nodo2.Tag = "modelo" 'recorro la tabla, agregando las caracteristicas colu = 2 While Sheets(Hoja.Name).Cells(Fila, colu) <> "" 'armo el texto a mostrar: Cadena = Sheets(Hoja.Name).Cells(1, colu) & ": " _ & Sheets(Hoja.Name).Cells(Fila, colu) 'seteo el nuevo nodo, indicando que su padre es el nodo2 Set Nodo3 = TreeView1.Nodes.Add(Nodo2, tvwChild, , Cadena) 'indico que el nodo es una característica Nodo3.Tag = "caracteristica" colu = colu + 1 Wend Fila = Fila + 1 Wend Next Hoja Set Hoja = Nothing Set Nodo1 = Nothing Set Nodo2 = Nothing Set Nodo3 = Nothing End Sub
¿Las ventajas de haber hecho uso de la propiedad Tag? Muy sencillo: cuando posteriormente los usuarios utilicen el programa y hagan click sobre un nodo, estaré en condiciones de saber que tipo de nodo seleccionó (marca, modelo o características) y así manejar si dicho nodo puede ser borrado, editado, etc, etc, con solo invocar a Tag y conocer su valor. Tag es "invisible", o sea que no aparece en ningún lado para el usuario, es como una "etiqueta" que le pego al nodo para consultarla luego.
Hablemos un poco del parámetro Image. El control TreeView es incapaz de almacenar imágenes o cargarlas desde el exterior. ¿Como hacemos entonces para que muestre sus característicos íconos? Valiéndonos de un control adicional: el ImageList (lista de imágenes). el cual tampoco viene por defecto en el Cuadro de herramientas, así que deberemos buscarlo e incorporarlo como lo hicimos con el TreeView.
Es contenedor de imágenes nos permitirá cargar íconos dentro de el, pudiendo especificar los pixeles (16x16, 32x32, 48x48 o personalizado). Cada imagen será invocada luego por su posición, por el orden en el cual la incluímos dentro del control. Si pusimos 3 íconos (rojo, azul y verde), para luego "llamar" al color azul deberemos usar el numero 2, ya que es la segunda imagen dentro del ImageList. Y así sucesivamente.
Este es el control:
si, ese pequeño cuadradito, el cual es invisible en tiempo de ejecución.
Nos vamos ahora a la ventana de propiedades del ImageList y hacemos click sobre el item (Personalizado):
los tres íconos que seleccioné: el auto será la imagen 1, la flecha la imagen 2 y el grafico de barras la imagen 3
Vean como ligo el ImageList al TreeView mediante código y la forma en que paso el 5 parámetro del método Add (Image):
Private Sub UserForm_Initialize() 'vinculo el imagelist al treeview Set TreeView1.ImageList = ImageList1 'recorro las hojas del libro: For Each Hoja In ActiveWorkbook.Sheets 'y voy creando un nodo padre con cada una de ellas. 'de Key le pongo la marca del auto y como text también: Set Nodo1 = TreeView1.Nodes.Add(, , Hoja.Name, Hoja.Name, 1) 'indico que el nodo refleja la marca del auto Nodo1.Tag = "marca" 'las marcas de autos comienzan en la segunda fila de cada hoja. Fila = 2 'recorro con un bucle, dando de alta en el control solo las 'filas que contienen datos: While Sheets(Hoja.Name).Cells(Fila, 1).Value <> "" 'almaceno el modelo: Modelo = Sheets(Hoja.Name).Cells(Fila, 1).Value 'y lo doy de alta en el treeview. noten que aquí paso 'el primer argumento "Relative", indicando que el Nodo1 'es el padre del nodo que estoy creando. El segundo ar 'gumento (relationship) es tvwChild, es decir, "hijo" Set Nodo2 = TreeView1.Nodes.Add(Nodo1, tvwChild, , Modelo, 2) 'indico que el nodo hace referencia al modelo del auto Nodo2.Tag = "modelo" 'recorro la tabla, agregando las caracteristicas colu = 2 While Sheets(Hoja.Name).Cells(Fila, colu) <> "" 'armo el texto a mostrar: Cadena = Sheets(Hoja.Name).Cells(1, colu) & ": " _ & Sheets(Hoja.Name).Cells(Fila, colu) 'seteo el nuevo nodo, indicando que su padre es el nodo2 Set Nodo3 = TreeView1.Nodes.Add(Nodo2, tvwChild, , Cadena, 3) 'indico que el nodo es una característica Nodo3.Tag = "caracteristica" colu = colu + 1 Wend Fila = Fila + 1 Wend Next Hoja Set Hoja = Nothing Set Nodo1 = Nothing Set Nodo2 = Nothing Set Nodo3 = Nothing End Sub
Y logramos incorporar imágenes a nuestro TreeView:
Nodo3.Tag = Hoja.Name & "!" & Sheets(Hoja.Name).Cells(Fila, colu).Address
Explicación: al nombre de la hoja le concateno el signo ! y luego la dirección de la celda desde donde está cargando el dato.
En la ventana de código del TreeView utilizaremos el evento KeyDown para borrar esa característica... y va con yapa: presionando Enter nos permitirá llamar a un InputBox para modificar un valor existente:
Private Sub TreeView1_KeyDown(KeyCode As Integer, ByVal Shift As Integer) With TreeView1.SelectedItem 'aqui una de las ventajas de tener bien definido el Tag. si este es 'distinto a marca y modelo, entonces es una caracteristica: If .Tag <> "marca" And .Tag <> "modelo" Then 'si la tecla presionada es la de borrar: If KeyCode = vbKeyDelete Then 'borro el dato en excel Range(.Tag).ClearContents 'y quito el nodo TreeView1.Nodes.Remove (.Index) ElseIf KeyCode = vbKeyReturn Then 'si presiona la tecla Enter, llamo a un inputbox que me permi 'tirá modificar el dato existente: dato = InputBox("Ingrese el nuevo valor: ", "Modificación") 'coloco en excel: Range(.Tag).Value = dato 'debo actualizar la propiedad text del nodo, con el nuevo 'dato ingresado: 'tomo la primer parte del texto, el cual se encuentra separado 'por los dos puntos: posi = InStr(1, .Text, ":") 'separo la primer parte (que es la descripcion) primerParte = Left(.Text, posi) 'y lo coloco en el nodo, concatenando la descripcion '(precio:, abs:, alarma:, etc, etc) con el nuevo 'valor ingresado .Text = Trim(primerParte) & " " & Trim(dato) End If End If End With End Sub
Listo. Si presionamos "suprimir" sobre un nodo de característica se borrará y si presionamos "enter" aparecerá un inputbox que nos dejará modificar el valor que esa característica tenga. Comparen esta imagen con la anterior:
modifiqué el precio y borré la alarma, todo desde este formulario.
Varios:
En VBA hacemos click sobre el TreeView y desde la Ventana de propiedades seleccionamos el item (Personalizado):
Style: aquí seleccioné la opción 7, que muestra las lineas junto al signo mas y menos (PlusMinus), permitiendo texto e imagen en el nodo (PictureText). Exploren las demás, encontrarán cosas buenas.
Indentation: la longitud de las líneas. Por defecto viene en 28 unidades, pero generalmente es muy largo y conviene acortarlo, por un efecto meramente visual.
PathSeparator: que caracter separará a cada elemento de la jerarquía. Por defecto viene la barra invertida, lo que nos acerca mucho al path del Explorador de Windows (ej: marca\modelo\caracteristica). Mas abajo daré un ejemplo sobre esto.
Sorted: si lo elementos se mostrarán ordenados.
CheckBoxes: para que cada nodo sea una casilla de verificación
Reflejaré en el Caption del formulario la propiedad PathSeparator de los nodos:
Private Sub TreeView1_NodeClick(ByVal Node As MSComctlLib.Node)
'cada vez que hagamos click sobre algun nodo se mostrará en el caption del
'userform la "ruta de acceso" al nodo:
UserForm1.Caption = Node.FullPath
End Sub
Y obtengo:
ruta = Split(Node.Text, "\")
guardo en una matriz todos los integrantes de la "familia" del nodo en cuestión.
¿Hay mas? Si, muchísimo. El control TreeView es muy complejo y posee una gran cantidad de propiedades, metodos y eventos, a los que debemos sumar los que ostentan cada uno de sus nodos.
Pero les aseguro que vale la pena aprender a manejar este control, dado que brindará un aspecto muy profesional a nuestros proyectos.
Si surgen dudas o problemas, quedo al aguardo de sus comentarios. Link al archivo.
Salu2.xlsx
Comentarios
Publicar un comentario