SISTEMA DE AYUDA1 El control TreeView es uno de estos controles ActiveX que está ahí y que, hace años, me preguntaba: ¿para qué servirá en Access? Por otro lado, también hace años, las ayudas de los controles (entre otros) venían en un formato de archivo de extensión *.chm. ¿Cuál es el problema? Que no se proporciona ninguna aplicación que lea archivos *.chm, por lo que toda la información incluida en dichos archivos resulta inaccesible (hay programas disponibles en la Red, pero, hasta donde yo sé, todos son de pago). Entonces es cuando “se enciende la bombilla” y se me ocurre encontrarle una utilidad práctica al TreeView para poder crearnos un sistema de ayuda “chulo” para nuestra aplicación en Access. Seamos realistas: el sistema no es algo que “deslumbre” (creo yo) si lo comparamos con, por ejemplo, crearnos un pdf con marcas, pero creo que sí queda bastante “resultón” de cara al usuario. Puede ser que no obtengamos una ayuda visualmente impactante, pero, al fin y al cabo, sí obtendremos una ayuda muy práctica (de nuevo, creo yo). Este ejemplo no explica el funcionamiento detallado del control TreeView (si nos interesa profundizar en el mismo podemos echar un vistazo a este ejemplo), sino sólo se explica cómo utilizarlo de cara a crearnos nuestro sistema de ayuda. Y dicho todo esto vamos a ver cómo hacer funcionar nuestro sistema de ayuda. PLANTEAMIENTO INICIAL Vamos a reflexionar un momento sobre lo que queremos obtener. Plantearemos la ayuda a tres niveles: 1er nivel: los grandes epígrafes 2º nivel: los subepígrafes 3er nivel: los títulos, que llevarán aparejada su ayuda correspondiente. Para que nos entendamos, un ejemplo de lo anterior podría ser: 1er nivel: LIBROS DE ACCESS 2º nivel: ACCESS BÁSICO ACCESS INTERMEDIO 3er nivel (dentro de ACCES BÁSICO, por ejemplo): Mi primer Access (y en la ayuda saldría la información del libro) Nadando con Access Access para torpes Aunque podríamos complicarlo bastante, lo dejaremos en la estructura anterior, de manera que sólo cuando el usuario haga clic sobre el título de uno de los libros le salga la ayuda. El ejemplo anterior es eso, sólo un ejemplo. Yo voy a utilizar una descripción de los contenidos 1 La BD de ejemplo os la podéis bajar aquí 1 Visítame en http://neckkito.siliconproject.com.ar de la BD para “ayudar” al usuario. PLANIFICANDO NUESTRAS TABLAS Si no perdemos de vista la estructura que acabamos de explicar veremos que la misma nos determina la estructura de las tablas que vamos a necesitar. Así pues vamos a crearnos una tabla, que guardaremos con el nombre de TAyudaEpigrafes, que tendrá la siguiente estructura. Ahí construimos lo que hemos llamado nuestro primer nivel. Para que os hagáis una idea esto son los datos que he introducido: Vamos a construirnos una segunda tabla, que guardaremos TAyudaSubEpigrafes, que tendrá la siguiente estructura: con el nombre de Esta tabla constituirá nuestro segundo nivel. Los datos que yo he introducido han sido: Por ahora no tenemos ninguna relación entre la primera tabla y la segunda. Esta relación la definiremos en nuestra tercera tabla, que contendrá los descriptores del tercer nivel y el texto de ayuda. Así pues, esta tercera tabla, que guardaremos como TAyudaTextos, tendrá la siguiente estructura: 2 Visítame en http://neckkito.siliconproject.com.ar Como vemos en su estructura, tenemos el primer nivel ([IdTE]), el segundo nivel ([IdTSubE]) y el tercer nivel ([DescripT]), con su texto de ayuda. Aquí es donde deberemos, como comentaba antes, crearnos la estructura de dependencias que le queramos dar a nuestro árbol de ayuda. Para que os situéis un poco parte de los datos que he introducido son: Si nos fijamos, y si convertimos los identificadores es su texto descriptivo, tenemos para el primer registro, por ejemplo: Introducción → Sistema de ayuda → Prólogo: En esta base de datos... (bla, bla...) MUY IMPORTANTE El control TreeView funciona a través de claves o “keys”. Estas claves deben ser únicas, dado que precisamente son “identificadores inequívocos”. Si pudiera haber dos iguales no sabríamos a cuál de ellos nos referiríamos. Como vamos a utilizar los descriptores de los epígrafes, subepígrafes y descriptor de texto, ello nos obliga a utilizar nombres diferentes para cada uno de ellos. Si convertimos la estructura anterior en “keys” (que serán las que utilizaremos) obtendríamos: introducción → sistema de ayuda → prólogo Los tres valores son diferentes: ningún problema. Si tuviéramos lo siguiente, por ejemplo: introducción → prólogo → prólogo El código nos dará error cuando intente asignar la clave “prólogo” en el nivel tres del TreeView porque “prólogo” ya existe como “key” en el nodo del nivel dos. Hay que tener esto en cuenta cuando vayamos a rellenar los valores en las tablas para 3 Visítame en http://neckkito.siliconproject.com.ar evitarnos errores de código en la creación del TreeView. ¡Ojo! CREANDO NUESTRA CONSULTA CAYUDA Para facilitarnos la búsqueda de valores en el código vamos a crearnos una consulta, que llamaremos CAyuda. Dicha consulta tendrá la siguiente estructura: Si os fijáis, de alguna manera lo que nos hace la consulta es mostrarnos la tabla TAyudaTextos con las descripciones en lugar de los identificadores (más o menos, quiero decir). CREANDO NUESTRO FORMULARIO DE AYUDA Vamos a crearnos un formulario en blanco, que guardaremos como FAyuda. En dicho formulario vamos a insertar el control ActiveX “Microsoft TreeView Control, version 6.0”. Vamos a sacar sus propiedades y le pondremos de nombre miTreeVw2. Introducimos también un cuadro de texto, al que pondremos de nombre txtAyuda. Con algo de maña nos trabajamos el diseño y a mí me ha quedado una cosa así: Vamos a sacar las propiedades del propio TreeView (las propiedades del objeto Tree Ctrl) 3 y vamos a configurar lo siguiente: • 2 3 Nos aseguramos que la propiedad <LineStyle> esté en <1 – tvwRootLines> Para asignar un nombre a un control lo que debemos hacer es sacar las propiedades de ese control e irnos a la Pestaña Otras → Nombre. Ahí escribimos el nombre que queramos. Seleccionamos el control TreeView → Clic con el botón de la derecha → En el menú emergente seleccionamos la opción “Objeto de Tree Ctrl” → Propiedades 4 Visítame en http://neckkito.siliconproject.com.ar • • Nos aseguramos que la propiedad <LabelEdit> esté en <1 – tvwManual> En la pestaña “Fuente” podemos configurar la fuente y algunas de sus características para que se adapten mejor a nuestra aplicación. PROGRAMANDO EL RELLENO DE NUESTRO TREEVIEW Como queremos que nuestro TreeView sea lo más flexible posible debemos construirlo cada vez que abramos el formulario para que “lea” los datos de las tablas de ayuda que nos hemos creado. Así, podremos ir incluyendo datos en esas tablas tranquilos de que cuando se abra FAyuda esos datos aparecerán en el TreeView. Para ello vamos a sacar las propiedades del formulario y en su evento “Al cargar” generamos el siguiente código: … Private Sub Form_Load() 'Requiere registro de referencia "Microsoft DAO 3.6 Object Library" o 'módulo equivalente 'Declaramos las variables Dim miMarca As String, miKey As String Dim miEpi As String, miSubEpi As String Dim miSql As String Dim misNodos As Node Dim rst As DAO.Recordset '-----FASE 1: CREAMOS LOS EPÍGRAFES 'Creamos el recordset sobre la tabla TAyudaEpigrafes Set rst = CurrentDb.OpenRecordset("TAyudaEpigrafes", dbOpenForwardOnly) 'Manipulamos el recordset With rst 'Iniciamos el recorrido de registros Do Until .EOF 'Cogemos la descripción del epígrafe miMarca = .Fields(1).Value 'Convertimos la marca en mi key miKey = LCase(Trim(miMarca)) 'Añadimos el nodo a nuestro TreeView Set misNodos = Me.miTreeVw.Nodes.Add(, , miKey, miMarca) 'Lo marcamos en azul Me.miTreeVw.Nodes.Item(miKey).ForeColor = vbBlue 'Nos movemos al siguiente registro .MoveNext Loop End With '-----FASE 2: CREAMOS LOS SUBEPÍGRAFES 'Creamos una SQL sobre la consulta CAyuda miSql = "SELECT DISTINCT CAyuda.SubEpigrafe, CAyuda.Epigrafe FROM CAyuda" 'Creamos el recordset sobre nuestra SQL Set rst = CurrentDb.OpenRecordset(miSql) 'Manipulamos el recordset With rst 'Nos movemos al primer registro .MoveFirst 'Iniciamos el recorrido de registros Do Until .EOF ' 'Cogemos la descripción del epígrafe miEpi = .Fields(1).Value 'Lo transformamos en la key miEpi = LCase(Trim(miEpi)) 5 Visítame en http://neckkito.siliconproject.com.ar 'Cogemos la descripción del subepígrafe miSubEpi = .Fields(0).Value 'Creamos la key miKey = LCase(Trim(miSubEpi)) 'Lo añadimos al treeview Set misNodos = Me.miTreeVw.Nodes.Add(miEpi, tvwChild, miKey, miSubEpi) 'Lo marcamos en magenta Me.miTreeVw.Nodes.Item(miKey).ForeColor = vbMagenta 'Nos movemos al siguiente registro .MoveNext Loop End With '-----FASE 3: CREAMOS LOS ELEMENTOS DE AYUDA 'Manipulamos de nuevo el recordset Set rst = CurrentDb.OpenRecordset("CAyuda") With rst 'Nos situamos en el primer registro .MoveFirst 'Recorremos los registros Do Until .EOF 'Cogemos la descripción del subepígrafe miSubEpi = .Fields(1).Value 'Lo convertimos en la key miSubEpi = LCase(Trim(miSubEpi)) 'Cogemos la descripción de la ayuda miMarca = .Fields(2).Value 'Creamos la key miKey = LCase(Trim(miMarca)) 'Lo añadimos al treeview Set misNodos = Me.miTreeVw.Nodes.Add(miSubEpi, tvwChild, miKey, miMarca) 'Nos movemos al siguiente registro .MoveNext Loop End With 'Cerramos conexiones y liberamos memoria rst.Close Set rst = Nothing End Sub … Cotilleemos un poco sobre el código (que está ya ampliamente comentado): • Lo primero que vemos es que como tenemos tres niveles lo hemos dividido en tres fases, que van desde el nivel superior al nivel inferior. • Para el primer nivel creamos un recordset sobre TAyudaEpigrafes (y lo abrimos como dbOpenForwardOnly para que la lectura de los registros sea lo más rápida posible). Vamos recorriendo los registros, cogemos el valor de la descripción, lo convertimos en minúsculas y eliminamos posibles espacios en blanco (LCase(Trim(...)), lo que nos permite darles valor a dos variables: miMarca y miKey. • Con el Set misNodos = … añadimos esa “key” y su descripción en el nodo correspondiente. • En la fase 2 echamos mano de la consulta CAyuda que nos hemos creado anteriormente. Sin embargo, no recurrimos a ella directamente, sino que lo hacemos a través de una SQL. Esa SQL lo único que hace es cogernos los valores únicos de la descripción de los subepígrafes, eliminando los duplicados. La diferencia es que, a partir de aquí, también debemos “pillar” las claves que constituyen el nodo superior y de las cuales dependen los nuevos valores que vamos rellenando en el TreeView. • Creamos el recordset y la recorremos, de nuevo rellenando las variables que nos van a proporcionar la clave y la descripción. Os remito al código para ver los detalles. 6 Visítame en http://neckkito.siliconproject.com.ar • He aprovechado la ocasión para marcar los nodos de primer y segundo nivel de otro color. Eso ofrece una ayuda visual adicional. Si no nos gusta simplemente eliminamos esas líneas de asignación de color del código. • Finalmente, para la fase 3, sí que creamos un recordset directamente sobre CAyuda y repetimos el proceso de crear “key” y valor de la key, junto con la clave del nodo inmediatamente superior del cual dependen. Aunque a primera vista el código puede parecer muy complejo si lo dividimos en elementos más simples veremos su lógica, ya que la mecánica es simplemente ir repitiendo el bucle a través del recorrido de registros y asignar los valores correspondientes (¡qué fácil es decirlo, ¿verdad? Je, je...). PROGRAMANDO LA ACCIÓN DEL TREEVIEW Si sacamos las propiedades “normales” del TreeView veremos que tenemos muy pocos eventos disponibles. Y, además, de los que tenemos ahí ninguno nos sirve. Para hacer operativo el funcionamiento del TreeView debemos utilizar un evento que no existe en esas propiedades, que es “Al hacer clic”. Para ello, en el módulo asociado al formulario FAyuda, vamos a escribir el siguiente código, describiendo nosotros “a mano” el tipo de evento: … Private Sub miTreeVw_Click() 'Declaramos las variables Dim laAyuda As String Dim laKey As Variant 'Cogemos el valor seleccionado por el usuario laKey = Me.miTreeVw.SelectedItem.Text 'Buscamos su equivalencia en la descripción laAyuda = Nz(DLookup("TextoAyuda", "TAyudaTextos", "DescripT='" & laKey & "'"), "") 'Si laAyuda devuelve un valor nulo salimos If laAyuda = "" Then Exit Sub 'Pasamos el texto de la ayuda al textbox Me.txtAyuda.Value = laAyuda End Sub … Con esto cada vez que hagamos clic sobre el TreeView se ejecutará el código. Pero hemos dicho que los nodos de los niveles uno y dos no tienen ayuda asociada. ¿Cómo lo solventamos? Pues en el código veréis que existe un DLookup que nos busca el texto de ayuda. Si no hay devuelve un valor NULL, que convertimos a una cadena vacía (“”) gracias a la función NZ(). Y, a continuación, le decimos simplemente que si el valor devuelto es una cadena vacía pues... a salir del proceso. Finalmente, cuando por fin encuentra algún texto de ayuda, lo traspasa al textbox del formulario... y así se “obra el milagro”... ji, ji... PARA FINALIZAR EL EJEMPLO Bueno. Como indicaba en la introducción al ejemplo, creo que ha salido un ejemplo bastante 7 Visítame en http://neckkito.siliconproject.com.ar “resultón” y práctico. Si necesitáis utilizar este sistema en vuestras aplicaciones sólo tendréis que construíroslo o bien, más fácil, importar de la BD de ejemplo tablas, consulta y formulario, y, evidentemente, sustituir los valores que yo he introducido por los que se adapten a vuestra BD. Espero que el ejemplo os sea útil. Un saludo, y... ¡suerte! 8 Visítame en http://neckkito.siliconproject.com.ar