Beruflich Dokumente
Kultur Dokumente
Índice
Información
o Título, autor...
Introducción
o Introducción
o Descarga de los ejemplos del libro
Excel y la programación
o Los objetivos
o Las bases del lenguaje VB
o Organización del código y metodología
o El entorno de desarrollo en Excel
Primeras manipulaciones y gestión de candidatos
o Objetivos
o La gestión de los libros y las hojas
o Llamada de procedimientos en la herramienta y gestión del código
o VBA y la gestión de datos cargados
o Visualización de estadísticas por habilidades con gestión de errores en el
procedimiento
o Copias de seguridad del proyecto Gestión de perfiles
Transformación de la herramienta en ejecutable
o Objetivos
o Formularios para gestionar los candidatos
o Presentación del objeto Formulario y de los principales controles del
cuadro de herramientas
o Creación de subtotales en la lista de inscritos
o La gestión de estado de los miembros
Implementación del planning de los candidatos
o Objetivos
o Extensión de los procesos de navegación al conjunto de la aplicación
Gestión de perfiles
o Integración de la plantilla para la creación de trabajos
o Gestión de las asignaciones y de las fechas de los trabajos
Gestión de la aplicación con una base de datos
o Objetivos
o La base de datos BDD_Agencia y su entorno
o La gestión de los datos de la agencia
Proteger la aplicación Gestión de Perfiles
o Objetivos
o La contraseña del IDE
o El control de las entradas del usuario
o La protección de las hojas en la aplicación
o La recuperación de los permisos de usuario y la navegación en la
aplicación
Ludovic LANNOY
Ludovic Lannoy es Ingeniero desde hace más de 10 años. Como parte de su trabajo,
desarrolla aplicaciones en VB o VBA para responder a las necesidades de empresas muy
diversas. A través de este libro proporciona al lector una base muy eficaz para introducirse en
el desarrollo VBA.
Introducción
Introducción
Esta obra se dirige principalmente a los usuarios avanzados de Excel. Efectivamente,
antes de atreverse con la programación es preferible conocer la parte de usuario y las
diferentes funcionalidades a disposición de los usuarios a través de los menús. Esto no
es excluyente pero los programas escritos en Excel se basan en las funcionalidades de
esta hoja de cálculo adaptándolas a las necesidades específicas de los usuarios. Además,
sin ser un experto, deberá dominar los menús más utilizados como los filtros, las tablas
dinámicas, las ordenaciones... Debe ser capaz de insertar funciones o elaborar fórmulas
de cálculos más o menos elaboradas.
Este libro está destinado también a los programadores con experiencia como usuarios de
VB (Visual Basic) que pensaban que podían librarse de un estudio previo del lenguaje
VBA (Visual Basic for Application) aplicado en el entorno de desarrollo específico de
Excel.
No es necesario tener un perfil científico para escribir código. Basta con un poco de
sentido común y sobre todo de un buen conocimiento funcional del dominio de la
aplicación a desarrollar. La programación no es exclusiva de los informáticos.
El cuarto capítulo, Puesta en marcha del horario de los candidatos, tratará de la puesta
en marcha de un horario en forma de un histograma de asignaciones de candidatos por
trabajo.
El quinto capítulo, Gestión de la aplicación con una base de datos, establecerá los
intercambios con la base de datos.
Excel y la programación
Los objetivos
Este capítulo del libro es esencialmente teórico. De hecho, antes de comenzar con la
programación, hay que asimilar ciertas bases como la sintaxis del lenguaje o las reglas
de escritura de código.
Por último, durante todo el capítulo se irá recordando la manera de organizar el código y
el progreso general del proyecto.
1. Las variables
Las variables sirven para almacenar y manipular información. La que puede variar
durante la ejecución tiene el nombre de variables.
Para asignar un valor a una variable o a una propiedad hay que usar el símbolo =.
Vea la siguiente tabla de resumen de los diferentes tipos de variables:
Pero puede que lo necesite para el conjunto de módulos de su aplicación. En este caso
utilice siempre en la cabecera del módulo Public en lugar de Dim.
Si por el contrario desea que no sea accesible por otros módulos hay que declararla
como Private.
d. Las constantes
Las constantes se parecen a las variables pero como su nombre indica no se pueden
modificar. Además, la asignación de un valor a una constante se hace en el momento de
su declaración y no durante la ejecución de la aplicación.
El interés de una constante es que conserva su valor durante toda la duración del
programa. Es inútil pues, declarar una cadena de caracteres compleja, como la ruta de
acceso (ver arriba) o una clave de identificación, en diferentes sitios del código.
Igualmente, si hay que modificar esta cadena, basta con hacerlo una sola vez a nivel de
la declaración de la constante.
2. Los operadores
Para efectuar operaciones sobre los datos, Visual Basic proporciona operadores. Éstos
se utilizan en función del tipo de datos utilizados.
Ponga atención en el orden de los operadores ya que existe una prioridad. En una
fórmula compleja, la prioridad absoluta la dan los paréntesis (). Los operadores de
comparación tienen la misma prioridad y se evalúan en su orden de aparición, de
izquierda a derecha. Por el contrario, los operadores lógicos y matemáticos se evalúan
en el siguiente orden: ^, -, * y /, \, MOD, + y por último -.
La estructura de una condición sencilla con los términos If (si), Then (entonces) y End
If (fin del si) es la siguiente:
If <Condición> Then
<Instrucciones>
End If
If <Condición_1> Then
<Instrucciones_1>
ElseIf <Condición_2> Then
<Instrucciones_2>
Else
<Instrucción_3>
End If
En el siguiente ejemplo, probamos dos individuos para definir cual es el mayor de los
dos. Para que esta comparación tenga sentido, hace falta que al menos uno de los dos
individuos haya nacido.
Con Select Case, existe otra manera de estructurar las condiciones especialmente si hay
varias respuestas posibles. Se ejecuta más rápido que con If y el código es más legible.
b. Los bucles
Hay varios tipos de bucles para repetir una instrucción un determinado número de
veces.
For - Next
For <Variable Contador> To <Variable Bucle>
<Instrucción>
Next
<Variable Contador> permite definir el valor de inicio y <Variable Bucle> indica el
número de veces que se ejecutará la secuencia de código.
Vea un ejemplo:
Es posible añadir al lado de <Variable Bucle> una <Variable Incremento> que modifica
el valor de incremento. De hecho, este valor por defecto es 1. Con For i = 1 To 10 Step
2, sólo se producen 5 repeticiones en el bucle y el resultado mostrado por MsgBox será
entonces 32.
La variable IntResult está limitada a la capacidad del tipo Integer, un número entero
entre -32 768 y +32 767. Además, si nuestro bucle se ejecuta 15 veces, el resultado
mostrado será 32 768. Este número rebasa la capacidad de una variable de tipo Integer.
Es posible optar por un tipo Long, un número entero entre -2 147 483 648 y
2 147 483 647. Pero es mejor utilizar la instrucción Exit que permite interrumpir una
secuencia de código. En el ámbito de un bucle For - Next, hay que escribir Exit For.
Una vez que la variable i alcanza el número 15, la ejecución del bucle se para y se
ignora la instrucción IntResult = IntResult * 2.
While - Wend
Es un bucle muy simple que repite las instrucciones mientras la condición de inicio sea
verdadera.
No hay que olvidar incrementar el contador en este tipo de bucle, ya que se puede
crear un bucle infinito.
Do - Loop
Es un bucle un poco más elaborado que el anterior. Repite las instrucciones mientras
que (Do While) o hasta que (Do Until) la condición de inicio sea cierta.
Vea los dos ejemplos que conducen a dos resultados diferentes: 21 con While y 20 con
Until. Esta diferencia se explica por el hecho de que la condición debe ser verdadera
para empezar con While y falsa para empezar con Until. La regla se invierte para salir
de la condición. Debe ser falsa con While y verdadera con Until.
Para obtener un resultado idéntico, sería necesario, por ejemplo, modificar la primera línea de
código del While : Do While i <= 19. Así, es posible utilizar las dos formas. En función del
contexto, convendrá utilizar más uno u otro.
Hay que resaltar que después del primer bucle hay que reinicializar la variable i
porque ya se ha incrementado y es importante no acumular el valor que ya tiene con el
incremento del siguiente bucle. Este es un concepto importante que conviene recordar
en relación con el uso de variables globales visitadas por una multitud de
procedimientos en el programa.
Estos bucles pueden no ser ejecutados si en el inicio la <Condición de Inicio> no es
verdadera. No es el caso del siguiente bucle que al contrario que los anteriores se
ejecuta al menos una vez.
Do
<Instrucción>
Loop While o Do Until <Condición de Inicio>
Consideremos por ejemplo un objeto llamado Perro. Sus propiedades podrían ser su
tamaño, su peso o el color del pelo. Los métodos relacionados serían ladrar, comer o
llevar un palo.
En el ámbito de VB, existen objetos con propiedades y métodos predefinidos por los
creadores del lenguaje.
La propiedad Caption del objeto recupera el nombre de esta hoja en el libro (Hoja1 u
otro). Del mismo modo, Visible define si esta hoja se muestra o se oculta (la propiedad
está entonces a True o False).
El método Add permite añadir una hoja al libro. Con Move puede desplazar esta hoja y
modificar su posición.
5. Los procedimientos
Los procedimientos y las funciones agrupan secuencias de código a ejecutar. Permiten
descomponer el código en sub-programas más fáciles de comprender y mantener. Estos
procedimientos o funciones son reutilizables en todo el programa.
a. Los procedimientos
Un procedimiento puede estar vinculado a un evento pero también puede ser llamado
por una instrucción en cualquier lugar del programa. Se trata entonces de un
procedimiento general.
Evidentemente, sólo hay que escribir código en los eventos útiles para la aplicación.
Este código se ejecutará cada vez que el o los evento(s) asociado(s) se desencadene(n)
por parte del usuario o del sistema.
c. Las funciones
IsDate: devuelve un valor booleano verdadero (True) si el dato pasado por parámetro es
de tipo Date.
IsEmpty: devuelve un valor booleano verdadero (True) si el dato pasado por parámetro
no ha sido inicializado.
IsNull: devuelve un valor booleano verdadero (True) si el dato pasado por parámetro
contiene el valor Null.
IsObject: devuelve un valor booleano verdadero (True) si el dato pasado por parámetro
es de tipo Object.
Asc: devuelve el código ASCII del primer carácter de la cadena pasada por parámetro.
Chr: devuelve el carácter del código ASCII que se ha pasado por parámetro.
Format: Da formato para presentar datos de tipo fecha, numérico o cadena de caracteres.
Space: devuelve una cadena de espacios cuyo número se pasa por parámetro.
Trim: elimina los espacios a izquierda y derecha de la cadena de caracteres pasada por
parámetro.
DateAdd: suma un periodo a la fecha pasada por parámetro. El periodo a sumar será en
años, trimestres, meses, días, horas, minutos o segundos.
DateDiff: devuelve la diferencia entre las dos fechas proporcionadas. El periodo resultante será
en años, trimestres, meses, días, horas, minutos o segundos.
Day: devuelve el número de día de la fecha pasada por parámetro.
InputBox es una función que muestra un cuadro de diálogo que permite una entrada de
información con validación por el usuario (clic del botón Aceptar).
Defecto: cadena de caracteres mostrada por defecto en la zona de entrada del usuario.
Hasta ahora, hemos utilizado el cuadro de diálogo MsgBox para mostrar en pantalla los
resultados de las pruebas. Pero su utilización se ha limitado a su expresión más simple:
un mensaje y un botón Aceptar para cerrarlo. Es posible añadir botones o iconos.
Tipo: define el número y el tipo de botones, así como el icono que aparece en el cuadro.
Este número es una suma de código que proceden de 4 grupos: tipos de botones, tipos
de iconos, botón por defecto y modalidad.
El clic de un botón retorna un valor que devuelve la función MsgBox. Este valor es de tipo
Integer.
Las líneas de código demasiado largas pueden suponer problemas en función del
tamaño de la pantalla. La lectura de un código puede ser más difícil si hay que
desplazarse por la pantalla. Se puede utilizar el carácter Under Score (_) para saltar a la
línea siguiente sin romper la instrucción en 2.
La sangría hace resaltar en el código algunos bloques que agrupan condiciones y bucles.
Esto es útil en algunos algoritmos en los que condiciones y bucles están anidados unos
en otros en diferentes niveles. La sangría ayuda a resaltar el nivel de anidamiento y la
prioridad de ejecución de las secuencias.
Las mayúsculas no están aquí sólo para resaltar las cadenas de caracteres. Dado que el
lenguaje VB distingue las mayúsculas y minúsculas, si escribe un nombre de un objeto
en su código cuidando respetar la ortografía, VB pondrá automáticamente los caracteres
que deban estar en mayúsculas. De este modo usted sabe que el programa ha reconocido
bien su objeto. Esto no ocurre cuando hay una falta de ortografía. En el momento de la
compilación del programa, en el momento de la ejecución, no se reconoce el objeto y el
programa muestra un error. Este tipo de error no es fácil de detectar en el código. Es
peor cuando hay concatenaciones de cadena de caracteres con variables u otros tipos de
objetos.
Por último, hay que tratar el aspecto gráfico eligiendo colores, fuentes y una buena
disposición de los controles. Las aplicaciones deben ser fáciles de utilizar para los
usuarios.
5. La gestión de un proyecto
La gestión de un proyecto se divide en 3 fases ineludibles: el análisis, el desarrollo y la
entrega.
1. La plataforma de desarrollo
Para utilizar el IDE, puede instalarlo a través de la pestaña Programador de su hoja
Excel.
1. Abra con un clic secundario del ratón en la cinta un menú contextual en el que
tiene que escoger el menú Personalizar la Cinta de opciones :
Vea el IDE tal y como se ve al abrir. Este entorno se compone de diferentes ventanas
que vamos a revisar.
La ventana de proyecto
Esta ventana agrupa los diferentes componentes del proyecto como los objetos del libro,
los UserForm (o formularios), los módulos y los módulos de clases (creación de objetos
personalizados). Estos componentes se organizan en forma de árbol en las carpetas. Es
posible modificar la apariencia de este árbol con el botón . Cada uno de los
componentes contiene código visible en la ventana de código. Basta con seleccionar el
componente y hacer clic en el botón . Esta operación también se puede hacer con un
doble clic en el componente. Si selecciona un Userform, puede visualizar los diferentes
objetos en la ventana de código con el botón .
Esta ventana muestra todas las propiedades de los objetos y controles del proyecto.
Según la pestaña elegida, estas propiedades se pueden visualizar clasificadas
alfabéticamente o agrupadas por categorías.
El explorador de objetos
Esta ventana visualiza los diferentes objetos de VBA con sus propiedades, sus métodos
y sus eventos. Una función de búsqueda permite encontrar fácilmente estos objetos,
propiedades, métodos o eventos.
b. La zona de código
Después, introduciendo un punto a continuación del nombre de un objeto, aparece una lista
desplegable que muestra las propiedades, métodos y eventos asociados a este objeto.
Esta funcionalidad es una gran utilidad y de muy fácil uso. Permite a los
programadores encontrar la propiedad, el método o el evento que se ajuste a las
necesidades del programa. Como último recurso le queda, naturalmente, el explorador
de objetos.
En modo depuración o Break, si el cursor está sobre una variable, VBA muestra su
contenido.
Los siguientes botones están en la barra de menú Edición y en el menú del IDE Ver - Barras de
herramientas - Edición.
c. La ejecución del código
Para ejecutar el código a partir de la ventana de código, basta con situar el cursor en un
bloque de código ejecutable. Por ejemplo en el caso de un procedimiento que no tiene
espera parámetros.
A continuación se puede utilizar la tecla [F8] para hacer avanzar el código paso a paso.
Este método tiene todo su interés en el ámbito de un proceso de depuración. [Ctrl][F8]
ejecutará el código hasta el cursor.
Con un clic izquierdo del ratón en el margen del programa, se puede insertar un punto
de interrupción. Esto suspende la ejecución del código al nivel de la instrucción donde
se ha colocado.
d. La caja de herramientas
Pero existen límites a este proceso. De hecho, la macro graba todas las acciones del
usuario incluidas las equivocaciones y correcciones. Ocurre igual con los clics de
validación o de selección y los desplazamientos con los cursores. Por el contrario, no se
graban las instrucciones entradas con el ratón. Tanto para un clic izquierdo como para
uno derecho (menus contextuales).
Es posible llenar directamente la celda A1 sin tener que seleccionarla previamente con
el código Range("A1").Value = "1 hora".
Se obtiene un resultado idéntico sólo con dos líneas de código. Por tanto es útil hacer un
poco de limpieza en el código recuperado de una macro.
Añadamos ahora una acción suplementaria. Usted quiere que se seleccione la última
celda de la linea (Celda X1). En lugar de utilizar la barra de desplazamiento horizontal
en la grabación de su macro para desplazarse, utilice el atajo de teclado [Ctrl][Cursor
derecho] que selecciona directamente la última celda de la línea.
La instrucción se traduce en una sola instrucción, sea cual sea la longitud de la línea.
La tecla [F1] permite acceder a esta misma ayuda si el cursor está situado en una instrucción
del código, una ventana del IDE o un control de un formulario. Si por ejemplo se selecciona
una instrucción Sub, la ventana de ayuda se abre en la definición correspondiente.
Internet es otra fuente de información. Basta con teclear directamente una pregunta en un
motor de búsqueda para que aparezca una lista de sitios y foros de discusión que tratan
problemas de código VBA, todo en entornos de habla española.
Además, se grabarán los trabajos realizados tanto a nivel de hojas del libro como a nivel
de la plataforma de desarrollo.
El capítulo termina con operaciones de directorios en VBA.
Hoja de trabajo
Hoja de referencia del proyecto
Plantillas
Es interesante coger el hábito de tener las dos ventanas abiertas al mismo tiempo. Así es
posible acceder rápidamente al código o a las hojas del libro de trabajo. Además durante
las pruebas de las macros, es posible ver el resultado del despliegue del código (modo
depuración con la tecla [F8]) a nivel de las hojas del libro. Naturalmente, cuanto más
grande sea su pantalla, más interesante será este método.
Inserte un módulo en el proyecto. Utilice el menú Insertar - Módulo o busque
esta funcionalidad en el menú contextual haciendo clic con el botón secundario
del ratón en el explorador del proyecto.
Ahora debe insertar en este nuevo módulo (Modulo1 por defecto, según el orden de
creación) un procedimiento que debe llamar AperturaLibro.
La línea de comentario comienza siempre con una comilla simple incluso cuando
hay un salto de línea.
El término ChDir permite apuntar al directorio en el que se encuentra el libro que desea
abrir.
En estas dos líneas de código, se pueden utilizar variables para que el procedimiento
pueda abrir diferentes libros modificando la ruta de acceso y el nombre del documento.
Las dos variables se alimentan por código pero es posible utilizar el paso por
parámetros. Este concepto se verá con más detalle en este capítulo un poco más tarde.
Hay que posicionarse en la hoja correcta del libro, seleccionar los datos, copiarlos para a
continuación volver al nuevo libro y pegar los datos en una hoja de trabajo.
Una hoja del libro se llama por su nombre pero también por un número de índice
directamente vinculado a su orden de creación. De este modo la instrucción
Sheets("Lista de inscritos").Select es equivalente a la instrucción Sheets(1).Select.
La instrucción Array crea una tabla que permite seleccionar las dos hojas a la vez.
SelectedSheets hace referencia a las hojas seleccionadas para suprimirlas con Delete.
La hoja de trabajo es una hoja con la que podrá hacer diversas operaciones como
almacenar datos, efectuar cálculos o ajustes. Es provisional y se crea durante la
ejecución del programa. Piense también en eliminar las hojas inservibles como las
insertadas por defecto cuando se crea un libro (ver las opciones definidas por defecto).
Esta operación permite aligerar al máximo sus libros para el almacenamiento o su envío
a terceros.
Para volver a poner el código activo, basta con utilizar el botón Bloque sin comentarios
.
Todo ha funcionado según lo previsto excepto un mensaje que le pide que confirme la
eliminación de las hojas.
Los mensajes que proceden del sistema operativo no están afectados por este código
y siguen estando activos.
En primer lugar, Sheets.Add inserta una nueva hoja. Se sitúa por defecto delante de la
hoja activa. Aunque esta hoja se va a ocultar, es preferible situarla al final del libro.
Partiendo de la base de que es posible encontrar una hoja tanto por su nombre como por
su número de índice, es posible identificarla como la última de un conjunto de hojas. De
este modo, after:=Sheets(Sheets.Count) permite situarla después de la última hoja del
libro en el momento de su creación. La propiedad Count cuenta el número de hojas del
libro. Podemos descontar dos hojas en el libro. La nueva hoja se insertará después de la
segunda.
Esta hoja la dejaremos de lado por el momento, se utilizará en el proyecto más tarde.
Sería una perdida de tiempo automatizar con VBA el diseño de esta máscara de
entrada, ya que el hecho de que esté guardada en un directorio la preserva de cualquier
modificación voluntaria o involuntaria por parte del usuario.
Inserte en el módulo un nuevo procedimiento llamado AperturaPlantillas. Inserte
a continuación el siguiente código:
Respecto a la apertura del libro, el código es idéntico al del primer procedimiento. Así que en
lugar de declarar de nuevo las variables StrRuta y StrNomFic, es más sensato modificar su
ámbito. Basta con desplazar estas declaraciones a la parte superior del módulo, justo debajo
de la instrucción Option Explicit.
Estas dos variables están ahora disponibles para todo el módulo. No es necesario
declararlas al inicio del procedimiento, sólo hay que alimentarlas.
Lista de inscritos
Perfiles
Referencias (Hoja oculta)
Para cambiar el nombre por defecto que da VBA, hay que ir a la ventana de
propiedades. Si selecciona el nuevo módulo en la ventana de proyecto, aparecen sus
propiedades en la ventana de propiedades.
A continuación tiene la posibilidad de abrir la lista desplegable en la parte superior
de la ventana de propiedades son están todos los objetos disponibles del módulo, del
formulario o de la hoja seleccionada.
En este caso, sólo hay una propiedad disponible para el nuevo módulo. Se trata de la
propiedad Name. Asígnele el nombre ModDeclaracion en lugar del nombre por defecto.
Mod como prefijo que identifica un módulo y Declaracion como el lugar donde se
guardarán las variables globales del programa.
Las variables se declaran como Public y así son accesibles por todos los módulos y
todos los procedimientos de la aplicación. El módulo de declaración crecerá a medida
que avance el desarrollo. Entonces será necesario agrupar las variables por temas y
añadir un breve comentario descriptivo. Todo esto se hace para facilitar la comprensión
del código en el futuro.
Esta alimentación por código sólo es provisional y lo corregiremos más tarde. Este
proceder se debe evitar en la medida de lo posible. En el estudio de este ejemplo,
significará que la ruta de acceso a los ficheros es inamovible. Esto es un absurdo y
elimina cualquier flexibilidad a la aplicación en su interacción con el entorno.
El libro vuelve a tener tres hojas y vea el código del módulo ModGestionLibro actualizado:
4. Procedimientos con parámetros
Vamos a llevar a cabo una nueva funcionalidad que permitirá al usuario extraer una lista
de candidatos según el perfil buscado.
a. Concepción de la interfaz
b. Codificación de la funcionalidad
También la hoja Gestion de Perfiles que depende del botón CmdListaPerfiles se verá
afectada por el código que escribiremos.
En primer lugar, se vacía el contenido de la celda destino. De este modo, si al final el usuario
no escribe nada, el posible contenido anterior que hubiera en Range("F5").value no se tendrá
en cuenta. El objeto InputBox recupera la habilidad introducida por el usuario y la carga en la
celda de destino.
Si el usuario no introduce ninguna habilidad, el procedimiento se interrumpe con la
instrucción Exit Sub.
Cree ahora un procedimiento general que extraiga los candidatos cuyo perfil
corresponda a la habilidad introducida por el usuario. Llámelo
CargaListaPerfiles.
Como puede comprobar, toda esta serie de idas y venidas entre las dos hojas es visible
en la pantalla durante la ejecución del código. Esto no es lo mejor. Puede remediarlo
congelando la pantalla de código. Basta con utilizar la instrucción
Application.ScreenUpdating a True para congelar la pantalla y a False para
descongelarla.
Igual que para los mensajes de aviso de Excel, no olvide desactivar el congelado de
la pantalla.
1. Formato de listas
Nuestra lista se va a presentar en forma de una tabla con una cuadrícula de celdas. Esta
cuadrícula se tiene que adaptar a la longitud de la lista y redimensionarse en cada
petición del usuario. Esto es la base de un código dinámico.
La macro comienza por un rango de celdas definidas por código en el programa. Para
que el código sea dinámico, este rango debe adaptarse a las dimensiones de la lista y
ésta a cada petición del usuario. Hay que crear un bloque de código que recupere la
dirección de la última celda de la segunda columna. Conociendo la de la celda de inicio
([F5]), es posible entonces definir el rango y pasar sus coordenadas por parámetro al
procedimiento Sub CuadriculaRango().
b. Configuración de la cuadrícula
Este código ser refiere al rango seleccionado y el objeto With permite no tener que
repetir cada vez Selection.Borders(xlEdgeTop). Una vez se han escrito todas las
instrucciones, se cierra con la sentencia End With.
Estas secuencias With - End With se repiten seis veces. Las cuatro primeras representan
los bordes externos del rango. (Superior, inferior, lado izquierdo y lado derecho). Las
dos últimas son la cuadrícula interna (vertical y horizontal). De este modo, xlEdgeTop
define por ejemplo el borde superior.
Cada secuencia tiene tres propiedades: LineStyle define el tipo de línea, Weight su
grosor y ColorIndex su color. En el ámbito de nuestra aplicación, sólo nos interesa el
grosor de la línea. Las otras dos propiedades no cambian. Estos son los parámetros
enviados en la llamada del procedimiento Call CuadridulaRango("B6:" &
ActiveCell.Address, xlMedium, xlMedium, xlMedium, xlMedium, xlThin, xlThin,
xlContinuous, xlAutomatic) que alimentan las tres propiedades.
xlThin envía una línea fina para la cuadrícula interna y xlMedium una línea gruesa para
los bordes externos.
Los parámetros que se envían se recuperan en las siguientes variables declaradas como
Integer ya que xlThin, xlMedium, xlContinuous y xlAutomatic se interpretan en forma
de valores numéricos.
Estas variables son ocho: Superior As Integer, Inferior As Integer, Izquierda As Integer,
Derecha As Integer, Vertical As Integer, Horizontal As Integer, EstiloLinea As Integer
y Color As Integer. Hay seis variables para cada una de las seis secuencias With - End
With. Las dos últimas corresponden a las propiedades LineStyle y ColorIndex que no
cambian en todas las secuencias.
Hay que vaciar la lista antes de recargarla y suprimir la cuadrícula que la acompaña.
Las secuencias With - End With se reemplazan por una sola línea de código:
Selection.Borders(xlEdgeLeft).LineStyle = xlNone. Especifica a la propiedad LineStyle
que no hay ninguna línea visible.
Si para el código de creación de la cuadrícula hubiéramos optado por incluir solo la
propiedad Weight, el procedimiento CuadriculaRango hubiera sido el siguiente:
Antes de crear la cuadrícula, hay que comprobar que el filtro de la lista de perfiles
devuelve los candidatos correctamente. En caso contrario creará una cuadrícula que irá
de la celda de inicio ("B6") hasta los límites de la hoja ("IV:65536").
Para recuperar los niveles respectivos de los candidatos de la hoja Lista de inscritos,
vamos a utilizar la función integrada de Excel CONSULTAV (consulta vertical en una
lista). Esta consulta se puede hacer por el apellido de los individuos pero no es
suficiente ya que siempre existe la posibilidad de que haya dos nombres iguales. Para
limitar este riesgo, hay que añadir el nombre. Empezaremos por insertar una nueva
columna al inicio de la hoja en la que vamos a concatenar el apellido y el nombre de
cada candidato.
Para recuperar una función integrada con el grabador de macros, simplemente escríbala
en la barra de fórmulas de su libro con el grabador funcionando. A continuación valide
la fórmula con el botón Introducir de la barra de fórmulas.
Si cierra el grabador y mira la nueva macro generada, encontrará una traducción VBA
de esta función.
R define la fila (Row) y C la columna (Column). RC[1] equivale a la misma fila que la
celda destino pero una columna después para B2. RC[2] equivale a la misma fila pero
dos columnas después para C2. A continuación basta con estirar la fórmula hasta el fin
de la lista con la propiedad Autofill habiendo previamente seleccionado la última celda
de la lista. ActiveCell.Row recupera el número de la fila de la celda seleccionada.
Ahora vamos a insertar una nueva columna en la hoja Gestion de perfiles e insertar la
fórmula CONSULTAV para devolver el nivel correspondiente de cada candidato que
tenga la habilidad esperada por el usuario.
Se inserta una nueva columna y a través de un pegado especial, se copia el formato del
encabezamiento de la columna C en la nueva columna. A continuación, basta con
insertar la fórmula y estirarla hasta el fin de la lista. El código insertado en la celda D6,
FormulaR1C1 = "=VLOOKUP(RC[-2] & "" "" & RC[-1],’Lista de inscritos’!R1C1:R"
& ActiveCell.Row & "C5,5,FALSE)", corresponde a =CONSULTAV(B6 & " " &
C6;’Lista de inscritos’!$A$1:$E$83;5;FALSO) en la barra de fórmulas del libro.
Observe que el valor que alimenta FormulaR1C1 es una cadena de caracteres. Además,
para pasarle el número de línea de la última celda de la lista devuelta por IntLgLista,
hay que concatenar esta variable para que el programa la reconozca como tal y no como
parte de la cadena de caracteres.
Con un nuevo pegado especial, se copian los valores devueltos por la fórmula para
guardar sólo en las celdas éstos últimos. Deshacerse de la fórmula (CONSULTAV)
permite no depender de la columna de concatenación de la hoja Lista de inscritos y a la
vez poder suprimirla sin perder los niveles de los candidatos ya que la fórmula ha
quedado obsoleta.
Terminamos con la cuadrícula de nuestra nueva tabla, lo que permite comprobar que el
procedimiento CuadriculaRango funciona perfectamente.
Para poner a blancos la celda que indica la habilidad requerida por el usuario, hay que
tener en cuenta el hecho de que la inserción de una columna desplaza la dirección de las
celdas de columnas siguientes.
c. Gestión de la Clasificación
Ahora debemos insertar dos nuevos controles de tipo Botón de opción (llamado también
botón radio) para la gestión de la clasificación.
En la pestaña Propiedades, escoja la opción Mover, pero no cambiar tamaño con celdas
que permite la inserción de la columna de los niveles desplazando los controles sin que
se vea afectado su tamaño. Efectúe esta operación para los dos controles a la vez
seleccionándolos con la tecla [Ctrl] del teclado.
Name = OptClasApeNom
Caption = Clasificación por Apellido/Nombre
Name = OptNivel
Las siguientes propiedades serán idénticas para los dos botones de opción.
Height, Width, Left y Top definen la altura, la longitud, la distancia del control con
respecto al margen izquierdo y al margen superior. Las dimensiones deben ser idénticas.
Por el posicionamiento, sólo el margen izquierdo se debe definir para que se alineen los
controles. No ocurre lo mismo con el margen superior.
1. Ejecute la grabadora de macros para recuperar el código que permita ordenar por
el apellido y el nombre, ascendentemente.
Key1 indica la primera columna a ordenar y Order1 el orden con el que se efectuará. Es
posible efectuar como máximo 3 ordenaciones a la vez. Vamos a crear un
procedimiento que permitirá ordenar entre 1 y 3 columnas. Hay que pasarle por
parámetro las dimensiones del rango de celdas a tratar, el número de columnas a tratar y
por último los encabezados de las diferentes columnas en el orden de prioridad elegido.
Sería posible alimentar todas las propiedades de este bloque de código pasándolas en los
parámetros del procedimiento. Pero en el ámbito de este desarrollo, no es necesario
hacerlo tan complejo.
Sólo queda codificar las llamadas a la función después de la inserción de los niveles en
la hoja Gestion de perfiles y después de la consulta de la lista por el usuario con los
botones de opción.
Cuando durante la consulta el usuario decide modificar el orden de la tabla, hace clic en
una de los dos botones de opción. Hay que generar el evento Click de los dos controles.
Es posible acceder a estos eventos a través del menú contextual (clic derecho) de los
controles a nivel de la hoja Gestion de perfiles. Puede también utilizar en el IDE la lista
desplegable de los objetos del libro. Si selecciona OptNivel, la lista de eventos
disponibles se posiciona por defecto en el evento Click.
El procedimiento Private Sub OptNivel_Click() - End Sub se crea en el IDE. Sólo queda
llamar al procedimiento OrdLista con los parámetros esperados.
La cuenta por nivel para el cálculo de la parte porcentual de los niveles de una habilidad
se hace con un Select Case. Si el contador es diferente de 0, el resultado se formatea a
través de la función Format, en caso contrario la palabra "Ningún" reemplaza el
resultado.
En el mensaje mostrado al usuario, los saltos de línea segmentan y agrupan la
información a fin de facilitar su lectura y su interpretación.
Esta vez la ejecución del procedimiento comienza pero se para durante la ejecución del
código (Error de ejecución). Un mensaje indica un problema de división por 0, que es
un error grave en el lenguaje VB.
Si hace clic en el botón Finalizar, parará el programa. Hay que ejecutar el procedimiento
en modo depuración. En lugar de utilizar la tecla [F5] del teclado, pulse [F8]. La lectura
de las instrucciones se hará entonces paso a paso. Esto permite, posicionando el cursor
sobre cada variable, comprobar su valor y situar la instrucción exacta en la que se para
la ejecución.
El hecho de poder apuntar a una celda de una hoja de un libro que no esté activo,
evita las idas y venidas y la utilización de la propiedad ScreenUpdating.
Este procedimiento llama a una jerarquía de objetos o de controles que pueden ser
"padre" o "hijos" los unos de los otros.
4. La gestión de errores
Siempre se puede producir un error a pesar de las pruebas efectuadas antes del
despliegue de una aplicación. Pero la aparición de mensajes de error o la interrupción
brusca del programa causan mal efecto a ojos del usuario. Además, los parones
intempestivos pueden ocasionar pérdidas de datos o de trabajos que se están validando.
Una gestión eficaz de los errores permite seguir teniendo credibilidad a ojos del usuario,
incluso si la aplicación se bloquea.
Esta instrucción permite enviar cualquier error en el código a una etiqueta llamada
TratarError que vamos a insertar al final del procedimiento.
En este ejemplo, el número de error (gracias al objeto Err y una de sus propiedades Number)
se muestra al usuario que podrá indicarlo a informática. El mensaje también indica que se ha
interrumpido el proceso pero la aplicación está activa todavía.
El libro tiene como módulos sólo los objetos creador por defecto, es decir, los módulos
Hoja1, Hoja2, Hoja3 y ThisWorkbook. Ahora podemos probar la importación de
módulos a partir del directorio Copia_seguridad_Modulos.
El archivo se carga en el árbol del proyecto como módulo de clase y en el nuevo directorio del
mismo nombre. Si se selecciona el módulo de clase la zona de código está vacía. De hecho, en
él no se ha hecho ningún desarrollo en Gestion de perfiles.
Por contra, hay un procedimiento de captura de errores que avisa al usuario en caso de
error en el procedimiento de archivado.
Por medio de estos formularios, podemos crear un menú de usuario. Pero también es
posible intervenir directamente en los menús y barras de Excel para insertar nuevas
funcionalidades de la aplicación.
A partir de ahora, la mayor parte del diseño de los módulos o formularios se harán
insertando archivos .cls y .bas que se pueden descargar en la web que el editor dedica a este
libro.
Los formularios se almacenan en el directorio Hojas del árbol del proyecto. Cuando un
formulario se selecciona en la ventana de proyectos, es su aspecto, así como el de sus
controles, lo que aparece en la ventana de código.
Permite visualizar el aspecto del objeto. De este modo es posible insertar controles a
partir del cuadro de herramientas.
a. El formulario de identificación
El procedimiento Private Sub CmdSalir_Click (botón Salir) cierra la aplicación sin cerrar Excel, lo
que hubiera pasado con la instrucción Application.Quit. Se comienza por descargar
Frm_Identificacion y a continuación se cierra el libro activo ThisWorkBook sin grabar las
modificaciones ya que en este momento de la aplicación, el usuario no ha accedido a las
diferentes funcionalidades ni a las hojas de la aplicación.
b. El menú de usuario
Hay que desactivar el botón de cierre del formulario para que la introducción de la
contraseña sea obligatoria para acceder a la aplicación.
En el capítulo Gestión de la aplicación con una base de datos veremos que con la
identificación es posible recuperar su nivel de acceso sobre la aplicación. Esto significa
que un usuario tendrá acceso a diferentes funcionalidades o menús de esta aplicación en
función de sus permisos. En este caso, no hay que dejar ver al fondo una hoja que puede
tener datos para los que no tiene acceso.
Al abrir el libro, la propiedad IsAddin del objeto ThisWorkbook se pone a True. Las
hojas de trabajo no se pueden ver ni acceder. No es posible abrir el IDE mientras la
propiedad está activada. Se crea un nuevo procedimiento
Workbook_WindowDeactivate para determinar el momento en que el libro Gestion de
perfiles no está activo. Esto ocurre por el cierre de este último por el usuario o por la
selección de otro libro abierto. La propiedad IsAddin pasa a False.
Antes de probar la aplicación, hay que tener en cuenta el hecho de que las hojas del
libro no están accesibles. En la validación de los datos entrados por el usuario, la
propiedad IsAddin debe estar a False y luego volver a True mientras el programa vaya a
consultar la hoja Referencias. Esto permite evitar el siguiente mensaje de error:
Puede probar el libro cerrándolo sin tener que grabar sus modificaciones, ya que
el procedimiento de evento Workbook_BeforeClose del módulo ThisWorkBook
lo hace automáticamente.
El resultado en la pantalla corresponde a lo que esperamos pero, a pesar de todo, queda
un problema. En la carga del libro, justo antes de que el programa ponga la propiedad
IsAddin a True, aparece fugazmente la última hoja activa del libro.
Es probable que esté habituado en las aplicaciones que utiliza cotidianamente a activar
un botón por defecto con la tecla [Intro] del teclado. Es lo que va a hacer en los
formularios Frm_Identificacion y Frm_Menu.
1. El formulario o UserForm
Vamos a ver las principales propiedades, los principales métodos y los principales
eventos del formulario.
Ejemplo de propiedades
Ejemplo de métodos
Ejemplo de eventos
Load: carga en memoria del formulario que no tiene porqué estar visible.
TabIndex: gestiona el orden de acceso a los diferentes controles a través del tabulador.
DblClick: corresponde a un doble clic del ratón del usuario sobre el control.
ListIndex: permite definir o enviar el índice del elemento seleccionado. Por defecto el
índice está a 0 para el primer elemento de la lista. Si no hay ningún elemento
seleccionado, el índice devuelve el valor -1.
Sorted: ordena la lista por orden alfabético.
ListIndex: permite definir o enviar el índice del elemento seleccionado. Por defecto el
índice está a 0 para el primer elemento de la lista. Si no hay ningún elemento
seleccionado, el índice devuelve el valor -1.
Default: define el botón como botón por defecto activado por la tecla [Intro] (propiedad
a True).
Para reducir las posibilidades de acciones en la hoja Inicio, vamos a eliminar las barras
de herramientas.
La activación se hace con los valores de tipo booleano True o False. Sus equivalentes
numéricos son 1 y 0. En la llamada al procedimiento, el valor 0 se pasa por parámetro
para desactivar las barras.
Respecto a los valores booleanos, 0 se utiliza como False mientras que cualquier
valor diferente de 0 puede ser True.
Vea la hoja sin estos atributos habituales como la barra de fórmula, la barra de estado, la
cuadrícula y los encabezados de fila y columna.
b. La creación del menú Gestión de perfiles
For Each - Next, revisa todos los menús activos en Excel y los identifica por su
propiedad Caption (nombre visible). Si el menú existe, el programa sale del
procedimiento. La instrucción Each permite recorrer todos los controles contenidos en
un objeto. En este caso se trata de los controles de Menús de Excel:
MenuBars(xlWorksheet).Menus. La variable Cmd de tipo objeto se declara en el
encabezado del módulo ya que se utiliza en diversos procedimientos.
El menú Gestión de perfiles se crea así como los submenús asociados. La propiedad
OnAction vincula el submenú (MenuItems) a un procedimiento que se ejecutará por la
selección del usuario.
c. La eliminación del menú Gestión de Perfiles
La inserción y eliminación del menú se hacen al abrir y cerrar el libro. Estos son los
procesos de eventos del módulo ThisWorkbook que se van a utilizar de nuevo.
El nombre de la hoja a eliminar se pasa por parámetro. El bucle de tipo For - Next
permite comprobar la existencia de la hoja. Si llega el caso, se elimina desactivando los
mensajes de alerta durante la eliminación y el programa sale del bucle.
b. Estudio de la función de comprobación de carga del formulario Frm_Seleccion
El botón Salir cierra el formulario. La hoja Selección por profesión se elimina previamente. El
cierre con el aspa se deshabilita como en otros formularios de la aplicación.
El botón Validar ejecuta la implementación de los subtotales.
El procedimiento se inicia con una instrucción de captura de errores que envía a una
etiqueta llamada TratarError. La variable StrRango se declara localmente en el
encabezado del procedimiento ya que no es necesario que se vea desde ningún otro
procedimiento. A continuación se comprueba la entrada de usuario y se sale de
procedimiento si no se ha introducido ningún rango.
El programa excluye a continuación todas las filas que no forman parte de la selección
del usuario.
El rango ahora está ordenado por profesión y por nivel. Estos grupos son necesarios
para el cálculo de los subtotales.
Ahora hay que insertar los subtotales en la lista. Esta vez, la barra de título se incluye en
la lista seleccionada: Range("A1:E" & Range("A2").End(xlDown).Row).Select. El
método Subtotal se aplica a la selección con opciones de formato del resultado:
Este submenú permite al usuario volver al formulario del menú principal. Ejecuta el
procedimiento Menu_CargaMenu del módulo ModMenus.
La hoja Subtotal por profesión se elimina llamando al procedimiento EliminarHoja.
Antes de cargar Frm_Menu, se vuelve a activar la propiedad IsAddin.
Este submenú permite al usuario cerrar la aplicación sin cerrar Excel. Ejecuta el
procedimiento Menu_CerrarAplicacion del módulo ModMenus.
El usuario puede elegir salir de la aplicación por el menú Excel Archivo - Cerrar o Salir
pero también con un clic en el aspa de cierre del libro.
a. Creación de la barra
Sus respectivas referencias de objeto se les asignan a continuación con las siguientes
propiedades asociadas:
Una vez creada, la barra de herramientas se muestra con la propiedad Visible a True.
b. Submenú y gestión de la barra en la aplicación
Sólo queda gestionar las llamadas a los procedimientos para la creación y la eliminación
de la barra.
CboNivel se llena con la variable TabNivel de tipo Variant. La función Array permite
asignarle una tabla que contiene los tres niveles. La propiedad List permite asignar los
valores de una tabla a contenedores como los ComboBox o los ListBox.
Para este ejemplo, los datos se escriben por código pero este proceder se debe
evitar para poder modificarlos si es necesario.
CboProfesiones se llena a partir de una lista almacenada en la hoja Referencias. Esta lista se
revisa en un bucle Do While - Loop mientras la celda no esté vacía. El valor recogido se carga
con la propiedad AddItem.
Hay que tener cuidado de no provocar un bucle infinito en el programa. Es lo que
ocurre cuando se olvida la suma del contador (la variable i en nuestro ejemplo).
La celda que se revisa es la misma y el código no puede salir del bucle. Si el
programa entra en un bucle, puede utilizar la combinación de teclas [Ctrl][Pausa]
para interrumpir la ejecución del código.
Para tratar los diferentes elementos de la tabla, se utiliza el índice. Por defecto,
inicialmente el índice es 0 pero la instrucción Option Base 1 en el encabezamiento del
código del formulario da el valor 1 al primer elemento de la tabla TabInscrit().
Si el botón Validar está habilitado, es posible registrar las modificaciones realizadas por el
usuario.
Si el usuario escoge el botón No, los detalles del estado del miembro se vuelven a
cargar con el procedimiento DetalleMiembro.
Cada integración en este calendario corresponde a una tarea. Cada una de estas tareas se
vincula el apellido, el nombre y la profesión del individuo afectado. Sólo se puede
considerar a los individuos disponibles para un trabajo. Se asocia una fecha de inicio de
tarea a un número de días asignados. La fecha final se calcula entonces en función de un
calendario en el que se tendrá en cuenta los días laborables y no laborables. Toda esta
información se convertirá en un histograma en el que cada barra está vinculada a una
tarea asociada al trabajo en curso.
La creación de un nuevo trabajo pasa por la carga de un nuevo calendario a partir del
directorio Plantillas. Una vez se introduce el trabajo y el nombre del cliente, el usuario
puede grabarla en un directorio con el nombre del cliente en el directorio Trabajos.
Es necesario ocultar las hojas Gestion de perfiles y Lista de inscritos que se convierten
en hojas de referencia del proyecto. Para ello, hay que asegurarse que en cada referencia
en el programa a una de estas hojas se pueda acceder a ellas y que su propiedad Visible
sea True. Por el contrario, al volver al formulario de menú o al salir de la aplicación, su
propiedad Visible debe entonces pasar a False.
Sitúe este bloque de código justo antes de las llamadas a procedimientos para la
eliminación de menús y de barras en:
Menu_CargaMenu en ModMenus.
Workbook_BeforeClose en ThisWorkbook.
La hoja Calendario Trabajo contiene el calendario del trabajo para cada candidato
asignado, las fechas de las tareas que tiene que realizar. Estas fechas y duraciones se
muestran en el histograma.
Mueva y copie en el libro Gestion de Perfiles, la hoja de referencias Calendario del
libro Plantilla_Equipo.
Este calendario cubre para nuestra presentación todo el año 2010 hasta el 31 de
diciembre incluido. Basándose en la columna H (Fecha), puede efectuar una búsqueda a
partir de la fecha introducida por el usuario y devolver los datos correspondientes como
el número y el nombre del mes, el día de la semana, el número del día y su estado (F
para festivo o W para fin de semana).
Puede hacer un copiar-pegar de uno de los dos botones. Esto permite recuperar
las dimensiones idénticas a los anteriores.
Name: CmdCalendario
Caption: Creación de un calendario de trabajo
a. Modificación en InsertarMenu
b. Modificación en EliminarMenu
Si se cargan hojas para grabar un nuevo trabajo, debemos poder suprimirlas para poder
volver a hacer la misma operación más veces.
Inserte el siguiente código en los procedimientos Workbook_BeforeClose del
módulo ThisWorkbook y Menu_CargaMenu del módulo ModMenus. En los dos
casos, el bloque de código se debe situar justo después de la instrucción que
elimina la hoja Subtotal por profesion.
Para el cierre de la aplicación por el submenú Salir de la aplicación del menú Gestión de
Perfiles, la gestión de la eliminación de la hojas se hace en el procedimiento
Menu_SalirAplicacion que es llamado por el submenú.
Ahora puede probar la carga de las hojas plantillas de gestión de calendario y los
menús asociados.
Gestión de las asignaciones y de las fechas de los trabajos
Ahora, el proceso de carga y de eliminación de las hojas para la creación de nuevos
trabajos está totalmente integrado en la navegación de la aplicación. Queda implementar
la asignación de candidatos a los nuevos trabajos y la gestión del calendario.
Un ListBox que se llama Lst_Equipo. Todas las propiedades son las que tiene
por defecto el objeto cuando se crea.
Un botón de comando que se llama Cmd_Salir.Su nombre visible es Salir
(Caption) y la propiedad Default está a True.
La ejecución del formulario se hace por medio del botón de comando Asignación de
individuos o Cmd_Gestion_Equipo de la hoja Asignación Trabajo.
Para la carga del ListBox, se inserta una hoja de trabajo Carga del ListBox en el libro.
Préviamente, se inserta un filtro en la hoja Lista de inscritos para copiar sólo en la nueva
hoja de trabajo los individuos que tenga disponibilidad a SÍ (Field:=5, Criteria1:="Si").
Entonces se copian sólo las columnas de los apellidos, nombres y profesiones
(ActiveSheet.Columns("A:C").Copy).
A continuación hay que ordenar la lista copiada en la hoja Carga del ListBox. En lugar
de ejecutar el procedimiento OrdenarLista del módulo ModFormato, se utiliza el
siguiente código:
El método Sort permite ordenar el rango de celdas de datos que se adjunta. Las
instrucciones Key1 y Key2 recuperan el encabezado de las dos columnas sobre las que
se hace la ordenación.
El ListBox se puede alimentar ahora. Esta operación se hace sin utilizar la propiedad
List asociada a una tabla o el método AddItem insertado en un bucle.
El clic del usuario en un línea del ListBox selecciona en la tabla al individuo que se a
inscribir.
Como en una tabla de dos dimensiones, los datos se recuperan con dos índices que
empiezan en 0: el número de columna asociado al número de línea.
c. Cierre del formulario e inicialización de las hojas Asignación Trabajo y Calendario
Trabajo
Antes de insertar una tarea, el programa comprueba que la tabla de asignaciones se ha llenado
correctamente y se ha introducido una fecha de inicio para la celda C7. Si no se cumple una de
las dos condiciones, se muestra unos de los siguientes mensajes y el programa sale del
procedimiento.
La fecha de actualización del documento se recupera en la celda C8 con la fórmula
=AHORA() en cada apertura del calendario.
En este momento sólo queda insertar las listas creadas al cerrar el formulario
Frm_Gestion_Equipo. Se inserta la lista de inscritos seleccionados Rango_Apellidos en
la columna Apellido y Nombre (Columna C). La lista de profesiones referenciadas
Rango_Profesiones se inserta en la columna Profesión (Columna D) del calendario.
Antes de salir del procedimiento, si la celda no está vacía antes de la entrada el valor inicial se
recarga con una llamada a la función EntradaOrigen. El código correspondiente es el siguiente:
Target.Value = EntradaOrigen(Target.Value).
Si existe un valor introducido por el usuario y pasado a través del parámetro
VarValorOrigen, se reenvía el valor original almacenado en VarValorOrigen a través de
la función y la variable Target se incrementa. Es posible que la celda esté vacía en
origen. En este caso, EntradaOrigen devuelve un valor vacío de tipo String: "".
Estas dos variables se declaran de tipo Variant ya que el dato introducido por el usuario
o el contenido original de la celda pueden ser de cualquier tipo.
La entrada de una fecha de inicio no puede ser inferior a la fecha de inicio del trabajo.
Se anula la entrada del usuario. El valor inicial se vuelve a cargar, un mensaje avisa al
usuario y el programa sale del procedimiento.
La fecha de inicio no puede ser superior a la fecha de inicio de cualquier tarea. Se anula la
entrada del usuario. El valor inicial se vuelve a cargar, un mensaje avisa al usuario y el
programa sale del procedimiento.
El siguiente bloque de código implementa con una función integrada de Excel de tipo
ConsultaV la recuperación de la profesión del individuo cuando el usuario elige en la
lista desplegable.
Se preve la alimentación de los encabezados hasta el final del año en curso, como los
datos de la hoja Calendario.
A continuación, el código comprueba que la fecha no sea inferior a la fecha de inicio del
trabajo. El valor inicial se vuelve a cargar, un mensaje avisa al usuario y el programa sale del
procedimiento.
Una vez pasa la validación, el programa muestra las columnas que han quedado ocultas:
Selection.EntireColumn.Hidden = False. Con un bucle For - Next se ocultan las columnas. El
contador del bucle se incrementa por la diferencia entre la fecha de inicio del trabajo y la fecha
de inicio de visualización: For i = 9 To DateDiff("WW", Range("C7").Value, Range("C9").Value) +
8. La función DateDiff devuelve la diferencia entre las dos fechas en semanas (WW).
En una eliminación de los días asignados, se vacían las zonas de entrada de datos de
inicio de tarea y de fin de tarea.
La línea del histograma se vacía cada vez.
Para las entradas no nulas de fecha de inicio de tarea y de días asignados, se ejecuta el
procedimiento de cálculo de fecha de fin Gestion_Fechas del módulo ModGestionFechas. El
procedimiento de alimentación del histograma CalendarioTrabajo se ejecuta al mismo tiempo.
La fecha de fin de tarea se calcula con la fecha de inicio como punto de inicio y el
número de días asignados como la duración.
Si hay una fecha de inicio y un número de días asignados, estos datos se pasan por
parámetro al procedimiento Buscar_Fecha.
Como máximo, el bucle Do While - Loop hará 53 pasadas antes de parar. Esto
corresponde al tamaño máximo del histograma de la columna I hasta AZ.
Ahora hay que entrar en un nuevo bucle Do While - Loop anidado en el primero.
Mientras el número de semana corresponda, si el día seleccionado en la hoja no es
festivo o de fin de semana, la celda de del calendario se incrementa en una unidad por
día trabajado. Por el contrario, el número de días asignados se disminuye en una unidad
(numDia = numDia - 1) y si es igual a 0, el programa sale del bucle.
Una vez se ha salido de este segundo bucle anidado, si numDia es igual a 0, se sale del
primer bucle y del procedimiento. El programa pasa a la fila siguiente de la hoja
Calendario. La variable Fecha_Ini se alimenta con el número de días pasados,
trabajados o no (variable j): Fecha_Ini = DateAdd("d", j, Fecha_Ini).
4. Guardar el trabajo
Cada calendario creado es un nuevo trabajo que hay que guardar. Esto requiere una
copia de la hoja plantilla Calendario Trabajo sin copiar el código VBA que contiene la
hoja.
El menú Grabar el trabajo llama al procedimiento GuardarTrabajo del módulo
ModGestionCalendario.
Se valida la existencia del directorio destino, así como la existencia del archivo a
guardar.
A continuación, el programa inserta una nueva hoja de trabajo en el libro llamado
Trabajo. Se copia el contenido de la hoja Calendario Trabajo sin el código VBA
vinculado a esta hoja (sólo los valores y el formato).
Por último, la hoja Trabajo se mueve a un nuevo libro y este último se graba con las
variables StrRuta y StrNomFic.
A continuación se cierra el nuevo libro sin grabar las modificaciones. Si todo va bien y no se
captura ningún error, se envía un mensaje al usuario comunicándole que el archivo se ha
guardado correctamente.
Hay que establecer una conexión desde Excel a la Base De Datos (BDD) para tener
acceso a los datos que contiene. Es entonces cuando es posible consultar, crear, eliminar
o modificar los datos de esta base de datos. Por medio de los comandos del lenguaje
Visual Basic Application que también soporta Access vamos a poder utilizar otro
lenguaje dedicado al tratamiento de datos, llamado SQL (Structured Query Language).
En este capítulo, vamos a ver como gestionar datos como la lista de inscritos, las fechas
del calendario o la lista de las diferentes profesiones definidas en la agencia de trabajo
temporal.
Haremos una presentación rápida del lenguaje SQL y de la base de datos Access.
Veremos también como utilizar cuadros de diálogo para encontrar y grabar las rutas de
acceso a los archivos destino en el árbol de directorios del sistema operativo. Entonces,
podremos estudiar la base de datos y lanzar consultas SQL con VBA para manipular los
datos.
Esto no significa que no se puedan hacer cambios en los datos. Si tomamos el ejemplo
del estado de los miembros de la agencia, su disponibilidad evoluciona con las
asignaciones a trabajos. Esta evolución se debe poder informar en cada cambio de
estado en la base de datos.
Una vez gestionada en una base de datos, la información está disponible para otras
aplicaciones desarrolladas en VBA Excel, en otra herramienta de Office o en otro
lenguaje de programación. Lo esencial es poder establecer una conexión a la base de
datos Access BDD_Agencia. Todas las operaciones como la creación, la eliminación o
la modificación de datos son visibles después de la validación por el conjunto de
aplicaciones conectadas a la base.
1. Las tablas
Todos los datos contenidos en las hojas del libro Excel se han introducido en la tablas
Tab_Calendario y Tab_Inscritos. Lo mismo ocurre para algunos datos de la hoja
Referencias (es importante no eliminarla). La información del nombre de usuario y la
contraseña se gestionan ahora en la tabla Tab_Usuarios. La lista de profesiones de los
miembros de la agencia se almacena en la tabla Tab_Profesiones. En la siguiente
ilustración se representan las diferentes tablas de la base de datos BDD_Agencia y sus
diferentes campos.
En Access, las tablas son objetos en los que se agrupa la información. Si tomamos como
ejemplo la tabla Tab_Calendario, los encabezados de la columnas son ahora los encabezados
en los campos.
La mayor parte de las operaciones relativas a los intercambios con la base de datos se
agrupan en este módulo.
El procedimiento comienza recuperando la ruta de la base de datos. Esto se consigue con una
llamada al procedimiento CargaRutaBDD del módulo ModBDD.
Por defecto, la ruta de la base de datos se almacena en la hoja Referencias del libro. El
proceso que establece la conexión se ejecuta en el formulario de identificación
Frm_Identificacion, lo que justifica deshabilitar la propiedad IsAddin en el acceso a la
hoja Referencias que está oculta. La variable StrRuta se carga con la ruta de la base de
datos recuperada.
Hasta ahora, nos hemos conformado con tener la ruta de los ficheros a leer o guardar en
el código. Esta solución no es nada flexible ya que en caso de modificación del entorno
de trabajo, hay que modificar el código para modificar la ruta. Pero vamos a
implementar otra solución más dinámica con los cuadros de diálogo.
El usuario puede escoger cancelar (IntRes = 2) y la aplicación se cierra sin guardar los
datos. Si por contra escoge reintentar (IntRes = 4), el programa llama al procedimiento
RutaBDD del módulo ModBDD.
Después de haber analizado los procedimientos CargaRutaBDD y RutaBDD, volvamos otra vez
al procedimiento CnxDB.
Si la conexión está abierta, hay que cerrarla para no dejar que los procesos consuman
inútilmente recursos del sistema.
Antes de intentar cerrar la conexión, hay que comprobar que está activa con la función
IsEmpty que comprueba si el objeto CnxBDD está inicializado correctamente. En el
caso en que el usuario decida salir de la aplicación antes de conectarse o si no se llega a
identificar, en el cierre del libro (procedimiento Workbook_BeforeClose) se ejecuta de
todos modos el procedimiento FinCNX.
El aspecto de este formulario es bastante sencillo. Muestra un TextBox que tiene como
nombre real TxtRutaBDD. Este TextBox de entrada y salida es suficientemente largo
para contener una ruta de base datos.
El botón que sólo tiene tres puntos (...) tiene como nombre real CmdOrigenBDD.
Este formulario no hace más que repetir las funcionalidades presentadas en los párrafos
anteriores.
Puede probar esta funcionalidad para que la aplicación apunte a la base de datos
BDD_Agencia.accdb del nuevo directorio Base de datos.
3. El lenguaje SQL
Este es un lenguaje de consulta de base de datos relacionales. Existen instrucciones para
seleccionar, eliminar, inserter o actualizar registros en la base de datos. Vamos a
comenzar por mostrar los principales comandos susceptibles de utilizarse en la
aplicación Gestión de Perfiles.
Los comandos:
b. La elaboración de la consulta SQL
Para ilustrar nuestro ejemplo, vamos a utilizar una tabla ficticia llamada Tab_Clientes
que contiene diversos campos como Apellidos, Nombre, Edad, Dirección, Población,
Código Postal, País, Teléfono... Estos campos agrupan todos lo datos relacionados con
los clientes.
La selección de registros
SELECT * FROM Tab_Clientes.
Esta consulta devuelve por defecto todos los datos de todos los campos de la tabla
Clientes. El carácter * indica que se seleccionan todos los campos después del SELECT.
No hay condición(es) y se seleccionan todos los clientes.
En esta segunda consulta, sólo se devolverán los valores de los campos indicados.
Además, se seleccionarán sólo los clientes de la tabla Tab_Clientes que tengan más de
25 años y vivan en España. Este último criterio de la condición no forma parte de los
campos devueltos por la consulta. En el código SQL, todos los datos de tipo
alfanumérico van entre comillas simples (’España’). Las fechas van entre almohadillas
(#01/10/2010#) y sólo los datos de tipo numérico no se enmarcan con ningún otro
elemento.
Esta consulta es idéntica a la segunda pero además está ordenada por los apellidos y el
nombre del cliente. Por defecto, la clasificación es ascendente (ASC). Para que sea
descendente, hay que incluir la sentencia DESC.
La eliminación de registros
DELETE FROM Tab_Clientes WHERE CONDICION.
Con esta consulta, se eliminan registros de una tabla. Pero hay que indicar una
condición poque sino se eliminan todos los registros de la tabla.
La inserción de registros
INSERT INTO Tab_Clientes VALUES (Campo1, Campo2, Campo3 ...).
Con esta sentencia, se modifica el Campo1 de todos los registros de la tabla. Igual que
en la eliminación, hay que indicar una condición si no quiere actualizar todos los
registros de la tabla.
Para una mejor legibilidad del código, es aconsejable poner todas las sentencias
SQL en mayúsculas.
Para dar más facilidades, la base de datos Access tiene una herramienta gráfica
integrada de creación de consultas con un generador de SQL. Esto permite
probar la consulta una vez que está construida para validar el resultado y
sobretodo para recuperar el código SQL e integrarlo en el código de la aplicación
en desarrollo.
a. El objeto Recordset
La instrucción Set asigna a RstBDD una referencia al objeto Recordset que le permite
recuperar todas las propiedades y métodos (Set RstBDD = New ADODB.Recordset).
RstBDD se asocia a la conexión activa a la base de datos (RstBDD.ActiveConnection =
CnxBDD) y se transmite la consulta (RstBDD.Open (RequeteSQL)).
Para autentificar la entrada del usuario en su petición de identificación, hay que acceder
a la tabla Tab_Usuarios. La conexión a la base se ha activado por medio de la
instrucción Call CnxDB. Tenemos que construir la consulta SQL adecuada y cargarla en
la variable de tipo String StrSQL.
La función Trim permite suprimir los espacios que se hayan podido dejar en la
introducción y la función UCase convierte el texto en mayúsculas.
El objeto a concatenar se enmarca del siguiente modo: " & Objet & ". Las dobles
comillas permiten cerrar la cadena SQL pero como en nuestro caso se trata de datos de
tipo alfanumérico, debemos añadir las comillas simples: ’" &
UCase(Trim(TxtNombreUsuario.Value)) & "’.
Puede eliminar las hojas ocultas Calendario y Lista de Inscritos. Pero también
hay que modificar los tres procedimientos de evento Workbook_BeforeClose del
módulo ThisWorkbook, Menu_CerrarAplicacion del módulo ModMenus y
CmdSalirMenu_Click del formulario Frm_Menu. En el bloque de código que
gestiona la eliminación de las hojas de trabajo, el número de hojas totales por
defecto de la aplicación pasa de 5 a 3 (método Count) como se muestra en las
siguientes líneas:
Las hojas Calendario y Lista de Inscritos que se crean y alimentan a partir de la base de
datos se suprimirán después de utilización igual que las hojas Calendario Trabajo y
Asignación Trabajo.
La recuperación de los datos enviados por el Recordset se hace de nuevo con un bucle Do
While - Loop y comprobando el fin de archivo EOF. Una vez que se han cargado todos los
inscritos de la agencia en la hoja, se cierra el objeto RstBDD.
Se oculta la hoja Lista de inscritos y se pone la propiedad IsAddin a True.
Como hemos visto anteriormente, existen comillas simples en las cadenas de caracteres
SQL para delimitar valores de tipo alfanumérico. Pero pueden existir comillas simples
en forma de apóstrofos dentro de las cadenas de caracteres que componen los valores
que se soportan por las consultas SQL. Es por ejemplo el caso del apellido DE L’HORT
en la lista de inscritos. Pero en el lenguaje Visual Basic y en VBA, la comilla simple se
utiliza para comentar código obsoleto o texto relativo al desarrollo en curso.
Para solucionar esto, la función Apostrofo se utiliza para recuperar con un bucle For -
Next la cadena de caracteres y buscar si hay alguna comilla simple para duplicarla
(Apostrofo = Apostrofo & Mid(StrCadena, i, 1) & "’"). A pesar de esto, sólo se grabará
una comilla en la base de datos. Puede probar esta función grabando un trabajo con un
nombre de cliente que tenga una o varias comillas.
Los valores de tipo fecha se delimitan con el carácter # para identificarse como
tales.
Por contra si la disponibilidad pasa a NO, esta cadena se ignora ya que toda la
información relativa a la duración del trabajo y el nombre del cliente no se pueden
recuperar más que de la hoja Calendario Trabajo en la creación y la grabación de un
nuevo trabajo.
Antes de ejecutar la consulta, hay que volver a ejecutar la conexión con la base de datos
ya que es posible que esta última se haya interrumpido. Esto es especialmente así
después de la llamada al procedimiento Menu_CargaMenu. Se comprueba la conexión y
después se vuelve a ejecutar si es necesario: If IsObject(CnxBDD) Then. No es
necesario pasar de nuevo por las fases de búsqueda y verificación de la ruta ya que la
variable StrRuta ya se ha cargado y se ha validado su contenido.
A continuación, es posible ejecutar la consulta. Pero esta cadena SQL tiene una
particularidad más: la de ser una consulta de actualización. De hecho, no sólo recupera
registros como el comando SELECT. Actúa modificando los registros de la base de
datos. Además, vamos a utilizar el método Execute del objeto CnxBDD. Basta con
enviarle la consulta para que se aplique en la base de datos (CnxBDD.Execute StrSQL).
El método Execute se puede utilizar con las cadenas SQL de tipo UPDATE, INSERT
o DELETE.
El programa recorre todas las tareas creadas por el trabajo con un bucle de tipo Do
While - Loop.
Para resolver este problema, basta con concatenar los campos APELLIDO y NOMBRE
de la consulta: "WHERE (APELLIDO + ’ ’ + NOMBRE) = ’" & Range("C" & i).Value
& "’". En el lenguaje SQL, el signo + permite sumar valores numéricos pero también
concatena valores de tipo alfanumérico.
Haga clic con el botón secundario del ratón en la ventana del proyecto y seleccione en
el menú desplegable Propiedades de VBAProject.
En el formulario VBAProject - Propiedades del proyecto, seleccione la pestaña
Protección.
Para activar su contraseña y probarla, tiene que cerrar el proyecto y volver a ejecutarlo.
Después de seleccionar un menú, ejecute el IDE.
Para su mayor comodidad, se aconseja que una vez haya validado la contraseña y
cuando el IDE esté accesible, realice la operación inversa deshabilitando la contraseña.
Así, puede comprobar por código si la entrada es lo que el programa espera y detectar
una posible entrada no válida. La solución implica una interrupción del programa en
curso, la eliminación de la entrada no válida y el envío de un mensaje al usuario. Este es
el método que hemos aplicado en Gestión de Perfiles.
Existe una solución más radical que consiste en bloquear cualquier posibilidad de
introducir caracteres no deseados incidiendo directamente cuando el usuario escribe en
el teclado.
1. El código ASCII
Cada carácter tiene su equivalente en código numérico: es el código ASCII (American
Standard Code for Information Interchange). Este código permite identificar y trabajar
sobre las pulsaciones en el teclado. Hay que utilizar el evento KeyPress. Se activa
cuando el usuario presiona una tecla, lo que permite recuperar el código ASCII de la
tecla. Se devuelve un valor numérico que permite identificar la tecla presionada en el
teclado.
Vea la tabla que presenta los códigos de las principales teclas susceptibles de utilizarse
en un programa.
El código ASCII de la tabla identifica un total de 96 tecas pero existen más.
Los códigos entre 0 y 31 son caracteres de control ya que permiten hacer acciones como
saltos de línea o tabulaciones. Los códigos entre 48 y 57 son los números. Los códigos
entre 65 y 90 representan las letras del alfabeto en mayúsculas. Los códigos entre 97 y
122 representan las letras del alfabeto en minúsculas. El resto de códigos representan los
operadores y signos de puntuación.
Cada pulsación en las teclas del teclado genera un evento de tipo KeyPress recuperado
por el procedimiento con la instrucción ByVal. Conociendo el valor de las teclas,
podemos limitar las teclas disponibles a las letras del alfabeto y al retorno de carro.
Todas las teclas que no estén incluidas en la condición del If se deshabilitan
(KeyAscii.Value = 0). El retorno de carro (código ASCII igual a 8) se permite para
permitir al usuario volver hacia atrás en caso de error al teclear.
Desmarque todas las opciones seleccionadas en la lista Permitir a los usuarios de esta hoja de
cálculo:. En la zona de entrada de la contraseña, respetando la mayúscula, escriba: Inicio.
Después de haber validado la entrada con el botón Aceptar, un segundo cuadro de diálogo
llamado Confirmar contraseña le pide introducir de nuevo la contraseña en una zona llamada
Contraseña.
En esta hoja, todas las celdas se dejan bloqueadas ya que no hay ninguna entrada directa
del usuario. La creación del equipo para alimentar las tareas del trabajo se hace con el
formulario Frm_Gestion_Equipo y el código asociado.
Después de haber hecho clic en el botón Aceptar del MsgBox, se muestra el IDE en modo
depuración.
Haga clic en el botón Restablecer del IDE para interrumpir el proceso de depuración.
Hay que desbloquear las celdas de entradas del usuario. En la hoja Asignación Trabajo
se trata de las zonas de entrada del nombre del trabajo (B4), de las fechas de inicio de
trabajo (C7) y de inicio de visualización del calendario (C9). Hay dos métodos para
hacerlo.
Con un clic en el botón secundario del ratón en la celda, seleccione en el menú
contextual el submenú Formato de celda.
Seleccione las tres celdas (B4, C7 y C9) al mismo tiempo y el menú Formato.
Haga clic en el submenú Bloquear celda. Como las celdas estaban bloqueadas
por defecto, esta acción las desbloquea. Con el mismo método, podría
bloquearlas de nuevo.
Como para la hoja Asignación Trabajo, se han dejado bloqueadas todas las celdas ya
que no hay ninguna entrada directa del usuario. La gestión de los datos de esta hoja se
hace exclusivamente por el código contenido en los formularios y los procedimientos
llamados.
Una vez cargada la tabla, hay que reactivar de nuevo la protección de la hoja.
Aquí, el procedimiento de reactivación se llama con el parámetro True que significa que
se va a reactivar la protección de la hoja. La contraseña siempre acompaña a la llamada
de la función.
Aparte del nombre del cliente, el usuario introduce las fechas del inicio del trabajo y el
inicio de la visualización del calendario. La fecha de inicio de visualización la puede
modificar el usuario directamente o indirectamente en la introducción o modificación de
la fecha de inicio de trabajo. En ambos casos, esta acción implica una modificación de
la visualización de la tabla y entonces las columnas se marcan o desmarcan. Esto
necesita una gestión del bloqueo de la hoja Calendario Trabajo.
Recuerde que en la inserción de una nueva tarea (botón Insertar tareas), se llama al
procedimiento InsertarTareas del módulo ModGestionCalendario. Se crea una máscara
con dos listas desplegables para elegir el individuo y la profesión.
Pero hay que dar la posibilidad al usuario de realizar la selección en las dos listas
desplegables, de introducir una fecha de inicio de tarea y de asignar un número de días.
Se deben desbloquear estas celdas al mismo tiempo que se añade la tarea al calendario.
Para introducir una fecha de inicio de tarea, debe insertar las llamadas de
desactivación y de reactivación de la protección de la hoja en el procedimiento
de evento Gestion_Entrada del módulo ModIntroduccionCalendario, en concreto
en el bloque de código de las modificaciones de la fecha de inicio de tarea al
final del procedimiento.
Para la entrada del número de días asignados, debe insertar las llamadas de
desactivación y de reactivación de la protección de la hoja en el mismo procedimiento
que antes. Pero esta vez la inserción se hace en el bloque de código del control de la
entrada del número de días asignados.
La introducción de días asignados asociados a la fecha de inicio de tareas comporta la
ejecución de los procedimientos de cálculo y de informe en el histograma de la tarea en
cuestión.
Inserte en el bucle Do - While que lista todas las tareas del calendario, las
llamadas de desactivación y reactivación de la protección de la hoja. Deben estar
antes y después de la recuperación del estado del día y de la fecha de fin de
tarea.
1. El perfil de usuario
Debemos recuperar el perfil de usuario para gestionar la navegación y los menús en la
aplicación Gestión de Perfiles.
a. La tabla de usuarios
A modo de ejemplo, hemos definido dos perfiles: un perfil User que es el perfil de los
usuarios de base que tienen derechos limitados en la aplicación y el perfil Admin que
está reservado a los usuarios que tienen acceso ilimitado a la herramienta.
Inserte una nueva variable pública de tipo String llamada StrPerfil en el módulo
ModDeclaration.
Podríamos decidir que a partir de ahora la posibilidad de cambiar la base de datos sea un
operación delicada y que sólo un usuario con permisos de tipo Admin pueda efectuar
esta operación.
Además de deshabilitar un botón del formulario para bloquear el acceso a una o más
funcionalidades, también es posible bloquear la instalación de estas últimas.
Vamos a reservar la utilización del menú Miembros considerado también crítico sólo a
los usuarios que tengan el perfil Admin.
Inserte el siguiente código en el procedimiento de evento CmdConsultaPerfiles_Click
del formulario Frm_Menu. Hay que insertarlo al final del procedimiento antes que se
visualice la hoja Gestion de perfiles.