Vba Access Libro
Vba Access Libro
Vba Access Libro
VBA - Access
Apndice
01
Eduardo Olaz
A_01 - 2
Eduardo Olaz
Apndice 1
Primeros conceptos 2
A_01 - 3
Hay otras medidas como Tera =1.024 gigas, etc, pero ahora no vienen al caso aunque, tal y como est avanzando todo esto, en fechas muy prximas seguro que se habla de discos con Tantos Teras.
2005
Si descomponemos la cifra 2005 nos da 2 unidades de mil ms 5 unidades. Es decir 2005 = 2 * 1000 + 5 * 1 Tambin podramos decir que 2005 = 2 * 1000 + 0 * 100 + 0 * 10 + 5 * 1 Si lo representramos mediante potencias de 10 tendramos. 2005 = 2 * 10^3 + 0 * 10^2 + 0 * 10^1 + 5 * 10^0 Nota: Ya indicamos que el operador producto en programacin suele ser el Asterisco [ * ] El operador Exponente se representa mediante ^ . En otros lenguajes tambin se usa ** Haced ahora una prueba. Abrid la calculadora de Windows. En el men Ver de sta, seleccionad Cientfica. Escribid ahora la cifra 2005. A la izquierda, debajo de la ventana de las cifras podis ver unos Botones de Opcin, en los que pone Hex Dec Oct y Bin. Por defecto est seleccionado Dec. Esto indica que por defecto funciona con cifras en el formato decimal (de base 10). Despus de escribir 2005, seleccionad el Botn de opcin Bin. La cifra cambia a 11111010101
Comencemos a programar con VBA - Access
A_01 - 4
Esta es la representacin del valor 2005 en formato binario. En realidad su representacin es:
0000011111010101
Vamos a analizar esta cifra como hemos hecho con 2005 en decimal. As como 2 * 10^3 + 0 * 10^2 + 0 * 10^1 + 5 * 10^0 Tenemos que 11111010101 = 1*2^10 + 1*2^9 + 1*2^8 + 1*2^7 + 1*2^6 + 0*2^5 + 1*2^4 + 0*2^3 + 1*2^2 + 0*2^1 + 1*2^0= 2^10 + 2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 2^2 + 2^0 = 1024 + 512 + 256 + 128 + 64 + 16 + 4 + Si no os lo creis haced la suma. La diferencia entre el sistema binario que usan los ordenadores y el sistema decimal, es que el decimal usa potencias de 10, con coeficientes del 0 al 9, y el binario usa potencias de 2, usando como coeficientes el 0 y el 1. El sistema hexadecimal usa potencias de 16 y sus cifras van del 0 al 9, ms las letras A B C D E F para completar las cifras coeficiente desde el 10 al 15. A vale 10, B vale 11, C 12, D 13, E 14, F 15. Si despus de escribir 2005 en la calculadora, en el modo decimal, seleccionis la opcin Hex en los Botones de Opcin, veris que cambia por 7D5. El sistema Hexadecimal trabaja en base 16 7D5 = 7 * 16^2 + 13 * 16^1 + 5 * 16^0 = 7 * 256 + 13 * 16 + 5 * 1 = 1792 + 208 + 5 = 2005 Si ahora seleccionis la opcin Oct en los Botones de Opcin, veris que cambia a 3725. El sistema Octal trabaja en base 8. 3725 = 3 * 8^3 + 7 * 8^2 + 2 * 8^1 + 5 * 8^0 = 3 * 512 + 7 * 64 + 2 * 8 + 5 * 1 = 1536 + 448 + 16 + 5 = 2005 Con todo esto ya tenemos las bases para entender cmo maneja la memoria ciertos datos. Vamos a empezar con el tipo ms bsico, el tipo Byte. El tipo Byte es un dato que puede ser almacenado en 8 bits, o como su nombre indica en un Byte. Sus valores van del 00000000 al 11111111 255 Con esta estructura de bits podramos, como ya he dicho, representar nmeros del 0 al 255, e incluso si asignramos a cada valor numrico una letra, representar hasta 255 caracteres. El sistema de caracteres ASCII asigna caracteres a valores que estn dentro de este rango. Por ejemplo el carcter a tiene el valor 97. el b el valor 98, el A el 65, el B el 66, la cifra 0 el valor 48.
eduardo@olaz.net
1 = 2005
Eduardo Olaz
Apndice 1
Primeros conceptos 2
A_01 - 5
En entregas anteriores he hablado del valor Nulo Null. Pues bien, el Nulo se suele representar en memoria como un carcter de cdico cero. Si ahora tomramos 2 bytes y utilizramos sus 16 bits tendramos posibilidad de representar del 0000000000000000 0 al 1111111111111111 65535 Si asignamos a valores dentro de este rango, caracteres de texto concretos podramos representar prcticamente todos los caracteres que existen en los diferentes idiomas, incluyendo el Chino y el japons. Algo as es lo que hace el sistema de caracteres UNICODE. Igualmente podramos almacenar valores enteros del 0 al 65535. Este tipo de dato numrico no existe en VBA, pero s por ejemplo en C# VB.Net y recibe el nombre de Char. En VBA existe el dato llamado Integer, que utiliza para el rango numrico los 15 bits de la derecha, con lo que se pueden representar valores que van del 000000000000000 al 111111111111111 32767 El primer bit se utiliza para el signo. Luego hace una serie de operaciones a nivel de bits, pero para nosotros eso no es transcendente. En realidad el tipo Integer puede tomar valores en el rango que va del - 32.768 al 32.767 usando para ello 2 Bytes. De forma semejante tenemos otro nmero que maneja enteros, pero de ms precisin. Es el llamado Long. El tipo Long usa 31 bits para el valor numrico ms el primer bit para el signo Con 31 bits podramos representar valores del 0000000000000000000000000000000 al 1111111111111111111111111111111 2147483647 Con el bit del signo, el tipo Long puede tomar valores en el rango que va del - 2147483648 al 2147483647 usando para ello 4 Bytes. Hay un caso muy especial de dato, y es el llamado Currency (Tipo moneda). El tipo Long usa 63 bits para el valor numrico ms el primer bit para el signo. Con 63 bits se pueden representar valores del 0 al 9223372036854775803. Usando un bit adicional para el signo, el tipo currency es capaz de representar en realidad datos que van del -922.337.203.685.477,5808 al 922.337.203.685.477,5807. El tipo Currency tiene la peculiaridad de que maneja 4 cifras decimales. En realidad no es un tipo de los llamados de Coma flotante, sino que se almacena internamente como si fuera un valor numrico de cifras enteras, y luego en los clculos considera que las 4 cifras decimales menos significativas estn a la derecha de la coma. Cmo podramos representar un valor no entero, por ejemplo el 0,25? 0 0
A_01 - 6
Supongamos que quisiramos representar valores menores que 1 y mayores que 0 usando para ello 1 Byte. Podramos hacer lo siguiente El primer bit representara al valor 1/2, el segundo a 1/4, el tercero 1/8, el cuarto 1/16, y as hasta el 1/256 Para representar el valor 0,25 nos bastara con el bit 2, con lo que el valor 0,25, mediante nuestro mtodo se representara as: 01000000 Si quisiramos representar 0,75 nos bastara con el primer bit ms el segundo Lo que equivaldra a 0,75 = 0,5 + 0,25 Es decir 11000000 Para algunas cifras tendramos un problema de precisin. Por ejemplo cmo representar el valor 0,3?. En el sistema decimal es muy sencillo y se hace 3 * 0,1 es decir 0,3. En cambio con 8 bits la forma ms aproximada sera: 0 + 1/4 + 0 + 0 + 1/32 + 1/64 + 0 + 1/256 0,30078125 lo que equivaldra a 01001101 La combinacin, sin pasarse de 0,3 ms aproximada sera 0 + 1/4 + 0 + 0 + 1/32 + 1/64 + 0 + 0 0,296875 lo que equivaldra a 01001100 Si quisiramos representar el nmero 23,3 podramos usar 16 bits (2 Bytes) el primero para la parte entera, y el segundo para la parte decimal. Con este sistema quedara 23,3 00010111 01001100 Este procedimiento, que yo sepa, no lo utiliza ningn ordenador. En la vida real se usan mtodos ms sofisticados, pero la base de su funcionamiento es esta. Ver Como se puede comprender, la precisin y la magnitud que se pueden representar es directamente proporcional al nmero de Bytes utilizados. As por ejemplo una variable declarada como de tipo Single utiliza 4 Bytes. Esto le permite manejar nmeros en los rangos que van de -3,402823E38 a -1,401298E-45 para valores negativos y de 1,401298E-45 a 3,402823E38 para valores positivos Una variable de tipo Double, utiliza 8 Bytes. Sus datos se almacenan siguiendo el formato IEEE 754. El siguiente texto est sacado del sitio correspondiente a Tutoras de la UNED http://www.etsimo.uniovi.es/~antonio/uned/ieee754/formato.html En l se explica la estructura del formato IEEE 754. Es una informacin para los que quieren de verdad conocer las tripas de la cosa, aunque reconozco que su digestin no es precisamente sencilla. Como aclaracin el operador **, es el operador Exponente. As la expresin 2**(129-127) indica que se eleva 2 al cuadrado. Lo indicado como Simple precisin sera aplicable al tipo Single, y por doble precisin al tipo Double. No os preocupis si no entendis lo aqu expuesto; lo pongo como informacin avanzada.
El IEEE (Instituto de Ingenieros Elctricos y Electrnicos) ha creado un estndar la presentacind de nmeros en coma flotante. Este estndar especifica como deben representarse los nmeros en coma flotante con simple precisin (32 bits) o doble precisin (64 bits), y tambin cmo deben realizarse las operaciones aritmticas con ellos.
eduardo@olaz.net
Eduardo Olaz
Apndice 1
Primeros conceptos 2
A_01 - 7
Simple Precisin El estndar IEEE-754 para la representacin en simple precisin de nmeros en coma flotante exige una cadena de 32 bits. El primer bit es el bit de signo (S), los siguientes 8 son los bits del exponente (E) y los restantes 23 son la mantisa (M):
S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM 0 1 8 9 31
El valor V representado por esta cadena puede ser determinado como sigue:
Si E=255 y M es no nulo, entonces V=NaN ("Not a number") Si E=255 y M es cero y S es 1, entonces V=-Infinito Si E=255 y M es cero y S es 0, entonces V=Infinito Si 0<E<255 entonces V=(-1)**S * 2 ** (E-127) * (1.M) donde "1.M" se emplea para representar el nmero binario creado por la anteposicin a M de un 1 y un punto binario. Si E=0 y M es no nulo, entonces V=(-1)**S * 2 ** (-126) * (0.M) Estos son valores "sin normalizar". Si E=0 y M es cero y S es 1, entonces V=-0 Si E=0 y M es cero y S es 0, entonces V=0
En particular,
0 00000000 00000000000000000000000 = 0 1 00000000 00000000000000000000000 = -0 0 11111111 00000000000000000000000 = Infinito 1 11111111 00000000000000000000000 = -Infinito 0 11111111 00000100000000000000000 = NaN 1 11111111 00100010001001010101010 = NaN 0 10000000 00000000000000000000000 = +1 * 2**(128-127) * 1.0 = 2 0 10000001 10100000000000000000000 = +1 * 2**(129-127) * 1.101 = 6.5 1 10000001 10100000000000000000000 = -1 * 2**(129-127) * 1.101 = 6.5 0 00000001 00000000000000000000000 = +1 * 2**(1-127) * 1.0 = 2**(126) 0 00000000 10000000000000000000000 = +1 * 2**(-126) * 0.1 = 2**(127) 0 00000000 00000000000000000000001 = +1 * 2**(-126) * 0.00000000000000000000001 = 2**(-149) (valor positivo ms pequeo)
Doble precisin El estndar IEEE-754 para la representacin en doble precisin de nmeros en coma flotante exige una cadena de 64 bits. El primer bit es el bit de signo (S), los siguientes 11 son los bits del exponente (E) y los restantes 52 son la mantisa (M):
S EEEEEEEEEEE MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM 0 1 11 12 63
El valor V representado por esta cadena puede ser determinado como sigue:
Comencemos a programar con VBA - Access
A_01 - 8
Si E=2047 y M es no nulo, entonces V=NaN ("Not a number") Si E=2047 y M es cero y S es 1, entonces V=-Infinito Si E=2047 y M es cero y S es 0, entonces V=Infinito Si 0<E<2047 entonces V=(-1)**S * 2 ** (E-1023) * (1.M) donde "1.M" se emplea para representar el nmero binario creado por la anteposicin a M de un 1 y un punto binario. Si E=0 y M es no nulo, entonces V=(-1)**S * 2 ** (-1022) * (0.M) Estos son valores "sin normalizar". Si E=0 y M es cero y S es 1, entonces V=-0 Si E=0 y M es cero y S es 0, entonces V=0
Bibliografa: ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic
Tipo Decimal Dentro de los tipos numricos hay un tipo muy especial. Es el tipo Decimal. Este tipo necesita de 12 Bytes para su representacin, o lo que es lo mismo 96 bits. Tal cantidad de bits le permite manejar nmeros de un tamao y precisin extrema. El tipo Decimal permite definir el nmero de decimales que va a utilizar. El nmero de decimales se puede establecer entre 0 y 28. Segn la ayuda los rangos de valores mximos y mnimos que puede manejar son +/-7,9228162514264337593543950335 y +/-0,0000000000000000000000000001 El inconveniente de este tipo de nmero es que para manejarlo precisa declarar primero la variable como Variant y luego hacer una conversin a Decimal mediante la funcin de Conversin de Tipo CDec. Para probarlo vamos a crear un procedimiento Sub en un mdulo estandar
Public Sub PruebaDecimal() Dim varDecimal As Variant varDecimal = CDec(varDecimal) varDecimal = CDec(1) / 3 Debug.Print varDecimal End Sub
Si lo llamamos desde la ventana Inmediato, escribiendo en ella PruebaDecimal y presionando la tecla [Intro], nos imprimir:
0,6666666666666666666666666667
Es decir un resultado con una precisin de 28 decimales. Por qu he hecho la conversin a Decimal de la cifra 1? en la expresin:
varDecimal = CDec(2) / 3
Porque para que a la variable varDecimal se le pueda asignar un valor decimal, la cantidad a asignar debe ser Decimal. Esto implica que en la operacin asignada, al menos uno de los operandos debe ser convertido a Decimal.
eduardo@olaz.net
Eduardo Olaz
Apndice 1
Primeros conceptos 2
A_01 - 9
varDecimal = 2 / 3
Nos hubiera impreso slo
0,666666666666667
Que est en el rango del Double, no del Decimal. Por ejemplo si quisiramos asignar a varDecimal el resultado de la operacin
varDecimal = 2 / 3 + CDec(4)
Nos devolvera un dato de tipo Double. Por ello, para no tener errores, si es que necesitramos utilizar variables Decimales, por ejemplo si nos contratara la Nasa para programar la trayectoria de un viaje estelar, o tuviramos que efectuar clculos estadsticos muy complejos, convendra hacer la conversin de todas las operaciones al formato Decimal antes de asignarlas a una variable. Por suerte hay pocos casos en los que seran necesarios este tipo de valores. Manejo de textos Los textos se almacenan carcter a carcter Windows maneja unas tablas de caracteres que asignan a cada carcter un nmero del tipo Byte. (entre 0 y 255) Los 128 primeros cdigos de Windows guardan correlacin con los 128 que utilizaba MsDos. Por ejemplo, si a la variable strNombreApellido declarada como de tipo String, le asignamos mis datos:
69 100 117 E d u
32
79 108 O l
97 122 a z
(Al espacio en blanco se le asigna el valor 32). Es el propio Windows el que define los cdigos a aplicar. Hay discrepancias entre algunos de los caracteres superiores al 127, es el caso de las ees y las vocales acentuadas. No obstante se pueden utilizar funciones de conversin definidas en el API de Windows, como son:
OemToChar cuya cabecera es Declare Function OemToChar _ Lib "user32" _ Alias "OemToCharA" ( _ ByVal CadenaAConvertir As String, _ ByVal CadenaConvertida As String) _ As Long
Comencemos a programar con VBA - Access
A_01 - 10
Y CharToOem de cabecera:
Declare Function CharToOem _ Lib "user32" _ Alias "CharToOemA" ( _ ByVal CadenaAConvertir As String, _ ByVal CadenaConvertida As String) _ As Long
Si tuviramos que realizar cambios entre textos de Dos como seran los que almacena DBase y textos Windows, o a la inversa para imprimir de forma directa en impresoras de agujas o grabar en ficheros DBase podramos declarar unas funciones de conversin. El cdigo para estas operaciones podra ser el siguiente:
Declare Function CharToOem _ Lib "user32" _ Alias "CharToOemA" ( _ ByVal CadenaAConvertir As String, _ ByVal CadenaConvertida As String) _ As Long Declare Function OemToChar _ Lib "user32" _ Alias "OemToCharA" ( _ ByVal CadenaAConvertir As String, _ ByVal CadenaConvertida As String) _ As Long Public Function TextoWindowsADos( _ ByVal Cadena As String) _ As String Dim strBuffer As String Dim Resultado As Long strBuffer = String(Len(Cadena), " ") Resultado = CharToOem(Cadena, strBuffer) TextoWindowsADos = strBuffer End Function Public Function TextoDosAWindows( _ ByVal Cadena As String) _ As String Dim strBuffer As String Dim Resultado As Long strBuffer = String(Len(Cadena), " ") Resultado = OemToChar(Cadena, strBuffer) TextoDosAWindows = strBuffer End Function
eduardo@olaz.net
Eduardo Olaz
VBA - Access
Entrega
00
ndice
Eduardo Olaz
00 - 2
Entrega 01
Introduccin
10 pginas
Planteamiento .......................................................................................................................... 2 Objetivos....................................................................................................................... 2 A quin va dirigido? .................................................................................................... 2 Quin soy yo?............................................................................................................. 2 Qu es VBA? ......................................................................................................................... 3 Los mdulos ................................................................................................................. 4 Primer formulario con cdigo........................................................................................ 6 Un poco de teora ......................................................................................................... 7 Entrega 02 Entorno de Desarrollo 7 pginas
Entorno de desarrollo ................................................................................................... 2 Ventanas....................................................................................................................... 3 Uso de la ventana Inmediato (ventana de Depuracin) ............................................... 4 Entrega 03 Primeros conceptos 7 pginas
Primeros conceptos ...................................................................................................... 2 mbito Alcance de las Constantes y variables.......................................................... 3 Procedimientos Sub...................................................................................................... 4 Entrega 04 Primeros conceptos II 12 pginas
Funciones ..................................................................................................................... 2 Funciones en formularios ............................................................................................. 5 Entrega 05 Tipos de datos y Declaraciones 13 pginas
Declaracin de variables .............................................................................................. 2 Construccin del nombre de una variable (o constante). ............................................. 2 Tipos de datos .............................................................................................................. 3 Datos numricos........................................................................................................... 3 Prefijos.......................................................................................................................... 4 Nmeros de Coma Flotante.......................................................................................... 5 Tipo Decimal................................................................................................................. 5 Tipo Date ...................................................................................................................... 5 Tipo Boolean (boleano) ................................................................................................ 8 Tipo Variant ................................................................................................................ 10 Empty - Null - Cadena vaca....................................................................................... 10 Declaraciones mltiples en una lnea ......................................................................... 11 Valores por defecto..................................................................................................... 12 mbito alcance de una declaracin......................................................................... 12 Dim ............................................................................................................................. 12 Private......................................................................................................................... 12 Public .......................................................................................................................... 12 Global ......................................................................................................................... 13 Entrega 06 Estructuras de datos 11 pginas
Matrices Arrays.......................................................................................................... 2
eduardo@olaz.net
Eduardo Olaz
Entrega 00
ndice
00 - 3
Matrices de varias dimensiones ................................................................................... 4 Matrices dinmicas ....................................................................................................... 4 Instruccin Erase .......................................................................................................... 7 Redim con varias dimensiones..................................................................................... 7 ndices superior e inferior de una matriz....................................................................... 7 Registros (Estructuras de variables definidas por el usuario) ...................................... 8 La instruccin With ..................................................................................................... 10 Matrices de Registros ................................................................................................. 11 Entrega 07 Colecciones y Objetos 12 pginas
Introduccin a las Colecciones ..................................................................................... 2 Antes de seguir: For Each - - - Next ............................................................................ 4 Colecciones de Controles............................................................................................. 5 Introduccin a las Clases.............................................................................................. 7 Entrega 08 Continuando con las Clases 9 pginas
Aclaraciones sobre el cdigo del ltimo captulo.......................................................... 2 Sigamos analizando el cdigo ...................................................................................... 8 Entrega 09 Estructuras de Control 15 pginas
Estructuras de Control .................................................................................................. 2 Estructuras de Decisin................................................................................................ 2 La Instruccin If ............................................................................................................ 2 La Funcin IIf ................................................................................................................ 5 La Instruccin Select Case ........................................................................................... 5 Estructuras de Bucle..................................................................................................... 9 Las Instrucciones For - - - Next ................................................................................... 9 La Instruccin For Each - - - Next............................................................................ 14 Entrega 10 Estructuras de Control II 12 pginas
Estructuras de Control, segunda parte ......................................................................... 2 Las Instrucciones While - - - Wend............................................................................. 2 Las Instrucciones Do - - - Loop................................................................................... 4 Entrega 11 Gestin de errores 12 pginas
Errores .......................................................................................................................... 2 La gestin de los errores en procedimientos................................................................ 2 Errores en Tiempo de Ejecucin .................................................................................. 3 Instrucciones de salto ................................................................................................... 5 La Instruccin Goto....................................................................................................... 5 Gosub - - Return ........................................................................................................... 5 Capturar Errores ........................................................................................................... 6 El objeto Err .................................................................................................................. 6 La instruccin Resume ................................................................................................. 7 Gestionando errores ................................................................................................... 10 Generacin directa de errores (Err.Raise).................................................................. 12
00 - 4
Entrega 12
22 pginas
Procedimientos ............................................................................................................. 2 Variables Static............................................................................................................. 2 Paso de parmetros Por Valor y Por Referencia.......................................................... 3 Parmetros Opcionales ................................................................................................ 5 Puntualizaciones sobre parmetros opcionales. .......................................................... 5 Procedimientos Recursivos frente a Iterativos ............................................................. 7 Pasar un parmetro, tipo matriz, mediante ParamArray .............................................. 9 Uso de parmetros Con Nombre................................................................................ 10 Constantes Enumeradas ............................................................................................ 11 Uso de las Constantes Enumeradas .......................................................................... 12 Funciones para intercambio de informacin............................................................... 15 Funcin MsgBox ......................................................................................................... 15 Funcin InputBox........................................................................................................ 19 Entrega 13 Funciones de VBA 12 pginas
Funciones propias de VBA ........................................................................................... 2 Funcin Choose............................................................................................................ 2 Funcin Switch ............................................................................................................. 3 Funcin Format............................................................................................................. 4 Utilizacin con cadenas String...................................................................................... 4 Utilizacin con fechas ................................................................................................... 5 Cadenas con nombre ................................................................................................... 7 Utilizacin de Format con nmeros .............................................................................. 9 Cadenas con nombre para nmeros ......................................................................... 10 Formato compuesto.................................................................................................... 11 Configuracin Regional .............................................................................................. 11 Entrega 14 Funciones de VBA II 21 pginas
Funciones adicionales para formato............................................................................. 2 Funcin FormatNumber................................................................................................ 2 Funcin FormatCurrency .............................................................................................. 3 Funcin FormatPercent ................................................................................................ 3 Funcin FormatDateTime ............................................................................................. 3 Funcin MonthName .................................................................................................... 5 Funcin WeekdayName ............................................................................................... 5 Manipulacin de cadenas ............................................................................................. 7 Funcin Left .................................................................................................................. 8 Funcin LeftB................................................................................................................ 8 Function Right............................................................................................................... 9 Function Mid ................................................................................................................. 9 Instruccin Mid.............................................................................................................. 9 Funciones LTrim, Rtrim y Trim ................................................................................... 11 Funciones Len y LenB ................................................................................................ 11 Buscar y sustituir cadenas.......................................................................................... 13 Funcin InStr .............................................................................................................. 13 Funcin InStrReverse ................................................................................................. 14
eduardo@olaz.net
Eduardo Olaz
Entrega 00
ndice
00 - 5
Funcin StrComp........................................................................................................ 14 Funcin Replace......................................................................................................... 14 Funcin StrReverse .................................................................................................... 15 Funcin Filter .............................................................................................................. 15 Funcin Split ............................................................................................................... 16 Funcin Join ............................................................................................................... 18 Operador Like ............................................................................................................. 18 Funciones Asc y AscB ................................................................................................ 19 Funciones Chr y Chr$................................................................................................. 19 Diferencia entre funciones que trabajan en modo Carcter y en modo Byte. ............ 20 Entrega 15 Operadores 22 pginas
Operadores................................................................................................................... 2 Tipos de Operadores .................................................................................................... 2 Operadores aritmticos ................................................................................................ 2 Operador Suma ............................................................................................................ 2 Operador Resta ............................................................................................................ 5 Operador Producto ....................................................................................................... 5 Operador Divisin ......................................................................................................... 6 Operador Elevar a potencia.......................................................................................... 6 Operador Divisin entera.............................................................................................. 7 Operador Mdulo o Resto ............................................................................................ 8 Operadores de Comparacin ....................................................................................... 8 Operador Like ............................................................................................................... 9 Operador Is................................................................................................................. 11 Operadores de concatenacin.................................................................................... 15 Operadores lgicos..................................................................................................... 15 Operador And ............................................................................................................. 15 Operador Or................................................................................................................ 17 Operador Xor .............................................................................................................. 18 Operador Not .............................................................................................................. 19 Operador Eqv ............................................................................................................. 19 Operador Imp.............................................................................................................. 20 Prioridad de los operadores........................................................................................ 20 Entrega 16 Cdigo vs. Macros - Objeto DoCmd 30 pginas
Cdigo frente a macros ................................................................................................ 2 Cundo usar Macros y cundo cdigo VBA?............................................................. 7 El objeto DoCmd........................................................................................................... 7 Uso de DoCmd en los Asistentes para controles ....................................................... 29 Entrega 17 Trabajar con ficheros 17 pginas
Trabajar con Ficheros................................................................................................... 2 Trabajar con carpetas................................................................................................... 2 Funcin Dir ................................................................................................................... 3 Funcin CurDir.............................................................................................................. 5
Comencemos a programar con VBA - Access
00 - 6
Instruccin ChDir .......................................................................................................... 6 Instruccin ChDrive ...................................................................................................... 6 Instruccin MkDir .......................................................................................................... 6 Instruccin RmDir ......................................................................................................... 7 Instruccin Kill...............................................................................................................7 El objeto FileSearch...................................................................................................... 8 Propiedades y mtodos de FileSearch......................................................................... 8 Propiedad LookIn.......................................................................................................... 8 Propiedad Filename...................................................................................................... 8 Propiedad SearchSubFolders....................................................................................... 8 El objeto FileSearch...................................................................................................... 8 Mtodo Execute............................................................................................................ 8 Propiedad LastModified .............................................................................................. 10 Objeto FoundFiles ...................................................................................................... 11 Mtodo NewSearch .................................................................................................... 12 Propiedad FileType..................................................................................................... 12 Otras propiedades y mtodos..................................................................................... 14 Coleccin SearchFolders............................................................................................ 14 Objeto ScopeFolder.................................................................................................... 14 Coleccin ScopeFolders............................................................................................. 14 Coleccin SearchScopes............................................................................................ 14 Objeto SearchScope................................................................................................... 14 Mtodo RefreshScopes .............................................................................................. 15 Entrega 18 Trabajar con ficheros II 18 pginas
Trabajando con Ficheros .............................................................................................. 2 Instruccin Open........................................................................................................... 2 Funcin FreeFile........................................................................................................... 3 Instruccin Print # ......................................................................................................... 3 Funcin Tab.................................................................................................................. 5 Funcin Spc.................................................................................................................. 6 Instruccin Width # ....................................................................................................... 7 Instruccin Write # ........................................................................................................ 8 Instrucciones Input # y Line Input # .......................................................................... 10 Ficheros de acceso Aleatorio ..................................................................................... 12 Instruccin Get ........................................................................................................... 13 Instruccin Put ............................................................................................................ 13 Funcin Seek.............................................................................................................. 16 Ejemplos de apertura de ficheros con la instruccin OPEN ....................................... 17 Notas sobre esta entrega ........................................................................................... 18 Entrega 19 Trabajar con ficheros III 40 pginas
Exportar, importar y vincular ficheros de texto ............................................................. 2 Estructura de un fichero Schema.ini............................................................................. 3 Otros tipos de formato en ficheros Schema.ini............................................................. 9 Varios esquemas en un nico archivo Schema.ini ..................................................... 11 Abrir ficheros de texto como si fuera un Recordset.................................................... 11
eduardo@olaz.net
Eduardo Olaz
Entrega 00
ndice
00 - 7
Ficheros ini ................................................................................................................. 14 Leer y escribir un fichero ini........................................................................................ 14 Introduccin (necesaria) a la utilizacin de las funciones API.................................... 15 Informacin en Internet sobre las APIs de Windows .................................................. 16 Declaracin de las funciones API para el manejo de un fichero ini............................ 17 Escritura y lectura en un fichero ini............................................................................. 19 Lista las Secciones de un fichero ini........................................................................... 26 Lectura y escritura en bloque de toda una seccin en un fichero ini.......................... 29 El Registro de Windows.............................................................................................. 36 Escritura y lectura del registro con VBA ..................................................................... 36 Instruccin SaveSetting .............................................................................................. 37 Funcin GetSetting ..................................................................................................... 38 Funcin GetAllSetting ................................................................................................. 38 Instruccin DeleteSetting............................................................................................ 39 Grabar, leer y borrar cualquier Seccin Clave del registro...................................... 40 Notas sobre este captulo ........................................................................................... 40 Entrega 20 Ms sobre Clases y Objetos (1) 47 pginas
Recordemos lo expuesto hasta ahora sobre las clases ............................................... 2 Antes de seguir: consideraciones sobre las clases. ..................................................... 3 Propiedades.................................................................................................................. 4 Mtodos de clase.......................................................................................................... 9 Clases que hacen referencia a s mismas.................................................................... 9 Creacin de estructuras con clases............................................................................ 13 Funcin Is Nothing...................................................................................................... 21 Eventos....................................................................................................................... 21 Qu es un Evento ....................................................................................................... 22 Crear clases en las que se definan Eventos ............................................................. 24 Instruccin Event ........................................................................................................ 25 Manos a la obra. Clase Depsito................................................................................ 26 Instruccin RaiseEvent ............................................................................................... 28 Palabra clave WithEvents........................................................................................... 37 Probando la clase CDeposito ..................................................................................... 38 Eventos Initialize y Terminate de la clase................................................................... 44 Propiedades en los mdulos estndar ....................................................................... 46 Nota sobre la prxima entrega ................................................................................... 47 Entrega 21 Ms sobre Clases y Objetos (2) 50 pginas
Continuamos con las clases ......................................................................................... 2 Procedimientos Friend.................................................................................................. 2 Gestin de colores - 2 Caso prctico .......................................................................... 3 Funcin RGB ................................................................................................................ 4 Clase bsica para gestionar el color............................................................................. 5 Utilizacin de la clase Color en un informe................................................................... 7 Enumeraciones en un mdulo de clase...................................................................... 11 Herencia y Polimorfismo............................................................................................. 18
Comencemos a programar con VBA - Access
00 - 8
Interfaces .................................................................................................................... 19 Creacin de una Interfaz ............................................................................................ 20 Ejemplo sobre la creacin y uso de interfaces ........................................................... 29 Uso de las clases y de la interfaz ............................................................................... 37 Herencia ..................................................................................................................... 42 Constructor de la clase ............................................................................................... 47 Factora de Clases...................................................................................................... 49 Propiedad Instancing de la clase................................................................................ 50
Apndice 01
10 pginas
Byte, bit, Bytes, bits Qu es eso?............................................................................... 2 Kas, Megas, Gigas ....................................................................................................... 2 Cmo afecta esto a la memoria del PC ........................................................................ 3 Lectura / Escritura de los datos en memoria (Tipos numricos) .................................. 3 Estndar IEEE-754 ....................................................................................................... 7 Simple precisin ........................................................................................................... 7 Doble precisin ............................................................................................................. 7 Tipo Decimal................................................................................................................. 8 Manejo de textos .......................................................................................................... 9
eduardo@olaz.net
Eduardo Olaz
VBA - Access
Entrega
01
Introduccin
Eduardo Olaz
01 - 2
Planteamiento
Este cursillo nace como respuesta a las continuas demandas por parte de los intervinientes en los foros de Access, de un manual que permita, desde cero, aprender los fundamentos de la programacin con VBA. La idea inicial es ir suministrando sucesivas entregas imprimibles y descargables desde uno varios enlaces de Internet. Estos textos se complementarn, si fuese necesario, con ficheros mdb que contendrn el cdigo y los ejemplos planteados en el curso.
Objetivos
El objetivo que me planteo, espero no ser demasiado ambicioso, es que el lector, tras seguir el cursillo, adquiera las bases de programacin suficiente como para poder manejar las herramientas que le suministra VBA de Access, y sea capaz por s mismo de ir evolucionando y completando sus conocimientos. Por mi experiencia personal y con alumnos de cursos que he impartido, el problema fundamental para soltarse en la programacin es conocer los elementos esenciales del lenguaje y las herramientas que el sistema te ofrece. En nuestro caso vamos a utilizar el lenguaje Visual Basic para Aplicaciones y su entorno de desarrollo, orientado principalmente a su utilizacin con Access.
A quin va dirigido?
Va dirigido a todos aquellos que quieran comenzar a programar con VBA. Como requisitos necesarios, el lector deber haber manejado previamente Access y ser capaz de crear tablas, formularios, informes y consultas bsicas.
Eduardo Olaz
Entrega 01
Introduccin
01 - 3
Qu es VBA?
VBA quiere decir Visual Basic para Aplicaciones. Es un conjunto de libreras, (un tipo especial de programas), desarrollado por Microsoft que incluye, entre otras cosas, un entorno de desarrollo y un lenguaje de programacin. VBA no es exclusivo de Access; lo podemos encontrar tambin en todas las aplicaciones de Office como Word o Excel, en otro tipo de programas como Project o Visio, y en programas que no son de Microsoft y tan diversos como Corel Draw o AutoCad. Dominando los fundamentos de VBA, se podra desarrollar aplicaciones en cualquiera de esos aplicativos. VBA tiene un entorno de programacin que es semejante para todos estos programas. Por ejemplo: VBA para Access VBA para Word
01 - 4
-Eso est muy bien, pero cmo empiezo a programar?-Vale. Un poco de paciencia.-
Los mdulos
Los mdulos son los objetos en los que se guarda el cdigo que va a utilizar VBA. Hay tres tipos de mdulos. Mdulos generales Mdulos asociados a formularios e informes Mdulos de Clase.
Manos a la obra: vamos a crear una nueva base de datos con el nombre Entrega01.mdb Para acceder a los mdulos generales debemos presionar en la pestaa Mdulos de la Barra de Objetos de Access: Para crear un mdulo nuevo haga Clic en el botn [Nuevo]. Una vez hecho esto se abre el editor de VBA y nos crea un mdulo con el original nombre de Mdulo1.
Eduardo Olaz
Entrega 01
Introduccin
01 - 5
Si no apareciera la lnea Option Explicit, vamos a hacer lo siguiente: Desde la ventana del editor que se ha abierto, presionamos la opcin de men: Herramientas > Opciones
Le damos a Aceptar. -Pero qu es esto?Aunque adelantemos conceptos tratar de explicarme. Cuando empecemos a escribir cdigo, veremos que existen unos elementos que se llaman variables, a los que podremos asignar valores. Si tenemos activada la opcin [Requerir declaracin de variables] nos obligar a declarar las variables antes de poder usarlas. Ms adelante veremos que hay variables que pueden contener Texto, Nmeros, Fechas, Objetos, etc. En una entrega posterior aprenderemos a declarar una variable. Esto significa darle nombre y expresar qu tipo de dato va a contener. Una vez hecho esto grabamos el mdulo dndole al botn Guardar [Disquete] a la opcin de men Archivo>Guardar combinando las teclas [Ctrl] + [S] Aceptamos el nombre por defecto Mdulo1 y cerramos la ventana.
Comencemos a programar con VBA - Access
01 - 6
Para cerrar la ventana del editor podemos usar Varios caminos: Archivo > Cerrar y volver a Micro Botn Cerrar Combinacin de teclas [Ctrl] + [Q]
Si volvemos a crear un nuevo mdulo veremos que ahora s contiene la lnea Option Explicit Ahora cerramos este mdulo.
1. Nos vamos a la opcin Formularios y presionamos en [Crear formulario en vista diseo] 2. Lo guardamos como Saludo 3. Aadimos una Etiqueta al formulario.
a. Utilizando las Propiedades, ponemos a la propiedad [Nombre] de la etiqueta el valor lblSaludo (Pestaa [Otras] de la hoja de Propiedades) sustituyendo el que tena por defecto: Etiqueta1. b. Desde la pestaa [Formato] le ponemos a la propiedad [Ttulo] el valor Saludo, a la propiedad [Tamao de la fuente] el valor 24, y al [Color de texto] el valor 255 (Rojo) y a la [Alineacin del texto] el valor Centro. 4. Aadimos un Botn y le ponemos como [Nombre], en vez de Comando2, cmdSaludo
Eduardo Olaz
Entrega 01
Introduccin
01 - 7
5. Seleccionamos el Formulario y ponemos a No las propiedades [Selectores de registro], [Botones de desplazamiento] y Separadores de registro. 6. La propiedad [Centrado automtico] la ponemos a S, y a la propiedad [Ttulo] el valor Mi primer cdigo. a. Le ponemos como [Nombre] Saludo Si visualizamos el formulario se ver algo as como esto: Si presionamos el botn vemos que no pasa nada. Vamos a hacer que cuando se presione el botn aparezca el mensaje Hola Mundo!!! en la etiqueta lblSaludo, sustituyendo su contenido actual que es Saludo.
Un poquito de teora
Windows funciona con las llamadas Ventanas. Esas ventanas contienen objetos, y tanto las ventanas como los objetos pueden mandar unos mensajes para indicar a la aplicacin que usa Windows, o al propio Windows que han ocurrido determinadas cosas. Esos mensajes se llaman Eventos. Por cierto; en realidad muchos de los objetos, como los botones, tambin son ventanas; pero esa es otra guerra. En nuestro caso tenemos los siguientes objetos: Formulario Saludo Etiqueta lblSaludo Botn cmdSaludo
Cada uno de esos Objetos posee una serie de Propiedades, generan determinados Eventos y pueden hacer determinadas cosas mediante unos procedimientos propios que se llaman Mtodos. Por ejemplo, al presionar el botn cmdSaludo, se genera el evento [Al hacer Clic] (Click). Podramos capturar ese evento y aprovecharlo para, por ejemplo, cambiar el texto de la etiqueta lblSaludo. El texto que aparece en la etiqueta est en la propiedad [Ttulo]; en ingls [Caption]. Ahora la pregunta: dnde y cmo se hace todo eso? Cada formulario, y veremos ms adelante que tambin cada informe, lleva asociado un mdulo especial. Se llama mdulo de clase del formulario. Vamos a verlo. En el modo Diseo del formulario Saludo, seleccionamos el botn cmdSaludo. Abrimos la Hoja de propiedades y seleccionamos la pestaa [Eventos], y dentro de stos el evento [Al hacer clic]. Al poner el cursor sobre l, nos muestra un botoncito con tres puntos. Pulsamos en l, y seleccionamos [Generador de cdigo] en la ventana que se nos abre. A continuacin pulsamos en el botn [Aceptar].
01 - 8
Tras esto nos abrir el mdulo de clase del formulario Saludo, con el esquema de cdigo correspondiente al evento Clic del botn cmdSaludo:
Option Compare Database Option Explicit Private Sub cmdSaludo_Click() End Sub
Lo que ahora nos importa es el conjunto
Private Sub indica que empieza un procedimiento del tipo Sub, ms adelante veremos
en detalle qu es esto. botn cmdSaludo.
cmdSaludo_Click() Indica que es el procedimiento al que llama el evento Click del End Sub Indica el punto donde acaba el procedimiento
Eduardo Olaz
Entrega 01
Introduccin
01 - 9
Entre esas dos lneas podemos escribir el cdigo que le dir a Access qu es lo que tiene que hacer. Vamos a hacer 2 cosas: 1. Escribiremos en la propiedad [Ttulo] (Caption) de la etiqueta el texto Hola Mundo. 2. En la propiedad [Ttulo] (Caption) del formulario mostraremos el texto. Aqu estoy. Debo modificar el texto del cdigo para que ponga:
Option Compare Database Option Explicit Private Sub cmdSaludo_Click() Me.lblSaludo.Caption = "Hola Mundo!!!" Me.Caption = "Aqu estoy!" End Sub
Al escribir el texto, se abre una ventana de Ayuda contextual, lo que simplifica en gran medida la escritura correcta del texto.
Esta ventana te va mostrando las propiedades y mtodos. Para seleccionar uno en concreto podemos utilizar la tecla [Enter], o mejor an, la tecla [tab] de tabulacin (es la tecla que suele estar encima de la de Bloqueo de Maysculas).
Me es el propio formulario.
Si nos fijamos en la sentencia Me.lblSaludo.Caption est indicando la propiedad Caption del objeto lblsaludo del formulario actual.
Comencemos a programar con VBA - Access
01 - 10
Fijaros que cada objeto propiedad est separada por un punto. Hablaremos ms de esto. Cerramos la ventana de edicin del cdigo, y si hemos seguido correctamente los pasos, al presionar el botn formulario debera tener un aspecto semejante a ste:
Eduardo Olaz
VBA - Access
Entrega
02
Entorno de desarrollo
Eduardo Olaz
02 - 2
Entorno de desarrollo
Ya hemos comentado que VBA para Access tiene un entorno de desarrollo similar al de otras aplicaciones con VBA. Este entorno recibe el nombre de IDE, que significa algo as como Entorno de Desarrollo Integrado. En el idioma brbaro se escribe Integrated Development Environment. Este entorno consta de una serie de ventanas y botones, algunos de los cuales se muestran en el siguiente grfico.
Eduardo Olaz
Entrega 02
Entorno de desarrollo
02 - 3
02 - 4
? 4 * 5 ms [Enter] 20
a = 2 : b = 4 : Print a * b
Pues s, te escribe 8 en la siguiente lnea. Qu pasar si escribo ? a^b y aprieto [Enter]? Sorpresa! Ha escrito 16 que es 2 elevado a 4 Si ahora presiono la tecla de la barra de men que tiene la forma de un cuadradito azul y que en el grfico la identifico con Interrumpir la ejecucin y reiniciar, si vuelvo a escribir
a = 2 : b = 4 : Print a * b
parece que Access recuerda que existe algo llamado a, que se le ha dado el valor 2 y algo llamado b que tiene el valor 4, como se demuestra tras el resultado de escribir la segunda vez ? a^b
Eduardo Olaz
Entrega 02
Entorno de desarrollo
02 - 5
Pero resulta que tras presionar el botn de Reiniciar (el cuadradito azul) resulta que Access se equivoca y da como resultado 1. Pues no, Access no se ha equivocado. El exponente b tiene el valor 0, y cualquier nmero elevado al exponente 0 da como resultado 1. En realidad ste no es un gran ejemplo, ya que estaramos en el caso singular de 0 elevado a cero, que no est definido en el campo de los nmeros reales. Pero no vamos a ser ms papistas que el Papa. Vamos a hacer ahora otro experimento. Escribimos en la ventana Inmediato:
a = "2" : b = "4" : ? a + b
Tras presionar la tecla Enter el resultado es chan, chan.! Si se te ha ocurrido que sera 6 te puedo decir que ests equivocado. Si tu respuesta ha sido 24, has acertado; pero yo me pregunto No ests perdiendo el tiempo leyendo este captulo? so listo!. Bromas aparte expliquemos el resultado. Como introduccin hemos usado unos elementos que se llaman Operadores. Los que hemos usado por ahora son:
Print 2,4 * 2
Sorpresa!. Qu ha pasado?
Comencemos a programar con VBA - Access
02 - 6
Por qu no nos ha dado como resultado 4,8 que es lo que le dara a un buen cristiano, judo musulmn, (o lo que sea siempre que sea bueno)?. De toda la vida la operacin 2,4 * 2 ha dado como resultado 4,8 La culpa de esto la tienen que tener los americanos! Y no es broma. Slo un pequeo matiz: Los americanos utilizan la coma como separador de miles. En cambio el separador decimal, en vez de la coma, es el punto. En una sentencia Print, cuando encuentra una coma entre dos valores, intercala una tabulacin. Probemos ahora:
a = 2 : b = "Peras" : Print a + b
-No salimos de sustos!-Qu he roto?-
Eduardo Olaz
Entrega 02
Entorno de desarrollo
02 - 7
Tranquilo que no has roto nada. No nos ensearon en el colegio que no haba que sumar Peras con Manzanas? Le estamos diciendo a VBA que sume un nmero con un texto, y lgicamente se queja. Cambiemos la lnea
2 Peras
Al poner el 2 entre comillas, lo hemos cambiado al modo texto y entonces ha funcionado. Si hacemos
Dos Peras
-A ver profe si me aclaroTenemos algo llamado a que le da lo mismo ser Nmero que Texto. Cmo se come esto? Tranquilo pequeo saltamontes. No seas impaciente. La respuesta en las prximas entregas.
VBA - Access
Entrega
03
Primeros conceptos
Eduardo Olaz
03 - 2
Primeros conceptos
Ya hemos visto que en la ventana Inmediato podemos efectuar clculos, con valores directos o con unos elementos a los que les asignamos valores. Todo esto es interesante, pero todava no podemos hacer grandes cosas, de hecho con una simple calculadora seramos ms eficientes. Un poco de paciencia; todo a su tiempo Si no tenemos abierto un fichero Access, es el momento para hacerlo, por ejemplo crea un nuevo fichero con el nombre Entrega03.mdb. Abrimos el apartado [Mdulos] y presionamos el botn [Nuevo]. Pulsamos con el cursor en la ventana de Cdigo y escribimos lo siguiente:
Pi = 25.36
-Por qu?-Porque el valor de una constante es en definitiva Constante, y no se puede cambiar-. -Y cmo podemos declarar algo que se pueda cambiar?-Ese algo, en contraposicin a una constante, se llama Variable. Para declarar variables se podra hacer algo as como
? Pi y presionamos [Enter]
Y no pasa nada! Se supona que seramos muy felices si nos imprimiera el valor 3,14159265358979 Por qu no lo ha hecho?
Eduardo Olaz
Entrega 03
Primeros conceptos
03 - 3
Option Compare Database Option Explicit Public Const Pi As Double = 3.14159265358979 Public Precio As Currency Public Nombre As String
Si ahora pulsamos con el ratn en la ventana Inmediato, escribimos ? Pi y [Enter] Nos escribir 3,14159265358979. Vamos a hacer otra prueba Escribe en la ventana Inmediato Pi = 3.1416 y pulsa [Enter]. Qu ha pasado?
Simplemente que estamos intentando asignar un valor a una constante que ya lo tena. En definitiva una constante slo puede tomar el valor una vez, y a ti te encontr en la calle. Por definicin una constante no puede cambiar. Slo mantiene relaciones y se casa una vez.
Comencemos a programar con VBA - Access
03 - 4
En cambio las variables son mucho ms promiscuas. Todo lo anterior, entendedlo como una introduccin a la declaracin de variables y Constantes, que en realidad presenta ms posibilidades que las aqu vistas. En puntos posteriores, estudiaremos el tema con ms profundidad. Aqu han aparecido cosas nuevas; por ejemplo la palabra Currency y la palabra String. En el ejemplo, con Currency, hacemos que la variable Precio sea del tipo Moneda; un tipo de datos aparentemente de coma flotante creado para manejar datos monetarios sin errores de Redondeo. Podis obtener ayuda sobre estos tipos de datos poniendo el cursor, por ejemplo en cualquiera de sus nombres, por ejemplo Currency y pulsando [F1]. Con String hacemos que la variable Nombre sea tratada como una variable de tipo Cadena, que permite manejar cadenas de Caracteres. En realidad podramos haber hecho las declaraciones sin especificar su tipo pero, por el momento sin ms explicaciones, os dir que esto genera cdigo menos optimizado e incluso puede dar problemas a la hora de encontrar posibles errores. Por ejemplo podramos haber hecho:
Public Const Pi As Variant = 3.14159265358979 Public Precio As Variant Public Nombre As Variant
-Y qu es eso de Variant?El Variant es un tipo de dato al que se le puede asignar casi cualquier cosa. Esto lo hace a costa de consumir ms recursos que otros tipos de variables y por consiguiente una velocidad ms lenta en la ejecucin. Ya tenemos las bases para el siguiente paso: Procedimientos Sub Procedimientos Function Funciones
Procedimientos Sub
Un procedimiento Sub llamado tambin Procedimiento a secas es un conjunto de cdigo que realiza determinadas tareas. Suele estar contenido entre las expresiones Sub y End Sub
Eduardo Olaz
Entrega 03
Primeros conceptos
03 - 5
El trmino Sub puede ir precedido de otras expresiones, por ejemplo para delimitar el mbito en el que puede ser llamado el procedimiento. Al procedimiento se le pueden pasar una serie de datos para que los use internamente. A estos datos se les llama Parmetros Argumentos. Si en la ventana de cdigo escribimos la palabra Sub, le ponemos encima el cursor y presionamos la tecla [F1], Access nos mostrar la ayuda aplicable a Sub. En la ayuda podemos entre otras cosas podemos ver:
[Private | Public | Friend] [Static] Sub nombre [(lista_argumentos)] [instrucciones] [Exit Sub] [instrucciones] End Sub
Por cierto, estas lneas indican cmo es la estructura de un procedimiento Sub. Los elementos que estn entre Parntesis Cuadrados [ ] son opcionales. Cuando hay varias palabras separadas por Barras Verticales |, nos est indicando que podemos seleccionar una de ellas. Segn esto seran igual de vlidas las sentencias:
Sub Procedimiento_01() End Sub Public Sub Procedimiento_02() End Sub Private Sub Procedimiento_03() End Sub Public Sub Procedimiento_04(Argumento1 As Double) End Sub
Por supuesto que cada opcin genera un cdigo que se comportar de forma diferente. Es igualmente vlido el que ya hemos usado en la primera entrega.
Private Sub cmdSaludo_Click() Me.lblSaludo.Caption = "Hola Mundo!!!" Me.Caption = "Aqu estoy!" End Sub
Comencemos a programar con VBA - Access
03 - 6
Con una salvedad, este ltimo es un tipo especial de Sub. Y es especial porque captura el evento que se produce cuando de presiona el botn cmdSaludo de su formulario. Recordemos que un Evento es un mensaje que enva Windows y en este caso es capturado por Access. En el mdulo que hemos creado vamos a escribir el siguiente cdigo:
Public Sub Circunferencia() Dim Radio As Single Radio = 2.5 Debug.Print "Circunferencia = " & 2 * Pi * Radio Debug.Print "Crculo = " & Pi * Radio ^ 2 & " m2" End Sub
Ahora, en la ventana Inmediato escribe Circunferencia y presiona [Enter] Efectivamente, en esa ventana se escribir:
Eduardo Olaz
Entrega 03
Primeros conceptos
03 - 7
Lo primero que imprime es una cadena, en nuestro caso "Circunferencia = " A continuacin vemos el operador & seguido de la expresin 2 * Pi * Radio Este operador hace de Pegamento entre dos expresiones. En este caso enlaza la cadena primera con el resultado de la operacin 2 * Pi * Radio Dando como resultado Circunferencia = 15,7079632679489 Lo mismo es aplicable a la siguiente lnea. Con End Sub se acaba el cdigo del procedimiento. -Esto est bien, pero slo puede imprimir los datos de la circunferencia de radio 2,5, lo que no es gran cosa-. -Bueno, s. Es un inicio-. -Ahora, me voy a sacar un conejo de la chistera y modifico el procedimiento Sub, de la siguiente forma:
Public Sub Circunferencia(Radio As Single) Debug.Print "Circunferencia = " & 2 * Pi * Radio Debug.Print "Crculo = " & Pi * Radio ^ 2 & " m2" End Sub
Fijaros que la declaracin de la variable Radio ya no se hace en el cuerpo del procedimiento, sino en su cabecera. Adems ha desaparecido la lnea en la que asignbamos el valor del radio. -Y cmo se utiliza esto?-. No tiene ninguna complicacin especial Vamos a modificar la forma como llamamos desde la ventana Inmediato al procedimiento, y escribimos
Circunferencia 2.5
El resultado es el mismo que en el caso anterior. Pero si escribimos
Circunferencia 7.8
Nos dar los valores correspondientes a una circunferencia de radio 7.8. En este caso hemos utilizado un procedimiento con un nico argumento. El argumento es precisamente 7.8. Si tuviera, por ejemplo 2, se escribiran separados por una coma
VBA - Access
Entrega
04
Primeros conceptos 2
Eduardo Olaz
04 - 2
Funciones
En la entrega anterior hemos visto una introduccin a los procedimientos Sub. Otro de los tipos de procedimientos son los procedimientos Function, que al igual que los procedimientos Sub estn delimitados por Function y End Function. La principal diferencia entre un procedimiento Sub y un procedimiento Function es que este ltimo devuelve un valor. Entre los dos delimitadores se escribe el cdigo que va a realizar las tareas que deseemos, y al final devolver algn valor. Son las llamadas Funciones. Veamos la forma como lo explica la ayuda de Access. Si vamos a la ventana Inmediato, escribimos Function, ponemos el cursor en lo escrito y presionamos [F1], entre otras cosas nos aparece la sintaxis de su declaracin:
[Public | Private | Friend] [Static] Function nombre [(lista_argumentos)] [As tipo] [instrucciones] [nombre = expresin] [Exit Function] [instrucciones] [nombre = expresin] End Function
Vemos que para declarar una funcin empezaramos, por ejemplo poniendo la palabra Public (si quisiramos que la funcin sea accesible desde cualquier parte de Access) a continuacin la palabra Function, despus abriramos un parntesis y pondramos los parmetros que necesitramos, cerraramos el parntesis y finalmente pondramos el tipo de datos que devolvera la funcin. Supongamos que quisiramos escribir una funcin en la que pasndole el ngulo y el radio, nos devolviera la longitud de un arco de circunferencia. La cabecera de esta funcin sera: Public Function Arco(Angulo As Single, Radio As Single) As Single Hay un truco para que una lnea con mucho texto se pueda dividir en varias lneas, sin que deje de ser una nica lnea. Se coloca al final de la lnea que se quiere dividir un espacio en blanco y la raya de subrayado. Con ello la lnea anterior se podra poner as:
Eduardo Olaz
Entrega 04
Primeros conceptos 2
04 - 3
Abrimos parntesis y escribimos los dos parmetros que necesitamos, Angulo y Radio, declarndolos del tipo Single (tipo numrico de coma flotante de 4 Bytes). Cerramos el parntesis y declaramos la funcin Arco tambin del tipo Single. Si escribs la cabecera de la funcin en un mdulo normal, veris que VBA, al igual que pasaba con los procedimientos Sub, os aade automticamente el final correspondiente, es decir
End Function
Con esto la funcin quedara as:
Public Function Arco( _ Angulo As Single, _ Radio As Single _ ) As Single End Function
Por ahora no es gran cosa, ya que no hace nada. Permitidme que escriba el cdigo completo de esta funcin y os la explique paso a paso. Como en la entrega anterior, voy a declarar la constante Pi como pblica en la cabecera del mdulo, para que pueda ser utilizada por la aplicacin. Si ya la tuviera declarada en otro mdulo este paso no sera necesario. El cdigo sera el siguiente:
Option Compare Database Option Explicit Public Const Pi as Double = 3.14159265358979 Public Function Arco( _ Angulo As Single, _ Radio As Single _ ) As Single Arco = Pi * Radio * Angulo / 180 End Function
Aparte de la frmula matemtica de Trigonometra elemental, vemos lo siguiente. Asignamos al nombre de la funcin Arco el resultado de la frmula de la izquierda. Esto har que la funcin Arco devuelva el resultado de la operacin matemtica. Vamos a centrarnos en lo que aparece entre Function y el final de la lnea.
Comencemos a programar con VBA - Access
04 - 4
? Arco(360,1)
Supongo que ya no ser sorpresa para vosotros la ayuda en lnea que os va prestando el propio VBA de Access:
Tras completar la expresin, y darle a [Enter] nos escribir: 6,283185 que es la longitud de un arco de radio 1 y ngulo 360, lo que equivale a la circunferencia completa. Vamos a hacer un experimento: Qu pasa si cambiamos la declaracin de tipo de esta funcin? Vamos a cambiar el Single de la declaracin de la cabecera, por Double. Double es un nmero de coma flotante de ms precisin que single (8 Bytes de Double frente a 4 de single). La cabecera quedar as:
6,28318530717958
Por ello si cambiamos ahora la cabecera:
eduardo@olaz.net
Eduardo Olaz
Entrega 04
Primeros conceptos 2
04 - 5
Efectivamente as es, pero en una aplicacin crtica deberamos jugar que el tipo de precisin adecuada, para hacer la aplicacin lo ms ligera posible. Si probamos a hacer el cambio vemos que en nuestra funcin, con los datos anteriores apenas cambia el resultado. Pero qu pasara si estuviramos programando la rbita de una nave con destino a Marte? En este caso la precisin sera un elemento clave.
Funciones en formularios
Vamos a hacer una pequea y elemental calculadora que nos permita sumar dos cifras. Cerramos los mdulos y creamos, abrimos un formulario en modo diseo. Este formulario no lo enlazamos con ninguna tabla ni consulta. Ahora vamos a deshabilitar el asistente para controles. En la barra de controles, pulsamos en la Varita mgica para deshabilitarla. Deber quedar sin resaltar, como se indica en el grfico con las flechas rojas.
Ponemos dos cuadros de texto en el formulario. Por defecto, Access les pondr un nombre semejante a Texto2 y Texto2. Vamos a cambiar esos nombres. Abrimos la hoja de propiedades, os recuerdo Men Ver > Propiedades, pulsando las teclas [Alt] + [Enter]. En la pestaa [Otras] ponemos el cursor en la primera propiedad, que casualmente es la propiedad Nombre. Ah es donde, pulsando en los correspondientes cuadros de texto, vamos a cambiar su nombre. Les ponemos, al primer Cuadro de texto el nombre de txtSumando_1, y al segundo txtSumando_2. Ahora le vamos a poner un botn, cambiando su nombre , algo as como Comando5, por cmdSumar, y su ttulo texto del botn, por Sumar. Al programar es muy importante que los objetos, variables, constantes, procedimientos, etc. tengan nombres con cierto sentido. Si no fuese as, si ms adelante tuvieras que revisar tu cdigo, o si lo tuviese que hacer otra persona, tendrais que invertir una gran cantidad de tiempo en averiguar qu es y qu hace cada cosa en el cdigo. Ms adelante hablaremos sobre normalizacin de cdigo, pero fjate que el nombre de los cuadros de texto est precedido por las letras txt y el del botn por cmd. As si en un pedazo de cdigo veo una palabra que empieza por cmd s que se refiere a un botn, y si empieza por txt s que se refiere a un cuadro de texto. Otro ejemplo: Los nombres de las etiquetas los suelo empezar por lbl. Estas son convenciones de normalizacin ms menos estndares. A partir de este momento las ir aplicando al cdigo y veris como su uso se ir convirtiendo en algo cada vez ms natural.
Comencemos a programar con VBA - Access
04 - 6
A lo que bamos. Tenemos en un formulario los siguientes elementos: txtSumando_1 txtSumando_2 cmdSumar Cuadro de texto Cuadro de texto Botn de comando
Probablemente al poner los cuadros de texto se nos hayan colocado dos etiquetas. De momento no vamos a cambiar su nombre, pero les vamos a poner como Ttulo Operando 1 Operando 2 Ahora vamos a poner una etiqueta nueva, con ttulo Resultado, y de nombre lblResultado. Ms de uno se habr dado cuenta que lbl viene de Label (etiqueta en ingls), txt de Text y cmd de Command. A esta etiqueta le vamos a poner un texto un poco ms grande y como color de texto el rojo. Pero qu quieres que hagamos con todo esto? Tan simple como tener un formulario en el que escribir dos nmeros en las correspondientes casillas, y que al pulsar un botn nos muestre su suma. Y cmo sabe Access cuando se ha pulsado el botn y qu es lo que tiene que hacer? Para eso utilizaremos el Evento Clic del botn cmdSumar. Ya he comentado que un Evento es un mensaje que manda Windows, cuando pasa algo, que puede ser captado por Access y a su vez reenviado a Windows con un cdigo que se tiene que ejecutar. Seleccionamos el botn, y en la [Hoja de Propiedades] pulsamos en la pestaa Eventos. Seleccionamos el cuadro correspondiente a Al hacer clic y pulsamos en el pequeo botn con tres puntos que nos aparece. A continuacin veremos una pequea pantalla que nos permite seleccionar entre Generador de expresiones Generador de macros Generador de cdigo
Lgicamente seleccionamos Generador de cdigo, y pulsamos [Aceptar] doble clic en [Generador de cdigo]. Inmediatamente se nos abre el editor de cdigo con el siguiente cdigo
Option Compare Database Option Explicit Private Sub cmdSumar_Click() End Sub
El cursor estar seguramente posicionado entre las dos lneas.
eduardo@olaz.net
Eduardo Olaz
Entrega 04
Primeros conceptos 2
04 - 7
Qu es esto? Es el procedimiento que captura el evento Clic del botn cmdSumar. Ahora lo que quiero es que tras poner un valor en cada uno de los cuadros de texto, si pulsamos el botn, nos muestre el resultado de la suma en la etiqueta lblResultado. El contenido de un cuadro de texto lo maneja su propiedad Value, que se puede leer y escribir. El contenido de una etiqueta (su ttulo) lo maneja su propiedad Caption, y tambin es de lectura y Escritura. Vamos al curro. Escribimos lo siguiente
End Sub
Que traducido al cristiano quiere decir: Cuando se presione sobre el botn cmdSumar (evento clic) pon como ttulo de la etiqueta lblResultado (propiedad caption) el resultado de sumar el contenido (propiedad Value) del cuadro de texto txtSumando_1 y del cuadro de texto txtSumando_2. El control Cuadro de texto es un TextBox y la Etiqueta un control Label. Bueno, grabamos el cdigo y el formulario y lo abrimos. Nos quedar algo as como
Si ahora introducimos, por ejemplo el valor 2 en el primer cuadro de texto, el valor 3 en el segundo y presionamos el botn Sumar, nos llevamos una pequea sorpresa:
04 - 8
Estas son algunas de las cosillas que desaniman a los principiantes No es que Access nos est tomando el pelo. Recordemos que estamos hablando del contenido de un [Cuadro de Texto], y que como tal todo lo que contiene lo interpreta como texto. Aunque hemos introducido un 2 para el control es como si introdujramos el texto 2. Vamos a probar a introducir en el primer Cuadro de texto Pepe, y en el segundo Gotera. Al presionar el botn nos da
Un par de prrafos ms adelante veremos cmo solucionar este problema. Primero un par de matizaciones sobre el cdigo. Tomemos el texto
Me.lblResultado.Caption
Vamos a desmenuzarlo.
Me representa el formulario al que pertenece el cdigo. Me.lblResultado lblResultado representa a la etiqueta lblResultado del formulario actual. Me.lblResultado.Caption Caption representa a la propiedad Ttulo de la etiqueta.
La utilizacin de Me no es necesaria; pero facilita a la ayuda en lnea el que te presente las diferentes opciones a escribir. Tomemos ahora el texto
Me.txtSumando_1.Value
Ya hemos visto que me representa al formulario.
Me.txtSumando_1 txtSumando_1 representa al cuadro de texto txtSumando_1 Me.txtSumando_1.Value Value representa el contenido del cuadro de texto la propiedad Value.
La propiedad Value de los cuadros de texto tiene dos peculiaridades: La primera es que es la propiedad por defecto del control. Eso quiere decir, que si se escribe en el cdigo el nombre del cuadro de texto, sin especificar nada ms, har implcitamente referencia a la propiedad Value.
eduardo@olaz.net
Eduardo Olaz
Entrega 04
Primeros conceptos 2
04 - 9
Esto hace referencia a la segunda peculiaridad de los cuadros de texto: Si el cuadro de texto contiene algn valor, nos devolver un texto con ese valor, pero si no contiene nada nos devolver el valor Nulo (Null). Para poder funcionar as, la propiedad Value del cuadro de texto tiene que ser del tipo Variant. S ese tipo de dato capaz de tragarlo todo. Un valor nulo no es cero, ni una cadena de texto vaca. Un valor nulo en realidad se corresponde al resultado del cdigo ASCII 0. Por el momento no os importe no saber qu significa esto.
Comencemos a programar con VBA - Access
04 - 10
Como uno de los cuadros de texto tena el valor Nulo (Null), VBA no puede sumarlo al contenido del otro cuadro de texto. Podramos decir que un nulo no se puede sumar con nada. Pero qu tenemos que hacer para poder sumar los datos? Primero deberamos convertir los valores contenidos en los cuadros de texto a valores numricos. Para efectuar esa conversin existe una funcin de VBA llamada Val(Valor) Si escribimos en la ventana inmediato la palabra Val, ponemos en ella el cursor y presionamos [F1] nos aparece la ayuda de la funcin Val. Con la siguiente informacin:
Devuelve los nmeros contenidos en una cadena como un valor numrico del tipo adecuado. Sintaxis Val(cadena) El argumento obligatorio cadena es cualquier expresin de cadena vlida.
Vamos a hacer pruebas en la ventana Inmediato. Escribimos las sucesivas expresiones y presionamos [Enter] Expresin ? "2" + "3" ?2+3 ? Val("2") + Val("3") ? Val("12.234") ? Val(" 23 Caballos") ? Val(" 3,1416") ? Val(Pepe) ? Val(Null) ? Val(Nz(Null,"")) ? Nz(Null,Valor Nulo) ? Nz(Pepe,Valor Nulo) Resultado impreso
23 5 5 12,234 23 3 0
Genera un error (Nulo no tiene un valor numrico)
Despus de estos pequeos experimentos dejo que saquis vuestras propias conclusiones. Vemos aqu una cosa nueva, la funcin Nz(Valor). Vamos a escribir Nz en la ventana Inmediato y pulsamos [F1]. Pone algo semejante a esto Nz(ValorVariant[,ValorSiEsNulo]) El resumen de la informacin es que la funcin Nz devuelve uno de esto dos tipos de datos. Si el primer argumento no es nulo, devuelve el primer argumento. Si fuera nulo, devolvera el valor del segundo argumento.
eduardo@olaz.net
Eduardo Olaz
Entrega 04
Primeros conceptos 2
04 - 11
Y con esto qu conseguimos?. Vamos a efectuar un nuevo cambio en el evento Clic del botn y escribimos lo siguiente:
Private Sub cmdSumar_Click() Dim dblSumando1 As Double Dim dblSumando2 As Double dblSumando1 = Val(Nz(txtSumando_1, "")) dblSumando2 = Val(Nz(txtSumando_2, "")) lblResultado.Caption = CStr(dblSumando1 + dblSumando2) End Sub
Si ahora no escribimos nada en alguno, o los dos cuadros de texto, ya no nos genera el error. Igualmente si escribimos valores numricos, nos muestra el dato correcto. Si escribimos un dato no numrico lo cambia al valor Cero, al resultado de Val(Expresin). Tenemos una nueva funcin. En la penltima lnea del cdigo vemos:
Public Function ValorNumerico ( _ Valor As Variant _ ) As Double ValorNumerico = Val(Nz(Valor,)) End Function
Por cierto, las dos comillas seguidas equivalen a una cadena de texto de longitud 0. No confundir con Null. Ya se que es lioso, pero por ahora creedme.
Comencemos a programar con VBA - Access
04 - 12
Esta funcin devuelve el valor Cero cuando le pasamos un valor que sea texto no numrico, o un valor nulo. El parmetro Valor se declara como Variant para que pueda admitir tanto Cadenas de Texto, como Nmeros e incluso el valor Null. Os recuerdo que la funcin la debis escribir en un mdulo estndar si la queris usar desde cualquier otra parte de Access. Tras esto podramos simplificar el cdigo del formulario y cambiarlo por el siguiente:
Private Sub cmdSumar_Click() Dim dblSumando1 As Double Dim dblSumando2 As Double dblSumando1 = ValorNumerico(txtSumando_1) dblSumando2 = ValorNumerico(txtSumando_2) lblResultado.Caption = CStr(dblSumando1 + dblSumando2) End Sub
Ya se que todo esto os puede resultar un poco indigesto, pero haciendo pruebas es como mejor lo podris asimilar. En la prxima entrega haremos un repaso y ampliacin de lo visto hasta ahora.
eduardo@olaz.net
Eduardo Olaz
VBA - Access
Entrega
05
Eduardo Olaz
05 - 2
Declaracin de variables
En entregas anteriores hemos visto cmo se declaran las variables. En esta entrega vamos a ampliar conceptos. Una variable es un elemento del cdigo que apunta a una direccin de memoria en donde se almacena un dato. Haciendo referencia a la variable se puede devolver el dato al que apunta e incluso modificarlo. Las constantes son similares a las variables, slo que su contenido se le asigna en el momento en el que se declaran, y despus no es posible cambiarlo. Hay tres temas que debemos considerar en una variable El nombre de la variable El tipo de dato al que apunta El mbito en el que es visible.
% & ! # @ $
Estos caracteres slo se pueden usar al final del nombre de la variable. Nota: Estos caracteres tambin se pueden usar para declarar el tipo de dato que devuelve una funcin. Por ejemplo esta cabecera de funcin sera vlida:
eduardo@olaz.net
Eduardo Olaz
Entrega 05
05 - 3
No se pueden declarar dos variables con el mismo nombre dentro del mismo procedimiento o en la cabecera de un mismo mdulo.
Tipos de datos
Adems de las Variables hay otra serie de elementos que manejan datos, como son las Constantes, Procedimientos Sub y procedimientos Function que son capaces de manejar datos de distintos tipos, e incluso las funciones que devuelven datos. Pero qu tipos de datos podemos manejar? Y qu caractersticas tienen? Hay varios tipos de datos. Entre ellos podemos definir Numricos Booleanos Fecha / Hora De texto (cadenas) Variant De objeto Registros de datos definidos por el usuario, . . .
Datos numricos
Existen dos familias de datos numricos. Datos numricos de nmero entero Datos numricos de coma flotante. Como datos enteros tenemos los siguientes tipos: Nombre del Tipo Tamao 1 Byte 2 Bytes 4 Bytes Valor inferior 0 -32.768 -2.147.483.648 Valor Superior 255 32.767 2.147.483.647 % & Sufijo Prefijo byt int lng
Por Sufijo entendemos un carcter de definicin de tipo que se puede aadir a la Variable Constante, para definir el tipo al que pertenece. Por ejemplo como Long.
Dim ValorLong&
05 - 4
Por ejemplo
eduardo@olaz.net
Eduardo Olaz
Entrega 05
05 - 5
En las pginas del MSDN de Microsoft podis encontrar informacin muy interesante sobre la normativa de codificacin: http://msdn.microsoft.com/library/spa/default.asp?url=/library/SPA/vbcn7/html/vbconP rogrammingGuidelinesOverview.asp La ventaja de utilizar estos mtodos se aprecia inmediatamente. As, slo con leer que una tiene como nombre lngDiasTrabajados podramos deducir que es una variable de tipo Long que probablemente sirva para manejar los das trabajados. Si en una lnea de cdigo aparece el nombre cmdSalir, podramos deducir que es un botn de comando que probablemente sirva para salir de un formulario u otro sitio. Igualmente lblNombre es una etiqueta y txtApellido1 es un cuadro de texto. Ya iremos viendo estas cositas poco a poco. Repito que los prefijos, int, lng, txt, lbl, cmd, etc. no modelan el tipo de contenido de la variable, sino que sirven como informacin adicional para la persona que escribe lee el cdigo.
2856.1#
! # @ &
Por Sufijo entendemos un carcter de definicin de tipo que se puede aadir a la Variable Para el manejo de valores monetarios se suele utilizar el tipo Currency. Este tipo no da errores de redondeo y permite manejar hasta magnitudes de 15 dgitos exactos en su parte entera. En posteriores captulos haremos mencin a algunos errores de redondeo que pueden llegar a dar los tipos de coma flotante.
Tipo Decimal
El tipo Decimal es un tipo muy especial. Permite trabajar con hasta 29 dgitos enteros exactos hasta con 28 dgitos exactos en su parte decimal. Es un tipo con muy poco uso en Access, ya que normalmente no se necesita ese tipo de precisin, y adems resulta engorroso su uso. Algunas de sus peculiaridades se explican en el Apndice 01.
Tipo Date
El tipo Date es un tipo de dato adecuado para manejar datos de tipo Fecha / Hora.
Comencemos a programar con VBA - Access
05 - 6
El valor 0 representa las 0 horas del 30 de diciembre de 1899. La parte entera representa el nmero de das que han pasado desde esa fecha. La parte decimal representa el tanto por 1 de da adicional que ha pasado. Por ejemplo, ahora son las 18 horas 15 minutos 30 segundos del 20 de enero del 2005 Internamente este dato lo guarda como 38372,7607638889. Qu quiere decir? Han pasado 38372 das desde las 0 horas del 30 de diciembre de 1899 Pero adems ha transcurrido 0,7607638889 das, lo que equivale a 18 horas 15 minutos 30 segundos frente a 24 horas Si pasamos la hora actual a segundos nos da 3600 * 18 + 15 * 30 + 30 65730 segundos Un da completo tiene 3600 seg/hora * 24 horas = 86400 Si dividimos 65730 entre 86400 nos da aproximadamente 0,7607638889 Para ver cmo utilizar un tipo date, vamos a crear un procedimiento Sub en un mdulo estndar que nos permita ver lo explicado en las lneas anteriores. En realidad un tipo Date es un tipo Double y como l utiliza 8 Bytes. Por cierto, el prefijo para indicar que es tipo Date es dat. Su cdigo ser el siguiente
Public Sub PruebaTipoDate() Dim datFechaActual As Date Dim datSemanaProxima As Date Dim datAyer As Date Dim datMaana As Date datFechaActual = #1/20/2005 6:15:30 PM# datSemanaProxima = datFechaActual + 7 datAyer = datFechaActual - 1 datMaana = datFechaActual + 1 Debug.Print Debug.Print Debug.Print Debug.Print Debug.Print End Sub "Tipo Doble", CDbl(datFechaActual) "Ahora", datFechaActual "Prx. Semana", datSemanaProxima "Ayer", datAyer "Maana", datMaana
Entrega 05
05 - 7
Puntualizaciones:
Ya os habris dado cuenta que VBA utiliza el Ingls como lenguaje, y ms concretamente el Ingls americano. Esto hace que los datos usen ese mismo modelo. Por ejemplo, la coma flotante nuestra, ellos la cambian por el punto. Algo as pasa con las fechas. El formato americano de fecha usa el siguiente orden: Mes / Da / Ao Por eso en la lnea
datFechaActual = #1/20/2005 6:15:30 PM# se pone 1/20/2005, en vez de 20/1/2005 como hacemos nosotros.
Esto es importante recordarlo ya que nos evitar futuros errores. Adems VBA tiene un comportamiento que puede despistar. Si hubiramos hecho
datFechaActual = #1-20-2005#
VBA te cambia automticamente a
datFechaActual = #1/20/2005#
Para construir una fecha no es necesario introducirle la hora. Tampoco es necesario introducirle el ao entero. Estas fechas son perfectamente vlidas
Comencemos a programar con VBA - Access
05 - 8
Entrega 05
05 - 9
blnDatoBoleano = 8<7 Asignara False a blnDatoBoleano (es falso que 8 sea menor que 7). blnDatoBoleano = 8>7 Asignara True a blnDatoBoleano.
Para asignar una cadena de longitud variable, la declaracin se hace de forma similar al resto de los tipos
Dim strNombre As String Dim strNombre$ Public strNombre As String Private strNombre As String
Para declarar una variable como de longitud fija se utiliza el asterisco (*) y a continuacin el nmero de caracteres que va a contener
Dim strCuentaCorriente$ * 20
Una cadena de longitud variable podra en teora almacenar hasta 2^31 caracteres, es decir unos 2000 millones de caracteres. Lgicamente siempre que el PC tuviera suficiente memoria. Una cadena de longitud fija puede contener hasta 65536 caracteres (64 KBytes). Si por ejemplo hacemos:
Comencemos a programar con VBA - Access
05 - 10
Dim strCliente As String * 40 strCliente = "Pepe Gotera" strCliente contendr la cadena Pepe Gotera seguida de 29 espacios en blanco
(para completar los 40 que debe contener la variable.
Tipo Variant
El tipo Variant es un tipo de dato que puede contener prcticamente cualquier tipo de datos. El prefijo que se suele utilizar para identificar una variable Variant es var. Hay excepciones, por ejemplo no puede contener una cadena de longitud fija. Cuando declaramos una variable o escribimos la cabecera de una funcin, sin especificar el tipo que va a contener devolver, implcitamente las estamos declarando como de tipo Variant. Si declaramos una constante, sin especificar su tipo, el tipo que tome lo har en funcin del dato que le asignemos en la propia declaracin. Por ejemplo si declaramos en la cabecera de un mdulo estndar
Public Const conEmpresa = "ACME s.l." Public Const conEmpresa As String = "ACME s.l." Public Const conEmpresa$ = "ACME s.l."
La forma de declarar una variable explcitamente como Variant es la siguiente.
Dim varMiVariable
eduardo@olaz.net
Eduardo Olaz
Entrega 05
05 - 11
Public Sub PruebaEmpty() Dim varVariant As Variant Debug.Print "Variable vaca = "; IsEmpty(varVariant) varVariant = Null Debug.Print "Variable vaca = "; IsEmpty(varVariant) Debug.Print "Valor actual = "; varVariant End Sub
Si llamamos a este procedimiento desde la ventana Inmediato, nos imprimir en ella lo siguiente:
strCadenaVacia = ""
05 - 12
Static Friend
Dim
Si la instruccin Dim se utiliza para declarar una variable en la cabecera de un mdulo, esa variable slo ser visible por los procedimientos que estn dentro de ese mdulo. De forma semejante, si la instruccin Dim se utiliza dentro de un procedimiento, esa variable slo podr ser vista por el cdigo del interior del procedimiento.
Private
La instruccin Private se suele utilizar para definir de forma explcita que una constante, variable o procedimiento slo van a ser visibles desde dentro de un mdulo. Se suele utilizar para la declaracin de variables y constantes en las cabeceras de mdulos, as como para definir el alcance de los procedimientos de ese mdulo. Dentro de un procedimiento se usa Dim en vez de Private.
Public
La instruccin Public se suele utilizar para definir que una constante, variable o procedimiento van a ser visibles desde cualquier parte de la aplicacin. Se suele utilizar para la declaracin de variables y constantes en las cabeceras de mdulos, as como para definir el alcance de los procedimientos de ese mdulo. Por ello no es factible declarar una variable como Public dentro de un procedimiento. S se puede en cambio, declararla en la cabecera de cualquier mdulo estndar o de clase.
eduardo@olaz.net
Eduardo Olaz
Entrega 05
05 - 13
Nota: Una aclaracin respecto a los elementos declarados en un mdulo de clase. Si declaramos una variable, constante procedimiento como Public en un mdulo de clase, por ejemplo en un formulario, para poder usarlo hay que hacerlo a travs de una Instancia de ese mdulo de clase. A las variables y constantes pblicas de un mdulo de clase, as como a los procedimientos Property, se les llama Propiedades de la clase. Al resto de los procedimientos de la clase que sean pblicos se les llama Mtodos de la clase. Cuando veamos las clases, veremos que una instancia es un ejemplar de una clase. La clase como tal es el propio cdigo, pero una instancia es un objeto creado mediante la clase. Se que es un poco crptico, pero ms adelante lo entenderis sin problema. Por ejemplo una instancia de un formulario es el propio formulario, o si tenemos la clase Persona y creamos una persona llamada Pepe, la instancia de la clase es Pepe. Si hemos declarado una variable, por ejemplo Nombre como Public para poder usarla deberemos hacer referencia a ella mediante la instancia de la clase, que es Pepe
Pepe.Nombre
Lo mismo ocurrira si hemos declarado como public la funcin Edad, es decir el mtodo Edad.
Pepe.Edad
As mismo, si en el formulario Pedidos declaramos datFechaUltimoPedido como Public, para acceder a ese dato lo tenemos que hacer a travs del propio formulario.
Pedidos.datFechaUltimoPedido
Desde el cdigo del formulario, no es necesario hacer referencia al mismo formulario, aunque s puede hacerse utilizando la palabra clave Me, que es una variable declarada implcitamente y que representa a la instancia de la clase u objeto creado mediante la clase. Desde el propio formulario sera lo mismo
Me.datFechaUltimoPedido
que
datFechaUltimoPedido
No tenis por qu entender todo esto todava. Lo ir comentando poco a poco y lo desarrollar en la entrega en la que hablemos de las Clases.
Global
La instruccin Global se utiliza de forma semejante a Public, dentro de mdulos estndar. Yo personalmente no la suelo usar; prefiero utilizar la instruccin Public.
VBA - Access
Entrega
06
Estructuras de datos
Eduardo Olaz
06 - 2
Matrices Arrays
Una matriz en VBA es un conjunto de variables del mismo tipo, a las que se puede acceder mediante un ndice, que indica su posicin en ella. Imaginemos que queremos almacenar en el cdigo, para su posterior utilizacin, el nmero de das de cada mes del ao. Por ejemplo, podemos hacer esto
As As As As As As
As As As As As As
Public Sub CargarMeses() Mes01 = 31 Mes02 = 28 Mes03 = 31 Mes04 = 30 Mes05 = 31 Mes06 = 30 Mes07 = 31 Mes08 = 31 Mes09 = 30 Mes10 = 31 Mes11 = 30 Mes12 = 31 End Sub
Frente a esta declaracin de variables, un tanto engorrosa, vamos a considerar estas alternativas:
Public Mes(12) As integer Public Sub Mes(1) Mes(2) Mes(3) Mes(4) Mes(5) Mes(6) Mes(7) Mes(8) Mes(9)
eduardo@olaz.net
CargarMeses() = 31 = 28 = 31 = 30 = 31 = 30 = 31 = 31 = 30
Eduardo Olaz
Entrega 06
Estructuras de datos
06 - 3
CargarMeses
La tercera forma an resulta ms directa
Dim Mes() As Variant Public Sub CargarMeses() Mes = Array(0, 31, 28, 31, 30, 31, 30, _ 31, 31, 30, 31, 30, 31) End Sub
A veces, por facilidad de lectura del cdigo, interesa distribuir la escritura de una nica lnea de cdigo, en varias lneas fsicas. Para ello se pone al final de la lnea un espacio en blanco seguido de la barra baja, como se ve en el ejemplo anterior. Para acceder a los das de un mes, por ejemplo Julio, en el ejemplo primero tenemos que utilizar directamente la variable
Mes07
Para hacer lo mismo en los ejemplos 2 y 3, tenemos que tomar en cuenta que la variable Mes contiene los doce valores de los das del mes.
Mes(7)
Este mtodo es mucho ms prctico y da muchas ms posibilidades para usar estructuras de bucle, como veremos ms adelante. Pero, por qu he puesto Mes = Array(0, 31, . . .? Por defecto las matrices, si no se indica el rango de sus ndices, empiezan con el ndice 0. Lo de aadir un valor ms al principio como valor de Mes, en este caso 0 aunque podra haber puesto cualquier otro valor, es para que haya una coherencia entre los casos 2 y 3, y que por ejemplo Mes(7) sea el valor de Julio en las dos matrices. La declaracin Dim Mes(12) As integer genera trece variables, accesibles desde el ndice 0 Mes(0) al ndice 12 Mes(12). Una segunda puntualizacin: La lnea de cdigo
Mes = Array(0, 31, 28, 31, 30, 31, 30, _ 31, 31, 30, 31, 30, 31) hace que, Mes(i), para cualquiera de los valores de i, sea del tipo integer.
Si quisiramos que Mes(i) devolviera un tipo Long, deberamos poner el sufijo de declaracin de tipo Long & detrs de cada nmero:
Mes = Array(0&, 31&, 28&, 31&, 30&, 31&, 30&, _ 31&, 31&, 30&, 31&, 30&, 31&)
Comencemos a programar con VBA - Access
06 - 4
Si quisiramos que las matrices por defecto comenzaran con el ndice 1, deberamos escribir en uno de los mdulos, antes que cualquier procedimiento o declaracin de matriz, la instruccin Option Base 1 Si hubiramos escrito en la cabecera del mdulo
Option Base 1
Y a continuacin Dim Mes(12) As integer se puede acceder a la variable Mes mediante ndices que van del 1 al 12. Existe la posibilidad de incluir en la declaracin el rango de ndices que va a manejar una matriz. La forma de hacerlo es
Vivienda(13, 0, 2) = 3
Si despus en algn punto del cdigo hacemos
intPersonas = Vivienda(13, 0, 2) = 3
la variable intPersonas contendr el valor 3.
Matrices dinmicas
Supongamos que estamos haciendo un programa de ventas y que uno de sus condiciones es poder tener controladas en memoria, una vez seleccionado un tipo de producto, las referencias que existen del mismo. Nos podremos encontrar tipos de producto con 1 referencia, otros con 4 con cualquier nmero de ellas. A priori desconocemos el nmero de referencias que puede tener un tipo de producto, es ms su nmero puede cambiar con el tiempo. Para solucionar este tipo de problemas existen las llamadas Matrices Dinmicas. Una matriz dinmica debe ser declarada, a nivel de mdulo o de procedimiento, sin ndices. En nuestro caso se hara
Eduardo Olaz
Entrega 06
Estructuras de datos
06 - 5
Si vamos a trabajar con un tipo de producto que tuviera 8 referencias, podremos redimensionar la matriz mediante la instruccin Redim.
ReDim Referencias(1 to 8)
Supongamos que posteriormente cambiamos de tipo de producto y pasamos a uno con slo 2 referencias. En el cdigo haremos
ReDim Referencias(1 to 2) Tras redimensionar una matriz con Redim, los valores que contena la matriz se
reinicializan, tomando el valor por defecto del tipo de dato declarado. En el caso de las cadenas el valor por defecto es la cadena vaca , en el de los nmeros el valor es 0 y en el de los Variant el valor Empty. Si por necesidades de programacin deseramos conservar los valores que tena una matriz dinmica antes de su redimiensionado, hay que utilizar la instruccin Preserve entre Redim y el nombre de la variable matriz. Para ver esto vamos a analizar este cdigo:
Public Sub PruebaRedim() Dim n As Long Dim Referencias() As String Debug.Print n = 5 ReDim Referencias(1 To n) Referencias(5) = "Referencia 05" Debug.Print Referencias(5) n = 8 ReDim Referencias(1 To n) Debug.Print Debug.Print "Tras Redim" Debug.Print "Los datos se han borrado" Debug.Print """" & Referencias(5) & """" Debug.Print Referencias(5) = "Referencia 05" Referencias(8) = "Referencia 08" Debug.Print "Los datos se han cargado" Debug.Print """" & Referencias(5) & """" Debug.Print """" & Referencias(8) & """" Debug.Print n = 10 ReDim Preserve Referencias(1 To n) Debug.Print "Tras Redim con Preserve" Debug.Print "los datos se han conservado" Debug.Print """" & Referencias(5) & """"
Comencemos a programar con VBA - Access
06 - 6
Debug.Print """" & "MiTexto" & """" hace que se imprima "MiTexto" en la ventana Inmediato. En el caso del ejemplo: "Referencia 05".
Si ejecutamos el procedimiento PruebaRedim, nos imprimir en la ventana Inmediato:
Referencia 05 Tras Redim Los datos se han borrado "" Los datos se han cargado "Referencia 05" "Referencia 08" Tras Redim con Preserve los datos se han conservado "Referencia 05" "Referencia 08"
Tras las lneas
n = 5 ReDim Referencias(1 To n)
Redimensiona la matriz Referencias como si hubiramos hecho
ReDim Referencias(1 To 5)
Posteriormente asigna un valor al elemento de la matriz de ndice 5 y lo imprime. Lo siguiente que hace es redimensionar la matriz a 8 elementos. Tras ello el elemento 5 de la matriz ha desaparecido Vuelve a asignar valores, en este caso a los elementos 5 y 8 y los imprime. Redimensiona otra vez la matriz, esta vez con preserve, y se comprueba que no han desaparecido los valores anteriores. Nota: Lgicamente, aunque usemos Preserve, si redimensionamos una matriz a un nmero menor de elementos que la matriz anterior, los elementos superiores al nuevo ndice mximo desaparecern.
eduardo@olaz.net
Eduardo Olaz
Entrega 06
Estructuras de datos
06 - 7
Instruccin Erase Si tenemos declarada una matriz dinmica, VBA reserva una zona de memoria para guardar sus datos. Si quisiramos dejar libre, de forma explcita esa memoria una vez utilizada esa matriz, podemos usar la instruccin Erase. Si consultamos la ayuda de VBA indica que Erase Vuelve a inicializar los elementos de matrices de tamao fijo y libera el espacio de almacenamiento asignado a matrices dinmicas. Esto quiere decir que si tenemos declarada una matriz de tamao fijo por ejemplo:
Redim Vivienda(1 To 4, 1 To 8, 1 To 6)
ndices superior e inferior de una matriz. En un punto del cdigo nos puede ocurrir que necesitemos saber qu indices tiene como mximo y mnimo una matriz, ya sea no dinmica. Para ello tenemos las funciones UBound y LBound.
06 - 8
ReDim MatrizDinamica(-2 To 4) As integer Debug.Print "Valor mnimo de Matriz()" Debug.Print LBound(Matriz) Debug.Print "Valor mximo de Matriz()" Debug.Print UBound(Matriz) Debug.Print "Nmero de elementos" Debug.Print Debug.Print UBound(MatrizDinamica) - LBound(Matriz) + 1 Debug.Print "Valor mnimo de MatrizDinamica()" Debug.Print LBound(MatrizDinamica) Debug.Print "Valor mximo de MatrizDinamica()" Debug.Print UBound(MatrizDinamica) Debug.Print "Nmero de elementos" Debug.Print UBound(MatrizDinamica) - LBound(Matriz) + 1 Debug.Print Debug.Print "Valor mximo ndice 1 MultiDimensional()" Debug.Print UBound(MultiDimensional, 1) Debug.Print "Valor mnimo ndice 2 MultiDimensional()" Debug.Print LBound(MultiDimensional, 2) Debug.Print "Valor mnimo ndice 2 MultiDimensional()" Debug.Print LBound(MultiDimensional, 3) End Sub Para obtener el valor mximo mnimo de los ndices en una matriz de varias dimensiones, como se puede ver en el cdigo, hay que hacer lo siguiente
strNombre As String strApellido1 As String strApellido2 As String datNacimiento As Date strTelefono As String
Incluso si tuviramos que manejar varias personas simultneamente podramos crear esas variables como matrices dinmicas. Pero no sera una ventaja agrupar todos los datos en una misma variable?
eduardo@olaz.net
Eduardo Olaz
Entrega 06
Estructuras de datos
06 - 9
Supongamos que lo pudiramos hacer, y que esa variable se llamara Amigo. Sera interesante que
Public Type Persona Nombre As String Apellido1 As String Apellido2 As String FechaNacimiento As Date Telefono As String End Type
Tras esto podramos hacer
Public Sub PruebaRegistro() Dim Cliente As Persona Dim Vendedor As Persona Cliente.Nombre = "Antonio" Cliente.Apellido1 = "Vargas" Cliente.Apellido2 = "Gimnez" Cliente.Telefono = "979111111" Debug.Print Debug.Print Cliente.Nombre Debug.Print Cliente.Apellido1 Debug.Print Cliente.Apellido2 Debug.Print Cliente.Telefono Debug.Print 'Ahora usando With With Vendedor .Nombre = "Pedro" .Apellido1 = "Jaizqubel" .Apellido2 = "Gorriz" .Telefono = "979222222" Debug.Print.Nombre _ & " "; .Apellido1 _ & " "; .Apellido2 Debug.Print "Telfono " _ & .Telefono End With End Sub
06 - 10
eduardo@olaz.net
Eduardo Olaz
Entrega 06
Estructuras de datos
06 - 11
Matrices de Registros Adems de los tipos de datos estudiados tambin podemos declarar matrices de Estructuras tipo Registro, incluso Matrices Dinmicas. Por ejemplo podramos declarar en la cabecera de un formulario lo siguiente:
En el siguiente captulo veremos unos Objetos muy utilizados internamente por Access. Son las Colecciones. Estas estructuras del tipo Collection aparecen en muchos elementos de Access: Formularios Informes Controles ADO DAO Etc
VBA - Access
Entrega
07
Colecciones y Objetos
Eduardo Olaz
07 - 2
Son como un saco en los que podemos meter casi todo. Una de las pocas cosas que no se pueden aadir a una coleccin son las estructuras Registro definidas por el usuario, que vimos en la entrega anterior. Para poder meter en una matriz simultneamente diferentes tipos de datos, habra que declararla como Variant, o lo que es lo mismo, no especificar su tipo en el momento de declararla. En la declaracin de una variable del tipo Collection, no hay que declarar ni el nmero de elementos que va a contener, ni su tipo. Aunque ms adelante hablaremos de los Objetos, para declarar una coleccin, como objeto que es, hay que dar 2 pasos. 1. Se declara una variable como del tipo Collection. 2. Se crea una instancia del objeto Collection utilizando la palabra clave New y la palabra clave Set que sirve para la Asignacin de objetos.
3. Hay una alternativa que es declararla y realizar la instancia en un nico paso, que Public NombreDeLaColeccin As New Collection sera:
Para aadir elementos a una coleccin, se utiliza el mtodo Add. La forma es La forma es
Podemos incluso definir una palabra clave para acceder despus a un elemento dado de la coleccin.
eduardo@olaz.net
Entrega 07
Colecciones
07 - 3
Ejemplo: Supongamos que tenemos la coleccin Productos. Para aadir elemento "Llave fija de de 7-8 mm." podemos hacer:
"DestPhi009"
lngElementos = Productos.Count
Podemos obtener un elemento de la coleccin mediante su ndice su Clave. Para eliminar un elemento de una coleccin se utiliza el mtodo Remove, indicando su ndice su clave. Productos.Remove (3) Productos.Remove ("DestPhi009") Vamos ahora a probar todo. Escribimos en un mdulo estndar lo siguiente Public Sub PruebaColeccion1() Dim Productos As Collection Set Productos = New Collection Productos.Add Productos.Add Productos.Add Productos.Add Debug.Print Debug.Print Debug.Print Debug.Print Debug.Print "Llave fija de de 7-8 mm." "Destornillador Philips de 9 mm.", "DestPhi009" "Martillo Carpintero 4", "MrtCrp004", 2 "Martillo Carpintero 6", "MrtCrp006", , 2
Productos.Remove (3) Productos.Remove ("DestPhi009") Debug.Print Productos.Count & " elementos" End Sub
Comencemos a programar con VBA - Access
07 - 4
4 elementos Llave fija de de 7-8 mm. Martillo Carpintero 4 Martillo Carpintero 6 Destornillador Philips de 9 mm. 2 elementos
Si utilizamos la opcin de aadir un elemento antes despus de uno dado, deberamos cerciorarnos de que ese elemento existe. Si en el ejemplo anterior la lnea 6 hubiera sido Productos.Add "Martillo Carpintero 4", "MrtCrp004", 6 Nos hubiera dado un error de Subndice fuera de intervalo, ya que en ese momento la coleccin Productos no contena 6 elementos. Para averiguar si existe ese n de elemento usaremos el mtodo Count del objeto Productos.
For Each Elemento in Coleccin (Tras esto each pasa a ser uno de los elementos de esa coleccin y podemos usarlo) Next Elemento Elemento debe ser del tipo variant un objeto del tipo de los que estn en la coleccin
Por ejemplo, vamos a crear un nuevo procedimiento:
Public Sub PruebaColeccion2() Dim Clientes As New Collection Dim Cliente As Variant With Clientes .Add "Antonio Urrutia Garastegui", "Urrutia" .Add "Ibn Arregui Viana", "Arregui" .Add "Pedro Martnez Vergara", "Martnez" End With For Each Cliente In Clientes Debug.Print Cliente Next Cliente End Sub
eduardo@olaz.net
Eduardo Olaz
Entrega 07
Colecciones
07 - 5
Colecciones de Controles
Las colecciones las podemos encontrar en muchas partes de Access. Por ejemplo los formularios e informe tienen una serie de controles; pues bien, tambin cada formulario, o informe, posee una coleccin que es la coleccin Controls. Esta coleccin guarda las referencias de los controles del formulario, o informe. Vamos a ver cmo podemos leer datos de los Objetos de esta coleccin. Para ello vamos a utilizar la estructura
Private Sub Form_Load() Caption = " Controles en " & Me.Name MostrarControles End Sub
Y a continuacin escribiremos en el procedimiento MostrarControles, con lo que quedar as:
Option Compare Database Option Explicit Private Sub Form_Load() Caption = " Controles en el formulario"
Comencemos a programar con VBA - Access
07 - 6
MostrarControles End Sub Private Sub MostrarControles () Dim ctrl As Control Dim strMensaje As String For Each ctrl In Me.Controls strMensaje = strMensaje & ctrl.Name & vbCrLf Next ctrl MsgBox strMensaje, _ vbInformation + vbOKOnly, _ " Controles en " _ & Me.Name End Sub
Al arrancar el formulario, y antes de mostrarse aparecer este Cuadro de mensaje. Como inciso, quiero indicar que un Cuadro de Mensaje se muestra mediante la funcin MsgBox. Permite mostrar mensajes con diversos tipos de iconos, botones y configurar el ttulo de la barra superior.
El evento Form_Load() (al cargar) del formulario realiza 2 acciones Primero pone ttulo al formulario Access. En este caso Controles en el formulario. Para ello utiliza la lnea
Caption = " Controles en el formulario" La propiedad Caption, en este caso se refiere al ttulo del propio formulario. A continuacin llama al procedimiento MostrarControles que est definido en el mismo formulario como un procedimiento private.
Vamos a fijarnos detenidamente en el procedimiento MostrarControles. En l definimos dos variables, la primera de nombre ctrl, de tipo Control. La segunda es la cadena de texto strMensaje de tipo string. A continuacin utiliza la estructura de cdigo.
Eduardo Olaz
Entrega 07
Colecciones
07 - 7
Toma, uno a uno, todos los objetos Control que encuentres en la coleccin Controls del formulario actual y los vas asignando a la variable ctrl. Con esto se consigue que ctrl vaya adquiriendo sucesivamente la posibilidad de manejar cada uno de los controles de la coleccin Controls, lo que equivale a poder manejar todos los controles del formulario. En este cdigo, cuando ctrl apunta a un control, puede leer modificar algunas de las propiedades del control al que apunta. Por ejemplo puede leer su propiedad Name y escribirla en la cadena strMensaje. A continuacin llega a la siguiente lnea que le hace buscar el siguiente control de la coleccin Controls. Estas dos lneas son:
vbCrLf es una constante definida por VBA. Es una constante de texto que contiene dos
caracteres, en concreto el carcter que produce un Retorno de Carro (ASCII 13) y el carcter que genera un Salto de Lnea (ASCII 10). Forma parte de las llamadas Constantes Intrnsecas. En resumen va asignando uno a uno los controles de Controls a la variable ctrl, leemos su propiedad Name que escribimos en la variable strMensaje y vamos repitiendo el proceso y escribiendo al principio de la lnea siguiente. A continuacin mostramos el valor de la variable strMensaje en un cuadro de mensaje. Este es un ejemplo que nos muestra cmo podemos ir asignando los elementos de una coleccin de controles a una variable del tipo control. Las colecciones Controls las crea implcitamente Access en los formularios e informes.
07 - 8
Option Compare Database Option Explicit Public Public Public Public Public Nombre As String Apellido1 As String Apellido2 As String FechaNacimiento As Date Telefono As String
Public Function Edad() As Long ' Que conste que esta funcin no es exacta If FechaNacimiento > 0 Then Edad = (Date - FechaNacimiento) / 365.2425
eduardo@olaz.net
Eduardo Olaz
Entrega 07
Colecciones
07 - 9
End If End Function Public Function NombreCompleto() As String NombreCompleto = Nombre _ & " " & Apellido1 _ & " " & Apellido2 End Function
Tampoco nos ha pasado nada por escribir esto. Prcticamente la nica parte que todava no hemos visto es
Option Compare Database Option Explicit Public Empleados As Collection Public Sub PruebaConClase() Set Empleados = New Collection Dim psnEmpleado As Cpersona Dim Empleado As New Cpersona Dim i As Long 'Asignamos valores a la variable psnEmpleado Set psnEmpleado = New Cpersona With psnEmpleado
Comencemos a programar con VBA - Access
07 - 10
.Nombre = "Antonio" .Apellido1 = "Urrutia" .Apellido2 = "Garastegui" .FechaNacimiento = #2/24/1965# .Telefono = "998111111" End With ' Aadimos el contenido de la variable a la coleccin Empleados.Add psnEmpleado, "Urrutia" 'Asignamos valores del 2 empleado a psnEmpleado Set psnEmpleado = New Cpersona With psnEmpleado .Nombre = "Ibn" .Apellido1 = "Arregui" .Apellido2 = "Viana" .FechaNacimiento = #9/14/1985# .Telefono = "998222222" End With ' Aadimos el segundo empleado a la coleccin Empleados.Add psnEmpleado, "Arregui" 'Asignamos valores de otro nuevo empleado Set psnEmpleado = New Cpersona With psnEmpleado .Nombre = "Pedro" .Apellido1 = "Martnez" .Apellido2 = "Vergara" .FechaNacimiento = #3/11/1979# .Telefono = "998333333" End With ' Aadimos el segundo empleado a la coleccin Empleados.Add psnEmpleado, "Martnez" For Each Empleado In Empleados With Empleado Debug.Print .Nombre Debug.Print .Apellido1 Debug.Print .Apellido2 Debug.Print .FechaNacimiento Debug.Print .Telefono Debug.Print .NombreCompleto _ & ", " & .Edad & " aos"
eduardo@olaz.net
Eduardo Olaz
Entrega 07
Colecciones
07 - 11
End With Next Empleado Set Empleado = Empleados(2) With Empleado Debug.Print .Nombre Debug.Print .Apellido1 Debug.Print .Apellido2 Debug.Print .FechaNacimiento Debug.Print .Telefono Debug.Print .NombreCompleto _ & ", " & .Edad & " aos" End With Set Empleado = Empleados("Martnez") With Empleado Debug.Print .Nombre Debug.Print .Apellido1 Debug.Print .Apellido2 Debug.Print .FechaNacimiento Debug.Print .Telefono Debug.Print .NombreCompleto _ & ", " & .Edad & " aos" End With VaciaColeccion Empleados End Sub Public Sub VaciaColeccion(ByRef Coleccion As Collection) Dim i As Long For i = Coleccion.Count To 1 Step -1 Coleccion.Remove (i) Next i End Sub
Otro tema nuevo que aparece aqu es el bucle For - - - Next en el procedimiento VaciaColeccin. Os adelanto que en este caso i va tomando valores que van del nmero de elementos de la coleccin, hasta 1, disminuyendo de 1 en 1. Analizad este cdigo y en la prxima entrega lo comentar ms a fondo.
Comencemos a programar con VBA - Access
07 - 12
Antonio Urrutia Garastegui 24/02/1965 998111111 Antonio Urrutia Garastegui, 40 aos Ibn Arregui Viana 14/09/1985 998222222 Ibn Arregui Viana, 19 aos Pedro Martnez Vergara 11/03/1979 998333333 Pedro Martnez Vergara, 26 aos Ibn Arregui Viana 14/09/1985 998222222 Ibn Arregui Viana, 19 aos Pedro Martnez Vergara 11/03/1979 998333333 Pedro Martnez Vergara, 26 aos
eduardo@olaz.net
Eduardo Olaz
VBA - Access
Entrega
08
Eduardo Olaz
08 - 2
Option Compare Database Option Explicit Public Public Public Public Public Nombre As String Apellido1 As String Apellido2 As String FechaNacimiento As Date Telefono As String
Public Function Edad() As Long ' Que conste que esta funcin no es exacta If FechaNacimiento <> 0 Then Edad = (Date - FechaNacimiento) / 365.2425 End If End Function Public Function NombreCompleto() As String NombreCompleto = Nombre _ & " " & Apellido1 _ & " " & Apellido2 End Function
Las dos primeras lneas son similares a las que podemos encontrar en todos los mdulos de Access. Os recuerdo que la primera es para indicar que los criterios de comparacin entre cadenas sern los definidos por Access, y la segunda es para exigir la declaracin Explcita de las variables. En cuanto a las Propiedades tenemos 5,
Nombre Edad
FechaNacimiento
Telfono
Eduardo Olaz
Entrega 08
08 - 3
Tanto Edad como NombreCompleto tambin se podran considerar como propiedades de la clase CPersona, de slo lectura. No obstante existen otros mtodos especficos para crear las propiedades. Estos mtodos son los procedimientos Property. Hablaremos de ellos en otra entrega prxima cuando estudiemos las clases con ms de profundidad. Realmente estas propiedades, y mtodos no revisten ninguna especial complicacin. Edad tras comprobar que el dato pasado sea diferente que cero realiza un simple clculo matemtico de resta y divisin, devolviendo el resultado. Si el parmetro fuera igual a cero, devolver el valor por defecto de un Long, que tambin es cero. En cuanto a NombreCompleto, realiza una concatenacin de los contenidos de las tres primeras propiedades, separndolas con Espacios en Blanco (" ") y devuelve su resultado. Ya tenemos definida la clase CPersona, pero y ahora qu podemos hacer con ella?. Ya os he comentado que una clase sirve para crear Objetos. Y en qu se diferencia una clase de un objeto?. Aunque no sea una comparacin exacta, la que puede haber entre los planos de ingeniera de un automvil, y el automvil construido. O entre el cdigo gentico de un ser y el ser vivo real. Si mediante el cdigo de la clase CPersona, creamos el objeto Pepe, y el objeto Juan, (se les llamara Ejemplares de la Clase CPersona), ambos tienen en comn el cdigo en el que se basan para existir, me refiero a los objetos informticos, pero en concreto estos dos Ejemplares cambian en al menos, la propiedad Nombre. Por ejemplo, yo tengo un hermano gemelo, que se llama Enrique. Si utilizramos mis datos y los de mi hermano para atribuir las propiedades al Objeto Yo y al Objeto MiHermano, de la case CPersona, diferirn en dos propiedades, el Nombre y el Telfono, pero la clase en la que se basarn ser la misma. Es decir, el Objeto es la entidad creada, y la clase es el cdigo en el que se basa ese objeto. Para crear un objeto en VBA, se han de hacer dos cosas 1. Se declara la variable que va a manejar ese objeto, como del tipo de la clase del Dim Yo As CPersona objeto. Tras esta declaracin se define que la variable Yo va a ser un objeto del tipo del generado por la clase CPersona. Pero en realidad todava no es nada. Por ejemplo, si CPersona fueran los planos de un coche, tras esta declaracin Yo sera algo as como la orden de fabricacin de ese coche. Todava slo existe en un papel, aunque se sabe que se tiene intencin de fabricarlo. Y cmo se fabrica? 2. Utilizando la instruccin New. Set Yo = New CPersona Esta lnea sera equivalente a decir Haz que Yo sea una CPersona Nueva. Tras esto Yo saldra nuevecito y reluciente del taller de montaje de los modelos CPersona. Pero Yo quin es. Es slo un Ejemplar de la clase CPersona, Tambin podemos decir que es una Instancia de la clase CPersona un Objeto de la clase. Lo
08 - 4
que hemos hecho, es mediante la instruccin Set, asignar una Referencia de Objeto a la Variable Yo. Pobre Yo! Es todas estas cosas, en la jerga informtica, pero en realidad quin es? Es slo un indocumentado! Y es un indocumentado porque no tiene ni nombre ni apellidos ni telfono, y su fecha de nacimiento, as como su edad es Cero. Vamos, que es un Cero a la izquierda. Pero eso s, es un flamante y nuevecito objeto. Como he indicado la instruccin Set asigna una referencia de Objeto a la variable Yo. En realidad qu hace?. Con New CPersona creamos una zona de memoria en la que ubicamos el objeto, y con Set conectamos la variable Yo a esa zona de memoria. Si la declaracin de la variable Yo la hubiramos hecho de esta forma:
Option Compare Database Option Explicit Public Sub PruebaDeLaClaseCPersona() Dim Friki As CPersona Dim Alienigena1 As CPersona ' Hay un segundo aliengena que lo vamos _ a declarar y crear simultneamente Dim Alienigena2 As New CPersona ' Creamos una instancia del Friki Set Friki = New CPersona 'Todava no es nadie, vamos a identificarlo With Friki .Nombre = "Carlos Jess" .Apellido1 = "el Curandero" .Apellido2 = "Friki" .FechaNacimiento = #1/24/1945#
eduardo@olaz.net
Eduardo Olaz
Entrega 08
08 - 5
.Telefono = "696969696" End With ' Ya no es un Sin Papeles ' est documentado y nos saluda. Debug.Print Debug.Print Debug.Print Debug.Print Debug.Print " - - - - - - - - - - - - - - - - - -" "Hola terrcolas" "Soy " & Friki.NombreCompleto "Tengo " & Friki.Edad & " aos" "Y he venido a salvar al Mundo"
' Como todo el mundo sabe _ los aliengenas son capaces _ de apoderarse de las mentes de los Frikis ' Vamos a crear al Alienigena1 _ no slo con los datos del Friki _ sino siendo el verdadero Friki Set Alienigena1 = Friki ' y lo podemos comprobar Debug.Print " - - - - - - - - - - - - - - - - - -" Debug.Print "Brrlp. Brrlp" Debug.Print "Ahora habla el aliengena" Debug.Print "que se ha hecho Uno con el Friki:" Debug.Print "Soy un aliengena" Debug.Print "en el cuerpo de " _ & Alienigena1.NombreCompleto ' Al Alingena1 no le gusta _ lo que ha encontrado en el Friki ' Por ello recupera su verdadera personalidad With Alienigena1 .Nombre = "Christopher" .Apellido1 = "el Mensajero" .Apellido2 = "de Raticuln" .FechaNacimiento = #1/1/1492# End With 'Tras recuperar Alienigena1 su personalidad _ Qu ha pasado con el Friki? ' que al ser la misma persona que el aliengena _ tambin ha cambiado Debug.Print " - - - - - - - - - - - - - - - - - -" Debug.Print "Transferida la personalidad"
Comencemos a programar con VBA - Access
08 - 6
"del Aliengena al Friki" "Nuevo mensaje del Friki:" "Hola terrestres" "Soy " & Friki.NombreCompleto "Tengo " & Friki.Edad & " aos" "Y en el pi llevo una pila" "de 2 millones de voltios"
' El Alienigena2 todava est en el hiper-espacio ' Hagamos que pise la tierra With Alienigena2 .Nombre = "Micael" .Apellido1 = "el Vengador" .Apellido2 = "de Gamnedes" .FechaNacimiento = #12/31/999# End With ' Y trasvasa su personalidad al Friki Set Friki = Alienigena2 Debug.Print " - - - - - - - - - - - - - - - - - -" Debug.Print "Brrlp. Brrlp" Debug.Print "Ahora el Friki es el segundo Aliengena:" Debug.Print "Soy " & Friki.NombreCompleto Debug.Print "Tengo " & Friki.Edad & " aos" Debug.Print "Y castigar a los impos" ' Harto de tanta tontera, apago el televisor _ y destruyo las referencias de los objetos creados Set Friki = Nothing Set Alienigena1 = Nothing Set Alienigena2 = Nothing End Sub Si ejecutamos el procedimiento PruebaDeLaClaseCPersona nos mostrar en la
ventana inmediato los siguiente:
- - - - - - - - - - - - - - - - - Hola terrcolas Soy Carlos Jess el Curandero Friki Tengo 60 aos Y he venido a salvar al Mundo - - - - - - - - - - - - - - - - - Brrlp. Brrlp Ahora habla el aliengena
eduardo@olaz.net
Eduardo Olaz
Entrega 08
08 - 7
que se ha hecho Uno con el Friki: Soy un aliengena en el cuerpo de Carlos Jess el Curandero Friki - - - - - - - - - - - - - - - - - Transferida la personalidad del Aliengena al Friki Nuevo mensaje del Friki: Hola terrestres Soy Christopher el Mensajero de Raticuln Tengo 513 aos Y en el pi llevo una pila de 2 millones de voltios - - - - - - - - - - - - - - - - - Brrlp. Brrlp Ahora el Friki es el segundo Aliengena: Soy Micael el Vengador de Gamnedes Tengo 1005 aos Y castigar a los impos
Antes de que se termine el procedimiento, existen tres variables que hacen referencias a objetos de la clase CPersona. Tras las lneas
08 - 8
With.
Aadamos el objeto que instancia la variable psnEmpleado en la coleccin Empleados, mediante su mtodo Add. A continuacin volvamos a reconstruir el objeto al que instancia la variable psnEmpleado. Para ello volvemos a utilizar New. Asignamos datos a sus propiedades y volvemos a aadirlo a la coleccin. Por qu vuelvo a llamar a Set psnEmpleado = New Cpersona? Si no hubiera hecho, el primer elemento de la coleccin, sera el mismo que el objeto que instancia la variable psnEmpleado. Por ello al cambiar los datos en la variable, tambin se cambiaran en el elemento de la coleccin. Es decir, Empleados(1) apuntara al mismo objeto que el que es apuntado por psnEmpleado, por lo que, al ser el mismo, tendra las mismas propiedades, como hemos visto con el Friki y los Aliengenas. En unas lneas posteriores, hacemos asignaciones de Objetos a Variables de objeto mediante Set, como hemos visto en el punto anterior. Quiero resaltar que cuando hemos cargado los datos en la coleccin, con cada elemento le hemos asignado una clave. Esto nos permite acceder a los elementos de la coleccin Empleados mediante su ndice o su clave. Esto lo podemos ver en las lneas
Set Empleado = Empleados("Martnez") Voy a fijarme en el ltimo procedimiento, VaciaColeccion. Fijaos que este procedimiento es llamado al final del procedimiento PruebaConClase.
Public Sub VaciaColeccion(ByRef Coleccion As Collection) Dim i As Long For i = Coleccion.Count To 1 Step -1 Coleccion.Remove (i) Next i End Sub
eduardo@olaz.net
Eduardo Olaz
Entrega 08
08 - 9
Qu hace este procedimento? Primero observad que el parmetro Coleccin, tiene delante la palabra ByRef.
ByRef hace que se pase al procedimiento la propia coleccin, al contrario que si se hubiera utilizado ByVal, que hara que lo que se pasara como parmetro al procedimiento fuera una copia del parmetro
El bucle For . . . Next hace que se repita el cdigo que hay entre For y Next, mientras i sea mayor igual que 1. Me explico:
For i = Coleccion.Count To 1 Step -1 Hace que i vaya tomando como valores n, n-1, n-2, . . , 2 ,1 siendo n el nmero de elementos de la coleccin, devuelto por la propiedad Count de la misma. La variable i va cambiando de valor con cada vuelta, y el incremento decremento lo establece la cantidad que sigue a Step; en este caso -1, con lo que decrece de uno en uno. Si hubiera sido 2, la variable hubiera crecido de 2 en 2. despus de alcanzar i el valor que se indica a continuacin de To, en este caso 1, el bucle se detiene.
Si no se indica Step Incremento, la variable que sigue a For Variable = ir creciendo de 1 en 1. En este ejemplo, el cdigo que contiene el bucle es
VBA - Access
Entrega
09
Estructuras de Control
Eduardo Olaz
09 - 2
Estructuras de Control.
Las estructuras de control son segmentos de cdigo que nos permiten tomar decisiones en base a unos datos dados, o repetir procesos (bucles) mientras sucedan determinadas condiciones en los parmetros controlados por el cdigo. Ya hemos comentado algunas de ellas en las entregas anteriores. Estas estructuras vienen determinadas por una serie de instrucciones, entre las que destacaremos: Estructuras de Decisin
Nota:
If . . . . Then If . . . . Then . . . . Else IIF Select . . . . Case For . . . . Next For Each . In . . . . Next While . . . . Wend Do . . . . Loop Goto
Estructuras de Bucle
Instruccin de Salto
Antes de seguir adelante, adoptar el sistema habitual para las expresiones de la sintaxis de una sentencia. Las partes de cdigo situadas entre corchetes [] son opcionales. De las partes contenidas entre Llaves {} hay que seleccionar una de ellas.
Estructuras de Decisin.
La Instruccin If
Permite ejecutar un grupo de instrucciones de cdigo, en funcin de que el valor de una expresin sea Cierta o Falsa True / False. La forma ms bsica de esta instruccin es: If condicin Then [instrucciones] Condicin debe ser una expresin, numrica, relacional lgica que devuelva True False. Por ejemplo:
Eduardo Olaz
Entrega 09
Estructuras de Control
09 - 3
Esta forma de la instruccin If slo se puede poner en una nica lnea de cdigo, aunque admite mltiples instrucciones separadas por los dos puntos ":".
If condicin Then [instrucciones para el caso de que condicin sea True] Else [instrucciones para el caso de que condicin sea False] End If
Ejemplo:
Public Sub PruebaIf01() Dim Dividendo As Single Dim Divisor As Single Dim Cociente As Single Dividendo = 4 Divisor = 2 If Divisor <> 0 Then Cociente = Dividendo / Divisor Debug.Print Cociente Else MsgBox "No puedo dividir entre cero", _ vbOKOnly + vbCritical, _ "Divisin por cero" End If End Sub
Comencemos a programar con VBA - Access
09 - 4
En este caso, como Divisor <> 0 devuelve False, se ejecutar la lnea que aparece entre Else y End If, con lo que mostrar el mensaje de error. Estas sentencias admiten an un modo adicional, y es usar Else If. Es una nueva evaluacin tras una anterior que da como resultado falso. Supongamos que queremos hacer una funcin que devolviera el Nombre de provincia en funcin de un cdigo. Acepto por adelantado que habra otras formas ms adecuadas, pero es slo un ejemplo. Ejemplo:
Public Function Provincia(ByVal Codigo As Long) As String If Codigo < 1 Or Codigo > 52 Then Provincia = "Cdigo de provincia incorrecto" ElseIf Codigo = 1 Then Provincia = "lava" ElseIf Codigo = 8 Then Provincia = "Barcelona" ElseIf Codigo = 20 Then Provincia = "Guipuzcoa" ElseIf Codigo = 28 Then Provincia = "Madrid" ElseIf Codigo = 31 Then Provincia = "Navarra" ElseIf Codigo = 31 Then Provincia = "Navarra" ElseIf Codigo = 26 Then Provincia = "La Rioja" ElseIf Codigo = 48 Then Provincia = "Vizcaya" ElseIf Codigo = 50 Then Provincia = "Zaragoza" Else Provincia = "Otra Provincia" End If End Function
Con este cdigo Provincia(31) devolvera Navarra Las instrucciones If se pueden anidar, (poner unas dentro de otras).
Eduardo Olaz
Entrega 09
Estructuras de Control
09 - 5
Public Function EsPar(ByVal Numero As Long) As Boolean EsPar = IIf(Numero Mod 2 = 0, True, False) End Function
La funcin IIF, en este caso, sera igual a hacer
Public Function EsPar2(ByVal Numero As Long) As Boolean If Numero Mod 2 = 0 Then EsPar2 = True Else EsPar2 = False End If End Function
Nota: El operador Mod devuelve el resto de dividir Numero entre 2.
Select Case expresin_prueba [Case lista_expresion-1 [instrucciones-1]] ... [Case lista_expresion-2 [instrucciones-2]] ... - - - - - - - [Case lista_expresion-n [instrucciones-n]] ...
Comencemos a programar con VBA - Access
09 - 6
[Case Else [instrucciones_else]] End Select expresin_prueba debe ser una variable, o expresin que devuelva una cadena un
nmero.
Voy a poner un ejemplo para clarificarlo: Supongamos que queremos crear una funcin que nos cualifique el tipo de los pagars de los clientes en funcin del tiempo que queda hasta su cobro. La funcin recibir como parmetro la fecha del vencimiento. Si la fecha es anterior al da de hoy, deber devolver la cadena Pago vencido. Si es del da de hoy Vence hoy, si quedan entre 1 y 3 das Cobro inmediato, si menos de 31 das Corto Plazo si son menos de 181 das Medio Plazo y si es mayor Largo Plazo La funcin podra ser sta:
Public Function TipoVencimiento( _ Vencimiento As Date _ ) As String Dim lngDias As Long ' Date devuelve la fecha de hoy lngDias = Vencimiento - Date Select Case lngDias Case Is < 0 ' Si lngDias es menor que cero TipoVencimiento = "Pago vencido" Case 0 ' Si es cero TipoVencimiento = "Vence hoy" Case 1, 2, 3 ' De 1 a 3 TipoVencimiento = "Cobro inmediato"
eduardo@olaz.net
Eduardo Olaz
Entrega 09
Estructuras de Control
09 - 7
Case 4 To 30 ' De 4 a 30 TipoVencimiento = "Corto Plazo" Case 31 To 180 ' De 31 a 180 TipoVencimiento = "Medio Plazo" Case Else ' Si ninguno de los anteriores TipoVencimiento = "Largo Plazo" End Select End Function
Aqu mostramos algunas de las posibilidades de elaboracin de la lista_expresion.
Igual a Menor que Menor igual que Mayor que Mayor igual que Diferente que
Se pueden poner diferentes expresiones separadas por comas Esta lnea sera vlida:
Public Function Grupo( _ ByVal Apellido As String _ ) As Long Apellido = Trim(UCase(Apellido)) Select Case Apellido Case Is < "E" Grupo = 1 Case "E" To "LZZZZ" Grupo = 2 Case "M" To "PZZZZ"
Comencemos a programar con VBA - Access
09 - 8
Grupo = 3 Case "Q" To "TZZZZ" Grupo = 3 Case Is >= "U" Grupo = 4 End Select End Function
Nota: Hemos utilizado, como auxiliares dos funciones de VBA. En concreto en la lnea
Apellido = Trim(UCase(Apellido))
Primero se aplica la funcin Ucase sobre el parmetro Apellido y despus la funcin Trim.
Ucase convierte las minsculas que pueda haber en Apellido a Maysculas Trim elimina los posibles espacios en blanco que pudiera haber a la izquierda y a la derecha de Apellido.
En concreto, si Apellido contuviera el valor " La funcin Grupo("
Olaz
Olaz
Al ser "OLAZ" mayor que "M" y menor que "PZZZZ" ejecutara la lnea
Grupo = 3
Nota: Para que dos cadenas sean iguales, deben tener los mismos caracteres. Una cadena A es menor que otra B si aplicando los criterios de ordenacin, A estara antes que B. En este caso podemos decir que B es mayor que A porque si estuvieran en una lista ordenada alfabticamente, B estara despus que A. El definir si "OLAZ" es menor que "Olaz" es igual, se especifica en la primera lnea que aparece en el mdulo.
ValorNumericoInferior To ValorNumericoSuperior
En la funcin TipoVencimiento tenamos la siguiente lnea
Case 1, 2, 3 ' De 1 a 3
Esto haca que si la diferencia de das fuese de 1, 2 3 se ejecutara el cdigo de ese Case.
eduardo@olaz.net
Eduardo Olaz
Entrega 09
Estructuras de Control
09 - 9
Esta forma de generar una lista de comparaciones tambin se puede realizar con caracteres de texto. Sera vlido, por ejemplo Case "A","B","C"
Estructuras de Bucle.
Las Instrucciones For - - - Next
Supongamos que tenemos que construir una funcin que nos devuelva el Factorial de un nmero. Os recuerdo que Factorial de n es igual a 1*2*3* . . . *(n-1)*n, para n entero y mayor que cero. Adicionalmente se define que Factorial de Cero tiene el valor 1. Cmo se hara esta funcin:
Public Function Factorial(ByVal n As Integer) As Long Dim i As Integer Factorial = 1 For i = 1 To n Factorial = Factorial * i Next i End Function
Efectivamente funciona, ya que Factorial devuelve resultados correctos para valores de n entre 0 y 12. Pero esta funcin no sera operativa para un uso profesional ya que tiene una serie de fallos. Por ejemplo, si hacemos Factorial(-4) devuelve el valor 1, lo que no es correcto, ya que no existe el factorial de un nmero negativo. Igualmente podemos pasarle valores superiores a 12, que nos daran un error de desbordamiento, ya que 13! Supera el alcance de los nmeros Long. Probad haciendo en la ventana inmediato
? Factorial(13).
Observad este cdigo:
Public Function Factorial(ByVal n As Integer) As Long Dim i As Integer Select Case n Case Is < 0 MsgBox "No existe el factorial de un nmero negativo", _ vbCritical + vbOKOnly, _ " Error en la funcin Factorial" Exit Function Case 0 Factorial = 1 Exit Function Case 1 To 12 Factorial = 1 For i = 1 To n Factorial = Factorial * i
Comencemos a programar con VBA - Access
09 - 10
Next i Case Else MsgBox "Nmero demasiado grande", _ vbCritical + vbOKOnly, _ " Error en la funcin Factorial" Exit Function End Select End Function
He puesto una serie de sentencias Case para filtrar los valores que daran resultados incorrectos, o produciran error, avisndole al usuario de que ha tratado de utilizar la funcin Factorial con unos valores fuera de su rango vlido. As el mayor valor lo obtenemos de
12! = 479.001.600
El tener como rango vlido del 0 a 12 no resulta un poco corto ?. Dependiendo para qu, s. Supongamos que estemos programando un sistema estadstico que hace uso de clculos combinatorios grandes. Probablemente esta funcin no servira, aunque se podran usar trucos para saltarse sus limitaciones. El problema surge porque el resultado supera el rango de los nmeros Long, pero en el Captulo 5, y tambin en el Apndice 01, vemos que existen dos tipos de nmeros que superan esa limitacin. Uno es el de los Currency y el otro el de los Decimal. Vamos a cambiar el cdigo para trabajar con Currency:
Public Function FactorialCurrency(ByVal n As Integer) As Currency Dim i As Integer Select Case n Case Is < 0 MsgBox "No existe el Factorial de un nmero negativo", _ vbCritical + vbOKOnly, _ " Error en la funcin FactorialCurrency" Exit Function Case 0 FactorialCurrency = 1 Exit Function Case 1 To 17 FactorialCurrency = 1 For i = 1 To n FactorialCurrency = FactorialCurrency * i Next i Case Else MsgBox "Nmero demasiado grande", _ vbCritical + vbOKOnly, _
eduardo@olaz.net
Eduardo Olaz
Entrega 09
Estructuras de Control
09 - 11
" Error en la funcin FactorialCurrency" Exit Function End Select End Function
Ahora nos permite trabajar desde 0 a 17.
17! = 355.687.428.096.000
Esta ya es una cifra importante. Pero supongamos que nos contrata el Fondo Monetario Internacional, para hacer unos estudios estadsticos a nivel mundial. Es posible la capacidad de calcular hasta el factorial de 17, se nos quede corta. Para ello echamos mano de un tipo numrico de rango an ms alto. El tipo Decimal. El tipo Decimal tiene la particularidad de que VBA no puede trabajar directamente con l. La variable que lo vaya a contener debe ser primero declarada como Variant, y luego convertida a Decimal.
Public Function FactorialDecimal( _ ByVal n As Integer _ ) As Variant Dim i As Integer Dim Resultado As Variant ' Aqu hacemos la conversin de Variant a Decimal Resultado = CDec(Resultado) Select Case n Case Is < 0 MsgBox "No existe el factorial de un nmero negativo", _ vbCritical + vbOKOnly, _ " Error en la funcin Resultado" Exit Function Case 0 FactorialDecimal = 1 Exit Function Case 1 To 27 Resultado = 1 For i = 1 To n Resultado = Resultado * CDec(i) Next i Case Else MsgBox "Nmero demasiado grande", _ vbCritical + vbOKOnly, _ " Error en la funcin FactorialDecimal" Exit Function End Select
Comencemos a programar con VBA - Access
09 - 12
27! = 10888869450418352160768000000
Es decir con 29 cifras exactas. Una puntualizacin respecto al tipo Decimal. El tipo decimal no es el que permite un rango mayor de valores; es el que permite un rango de valores con ms cifras exactas. Los bucles del tipo For - - Next se pueden anidar (Poner unos dentro de otros). Por ejemplo, supongamos que tenemos que generar un procedimiento que nos imprima las tablas de multiplicar que van del 1 al 10.
Public Sub TablasDeMultiplicar() Dim n As Integer, m As Integer For n = 1 To 10 Debug.Print "-----------" For m = 1 To 10 Debug.Print n & " x " & m & " = " & n * m Next m Next n End Sub
Para cada valor que tomara n, ejecutara el bucle completo de For m --- Next m. imprimiendo en la ventana Inmediato los resultados de las tablas
- - - - 9 x 8 = 72 9 x 9 = 81 9 x 10 = 90 ----------10 x 1 = 10 10 x 2 = 20 10 x 3 = 30 10 x 4 = 40 10 x 5 = 50 10 x 6 = 60 10 x 7 = 70 10 x 8 = 80 10 x 9 = 90 10 x 10 = 100
En los ejemplos anteriores hemos utilizado la estructura ms tpica de VBA para la creacin de Bucles. La instruccin For m --- Next m., repite el cdigo contenido entre la lnea que contiene la palabra For y la lnea que contiene a su correspondiente Next.
eduardo@olaz.net
Eduardo Olaz
Entrega 09
Estructuras de Control
09 - 13
Su sintaxis es
For contador = principio To fin [Step incremento] [instrucciones] [Exit For] [instrucciones] Next [contador] Contador es una variable numrica que ir tomando sucesivos valores, con incrementos decrementos iguales al valor de incremento.
Si no se pusiera el valor incremento, contador ira creciendo en una unidad cada vuelta. El cdigo se ir repitiendo hasta que contador tome el valor de fin, se encuentre con la instruccin Exit For. En el siguiente ejemplo, el bucle For Next se ejecutar hasta que lngSuma sea mayor que 100, momento en que saldr del bucle o se imprima el nmero de impares especificado en el parmetro Numero. Si el parmetro Numero fuese cero menor, se sale directamente del procedimiento sin ejecutarse el bucle.
Public Sub ImprimeImpares(Numero As Long) Dim i As Long Dim lngImpar As Long Dim lngSuma As Long If Numero < 1 Then Exit Sub End If For i = 1 To Numero lngImpar = 2 * i - 1 lngSuma = lngSuma + lngImpar If lngSuma > 100 Then Exit For End If Debug.Print i & " - " & lngImpar & " - " & lngSuma Next i End Sub
La llamada al procedimiento se hara, por ejemplo para 4 impares
ImprimeImpares 4
09 - 14
Despus de la palabra Next, no es imprescindible escribir el nombre de la variable que sirve como contador. Por ejemplo este bucle es vlido a pesar de no escribir Next i:
For Each elemento In grupo [instrucciones] [Exit For] [instrucciones] Next [elemento]
Como en el caso de For - - - Next, es posible salir del bucle utilizando la instruccin Exit For. En las entregas anteriores, hemos puesto ejemplos de uso con Colecciones. El siguiente ejemplo extrae elementos de una Matriz.
Public Sub PruebaForEachConMatrices() Dim Datos() As String Dim Dato As Variant ' Llamamos al procedimiento _ Que rellena la matriz con datos RellenaMatriz Datos ' Leemos los elementos de la matriz For Each Dato In Datos Debug.Print Dato Next Dato End Sub Public Sub RellenaMatriz(ByRef Matriz As Variant) Dim i As Long ReDim Matriz(1 To 20) For i = 1 To 20 Matriz(i) = "Dato " & Format(i, "00") Next i End Sub
eduardo@olaz.net
Eduardo Olaz
Entrega 09
Estructuras de Control
09 - 15
Dato Dato Dato Dato Dato Dato Dato Dato Dato Dato Dato Dato Dato Dato Dato Dato Dato Dato Dato Dato
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20
VBA - Access
Entrega
10
Estructuras de Control II
Eduardo Olaz
10 - 2
While condicin [intrucciones] Wend Condicin es una expresin numrica o de tipo texto, que puede devolver True, False Null. Si devolviera Null, While lo considerara como False.
Las instrucciones de cdigo se ejecutarn mientras condicin de cmo resultado True. Supongamos que queremos crear un procedimiento que nos muestre los sucesivos valores que va tomando una variable, mientras esta variable sea menor que 100. Los valores que ir tomando la variable sern cada vez el doble que la anterior. Podramos realizarlo de esta forma
Public Sub PruebaWhile() Dim lngControl As Long lngControl = 1 While lngControl < 100 Debug.Print lngControl lngControl = lngControl * 2 Wend End Sub
eduardo@olaz.net
Eduardo Olaz
Entrega 10
Estructuras de Control II
10 - 3
1 2 4 8 16 32 64
Tras efectuar el 7 ciclo, la variable lngControl tomar el valor 128, por lo que la expresin lngControl < 100 devolver False. Esto har que el cdigo pase a la lnea siguiente a Wend, con lo que el procedimiento de prueba finalizar. Una utilizacin tradicional para While - - Wend ha sido la lectura de ficheros secuenciales de texto, utilizando la funcin Eof, ficheros de los que de entrada no se conoce el nmero de lneas,. Esta funcin, mientras no se llega al final del fichero devuelve el valor False. Cuando llega al final devuelve el valor True. Por ello el valor Not Eof, mientras no se haya llegado al final del fichero, devolver lo contrario, es decir True. Veamos el siguiente cdigo:
Public Sub MuestraFichero( _ ByVal Fichero As String) Dim intFichero As Integer Dim strLinea As String intFichero = FreeFile Open Fichero For Input As #intFichero While Not EOF(intFichero) Line Input #intFichero, strLinea Debug.Print strLinea Wend End Sub
ste es el clsico cdigo para leer el contenido de un fichero secuencial. Vamos a fijarnos en la estructura While - - Wend. Traducido a lenguaje humano quiere decir: Mientras no llegues al final del fichero #intFichero Lee la lnea del fichero, hasta que encuentres un retorno de carro y asgnaselo a la variable strLinea. Imprime el contenido de la variable en la ventana inmediato Vuelve a la lnea de While para repetir el proceso
Comencemos a programar con VBA - Access
10 - 4
While - - Wend no posee ninguna expresin que permita salir desde dentro del bucle
en un momento dado, sin antes haberlo completado. VBA posee una instruccin ms potente, es la instruccin Do - - - Loop. Su sintaxis posee dos formas distintas de utilizacin
Necesitis saber que en VBA, el operador que devuelve el resto de una divisin es Mod.
eduardo@olaz.net
Eduardo Olaz
Entrega 10
Estructuras de Control II
10 - 5
15 Mod 3 2
Ya s que este cdigo es manifiestamente mejorable, pero funciona y me viene bien para el ejemplo con Do Loop. Funciona si el nmero que probamos es menor igual que 2.147.483.647 Este es el mximo nmero Long positivo. Este nmero tambin es primo.
Public Function EsPrimo( _ ByVal Numero As Long _ ) As Boolean Dim lngValor As Long Dim dblRaiz As Double Select Case Numero Case Is < 1 MsgBox (Numero & " est fuera de rango") EsPrimo = False Exit Function Case 1, 2 EsPrimo = True Exit Function Case Else dblRaiz = Numero ^ 0.5 lngValor = 2 ' Comprobamos si Numero es divisible por lngValor If Numero Mod lngValor = 0 Then EsPrimo = False Exit Function End If lngValor = 3 EsPrimo = True Do While lngValor <= dblRaiz If Numero Mod lngValor = 0 Then EsPrimo = False Exit Function End If lngValor = lngValor + 2 Loop End Select End Function
Nota: En este cdigo he usado para calcular la raz cuadrada de un nmero, elevar ste a 0,5.
Comencemos a programar con VBA - Access
10 - 6
En VBA hay una funcin que calcula la raz cuadrada directamente: Sqr(Nmero). Es equivalente a Nmero^0.5 Habiendo escrito la funcin EsPrimo, en un mdulo estndar, vamos a crear un formulario en el que introduciendo un nmero en un cuadro de texto, tras pulsar un botn, nos diga si es primo no. Cerramos el editor de cdigo y creamos un nuevo formulario y lo ponemos en Vista Diseo. Aadimos al formulario una etiqueta, un cuadro de texto y un botn. Nombres aplicados a los controles: Etiqueta Cuadro de texto Etiqueta del cuadro de texto Botn lblMensaje txtNumero lblNumero cmdPrimo
Ajustamos algunas de las propiedades del formulario, por ejemplo para quitar los separadores de registro, botones, etc. Ya que va a ser un formulario con muy pocos controles, ponemos los textos algo mayores que lo normal, e incluso podemos jugar con los colores. A m me ha quedado as
Abrimos la ventana de propiedades y teniendo seleccionado el formulario, vamos a la pgina de Eventos. Hacemos que al abrir el formulario ponga como ttulo del mismo "Test de nmeros primos", y como texto de la etiqueta lblMensaje, "Introduzca un nmero entero".
Private Sub Form_Open(Cancel As Integer) Caption = "Test de nmeros primos" lblmensaje.Caption = _ "Introduzca un nmero mayor que cero" End Sub
eduardo@olaz.net
Eduardo Olaz
Entrega 10
Estructuras de Control II
10 - 7
Para que el formulario tenga este aspecto, he modificado algunas de sus propiedades: Propiedad Selectores de registro Valor No
Vamos a hacer ahora que tras introducir un nmero en el cuadro de texto, y presionar el botn, nos diga en la etiqueta si el nmero es primo. Volvemos a la hoja de propiedades y seleccionamos Eventos. Teniendo seleccionado el botn, activamos el evento Al hacer clic, pulsamos en el botoncito que aparece con los tres puntos y seleccionamos Generador de cdigo, y a continuacin Aceptar. Vamos a escribir el cdigo: Os recuerdo que detrs de la comilla simple lo que se escriba es un comentario (lneas en verde). Estas lneas VBA las ignora, sirviendo slo como ayuda al usuario. Tambin os recuerdo que el espacio en blanco seguido de la barra inferior, al final de una lnea, hace que la lnea siguiente se considere como la misma lnea. El dividir as las lneas lo hago como ayuda para la composicin de este texto y para ordenar el cdigo.
Private Sub cmdPrimo_Click() Dim strNumero As String Dim lngNumero As Long ' Pasamos a la variable el contenido _ de txtNumero, sin blancos en las esquinas ' Nz(txtNumero, "") devuelve una cadena vaca _ si txtNumero contuviera Null ' Trim (Cadena) quita los "Espacios en blanco" _ de las esquinas de la Cadena strNumero = Trim(Nz(txtNumero, ""))
Comencemos a programar con VBA - Access
10 - 8
' IsNumeric(strNumero) devuelve True _ si strNumero representa a un nmero If IsNumeric(strNumero) Then ' La funcin EsPrimo() _ funciona con nmeros long positivos _ entre 1 y 2147483647 If Val(strNumero) > 2147483647# _ Or Val(strNumero) < 1 Then lblmensaje.Caption = _ "El nmero est fuera de rango" txtNumero.SetFocus Exit Sub End If lngNumero = Val(strNumero) ' Format(lngNumero, "#,##0") _ devuelve una cadena con separadores de miles strNumero = Format(lngNumero, "#,##0") If EsPrimo(lngNumero) Then lblmensaje.Caption = _ "El nmero " _ & strNumero _ & " es primo" Else lblmensaje.Caption = _ "El nmero " _ & strNumero _ & " no es primo" End If Else lblmensaje.Caption = _ "No ha introducido un nmero" End If ' El mtodo SetFocus _ hace que el control txtNumero tome el foco txtNumero.SetFocus End Sub
Tras presionar el botn cmdPrimo se produce el evento clic, por lo que se ejecuta el procedimiento cmdPrimo_Click()que maneja ese evento Este procedimiento lo primero que hace es declarar dos variables, strNumero de tipo string y lngNumero de tipo Long.
eduardo@olaz.net
Eduardo Olaz
Entrega 10
Estructuras de Control II
10 - 9
A continuacin asigna el contenido del cuadro de texto txtNumero, procesado primero con la funcin Nz, que devuelve una cadena vaca si tiene el valor Null, y a continuacin le quita los posibles espacios en blanco de los extremos mediante la funcin Trim. Seguidamente pasa por la primera estructura de decisin If, controlando si la cadena strNumero es de tipo numrico. Si no lo fuera muestra en la etiqueta el mensaje "No ha introducido un nmero". Si lo fuera, primero comprueba si la expresin numrica de strNumero est entre 1 y 214748364, rango de valores vlidos en el rango de los Long, para la funcin EsPrimo. Si no fuera as, muestra el mensaje " El nmero est fuera de rango", lleva el cursor al control txtNumero y sale del procedimiento. Supongamos que el contenido de strNumero ha logrado pasar todos estos controles. Mediante la funcin Val(strNumero) asigna el valor a la variable lngNumero. Como ya no vamos a utilizar la cadena strNumero para ms clculos, para mostrar el nmero, le asignamos el resultado de la funcin Format(lngNumero, "#,##0"). Con esta utilizacin, la funcin Format devuelve una cadena formada por el nmero con los separadores de miles. La funcin Format tiene un amplio abanico de posibilidades en la conversin de nmeros y fechas a cadenas de texto. Merece por s misma un tratamiento ms extenso. Se lo daremos en una prxima entrega. El siguiente paso es comprobar si el nmero lngNumero es primo, utilizando la funcin EsPrimo que escribimos anteriormente. Si lo fuera, escribiramos en la etiqueta "El nmero " seguido del contenido de la cadena strNumero, y el texto " es primo". Si no lo fuera, escribiramos lo mismo, pero indicando " no es primo". Terminado todo esto llevamos el cursor al cuadro de texto txtNumero mediante su mtodo SetFocus. Todo muy bien. El cliente est contento y el programa responde a lo que nos peda, pero Casi siempre hay un pero Viendo lo efectivos y rpidos que hemos sido, al cliente se le ocurre que sera muy interesante poner dos botoncitos que al presionarlos, dado un nmero cualquiera, nos muestre el nmero primo inmediatamente mayor menor al nmero que hemos mostrado. -Tiene que ser fcil, total ya has hecho lo ms importante y ste es un pequeo detalle adicional, que no te costar prcticamente nada de tiempo y supongo que no tendrs problemas para hacrmelo sin aumentar el importe presupuestado A alguno le suena esta conversacin?. Y adems, aunque ya has terminado lo que te pedan, como hay que aadirle este pequeo detalle no te pagan hasta que no lo termines Decido aadir dos botones con unas flechas en su interior. Al primero, con una flecha hacia arriba lo llamo cmdPrimoSiguiente, y al segundo, con una flecha hacia abajo, cmdPrimoAnterior. Este es el diseo que le doy al formulario:
Comencemos a programar con VBA - Access
10 - 10
Private Sub cmdPrimoSiguiente_Click() ' La siguiente lnea hace que se ignoren _ los posibles errores en la ejecucin. On Error Resume Next Dim strNumero As String Dim lngNumero As Long Dim blnPrimo As Boolean strNumero = Trim(Nz(txtNumero, "")) If IsNumeric(strNumero) Then lngNumero = Val(strNumero) ' Si lngNumero est entre 0 y 2147483646 If lngNumero < 2147483647# And lngNumero >= 0 Then ' Mientras blnPrimo no sea Cierto _ Es decir Mientras lngNumero no sea primo. Do While Not blnPrimo lngNumero = lngNumero + 1 blnPrimo = EsPrimo(lngNumero) Loop txtNumero = CStr(lngNumero) cmdPrimo_Click Else txtNumero = "1" cmdPrimo_Click End If Else txtNumero = "1" cmdPrimo_Click End If
eduardo@olaz.net
Eduardo Olaz
Entrega 10
Estructuras de Control II
10 - 11
End Sub
En el cdigo anterior podemos ver algunas cosas interesantes. Lo primero que nos puede llamar la atencin es la sentencia:
Do While Not blnPrimo Not hace que la expresin lgica que le sigue cambie su valor.
As si blnPrimo contiene el valor True
Not blnPrimo
devolver el valor False. La expresin equivale a:
10 - 12
Private Sub cmdPrimoAnterior_Click() ' Ignorar el error On Error Resume Next Dim strNumero As String Dim lngNumero As Long strNumero = Trim(Nz(txtNumero, "")) If IsNumeric(strNumero) Then lngNumero = Val(strNumero) If lngNumero < 2147483648# And lngNumero > 1 Then lngNumero = lngNumero - 1 Do Until EsPrimo(lngNumero) lngNumero = lngNumero - 1 Loop txtNumero = CStr(lngNumero) cmdPrimo_Click Else txtNumero = "2147483647" cmdPrimo_Click End If Else txtNumero = "2147483647" cmdPrimo_Click End If End Sub
En primer lugar utilizamos una estructura del tipo Do Until, en vez de Do While. Adems, como condicin no utiliza una variable como en el caso anterior, sino que lo compara directamente con el valor devuelto por la funcin EsPrimo, que devuelve True False segn sea el caso:
Do Until EsPrimo(lngNumero)
Con esto nos evitamos utilizar una variable y una sentencia adicional. Adems el cdigo resulta algo ms claro.. En este caso, si la variable no supera los filtros, pone el valor "2147483647" en el cuadro de texto.
eduardo@olaz.net
Eduardo Olaz
VBA - Access
Entrega
11
Gestin de errores
Eduardo Olaz
11 - 2
Errores
La gestin de los errores en procedimientos
A la hora de utilizar el cdigo de un mdulo, hay dos tiempos Tiempo de Diseo. Tiempo de Ejecucin.
El tiempo de diseo transcurre mientras estamos modificando el contenido del cdigo de un mdulo, sea del tipo que sea, o cuando estamos cambiando las propiedades de controles, formularios o informes, en la llamada Vista Diseo, ya sea directamente mediante ejecucin de cdigo. Esto a ms de uno le sorprender: Access permite crear mediante cdigo, formularios por ejemplo utilizando el mtodo CreateForm, que devuelve una referencia a un nuevo formulario, tambin permite aadirle controles mediante el mtodo CreateControl , e incluso asociarle un mdulo, escribiendo dinmicamente todo su contenido. Para esto ltimo tendramos que crear una referencia al objeto Module del formulario, y para insertarle el cdigo utilizar su mtodo InsertText. De forma semejante existe el mtodo CreateReport para la creacin dinmica de informes. Si queremos usarlos posteriormente deberemos guardarlos, por ejemplo con el mtodo Save del objeto DoCmd. El tiempo de ejecucin transcurre cuando hemos creado una instancia de un objeto, formulario, informe, clase o hemos llamado a un procedimiento de un mdulo estndar. En el lenguaje normal podramos decir que estamos en tiempo de ejecucin cuando estamos ejecutando los objetos o el cdigo de Access. Errores en Tiempo de Diseo En Tiempo de Diseo podemos cometer una serie de errores, a la hora de escribir el cdigo. Muchos de estos errores sern detectados inmediatamente por el editor de Access. Cuando escribimos una lnea de cdigo, Access realiza un anlisis del texto que estamos escribiendo. En este proceso se realiza fundamentalmente su anlisis sintctico. Tambin comprueba si hay sentencias incompletas, por ejemplo If sin Then. Si encuentra una expresin errnea lanza un mensaje de Error de compilacin e incluso aporta una cierta informacin que nos puede orientar sobre el origen del error.
eduardo@olaz.net
Eduardo Olaz
Entrega 11
11 - 3
La lnea de cdigo incorrecta queda marcada en color rojo. Cuando ejecutamos el cdigo, la primera vez que lo hace, no slo realiza un anlisis sintctico, adems va comprobando que todas las constantes y variables, ya sean de tipos estndar, referencias de objetos tipos definidos por el usuario, estn perfectamente declaradas, y los tipos de objeto existan y sean correctos.
Si se detecta algn error se interrumpe la ejecucin del cdigo y se lanza un aviso, marcando la zona del cdigo donde el error se ha producido. Esta depuracin del cdigo se va realizando conforme se efectan llamadas a los diferentes procedimientos. Podra ocurrir que tuviramos un procedimiento que slo se usara en determinadas condiciones y que contuviera por ejemplo una variable mal declarada. Si al ejecutar el cdigo no se llega a utilizar ese procedimiento, no se detectara el error que contiene. Para evitar sorpresas posteriores, es aconsejable realizar una pre-compilacin del cdigo. Para realizarla podemos utilizar la opcin de men [Compilar NombreDelFicheroAccess] de la opcin de men [Depuracin]. A esta opcin de men se puede llegar tambin mediante el botn [Compilar] .
Esta pre-compilacin revisa todo el cdigo, e incluso posibles procedimientos que no seran utilizados durante la ejecucin del programa. Esto nos da ms garanta sobre la calidad del cdigo y nos protege frente a ciertos tipos de error que de otra forma no podramos detectar. Errores en Tiempo de Ejecucin Hay una serie de errores que se pueden producir durante la ejecucin del cdigo, que no son de sintaxis ni originados por cdigo incompleto o declaraciones inadecuadas. Son, por ejemplo los errores provenientes de valores no previstos por el cdigo pasados ya sea por el propio usuario extrados de tablas, ficheros u otras fuentes de origen. Son los tpicos errores de Tiempo de Ejecucin. Supongamos que tenemos que dividir entre s dos cantidades; si el denominador vale cero, nos dar un error de divisin entre cero. Podra ocurrir que no hayamos previsto que un cuadro de texto contuviera el valor Null. Esto nos podra generar error al intentar asignar este valor a una cadena de texto. Tambin podra ocurrir que en una expresin por la que queremos asignar el resultado de una operacin a una variable, ese resultado superara el rango admisible por el tipo de la variable, con lo que tendramos un error de Desbordamiento. Un programa profesional debe adelantarse a todas estas posibilidades. Por ejemplo, si tenemos que dividir dos nmeros, se debera comprobar que el denominador no contuviese el valor Cero.
Comencemos a programar con VBA - Access
11 - 4
Si tuviramos que obtener, un elemento de una matriz, coleccin, podramos evitar un error de subndice fuera de intervalo. Hay muchas circunstancias en las que se pueden producir errores en tiempo de ejecucin. VBA nos provee de una herramienta para poder controlarlos una vez que se han producido. Supongamos que tenemos el siguiente procedimiento.
Inmediatamente se interrumpir la aplicacin. Si esto nos ocurriera a nosotros mientras estamos haciendo pruebas no tendra mayor importancia. Pero si le ocurriera a nuestro cliente, no creo que nos llamara para felicitarnos. Por lo tanto deberemos anticiparnos a cualquier error que se pueda originar durante la ejecucin de nuestros programas. Tan importante como la redaccin previa de los Casos de Uso que nos ayudarn a definir nuestro programa, es la redaccin de las pruebas a las que deberemos someter nuestro programa antes de entregar al cliente la nueva versin. Y no slo redactarlas, sino tambin ponerlas en prctica. Frente a la posibilidad de un error, hay dos principales caminos Realizar todo tipo de filtros en el cdigo para evitar que llegue a darse una situacin de error En caso de que el error se produzca, capturarlo y darle una respuesta civilizada. Si en un segmento de cdigo existe la ms pequea posibilidad de que se produzca un error, ste acabar producindose irremediablemente, y adems en el momento ms inoportuno y de peores consecuencias.
Para capturar un error, VBA utiliza la ms denostada, criticada y repudiada de las sentencias de cdigo. Es la sentencia Goto. Es la instruccin de salto que nos faltaba por ver en las dos entregas anteriores.
eduardo@olaz.net
Eduardo Olaz
Entrega 11
11 - 5
Instrucciones de salto.
La Instruccin Goto
Cuando el cdigo se encuentra con esta sentencia realiza un salto incondicional. Su sintaxis tiene esta estructura:
GoTo lnea
Lnea puede ser una Etiqueta de lnea un nmero de lnea. Observa este cdigo
Public Sub PruebaGoto() GoTo Etiqueta_01 Debug.Print "No he saltado a Etiqueta_01" Etiqueta_01: Debug.Print "*** Salto a la Etiqueta_01 ***" GoTo 10 Debug.Print "No he saltado a 10" 10 Debug.Print "*** Salto a la lnea 10 ***" End Sub
Si ejecutamos el cdigo nos imprime en la ventana Inmediato
GoTo Etiqueta_01
Salta a la etiqueta sin ejecutar la lnea intermedia.
GoTo 10
Salta a la lnea precedida por el nmero 10.
Gosub - - Return
Es otra instruccin de salto, de la cual slo voy a comentar que existe por compatibilidad con las antiguas versiones de Basic. Si alguien quiere ms informacin puede acudir a la ayuda de VBA. Personalmente desaconsejo completamente su uso, ya que en VBA existen alternativas ms eficientes y claras. La utilizacin de Goto y Gosub se desaconseja ya que pueden convertir el cdigo en una sucesin de saltos de canguro imposible de seguir de una forma coherente.
11 - 6
Capturar Errores
Ya hemos comentado que durante la ejecucin de una aplicacin pueden producirse diversos tipos de errores, como rangos de valores no vlidos, divisin por cero, manipulacin de un elemento de una matriz, coleccin, o un fichero que no existan, etc. Si prevemos que en un procedimiento pudiera producirse un error, para poder gestionarlo, pondremos en su cabecera o en un punto anterior al lugar donde el error se pudiera generar, la sentencia:
El objeto Err
Este objeto contiene la informacin de los errores que se producen en tiempo de ejecucin. Cuando se produce un error, el objeto o el procedimiento que lo ha generado puede asignar datos a sus propiedades. Las propiedades ms importantes, o las que en este nivel nos interesan ms, son: Number Description Source HelpContext HelpFile LastDLLError
La propiedad Number contiene un nmero que sirve como identificador del error.
Description incluye una cadena de texto que nos sirve para interpretar las
caractersticas del error.
Public Sub ErrorControlado() On Error GoTo HayError Dim n As Byte n = 0 Debug.Print 4 / n Salir: Exit Sub HayError: Debug.Print "Error n " & Err.Number Debug.Print Err.Description Debug.Print Err.Source Resume Salir End Sub
El resultado de la ejecucin de este cdigo es:
eduardo@olaz.net
Eduardo Olaz
Entrega 11
11 - 7
Resume Salir
har que se salga del procedimiento. Despus de la palabra Resume, puede ponerse una etiqueta un nmero de lnea. En ambos casos se producir una salto hasta la lnea especificada. Si se pusiera
Resume
Resume 0
Si el error se hubiera producido en el procedimiento que contiene el controlador de errores, la ejecucin contina en la instruccin que lo caus. Si el error se produjera en un procedimiento llamado, la ejecucin continuar en la instruccin para el control de errores desde la cual se llam al procedimiento que contiene la rutina de gestin de errores.. Si se hubiera escrito
Resume Next
Se ejecutar la lnea siguiente a aqulla en la que se produjo el error se llam al procedimiento para la gestin de errores.
11 - 8
En la parte de la izquierda nos muestra un ejemplo de los iconos que se podran poner en el botn.
A continuacin hay una lista de categoras de acciones. La lista de la derecha muestra las acciones concretas que tiene el asistente para cada categora. Como lo que queremos es que el botn Cierre el Formulario, en la lista de la izquierda seleccionaremos Operaciones con formularios. Automticamente nos mostrar las acciones disponibles con los formularios. Seleccionaremos en la lista de la derecha la opcin Cerrar formulario. Pulsamos en el botn [Siguiente]. Ahora nos aparecen nuevas opciones. En concreto nos permite seleccionar si queremos poner un texto una imagen en el botn. Si seleccionramos [Texto] podramos cambiar el texto que nos sugiere Access. Esta vez vamos a seleccionar una Imagen. Al seleccionar el botn [Imagen] se activan el botn [Examinar] y la Casilla de verificacin [Mostrar todas las imgenes]. El botn [Examinar] permite seleccionar cualquier imagen compatible que tengamos en el ordenador. La casilla [Mostrar todas las imgenes] nos muestra los nombres de todas las imgenes prediseadas por Access. No vamos a hacer caso de estas opciones y seleccionaremos sin ms la imagen Salir, que nos muestra una Puerta entreabierta sealada con una flecha. Presionamos el botn [Siguiente] y nos pedir un nombre para ese botn. Le vamos a poner como nombre cmdsalir. Veamos qu nos ha hecho el asistente. Seleccionamos el botn, [Cdigo] la opcin de men [Ver] - [Cdigo]. Vemos que nos ha colocado, en el mdulo de clase del formulario, el procedimiento
Eduardo Olaz
Entrega 11
11 - 9
Private Sub cmdSalir_Click() On Error GoTo Err_cmdSalir_Click DoCmd.Close Exit_cmdSalir_Click: Exit Sub Err_cmdSalir_Click: MsgBox Err.Description Resume Exit_cmdSalir_Click End Sub
Lo primero que hace es utilizar On Error GoTo Err_cmdSalir_Click ; con ello si se produjera algn error saltar a la lnea marcada con Err_cmdSalir_Click: Esta es la forma como los asistentes de Access suelen nombrar las etiquetas que sealan el comienzo del cdigo de Gestin de Errores Err_NombreDelProcedimiento: A continuacin nos encontramos con que se ejecuta el mtodo Close del objeto DoCmd. El objeto DoCmd es un objeto especial de Access. Contiene un gran nmero de mtodos para ejecutar muchas de las tareas habituales con los objetos de Access. A gran parte de estos mtodos se les puede pasar una serie de argumentos. Por sus amplias posibilidades, DoCmd merece un captulo aparte. Lo desarrollaremos en prximas entregas. Basta decir que en este caso, al llamar al mtodo Close sin pasarle ningn argumento, DoCmd cierra la ventana activa y como sta es el formulario del botn, al pulsarlo cierra el formulario, que es lo que queramos conseguir. Si no ha habido ningn problema, la ejecucin del cdigo se encuentra con la etiqueta que marca el punto a partir del cual se sale del procedimiento. Como aclaracin, una etiqueta no ejecuta ninguna accin, slo indica dnde comienza un segmento de cdigo. En nuestro caso, a partir de este punto nos encontramos con la lnea Exit Sub que nos hace salir del procedimiento cmdSalir_Click. Despus de esta ltima lnea comienza el segmento de cdigo que controla cualquier error en tiempo de ejecucin que se pudiera originar dentro del procedimiento. Primero, mediante un cuadro de mensaje, nos muestra el texto contenido en la propiedad Descripcin del objeto Err. A continuacin, mediante Resume Exit_cmdSalir_Click hace que salte el cdigo a la etiqueta que marca la salida del procedimiento. Una puntualizacin: Resume, adems de dar la orden de que el cdigo se siga ejecutando desde un determinado punto del procedimiento, pone a cero las propiedades del objeto Err, lo que equivale a hacer que desaparezca el error.
11 - 10
Como hemos visto, cuando usamos un asistente para controles de Access, en el cdigo generado se suele colocar un segmento para la gestin de errores. Este cdigo escrito por Access lo podemos cambiar; por ejemplo podramos sustituir Exit_cmdSalir_Click por Salir, Err_NombreDelProcedimiento por hayError, e incluso lo podramos quitar, si estuviramos seguros de que nunca se podra llegar a producir un error o ya tuviramos desarrollado nuestro propio sistema para la gestin de errores.
Gestionando errores
Supongamos que nos han encargado un programa para visualizar en un formulario el contenido, en formato texto, de los ficheros que seleccionemos. Para especificar el fichero que se va a visualizar nos piden que su nombre, incluida su ruta, se escriba en un cuadro de texto. Tras esto, y presionar un botn, su contenido se mostrar en un segundo cuadro de texto. Se decide seleccionar un cuadro de texto como soporte, con el fin de que se puedan seleccionar y copiar segmentos del texto del contenido de los ficheros. Debo aclarar que no todo el contenido de un fichero cualquiera se podr mostrar en un cuadro de texto. Si el fichero es de tipo Binario, grficos, ficheros exe multimedia, habr caracteres que no se muestren que tengan secuencias ininteligibles. Como ya habamos escrito previamente un procedimiento para mostrar el contenido de ficheros (MuestraFichero del captulo anterior) vamos a aprovecharlo, con algunas modificaciones para adaptarlo a los nuevos requerimientos. El nuevo procedimiento exigir que se le pase como parmetro un cuadro de texto; en este caso con nombre Pizarra, que ser donde se ir escribiendo el contenido del fichero mientras se vaya leyendo. Adems le aadiremos un control de errores, ya que podra ocurrir que no se pudiera abrir el fichero por no existir, o porque estuviera abierto en modo exclusivo por algn usuario.
Public Sub MuestraFichero( _ ByVal Fichero As String, _ ByRef Pizarra As TextBox) On Error GoTo ProducidoError Dim intFichero As Integer Dim strLinea As String intFichero = FreeFile Open Fichero For Input As #intFichero While Not EOF(intFichero) Line Input #intFichero, strLinea Pizarra.Value = Pizarra.Value & strLinea Wend Salir: Exit Sub ProducidoError: MsgBox "Se ha producido el error " & Err.Number, _ vbCritical + vbOKOnly, _ "Error en el procedimiento MuestraFichero()"
eduardo@olaz.net
Eduardo Olaz
Entrega 11
11 - 11
El cuadro de texto txtFichero servir para introducir el nombre completo del fichero a visualizar. En txtContenido mostraremos el contenido del fichero. El botn cmdVerFichero activar el procedimiento que sirve para mostrar el contenido del fichero. La llamada al procedimiento lo colocaremos en el evento Al hacer clic del botn cmdVerFichero. Usaremos el procedimiento MuestraFichero al que le pasaremos como parmetro el control txtContenido. En el cuadro de texto txtContenido activamos la Barra Vertical en la propiedad Barras de Desplazamiento. En el evento Al hacer clic del botn escribiremos lo siguiente:
Private Sub cmdVerFichero_Click() txtContenido = "" MuestraFichero txtFichero, txtContenido End Sub
Lo que hace el procedimiento es: Limpia el posible contenido de txtContenido asignndole una cadena vaca. Llama al procedimiento MuestraFichero Pasndole como parmetros el nombre del fichero que queremos abrir, contenido en el control txtFichero, y el control en el que queremos mostrar los datos txtContenido. En el ejemplo se muestra el contenido del fichero CodigosDeBarras.ini ubicado en la carpeta Windows.
11 - 12
Err.Raise NmeroError, OrigenError, Descripcin, FicheroAyuda, ContextoDeAyuda NmeroError es el nmero de error que queremos pasar al objeto Err. OrigenError Objeto, Aplicacin, Procedimiento que genera el error. Descripcin Texto que identifica al propio error. FicheroAyuda Fichero de ayuda con informacin sobre el error. ContextoAyuda Identificador de contexto que especifica el tema del
archivo indicado en FicheroAyuda, que contiene la informacin de ayuda del error. De los cinco parmetros, el nico obligatorio es el Nmero que identifica al error. A ttulo de informacin, adelanto que con Access se pueden utilizar ficheros de ayuda creados al efecto por nosotros. La creacin y utilizacin de ficheros de ayuda es uno de los temas considerados como avanzados. Los nmeros de error reservados para ser definidos por el usuario son los que estn entre el nmero 513 y el 65535.
Public Sub GenerarError() On Error GoTo HayError ' Aqu generamos el error Err.Raise 666, "GenerarError", _ "Houston! Tenemos un problema" Salir: Exit Sub HayError: Debug.Print "Error n " & Err.Number Debug.Print "Se ha producido el error: " _ & Err.Description Debug.Print "en el procedimiento: " _ ; Err.Source Resume Salir End Sub
Si ejecutamos el procedimiento GenerarError nos mostrar en la ventana Inmediato:
eduardo@olaz.net
Eduardo Olaz
VBA - Access
Entrega
12
Eduardo Olaz
12 - 2
Procedimientos
Hemos hablado sobre diversos aspectos de los procedimientos Function y Sub. Relacionados con ellos, he dejado en el tintero completar la explicacin de una serie de temas que vamos a tratar en esta entrega. Entre ellos cabe destacar Declaracin de variables como Static, a nivel de procedimiento Paso de parmetros Por Valor y Por Referencia, ByVal y ByRef Parmetros Opcionales Optional Resolucin de problemas mediante Procedimientos Recursivos e Iterativos. Parmetros Tipo Matriz mediante ParamArray. Paso de parmetros Con Nombre mediante el operador de asignacin := Constantes Enumeradas Enum ... End Enum Cuadros de dilogo predefinidos para intercambiar informacin entre VBA y el usuario MsgBox e InputBox
Variables Static
A nivel de procedimiento hemos comentado que una variable puede declararse con Dim. No puede declararse ni como Public ni como Private. Declarada con Dim, cuando acaba el procedimiento, el contenido de la variable desaparece, y la siguiente vez que se llama al procedimiento, tras su declaracin, la variable se reinicia con el valor por defecto correspondiente a su tipo. Como aclaracin de esto ltimo, por ejemplo, si tenemos el procedimiento:
Public Sub ValorConDim() Dim n As Long n = n + 1 Debug.Print "Valor de n: " & n End Sub
Cada vez que llamemos al procedimiento ValorConDim imprimir en la ventana Inmediato
Valor de n: 1
Vamos ahora a escribir un nuevo procedimiento muy parecido al anterior
Public Sub ValorConStatic() Static n As Long n = n + 1 Debug.Print "Valor de n: " & n End Sub
Slo cambia el nombre y la declaracin de n que est declarada como Static en vez de con Dim. Ahora la primera vez que se ejecuta muestra tambin Valor de n: 1
eduardo@olaz.net
Eduardo Olaz
Entrega 12
12 - 3
de n: 2 de n: 3 de n: 4 . .
Comprobamos que cuando una variable es declarada como Static, se mantiene el valor que va tomando despus de ejecutarse el procedimiento. Vamos a usar ahora el primer procedimiento, cambiando slo su cabecera
Public Static Sub ValorConStatic2() Dim n As Long n = n + 1 Debug.Print "Valor de n: " & n End Sub
Si ejecutamos ahora varias veces seguidas el procedimiento ValorConStatic2 veremos que se comporta igual que en ValorConStatic, a pesar de haber declarado la variable n con Dim. Esto ocurre porque al poner delante de Sub Function, la palabra Static, hace que las variables declaradas dentro del procedimiento, sean declaradas como Static, y por tanto mantengan su valor entre diferentes llamadas al mismo.
Public Sub DemoByValByRef() Dim lngN As Long, lngM As Long, lngO As Long lngM = 1 lngN = 1 lngO = 1 PorValor lngM PorReferencia lngN PorDefecto lngO Debug.Print Debug.Print "lngM = " & lngM Debug.Print "lngN = " & lngN Debug.Print "lngO = " & lngO End Sub Public Sub PorValor(ByVal VariableByVal As Long) VariableByVal = 2 * VariableByVal Debug.Print "VariableByVal = " & VariableByVal End Sub
Comencemos a programar con VBA - Access
12 - 4
Public Sub PorReferencia(ByRef VariableByRef As Long) VariableByRef = 2 * VariableByRef Debug.Print "VariableByRef = " & VariableByRef End Sub Public Sub PorDefecto(Variable As Long) Variable = 2 * Variable Debug.Print "Variable = " & Variable End Sub
Si ejecutamos el procedimiento DemoByValByRef, nos imprimir:
Public Sub ProbarByRef() Dim lng1 As Long, lng2 As Long, lng3 As Long Dim lngExponente As Long lng1 = 2 lng2 = 3
eduardo@olaz.net
Eduardo Olaz
Entrega 12
12 - 5
lng3 = 4 lngExponente = 4 ' Pasamos por referencia las tres variables TresPotencias lng1, lng2, lng3, lngExponente ' Las variables lng1, lng2 y lng3 _ han sido elevadas a la potencia 4 Debug.Print "Variable 1 " & lng1 Debug.Print "Variable 2 " & lng2 Debug.Print "Variable 3 " & lng3 End Sub Public Sub TresPotencias( _ ByRef Valor1 As ByRef Valor2 As ByRef Valor3 As ByVal Exponente Valor1 = Valor1 ^ Exponente Valor2 = Valor2 ^ Exponente Valor3 = Valor3 ^ Exponente End Sub
Al ejecutar el procedimiento ProbarByRef nos mostrar
12 - 6
Si en un procedimiento, tenemos varios parmetros opcionales, que no vamos a introducir y varios de ellos son los ltimos, y consecutivos, no ser necesario introducir ninguna coma que acte como separador. Si alguno de los parmetros que no vamos a introducir est entre dos que s introduciremos, el espacio ocupado por el/los parmetros no introducidos los pondremos como un espacio en blanco. Por ejemplo:
Public Sub VariantOpcional( _ Optional ByVal Valor As Variant) If IsMissing(Valor) Then Debug.Print "No se ha asignado Valor" Else Debug.Print "Se ha asignado un Valor" End If End Sub
Si llamamos a este procedimiento sin asignar ningn contenido al parmetro Valor, por ejemplo llamando directamente
VariantOpcional
nos imprimir
No se ha asignado Valor
En cambio si la llamamos
Se ha asignado un Valor
Supongamos ahora que tenemos que desarrollar una funcin que nos devuelva la edad en aos de una persona; funcin que luego queremos utilizar, por ejemplo en consultas. Para ello creamos un nuevo mdulo y en l escribimos lo siguiente.
Public Const Pi As Double = 3.14159265358979 Public Function Edad( _ ByVal Nacimiento As Date, _ Optional ByVal FechaEdad As Date = Pi) ' Esta no es una funcin absolutamente exacta, _ pero s razonablemente operativa.
Eduardo Olaz
eduardo@olaz.net
Entrega 12
12 - 7
' 365.25 es por los aos bisiestos Const DiasAo As Double = 365.25 ' Si no se introduce FechaEdad (FechaEdad = Pi) _ le asignamos Date (la fecha de Hoy). If FechaEdad = Pi Then FechaEdad = Date End If ' Fix quita la parte decimal y devuelve _ la parte entera de un nmero de coma flotante Edad = Fix((FechaEdad - Nacimiento) / DiasAo) End Function
Supongamos que hoy es el 14 de febrero de 2005 Llamar a la funcin Edad(#9/3/53#, #2/14/05#) sera lo mismo que llamarla con Edad(#9/3/53#); en ambos casos nos devolvera 51, Primero la variable FechaEdad tomara el valor Pi, por haber sido llamada la funcin edad por no haber pasado ningn valor al parmetro. A continuacin, tras comprobar que su valor es Pi, le asigna la fecha de hoy mediante la funcin de VBA Date(). Puede resultar sorprendente la asignacin del valor definido para Pi como valor por defecto del parmetro FechaEdad. sta es una decisin puramente personal, basada en la prctica imposibilidad de que esa sea la fecha/hora en la que se quiera averiguar la edad de una persona. Adems Pi es un nmero por el que tengo cierta debilidad. Por cierto, para el que sienta curiosidad, el valor usado como Pi est entre el segundo 53 y el segundo 54 de las 3 horas, 23 minutos del da 2 de enero del ao 1900. Resumiendo lo anterior, podemos definir en los procedimientos parmetros que sern opcionales, e incluso asignarles valores que tomarn si al llamar al procedimiento no se les asignara ningn valor.
n! = n * (n-1) * (n-2) * * 3 * 2 * 1
Se define el 1 como valor del factorial de cero 0! = 1. Una forma tradicional (iterativa) de resolver este problema sera el que mostramos en la entrega 09:
Comencemos a programar con VBA - Access
12 - 8
Public Function Factorial(ByVal n As Integer) As Long Dim i As Integer If n < 0 Then Exit Function Factorial = 1 For i = 1 To n Factorial = Factorial * i Next i End Function
En esta funcin no hay puesto un control de errores; de hecho para un valor de n superior a 12 el resultado supera el rango de los Long y dar error de desbordamiento. Obvio estos detalles de cara a la simplificacin del cdigo, aunque en un desarrollo profesional esto sera imperdonable Este cdigo es un claro caso de resolucin mediante un procedimiento Iterativo. Por si sirve para aclarar conceptos, en el diccionario se define la iteracin como el acto de repetir. Vemos que en este procedimiento se repite una operacin hasta obtener el valor que deseamos, utilizando para ello un bucle que va iterando repitiendo la operacin producto, hasta que la variable i obtiene el valor de n. La definicin del factorial de un nmero n se puede hacer de otra manera:
n! = n * (n-1)! y el valor de 0! es 1
Nos dice que el factorial de un nmero n es igual a ese nmero multiplicado por el factorial de n-1. Podemos realizar una funcin que acte tal cual est definido el factorial de n:
Public Function Factorial( _ ByVal n As Integer _ ) As Long Dim i As Integer If n < 0 Then Exit Function If n = 1 Then Factorial = 1 Exit Function End If Factorial = n * Factorial(n - 1) End Function
Vemos que la funcin factorial se va llamando a s misma:
Factorial = n * Factorial(n - 1)
Cada resultado parcial de Factorial lo va guardando en una posicin de memoria. Esto ocurre hasta que en alguna de las llamadas se encuentra con que el parmetro n-1 pasado a la funcin Factorial toma el valor 1. En general un mtodo recursivo se va llamando a s mismo hasta que encuentra una solucin elemental bsica que le permite construir el proceso inverso y salir del mismo.
eduardo@olaz.net
Eduardo Olaz
Entrega 12
12 - 9
compara el valor de n. A partir de ese momento va realizando de forma inversa las operaciones con los resultados parciales anteriores de factorial. Es decir calcula 1 * 2 * 3 * . . . * (n-1) * n.
En este caso el mtodo recursivo, para calcular el factorial de un nmero, necesita efectuar n llamadas a la funcin, almacenando los correspondientes resultados parciales en memoria. Podemos deducir en este caso, que el mtodo recursivo requiere ms recursos de memoria y procesador que el mtodo iterativo. Hay una definicin que afirma que cualquier mtodo recursivo se puede transformar en iterativo, y viceversa. Generalmente los mtodos recursivos requieren ms memoria y procesador que sus equivalentes iterativos. Entonces para qu usarlos? Hay problemas en los que una resolucin iterativa es muchsimo ms compleja que su correspondiente recursiva. Apunto aqu un problema clsico como es el de Las torres de Hanoi, un juego de origen oriental que consiste en tres barras verticales y un conjunto de discos de diferentes dimetros con un orifico central, apilados en una de ellas y que hay que ir pasando a otra de las barras. No voy a explicar la mecnica del juego ni la resolucin del mismo. Si alguno tiene inters en conocerlo, Google, MSN, Altavista o cualquiera de los buscadores os indicarn una inmensa cantidad de pginas con informacin sobre el juego, el problema matemtico que representa y sus algoritmos para resolverlo. La solucin Recursiva es casi elemental en cambio la iterativa es mucho ms compleja. Esto mismo es aplicable a ciertos algoritmos, por ejemplo de ordenacin o de trazado grfico de figuras englobadas en la geometra fractal.
Public Function Media( _ ParamArray Sumandos() As Variant _ ) As Double Dim dblSuma As Double Dim lngElementos As Long Dim n As Variant For Each n In Sumandos dblSuma = dblSuma + n Next n lngElementos = UBound(Sumandos) + 1
Comencemos a programar con VBA - Access
12 - 10
Ubound nos devuelve el valor ms alto del ndice de una matriz; como en este caso el ndice ms bajo de Sumandos es 0, el nmero de elementos de la matriz es el devuelto por UBound ms 1.
Si en un procedimiento vamos a utilizar ParamArray, la matriz correspondiente ser el ltimo parmetro del procedimiento.
eduardo@olaz.net
Eduardo Olaz
Entrega 12
12 - 11
Constantes Enumeradas
En algunos tipos de procedimientos o funciones, vemos que alguno de los parmetros slo debe tomar un nmero limitado de valores. Sera muy interesante que a la hora de ir escribiendo un procedimiento, VBA nos fuera sugiriendo los valores vlidos. Para ayudar a este fin tenemos las Constantes Enumeradas. Las Constantes Enumeradas son un conjunto de constantes agrupadas en un tipo enumerador. Los valores que pueden admitir son slo del tipo Long La forma de construirlas es
[Public | Private] Enum nombre nombre_miembro [= expresin_constante] nombre_miembro [= expresin_constante] . . . End Enum
Se declaran a nivel del encabezado de un mdulo, pudiendo ser declaradas como Public o Private. En los mdulos de clase slo pueden declararse como Public La declaracin comienza opcionalmente indicando primero su alcance a continuacin la instruccin Enum seguida del nombre del tipo. En las sucesivas lneas se van aadiendo los nombres de los sucesivos valores, opcionalmente con su valor correspondiente.
Public Enum DiasSemana dsLunes = 1 dsMartes dsMiercoles dsJueves dsViernes dsSabado dsDomingo
Comencemos a programar con VBA - Access
12 - 12
End Enum
En este caso, por ejemplo dsJueves toma el valor 4. He dicho que a las constantes enumeradas slo se les puede asignar valores Long. Esto no es del todo cierto. Esto por ejemplo funcionara
Public Enum TipoSexo tsFemenino = 1 tsMasculino = 2 End Enum Public Enum EstadoCivil ecSoltero ecCasado ecSeparado ecDivorciado ecViudo
eduardo@olaz.net
Eduardo Olaz
Entrega 12
12 - 13
End Enum
Como hemos comentado asignar el valor 2 a tsMasculino no sera necesario. Una vez definidas estas constantes, estn inmediatamente disponibles en la Ayuda Contextual, como podemos ver en la siguiente imagen, pudindose utilizar como un tipo de datos ms.
Adems, conforme vayamos desarrollando el cdigo nos mostrar los posibles valores concretos que podrn tomar los parmetros
Completemos la funcin:
Public Function Tratamiento( _ ByVal Apellido As String, _ Sexo As TipoSexo, _ Optional ByVal Estado As EstadoCivil _ ) As String
Comencemos a programar con VBA - Access
12 - 14
If Sexo = tsMasculino Then Tratamiento = "Estimado Sr. " & Apellido ElseIf Sexo = tsFemenino Then Select Case Estado Case ecSoltero, ecDivorciado Tratamiento = "Estimada Sta. " & Apellido Case Else Tratamiento = "Estimada Sra. " & Apellido End Select Else Tratamiento = "Estimado/a Sr./Sra. " & Apellido End If End Function
Con esta funcin si desde la ventana inmediato escribimos
? Tratamiento("Goikoa", tsFemenino,ecCasado)
Nos escribir
Si hacemos las pruebas con las diferentes posibilidades veremos que responde a lo solicitado por la empresa contratante. Visto el xito, nos piden que tambin creemos una funcin para la lnea de la direccin de forma que nos ponga:
A la Att. del Sr. Apellido1 A la Att. de la Sta. Apellido1 A la Att. de la Sra. Apellido1
El desarrollo de esta funcin la dejo para el lector.
eduardo@olaz.net
Eduardo Olaz
Entrega 12
12 - 15
Para intercambiar informacin entre el usuario y VBA tenemos 2 procedimientos bsicos que se corresponden a los cuadros de dilogo predefinidos en VBA.
MsgBox InputBox.
Funcin MsgBox
La funcin MsgBox, muestra un mensaje en un Cuadro de dilogo con determinados botones, y puede devolver un nmero que identifica el botn pulsado por el usuario. En este cuadro de dilogo se puede definir el texto de la barra de ttulo, el mensaje mostrado, un icono que indica el tipo de mensaje y seleccionar botones de los predefinidos previamente por VBA. La sintaxis es la siguiente
MsgBox mensaje
Por ejemplo
MsgBox admite los parmetros, argumentos Con Nombre. As el cdigo anterior podra
haberse escrito
12 - 16
Dim lngRespuesta As Long strMensaje = _ "Est seguro de querer formatear el disco C:?" _ & vbCrLf _ & "Se perdern todos los datos!" lngRespuesta = MsgBox(strMensaje, _ vbExclamation + vbOKCancel + vbDefaultButton2, _ "Ha solicitado formatear C:") Select Case lngRespuesta Case vbOK Formatear "C" Case vbCancel MsgBox Prompt:= _ "Ha interrumpido el Formateo del disco", _ Buttons:=vbInformation, _ Title:="Ha salvado sus datos" Case Else MsgBox lngRespuesta End Select End Sub Public Sub Formatear(ByVal Disco As String) MsgBox Prompt:="Formateando el disco " _ & Disco & ": ...", _ Title:="Sus datos estn siendo borrados" End Sub
Este cdigo mostrar el siguiente mensaje:
Eduardo Olaz
Constante
Valor 64 48 32 16
Icono
La segunda parte del parmetro Buttons hace referencia a los botones que va a mostrar el cuadro de mensaje. Sus posibilidades son Constante Valor 0 1 2 3 4 5 16384 [ Botones [ Aceptar ] [ Aceptar ] [ Cancelar ] [ Anular ] [ Reintentar ] [ Ignorar ] S [ ] [ S No ] [ ] [Cancelar] No ]
El tercer grupo de constantes es el que define el botn seleccionado como predeterminado. Ser el que se active si directamente presionamos la tecla [ Intro ] Constante Valor 0 256 512 768 Botn predeterminado Primer botn Segundo botn Tercer botn Cuarto botn
El cuarto grupo de constantes define el tipo de presentacin Modal del cuadro. La presentacin modal hace que tome prioridad el cuadro frente al resto de la aplicacin, visualizndose en primer plano. Constante Valor 0 4096 Tipo de visualizacin modal El cuadro es modal frente a la aplicacin El cuadro es modal frente al resto de las aplicaciones
VbApplicationModal VbSystemModal
Hay una constante adicional VbMsgBoxRight que sirve para hacer que el texto se alinee a la derecha del mensaje. Su valor es 524288.
Eduardo Olaz
12 - 18
Las dos ltimas constantes que permite usar MsgBox son VbMsgBoxSetForeground y VbMsgBoxRtlReading. La primera define la ventana del cuadro como de primer plano, y la segunda afecta a la visualizacin de los textos en hebreo y rabe que se escriben de Derecha a Izquierda. Usando las constantes: Supongamos que queremos mostrar un mensaje con los botones [ S ] [ No ] [ Cancelar ]
Como icono deber tener el de Exclamacin Adems queremos que su ttulo sea El mensaje que aparecer ser Proceso de Votacin Seleccione su voto
El botn preseleccionado deber ser el de [ Cancelar ]. Tras seleccionar una opcin nos mostrar un mensaje informndonos del voto realizado. Vamos a escribir el cdigo que responde a estos requisitos:
Public Sub Votacion() Dim lngBotonesIcono As Long Dim strTitulo As String Dim strMensaje As String Dim strVoto As String Dim lngRespuesta As Long lngBotonesIcono = vbExclamation _ + vbYesNoCancel _ + vbDefaultButton3 strTitulo = "Proceso de Votacin" strMensaje = "Seleccione su voto" lngRespuesta = MsgBox( _ strMensaje, _ lngBotonesIcono, _ strTitulo) ' Tras pasar el cuadro el botn seleccionado _ a la variable lngRespuesta _ mostramos el voto ' Para ello usamos la funcin TeclaPulsada strVoto = TeclaPulsada(lngRespuesta) If lngRespuesta = vbCancel Then strMensaje = "Has cancelado la votacin" Else strMensaje = "Has votado: " _ & TeclaPulsada(lngRespuesta) End If
eduardo@olaz.net
Eduardo Olaz
Entrega 12
12 - 19
Public Function NombreTecla( _ ByVal Tecla As Long _ ) As String Select Case Tecla Case vbOK NombreTecla = "Aceptar" Case vbCancel NombreTecla = "Cancelar" Case vbAbort NombreTecla = "Anular" Case vbRetry NombreTecla = "Reintentar" Case vbIgnore NombreTecla = "Ignorar" Case vbYes NombreTecla = "S" Case vbNo NombreTecla = "No" Case Else NombreTecla = "Desconocida" End Select End Function
Las constantes aqu manejadas no son un caso particular del cuadro de mensaje. Este tipo de constantes son muy habituales en VBA, y funcionan igual que las constantes enumeradas que hemos visto en esta misma entrega. En concreto las ltimas que hemos utilizado estn definidas internamente como de los tipos enumerados VbMsgBoxStyle y VbMsgBoxResult.
Funcin InputBox
La funcin InputBox, al igual que MsgBox, puede mostrar un mensaje, pero se diferencia en que en vez de enviar un nmero Long en funcin de la tecla pulsada, nos pide que escribamos un texto, en un cuadro de texto. Su contenido ser el dato que devuelva. La sintaxis de InputBox es:
12 - 20
Parmetro
Descripcin Mensaje del cuadro de dilogo (obligatorio) Texto a visualizar en la barra de ttulo Valor que devolver, si no introducimos ningn texto Coordenada X del cuadro (distancia desde el lado izquierdo) Coordenada Y del cuadro (distancia desde arriba) Fichero de ayuda auxiliar Contexto del fichero de ayuda
prompt
title default xpos ypos helpfile context
Public Sub Pregunta() Dim strRespuesta As String strRespuesta = InputBox("Cmo te llamas?") MsgBox "Tu nombre es " & strRespuesta, _ vbInformation, " Respuesta suministrada" End Sub
Si ejecutamos el procediendo nos muestra
InputBox.
Si hubiramos introducido en el InputBox el texto por defecto, ya fuera mediante
eduardo@olaz.net
Entrega 12
12 - 21
Si no borramos ese texto, por ejemplo al introducir algn texto, se ser el texto que devolver InputBox. Al ejecutar ese cdigo podemos comprobar que el cuadro de entrada nos aparece en la prctica centrado en la pantalla. InputBox tiene dos parmetros que le permiten ubicar el cuadro en una posicin concreta. Estos parmetros son Xpos e Ypos. He modificado el cdigo del procedimiento para que muestre un ttulo y ubicarlo cerca de la posicin superior izquierda
Public Sub Pregunta() Dim strRespuesta As String strRespuesta = InputBox( _ Prompt:="Cmo te llamas?", _ Default:="Introduce aqu tu nombre", _ Title:="Escriba su nombre?", _ XPos:=500, YPos:=500) MsgBox "Tu nombre es " & strRespuesta, _ vbInformation, " Respuesta suministrada" End Sub
Las medidas de posicin se dan en Twips. Un Twip, TWentIeth of a Point equivale a la Vigsima parte de un punto. El Twip equivale a 1/1440 de pulgada, es decir, 567 twips equivalen a un centmetro. Esto es vlido para la teora. En la vida real, las pruebas con diferentes valores son las que nos indicarn qu valores sern los adecuados a cada caso. Nota: Los parmetros helpfile y context hacen referencia a ficheros de ayuda desarrollados al efecto para la aplicacin. En este nivel de estudio (bsico), no vamos a abordar el anlisis del desarrollo de ficheros de ayuda. Esto no es bice para indicar que, tanto con MsgBox como con InputBox, se puede hacer referencia a determinados contenidos de ficheros de ayuda desarrollados ex profeso para la aplicacin, y no slo al fichero en s, sino a una seccin concreta del mismo. Hay abundante documentacin disponible en Internet al respecto.
Comencemos a programar con VBA - Access
12 - 22
Slo quiero resear el magnfico trabajo desarrollado por mi amigo Guille en su pgina: http://www.elguille.info/vb/VB_HLP.HTM
En la prxima entrega examinan algunos de los operadores disponibles con VBA. Trataremos tambin funciones y procedimientos implementados en VBA. Entre ellas incluiremos Funciones para el tratamientos de cadenas Left Mid Right InStr ....
eduardo@olaz.net
Eduardo Olaz
VBA - Access
Entrega
13
Funciones de VBA
Eduardo Olaz
13 - 2
Funcin Choose
La funcin Choose, selecciona y devuelve un valor de entre una lista de argumentos. Sintaxis
Choose(ndice, opcin-1[, opcin-2, ... [, opcin-n]]) Choose busca el argumento situado en la posicin definida por el ndice.
Si ndice fuese menor que 1, mayor que el total de argumentos, devolvera el valor Null. Si ndice fuese un nmero no entero, lo redondeara al entero ms prximo. En este ejemplo hemos definido, como constantes enumeradas los posibles puestos de una hipottica empresa. Las constantes enumeradas contienen un valor long, por lo que vamos a desarrollar una funcin que nos devuelva, en base a su valor, la descripcin literal del cargo.
Public Enum Puesto eoEmpleado = 1 eoTecnico eoAdministrativo eoMandoIntermedio eoComercial eoDirectivo eoGerente End Enum Public Function PuestoDeTrabajo( _ ByVal Cargo As Puesto _ ) As String ' Comprobamos si Cargo est en un rango vlido If Cargo < eoEmpleado Or Cargo > eoGerente Then PuestoDeTrabajo = "" Else PuestoDeTrabajo = Choose(Cargo, _ "Empleado", _ "Tcnico", _ "Administrativo", _ "Mando Intermedio", _
eduardo@olaz.net
Eduardo Olaz
Entrega 13
Funciones de VBA
13 - 3
PuestoDeTrabajo(eoMandoIntermedio)
Nos devolver
Mando Intermedio
Los argumentos de la funcin Choose, en realidad son un ParamArray, como el que vimos en la entrega anterior.
Funcin Switch
La funcin Switch, evala una lista de expresiones y devuelve un valor, o una expresin asociada, a la primera expresin de la lista que sea cierta. Su sintaxis es
Public Function Capital(ByVal Pais As String) As String Dim varPais As Variant ' Uso un variant porque si Switch no encuentra _ una expresin cierta, devuelve Null Pais = Trim(Pais) varPais = Switch(Pais = "Espaa", "Madrid", _ Pais = "Francia", "Pars", _ Pais = "Portugal", "Lisboa", _ Pais = "Italia", "Roma", _ Len(Pais) = 0, "Tienes que introducir algo", _ IsNumeric(Pais), "Los nmeros no valen") If IsNull(varPais) Then Capital = "No conozco la capital de " & Pais Else Capital = varPais End If End Function
En cada caso devolver:
Capital("Italia") "Roma" Capital(3.1416) "Los nmeros no valen" Capital("Japn ") " No conozco la capital de Japn"
Comencemos a programar con VBA - Access
13 - 4
"Tienes que introducir algo" Como puede comprobarse en la funcin anterior, Switch permite un tipo de cdigo simple
que posibilita evaluar expresiones de diferentes tipos . No obstante, para las funciones Choose, Switch y la funcin IIF que vimos en la entrega 09 hay que tener en cuenta que su velocidad de ejecucin es inferior a la de If Then o Select Case. Por ello, en aquellos procedimientos en los que el tiempo de ejecucin puede ser crtico, es mejor no usarlas.
Capital("
")
Funcin Format
La funcin format recibe una expresin que contiene una cadena, un valor numrico, una fecha, un valor boleano un valor nulo, y devuelve una cadena formateada de acuerdo a las instrucciones contenidas en la cadena formato. Su sintaxis es
Format("Eduardo", "@@@@@@@@@@@@")
Devolver
Eduardo
Si ponemos delante de la cadena de formato el carcter ! har que se alinee a la izquierda. Otro carcter que podemos utilizar es &. Se diferencia de @ en que si en la posicin donde est el signo & hay un carcter, se presentar ste, en caso contrario no pondr nada. Si la cadena con & es precedida del carcter "!" recortar la parte izquierda de la cadena. Si ejecutamos el procedimiento PruebaFormat Public Sub PruebaFormat() Debug.Print Format("Esta cadena a maysculas", ">") Debug.Print Format ("ESTA CADENA A minsculas", "<") Debug.Print "#" & Format("Derecha", "@@@@@@@@@@@@") & "#" Debug.Print "#" & Format("Izquierda", "!@@@@@@@@@@@@") & "#" Debug.Print "#" & Format("Cadena Sin Cortar", "&&&&&") & "#" Debug.Print "#" & Format("Cadena Sin Cortar", "@@@@@") & "#" Debug.Print "#" & Format("Cadena Cortada", "!" & "&&&&&&&") & "#" Debug.Print "#" & Format("Eduardo", "@ @ @ @ @ @ @ @ @") & "#" Debug.Print "#" & Format("Eduardo", "!@ @ @ @ @ @ @ @ @") & "#"
eduardo@olaz.net
Eduardo Olaz
Entrega 13
Funciones de VBA
13 - 5
ESTA CADENA A MAYSCULAS esta cadena a minsculas # Derecha# #Izquierda # #Cadena Sin Cortar# #Cadena Sin Cortar# #Cortada# # E d u a r d o# #E d u a r d o #
Utilizacin con fechas.
13 - 6
El definir cul es el primer da de la semana, puede afectar al ltimo parmetro PrimeraSemanaDelAo. VBA tiene varias formas de considerar cul es la primera semana del ao. En concreto puede tomar 4 criterios diferentes, configurables mediante 4 constantes. Constante vbUseSystem vbFirstJan1 vbFirstFourDays vbFirstFullWeek Valor 0 1 2 3 Significado Primer semana definida por el sistema Ser la que contenga al da 1 de enero La primera que contenga al menos 4 das. La primera que contenga los 7 das
La configuracin de estos dos parmetros afecta al resultado de Format, si queremos mostrar el nmero de semana de una fecha, mediante la cadena de formato "ww".
format(#12/30/05#,"ww",vbSunday,vbFirstJan1)
53
Format(#2/8/05 9:5:3#,Cadena)
Fecha/Hora 9 horas 5 minutos 3 segundos del 8 de febrero de 2005 Cadena Resultado Descripcin "d" "dd" "w" "ddd" "dddd" "ddddd" "dddddd" "mmm" "mmmm" "q" "y" "yy"
eduardo@olaz.net
"8" "08" vbMonday:"2", por defecto:"3" "mar" "martes" "08/02/2005" "martes 8 de febrero de 2005" "02" "feb" "febrero" "1" "39" "05"
Eduardo Olaz
Da del mes con 1 2 dgitos Da del mes con 2 dgitos Nmero de da de la semana Nombre del da con 3 caracteres Nombre del da de la semana Fecha corta Fecha larga Nmero del mes con 1 2 dgitos Nombre del mes con 3 caracteres Nombre del mes Trimestre del ao Da del ao Ao con 2 dgitos
Entrega 13
Funciones de VBA
13 - 7
"yyyy"
Ao con 4 dgitos Hora con 1 2 dgitos Minutos con 1 2 dgitos Segundos con 1 2 dgitos Nmero de semana del ao
"h" "hh" "9" "n" "nn" "5" "s" "ss" "3" "ww" "h/n/s" "h:nn:ss"
7:05:03 PM 7:05:03 P
Podemos poner en la cadena de formato texto que queramos aparezca en el resultado final. Para que no nos den problemas los caracteres que se pueden usar como formato y los interprete como texto se les pone la barra invertida delante.
13 - 8
"Long Date" " Medium Date" "Short Date" "Long Time" "Medium Time" "Short Time"
El procedimiento PruebaFormatoFechas, nos muestra varias maneras de dar formato a una fecha, utilizando las cadenas de formato propuestas en los puntos anteriores. As mismo muestra diversas Cadenas con Nombre y sus equivalentes en cadenas de formato con texto.
Public Sub PruebaFormatoFechas() Dim datFecha As Date Dim strFecha As String datFecha = #9/3/1953 3:34:20 PM# Debug.Print "***** Modo 1 *****" strFecha = Format(datFecha, "Medium Date") Debug.Print strFecha strFecha = Format(datFecha, "dd-mmm-yy") Debug.Print strFecha Debug.Print "***** Modo 2 *****" strFecha = Format(datFecha, "Short Date") Debug.Print strFecha strFecha = Format(datFecha, "dd/mm/yyyy") Debug.Print strFecha Debug.Print "***** Modo 3 *****" strFecha = Format(datFecha, "Long Date") Debug.Print strFecha strFecha = Format(datFecha, "dddd d ") _ & "de " _ & Format(datFecha, "mmmm ") _ & "de " _ & Format(datFecha, "yyyy") Debug.Print strFecha strFecha = Format(datFecha, "ddddd") Debug.Print strFecha Debug.Print "***** Modo 4 *****"
eduardo@olaz.net
Eduardo Olaz
Entrega 13
Funciones de VBA
13 - 9
strFecha = Format(datFecha, "General Date") Debug.Print strFecha strFecha = Format(datFecha, "ddddd hh:nn:ss") Debug.Print strFecha strFecha = Format(datFecha, "dd/mm/yyyy hh:nn:ss") Debug.Print strFecha End Sub
El resultado de este cdigo es
***** Modo 1 ***** 03-sep-53 03-sep-53 ***** Modo 2 ***** 03/09/1953 03/09/1953 ***** Modo 3 ***** jueves 3 de septiembre de 1953 jueves 3 de septiembre de 1953 03/09/1953 ***** Modo 4 ***** 03/09/1953 15:34:20 03/09/1953 15:34:20 03/09/1953 15:34:20
Utilizacin de Format con nmeros. La utilizacin de Format con nmeros, nos concede una gran flexibilidad a la hora de presentar los datos numricos con el formato ms adecuado a cada caso. Tanto si usamos nmeros, como en el caso de las cadenas de texto, no necesitaremos los parmetros para definir el primer da de la semana ni la primera semana del ao. Por ello su sintaxis quedar as: Format(expresin[, formato]) Si ejecutamos directamente la orden Format sobre un nmero, nos lo convertir simplemente a una cadena cambiando, el punto decimal por el carcter definido en la configuracin regional.
Format(3.141592) "3,141592"
Con los nmeros podemos definir si queremos utilizar separadores de miles. Al contrario que en el formato espaol, el separador de miles, en la cadena de formato, es la coma y el separador de los enteros con los decimales es el punto.
Format(1234567890,"#,###") " 1.234.567.890" Format(3.141592,"#,###") " 3" Format(3.141592,"#,###") " 3" Format(3.141592,"#,###.##") " 3.14"
Comencemos a programar con VBA - Access
13 - 10
Format(1234567890,"#,###") " 1.234.567.890," Format(1234567890,"#,###.##") "1.234.567.890," Format(1234567890,"#,###.00") "1.234.567.890,00" Format(3.141592654,"#,###.0000") " 3,1416" Format(0.008,"#,###.0000") ",0080" Format(0.008,"#,##0.0000") "0,0080" Format(023.00800,"#,###.##") "23,01"
El smbolo # representa un dgito cualquiera, salvo los ceros no significativos (los que estn a la izquierda de la parte entera y a la derecha de la parte decimal). El smbolo 0 muestra un dgito o un cero. Si el nmero a formatear tiene en la posicin del cero de la cadena de formato alguna cifra, se mostrar sta. Si no hubiera ninguna mostrar un cero.
Format(0.08120,"0.00E+00") "8,12E-02"
Como en el caso de las cadenas podemos utilizar @ con sin el signo de exclamacin
Format(1234567890,"@ @@@ @@@ @@@") "1 234 567 890" Format(12345,"@ @@@ @@@ @@@") Format(12345,"!@ @@@ @@@ @@@")
Cadenas con nombre para nmeros Al igual que con las fechas, podemos aplicar a los nmeros formatos con nombre. Resultado para diversas cadenas de: Format(1234567.8901,CadenaConNombre) Cadena con Nombre "General Number" "Currency" "Fixed" "Standard"
eduardo@olaz.net
12 345" "
Entrega 13
Funciones de VBA
13 - 11
"123456789,01%" "1,23E+06" "S" "Verdadero" "Activado" Si Cero Si Cero Si Cero "No" "Falso" "Desactivado"
Como los cuadros de texto de los formularios de Access, Format admite cadenas de formato compuesto, es decir formato diferente para distintos tipos de valores. Estas cadenas pueden constar de 4 secciones, separadas cada una de ellas con un punto y coma. En el caso de los nmeros, la primera define cmo se mostrarn los valores Positivos, la segunda los negativos, la tercera para el cero y la cuarta para el valor nulo.
Format(-28,"+ #,##0.00;(#,##0.00);Cero pelotero;Valor Nulo") Format(0,"+ #,##0.00;(#,##0.00);Cero pelotero;Valor Nulo") Format(Null,"+ #,##0.00;(#,##0.00);Cero pelotero;Valor Nulo")
Si alguna de las secciones no estuviera definida, se utilizara para ella el formato positivo.
Configuracin Regional La funcin Format nos permite averiguar cul es la Configuracin Regional en algunas de sus propiedades sin tener, por ejemplo, que acceder al registro de Windows. Si ejecutamos el procedimiento Configuracin nos mostrar algunos elementos de la Configuracin Regional.
Public Sub Configuracion() Dim datPrueba As Date Dim sngDato As Single Dim strFormato As String Dim strSeparador As String Dim strDato As String Dim strSeparadorMiles As String Dim strSeparadorDecimal As String
Comencemos a programar con VBA - Access
13 - 12
datPrueba = #12/31/2000# strDato = Format(datPrueba, "Short Date") strSeparador = Mid(strDato, 3, 1) If Left(strDato, 2) = "31" Then strFormato = "dd" & strSeparador _ & "mm" & strSeparador & "yy" Else strFormato = "mm" & strSeparador _ & "dd" & strSeparador & "yy" End If Debug.Print "Separador fecha " & strSeparador Debug.Print "Formato fecha " & strDato sngDato = 1234.5 strDato = Format(sngDato, "Standard") strSeparadorMiles = Mid(strDato, 2, 1) strSeparadorDecimal = Mid(strDato, 6, 1) Debug.Print "Separador de millares " _ & strSeparadorMiles Debug.Print "Separador de decimales " _ & strSeparadorDecimal Debug.Print "Formato numrico " & strDato strDato = Format(sngDato, "Currency") Debug.Print "Formato moneda " & strDato End Sub
Este procedimiento en mi ordenador muestra:
Separador fecha / Formato fecha 31/12/2000 Separador de millares . Separador de decimales , Formato numrico 1.234,50 Formato moneda 1.234,50
El resultado ser diferente en funcin de la Configuracin Regional propia de cada equipo.
eduardo@olaz.net
Eduardo Olaz
VBA - Access
Entrega
14
Funciones de VBA II
Eduardo Olaz
14 - 2
Estas funciones estn derivadas de sus correspondientes en VBScript; una versin de Visual Basic interpretado desarrollado inicialmente para su implementacin en pginas Web.
Funcin FormatNumber
Devuelve una cadena obtenida al formatear un nmero. Su sintaxis es
numDgitosDespusDeDecimal define el nmero de decimales del formato de salida. Si se pusiera el valor -1, usara la configuracin por defecto. Si necesita recortar los decimales de expresin, realizar en su caso, un redondeo de cifras. incluirDgitoInicial define si se pone un cero inicial, antes del separador de decimales, si el valor de la parte entera fuera cero.
utilizarParntesisParaNmerosNegativos define si se utilizan los parntesis para indicar un valor negativo.
Eduardo Olaz
Entrega 14
Funciones de VBA II
14 - 3
FormatNumber(1234567890.123) " 1.234.567.890,12" Funcin FormatCurrency FormatCurrency devuelve una cadena tras formatear un nmero al modo moneda.
Su sintaxis es
FormatCurrency(1234567890.123) " 1.234.567.890,12 " FormatCurrency(1234567890.123, 0) " 1.234.567.890 " Funcin FormatPercent FormatPercent devuelve una cadena tras formatear un nmero al modo porcentaje.
Multiplica el valor de expresin por 100 y le aade el signo de porcentaje %. Su sintaxis es
FormatPercent (0.123) "12,3%" FormatPercent (3.1415926,4) " 314,1593%" Funcin FormatDateTime FormatDateTime Devuelve una cadena tras formatear un nmero al modo fecha hora,
fecha. Su sintaxis es
Comencemos a programar con VBA - Access
14 - 4
FormatDateTime (fecha[, formatoConNombre]) formatoConNombre puede tomar cualquiera de estos valores vbGeneralDate vbLongDate vbShortDate vbLongTime vbShortTime
Muestra la fecha en Fecha Corta, la hora en Hora Larga ambas en formato si el parmetro es una Fecha con una parte de Hora Muestra la fecha en el formato Fecha Larga de la configuracin. Muestra la fecha en el formato Fecha Corta. Muestra la fecha en el formato especificado en la configuracin. Muestra la fecha en el formato de 24 Horas (hh:mm).
Public Sub PruebaFormatDateTime() Dim datFechaHora As Date Dim datFecha As Date Dim datHora As Date datFecha = Date datHora = Time datFechaHora = datFecha + datHora Debug.Print Debug.Print Debug.Print Debug.Print Debug.Print Debug.Print Debug.Print Debug.Print Debug.Print End Sub FormatDateTime(datFecha) FormatDateTime(datHora) FormatDateTime(datFechaHora) FormatDateTime(datFechaHora,vbGeneralDate) FormatDateTime(datFechaHora, vbLongDate) FormatDateTime(datFechaHora, vbLongTime) FormatDateTime(datFechaHora, vbShortDate) FormatDateTime(datFechaHora, vbShortTime)
20/02/2005 21:15:35 20/02/2005 21:15:35 20/02/2005 21:15:35 domingo 20 de febrero de 2005 21:15:35 20/02/2005 21:15
El resultado puede cambiar, respecto a otros ordenadores, segn su Configuracin regional.
eduardo@olaz.net
Eduardo Olaz
Entrega 14
Funciones de VBA II
14 - 5
Funcin MonthName MonthName Devuelve el nombre del mes en el idioma especificado en la configuracin
regional. Su sintaxis es
MonthName(mes [, abreviar])
El parmetro que se le pasa es un nmero que representa al mes. Su valor debe estar entre 1 y 12; caso contrario dar error de Argumento o llamada a procedimiento no vlida El parmetro abreviar , de tipo boleano, indica si queremos el nombre abreviado. En mi ordenador devuelve los siguientes valores
MonthName(1) "enero" MonthName(2) "febrero" - - - - - - - - - MonthName(11) " noviembre" MonthName(12) " diciembre" MonthName(9,True) " sep" MonthName(10,True) " oct" Funcin WeekdayName WeekdayName Devuelve el nombre del da de la semana que se le pasa como parmetro, en el idioma especificado en la configuracin regional. Su sintaxis es WeekdayName (daDeLaSemana, abreviar, primerDaDeLaSemana)
El parmetro que se le pasa es un nmero que representa al da de la semana. Su valor debe estar entre 1 y 7. El parmetro abreviar , de tipo boleano, indica si queremos el nombre abreviado. El parmetro primerDaDeLaSemana, es una de las constantes que vimos en la funcin Format , indica el da que queremos adoptar como el primero de la semana.
14 - 6
En mi equipo esta funcin devuelve los siguientes valores para distintas llamadas.
WeekdayName(1) " lunes" WeekdayName(1 ,True ,vbUseSystemDayOfWeek) " lun" WeekdayName(1,False,vbSunday) " domingo" WeekdayName(1,,vbMonday) " lunes" WeekdayName(1,,vbMonday) " lunes"
Se pueden obtener resultados exticos cambiando el parmetro primerDaDeLaSemana. Todas estas funciones tienen algo en comn: Facilitan la presentacin de datos acorde con la configuracin del ordenador de cada usuario, evitando tener que acceder a la configuracin regional para efectuar el formateo adecuado del dato.
eduardo@olaz.net
Eduardo Olaz
Entrega 14
Funciones de VBA II
14 - 7
Manipulacin de cadenas
Consideraciones previas sobre el uso del signo Dlar en funciones que devuelven cadenas de texto
Basic, desde sus principios, ya inclua todo un conjunto de procedimientos para el tratamiento de cadenas, entre ellos podemos mencionar Left$, Right$ y Mid$. Estos mismos procedimientos estn disponibles con VBA, pero con una salvedad; pueden usarse sin poner el signo del Dlar al final del nombre del procedimiento. A pesar de que la ayuda de VBA no lo utiliza, podemos afirmar que su uso es muy recomendable. La ayuda de VBA indica que estas funciones devuelven un tipo Variant (String). Esto supone que internamente VBA tiene que hacer una conversin previa de Variant a String. La utilizacin del signo del Dlar fuerza a la funcin a devolver directamente un tipo
Public Sub Prueba() Const Bucle As Long = 10000000 Const conCadena As String = "ABCDEFGHIJKLMN" Dim i As Long Dim strCadena As String Dim tim0 As Single tim0 = Timer For i = 1 To Bucle strCadena = Left(conCadena, 7) Next i Debug.Print Timer - tim0 & " Segundos" tim0 = Timer For i = 1 To Bucle strCadena = Left$(conCadena, 7) Next i Debug.Print Timer - tim0 & " Segundos" tim0 = Timer For i = 1 To Bucle strCadena = Left$(String:=conCadena, Length:=7) Next i Debug.Print Timer - tim0 & " Segundos" End Sub
Comencemos a programar con VBA - Access
14 - 8
Funcin Left
Devuelve los n caracteres situados a la izquierda de una cadena dada. Sintaxis
length debe ser mayor igual a cero. Si es cero devolver la cadena vaca "".
Si fuese menor a cero generar el error 5 Argumento o llamada a procedimiento no vlida.
Funcin LeftB
Devuelve los n Bytes situados a la izquierda de una cadena dada. Sintaxis
"Eduardo"
Eduardo Olaz
Entrega 14
Funciones de VBA II
14 - 9
LeftB extrae Bytes. Left extrae Caracteres. Por ello LeftB es adecuada para el manejo directo de cadenas de Bytes y Left para el manejo de caracteres.
Lo dicho para LeftB ser igualmente aplicable para las funciones RightB y MidB que se corresponden con Right y Mid, funciones que veremos a continuacin.
Function Right
Devuelve los n caracteres situados a la derecha de una cadena dada. Sintaxis
Right (string, length) Right$(string, length) Right( "Eduardo Olaz", 4) " Olaz" "Olaz"
length debe ser mayor igual a cero. Si es cero devolver la cadena vaca "".
Tanto para Left como para Right si length es mayor que el nmero de caracteres contenidos en la cadena, devolver la cadena completa sin generar un error.
Left$( "Eduardo Olaz", 50) Right( "Eduardo Olaz", 50) Function Mid
Devuelve los n caracteres de una cadena dada, situados a partir de una posicin. Sintaxis
Mid(string, start[, length]) Mid$(string, start[, length]) Mid$( "Eduardo Olaz", 9, 3) "Ola" Mid( string:="Eduardo Olaz",start:= 9,length:= 3) "Ola" String es la cadena de la que vamos a extraer caracteres. start es la posicin a partir de la cual vamos a extraerlos. Es de tipo Long y mayor que 0. length es el nmero de caracteres que queremos extraer. Su tipo es Long.
Este parmetro es opcional. Si no se incluyera, o su valor fuera superior al nmero de caracteres que hay desde la posicin de partida, incluyendo su carcter, devolvera todos los caracteres que hay desde la posicin de start.
"Olaz" "Olaz"
La instruccin Mid, Reemplaza determinado nmero de caracteres de una cadena con los caracteres de otra. Sintaxis
14 - 10
Public Sub PruebaInstruccionMid() Dim strAlumno As String strAlumno = "Maitane Gaztelu" Debug.Print "Cadena inicial " & strAlumno Mid(strAlumno, 1, 7) = "Enrique" Debug.Print "Cadena cambiada " & strAlumno Mid(strAlumno, 9, 7) = "Garayoa" Debug.Print "Segundo cambio " & strAlumno ' se puede cambiar por una parte de la 2 cadena Mid(strAlumno, 13, 3) = "llmame" Debug.Print "Cadena parcial " & strAlumno ' Mid slo puede cambiar caracteres que ya existan _ Esto no va a funcionar bien Mid(strAlumno, 9, 10) = "Martnez" Debug.Print "Cambio incompleto " & strAlumno End Sub
Si lo ejecutamos nos mostrar
Cadena inicial Cadena cambiada Segundo cambio Cadena parcial Cambio incompleto
Si no se indica la longitud (parmetro length) pasar todo el contenido de la segunda cadena, si su longitud es menor o igual al resto de VariableString. Si fuera menor cambiar un nmero de caracteres igual a los caracteres que restan de la primera, contando desde el que est en la posicin start. Hasta el final de VariableString.
Public Sub PruebaMid() Dim strInicial As String strInicial = "Cadena Inicial" Debug.Print strInicial Mid(strInicial, 7) = "123"
eduardo@olaz.net
Eduardo Olaz
Entrega 14
Funciones de VBA II
14 - 11
LTrim quita los posibles espacios de la izquierda. RTrim quita los posibles espacios de la derecha. Trim quita tanto los de la izquierda como los de la derecha. Sintaxis LTrim(cadena) RTrim(cadena) Trim(cadena) El parmetro cadena es obligatorio, y se puede pasar cualquier cadena o expresin que
devuelva una cadena de texto. Como valor para cadena admite Null, en este caso devolver tambin Null.
LTrim , RTrim y Trim admiten tambin ser llamadas con el signo de dlar.
En este caso si se les pasa un Null como parmetro, generarn un error. Dim Variable as String * Longitud
"
34567890"
Trim( Null)
Null
14 - 12
El parmetro cadenaOVariable puede ser una cadena o una variable. A Len puede pasrsele una variable definida por el usuario. Si a Len se le pasa un valor Null, devuelve Null. Este cdigo devuelve la longitud de diferentes tipos de variables. Incluso da la longitud del Tipo Linea definido por el usuario y a su vez compuesto por dos tipos Punto, tambin definido por el usuario. Public Type Punto X As Single Y As Single End Type Public Type Linea pto1 As Punto pto2 As Punto End Type Public Sub PruebaLen() Dim intEntero As Integer Dim lngLong As Long Dim curMoneda As Currency Dim strCadena As String * 6 Dim ptoCoordenadas As Punto Dim linSegmento As Linea Dim a(1 To 10) As String a(2) = "1234567890" Debug.Print "Longitud del tipo Integer " & _ Len(intEntero) Debug.Print "Longitud del tipo Long " & _ Len(lngLong) Debug.Print "Longitud del tipo Currency " & _ Len(curMoneda) Debug.Print "Longitud del tipo Punto " & _ Len(ptoCoordenadas) Debug.Print "Longitud del tipo Linea " & _ Len(linSegmento) Debug.Print "Longitud caracteres de strCadena " & _ Len(strCadena) Debug.Print "Longitud (Bytes) de strCadena " & _ LenB(strCadena) Debug.Print "Longitud de a(1) " & _ Len(a(1))
eduardo@olaz.net
Eduardo Olaz
Entrega 14
Funciones de VBA II
14 - 13
Debug.Print "Longitud Caracteres de a(2) " & _ Len(a(2)) Debug.Print "Longitud (Bytes) de a(2) " & _ LenB(a(2)) End Sub El resultado de este cdigo es: Longitud Longitud Longitud Longitud Longitud Longitud Longitud Longitud Longitud Longitud del tipo Integer 2 del tipo Long 4 del tipo Currency 8 del tipo Punto 8 del tipo Linea 16 caracteres de strCadena 6 (Bytes) de strCadena 12 de a(1) 0 Caracteres de a(2) 10 (Bytes) de a(2) 20
Funcin InStr
Devuelve un tipo Variant, convertido a Long que indica la posicin en la que podemos encontrar una Subcadena en otra cadena. Si como cadena buscada, o a buscar, se pasa un Null, devolver un Null. Sintaxis InStr([start, ]string1, string2[, comparar]) Parmetros start: opcional, posicin donde empezar a buscar. String1: Cadena expresin de cadena en la que se busca string2: Cadena buscada comparar: opcional, constante que indica qu se consideran cadenas iguales. Si no se escribe toma el valor por defecto. Las opciones para comparar son:
Sigue el criterio definido en Option Compare Hace una comparacin a nivel de Bytes. Compara los textos Sigue el criterio definido en la base de datos Access
14 - 14
Como en el caso de las funciones anteriores, tambin existe la funcin InStrB que busca valores Byte en una cadena de Bytes.
Funcin InStrReverse
Es similar a InStr, salvo que la bsqueda comienza desde el final de la cadena Como puede comprobarse, su sintaxis difiere ligeramente de la de InStr.
Funcin StrComp
Devuelve un entero como resultado de la comparacin de dos cadenas. Si como parmetro de alguna de las dos cadenas se pasara un nulo, devolvera Null. Sintaxis:
-1 0 1 Null
Eduardo Olaz
Entrega 14
Funciones de VBA II
14 - 15
Public Sub PruebaReplace() Dim strCadena As String strCadena = "aaabbbcccdddeeeEEEfff" Debug.Print strCadena Debug.Print Replace(strCadena, "a", "B") Debug.Print Replace(strCadena, "f", "C", 8) Debug.Print Replace(strCadena, "e", "1", , 2) Debug.Print Replace(strCadena, _ "e", "1", , , vbBinaryCompare) Debug.Print Replace(strCadena, "e", "1") Debug.Print Replace(strCadena, "cccddd", "$$") End Sub
El resultado de este cdigo es
Sintaxis Filter(sourcesrray, match[, include[, comparar]]) Sourcesrray es la matriz en donde se va a buscar. match es el valor con el que queremos comparar. include comparar
Si es True hace que se busquen los valores que contengan a match. Si es False, los que no lo contengan. Forma de comparar. Como referencia ver comparar en InStr.
14 - 16
El siguiente procedimiento primero busca en una matriz llamada aMatriz los datos que contengan la palabra "Jess" y se los asigna a la matriz aFiltrada. Muestra los datos en la ventana Inmediato y seguidamente busca aqullos elementos que no contengan la palabra "Eduardo" y los muestra.
Public Sub PruebaFilter() Dim aFiltrada() As String Dim aMatriz(1 To 6) As String Dim i As Long aMatriz(1) = "Antonio Martnez" aMatriz(2) = "Jess Martnez" aMatriz(3) = "Eduardo Olaz" aMatriz(4) = "Eduardo Prez" aMatriz(5) = "Jess Prez" aMatriz(6) = "Juan Prez" ' Mostramos los elementos _ que contienen "Jess" aFiltrada = Filter(aMatriz, "Jess") For i = LBound(aFiltrada) To UBound(aFiltrada) Debug.Print aFiltrada(i) Next i Debug.Print ' Mostramos los elementos _ que no contienen "Eduardo" aFiltrada = Filter(aMatriz, "Eduardo", False) For i = LBound(aFiltrada) To UBound(aFiltrada) Debug.Print aFiltrada(i) Next i End Sub
El resultado de todo esto es
Jess Martnez Jess Prez Antonio Martnez Jess Martnez Jess Prez Juan Prez Funcin Split
Devuelve una matriz, basada en el ndice 0, que contiene un nmero especificado de subcadenas extradas de una cadena de texto.
eduardo@olaz.net
Eduardo Olaz
Entrega 14
Funciones de VBA II
14 - 17
Sintaxis Split(expresin[, delimitador[, contar[, comparar]]]) expresin Una cadena o una expresin que devuelva una cadena de caracteres. La
cadena puede contener caracteres especificadores de separacin.
delimitador Es un carcter que sirve para definir los lmites de las subcadenas dentro
de la cadena pasada. Si no se indica, toma como carcter de separacin el espacio en blanco.
contar Indica el nmero de subcadenas que queremos extraer. Si se pasa el valor -1, se especifica que queremos extraer todas las subcadenas. Es el modo por defecto comparar
Al igual que en las funciones anteriores, forma de comparar entre s las cadenas. Como referencia ver comparar en InStr.
La funcin Split es til para obtener datos desde un fichero de texto con los campos separados por Delimitadores. El siguiente cdigo hace primero un
Public Sub PruebaSplit() Dim strDatos As String Dim aDatos() As String Dim i As Long strDatos = "Andrs Toms Iker" aDatos = Split(strDatos) For i = LBound(aDatos) To UBound(aDatos) Debug.Print aDatos(i) Next i Debug.Print strDatos = "Martnez Prez;Gmez Iturri;Garca Martn" aDatos = Split(strDatos, ";") For i = LBound(aDatos) To UBound(aDatos) Debug.Print aDatos(i) Next i End Sub
Este cdigo mostrar lo siguiente
14 - 18
Funcin Join
La funcin Join se podra decir que es la inversa de la funcin Split. Toma una matriz de caracteres y los junta en una nica cadena. El siguiente cdigo hace lo siguiente Asigna las estaciones del ao a una matriz mediante la funcin Split. A continuacin vuelve a juntar los datos en pero usando como separador un espacio en blanco " ".
Public Sub PruebaJoin() Dim strDatos As String Dim aDatos() As String Dim strResultado As String strDatos = "Primavera;Verano;Otoo;Invierno" aDatos = Split(strDatos, ";") strResultado = Join(aDatos, " ") Debug.Print strResultado End Sub
Tras ejecutar el cdigo se muestra
Si la cadena responde a la configuracin definida en patrn devolver True, en caso contrario devolver False. Si alguno de los operadores fuese Null, devolver Null. Como cadena puede usarse una cadena o una expresin que devuelva una cadena. Para una comparacin con Option Compare Text, las siguientes expresiones devolvern: left("Eduardo Olaz", 7) like "EDUARDO" True
Eduardo Olaz
Entrega 14
Funciones de VBA II
14 - 19
* Representa a cualquier nmero de caracteres (incluso ninguno) # Representa un dgito (del 0 al 9) Un carcter cualquiera incluido en listacaracteres [listacaracteres] Ninguno de los caracteres incluido en listacaracteres [!listacaracteres] "BMP1956" like "???1956" True "BMP1956" like "???####" True "BMP1956" like "???????" True "BMP1956" like "*####" True "BMP1956" like "*##57" False "Cualquier cadena" like "*" True "Referencia de producto BMP1956" like "*1956" True "BMP1956" like "???1956" True "BMP1956" like "*####" True left("Eduardo Olaz", 7) like "EDUARDO" True "Eduardo Olaz" like Null Null Null like Null Null Funciones Asc y AscB
La funcin Asc devuelve un entero con el cdigo del primer carcter de la cadena pasada como parmetro.
AscB devuelve un entero con el cdigo del primer Byte de la cadena. Sintaxis Asc(cadena) AscB(cadena) Asc("ABCDEFGHIJK") 65 Funciones Chr y Chr$
La funcin Chr y su equivalente Chr$, actan de forma inversa a la funcin Asc. Devuelven un carcter que tiene como cdigo de carcter el valor que se le pasa como parmetro.
Sintaxis Chr(cdigocar)
As como Asc("A") devuelve 65, Chr(65) "A"
Comencemos a programar con VBA - Access
14 - 20
El procedimiento PruebaChrAsc, muestra los cdigos de carcter correspondientes a las letras que van de la "A" a la "Z".
Public Sub PruebaChrAsc() Dim i As Long For i = Asc("A") To Asc("Z") Debug.Print Format(i, "00 - ") & Chr$(i) Next i End Sub
El resultado ser
Public Sub ComparaFuncionB(ByVal Cadena As String) Dim i As Long Dim intCodigo As Integer Dim strCaracter Debug.Print "Cdigos de Caracteres" For i = 1 To Len(Cadena) intCodigo = Asc(Mid(Cadena, i, 1)) strCaracter = Mid(Cadena, i, 1) Debug.Print Format(intCodigo, "000 ") _
eduardo@olaz.net
Eduardo Olaz
Entrega 14
Funciones de VBA II
14 - 21
";
Next i Debug.Print Debug.Print "Cdigos Bytes" For i = 1 To 2 * Len(Cadena) intCodigo = AscB(MidB(Cadena, i, 1)) Debug.Print Format(intCodigo, "000 "); Next i Debug.Print End Sub
Recordemos que
Cdigos de Caracteres 086 V 066 B 065 A 032 128 Cdigos Bytes 086 000 066 000 065 000 032 000 172 032
A primera vista vemos que por cada carcter que extrae la funcin Mid, la funcin MidB extrae 2. Esto se produce porque las cadenas String de VBA manejan caracteres Unicode, y un carcter Unicode est compuesto de 2 Bytes. Voy a hacer una reflexin especial sobre el carcter , correspondiente al Euro. La funcin Asc("") devuelve 128, que es el nmero de carcter que maneja Windows. En cambio, de forma similar a lo que hemos visto en el procedimiento anterior
VBA - Access
Entrega
15
Operadores
Eduardo Olaz
15 - 2
Operadores
A la hora de construir instrucciones en VBA, que contengan operaciones, no slo manejamos constantes, variable y expresiones, sino que utilizamos unos elementos llamados Operadores, que aplicados a uno varios operandos, generan el resultado de la operacin. Tipos de Operadores Aritmticos De Comparacin De Concatenacin Lgicos Se usan para efectuar clculos matemticos Permiten efectuar comparaciones Combinan cadenas de caracteres Realizan operaciones lgicas
Operadores aritmticos
VBA maneja la mayor parte de los operadores aritmticos habituales en los lenguajes de programacin. Estos operadores son
+ Suma - Resta * Producto / Divisin ^ Elevar a potencia \ Divisin entera Mod Mdulo Resto
En general, el tipo devuelto por el resultado de una operacin, es el del tipo del ms preciso de los operadores, salvo que el resultado supere su rango; en ese caso devolver el siguiente tipo de mayor precisin. Esta regla tiene abundantes excepciones. Para ms informacin consulte la ayuda de Access en la que se relata toda la casustica pormenorizada para cada uno de los operadores. Si el resultado de una operacin fuese un dato de coma flotante y se asignara a un tipo entero, se efectuara un redondeo. Si se trata de asignar un resultado fuera del rango de valores de la variable que va a recibir el resultado de la operacin, se generar un error de Desbordamiento y se interrumpir la ejecucin del cdigo, salvo que el error fuera capturado y tratado.
Operador Suma
El operador suma (+), sirve para asignar el resultado de la suma de dos nmeros, en el caso de cadenas, dar como resultado una cadena compuesta por las dos anteriores. La forma de usarlo es
resultado = expresin1+expresin2
eduardo@olaz.net
Eduardo Olaz
Entrega 15
Operadores
15 - 3
Si expresin1 expresin2 tuvieran el valor Null, el resultado sera tambin el valor Null.
expresin1 y expresin2 son los operandos, pudiendo ser cualquier valor numrico
expresiones que los generen. Al contrario de otros lenguajes, VBA permite utilizar como operadores, tipos numricos distintos. Por ejemplo podemos sumar un tipo Byte con un tipo Long, con un tipo Date. Igualmente la variable resultado no tiene por qu ser del mismo tipo que los operandos. Una de las limitaciones es que el resultado de la operacin no debe sobrepasar la capacidad del tipo correspondiente a la variable que va a recibir el resultado de la misma. Esto es aplicable al resto de los operadores. Por ejemplo
Public Sub SumaConError() Dim bytResultado As Byte bytResultado = 10 + 23 Debug.Print bytResultado bytResultado = 150 + 150 Debug.Print bytResultado End Sub
Nos imprimir el resultado 33 y a continuacin nos generar el error n 6 Desbordamiento, ya que un tipo Byte slo admite valores que van de 0 a 255. Nos dara ese mismo tipo de error si tratramos de hacer
bytResultado = 15 + (-16)
Ya que a un Byte no se le pueden asignar valores negativos Lo mismo ocurrira en el siguiente cdigo
Dim intResultado As Integer intResultado = 30000 + 30000 ya que un tipo Integer maneja valores entre -32.768 y 32.767.
Como hemos indicado en un punto anterior, si a una variable que maneja nmeros enteros le asignamos el resultado de una suma de nmeros de coma flotante, efectuar un redondeo del resultado al nmero entero ms prximo.
Public Sub PruebaSumaComaFlotante() Dim intResultado As Integer intResultado = 3.1416 + 2.5468 Debug.Print intResultado intResultado = -3.1416 + (-2.5468) Debug.Print intResultado End Sub
Comencemos a programar con VBA - Access
15 - 4
Nos mostrar como resultado los valores 6 y -6. Cuando se utilizan como operandos dos valores de tipos distintos, VBA cambia el menos preciso al tipo del ms preciso. Por ejemplo si vamos a sumar un Integer con un Long, VBA realiza un moldeado de tipo con el Integer, convirtindolo a Long antes de realizar la operacin. Si uno de los operadores fuera del tipo Date, el resultado tambin lo ser. Si desde la ventana Inmediato hacemos
? #3/14/5# + 1
nos mostrar
Public Sub PruebaSumaCadenas() Dim strResultado As String strResultado = 3 + 4 Debug.Print strResultado strResultado = 3 + "4" Debug.Print strResultado strResultado = "3" + "4" Debug.Print strResultado End Sub
Este cdigo nos devolver
7 7 34
Curiosamente 3 + "4" devuelve 7. Adems si tratamos de hacer
Eduardo Olaz
Entrega 15
Operadores
15 - 5
Hay otro sumando permitido por VBA que no deja de ser sorprendente. Es el valor Empty (vaco) que si lo usamos con un nmero, consigo mismo, se asimila al Cero. Y con una cadena a la cadena vaca "".
Empty + "A"
"A" 0 3.1416
Esta promiscuidad en el manejo y la asignacin entre tipos diferentes de datos que permite Visual Basic para Aplicaciones, es algo que personalmente no me termina de convencer, pero como deca el castizo, - es lo que hay. VBA no permite los operadores del tipo Pre Post Incremental como seran:
Y = X++
El operador resta (-), sirve para asignar el resultado de la sustraccin entre dos nmeros. Tiene dos formas sintcticas
04/12/2004
Para obtener informacin sobre los distintos tipos devueltos, en funcin del de los tipos de los operadores, consulte la ayuda de VBA.
Operador Producto
El operador producto (*), sirve para asignar el resultado del producto de dos nmeros. La forma de usarlo es
resultado = expresin1*expresin2 resultado es una variable de tipo numrico y tanto expresin1 como expresin2
pueden ser cualquier expresin que de como resultado un valor numrico. El tipo numrico que se suministra a resultado depender de los tipos de expresin1 y expresin2.
15 - 6
Si uno de los operandos es del tipo Single y el otro del tipo Long, resultado recibir un tipo Double. Para ms informacin consultar la ayuda de VBA. Si alguno de los operadores es Null, resultado tomar tambin el valor Null. Si alguno de los operandos es Empty, resultado ser Cero. Ya que considerar que ese operando contiene el valor Cero.
Operador Divisin
El operador divisin (/),asigna el resultado de la divisin de dos nmeros. La forma de usarlo es
resultado = expresin1/expresin2 resultado es una variable de tipo numrico y tanto expresin1 como expresin2
pueden ser cualquier expresin que de como resultado un valor numrico. Si expresin2 fuera el valor Cero, dara el error 11 de Divisin por cero. El tipo numrico que recibe resultado normalmente ser del tipo Double. Esta regla tiene varias excepciones Si los operandos son del tipo Byte, Integer Single, resultado recibir un Single, a menos que supere el rango de Single en cuyo caso ser del tipo Double. Si uno de los operandos fuera del tipo Decimal, resultado tambin lo ser. Es aplicable lo dicho en los anteriores operadores matemticos respecto a Null y Empty. Para ms informacin consultar la ayuda de VBA.
resultado = expresin1^exponente resultado es una variable de tipo numrico y tanto expresin1 como exponente
pueden ser cualquier expresin que de como resultado un valor numrico. Si exponente fuera el valor Cero, dara como resultado 1 , incluso en el caso de que la base tuviera el valor Cero. ???.
45^0 0^0
343 ^ (1/3)
Eduardo Olaz
Entrega 15
Operadores
15 - 7
16 ^ 0.5 Sqr(16)
4 4
Adicionalmente, para obtener la raz cuadrada en VBA existe la funcin Sqr. Cuando estudibamos matemticas nos contaron que en el campo de los nmeros reales, la raz cuadrada de un nmero negativo no tiene solucin. Por ello se crearon los nmeros imaginarios. Si tratamos de hacer Sqr(-16)nos generar un error 5 en tiempo de ejecucin, indicando que el valor -16 no es correcto para la funcin Sqr. Puede sorprender que esto no ocurra si calculamos la raz cuadrada elevando a 0.5
-16 ^ 0.5
-4
En cambio si hacemos (-16)^0.5, s dar el error. La razn la veremos ms adelante en la prioridad de operadores. El clculo del operador potencia se realiza antes que el del operador de cambio de signo. Tanto en la base como en el exponente admite nmeros de coma flotante. Por ejemplo podramos obtener algo tan extico como el resultado de elevar el nmero e, base de los logaritmos Neperianos, al valor de Pi
23,1406906315563
El operador divisin entera (\), realiza dos procesos. Si no tuvieran valores enteros, efectuara el redondeo del numerador y del denominador. A continuacin realiza la divisin de los valores resultantes, devolviendo la parte entera del resultado. La forma de usarlo es
resultado = expresin1\expresin2 resultado recibir un valor entero Byte, Integer Long. 8 \ 3 -8 \ 3 12 \ 3 Null \ 3 Empty \ 3 2 -2 4 Null 0
Si al redondear expresin2 diera como resultado el valor Cero, se producira el error 11 de Divisin por cero.
15 - 8
4 \ 0
Error 11
Hay que tener un especial cuidado con este operador, y no olvidar que previamente realiza un redondeo a cero decimales, tanto del numerador como del denominador. Este redondeo utiliza el mtodo de redondeo vigente en el sistema bancario americano, que es ligeramente diferente al europeo. De este tema hablaremos cuando veamos la funcin Round.
1 2 3 1 Null 0
8.9 Mod 7.51 Null Mod 7.51 Empty Mod 7.51 27 Mod 12
Operadores de Comparacin
En algunos procesos podemos necesitar establecer si un valor es menor, igual mayor que otro, por ejemplo para la toma de una decisin mediante una sentencia If . . . Then. En VBA tenemos 6 tipos de operadores para esta tarea. Estos operadores son
Menor que
Menor igual que Mayor que Mayor igual que Igual a Distinto de
Vemos que para los operadores menor o igual, mayor o igual y distinto de hay dos posibles formas de escritura. La forma ms usual de uso es la escrita en primer lugar.
Eduardo Olaz
Entrega 15
Operadores
15 - 9
A la hora de comparar cadenas, hay que tener en cuenta que el resultado puede cambiar en funcin de cmo hayamos establecido la forma de comparacin en
Option Compare
Por ejemplo, si no hemos establecido, a nivel del mdulo, valor para Option Compare, o hemos puesto Option Compare Binary, las siguiente expresiones darn False.
"A"="a"
False False
En cambio si tenemos Option Compare Text, las siguientes expresiones darn True.
True
Si utilizamos Option Compare Database, (slo con bases de datos Access) depender de cmo hemos definido la comparacin a nivel local en la base de datos. Todo lo anterior es aplicable para el resto de los operadores de comparacin con cadenas de texto.
Operador Like
En la entrega anterior vimos la funcin InStr, que permite buscar una cadena dentro de otra y nos devuelve la posicin en la que se encuentra. Tambin vimos la funcin StrComp que compara dos cadenas dicindonos cul es menor. Para la comparacin entre cadenas tenemos tambin un operador adicional Permite comparar una expresin patrn con una cadena, indicndonos si esa cadena cumple con lo indicado en la patrn. Su sintaxis es
Like
15 - 10
Si el contenido de cadena coincide con el contenido de patrn, resultado recibir el valor True. En caso contrario se le asignar False, salvo que alguno de los operandos cadena o
True
En este caso "MAR*" devuelve True, porque "MARTINEZ ELIZONDO" comienza por "MAR" . El carcter "*" sustituye a cualquier conjunto de caracteres. Incluso "MAR" Like "MAR*" "MAR-2" Like "MAR*" tambin devolveran True. Hay otros dos caracteres comodines.
"?" sustituye a un carcter cualquiera "#" sustituye a un dgito cualquiera "MARTA" Like "MART?" "MART2" Like "MART?" "MART24" Like "MART?" "MARTA" Like "MART#" "MART2" Like "MART#" "MART24" Like "MART#" True True False False True False
Podemos ver si un carcter est dentro de un intervalo de caracteres, mediante Listas de caracteres. Las listas de caracteres se especifican escribiendo los caracteres con los que queremos comparar el carcter, entre corchetes, empezando el patrn con comillas. Hay varias formas de construir una lista de caracteres. Caracteres sueltos entre comas
True False
True False
Podemos definir rangos de caracteres indicando el carcter inferior y el superior separados por un guin.
True False
Podemos combinar caracteres sueltos y rangos separndolos por comas. Supongamos que queremos comprobar si un carcter est entre la "a" y la "d" o entre la "m" y la "q", considerando tambin como carcter vlido la "".
Eduardo Olaz
Entrega 15
Operadores
15 - 11
False False
Podemos combinar en las cadenas patrn, caracteres, comodines y listas lo que nos permite definir patrones complejos. Supongamos que queremos que nos de True si la cadena chequeada cumple con estas condiciones:
1. Empieza por una letra entre la "C" y la "H", o entre la "Q" y la "S". 2. El segundo carcter debe ser un nmero 3. El tercer carcter puede ser cualquiera 4. Los cuatro siguientes caracteres deben ser "00BB" 5. El 8 carcter no debe ser la letra V. (Si utilizamos el carcter Exclamacin "!"
equivale a la negacin de lo que le sigue). Vayamos por partes y construyamos los componentes de la cadena patrn.
1. "[Q-R]". 2. "#". 3. "?". 4. "00BB" 5. "[!V]".
"R500BBS" Like "[C-H,Q-R]#?00BB[!V]*" True "R5Z00BBS-12345" Like "[C-H,Q-R]#?00BB[!V]*" True "Q8W00BBAACC" Like "[C-H,Q-R]#?00BB[!V]*" True
De lo aqu expresado podemos ver que el operador Like permite efectuar comprobaciones validaciones de cadenas verdaderamente complejas y con una sola lnea de cdigo. Nota: A la hora de definir una lista de rangos del tipo [A-H], el primer carcter debe ser menor que el segundo. Por ejemplo [H-A] no sera un patrn vlido. En la ayuda de VBA se realiza una descripcin pormenorizada de toda la casustica con la que nos podemos encontrar.
Operador Is
Para la comparacin entre objetos tenemos un operador adicional, es el operador IS.
Is
devuelve True o False en funcin de que dos variable objeto hagan referencia al mismo objeto.
Este tema lo veremos cuando veamos ms a fondo los Objetos. Hay otras 2 formas de utilizar el operador Is. La primera ya la vimos con la estructura Select . . . Case Lo usamos, por ejemplo en las expresiones del tipo
15 - 12
Procuraremos poner los controles de una manera desordenada, y variando sus tamaos. Nuestro formulario podra tener un aspecto tan pattico como ste:
eduardo@olaz.net
Eduardo Olaz
Entrega 15
Operadores
15 - 13
Vemos que no es probable que obtenga el premio al diseo del formulario del ao. Para tratar de poner orden en este caos, vamos a hacer que sea Access el que lo haga utilizando cdigo. Aprovecharemos el evento Al cargar del formulario Teniendo seleccionado el formulario, en la ventana de propiedades seleccionamos el evento Al cargar y en el editor de cdigo escribimos lo siguiente:
Private Sub Form_Load() Const conlngAltura As Long = 300 Dim ctr As Control Dim lngEtiquetas As Long Dim lngCuadrosTexto As Long Dim lngBotones As Long Caption = "Prueba del operador Is" For Each ctr In Me.Controls ctr.Height = conlngAltura If TypeOf ctr Is CommandButton Then ' Si el control es un botn lngBotones = lngBotones + 1 ctr.Left = 500 ctr.Top = lngBotones * 500 ctr.Width = 1500 ctr.Caption = "Botn " _ & Format(lngBotones, "00") End If If TypeOf ctr Is Label Then ' Si el control es una etiqueta lngEtiquetas = lngEtiquetas + 1 ctr.Left = 3000 ctr.Top = lngEtiquetas * 500 ctr.Width = 2500 ctr.Caption = "Etiqueta " _ & Format(lngEtiquetas, "00") End If If TypeOf ctr Is TextBox Then ' Si el control es un cuadro de texto lngCuadrosTexto = lngCuadrosTexto + 1 ctr.Left = 4000 ctr.Top = lngCuadrosTexto * 500 ctr.Width = 2000 ctr = "Cuadro de texto " _ & Format(lngCuadrosTexto, "00")
Comencemos a programar con VBA - Access
15 - 14
Podemos decir que esto ya es otra cosa. En vez de utilizar una sentencia If TypeOf ctr Is Control podramos haber utilizado una de las propiedades que tienen los controles que es ControlType.
Ctr.ControlType nos hubiera devuelto una constante enumerada del tipo acControlType. En concreto, y en este caso, acLabel, acTextBox o acCommandButton.
Hay una funcin adicional que tiene relacin con TypeOf. Es la funcin TypeName.
TypeName devuelve una cadena con informacin acerca de la variable que se le pase
como parmetro. Su sintaxis es TypeName(nombrevariable) La variable nombrevariable puede ser de prcticamente cualquier tipo. En nuestro caso, vamos a aadir la lnea escrita en negrita al cdigo anterior
Eduardo Olaz
Entrega 15
Operadores
15 - 15
Tanto ControlType como TypeOf ctr Is Control o TypeName tienen utilidad, por ejemplo para asignar dinmicamente propiedades a controles y evitar aqullas propiedades que no pertenecen al control con el que se est trabajando. Esto sucede por ejemplo, en aplicaciones multi idioma, en las que queremos adaptar los textos de las diferentes Interfaces al idioma seleccionado por el usuario, de forma dinmica. Otra ejemplo de aplicacin sera disear formularios ajustables a la resolucin que utilice el usuario en su pantalla.
Operadores de concatenacin
Los operadores de concatenacin sirven para unir varias cadenas en una sola. Estos operadores son
Operadores lgicos
Los operadores lgicos sirven para realizar operaciones lgicas con los operandos. Estos operadores son
Conjuncin Lgica Disyuncin lgica Exclusin lgica Negacin lgica Equivalencia lgica Implicacin lgica
Los ms usados son los cuatro primeros, And, Or, Xor y Not. Los seis operadores, adems de trabajar con expresiones de tipo lgico
Operador And
El operador And, realiza una conjuncin lgica entre dos expresiones operandos. Su sintaxis es
Comencemos a programar con VBA - Access
15 - 16
resultado tomar el valor True, slo y slo si las dos expresiones son ciertas.
Las combinaciones de Null con True dan Null.
8 > 4 And 4 >= 3 True IsNumeric("1234") And Len("1234") = 4 IsNumeric("Pepe") And Len("1234") = 4
Las operaciones de And con nmeros se realizan a nivel de Bits.
True False
And devuelve el bit 1 slo si los dos bits correspondientes de los operandos, valen 1.
Este operador es til para poder averiguar si un determinado bit de una expresin est activado (tiene el valor 1). Por ejemplo, si queremos averiguar directamente el valor del tercer bit de un nmero entero M, es suficiente ver el resultado de realizar N And 4. Si el resultado es 4 el tercer bit de M contiene el valor 1, si es 0 el tercer bit contendr el valor 0.
eduardo@olaz.net
Eduardo Olaz
Entrega 15
Operadores
15 - 17
En general si queremos averiguar el bit N N de una expresin M comprobaremos si el resultado de N And 2^(N-1) = 2^(N-1). Hay determinados dispositivos en los que la configuracin viene determinada por los bits de una determinada propiedad.
Operador Or
El operador Or, realiza una Disyuncin lgica entre dos operandos. Su sintaxis es
A nivel de bits, en operaciones con nmeros, Or dar 1 salvo en el caso de que ambos bits valgan 0.
5 Or 13
13
El binario 1101 es en notacin decimal el nmero 13 Con el operador And, hemos visto que podemos averiguar qu bit est activado en una expresin. El operador Or nos permite definir el valor de un determinado bit de un valor. Supongamos que tenemos un dispositivo que contiene la propiedad Config de tipo Long, que controla el funcionamiento del mismo.
Comencemos a programar con VBA - Access
15 - 18
Por circunstancias de la programacin queremos hacer que el 4 bit, contando por la derecha, de dicha propiedad, tome el valor 1. Para ello utilizaremos el operador Or de la siguiente manera:
En operaciones a nivel de bit, dar 1 si uno de los dos es 0 y el otro 1. Si los dos bits fueran iguales dar 0.
5 Xor 13
El binario 1000 es el nmero 8 Supongamos que queremos hacer que el 4 bit, contando por la derecha, de un valor M tome el valor 1. Esto lo haremos en dos pasos 1. Comprobamos si el valor M ya tiene el bit a cero. 2. Si no fuera as lo cambiamos a cero usando Xor
Eduardo Olaz
Entrega 15
Operadores
15 - 19
Operador Not
El operador Not, realiza una Negacin lgica sobre una expresin. Su sintaxis es
Not 13 13 Not 13
-14
0000000000001101 1111111111110010
Operador Eqv
El operador Eqv, realiza una Equivalencia lgica entre dos expresiones. Su sintaxis es
resultado = expresin1 Eqv expresin2 resultado puede ser cualquier variable numrica boleana.
Si alguna de las expresiones fuera Null, el resultado ser Null. Si los dos operandos fuesen True False, dara como resultado True. En caso contrario dara False. Cuadro de resultados del operador Eqv Expresin 1 Expresin 2 Resultado
Podra considerarse como el operador inverso a Xor. En operaciones a nivel de bit, dar 1 si los dos bits fueran iguales. Si uno de los dos es 0 y el otro 1 dar 0.
Comencemos a programar con VBA - Access
15 - 20
5 Eqv 13
-9
Operador Imp
El operador Imp, realiza una Implicacin lgica entre dos expresiones. Su sintaxis es
resultado = expresin1 Imp expresin2 resultado puede ser cualquier variable numrica boleana.
Cuadro de resultados del operador Imp Expresin 1 Expresin 2 Resultado
De forma equivalente, a nivel de bits los resultados seran Expresin 1 Expresin 2 Resultado
0 1 0 1
1 1 0 1
Cuando tenemos una expresin con un operador que une dos operandos, no se plantea problema alguno de interpretacin. Si tenemos
Eduardo Olaz
Entrega 15
Operadores
15 - 21
La variable intC sabemos intuitivamente que tomar el valor 6. En expresiones ms complejas, puede que no lo tengamos tan claro. Por ejemplo, si a continuacin de ese cdigo hiciramos:
Like Is
Las multiplicaciones y divisiones se evalan entre s de izquierda a derecha, siguiendo el orden en el que estn escritas. Esto mismo ocurre con la suma y la resta. Los operadores aritmticos se evalan antes que los de comparacin. Todo esto lleva a al conveniencia de la utilizacin de parntesis, para tener un perfecto control del orden de interpretacin. En una expresin lo primero que se evala son las subexpresiones contenidas dentro de parntesis, si las hubiera. En el caso del ejemplo, dblD = intA ^ 12 / intC si quisiramos que primero se efectuara la divisin de 12 entre intC deberamos haber escrito
4 Or 19 And 7 Or 13
se evala en este orden
Comencemos a programar con VBA - Access
15 - 22
1. 2. 3.
19 And 7 4 Or 3 7 Or 13
3 15
Si quisiramos que evaluara primero 4 or 19, 7 or 13 y luego los respectivos resultados mediante And, deberamos haber escrito
eduardo@olaz.net
Eduardo Olaz
VBA - Access
Entrega
16
Eduardo Olaz
16 - 2
Si nos ponemos en el cuadro Nombre del formulario, vemos que nos aparece una flecha hacia abajo que nos permite seleccionar un formulario. Si la base de datos era nueva nos aparecer nicamente el formulario PruebaMacro que acabamos de guardar. Seleccionaremos ese formulario.
Vamos a la fila siguiente de la columna Accin, y seleccionamos la accin [CuadroMsj], y en los cuadros de abajo ponemos: En Mensaje: Formulario abierto, y en Tipo: Informacin.
eduardo@olaz.net
Eduardo Olaz
Entrega 16
16 - 3
A continuacin presionamos en el botn guardar, asignndole como nombre Autoexec. Cerramos la ventana de macros, e incluso Access. Volvemos a abrir el fichero mdb y veremos que ahora se abre el formulario PruebaMacro inmediatamente despus de cargar el fichero. y nos muestra una cuadro de mensaje tipo a los que ya hemos visto con la funcin MsgBox en la Entrega 12. Hemos comprobado lo siguiente: si a una macro, le ponemos como nombre Autoexec, se ejecutarn sus acciones en el momento de abrir el fichero Access. Este es un comportamiento interesante al que en algn momento podremos sacar partido. Y a cuento de qu viene ahora el hablar de las macros?. No es ir hacia atrs? Las macros ha sido una herramienta muy utilizada por usuarios avanzados de Access que no tenan conocimientos de programacin y que queran dotar a sus aplicativos de cierto grado de automatismo. Lo que quizs algunos de esos usuarios no saban es que Access permite realizar una conversin directa de las macros a cdigo VBA. Las funciones y procedimientos de VBA tienen ms flexibilidad y posibilidades que las macros. Adems las macros no pueden realizar tareas como el control de errores, o el acceso a un nico registro con la misma simplicidad que con VBA. Vamos a la pestaa de Macros, colocamos el cursos encima del nombre de la macro Autoexec, y presionamos el botn derecho del ratn. Nos aparecer un men contextual en el que seleccionaremos la opcin [Guardar como]. Tras esto nos aparece una nueva ventana en la que se nos propone guardar la macro con el nombre Copia de Autoexec. En el segundo cuadro seleccionamos [Mdulo] y presionamos el botn [Aceptar].
Tras ello nos aparece una nueva ventana en la que nos sugiere que la exportacin a un mdulo la va a efectuar con control de errores y comentarios.
No cambiamos esos valores y presionamos el botn [Convertir]. Si ahora nos vamos a la ventana de Mdulos veremos que aparece un nuevo mdulo llamado Macro convertida - Autoexec. El proceso de conversin de macros se puede tambin realizar siguiendo estos pasos
Comencemos a programar con VBA - Access
16 - 4
1. 2. 3. 4.
Seleccione la macro a convertir en la ventana de macros. Seleccionamos la opcin de men [Herramientas] y dentro de ella [Macro] Del men que se despliega seleccionamos [Convertir macros a Visual Basic] Nos aparece el cuadro Convertir macro del punto anterior.
Se pueden convertir tanto las macros generales como las macros asociadas a formularios o informes. Si abrimos el mdulo generado, podremos ver que contiene el siguiente cdigo.
Option Compare Database Option Explicit '-----------------------------------------------------------' Autoexec ' '-----------------------------------------------------------Function Autoexec() On Error GoTo Autoexec_Err DoCmd.OpenForm "PruebaMacro", acNormal, "", "", , acNormal Beep MsgBox "Formulario abierto", vbInformation, "" Autoexec_Exit: Exit Function Autoexec_Err: MsgBox Error$ Resume Autoexec_Exit End Function
Vemos que la macro ha sido cambiada por un mdulo que contiene la funcin Autoexec. Quiero suponer que en vez de un procedimiento sub, genera una funcin que no devuelve explcitamente ningn valor, para que pueda ser utilizada directamente desde la pestaa eventos de la ventana de propiedades de un formulario, informe o control. Ya hablaremos ms delante de este tema Esta funcin tiene dos partes que se corresponden con las dos acciones de la macro. La accin AbrirFormulario se sustituye por el mtodo OpenForm del objeto DoCmd. La accin CuadroMsj se sustituye por la funcin MsgBox que ya hemos visto. Har mencin aparte a la instruccin Beep. Esta instruccin en teora sirve para que el equipo emita un sonido de advertencia. Dependiendo de la tarjeta de sonido que se tenga, y la configuracin del software, se oir un pitido, un sonido o incluso puede que no se oiga absolutamente nada.
eduardo@olaz.net
Eduardo Olaz
Entrega 16
16 - 5
Las mismas acciones que hemos puesto en la macro Autoexec, podramos haberlas puesto en un mismo fichero de Macro, como dos macros independientes. Para ello vamos a hacer lo siguiente: Desde la ventana de Macros, ponemos el cursor del ratn encima de la macro Autoexec y presionamos el botn derecho del ratn (el izquierdo para los zurdos que as lo tengan configurado). Nos aparecer un men contextual, una de cuyas opciones es Cambiar nombre. Cambiamos el nombre de la macro Autoexec, por el de MacrosInicio. A continuacin pulsamos el botn de Diseo (el la escuadra, la regla y el lpiz ) y se nos abrir la macro en vista diseo.
Vamos a activar, si no lo estuviera, una columna a la izquierda que contendr el nombre de la macro. Recordemos que hay que hacer visible, en el editor de macros, la columna correspondiente al nombre de macro. Para ello, desde el editor de Macros, en la opcin de men [Ver] activamos la opcin [Nombres de macro]. Tras esto nos aparecer una columna vaca a la izquierda. Por pura cuestin de orden vamos a separar las dos acciones, que se convertirn en macros independientes. Para ello ponemos el cursor a la izquierda de la celda donde aparece CuadroMsj, y pulsamos dos veces en la opcin de men [Insertar] [Filas]. Ahora nos ponemos a la izquierda de la primera Accin (AbrirFormulario) y escribimos como nombre para esa macro Autoexec. En la celda que est a la izquierda de la accin CuadroMsj escribimos como nombre de la macro algo tan original como Mensaje. Con esto ya tenemos dos macros diferentes escritas en el mismo fichero de macros. En el caso anterior tenamos una nica macro llamada Autoexec que ejecutaba 2 acciones diferentes. Ahora, al arrancar el fichero de access se ejecutar la macro Autoexec, que nos abrir el formulario, pero no se ejecutar la accin de la macro Mensaje. Puedes comprobarlo cerrando el fichero access y volvindolo a abrir. Para solventar este problema, podemos llamar a la macro Mensaje desde la macro Autoexec. Lo podemos conseguir mediante la Accin EjecutarMacro. En la columna Accin, nos ponemos debajo de la celda en la que est escrita la accin AbrirFormulario y seleccionamos la accin EjecutarMacro. A continuacin seleccionamos como parmetro la macro MacrosInicio.Mensaje.
16 - 6
Guardamos todo y vemos que ahora s que se muestra el cuadro de mensaje para indicarnos la apertura del formulario. Cmo afectara este cambio en las macros al cdigo que se generara con el conversor de macros a cdigo VBA?. La respuesta la podemos obtener de forma inmediata. Guardamos la macro, si es que no lo habamos hecho, y desde la ventana macro, seleccionamos nuestra flamante MacrosInicio y activamos la opcin de men [Herramientas] [Macro] [Convertir Macros a Visual Basic] Se nos genera un nuevo mdulo, esta vez con el nombre Macro convertida- MacrosInicio. El cdigo que contiene ese mdulo es el siguiente
Option Compare Database Option Explicit '-----------------------------------------------------------' MacrosInicio_Autoexec ' '-----------------------------------------------------------Function MacrosInicio_Autoexec() On Error GoTo MacrosInicio_Autoexec_Err DoCmd.OpenForm "PruebaMacro", acNormal, "", "", , acNormal DoCmd.RunMacro "MacrosInicio.Mensaje", , ""
MacrosInicio_Autoexec_Exit: Exit Function MacrosInicio_Autoexec_Err: MsgBox Error$ Resume MacrosInicio_Autoexec_Exit End Function
Eduardo Olaz
Entrega 16
16 - 7
MacrosInicio_Mensaje_Exit: Exit Function MacrosInicio_Mensaje_Err: MsgBox Error$ Resume MacrosInicio_Mensaje_Exit End Function
As como la primera vez nos cre un mdulo con una nica funcin, al tener ahora dos macro en un mismo fichero de macros, nos crea un mdulo con dos funciones.
El objeto DoCmd
El objeto DoCmd es un objeto especfico de Access, creado para sustituir a las acciones de las Macros. De hecho sustituye con ventaja a casi todas ellas. Hasta Access 97, no exista ese objeto, pero s exista el procedimiento DoCmd. Por tanto el objeto DoCmd empez a existir con Access 97. Los argumentos de la accin sern ahora los argumentos del mtodo de DoCmd. En la accin AbrirFormulario, ponamos como Nombre del formulario PruebaMacro, como vista Formulario y como modo de la ventana Normal. Al mtodo OpenForm, que abre un formulario, le pasamos como nombre del formulario (FormName) "PruebaMacro", como vista (View) acNormal, y como tipo de ventana (WindowMode) acNormal. Las constante acNormal est definida como una constante enumerada miembro de Access.AcFormView. Su valor numrico es 0.
Comencemos a programar con VBA - Access
16 - 8
Ya comentamos las Constantes Enumeradas en la entrega 12. Este tipo de constantes van a ir apareciendo con mucha frecuencia conforme vayamos avanzando en VBA. Como habrs podido ver hay un paralelismo total entre la accin de macro y el mtodo correspondiente de DoCmd. Pero no todas las Acciones de las macros estn implementadas en los mtodos de DoCmd. Por ejemplo, en la conversin de macros a cdigo VBA hemos visto que CuadroMsj se sustituye por la funcin MsgBox. Otras acciones no implementadas en DoCmd son Accin RunApp RunCode SendKeys SetValue StopAllMacros StopMacro Equivalencia en VBA Funcin Shell Llamada a la subrutina correspondiente Instruccin SendKeys Operador = de asignacin Instrucciones Stop o End Instrucciones Exit Sub o Exit Function Podemos ver directamente los mtodos implementados en la clase DoCmd, mediante la ventana del Examinador de objetos. Para activar esa ventana pulsaremos la tecla [F2] desde el editor de cdigo. Para localizar la clase DoCmd seleccionamos la biblioteca de Access y escribimos el nombre de la clase que queremos buscar. Tras pulsar la tecla de bsqueda se nos posicionar en la clase DoCmd. En la columna de la derecha podremos ver sus mtodos. Se puede comprobar que, al contrario de otras clases, no posee ni propiedades ni eventos. Para la distincin entre Clase, Objeto, Propiedad, Evento y Mtodo, os remito a las anteriores entregas en las que se daba una explicacin a estos conceptos, as como a entregas posteriores en las que se profundizar sobre los mismos.
A continuacin se describen los mtodos implementados en DoCmd, junto con algunos pequeos ejemplos de cdigo.
eduardo@olaz.net
Eduardo Olaz
Entrega 16
16 - 9
Si se requiriera una informacin ms extensa se puede acudir a la ayuda de Access. Mtodo AddMenu Funcionalidad Permite crear barras de men y mens contextuales. Se le pasa el Nombre del men, el Nombre de la macro de men y el Texto de la barra de estado. Permite aplicar un filtro, una consulta o una clusula Tipo WHERE de una instruccin SQL a una tabla, a un formulario o a un informe. Se le pasan como parmetros el Nombre del filtro y la Condicin que aparecera despus del Where. La siguiente lnea de cdigo hace que el formulario en donde est ubicada muestre slo aquellos registros cuyo campo Marca comience por A, B C. DoCmd.ApplyFilter , "Marca Like '[ABC]*'" Se puede utilizar para emitir un sonido por los altavoces del PC Se utiliza para cancelar un evento. Slo tiene efecto cuando se ejecuta como resultado de un evento. No usa parmetros. Permite cerrar la ventana de un objeto. Hay que pasarle como parmetros el Tipo y el Nombre del objeto. Tambin se le puede pasar un tercer argumento para indicar si se guardan o no los posibles cambios. Este cdigo cierra el formulario actual
DoCmd.Close
ApplyFilter
Esta lnea cierra el formulario PruebaMacro y guarda los posibles cambios sin preguntar si deben o no ser guardados.
DoCmd.Close acForm, " PruebaMacro", acSaveYes
CopyDatabaseFile
Copia la base de datos conectada al proyecto actual en un archivo de base de datos de Microsoft SQL Server para la exportacin. Los parmetros son Nombre del Archivo Base De Datos, Sobrescribir un Archivo Existente y Desconectar Todos Los Usuarios. Este ejemplo est sacado de la ayuda de Access. Copia la base de datos conectada al proyecto activo en un archivo de base de datos SQL Server. Si el archivo ya existiera, lo sobrescribe, y se desconectan todos los dems usuarios que estn conectados a la base de datos antes de realizar la copia. DoCmd.CopySQLDatabaseFile _ DatabaseFileName:="C:\Export\Sales.mdf", _ OverwriteExistingFile:=True, _ DisconnectAllUsers:=True Para pasar los parmetros utiliza parmetros Con Nombre mediante el operador de asignacin :=. Permite copiar objetos de la base de datos, como Tablas,
Comencemos a programar con VBA - Access
CopyObject
16 - 10
Consultas, Formularios, Informes, Mdulos, etc en la base de datos actual o en otra que se especifique. Permite incluso copiar un objeto cambindole el nombre. Como parmetros se pasa el Nombre de la base de datos destino, el Nuevo nombre que vaya a tener el objeto copiado, el Tipo de objeto a copiar y el Nombre del mismo. Este cdigo copia la tabla Marcas en la tabla CopiaMarcas DoCmd.CopyObject, "CopiaMarcas", acTable, "Marcas" Si se aplica sobre tablas vinculadas crea un nuevo vnculo. DeleteObject Elimina un objeto de una base de datos. Hay que pasarle como parmetros el Tipo y el Nombre del objeto. Este ejemplo borra la tabla CopiaMarcas. DoCmd.DeleteObject acTable, "CopiaMarcas" Si se aplica sobre tablas vinculadas elimina slo el vnculo. DoMenuItem Este mtodo es un mtodo obsoleto, mantenido por compatibilidad con las versiones anteriores a Access 97. Ha sido sustituido por RunCommand. Consulte la ayuda para mayor informacin Echo Se utiliza para mostrar u ocultar los resultados de la ejecucin de una macro mientras se est ejecutando. DoCmd.Echo True Para desactivar Eco mostrando un mensaje DoCmd.Echo False, "Ejecutndose cdigo de VBA" Busca el siguiente registro que cumpla las condiciones definidas previamente mediante el mtodo FindRecord. Busca un registro que cumpla determinados criterios. Usa los mismos criterios que la opcin de men [Edicin] [Buscar]. Los criterios son Cadena a buscar, Coincidencia de la cadena de bsqueda, Distinguir maysculas, Sentido a buscar, buscar en Campo activo o en Todos, Buscar el primer registro o en Siguientes, buscar en Campos con formato, en el Campo activo y buscar Primero El parmetro Coincidencia toma una de las siguientes constantes del tipo AcFindMatch acAnywhere acEntire (valor predeterminado) acStart Como Sentido toma una de las constantes AcSearchDirection acDown acSearchAll (valor predeterminado) acUp Como Campo activo toma una de las constantes AcFindField acAll
eduardo@olaz.net
FindNext FindRecord
Eduardo Olaz
Entrega 16
16 - 11
acCurrent
(valor predeterminado)
La siguiente instruccin busca el primer registro que contenga el texto "Olaz" en cualquiera de sus campos, distinguiendo entre maysculas y minsculas, considerando tambin los campos con formato. DoCmd.FindRecord "Olaz", _ acAnywhere, _ True, _ acSearchAll, _ True GoToControl Desplaza el foco al campo o control especificado. El siguiente cdigo desplaza el foco al control txtFecha del formulario. DoCmd.GoToControl "txtFecha" Lleva el foco al primer control de la pgina especificada en un formulario en el que se han colocado saltos de pgina, o a una ficha determinada de un control ficha. El parmetro Nmero de pgina es obligatorio. Los parmetros opcionales posicin Horizontal y posicin Vertical son tiles si el formulario es mayor que el tamao de la ventana de Windows. Este cdigo traslada el foco al primer control de la pgina 3 del formulario. DoCmd.GoToPage 3 La accin GoToRecord (IrARegistro) convierte el registro especificado en el registro actual de un conjunto de resultados de una tabla, formulario o consulta. Admite los siguientes parmetros: Tipo de objeto, Nombre, Registro (Anterior, Siguiente, Primero, ltimo, Ir a o Nuevo), Nmero de registros a desplazar. Como tipo de objeto se utiliza una constante AcDataObjectType Esta constante puede tomar los valores acActiveDataObject (Valor predeterminado) acDataForm acDataFunction acDataQuery acDataServerView acDataStoredProcedure acDataTable El parmetro Registro puede tomar el valor de una constante del tipo AcRecord. Sus posibles valores son acFirst acGoTo acLast acNewRec acNext (Valor predeterminado) acPrevious Este cdigo activa el sptimo registro del formulario "Empleados". DoCmd.GoToRecord acDataForm, "Empleados", acGoTo, 7
Comencemos a programar con VBA - Access
GoToPage
GoToRecord
16 - 12
Hourglass
Hace aparecer un reloj de arena en vez del cursor normal del ratn. Si el parmetro es False, vuelve a colocar el cursor por defecto. DoCmd.Hourglass True Maximiza la ventana actual hasta ocupar totalmente la ventana de Windows. No tiene parmetros. DoCmd.Maximize Minimiza la ventana actual y la coloca en la parte inferior de la ventana de Microsoft Access. DoCmd.Minimize Permite desplazar y cambiar el tamao de la ventana activa. Se corresponde con la macro DesplazarTamao. Sus parmetros son: PosicinHorizontal y Vertical de la esquina superior izquierda, ms la Anchura y Altura nuevos de la ventana. Los cuatro parmetros son opcionales. Este cdigo cambia la posicin izquierda de la ventana actual, as como su altura. DoCmd.MoveSize 3000, , , 4000 Abre una pgina de acceso a datos. Sus parmetros son el Nombre de la pgina y el Modo de apertura. En el parmetro Modo de apertura se utiliza una de las constantes del tipo AcDataAccessPageView. Sus valores pueden ser acDataAccessPageBrowse (predeterminado) acDataAccessPageDesign (Vista Diseo) El siguiente cdigo abre la pgina Alumnos en modo Examinar. DoCmd.OpenDataAccessPage "Alumnos", _ acDataAccessPageBrowse
Maximize
Minimize
MoveSize
OpenDataAccessPage
OpenDiagram
Este mtodo sirve, en un fichero de Proyecto (.adp) conectado a una base de datos SQLServer, para abrir en modo diseo un diagrama de relaciones definido en la base de datos. Como parmetro se introduce el Nombre del diagrama. En esta lnea se abre el diagrama guardado previamente como ModeloDeDatos. DoCmd.OpenDiagram "ModeloDedatos" Abre el formulario especificado Se pueden pasar hasta 7 parmetros que controlarn el Nombre del formulario a abrir, el Tipo de vista como se Mostrar el formulario, el Filtro que se aplicar a los datos, una Clusula Where que define las caractersticas que deben cumplir los datos, el Modo como se editarn (slo lectura, etc.) y el Modo como se mostrar la ventana. Adicionalmente se le puede incluir los Argumentos de apertura. Como Tipo de vista se puede usar una de las siguientes constantes AcFormView
Eduardo Olaz
OpenForm
eduardo@olaz.net
Entrega 16
16 - 13
acDesign acFormDS acFormPivotChart acFormPivotTable acNormal (predeterminado) acPreview El Modo de edicin toma una constante AcFormOpenDataMode acFormAdd Se puede agregar registros nuevos pero no se pueden modificar los existentes. acFormEdit Se pueden modificar los registros existentes y agregar registros nuevos. acFormPropertySettings (predeterminado) acFormReadOnly Los registros slo se pueden ver. El Modo como se mostrar la ventana toma uno de los valores AcWindowMode acDialog El formulario ser Modal y Emergente. acHidden El formulario estar oculto. acIcon Se abre minimizado. acWindowNormal valor predeterminado La siguiente lnea abre el formulario Alumnos, en modo de slo lectura, con los datos de los alumnos residentes en Pamplona. DoCmd.OpenForm "Alumnos", , , _ "Poblacion = 'Pamplona'", acFormReadOnly OpenFunction Abre una funcin definida por el usuario en una base de datos de Microsoft SQL Server para verla desde Microsoft Access. Los parmetros son el nombre de la funcin, la Vista como se va a mostrar la funcin, y el Modo de visualizacin o edicin de los datos. La Vista puede es una constante del tipo AcView. acViewDesign Abre la funcin en la vista Diseo. acViewNormal (predeterminado). acViewPivotChart Vista Grfico dinmico. acViewPivotTable Vista Tabla dinmica. acViewPreview Abre la funcin en la Vista preliminar. El modo de visualizacin es del tipo AcOpenDataMode. acAdd Abre la funcin para la insercin de datos. acEdit (predeterminado). Abre la funcin para actualizar los datos existentes. acReadOnly Abre la funcin en Slo lectura. La siguiente lnea abre la funcin Ajuste en vista normal y en el modo edicin de datos. DoCmd.OpenFunction "Ajuste", , acEdit OpenModule Abre un procedimiento dentro de un mdulo en el modo diseo. Como parmetros se pueden pasar el Nombre del mdulo y el del Procedimiento a abrir. Si slo se indica el nombre del mdulo se abre en el primer procedimiento del mismo. Si slo se indica el nombre del procedimiento, lo busca entre
Comencemos a programar con VBA - Access
16 - 14
todos los mdulos y si lo encuentra lo abre en el mismo. Si no existiera el mdulo generar el error 2516. Si no existiera el procedimiento generar el error 2517. Esta lnea abre el mdulo Utilidades en la funcin Edad. DoCmd.OpenModule "Utilidades", "Edad" OpenQuery Se puede utilizar para abrir una consulta de seleccin o una consulta de referencias cruzadas en la Vista Hoja de datos, diseo o en vista preliminar. Esta accin ejecuta una consulta de accin. Tambin se puede seleccionar un modo de entrada de datos para la consulta. Los parmetros a introducir son nombre de la consulta, modo de presentacin y Modo de edicin de los datos. El parmetro Vista es del tipo AcFormView ya visto en OpenForm. El Modo de edicin de los datos es del tipo AcOpenDataMode acAdd acEdit (predeterminado) acReadOnly El siguiente cdigo ejecutar una consulta de accin llamada ctaActualizarPrecios. DoCmd.OpenQuery "ctaActualizarPrecios" Este cdigo abrir una consulta llamada ctaPrecios en modo Vista Previa. DoCmd.OpenQuery "ctaPrecios ",acViewPreview Este cdigo abrir la misma consulta pero en modo edicin. DoCmd.OpenQuery "ctaPrecios ", , acEdit OpenReport Abre el informe especificado Los parmetros controlarn el nombre del informe, la vista preliminar o en modo diseo, el filtro que se aplicar a los datos, y la condicin Where que deben cumplir los datos, el modo de presentacin de la ventana del informe, pudindose incluir argumentos de apertura. La siguiente lnea abre el Informe infAlumnos, en vista previa, con los datos de los alumnos residentes en Pamplona. DoCmd.OpenReport "infAlumnos", _ acViewPreview, , _ "Poblacion = 'Pamplona'
OpenStoredProcedure Abre un procedimiento almacenado de una base de datos SQLServer desde un archivo de proyecto .adp Como argumentos se pasan el Nombre del procedimiento, el Modo de la Vista del tipo AcView visto en OpenFunction y el Modo de edicin de los datos del tipo AcOpenDataMode visto en OpenQuery. El siguiente cdigo abre el procedimiento almacenado ControlAlumnos en el modo Normal pero de slo lectura.
eduardo@olaz.net
Eduardo Olaz
Entrega 16
16 - 15
DoCmd.OpenStoredProcedure "ControlAlumnos", _ acViewNormal", _ acReadOnly OpenTable Abre una tabla en vista hoja de datos, diseo o vista previa, y permite, en su caso, especificar el Modo de la Vista (tipo AcView) y el del Modo de edicin de los datos (tipo AcOpenDataMode). El siguiente cdigo abre la tabla Alumnos en el modo Normal pero de slo lectura. DoCmd.OpenTable "Alumnos", _ acViewNormal", _ acReadOnly Abre una vista de una base de datos SQLServer desde un archivo de proyecto .adp La vista puede estar en modo hoja de datos, diseo o vista previa. Como parmetros se pasan el Nombre de la vista, el Modo como se va a ver (AcView) y el Modo de edicin de los datos (tipo AcOpenDataMode). El siguiente cdigo abre la vista AlumnosMatriculados en el modo tabla dinmica de slo lectura. DoCmd.OpenView "AlumnosMatriculados", _ acViewPivotTable, _ acReadOnly Este mtodo permite presentar los datos de un objeto (tabla, consulta, formulario, informe, mdulo, pgina de acceso a datos, vista, procedimiento almacenado o funcin) en formato HTML (*.htm; *.html), texto (*.txt), Microsoft Active Server Pages (*.asp), Excel (*.xls), Microsoft Internet Information Server IIS (*.htx, *.idc), texto enriquecido (*.rtf), Pginas de acceso a datos (*.htm; *.html) o datos XML (*.xml). Como pgina de acceso a datos slo se pueden presentar los formularios e informes. Las pginas de acceso a datos slo se pueden presentar en formato HTML. Los formatos de IIS y ASP slo estn disponibles para tablas, consultas y formularios. Los argumentos del mtodo son Tipo de objeto, Nombre del objeto, Formato de salida, Archivo de salida, Autoinicio, Archivo de plantilla, Codificacin. Como Tipo de objeto se utiliza una constante AcOutputObjectType acOutputDataAccessPage (No permitido) acOutputForm acOutputFunction acOutputModule acOutputQuery acOutputReport acOutputServerView acOutputStoredProcedure acOutputTable
Comencemos a programar con VBA - Access
OpenView
OutputTo
16 - 16
El siguiente cdigo guarda la tabla Alumnos en el fichero Alumnos.rtf ubicado en la carpeta C:\Datos e inmediatamente despus lo abre con Word para su posible edicin. DoCmd.OutputTo acOutputTable, _ "Alumnos", _ acFormatRTF, _ "C:\Datos\Alumnos.rtf", _ True Consulte la ayuda de Access para completar esta informacin. PrintOut Imprime el objeto activo (hoja de datos, informe, formulario, pgina de acceso a datos o mdulo). Se puede especificar el Rango a imprimir, pginas Desde y Hasta las que imprimir, Calidad de la impresin, Nmero de copias, e Intercalar copias. Como Rango se utiliza una constante del tipo AcPrintRange. acPages acPrintAll (Valor predeterminado) acSelection Como Calidad se utiliza una constante del tipo AcPrintQuality. acDraft acHigh (Valor predeterminado) acLow acMedium El siguiente cdigo imprimir 4 copias de todo el objeto actual en alta calidad, intercalando las pginas. DoCmd.PrintOut acPrintAll, , , _ acHigh , _ 4, _ True Quit Cierra todas las ventanas de Access y sale del programa. Se puede introducir como parmetro una Opcin de salida, constante del tipo AcQuitOption para que pregunte si tiene que guardar los cambios, los guarde directamente o no tenga en cuenta los cambios. acQuitPrompt Muestra un cuadro de dilogo que le pregunta si desea guardar cualquier objeto de base de datos que se haya modificado pero no se haya guardado. acQuitSaveAll (Predeterminado) Guarda todos los objetos sin mostrar un cuadro de dilogo. acQuitSaveNone Sale de Microsoft Access sin guardar ningn objeto La siguiente lnea hace que se salga sin guardar los posibles cambios DoCmd.Quit acQuitSaveNone Rename
eduardo@olaz.net
Entrega 16
16 - 17
Sus argumentos son: el Nuevo nombre del objeto, el Tipo de objeto y el Nombre anterior del mismo Como Tipo de objeto se usa una constante del tipo AcObjectType acDataAccessPage acDefault (predeterminado) acDiagram acForm acFunction acMacro acModule acQuery acReport acServerView acStoredProcedure acTable Esta lnea de cdigo cambia el nombre del formulario "tmpCuentas" a "Cuentas". DoCmd.Rename "Cuentas" , acForm, "tmpCuentas" RepaintObject Se usa para finalizar las actualizaciones pendientes de pantalla para un objeto de base de datos o para el objeto de base de datos activo, si no se especifica ninguno. Incluyen los reclculos pendientes de los controles del objeto. Como argumentos opcionales se pasan el Tipo de objeto del tipo AcObjectType (ver el mtodo anterior) y el Nombre del mismo. DoCmd. RepaintObject acForm, "Cuentas" Requery Actualiza los datos de un objeto, al volver a ejecutar la consulta origen del mismo. Como parmetro utiliza el nombre del objeto. Se puede aplicar a formularios, subformularios, cuadros de lista y combinados, objetos ole y controles con funciones agregadas de dominio. DoCmd. Requery "lstClientes" Restore Vuelve a mostrar, a su tamao normal, una ventana de Windows que pudiera estar minimizada maximizada. No utiliza parmetros, y se ejecuta en el objeto que lo llama. DoCmd. Restore RunCommand El mtodo RunCommand ejecuta un comando de men integrado de barra de herramientas. Reemplaza al mtodo DoMenuItem de DoCmd. Como parmetro se pasa el comando a ejecutar, representado por una de las constantes AcCommand. El mtodo RunCommand no se puede usar para ejecutar un comando en un men o barra de herramientas personalizada. Se usa con mens y barras de herramientas integradas. La siguiente lnea muestra el cuadro de dilogo: [Acerca de Microsoft Office Access] DoCmd.RunCommand acCmdAboutMicrosoftAccess Hay una gran cantidad de constantes para llamar a cada uno de los posibles comandos de men o de barra de herramientas de Microsoft Access. Las constantes AcCommand son:
Comencemos a programar con VBA - Access
acCmdAboutMicrosoftAccess acCmdAddInManager acCmdAddToNewGroup acCmdAddWatch acCmdAdvancedFilterSort acCmdAlignBottom acCmdAlignCenter acCmdAlignLeft acCmdAlignmentAndSizing acCmdAlignMiddle acCmdAlignRight acCmdAlignToGrid acCmdAlignTop acCmdAlignToShortest acCmdAlignToTallest acCmdAnalyzePerformance acCmdAnalyzeTable acCmdAnswerWizard acCmdApplyDefault acCmdApplyFilterSort acCmdAppMaximize acCmdAppMinimize acCmdAppMove acCmdAppRestore acCmdAppSize acCmdArrangeIconsAuto acCmdArrangeIconsByCreated acCmdArrangeIconsByModified acCmdArrangeIconsByName acCmdArrangeIconsByType acCmdAutoCorrect acCmdAutoDial acCmdAutoFormat acCmdBackgroundPicture acCmdBackgroundSound acCmdBackup acCmdBookmarksClearAll acCmdBookmarksNext acCmdBookmarksPrevious acCmdBookmarksToggle acCmdBringToFront acCmdCallStack acCmdChangeToCheckBox acCmdChangeToComboBox acCmdChangeToCommandButton acCmdChangeToImage
acCmdChangeToLabel acCmdChangeToListBox acCmdChangeToOptionButton acCmdChangeToTextBox acCmdChangeToToggleButton acCmdChartSortAscByTotal acCmdChartSortDescByTotal acCmdClearAll acCmdClearAllBreakPoints acCmdClearGrid acCmdClearHyperlink acCmdClearItemDefaults acCmdClose acCmdCloseWindow acCmdColumnWidth acCmdCompactDatabase acCmdCompileAllModules acCmdCompileAndSaveAllModules acCmdCompileLoadedModules acCmdCompleteWord acCmdConditionalFormatting acCmdConnection acCmdControlWizardsToggle acCmdConvertDatabase acCmdConvertMacrosToVisualBasic acCmdCopy acCmdCopyDatabaseFile acCmdCopyHyperlink acCmdCreateMenuFromMacro acCmdCreateRelationship acCmdCreateReplica acCmdCreateShortcut acCmdCreateShortcutMenuFromMacro acCmdCreateToolbarFromMacro acCmdCut acCmdDataAccessPageAddToPage acCmdDataAccessPageBrowse acCmdDataAccessPageDesignView acCmdDataAccessPageFieldListRefresh acCmdDatabaseProperties acCmdDatabaseSplitter acCmdDataEntry acCmdDataOutline acCmdDatasheetView acCmdDateAndTime acCmdDebugWindow
acCmdDelete acCmdDeleteGroup acCmdDeletePage acCmdDeleteQueryColumn acCmdDeleteRecord acCmdDeleteRows acCmdDeleteTab acCmdDeleteTable acCmdDeleteTableColumn acCmdDeleteWatch acCmdDemote acCmdDesignView acCmdDiagramAddRelatedTables acCmdDiagramAutosizeSelectedTables acCmdDiagramDeleteRelationship acCmdDiagramLayoutDiagram acCmdDiagramLayoutSelection acCmdDiagramModifyUserDefinedView acCmdDiagramNewLabel acCmdDiagramNewTable acCmdDiagramRecalculatePageBreaks acCmdDiagramShowRelationshipLabels acCmdDiagramViewPageBreaks acCmdDocMaximize acCmdDocMinimize acCmdDocMove acCmdDocRestore acCmdDocSize acCmdDocumenter acCmdDropSQLDatabase acCmdDuplicate acCmdEditHyperlink acCmdEditingAllowed acCmdEditRelationship acCmdEditTriggers acCmdEditWatch acCmdEncryptDecryptDatabase acCmdEnd acCmdExit acCmdExport acCmdFavoritesAddTo acCmdFavoritesOpen acCmdFieldList acCmdFilterByForm acCmdFilterBySelection acCmdFilterExcludingSelection
Eduardo Olaz
Entrega 16
16 - 19
acCmdFind acCmdFindNext acCmdFindNextWordUnderCursor acCmdFindPrevious acCmdFindPrevWordUnderCursor acCmdFitToWindow acCmdFont acCmdFormatCells acCmdFormHdrFtr acCmdFormView acCmdFreezeColumn acCmdGoBack acCmdGoContinue acCmdGoForward acCmdGroupByTable acCmdGroupControls acCmdHideColumns acCmdHidePane acCmdHideTable acCmdHorizontalSpacingDecrease acCmdHorizontalSpacingIncrease acCmdHorizontalSpacingMakeEqual acCmdHyperlinkDisplayText acCmdImport acCmdIndent acCmdIndexes acCmdInsertActiveXControl acCmdInsertChart acCmdInsertFile acCmdInsertFileIntoModule acCmdInsertHyperlink acCmdInsertLookupColumn acCmdInsertLookupField acCmdInsertMovieFromFile acCmdInsertObject acCmdInsertPage acCmdInsertPicture acCmdInsertPivotTable acCmdInsertProcedure acCmdInsertQueryColumn acCmdInsertRows acCmdInsertSpreadsheet acCmdInsertSubdatasheet acCmdInsertTableColumn acCmdInsertUnboundSection acCmdInvokeBuilder
acCmdJoinProperties acCmdLastPosition acCmdLayoutPreview acCmdLineUpIcons acCmdLinkedTableManager acCmdLinkTables acCmdListConstants acCmdLoadFromQuery acCmdMacroConditions acCmdMacroNames acCmdMakeMDEFile acCmdMaximiumRecords acCmdMicrosoftAccessHelpTopics acCmdMicrosoftOnTheWeb acCmdMicrosoftScriptEditor acCmdMoreWindows acCmdNewDatabase acCmdNewGroup acCmdNewObjectAutoForm acCmdNewObjectAutoReport acCmdNewObjectClassModule acCmdNewObjectDataAccessPage acCmdNewObjectDiagram acCmdNewObjectForm acCmdNewObjectFunction acCmdNewObjectMacro acCmdNewObjectModule acCmdNewObjectQuery acCmdNewObjectReport acCmdNewObjectStoredProcedure acCmdNewObjectTable acCmdNewObjectView acCmdObjBrwFindWholeWordOnly acCmdObjBrwGroupMembers acCmdObjBrwHelp acCmdObjBrwShowHiddenMembers acCmdObjBrwViewDefinition acCmdObjectBrowser acCmdOfficeClipboard acCmdOLEDDELinks acCmdOLEObjectConvert acCmdOLEObjectDefaultVerb acCmdOpenDatabase acCmdOpenHyperlink acCmdOpenNewHyperlink acCmdOpenSearchPage
acCmdOpenStartPage acCmdOpenTable acCmdOpenURL acCmdOptions acCmdOutdent acCmdOutputToExcel acCmdOutputToRTF acCmdOutputToText acCmdPageHdrFtr acCmdPageNumber acCmdPageProperties acCmdPageSetup acCmdParameterInfo acCmdPartialReplicaWizard acCmdPaste acCmdPasteAppend acCmdPasteAsHyperlink acCmdPasteSpecial acCmdPivotAutoAverage acCmdPivotAutoCount acCmdPivotAutoFilter acCmdPivotAutoMax acCmdPivotAutoMin acCmdPivotAutoStdDev acCmdPivotAutoStdDevP acCmdPivotAutoSum acCmdPivotAutoVar acCmdPivotAutoVarP acCmdPivotChartByRowByColumn acCmdPivotChartDrillInto acCmdPivotChartDrillOut acCmdPivotChartMultiplePlots acCmdPivotChartMultiplePlotsUnifiedScale acCmdPivotChartShowLegend acCmdPivotChartType acCmdPivotChartUndo acCmdPivotChartView acCmdPivotCollapse acCmdPivotDelete acCmdPivotDropAreas acCmdPivotExpand acCmdPivotRefresh acCmdPivotShowAll acCmdPivotShowBottom1 acCmdPivotShowBottom10 acCmdPivotShowBottom10Percent
16 - 20
acCmdPivotShowBottom1Percent acCmdPivotShowBottom2 acCmdPivotShowBottom25 acCmdPivotShowBottom25Percent acCmdPivotShowBottom2Percent acCmdPivotShowBottom5 acCmdPivotShowBottom5Percent acCmdPivotShowBottomOther acCmdPivotShowTop1 acCmdPivotShowTop10 acCmdPivotShowTop10Percent acCmdPivotShowTop1Percent acCmdPivotShowTop2 acCmdPivotShowTop25 acCmdPivotShowTop25Percent acCmdPivotShowTop2Percent acCmdPivotShowTop5 acCmdPivotShowTop5Percent acCmdPivotShowTopOther acCmdPivotTableClearCustomOrdering acCmdPivotTableCreateCalcField acCmdPivotTableCreateCalcTotal acCmdPivotTableDemote acCmdPivotTableExpandIndicators acCmdPivotTableExportToExcel acCmdPivotTableFilterBySelection acCmdPivotTableGroupItems acCmdPivotTableHideDetails acCmdPivotTableMoveToColumnArea acCmdPivotTableMoveToDetailArea acCmdPivotTableMoveToFilterArea acCmdPivotTableMoveToRowArea acCmdPivotTablePercentColumnTotal acCmdPivotTablePercentGrandTotal acCmdPivotTablePercentParentColumnItem acCmdPivotTablePercentParentRowItem acCmdPivotTablePercentRowTotal acCmdPivotTablePromote acCmdPivotTableRemove acCmdPivotTableShowAsNormal acCmdPivotTableShowDetails acCmdPivotTableSubtotal acCmdPivotTableUngroupItems acCmdPivotTableView acCmdPreviewEightPages acCmdPreviewFourPages eduardo@olaz.net
acCmdPreviewOnePage acCmdPreviewTwelvePages acCmdPreviewTwoPages acCmdPrimaryKey acCmdPrint acCmdPrintPreview acCmdPrintRelationships acCmdProcedureDefinition acCmdPromote acCmdProperties acCmdPublish acCmdPublishDefaults acCmdQueryAddToOutput acCmdQueryGroupBy acCmdQueryParameters acCmdQueryTotals acCmdQueryTypeAppend acCmdQueryTypeCrosstab acCmdQueryTypeDelete acCmdQueryTypeMakeTable acCmdQueryTypeSelect acCmdQueryTypeSQLDataDefinition acCmdQueryTypeSQLPassThrough acCmdQueryTypeSQLUnion acCmdQueryTypeUpdate acCmdQuickInfo acCmdQuickPrint acCmdQuickWatch acCmdRecordsGoToFirst acCmdRecordsGoToLast acCmdRecordsGoToNew acCmdRecordsGoToNext acCmdRecordsGoToPrevious acCmdRecoverDesignMaster acCmdRedo acCmdReferences acCmdRefresh acCmdRefreshPage acCmdRegisterActiveXControls acCmdRelationships acCmdRemove acCmdRemoveFilterSort acCmdRemoveTable acCmdRename acCmdRenameColumn acCmdRenameGroup
acCmdRepairDatabase acCmdReplace acCmdReportHdrFtr acCmdReset acCmdResolveConflicts acCmdRestore acCmdRowHeight acCmdRun acCmdRunMacro acCmdRunOpenMacro acCmdSave acCmdSaveAllModules acCmdSaveAllRecords acCmdSaveAs acCmdSaveAsASP acCmdSaveAsDataAccessPage acCmdSaveAsHTML acCmdSaveAsIDC acCmdSaveAsQuery acCmdSaveAsReport acCmdSaveLayout acCmdSaveModuleAsText acCmdSaveRecord acCmdSelectAll acCmdSelectAllRecords acCmdSelectDataAccessPage acCmdSelectForm acCmdSelectRecord acCmdSelectReport acCmdSend acCmdSendToBack acCmdServerFilterByForm acCmdServerProperties acCmdSetControlDefaults acCmdSetDatabasePassword acCmdSetNextStatement acCmdShowAllRelationships acCmdShowDirectRelationships acCmdShowEnvelope acCmdShowMembers acCmdShowNextStatement acCmdShowOnlyWebToolbar acCmdShowTable acCmdSingleStep acCmdSizeToFit acCmdSizeToFitForm
Eduardo Olaz
Entrega 16
16 - 21
acCmdSizeToGrid acCmdSizeToNarrowest acCmdSizeToWidest acCmdSnapToGrid acCmdSortAscending acCmdSortDescending acCmdSortingAndGrouping acCmdSpeech acCmdSpelling acCmdSQLView acCmdStartupProperties acCmdStepInto acCmdStepOut acCmdStepOver acCmdStepToCursor acCmdStopLoadingPage acCmdSubdatasheetCollapseAll acCmdSubdatasheetExpandAll acCmdSubdatasheetRemove acCmdSubformDatasheet acCmdSubformDatasheetView acCmdSubformFormView acCmdSubformInNewWindow acCmdSubformPivotChartView acCmdSubformPivotTableView acCmdSwitchboardManager acCmdSynchronizeNow acCmdTabControlPageOrder acCmdTableAddTable acCmdTableCustomView acCmdTableNames acCmdTabOrder acCmdTestValidationRules acCmdTileHorizontally acCmdTileVertically
acCmdToggleBreakPoint acCmdToggleFilter acCmdToolbarControlProperties acCmdToolbarsCustomize acCmdTransferSQLDatabase acCmdTransparentBackground acCmdTransparentBorder acCmdUndo acCmdUndoAllRecords acCmdUnfreezeAllColumns acCmdUngroupControls acCmdUnhideColumns acCmdUpsizingWizard acCmdUserAndGroupAccounts acCmdUserAndGroupPermissions acCmdUserLevelSecurityWizard acCmdVerticalSpacingDecrease acCmdVerticalSpacingIncrease acCmdVerticalSpacingMakeEqual acCmdViewCode acCmdViewDataAccessPages acCmdViewDetails acCmdViewDiagrams acCmdViewFieldList acCmdViewForms acCmdViewFunctions acCmdViewGrid acCmdViewLargeIcons acCmdViewList acCmdViewMacros acCmdViewModules acCmdViewQueries acCmdViewReports acCmdViewRuler acCmdViewShowPaneDiagram
acCmdViewShowPaneGrid acCmdViewShowPaneSQL acCmdViewSmallIcons acCmdViewStoredProcedures acCmdViewTableColumnNames acCmdViewTableColumnProperties acCmdViewTableKeys acCmdViewTableNameOnly acCmdViewTables acCmdViewTableUserView acCmdViewToolbox acCmdViewVerifySQL acCmdViewViews acCmdVisualBasicEditor acCmdWebPagePreview acCmdWebPageProperties acCmdWebTheme acCmdWindowArrangeIcons acCmdWindowCascade acCmdWindowHide acCmdWindowSplit acCmdWindowUnhide acCmdWordMailMerge acCmdWorkgroupAdministrator acCmdZoom10 acCmdZoom100 acCmdZoom1000 acCmdZoom150 acCmdZoom200 acCmdZoom25 acCmdZoom50 acCmdZoom500 acCmdZoom75 acCmdZoomBox acCmdZoomSelection
RunMacro
Sirve para ejecutar una Macro grabada. La Macro puede estar guardada de forma individual, o estar integrada dentro de un grupo de macros. Como parmetros se pasa el Nombre de la macro y opcionalmente el Nmero de veces que se va a repetir la macro y una Expresin numrica que se evala cada vez que se ejecuta la macro. Si esta expresin diera False (0), se detendra la ejecucin de la macro. La siguiente lnea de cdigo ejecuta 3 veces la macro Mensaje guardada en el grupo de macros MacrosInicio. Esta macro es la que habamos creado en un punto anterior.
Comencemos a programar con VBA - Access
16 - 22
DoCmd.RunMacro "MacrosInicio.Mensaje",3 RunSQL La accin RunSQL (EjecutarSQL) ejecuta una consulta de accin utilizando una instruccin SQL correspondiente. Tambin puede utilizarse para ejecutar una consulta de definicin de datos. Como consultas de accin se puede anexar, eliminar y actualizar registros e incluso guardar un conjunto de datos resultado de una consulta dentro de una nueva tabla. Como consulta de definicin de datos se pueden usar las siguientes instrucciones SQL Para crear una tabla Create Table Para modificar una tabla Alter Table Para borrar una tabla Drop Table Para crear un ndice Create Index Para borrar un ndice Drop Index El siguiente ejemplo crea la tabla Alumnos, con diferentes tipos de campo, utilizando el mtodo RunSQL. Public Sub CrearTablaAlumnos() Dim strSQL As String strSQL = "Create Table Alumnos " _ & "(IDAlumno Integer, " _ & "Nombre Varchar(20), " _ & "Apellido2 Varchar(20), " _ & "Apellido1 Varchar(20), " _ & "FechaNacimiento Date, " _ & "Sexo Varchar(1)," _ & "Activo bit)" DoCmd.RunSQL strSQL End Sub Save Guarda el objeto especificado o, si no se indica, el objeto activo. Como parmetros opcionales se pueden pasar el Tipo de objeto y el Nombre del mismo. El parmetro Tipo de objeto puede ser cualquiera de las constantes AcObjectType, vistas en RenameObject. DoCmd. Save acForm, "AlumnosActivos" SelectObject Seleccionar el objeto especificado de la base de datos. Como parmetros obligatorios hay que usar el Tipo de objeto (constante del tipo AcObjectType descrita en RenameObject) y el Nombre del mismo. Opcionalmente se puede indicar si se Selecciona el objeto en la ventana Base de datos, mediante True False. El siguiente ejemplo selecciona el formulario Alumnos en la ventana Base de datos: DoCmd.SelectObject acForm, "Alumnos", True SendObject
eduardo@olaz.net
Entrega 16
16 - 23
mdulo o pgina de acceso a datos de Access en un mensaje de correo electrnico. En las aplicaciones de correo electrnico que admitan la interfaz estndar Microsoft MAPI, se puede incluir objetos con los formatos de Microsoft Excel (*.xls), Texto MS-DOS (*.txt), Texto enriquecido (*.rtf), o HTML (*.html). Como parmetros se utilizan Tipo de objeto: Tabla, Consulta, Formulario, Informe, Mdulo, Pgina de acceso a datos, Vista de servidor o Procedimientos almacenados. Parmetros adicionales son Nombre del objeto, Formato de salida, Enviar a, Enviar copia a, Enviar copia oculta a, Asunto del mensaje, Texto del mensaje, Abrir (s no) el programa de correo para editar el mensaje antes de enviarlo, Plantilla para un archivo HTML. El tipo de objeto a enviar viene dado por una constante del tipo AcSendObjectType Puede ser una de las siguientes AcSendObjectType. acSendDataAccessPage acSendForm acSendModule acSendNoObject (valor predeterminado) acSendQuery acSendReport acSendTable El tipo de objeto a enviar viene dado por una de las siguientes constantes acFormatXLS acFormatTXT acFormatRTF acFormatHTML El siguiente cdigo enva los datos de la tabla DatosNumericos como datos adjuntos en formato Excel, al correo correspondiente a Eduardo Olaz, una copia a Javier Itiz y una copia oculta a Edurne Goizueta. El mensaje tiene como encabezado Datos Test maquinaria y como texto Datos resultado de las pruebas. El mensaje se enva sin necesidad de abrir el programa de correo. DoCmd.SendObject acSendTable, _ "DatosNumericos", _ acFormatXLS, _ "Eduardo Olaz", _ "Javier Itoiz", _ "Edurne Goizueta", _ "Datos Test maquinaria", _ "Datos resultado de las pruebas", _ False
16 - 24
SetMenuItem
Establece el estado de los elementos (habilitado o deshabilitado, activado o desactivado) de las barras de men y barras de men generales personalizadas creadas con macros de barras de mens. Los parmetros son ndice de men, ndice de comando, ndice de sub-comando e Indicador. El Indicador es una constante del tipo AcMenuType y toma alguno de los siguientes valores acMenuCheck acMenuGray acMenuUncheck acMenuUngray (valor predeterminado) Los ndices empiezan con el valor 0. Esta lnea de cdigo desactiva el tercer comando del segundo men de la barra de mens personalizada para la ventana activa: DoCmd.SetMenuItem 1, 2, , acMenuGray
SetWarnings
Activa o desactiva los mensajes de advertencia del sistema. Como parmetro Activar advertencias se pasa True False. Este cdigo desactiva los mensajes de advertencia DoCmd.SetMenuItem False
ShowAllRecords
Quita los filtros aplicados a una tabla, conjunto de resultados de una consulta o del formulario activo y muestra todos los registros. DoCmd. ShowAllRecords
ShowToolbar
Muestra u oculta una barra de herramientas integrada o una barra de herramientas personalizada. Sus parmetros son el Nombre de la barra de herramientas, y una constante del tipo AcShowToolbar que indica cmo se quiere Mostrar la barra de herramientas. Los valores de AcShowToolbar pueden ser acToolbarNo acToolbarWhereApprop acToolbarYes (valor predeterminado) La siguiente lnea de cdigo muestra la barra de herramientas personalizada denominada MiBarra en todas las ventanas Microsoft Access que se vuelvan activas: DoCmd. ShowToolbar " MiBarra" acToolbarYes Para ms informacin vea, en la ayuda de Access la informacin sobre la Accin ShowToolbar (MostrarBarraDeHerramientas)
TransferDatabase
Importa o exporta datos entre la base de datos de Microsoft Access actual o el proyecto de Microsoft Access (.adp) actual y otra base de datos. Tambin puede vincular una tabla a la base de datos de Access actual desde otra base de datos.
eduardo@olaz.net
Eduardo Olaz
Entrega 16
16 - 25
Como parmetros podemos usar Tipo de transferencia una constante del tipo AcDataTransferType que puede tomar los valores acExport acImport (valor predeterminado ) acLink El segundo parmetro indica el Tipo de base de datos. El tipo viene indicado por una expresin de cadena que puede tomar los siguientes valores Microsoft Access (predeterminada) Jet 2.x Jet 3.x dBase III dBbase IV dBase 5.0 Paradox 3.x Paradox 4.x Paradox 5.x Paradox 7.x ODBC Database WSS El siguiente parmetro es el Nombre de la base de datos. A continuacin se indica el Tipo de objeto que se va a importar exportar. ste se define mediante una constante del tipo puede ser cualquiera de las constantes AcObjectType, vistas en RenameObject. El parmetro Origen define el nombre de la tabla, consulta de seleccin u objeto de Access que se desea importar, exportar o vincular. Si la tabla es un fichero, como en el caso de las tablas DBF, se debe indicar la extensin de sta. Destino define el nombre que tendr el objeto, en la base de datos destino, una vez importado, exportado o vinculado. El parmetro Estructura solamente especifica si se va a importar o exportar la estructura de una tabla sin los datos. El ltimo parmetro StoreLogin especifica si, para una tabla vinculada desde la base de datos, se almacenan en la cadena de conexin la identificacin de inicio de sesin y la contrasea de una base de datos ODBC. Los siguientes ejemplos estn basados en los que aparecen en la ayuda de Access, correspondientes al Mtodo TransferDatabase Este siguiente ejemplo importa el informe rptVentasDeAbril desde la base de datos Access Ventas.mdb al informe VentasDeAbril en la base de datos activa:
DoCmd.TransferDatabase acImport, _ "Microsoft Access", _ "C:\Mis Documentos\ Ventas.mdb", _ acReport, _ "rptVentasDeAbril", _ "VentasDeAbril" Comencemos a programar con VBA - Access
16 - 26
El siguiente ejemplo vincula la tabla de la base de datos ODBC Autores a la base de datos activa:
DoCmd.TransferDatabase acLink, _ "ODBC Database", _ "ODBC;DSN=DataSource1;" _ & "UID=User2;PWD=www;" _ & "LANGUAGE=us_english;" _ & "DATABASE=pubs", _ acTable, _ " Autores", _ "dbo Autores"
En el siguiente ejemplo se exporta el contenido de la tabla Clientes a una lista nueva denominada Lista de clientes en el sitio Windows SharePoint Services "http://example/WSSSite".
DoCmd.TransferDatabase transfertype:=acExport, _ databasetype:="WSS", _ databasename:="http://example/WSSSite", _ objecttype:=acTable, _ Source:="Clientes", _ Destination:=" Lista de clientes", _ structureonly:=False
TransferSpreadsheet
Importa o exporta datos entre la base de datos Access o el proyecto de Access (.adp) actual y un archivo de hoja de clculo. Se puede vincular los datos de una hoja de clculo de Microsoft Excel a la base de datos de Access actual. Puede establecerse vnculos, de slo lectura, a los datos de un archivo de hoja de clculo Lotus 1-2-3. Los parmetros son. Tipo de transferencia, una constante del tipo AcDataTransferType vista en el mtodo TransferDatabase. Tipo de hoja de clculo del tipo AcSpreadSheetType Puede tomar los valores acSpreadsheetTypeExcel3 acSpreadsheetTypeExcel4 acSpreadsheetTypeExcel5 acSpreadsheetTypeExcel7 acSpreadsheetTypeExcel8 (valor predeterminado) acSpreadsheetTypeExcel9 (valor predeterminado) acSpreadsheetTypeLotusWJ2 - slo versin Japonesa acSpreadsheetTypeLotusWK1 acSpreadsheetTypeLotusWK3 acSpreadsheetTypeLotusWK4 Nombre de la tabla a la que se van a importar, de la que se van a exportar o con la que se van a vincular datos de hoja de clculo. Nombre de archivo desde el que se va a importar, al que se va a exportar o con el que se va a establecer un vnculo Contiene nombres de campo. Especifica si la primera fila de la hoja de clculo contiene los nombres de los campos. Rango. Rango de celdas que se van a importar o vincular.
eduardo@olaz.net
Eduardo Olaz
Entrega 16
16 - 27
El siguiente ejemplo importa los datos ubicados en el rango (A1:G12), de la hoja de clculo Lotus DatosEmpleados.wk3 a la tabla Empleados. Los nombres de los campos estn contenidos en la primera fila.
DoCmd.TransferSpreadsheet acImport, _ acSpreadsheetTypeLotusWK3, _ "Empleados", _ "C:\Lotus\DatosEmpleados.wk3", _ True, _ "A1:G12"
TransferSQLDatabase
Transfiere toda la base de datos del tipo Microsoft SQL Server especificada a otra base de datos SQL Server. La sintaxis completa de uso es la siguiente
DoCmd.TransferSQLDatabase( _ Servidor, _ BaseDeDatos, _ UsarConexinDeConfianza, _ InicioDeSesin, _ Contrasea, _ TransferirCopiaDatos)
Como parmetros se incluyen Servidor (Server): nombre del servidor al que se va a transferir la base de datos. BaseDeDatos (DataBase): nombre de la nueva base de datos en el servidor especificado. UsarConexinDeConfianza (UseTrustedConnection): indicando con True que la conexin activa utiliza un inicio de sesin con privilegios de administrador del sistema. Si tomara otro valor, se debern especificar los Parmetros InicioDeSesin y Contrasea. InicioDeSesin (Login): Nombre de un inicio de sesin del servidor de destino con privilegios de administrador de sistema Contrasea (Password): La contrasea para el inicio de sesin especificado en InicioDeSesin. TransferirCopiaDatos (TransferCopyData): True si todos los datos de la base de datos se transfieren a la base de datos de destino. En caso contrario slo se transfiere el esquema de la base de datos. Este cdigo transfiere la base de datos SQL Server actual a la nueva base de datos SQL Server denominada CopiaGestion en el servidor Principal. (El usuario debe disponer de privilegios de administrador del sistema en Principal.) Se copian los datos y el esquema de la base de datos.
DoCmd.TransferCompleteSQLDatabase _ Server:="Principal", _ Database:="CopiaGestion", _ UseTrustedConnection:=True, _ TransferCopyData:=True
TransferText
Importar o exporta texto entre la base de datos Access o el proyecto de Access (.adp) actual y un archivo de texto.
Comencemos a programar con VBA - Access
16 - 28
Puede vincular los datos de un archivo de texto a la base de datos de Access actual. Tambin puede importar, exportar y establecer vnculos con una tabla o lista de un archivo HTML. La sintaxis completa de uso es la siguiente
DoCmd. TransferText( _ TipoDeTransferencia, _ NombreDeEspecificacin, _ NombreDeLaTabla, _ NombreDelFichero, _ ContieneNombresDeCampo, _ NombreDeLaTablaHTML, _ PginaDeCdigos)
Los parmetros son los siguientes TipoDeTransferencia (TransferType): Importar, exportar datos o establecer un vnculo con archivos de texto con datos delimitados o de ancho fijo y archivos HTML. Tambin se puede exportar datos a un archivo de datos de combinacin de correspondencia de Microsoft Word. Su valor es una constante del tipo AcTextTransferType que puede tomar los siguientes valores acExportDelim acExportFixed acExportHTML acExportMerge acImportDelim (valor predeterminado) acImportFixed acImportHTML acLinkDelim acLinkFixed acLinkHTML NombreDeEspecificacin (SpecificationName): nombre de una especificacin de importacin o exportacin creada y guardada en la base de datos activa. Con un archivo de texto de ancho fijo, se debe especificar un argumento o utilizar un archivo schema.ini, guardado en la misma carpeta que el archivo de texto importado, vinculado o exportado. NombreDeLaTabla (TableName): Nombre de la tabla Access a la que desea importar, de la que desea exportar o a la que desea vincular datos de texto, o nombre de la consulta de Access cuyos resultados se desean exportar a un archivo de texto. NombreDelFichero (FileName): Nombre completo, incluso ruta de acceso, del archivo de texto del que desea importar, al que desea exportar o con el que desea crear un vnculo. ContieneNombresDeCampo (HasFieldNames): Si el valor es True -1, se indica que la primera fila del archivo de texto se va a usar como nombres de campos al importar, exportar o vincular. NombreDeLaTablaHTML (HTMLTableName): Nombre de la tabla o lista del archivo HTML que desea importar o vincular. Este argumento se omite salvo que el argumento TipoDeTransferencia se establezca en acImportHTML o acLinkHTML.
eduardo@olaz.net
Eduardo Olaz
Entrega 16
16 - 29
PginaDeCdigos (CodePage): Valor Long que indica el conjunto de caracteres de la pgina de cdigos
16 - 30
Sugiero al lector que intente realizar un formulario, conectado a una tabla o consulta y que contenga botones para efectuar las siguientes operaciones: 1. 2. 3. 4. 5. 6. 7. Ir al primer registro Ir al registro anterior Ir al registro siguiente Ir al ltimo registro Agregar nuevo registro Guardar registro Cerrar Formulario
Una vez realizadas estas operaciones analizar el cdigo que se genera. As mismo sugiero que efecte pruebas con diferentes mtodos de DoCmd.
eduardo@olaz.net
Eduardo Olaz
VBA - Access
Entrega
17
Eduardo Olaz
17 - 2
Los mtodos que ahora nos interesan son Path, Name y FullName. Veamos el siguiente cdigo:
Public Function CarpetaActual() As String CarpetaActual = CurrentProject.Path End Function Public Function NombreBaseDatos() As String NombreBaseDatos = CurrentProject.Name End Function Public Function NombreCompletoBaseDatos() As String NombreCompletoBaseDatos = CurrentProject.FullName End Function
Tras definir estas funciones podemos utilizarlas, por ejemplo para mostrar los datos en un cuadro de mensaje.
Eduardo Olaz
Entrega 16
17 - 3
Dim strMensaje As String strMensaje = "El fichero est en la carpeta " _ & vbCrLf & vbCrLf _ & CarpetaActual() _ & vbCrLf & vbCrLf _ & "y su nombre completo es" _ & vbCrLf & vbCrLf _ & NombreCompletoBaseDatos MsgBox strMensaje, _ vbInformation + vbOKOnly, _ "Datos de " & NombreBaseDatos() End Sub
Tras esto, si ejecuto el procedimiento PropiedadesBaseDatos en mi ordenador, me muestra el siguiente mensaje:
De esta manera, si los datos estuvieran en el fichero Datos.mdb ubicado en una carpeta llamada Datos, que cuelgue de la carpeta de la aplicacin, podramos definir una funcin llamada FicheroDatos que nos devuelva la ruta completa del fichero. Podra ser algo as como:
Public Function FicheroDatos() As String Const conBaseDatos As String = "Datos.mdb" FicheroDatos = CarpetaActual() _ & "\Datos\" & conBaseDatos End Function
Si ejecuto en mi ordenador esta funcin, me devuelve la cadena
C:\CursoVBAAccess\Captulo_17\Datos\Datos.mdb
Otro paso interesante sera comprobar si existen la carpeta y el fichero en nuestro disco. Una de las formas de averiguarlo sera utilizando la funcin Dir().
Funcin Dir
La funcin Dir(), nos devuelve un valor de tipo String que representa el nombre de un archivo, o carpeta que coincide con el patrn o atributo de archivo que se le ha pasado como parmetro. Tambin nos puede devolver la etiqueta de volumen de una unidad de disco.
Comencemos a programar con VBA - Access
17 - 4
NombreDeRuta es una expresin de cadena que indica la ruta donde queremos buscar y
el tipo de fichero que nos interesa. Adems de la ruta podemos especificar caractersticas que tenga el nombre del fichero, mediante el uso de los comodines "*" y "?". Por ejemplo para obtener los ficheros de cualquier tipo que empezaran por la letra A, en la carpeta "C:\Graficos\" podramos escribir Dir("C:\Graficos\A*.*"). El smbolo "*" sustituye a cualquier nmero de caracteres. El smbolo sustituye a 1 carcter, o ninguno. Por ejemplo, Dir("C:\Graficos\????.*") nos devolvera nombres de ficheros con hasta cuatro caracteres en el nombre, y con cualquier tipo de extensin.
gato.bmp bart.jpg
Para obtener todos los nombres de fichero de una carpeta que cumplan determinada condicin, la primera llamada se efecta con el nombre de la ruta y sus atributos. Las siguientes llamadas se hacen nicamente con Dir() devolviendo el nombre de los sucesivos ficheros. Mientras existan ficheros que cumplan la condicin, Dir() devolver su nombre. Cuando deje de haberlos, devolver la cadena vaca "".
Atributos es una constante. Los valores que puede tomar son: vbNormal (Predeterminado) Especifica archivos sin atributos. vbReadOnly Slo lectura y sin atributos Archivos ocultos y sin atributos vbHidden VbSystem Del sistema y sin atributos vbVolume Etiqueta del volumen Carpetas y archivos sin atributos vbDirectory
Si por ejemplo quisiramos obtener todos los ficheros que fueran del tipo Gif y que estuvieran en la carpeta C:\Graficos\", podramos hacer:
Public Sub FicherosGif() Dim colFicheros As New Collection Dim strCarpeta As String Dim strFichero As String Dim i As Long strCarpeta = "C:\Graficos\" strFichero = Dir(strCarpeta & "*.gif") If strFichero = "" Then
eduardo@olaz.net
Eduardo Olaz
Entrega 16
17 - 5
Exit Sub Else Do Until strFichero = "" colFicheros.Add strFichero strFichero = Dir() Loop End If For i = colFicheros.Count To 1 Step -1 Debug.Print colFicheros(i) colFicheros.Remove (i) Next i Set colFicheros = Nothing End Sub
En este ejemplo busca los ficheros del tipo gif, en la carpeta "C:\Graficos\" mediante la expresin
Dir("*.gif")
El procedimiento hubiera mostrado los posibles ficheros gif que existieran en la ruta de acceso actual. Una de las carpetas habituales suele ser la de "Mis documentos". El valor de esta carpeta la podemos obtener mediante la funcin CurDir. Mediante la funcin Dir, podemos tambin obtener el nombre del volumen, usando la letra de la unidad y la constante vbVolume. Por ejemplo:
Dir("C:",vbVolume) Dir("E:",vbVolume)
Funcin CurDir
La funcin CurDir(), nos devuelve un valor de tipo String que representa el nombre de la ruta de acceso actual. Su sintaxis es CurDir[(Unidad)] Podemos usar el nombre de la unidad de la que queremos obtener la ruta de acceso.
Comencemos a programar con VBA - Access
17 - 6
CurDir("D") CurDir("F") Si no se especifica la unidad de disco o el parmetro unidad es la cadena vaca (""), la funcin CurDir devuelve la ruta de acceso de la unidad de disco actual.
Podemos establecer desde VBA la ruta actual deseada. Para ello se utiliza la Instruccin ChDir.
CurDir("C")
Instruccin ChDir
La instruccin ChDir, establece la carpeta actual. Para ello se le pasa como parmetro la carpeta deseada. Su sintaxis es ChDir Ruta Si la ruta no existiera, generara el error 76
ChDir "C:\Programa\Datos" ChDir no cambia la unidad de disco actual, slo la carpeta del disco especificado
La siguiente instruccin cambia la carpeta de D:
ChDir "D:\Comercial\Datos"
Si la unidad seleccionada hasta ese momento era la C, seguir sindolo despus de esta ltima instruccin. Si en la ruta no se especifica la unidad, se cambia el directorio o carpeta predeterminado de la unidad actual. Se puede hacer que la carpeta actual sea la de un nivel superior pasndole dos puntos.
Instruccin ChDrive
La instruccin ChDrive, establece la unidad de disco actual. Su sintaxis es ChDrive Unidad
ChDrive "D"
Si no existiera, o no estuviera disponible la unidad pasada como parmetro, generara el error 68
"Dispositivo no disponible"
A veces podemos vernos en la necesidad de crear una carpeta nueva. Para ello tenemos la instruccin MkDir.
Instruccin MkDir
La instruccin MkDir, crea una carpeta.
eduardo@olaz.net
Eduardo Olaz
Entrega 16
17 - 7
MkDir "C:\MiNuevaCarpeta" MkDir "C:\MiNuevaCarpeta\Subcarpeta_1" MkDir "C:\MiNuevaCarpeta\Subcarpeta_2" Tras esto tendremos la carpeta C:\MiNuevaCarpeta con dos nuevas carpetas Subcarpeta_1 y Subcarpeta_2.
Instruccin RmDir
La instruccin RmDir, elimina una carpeta existente. Su sintaxis es RmDir Ruta Tras crear las carpetas del punto anterior podramos eliminarlas utilizando
RmDir "C:\MiNuevaCarpeta\Subcarpeta_1" RmDir "C:\MiNuevaCarpeta\Subcarpeta_2" RmDir "C:\MiNuevaCarpeta" Para eliminar una carpeta con RmDir es preciso que sta est vaca, es decir: no debe
tener ningn archivo ni otra subcarpeta. Caso contrario generara el error 75
Instruccin Kill
La instruccin Kill, elimina un archivo existente. Su sintaxis es Kill Ruta El argumento requerido Ruta es una cadena que especifica el nombre o los nombres de los archivos que se van a eliminar. Puede incluir la ruta completa del fichero. Al igual que la funcin Dir, tambin admite utilizar los comodines asterisco "*" y "?". Por ejemplo, si quisiramos borrar todos los ficheros gif de la carpeta actual, usaramos Kill "*.gif" Si quisiramos borrar todos los ficheros de la carpeta actual, usaramos Kill "*.*" Si quisiramos borrar todos los archivos que empezaran por Tmp : Kill "Tmp*.*" Si quisiramos borrar todos los ficheros cuyo nombre empiece por A y tengan hasta 4 caracteres en el nombre. Kill "A???.*"
17 - 8
El objeto FileSearch
Adems de los procedimientos vistos con anterioridad, VBA incorpora un objeto que nos puede suministrar una informacin ms completa que los procedimientos vistos en los puntos anteriores. Es el objeto FileSearch. El objeto FileSearch es devuelto por el objeto Application, a travs de su propiedad FileSearch.
SortBy
especifica el mtodo utilizado para la ordenacin de los datos obtenidos. Nombre de la constante msoSortByFileName predeterminado msoSortByFileType msoSortByLastModified msoSortByNone msoSortBySize Valor 1 3 4 5 2
Nota: Aunque FileSearch puede funcionar sin activar desde Access la referencia a la librera Microsoft Office NN.N Object Library si no se hace referencia explcita a ella, no se tiene acceso a las constantes propias de FileSearch, as como a la declaracin de ciertas variables. Por ello es aconsejable activar la referencia a esa librera.
eduardo@olaz.net
Eduardo Olaz
Entrega 16
17 - 9
Para hacerlo, desde la opcin de men [Herramientas] se selecciona la opcin [Referencias] y se activa la opcin correspondiente a la Biblioteca. En mi ordenador queda as:
Para poder trabajar con FileSearch, aunque no se haya efectuado una referencia a la biblioteca mencionada, y que se identifique el valor de las constantes, en esta y sucesivas tablas pongo su nombre y su valor.
Public Sub UsoDeExecute() Dim objFileSearch As Object Dim i As Long Set objFileSearch = Application.FileSearch With objFileSearch .LookIn = "C:\Grficos" .FileName = "*.jpg" ' msoSortbyFileName tiene el valor 1 _ msoSortOrderAscending tiene el valor 1 If .Execute(SortBy:= msoSortByFileName, _
Comencemos a programar con VBA - Access
17 - 10
SortOrder:= msoSortOrderAscending) > 0 Then MsgBox "Se han encontrado " & _ .FoundFiles.Count & _ " ficheros de tipo grfico jpg" For i = 1 To .FoundFiles.Count MsgBox .FoundFiles(i), _ vbInformation, _ "Fichero N " & _ Format(i, "0000") Next i Else MsgBox "No hay ficheros jpg" End If End With End Sub
Propiedad LastModified La propiedad LastModified (de tipo Boolean) devuelve o establece una constante que indica el tiempo transcurrido desde la ltima vez que se modific y guard el archivo. El valor predeterminado para esta propiedad es msoLastModifiedAnyTime, con el valor 7. Nombre de la constante msoLastModifiedAnyTime predeterminado msoLastModifiedLastMonth msoLastModifiedLastWeek msoLastModifiedThisMonth msoLastModifiedThisWeek msoLastModifiedToday msoLastModifiedYesterday Valor 7 5 3 6 4 2 1
Este ejemplo, extrado de la ayuda de Access, establece las opciones para la bsqueda de un archivo. Los archivos que devolver esta bsqueda han sido previamente modificados y estn ubicados en la carpeta C:\Grficos o en una subcarpeta de sta.
eduardo@olaz.net
Eduardo Olaz
Entrega 16
17 - 11
Objeto FoundFiles
El Objeto FoundFiles, es un objeto devuelto por la propiedad FoundFiles del objeto FileSearch, y que contiene una coleccin que almacena los datos de los ficheros encontrados mediante el mtodo Execute. Este objeto posee las propiedades de las colecciones Count e Item, adems de las propiedades Application y Creator.
Count devuelve el nmero de ficheros encontrados al ejecutar el mtodo Execute del objeto FileSearch. Item devuelve los nombres de los ficheros encontrados. Para ello le pasamos el ndice correspondiente basado en cero. Application devuelve una referencia a la aplicacin contenedora; en nuestro caso Access. Creator devuelve un nmero long asociado a la aplicacin contenedora.
Ejemplo Supongamos que queremos mostrar todos los ficheros que estn en la carpeta correspondiente al actual proyecto de Access. Es la carpeta de CurrentProject.Path Para ello utilizaremos el siguiente cdigo:
Public Sub UsoDeFileSearch() Dim i As Long Dim strFicheros As String With Application.FileSearch .LookIn = CurrentProject.Path .FileName = "*.*" .SearchSubFolders = True If .Execute() > 0 Then For i = 1 To .FoundFiles.Count strFicheros = strFicheros _ & .FoundFiles(i) _ & vbCrLf Next i MsgBox strFicheros, _ vbInformation + vbOKOnly, _ "Se han encontrado " _ & .FoundFiles.Count & _ " ficheros" Else MsgBox "No se han encontrado ficheros", _ vbInformation + vbOKOnly, _ "Bsqueda en " _
Comencemos a programar con VBA - Access
17 - 12
ObjetoFileSearch.NewSearch
Propiedad FileType La propiedad FileType nos permite averiguar o establecer el tipo de archivo que debe buscarse. Es de lectura y escritura Esta propiedad se debe corresponder a alguna de las constantes MsoFileType. Sus valores posibles son: Nombre de la constante msoFileTypeAllFiles msoFileTypeBinders msoFileTypeCalendarItem msoFileTypeContactItem msoFileTypeCustom msoFileTypeDatabases msoFileTypeDataConnectionFiles msoFileTypeDesignerFiles msoFileTypeDocumentImagingFiles msoFileTypeExcelWorkbooks msoFileTypeJournalItem msoFileTypeMailItem msoFileTypeNoteItem msoFileTypeOfficeFiles msoFileTypeOutlookItems msoFileTypePhotoDrawFiles msoFileTypePowerPointPresentations msoFileTypeProjectFiles
Eduardo Olaz
Valor 1 6 11 12
7 17 22 20 4 14 10 13 2 9 16 5 19
eduardo@olaz.net
Entrega 16
17 - 13
18 15 8 21 23 3
La constante msoFileTypeAllFiles, de valor 1, hace que se busquen todos los archivos. La constante msoFileTypeOfficeFiles, cuyo valor es 2, incluye todos los archivos que tienen una de las siguientes extensiones: *.doc, *.xls, *.ppt, *.pps, *.obd, *.mdb, *.mpd, *.dot, *.xlt, *.pot, *.obt, *.htm, o *.html. Ejemplo: El siguiente cdigo busca en la carpeta "C:\Grficos", sin tomar en cuenta las subcarpetas, los ficheros tipo Pgina Web.
Public Sub UsoDeFileType() Dim objFileSearch As Object Dim i As Long Set objFileSearch = Application.FileSearch With objFileSearch .NewSearch .SearchSubFolders = False .LookIn = "C:\Grficos" ' msoFileTypeWebPages tiene el valor 23 .FileType = msoFileTypeWebPages If .Execute > 0 Then MsgBox "Se han encontrado " _ & .FoundFiles.Count & _ " ficheros de tipo pgina web" For i = 1 To .FoundFiles.Count MsgBox .FoundFiles(i), _ vbInformation, _ "Fichero N " & Format(i, "0000") Next i Else MsgBox "No hay ficheros web" End If End With End Sub
Comencemos a programar con VBA - Access
17 - 14
El objeto ScopeFolder se corresponde a una carpeta en la que se pueden realizar bsquedas. Los objetos ScopeFolder pueden utilizarse con la coleccin SearchFolders. La coleccin ScopeFolders contiene la coleccin de objetos ScopeFolder que determina en qu carpetas se realizar la bsqueda al activar el mtodo Execute del objeto FileSearch. La coleccin SearchScopes pertenece al objeto FileSearch y contiene los objetos
SearchScope.
El objeto SearchScope se utiliza para proporcionar acceso a los objetos ScopeFolder que pueden agregarse a la coleccin SearchFolders. Se corresponde a un tipo de rbol de carpetas en las que pueden efectuarse bsquedas utilizando el objeto FileSearch. Como se indica en la ayuda de VBA, las unidades locales de su equipo representan un solo mbito de bsqueda. Las carpetas de red y las de Microsoft Outlook son tambin dos mbitos individuales de bsqueda disponibles. Cada objeto SearchScope incluye un solo objeto ScopeFolder que corresponde a la carpeta raz del mbito de bsqueda. El ejemplo siguiente est adaptado de la ayuda de VBA y muestra todos los objetos SearchScope disponibles actualmente.
Public Sub MostrarLosAmbitosDisponibles() ' Declara una variable que hace referencia _ a un objeto SearchScope Dim ss As SearchScope ' Utiliza un bloque With...End With para referenciar _ ' el objeto FileSearch
eduardo@olaz.net
Eduardo Olaz
Entrega 16
17 - 15
With Application.FileSearch ' Recorre la coleccin SearchScopes For Each ss In .SearchScopes Select Case ss.Type Case msoSearchInMyComputer MsgBox "Mi PC" _ & " es un mbito de bsqueda disponible." Case msoSearchInMyNetworkPlaces MsgBox "Mis sitios de red" _ & " son un mbito de bsqueda disponible." Case msoSearchInOutlook MsgBox "Outlook" _ & " es un mbito de bsqueda disponible." Case msoSearchInCustom MsgBox "Hay disponible" _ & " un mbito personalizado de bsqueda." Case Else MsgBox "No puedo determinar" _ & " el mbito de bsqueda." End Select Next ss End With End Sub
El mtodo RefreshScopes actualiza la lista de objetos ScopeFolder disponibles actualmente. Su sintaxis es :
ObjetoFileSearch.RefreshScopes
El siguiente ejemplo, adaptado de la ayuda de VBA, muestra todos los objetos ScopeFolder disponibles actualmente en la unidad C:\ del mbito de Mi PC y demuestra la necesidad de utilizar el mtodo RefreshScopes cuando se producen cambios en la lista de carpetas.
Sub PruebaDelMetodoRefreshScopes() ' Muestra lo que sucede antes o despus ' de llamar al mtodo RefreshScopes ' si previamante se ha aadido una nueva carpeta ' a la lista del mbito de bsqueda. Dim strCarpeta As String strCarpeta = "C:\_BorrarDespusDeSerUsado"
Comencemos a programar con VBA - Access
17 - 16
' Si ya existe la carpeta la borramos If Len(Dir(strCarpeta)) > 1 Then RmDir Path:=strCarpeta End If ' Refrescamos la lista de carpetas. Application.FileSearch.RefreshScopes ' Lista antes de crear la carpeta Call ListarNombresDeCarpetas ' Creamos una nueva carpeta en el disco C:\ ' Se producir un error si la carpeta ya existiera MkDir Path:=strCarpeta ' Lista despus de haber creado la carpeta ' La carpeta nueva no aparece en la lista. Call ListarNombresDeCarpetas ' Refrescamos la lista de carpetas. Application.FileSearch.RefreshScopes ' Ahora la carpeta nueva s aparece en la lista. Call ListarNombresDeCarpetas ' Borramos la carpeta RmDir Path:=strCarpeta End Sub Sub ListarNombresDeCarpetas() Dim i As Integer Dim strResultados As String ' ' ' ' Recorre todas las carpetas en el disco C:\ en Mi Pc e informa de los resultados .SearchScopes.Item(1) = "Mi Pc" .ScopeFolders.Item(2) = "C:\"
Eduardo Olaz
Entrega 16
17 - 17
For i = 1 To .ScopeFolders.Count strResultados = strResultados & .ScopeFolders. _ Item(i).Name & vbCrLf Next i MsgBox "Nombres de carpetas en C:\...." _ & vbCrLf _ & vbCrLf _ & strResultados End With End Sub
Nota: Los apartados anteriores, referentes al objeto FileSearch, tienen exclusivamente como objetivo, introducir al lector en el uso de este objeto y sus posibilidades. Profundizar en los mismos est fuera del alcance y de los objetivos de este texto, por lo que remito a los posibles interesados a la ayuda de VBA, en donde podrn encontrar una extensa resea sobre ellos.
VBA - Access
Entrega
18
Eduardo Olaz
18 - 2
Instruccin Open
Para poder acceder a un fichero lo primero que hay que hacer es abrirlo. La instruccin Open es la que se encarga de activar las funciones de Entrada/Salida en un fichero. Su sintaxis es
Open RutaDeAcceso For Modo [Access TipodeDeAcceso] [Bloquear] As [#]NmeroArchivo [Len=LongitudRegistro] RutaDeAcceso es el nombre del fichero, y opcionalmente su ruta completa
Por ejemplo Datos.txt o C:\Comercial\Datos.txt En el primer caso buscar en la carpeta definida por defecto; carpeta que podemos establecer o averiguar con las funciones CurDir y ChDir, tal y como explicamos en la entrega anterior. En el segundo lo har en la carpeta C:\Comercial. Si no existiera esa carpeta generara el error 76: No se ha encontrado la ruta de acceso.
Modo indica la forma como vamos a acceder al fichero. Este parmetro es obligatorio. Si no se indicara definira For Random. Se utiliza una de estas palabras clave: Append Binary Input Output Random
Aadir datos secuencialmente a partir del final. Acceso a ficheros binarios sin longitud fija. Acceso en modo lectura secuencial. Acceso en modo escritura secuencial. Acceso en modo aleatorio directo por nmero de registro.
Read Permite efectuar operaciones de lectura. Write Operaciones de escritura. Read Write Lectura y Escritura. Bloquear. Especifica las acciones permitidas para otros procesos concurrentes Shared
eduardo@olaz.net
Fichero compartido.
Eduardo Olaz
Entrega 18
18 - 3
Bloqueado para Lectura. Bloqueado para Escritura. Bloqueado para Lectura y Escritura.
NmeroArchivo. Parmetro obligatorio que especifica un nmero entero como manejador de archivo. Cuando se efectan operaciones de lectura y escritura hay que hacer mencin a este nmero. Su valor debe estar entre 1 y 511.
La funcin FreeFile, que veremos a continuacin, nos permite obtener un nmero libre para el manejador del archivo.
Funcin FreeFile
La Funcin FreeFile, nos devuelve un valor del tipo Integer que podremos utilizar con la instruccin Open para abrir, leer, escribir y cerrar ficheros. Su sintaxis es
FreeFile [(NmeroIntervalo)] NmeroIntervalo es un parmetro opcional que puede tomar el valor 0 por defecto y 1.
Si se pasa como parmetro 0, devuelve un nmero libre para el manejador de ficheros, en el rango de 1 a 255. Si se pasa el parmetro 1, el rango va de 256 a 511. El rango 256 a 511 se suele usar para ficheros que van a ser compartidos.
Instruccin Print #
La Instruccin Print #, graba los datos tal y como se mostraran, por ejemplo en la Ventana Inmediato, usando la sentencia Print. Su sintaxis es
Public Sub UsoDePrint() Dim lngFichero As Long Dim strFichero As String strFichero = CurrentProject.Path
Comencemos a programar con VBA - Access
18 - 4
' Definimos como carpeta actual la del fichero mdb ChDir (strFichero) strFichero = strFichero & "\Texto.txt" lngFichero = FreeFile ' Abrimos el fichero, como de Escritura _ y si no existe lo crea Open strFichero For Output As #lngFichero ' Escribimos en el fichero Print #lngFichero, "En un lugar de la Mancha, "; Print #lngFichero, "de cuyo nombre no quiero acordarme,"; Print #lngFichero, " no hace mucho que viva . . ." Close #lngFichero End Sub
Ejecute este procedimiento y abra el archivo, por ejemplo con el Bloc de Notas.
Como puede apreciar, las diferentes partes del texto han sido colocadas una detrs de la otra. Esto ha sido as, porque he colocado un punto y coma despus de cada texto.
Print# aade detrs de cada texto un Retorno de Carro VbCr y un Salto de Lnea vbLf,
salvo que encuentre un punto y coma, o una coma tras el texto a grabar. El conjunto vbCr y vbLf equivale a la constante vbCrLf. Si en lugar de poner un punto y coma, tras la expresin a grabar, pusiramos una coma,
Eduardo Olaz
Entrega 18
18 - 5
Ntese que deja un espacio de tabulacin entre cada carcter. Podemos apreciar que la primera sentencia Print #, deja un espacio en blanco delante de cada uno de los nmeros. En cambio la segunda, que graba los nmeros como caracteres, no lo hace. Con Print # se pueden usar, no slo tabuladores mediante comas, sino que podemos usar las funciones Tab y Spc. Los datos grabados con Print #, normalmente se leern con Line Input # Input #. Estas instrucciones las veremos ms adelante en esta misma entrega.
Funcin Tab
La Funcin Tab, marca el nmero de columna en el que se empezar a grabar el primer carcter de la lista de datos o la expresin a grabar mediante la instruccin Print # o el mtodo Print. Sintaxis: Tab[(n)]
Public Sub UsoDeTab() Dim lngFichero As Long Dim strFichero As String strFichero = CurrentProject.Path ChDir (strFichero) strFichero = strFichero & "\FicheroConTab.txt" lngFichero = FreeFile(1)
Comencemos a programar con VBA - Access
18 - 6
Open strFichero For Output As #lngFichero Print #lngFichero, Tab(1); "Nombre"; Tab(10); "Eduardo"; Print #lngFichero, Tab(25); "Apellido1"; Tab(35); "Olaz" Close #lngFichero End Sub
Como podemos apreciar los 4 datos se han grabado en las posiciones que hemos especificado: 1, 10, 25 y 35.
Funcin Spc
La Funcin Spc, especifica el nmero de espacios en blanco antes de grabar el primer carcter de la lista de datos o expresin, mediante Print # o el mtodo Print. Sintaxis: Spc(n) El parmetro n es obligatorio y marca el nmero de espacios.
Public Sub UsoDeSpc() Dim lngFichero As Long Dim strFichero As String strFichero = CurrentProject.Path ChDir (strFichero) strFichero = strFichero & "\FicheroConSpc.txt" lngFichero = FreeFile(1) Open strFichero For Output As #lngFichero Print #lngFichero, "Nombre"; Spc(3); "Eduardo"; Print #lngFichero, Spc(3); "Apellido1"; Spc(3); "Olaz" Close #lngFichero End Sub
Spc toma en cuenta la anchura de la lnea, si sta se ha definido mediante la instruccin Width #. Si no se ha definido esta anchura, Spc funciona de forma semejante a la funcin Space.
Para obtener ms informacin sobre estas funciones, consulte la ayuda de Access.
eduardo@olaz.net
Eduardo Olaz
Entrega 18
18 - 7
Instruccin Width #
La instruccin Width #, especifica el ancho de lnea de salida a un archivo abierto mediante la instruccin Open. Sintaxis: Width # NmeroArchivo, Ancho El parmetro Ancho es obligatorio y puede ser un nmero una expresin numrica cuyo resultado est entre 0 y 255. Si ejecutamos el siguiente cdigo:
Public Sub UsoDeWidth() Dim lngFichero As Long Dim i As Long Dim strTexto As String Dim strFichero As String Dim strCaracter As String * 1 strFichero = CurrentProject.Path ChDir (strFichero) strFichero = strFichero & "\FicheroConWidth.txt" strTexto ="Estos son los viajes de la nave Enterprise." _ & vbCrLf _ & "Misin durante los prximos aos: " _ & "Explorar nuevos mundos . . ." lngFichero = FreeFile(1) Open strFichero For Output As #lngFichero ' Establece el ancho de la lnea a 10. VBA.Width# lngFichero, 10 For i = 1 To Len(strTexto) ' Graba el texto carcter a carcter strCaracter = Mid$(strTexto, i, 1) Print #lngFichero, strCaracter; Next i Close #lngFichero End Sub
Obtendremos el siguiente resultado:
18 - 8
La utilizacin combinada de estos procedimientos, nos permite la construccin de diversos formatos de ficheros de texto y datos. Especialmente las funciones Tab, Spc y la instruccin Width, posibilitan la elaboracin de ficheros cuya caracterstica sea la definicin de campos de longitudes y posiciones establecidas, como ocurre por ejemplo con los ficheros para el intercambio de datos bancarios.
Instruccin Write #
La instruccin Write #, es semejante a Print#, y sirve tambin para escribir datos en un fichero secuencial. La diferencia fundamental con Print # es que Write #, escribe las cadenas marcndolas con el carcter Comilla doble como delimitador, adems separndolos con una coma, y los nmeros los escribe separndolos con comas. En el ejemplo con Print #, nos generaba el siguiente fichero:
Public Sub UsoDeWrite() Dim lngFichero As Long Dim strFichero As String strFichero = CurrentProject.Path ' Definimos como carpeta actual la del fichero mdb ChDir (strFichero) strFichero = strFichero & "\TextoConWrite.txt"
eduardo@olaz.net
Eduardo Olaz
Entrega 18
18 - 9
lngFichero = FreeFile ' Abrimos el fichero, como de Escritura _ y si no existe lo crea Open strFichero For Output As #lngFichero ' Escribimos en el fichero Write #lngFichero, 1, 2, 3, 4, 3.14 Write #lngFichero, "1", "2", "3", "4" Write #lngFichero, "E", "F", "G", "H" Write #lngFichero, Write #lngFichero, "En un lugar de la Mancha, " Write #lngFichero, "de cuyo nombre no quiero acordarme," Write #lngFichero, " no hace mucho que viva . . ." Write #lngFichero, " no hace mucho que viva . . ." Write #lngFichero, Date Write #lngFichero, True, False Close #lngFichero End Sub
El resultado ser:
Podemos observar que efectivamente nos crea un fichero con los elementos separados por comas. Otro aspecto interesante es que, tanto las fechas como los valores booleanos, los delimita con almohadillas #. Si hubiera que grabar un dato cuyo valor fuera Null, grabara #Null#. Un dato de Error, lo grabara con el siguiente formato #ERROR cdigoerror# Si hubiera que grabar un dato cuyo valor fuera Empty, no grabara nada en el fichero. Los datos grabados con Print #, normalmente se leern con Line Input # Input #. Estas instrucciones las veremos a continuacin.
18 - 10
Line Input #nmeroarchivo, VariableCadena VariableCadena es una variable de tipo String en la que se almacenarn los datos de
una lnea del fichero. Veamos cmo funciona todo esto:
Public Sub LecturaFichero() Dim lngFichero As Long Dim strFichero As String Dim strLinea As String Dim strCliente As String Dim datAperturaCuenta As Date Dim curSaldoCuenta As Currency strFichero = CurrentProject.Path ' Definimos como carpeta actual la del fichero mdb ChDir (strFichero) strFichero = strFichero & "\ClientesBanco.dat" lngFichero = FreeFile ' Abrimos el fichero, como de Escritura _ y si no existe lo crea Open strFichero For Output As #lngFichero ' Primero creamos el fichero ClientesBanco.dat _ que luego leeremos Write #lngFichero, _ "Pedro Rodrguez", #5/25/1983#, 100000.25 Write #lngFichero, _ "Juan Gorostiza", #12/30/2001#, 43235.37 Write #lngFichero, _ "Jon Estarriaga", #7/7/2004#, -105.85 Write #lngFichero, _ "Andrea Olazbal", #2/14/1995#, 128.36
eduardo@olaz.net
Eduardo Olaz
Entrega 18
18 - 11
Close #lngFichero ' Ahora ya tenemos el fichero creado ' Primero Vamos a leerlo con Line Input lngFichero = FreeFile ' Abrimos el fichero, como de lectura Open strFichero For Input As #lngFichero ' Leemos las lneas del mismo ' Realiza el bucle hasta el final del fichero. ' EOF indica que hemos llegado al final Debug.Print Debug.Print "Fichero " & strFichero Debug.Print Debug.Print "Datos ledos con Line Input #" Do While Not EOF(lngFichero) ' Lee la lnea y se la asigna a la variable String Line Input #lngFichero, strLinea Debug.Print strLinea Loop Close #lngFichero lngFichero = FreeFile ' Ahora vamos a leer el fichero con Input Open strFichero For Input As #lngFichero Debug.Print Debug.Print "Datos ledos con Input" ' Para ello deberemos asignrselos a sus variables Do While Not EOF(lngFichero) ' Lee la lnea y se la asigna a las variables Input #lngFichero, _ strCliente, datAperturaCuenta, curSaldoCuenta Debug.Print strCliente, _ datAperturaCuenta, _ curSaldoCuenta Loop Close #lngFichero End Sub
Al ejecutar este cdigo, nos mostrar en la venta [Inmediato] lo siguiente:
18 - 12
Fichero C:\Curso_VBA\Captulo_18\ClientesBanco.dat Datos ledos con Line Input # "Pedro Rodrguez",#1983-05-25#,100000.25 "Juan Gorostiza",#2001-12-30#,43235.37 "Jon Estarriaga",#2004-07-07#,-105.85 "Andrea Olazbal",#1995-02-14#,128.36 Datos ledos con Input Pedro Rodrguez Juan Gorostiza Jon Estarriaga Andrea Olazbal
En el cdigo y en el resultado, podemos apreciar la diferencia fundamental entre Line Input # e Input # la lectura de una lnea completa, en el primer caso, y de los datos individuales en el segundo. Lo que hemos visto hasta ahora se refiere a los llamados Ficheros de acceso Secuencial, en los que la informacin se va leyendo desde el inicio hasta el final, de manera secuencial. Ya desde los tiempos de BASICA, GWBasic QBasic, existan los llamados Ficheros de acceso aleatorio, en los que se poda grabar o leer una informacin en una posicin concreta. Aunque est considerado como un formato obsoleto, vamos a analizarlo por encima.
Open "Fichero" For Random As #Numero Len = TamaoRegistro Random especifica que se va a abrir el fichero en modo de acceso Aleatorio (modo por defecto de Open si no se especifica otro modo). Lan indica el tamao del registro.
Para grabar los datos en un fichero de este tipo, debemos indicarle a VBA en qu posicin vamos a grabarlos. Si no indicamos una posicin de registro, escribir al final del fichero. Para leer los datos de un fichero da acceso aleatorio se utiliza la instruccin Get; para escribir se usa la instruccin Put.
eduardo@olaz.net
Eduardo Olaz
Entrega 18
18 - 13
Instruccin Get
La instruccin Get, lee datos de un archivo abierto y asigna esos datos a una variable. Su sintaxis es:
Get #NmeroArchivo,[NumeroRegistro],Variable
La variable suele ser una variable Type de tipo registro.
Open "Clientes.dat" For Random As #lngFichero _ Len = Len(Datos) Get #lngFichero, 2, Datos Close #lngFichero
Tras esta instruccin, se habrn pasado los datos del registro N 2 a la variable Datos. El numero de registro es opcional
Instruccin Put
La instruccin Put, escribe datos en un archivo abierto , volcndolos desde una variable. Su sintaxis es:
Put #NmeroArchivo,[NumeroRegistro],Variable
La variable suele ser una variable Type de tipo registro. Si quisiramos grabar los datos contenidos en la variable Datos, en el registro N 2 de la tabla Clientes.dat, podramos hacerlo as:
Open "Clientes.dat" For Random As #lngFichero _ Len = Len(Datos) Put #lngFichero, 2, Datos Close #lngFichero
El procedimiento PruebaFicheroRandom graba 5 registros en Clientes.dat y luego los lee, mostrando los datos en la ventana de depuracin:
Public Type ClienteBanco Nombre As String * 15 Apellido1 As String * 15 Apellido2 As String * 15 Cuenta As Long Saldo As Currency End Type Public Sub PruebaFicheroRandom() GrabaFicheroRandom LeeFicheroRandom End Sub
Comencemos a programar con VBA - Access
18 - 14
Public Sub GrabaFicheroRandom() Dim Cliente As ClienteBanco Dim i As Long Dim strNumero As String ' Rnd genera un nmero aleatorio entre 0 y 0.999999... ' Randomize Timer inicializa cada vez una secuencia _ Diferente con Rnd Randomize Timer For i = 1 To 5 strNumero = "_" & Format(i, "000") With Cliente .Nombre = "Nombre" & strNumero .Apellido1 = "Apellido1" & strNumero .Apellido2 = "Apellido2" & strNumero .Cuenta = Format(100000 * Rnd, "00000") .Saldo = Int(1000000 * Rnd) / 100 End With GrabaCliente Cliente, i Next i End Sub Public Sub LeeFicheroRandom() Dim Cliente As ClienteBanco Dim i As Long For i = 1 To 5 LeeCliente Cliente, i Debug.Print With Cliente Debug.Print "Cliente " _ & CStr(i) & ": " _ & Trim(.Nombre) & " " _ & Trim(.Apellido1) & " " _ & Trim(.Apellido2) Debug.Print "Cuenta " & .Cuenta Debug.Print "Saldo " & Format(.Saldo, "#,##0.00 ") End With Next i End Sub Public Sub GrabaCliente( _
eduardo@olaz.net
Eduardo Olaz
Entrega 18
18 - 15
Datos As ClienteBanco, _ Optional ByVal Posicion As Long = -1) On Error GoTo HayError Dim lngFichero As Long Dim strFichero As String lngFichero = FreeFile Open "Clientes.dat" For Random As #lngFichero _ Len = Len(Datos) 'Grabamos los datos If Posicion >= 0 Then Put #lngFichero, Posicion, Datos Else Put #lngFichero, , Datos End If Close #lngFichero Salir: Exit Sub HayError: MsgBox "Error al grabar " & strFichero _ & vbCrLf _ & Err.Description Resume Salir End Sub Public Sub LeeCliente( _ Datos As ClienteBanco, _ Optional ByVal Posicion As Long = -1) On Error GoTo HayError Dim lngFichero As Long Dim strFichero As String lngFichero = FreeFile Open "Clientes.dat" For Random As #lngFichero Len = Len(Datos) 'Leemos los datos If Posicion >= 0 Then Get #lngFichero, Posicion, Datos Else Get #lngFichero, , Datos
Comencemos a programar con VBA - Access
18 - 16
End If Close #lngFichero Salir: Exit Sub HayError: MsgBox "Error al leer " & strFichero _ & vbCrLf _ & Err.Description Resume Salir End Sub
Tras ejecutarse este cdigo, mostrar algo as como:
Cliente 1: Nombre_001 Apellido1_001 Apellido2_001 Cuenta 73549 Saldo 5.571,35 Cliente 2: Nombre_002 Apellido1_002 Apellido2_002 Cuenta 41781 Saldo 2.964,22 Cliente 3: Nombre_003 Apellido1_003 Apellido2_003 Cuenta 96618 Saldo 9.775,93 Cliente 4: Nombre_004 Apellido1_004 Apellido2_004 Cuenta 6879 Saldo 8.564,24 Cliente 5: Nombre_005 Apellido1_005 Apellido2_005 Cuenta 99736 Saldo 5,18
Funcin Seek
La instruccin Seek, devuelve un valor del tipo Long que nos indica el nmero de registro de la posicin actual dentro de un fichero abierto. Su sintaxis es:
Seek(#NmeroArchivo)
eduardo@olaz.net
Eduardo Olaz
Entrega 18
18 - 17
Por ejemplo. Tras ejecutarse este cdigo, Seek devolver el valor 4. En el caso de los ficheros de acceso Random, Seek devuelve la posicin siguiente al ltimo registro al que se ha accedido para lectura o escritura.
Public Sub PruebaSeek() Dim lngFichero As Long Dim Datos As ClienteBanco lngFichero = FreeFile Open "Clientes.dat" For Random As #lngFichero _ Len = Len(Datos) Get #lngFichero, 2, Datos Debug.Print "La posicin actual es el registro " _ & CStr(Seek(lngFichero)) Close #lngFichero End Sub
El resultado en la ventana de depuracin ser:
18 - 18
eduardo@olaz.net
Eduardo Olaz
VBA - Access
Entrega
19
Eduardo Olaz
19 - 2
Fichero Schema.ini.
Este archivo es un archivo especial, que contiene una descripcin de la estructura, de un fichero de texto organizado como una tabla de datos, proporcionando informacin sobre: El formato que tiene el archivo. Los nombres, la longitud y tipos los tipos de datos de los campos. El juego de caracteres que se ha utilizado. Informacin sobre las conversiones especiales de los tipos de datos.
El fichero Schema.ini debe encontrarse en la misma carpeta donde estar ubicado el fichero de datos. La informacin contenida en un fichero Schema.ini, sobre los formatos de datos, tiene prioridad frente a la configuracin predefinida en el registro de Windows. Para que VBA pueda manejarlo de forma adecuada, el nombre de este fichero debe mantenerse, por lo que deber usarse siempre como Schema.ini. Como veremos, Schema.ini es un tipo especial de los ficheros ini de configuracin. El tipo genrico de ficheros ini ser analizado en un punto ms avanzado de esta misma entrega.
eduardo@olaz.net
Eduardo Olaz
Entrega 19
19 - 3
[Alumnos.txt] ColNameHeader=False Format=FixedLength MaxScanRows=25 CharacterSet=Oem Col1= idAlumno Integer Width 6 Col2= Nombre Char Width 15 Col3= Apellido1 Char Width 15 Col4= Apellido2 Char Width 15 Col5= FechaNacimiento Date Width 11 Col6= Poblacion Char Width 15 Col7= idCurso Integer Width 5
Grabamos el fichero con el nombre Schema.ini en la misma carpeta que la base de datos. Antes que nada veamos cmo se puede, por cdigo, exportar datos de una tabla a un fichero de texto. Voy a usar los mtodos de la librera DAO (que veremos ms adelante). Se supone que la tabla a exportar est en una base de datos llamada Datos.mdb, en la tabla Alumnos y que contiene los campos descritos. Para poder usar DAO, si no est activada, activamos la referencia a su Librera. Para ello usaremos, desde el editor de cdigo, la opcin Referenciasdel men Herramientas. Y en concreto la librera Microsoft DAO N.M Object Library. Tras esto escribimos
Public Sub ExportacionConDAO() Dim db As DAO.Database Dim strSQL As String Dim strcarpeta As String Dim strBaseDatos As String
Comencemos a programar con VBA - Access
19 - 4
Dim strTabla As String Dim strFichero_txt As String Dim n As Long ' Nombres de los elementos _ intervinientes en el proceso strcarpeta = CurrentProject.Path strBaseDatos = strcarpeta & "\Datos.mdb" strTabla = "Alumnos" strFichero_txt = strTabla & ".txt" ' Abrimos la base de datos Set db = OpenDatabase(strBaseDatos) ' Si existe la tabla, la borramos If Dir(strcarpeta & "\" & strFichero_txt) = strFichero_txt Then Kill strcarpeta & "\" & strFichero_txt End If ' definimos la consulta de accin strSQL = "SELECT * " _ & "INTO [TEXT;DATABASE=" _ & strcarpeta _ & "]." _ & strFichero_txt _ & " FROM " _ & strTabla ' Hacemos que se ejecute la consulta de accin db.Execute strSQL ' Cerramos la Base de Datos db.Close ' Eliminamos la referencia a la Base de datos Set db = Nothing End Sub
No se preocupe demasiado si no entiende ese cdigo. Lo que hace es exportar el contenido de la tabla Alumnos de la base de datos Datos.mdb a la tabla Alumnos.txt. Veamos ahora qu significa cada una de las lneas del fichero Schema.ini.
eduardo@olaz.net
Eduardo Olaz
Entrega 19
19 - 5
Ya hemos indicado que la primera lnea, [Alumnos.txt] indica el nombre del fichero al que se va a exportar. La segunda lnea ColNameHeader=False indica que la tabla no va a contener los nombres de los campos. Si hubiramos puesto: ColNameHeader=True, el resultado de exportar la tabla sera:
"idAlumno","Nombre","Apellido1","Apellido2","FechaNacimiento","Poblacion","idCurso" 1 2 3 4 Antonio Pedro Marta Enrique Martnez Iturralde Valduz Viana Ziga Etxegrate Goikoa Vergara 02/10/1984 Pamplona 03/01/1985 Tudela 27/07/1985 Tafalla 03/09/1984 Pamplona 35 14 10 10
La tercera lnea Format= FixedLength hace que los datos se exporten con un formato de longitud variable. Los posibles valores que puede tomar esta clave son:
Los campos se delimitan mediante el carcter Tab Utiliza comas como separador de campos Hace que los campos se delimiten por asteriscos. Se puede utilizar cualquier carcter, excepto las comillas dobles ("). Por ejemplo (;) hara que el delimitador fuera un punto y coma.
FixedLength
En Espaa, al usar en la configuracin regional la coma como separador decimal, si tambin la usramos como separador de campos, nos generara el error 3441, por esa coincidencia entre el separador de campos y el separador decimal.
Por ello sera aconsejable, en su lugar, la utilizacin del punto y coma Format= Delimited (;)como delimitador de campos. La cuarta lnea MaxScanRows=25 le indica al motor de la base de datos, el nmero de filas que debe examinar en la tabla, para determinar el tipo de dato que contiene cada campo. Si ponemos MaxScanRows=0 le estamos pidiendo que examine toda la tabla. La quinta lnea CharacterSet=OEM define el conjunto de caracteres que se va a utilizar. Esto es importante, dependiendo de sobre qu plataforma se va a exportar.
Comencemos a programar con VBA - Access
19 - 6
Los posibles valores para CharacterSet son Ansi, Oem y Unicode. En lugar de las palabras Ansi / Oem / Unicode, se puede usar unos valores numricos que las sustituyen. Estos valores son 1252 para el juego de caracteres Ansi, 1200 para Unicode, y 850 para el Oem. El valor predeterminado existente en el registro de Windows para una configuracin regional de Espaol es el valor 1252 (Ansi).. El usar la opcin Oem nos permite exportar la tabla sin problemas a un fichero que vaya a ser trabajado con herramientas del tipo MSDos. Por ejemplo, si abrimos el fichero desde el emulador de Dos, con el antiguo editor de textos Edit nos mostrar lo siguiente:
Si abriramos el fichero con un editor Windows, por ejemplo con el Bloc de Notas, veramos el fichero de forma incorrecta.
Vamos que las vocales acentuadas se ven de forma incorrecta, lo mismo que el carcter . Si abrimos ahora el mismo fichero con el Bloc de Notas, lo veramos de forma correcta.
1 2 3 4 Antonio Pedro Marta Enrique Martnez Iturralde Valduz Viana Ziga Etxegrate Goikoa Vergara 02/10/1984Pamplona 03/01/1985Tudela 27/07/1985Tafalla 03/09/1984Pamplona 35 14 10 10
La opcin para CharacterSet Unicode, permitira exportar tablas con caracteres internacionales, ubicados por encima del carcter ASCII N 255, por ejemplo los caracteres chinos.
eduardo@olaz.net
Eduardo Olaz
Entrega 19
19 - 7
Desde la versin 2000 Access, puede trabajar con caracteres Unicode, que necesitan 2 Bytes para el almacenamiento de cada carcter. Esta es la razn por la que, si no se usa la opcin Compresin Unicode en el diseo de las tablas, stas llegan a ocupar casi el doble de espacio que con las tablas Access de la versin 97 anteriores. Las siguientes lneas empiezan con la slaba Col seguida de un nmero.
Vemos que, adems del delimitador de campos, los campos tipo texto estn contenidos entre comillas. En cambio ni los valores numricos ni los del tipo Fecha/Hora, lo hacen. Las posibilidades de los ficheros Eschema.ini, llegan mucho ms lejos que lo expuesto hasta ahora. Por ejemplo hay todo un juego de posibilidades para dar formato a los campos, por ejemplo para mostrar los nmeros con decimales las fechas con un formato especfico. En la exportacin anterior vemos que la fecha de nacimiento la ha exportado con el formato
19 - 8
DateTimeFormat = dd/mm/yyyy
Si cambiamos el fichero anterior por
Vemos que ahora la fecha de nacimiento se muestra sin la hora. Para ver las opciones de formato que afectan al smbolo monetario y al formato de nmeros decimales, voy a aadir a la tabla original dos nuevos campos: Altura del tipo Single (Simple) e ImportePagado del tipo Currency (Moneda). El nuevo fichero Schema.ini que vamos a usar ser el siguiente:
eduardo@olaz.net
Eduardo Olaz
Entrega 19
19 - 9
A la hora de definir las columnas, un fichero con campos de anchura fija, se debe especificar el tipo de campo y la anchura que debe mostrar. Los tipos de campo que podemos usar son:
Byte, Bit Currency DateTime, Date Double, Float Long Memo, LongChar Short, Integer Single Text, Char
0 1 2 3 4 5 6 7
8 9 10 11 12 13 14 15
0 1
128,60 128,60
2 3
128,60 128,60
19 - 10
CurrencyThousandSymbol=, devolvera el formato 1,253.35 DecimalSymbol define el carcter para el separador de decimales. NumberLeadingZeros es un valor Boleano que define si los valores numricos menores que 1 y mayores que -1, deben llevar un cero a la izquierda. NumberLeadingZeros=False hara que 0,123 se representara como ,123
Si definiramos el fichero Schema.ini como
[Alumnos.txt] ColNameHeader=False Format=FixedLength MaxScanRows=25 CharacterSet=1252 CurrencySymbol= CurrencyDecimalSymbol=. CurrencyNegFormat=15 NumberDigits=2 Col1= idAlumno Integer Width 6 Col2= FechaNacimiento Date Width 11 Col3= Poblacion Char Width 15 Col4= idCurso Integer Width 5 Col5= ImportePagado Currency Width 11 Col6= Altura Single Width 5
Obtendremos este resultado
eduardo@olaz.net
Eduardo Olaz
Entrega 19
19 - 11
He aadido nuevos datos a la tabla original para comprobar los efectos del formato.
[Alumnos.txt] ColNameHeader=True Format=Delimited(;) MaxScanRows=0 CharacterSet=Ansi [Profesores.txt] ColNameHeader=True Format=Delimited(;) MaxScanRows=0 CharacterSet=Ansi
Sub AbrirFicheroDeTextoDAO() Dim db As DAO.Database Dim rst As DAO.Recordset Dim strcarpeta As String Dim strBaseDatos As String Dim strTabla As String Dim m As Long, n As Long strcarpeta = CurrentProject.Path strBaseDatos = strcarpeta & "\Datos.mdb" strTabla = "Alumnos.txt" ' Hacemos que la carpeta donde est el fichero _ la trate como una base de datos (DAO) Set db = OpenDatabase(strcarpeta, _
Comencemos a programar con VBA - Access
19 - 12
False, _ False, _ "Text;") ' Carga el recordset desde la tabla de texto Set rst = db.OpenRecordset(strTabla) With rst ' Mostramos los registros Do While Not .EOF For m = 0 To .Fields.Count - 1 ' Mostramos los campos Debug.Print .Fields(m), Next m Debug.Print ' Vamos al siguiente registro .MoveNext Loop End With rst.Close Set rst = Nothing db.Close Set db = Nothing End Sub
En mi ordenador el cdigo anterior imprime lo siguiente:
6 1 2 3 4 5 25/05/1983 02/10/1984 03/01/1985 27/07/1985 03/09/1984 24/12/1983 Huarte Pamplona Tudela Tafalla Pamplona Villava 5 35 14 10 10 1 1110 256,5 128,25 250 110 -1253,35 0,97 1,66 1,85 1,75 1,8 1,55
Este cdigo utiliza la librera de DAO. Os recuerdo lo comentado en la pgina 3 sobre activar la referencia a la librera correspondiente. Podemos hacerlo de forma semejante usando la librera de ADO en vez de la de DAO.
Sub AbrirFicheroDeTextoADO() Dim cnn As ADODB.Connection Dim rst As ADODB.Recordset Dim strcarpeta As String Dim strTabla As String Dim m As Long, n As Long strcarpeta = CurrentProject.Path strTabla = "Alumnos.txt"
eduardo@olaz.net
Eduardo Olaz
Entrega 19
19 - 13
' Creamos una nueva conexin (ADO) Set cnn = New ADODB.Connection ' Asignamos los parmetros de la conexin _ Incluida la carpeta donde est el fichero. With cnn .Provider = "Microsoft.Jet.OLEDB.4.0" .ConnectionString = "Data Source=" & strcarpeta .Properties("Extended Properties") = "TEXT;HDR=Yes" .Open End With ' Creamos un nuevo objeto Recordset Set rst = New ADODB.Recordset ' Abrimos el Recordset, _ asignndole la tabla de la carpeta rst.Open strTabla, cnn, , , adCmdTable With rst ' Mostramos los registros Do While Not .EOF ' Recorremos la coleccin de campos For m = 0 To .Fields.Count - 1 ' Mostramos los campos Debug.Print .Fields(m), Next m Debug.Print ' Vamos al siguiente registro .MoveNext Loop End With rst.Close Set rst = Nothing cnn.Close Set cnn = Nothing End Sub
El resultado es exactamente el mismo que con el procedimiento DAO. Nota: La utilizacin de la librera DAO y la ms moderna ADO, con sus mtodos y propiedades ms usuales las veremos en sus correspondientes entregas posteriores. No se preocupe si no termina de entender este cdigo. En entregas posteriores lo veremos.
Comencemos a programar con VBA - Access
19 - 14
Tome el cdigo como referencia cuando ya conozca estos objetos, o como plantilla si necesita cargar datos, por cdigo, desde un fichero de texto.
Ficheros ini
En la poca del Windows de 16 bits, (Windows 3.x) los programadores, para establecer una serie de valores de inicializacin, utilizbamos ficheros ini. Un fichero ini, del que el fichero Schema.ini es un caso particular, consta de una serie de secciones encabezadas por su nombre, entre parntesis cuadrados (corchetes), y seguidas de una serie de claves, con sus valores asignados. Recordemos un fichero .
Clave Clave
Otro ejemplo:
eduardo@olaz.net
Eduardo Olaz
Entrega 19
19 - 15
Su nombre API viene de Application Programming Interface , lo que en cristiano viene a significar algo as como Interfaz para la Programacin de Aplicaciones. Estas funciones son externas a los lenguajes de programacin, como VBA, VB.net, C++, C#, Pascal, etc Devuelven un valor y admiten parmetros, que al pasarlos, permiten obtener una serie de valores. Adems pueden ejecutar diversas acciones, en nuestro caso leer o escribir ficheros ini. Para poder usar una funcin API, primero debemos declararla Como ejemplo, vamos a usar la clsica funcin API que nos devuelve el nombre de la carpeta donde est instalado Windows. Es la funcin GetWindowsDirectory Para poder usar esta funcin API, desde VBA primero debemos declararla con la instruccin Declare. En esa instruccin se debe poner El nombre de la funcin La librera de Windows donde est definida El nombre de su Alias, o nombre nativo de la funcin en la librera. Los nombres de los parmetros y su tipo El tipo de valor devuelto por ella
Declare Function GetWindowsDirectory _ Lib "kernel32.dll" _ Alias "GetWindowsDirectoryA" ( _ ByVal lpBuffer As String, _ ByVal nSize As Long) _ As Long
Podemos cambiar a nuestra discrecin tanto el nombre de la funcin como el de los parmetros que utiliza, siempre que respetemos el orden y el tipo de de los mismos y el nombre del Alias de la funcin. Por ejemplo, la siguiente declaracin sera equivalente a la primera:
Declare Function DameNombreCarpetaWindows _ Lib "kernel32.dll" _ Alias "GetWindowsDirectoryA" ( _ ByVal ContenedorNombre As String, _ ByVal TamaoContenedor As Long) _ As Long
Aunque puede hacerse, y de hecho parece una declaracin ms clara, es algo que no se suele hacer. Microsoft tiene ya definidas una serie de declaraciones estndar para las funciones API, que tratan de respetar la mayora de los programadores. Ya hablaremos de este tema en una entrega posterior. En este caso, el valor devuelto por la funcin, ser la longitud de la cadena que contiene el nombre de la ruta completa de la carpeta de Windows. Una vez declarada la funcin podemos utilizarla sin problemas como si fuera una funcin desarrollada por nosotros mismos, o propia de VBA. El nombre que utilizaremos para llamar a la funcin es el que aparece detrs de Declare
Comencemos a programar con VBA - Access
19 - 16
Veamos el cdigo que utilizar la funcin. He creado la funcin DameCarpetaWindows que devuelve una cadena con el nombre de la carpeta donde est instalado Windows Este es su cdigo:
Public Declare Function GetWindowsDirectory _ Lib "kernel32.dll" _ Alias "GetWindowsDirectoryA" ( _ ByVal lpBuffer As String, _ ByVal nSize As Long) _ As Long Public Function DameCarpetaWindows() As String 'Devuelve la carpeta de Windows Const conlngCadena As Long = 255 Dim strDirectorio As String * conlngCadena Dim lngDirectorio As Long Dim lngPath As Long lngDirectorio = conlngCadena lngPath = GetWindowsDirectory( _ strDirectorio, _ lngDirectorio) DameCarpetaWindows = Left$( _ strDirectorio, _ lngPath) End Function
A la funcin GetWindowsDirectory le pasamos la cadena strDirectorio y se almacenar en ella la ruta de la carpeta de Windows. La variable lngPath recoge la longitud de la cadena obtenida, por lo que si hacemos
Left$(strDirectorio, lngPath)
Obtendremos la ruta completa de Windows. Si en mi ordenador escribo Debug.Print DameCarpetaWindows() Me mostrar C:\WINDOWS, que es donde efectivamente he instalado Windows en mi PC.
Eduardo Olaz
Entrega 19
19 - 17
http://www.ciberteca.net/articulos/programacion/win32apis/ejemplo.asp http://www.mentalis.org/index2.shtml La muy recomendable pgina de AllApi.net. http://mech.math.msu.su/~vfnik/WinApi/index.html Otra pgina clsica muy interesante. http://custom.programming-in.net/ http://www.mvps.org/access/api/ http://vbnet.mvps.org/index.html?code/fileapi/
Option Explicit ' GetPrivateProfileString _ Recupera una cadena en un fichero de inicializacin Declare Function GetPrivateProfileString _ Lib "kernel32.dll" _ Alias "GetPrivateProfileStringA" ( _ ByVal lpApplicationName As String, _ ByVal lpKeyName As String, _ ByVal lpDefault As String, _ ByVal lpReturnedString As String, _ ByVal nSize As Long, _ ByVal lpFileName As String _ ) As Long ' lpApplicationName _ Seccin en la que se va a buscar la Clave. ' lpKeyName _ Nombre de la clave a buscar. ' lpDefault _ Valor por defecto a devolver _ si no se encuentra la clave. ' lpReturnedString _ Cadena en la que escribir el resultado. _ Debe tener como mnimo la longitud de nSize. ' lpFileName _ Nombre del archivo de inicializacin.
19 - 18
' GetPrivateProfileInt _ Recupera un entero de un fichero de inicializacin Declare Function GetPrivateProfileInt _ Lib "kernel32.dll" _ Alias "GetPrivateProfileIntA" ( _ ByVal lpApplicationName As String, _ ByVal lpKeyName As String, _ ByVal nDefault As Long, _ ByVal lpFileName As String _ ) As Long ' nDefault _ Valor por defecto a devolver _ si no se encuentra la clave. ' GetPrivateProfileSection _ Recupera una lista con los nombres de todas las claves _ de una Seccin y sus valores. Declare Function GetPrivateProfileSection _ Lib "kernel32" _ Alias "GetPrivateProfileSectionA" ( _ ByVal lpApplicationName As String, _ ByVal lpReturnedString As String, _ ByVal nSize As Long, _ ByVal lpFileName As String _ ) As Long ' lpReturnedString _ Lista en la que se cargan las claves y sus valores. _ Cada cadena est separada por un valor Nulo _ Al final de la lista se encontrarn dos valores nulos. ' nSize _ Tamao asignado a lpReturnedString Declare Function WritePrivateProfileString _ Lib "kernel32.dll" _ Alias "WritePrivateProfileStringA" ( _ ByVal lpApplicationName As String, _ ByVal lpKeyName As String, _ ByVal lpString As String, _ ByVal lpFileName As String _ ) As Long ' lpString _
eduardo@olaz.net
Eduardo Olaz
Entrega 19
19 - 19
Valor a escribir en una clave. _ Si lo que se quiere es borrar la clave _ Pasar el valor vbNullString Declare Function WritePrivateProfileSection _ Lib "kernel32" _ Alias "WritePrivateProfileSectionA" ( _ ByVal lpApplicationName As String, _ ByVal lpString As String, _ ByVal lpFileName As String _ ) As Long ' lpApplicationName _ Seccin en la que vamos a escribir las Claves y Valores ' lpString _ Lista en la que se pasan las claves y sus Valores. _ Como en el caso de lpReturnedString _ cada cadena est separada por un valor Nulo _ Al final de la lista se pondrn dos valores nulos. Declare Function GetPrivateProfileSectionNames _ Lib "kernel32" _ Alias "GetPrivateProfileSectionNamesA" ( _ ByVal lpReturnedString As String, _ ByVal nSize As Long, _ ByVal lpFileName As String _ ) As Long ' lpReturnedString contiene la lista de las Secciones _ cada cadena est separada por un valor Nulo _ Al final de la lista se pondrn dos valores nulos.
19 - 20
ByVal Clave As String, _ ByVal Valor As String, _ Optional ByVal Carpeta As String) Dim lngRetornado As Long ' Se hace un pequeo control _ La Carpeta pasada Carpeta = CarpetaValida(Carpeta) ' Si la carpeta no existiera se generara un error ' Validamos tambin el fichero ' Si el fichero no existiera generara un error Fichero = FicheroValido(Fichero, Carpeta) ' Llamamos ahora a la funcin API ' WritePrivateProfileString _ que hemos declarado anteriormente. _ El valor de retorno de la funcin _ se lo asignamos a lngRetornado lngRetornado = WritePrivateProfileString( _ Seccion, _ Clave, _ Valor, _ Carpeta & FicheroINI) End Sub
Este es el cdigo de la funcin CarpetaValida, que vamos a usar en varios procedimientos, como una comprobacin muy primaria de la carpeta pasada:
Public Function CarpetaValida( _ Optional ByVal Carpeta As String = "" _ ) As String On Error GoTo HayError ' Esta funcin comprueba la carpeta pasada como _ Parmetro. Carpeta = Trim(Carpeta) ' Quita blancos de los extremos ' Si no se pasa nada, devuelve la de la aplicacin. If Carpeta = "" Then Carpeta = CurrentProject.Path End If ' Si el nombre de la carpeta no acaba con "\" lo pone
eduardo@olaz.net
Eduardo Olaz
Entrega 19
19 - 21
If Right(Carpeta, 1) <> "\" Then Carpeta = Carpeta & "\" End If ' Si no existe la carpeta genera un error' If Len(Dir(Carpeta, vbDirectory)) = 0 Then Err.Raise 1000, _ "CarpetaValida", _ "No existe la Carpeta " _ & Carpeta End If Salida: CarpetaValida = Carpeta Exit Function HayError: If Err.Number = 1000 Then MsgBox "No existe la carpeta " _ & Carpeta, _ vbCritical + vbOKOnly, _ " Error en la funcin " & Err.Source Else MsgBox "Se ha producido el error " _ & Format(Err.Number, "#,##0") _ & vbCrLf _ & Err.Description, _ vbCritical + vbOKOnly, _ " Error no controlado en la funcin " _ & "CarpetaValida()" End If Resume Salida End Function
De una forma similar a como hemos creado la funcin CarpetaValida, vamos a crear la funcin FicheroValido, que comprobar si existe el fichero.
19 - 22
Optional ByVal Carpeta As String = "" _ ) As String On Error GoTo HayError ' Esta funcin comprueba la existencia del fichero _ pasado como Parmetro. Carpeta = Trim(Carpeta) ' Quita blancos de los extremos ' Si no se pasa nada, devuelve la de la aplicacin. If Carpeta = "" Then Carpeta = CurrentProject.Path End If ' Si el nombre de la carpeta no acaba con "\" lo pone If Right(Carpeta, 1) <> "\" Then Carpeta = Carpeta & "\" End If ' Si no existe el fichero genera un error' If Len(Dir(Carpeta & Fichero)) = 0 Then Err.Raise 1100, _ "FicheroValido", _ "No existe el fichero " _ & Fichero End If Salida: FicheroValido = Fichero Exit Function HayError: If Err.Number = 1100 Then MsgBox "No existe el fichero " _ & Carpeta & Fichero, _ vbCritical + vbOKOnly, _ " Error en la funcin " & Err.Source Else MsgBox "Se ha producido el error " _ & Format(Err.Number, "#,##0") _ & vbCrLf _
eduardo@olaz.net
Eduardo Olaz
Entrega 19
19 - 23
& Err.Description, _ vbCritical + vbOKOnly, _ " Error no controlado en la funcin " _ & "FicheroValido()" End If Resume Salida End Function
Vamos a probar el procedimiento, para lo que en la ventana de depuracin escribimos:
Si abrimos el fichero con el Bloc de notas veremos que contiene lo que en principio esperbamos:
Si volvemos a efectuar la misma llamada, podremos comprobar que se actualiza la hora. Vamos a llamarlo ahora con estos parmetros:
EscribeClaveINI "Prueba.ini", "Usuario", "Nombre", "Eduardo" EscribeClaveINI "Prueba.ini", "Usuario", "Depto", "Software"
Si lo editamos con el bloc de notas, veremos que Prueba.ini ha cambiado. De hecho se ha actualizado la hora, se ha aadido la seccin Usuario que contiene las claves Nombre, con mi nombre, y Depto con el valor Software.
19 - 24
Cmo podramos borrar una clave por cdigo? Muy sencillo, pasndole la cadena nula representada por la constante vbNullString Si hacemos
Public Function LeeCadenaClaveINI( _ ByVal FicheroINI As String, _ ByVal Seccion As String, _ ByVal Clave As String, _ Optional ByVal Carpeta As String, _ Optional ByVal ValorDefecto As String _ ) As String Const conLongitud As Long = 255 Dim lngRetornado As Long Dim strDevuelto As String Carpeta = CarpetaValida(Carpeta) FicheroINI= FicheroValido(FicheroINI, Carpeta) ' Creamos la cadena contenedora con 255 caracteres * _ Esta cadena la pasaremos como cadena que contendr _ el valor ledo. strDevuelto = String(conLongitud, "*") ' Llamamos ahora a la funcin API lngRetornado = GetPrivateProfileString( _
eduardo@olaz.net
Eduardo Olaz
Entrega 19
19 - 25
Seccion, _ Clave, _ ValorDefecto, _ strDevuelto, _ Len(strDevuelto), _ Carpeta & FicheroINI) ' Devolvemos el dato ' El nmero de caracteres ledos estar en lngRetornado ' La cadena leda en strDevuelto LeeCadenaClaveINI = Left(strDevuelto, lngRetornado) End Function
Con el cdigo anterior, si escribimos la sentencia
19 - 26
ByVal Clave As String, _ Optional ByVal Carpeta As String, _ Optional ByVal ValorDefecto As Long _ ) As Long Carpeta = CarpetaValida(Carpeta) FicheroINI= FicheroValido(FicheroINI, Carpeta) ' Invocamos directamente la funcin API _ GetPrivateProfileInt LeeEnteroClaveINI = GetPrivateProfileInt( _ Seccion, _ Clave, _ ValorDefecto, _ Carpeta & FicheroINI) End Function
Si llamamos a la funcin mediante:
Public Function SeccionesINI( _ ByVal FicheroINI As String, _ Optional ByVal Carpeta As String _ ) As Variant On Error GoTo HayError Dim lngLongitud As Long Dim lngDevuelto As Long
eduardo@olaz.net
Eduardo Olaz
Entrega 19
19 - 27
Dim strContenedor As String Dim strSecciones As String Dim aSecciones As Variant On Error GoTo HayError ' lngLongitud ser la longitud de strContenedor _ que recoger los nombres de las secciones lngLongitud = 2 ^ 12 strContenedor = String(lngLongitud, "*") Carpeta = CarpetaValida(Carpeta) FicheroINI = FicheroValido(FicheroINI, Carpeta) lngDevuelto = GetPrivateProfileSectionNames( _ strContenedor, _ Len(strContenedor), _ Carpeta & FicheroINI) ' Si el valor devuelto fuera 0 sera porque _ no ha encontrado secciones en el fichero _ o no ha encontrado el fichero If Not CBool(lngDevuelto) Then ' Preparamos un array sin datos ReDim aSecciones(0 To 0) aSecciones(0) = "" Else strSecciones = Left(strContenedor, lngDevuelto - 1) ' El separador es el carcter Chr$(0) ' Split devuelve un Array _ desde una cadena con separadores aSecciones = Split(strSecciones, Chr$(0)) End If ' Devuelve el array SeccionesINI = aSecciones Salida: SeccionesINI = aSecciones Exit Function HayError: MsgBox "Se ha producido el error " _ & Format(Err.Number, "#,##0") _ & vbCrLf _
Comencemos a programar con VBA - Access
19 - 28
& Err.Description, _ vbCritical + vbOKOnly, _ " Error no controlado en la funcin " _ & "SeccionesINI()" ReDim aSecciones(0 To 0) aSecciones(0) = "" Resume Salida End Function
Si efectuamos una llamada a la funcin mediante SeccionesINI("Prueba.ini")(0) en mi ordenador, considerando que antes hemos creado esas secciones, devuelve el nombre de la seccin "Usuario" Si Hacemos SeccionesINI("Prueba.ini")(1), devuelve "Mi Seccin" Fijmonos en que la funcin SeccionesINI devuelve un Array, y por tanto se le puede llamar pasndole, adems de los parmetros propios de la misma, el ndice del Array devuelto. Podemos generar un procedimiento que muestre los nombres de las secciones de un fichero del tipo ini.
Public Sub MuestraSeccionesIni( _ ByVal FicheroINI As String, _ Optional ByVal Carpeta As String) Dim aSecciones As Variant Dim i As Long Carpeta = CarpetaValida(Carpeta) aSecciones = SeccionesINI(FicheroINI, Carpeta) Debug.Print "Secciones de " _ & Carpeta & FicheroINI For i = 0 To UBound(aSecciones) Debug.Print aSecciones(i) Next i End Sub
Si ejecuto este procedimiento en mi ordenador me muestra
eduardo@olaz.net
Eduardo Olaz
Entrega 19
19 - 29
GetPrivateProfileSection.
Recordemos que la declaracin de esta funcin es
Declare Function GetPrivateProfileSection _ Lib "kernel32" _ Alias "GetPrivateProfileSectionA" ( _ ByVal lpApplicationName As String, _ ByVal lpReturnedString As String, _ ByVal nSize As Long, _ ByVal lpFileName As String _ ) As Long lpApplicationName representa el nombre de la seccin. lpReturnedString es la cadena que recoger los datos de la seccin, con las claves y valores separados por el carcter ASCII 0. nSize es el tamao con el que se ha pasado la cadena.
El valor Long devuelto por la funcin es el nmero de caracteres cargados en la variable
lpReturnedString.
El nombre que vamos a poner a la funcin que va a devolvernos los datos de una seccin va a ser LeeSeccionINI.
Public Function LeeSeccionINI( _ ByVal FicheroINI As String, _ ByVal Seccion As String, _ Optional ByVal Carpeta As String _ ) As Variant ' Devuelve los datos de una seccin en un array _ de 2 dimensiones, de tipo String ' aDatos(1 to N, 0 to 1) As String ' Para ello utilizar la funcin API _ GetPrivateProfileSection Dim Dim Dim Dim lngLongitud As Long lngDevuelto As Long strContenedor As String strSecciones As String
19 - 30
' hacemos que el tamao de la cadena contenedora _ sea "razonablemente grande" lngLongitud = 2 ^ 15 - 1 strContenedor = String(lngLongitud, "*") Carpeta = CarpetaValida(Carpeta) FicheroINI = FicheroValido(FicheroINI) lngDevuelto = GetPrivateProfileSection( _ Seccion, _ strContenedor, _ lngLongitud, _ Carpeta & FicheroINI) ' La cadena con los datos separados por Chr$(0) _ est en los primeros lngDevuelto - 1 Caracteres _ de strContenedor strContenedor = Left(strContenedor, lngDevuelto - 1) ' Llamamos a la funcin que extrae los datos _ desde la cadena LeeSeccionINI = DescomponerCadenaConNulos( _ strContenedor) End Function
La funcin auxiliar DescomponerCadenaConNulos descompone los datos de una cadena del tipo Clave1=Dato1 Clave2=Dato2 Clave3=Dato3 . . . ClaveN=DatoN, en la que los sucesivos pares de Clave_i=Dato_i, estn separados por el carcter ASCII de ndice = 0 y los pasa a un array de 2 dimensiones. Veamos cmo podemos hacer que esta funcin devuelva un array ms manejable.
Public Function DescomponerCadenaConNulos( _ ByVal Cadena As String _ ) As Variant ' Esta funcin recibe una cadena _ con los datos separados por nulos _ y devuelve un array String del tipo _ aDatos(0 to n-1, 0 to 1) Dim lngLongitud As Long Dim lngDatos As Long Dim strNulo As String Dim aCadenas As Variant Dim aResultado() As String Dim aLinea As Variant
eduardo@olaz.net
Eduardo Olaz
Entrega 19
19 - 31
Dim i As Long strNulo = Chr(0) lngLongitud = Len(Cadena) ' Devolver una matriz bidimensional _ si existe algn separador If InStr(Cadena, strNulo) > 0 Then aCadenas = Split(Cadena, strNulo) Else ' Si no hay datos devuelve _ un array con caracteres vacos ReDim aResultado(0 To 0, 0 To 1) aResultado(0, 0) = "" aResultado(0, 1) = "" DescomponerCadenaConNulos = aResultado Exit Function End If lngDatos = (UBound(aCadenas) + 1) ReDim aResultado(0 To lngDatos, 0 To 1) For i = 0 To lngDatos - 1 aLinea = Split(aCadenas(i), "=") aResultado(i, 0) = aLinea(0) aResultado(i, 1) = aLinea(1) Next i DescomponerCadenaConNulos = aResultado End Function
Lectura de una seccin Vamos a probar estas 2 ltimas funciones. Para ello utilizaremos el fichero Schema.ini que habamos construido para usarlo en la exportacin importacin de datos, en un punto anterior de esta entrega. No debemos olvidar que slo es un caso muy especial de fichero ini. En este momento, en la carpeta de mi ordenador, el fichero Schema.ini contiene las siguientes lneas:
19 - 32
Vemos que slo tiene una seccin llamada Alumnos.txt Crearemos un procedimiento que muestre los datos de esa seccin en el fichero Schema.ini.
Public Sub MuestraDatosSchema() Const conTabulacion As Long = 30 Dim aDatos As Variant Dim i As Long Dim strSeccion As String strSeccion = "Alumnos.txt" ' Llamamos a la funcin LeeSeccionINI aDatos = LeeSeccionINI("Schema.ini", strSeccion) ' Muestra los datos en la ventana Inmediato Debug.Print Debug.Print "Contenido del fichero Schema.ini" Debug.Print "Seccin: [" & strSeccion & "]" Debug.Print "Clave", Tab(conTabulacion); "Valor" Debug.Print String(conTabulacion + 20, "-") For i = 0 To UBound(aDatos, 1) Debug.Print aDatos(i, 0), Debug.Print Tab(conTabulacion); aDatos(i, 1) Next i End Sub
Tras ejecutar este procedimiento nos mostrar lo siguiente en la ventana de depuracin o Inmediato.
eduardo@olaz.net
Eduardo Olaz
Entrega 19
19 - 33
Que, como podemos comprobar, efectivamente se corresponde con los datos contenidos en el fichero Schema.ini. Escritura de una seccin completa Para poder escribir una seccin completa, usaremos la funcin API
WritePrivateProfileSection.
Recordemos la declaracin de esta funcin:
Declare Function WritePrivateProfileSection _ Lib "kernel32" _ Alias "WritePrivateProfileSectionA" ( _ ByVal lpApplicationName As String, _ ByVal lpString As String, _ ByVal lpFileName As String _ ) As Long lpApplicationName Seccin en la que vamos a escribir las Claves y Valores lpString Lista que contiene las claves y sus Valores. lpFileName es el fichero en el que se va a escribir los datos.
Al procedimiento a crear lo vamos a llamar EscribeSeccionINI. El parmetro Datos, contendr un array semejante al devuelto por la funcin anterior LeeSeccionINI. Por tanto ser de dos dimensiones conteniendo datos de tipo String. Crearemos tambin la funcin ConstruyeCadenaConNulos que har la funcin inversa a la que hace la funcin anterior DescomponerCadenaConNulos; es decir ConstruyeCadenaConNulos extraer los datos del array Datos y los aadir a una cadena del tipo Clave_i=Valor_i, teniendo como separador el carcter ASCII 0. Al final de la cadena pondr 2 caracteres Ascii 0.
Comencemos a programar con VBA - Access
19 - 34
Public Sub EscribeSeccionINI( _ ByVal FicheroINI As String, _ ByVal Seccion As String, _ ByVal Datos As Variant, _ Optional ByVal Carpeta As String) ' Presuponemos que se le pasa un Array de _ 2 dimensiones, de tipo String ' aDatos(1 to N, 0 to 1) As String ' Para grabar los datos vamos a usar la funcin API _ WritePrivateProfileSection Dim strCadenaDatos As String Dim lngResultado As Long ' Como en los casos anteriores _ efectuamos una cierta validacin Carpeta = CarpetaValida(Carpeta) FicheroINI = FicheroValido(FicheroINI) ' Pasamos los datos de Datos a strCadenaDatos strCadenaDatos = ConstruyeCadenaConNulos(Datos) ' Llamamos a la funcin API WritePrivateProfileSection lngResultado = WritePrivateProfileSection( _ Seccion, _ strCadenaDatos, _ Cadena & FicheroINI) End Sub
Si la seccin no existiera en el fichero, la creara. Si la seccin ya existiera en el fichero, sustituir todos los valores anteriores por los nuevos. La funcin ConstruyeCadenaConNulos que convierte un array bidimensional a una cadena con separadores nulos, es la siguiente
Public Function ConstruyeCadenaConNulos( _ Datos As Variant _ ) As String Dim strDatos As String Dim i As Long Dim lngIndicemayor As Long Dim strNulo As String strNulo = Chr(0)
eduardo@olaz.net
Eduardo Olaz
Entrega 19
19 - 35
' Vamos aadiendo los sucesivos eslabones For i = 0 To UBound(Datos) strDatos = strDatos _ & Datos(i, 0) & "=" & Datos(i, 1) _ & strNulo Next i ' Aadimos un nulo adicional al final de la cadena ConstruyeCadenaConNulos = strDatos & strNulo End Function
Este procedimiento pone a prueba EscribeSeccionINI.
Public Sub PruebaGrabacionSeccion() Const conDatos As Long = 5 Dim aDatos(conDatos - 1, 1) As String Dim strSeccion As String Dim i As Long strSeccion = "Nueva Seccin" For i = 0 To conDatos - 1 aDatos(i, 0) = "Clave" & CStr(i) aDatos(i, 1) = "Valor" & CStr(i) Next i Call EscribeSeccionINI( _ "Prueba.ini", strSeccion, aDatos) End Sub
El resultado ser
19 - 36
El Registro de Windows.
Hasta la aparicin de Windows 95, la forma habitual de almacenar los parmetros de configuracin de una aplicacin, era la utilizacin de ficheros ini, tal y como hemos visto en los puntos anteriores de esta entrega. Desde la versin de Windows 95, tenemos a nuestro alcance una herramienta mucho ms potente, que evita adems la proliferacin de ficheros ini, por todas las carpetas de las aplicaciones instaladas. Esta herramienta es el fichero Regedit.exe. Este programa nos permite editar el Registro de Configuraciones de Windows, conocido tambin por su nombre reducido El registro. Normalmente suele hallarse en la carpeta C:\Windows. El registro de Windows se divide en varios bloques principales. Mi versin actual de Windows (XP Profesional - Service Pack 2) tiene los siguientes bloques:
Cada uno de los bloques almacena los parmetros de las diversas aplicaciones instaladas, y por supuesto de muchos de los virus, troyanos o programas SpyWare que hayan podido infectar nuestro ordenador. Para poder leer y escribir en esas secciones debemos utilizar una serie de procedimientos API que nos suministra el propio Windows. La utilizacin de estos procedimientos API la veremos en una seccin de esta publicacin en la que profundizaremos en esta y otras posibilidades no contempladas por el propio VBA.
Eduardo Olaz
Entrega 19
19 - 37
Para borrar las claves y sus datos deberemos utilizar DeleteSetting. Me permito sugeriros que hagis copia de seguridad del registro antes de manipularlo, ms que nada por el desastre que podra ocurrir tras un manejo inadecuado.
Instruccin SaveSetting
Actualiza o crea una entrada para una aplicacin en el registro de configuracin de Windows. Su sintaxis es
SaveSetting NombreAplicacin, seccin, clave, valor NombreAplicacin es el nombre de la subcarpeta que queremos poner colgando de VB and VBA Program Settings\ y que normalmente tiene como nombre el de la
propia aplicacin.
seccin es el nombre de la seccin donde queremos grabar el valor de la clave. valor es el valor, o la expresin que devuelve un valor a grabar.
Supongamos que en nuestro programa Gestin De Socios queremos almacenar dentro del registro de Windows los valores 1011 como ltimo socio, y 3 de septiembre de 2005 como fecha de ltima utilizacin. Para ello podemos crear el procedimiento GrabarDatosDeSalida al que llamaremos inmediatamente antes de cerrar el programa, pasndole los parmetros adecuados. Podra ser algo tan simple como esto:
Public Sub GrabarDatosDeSalida( _ ByVal Clave As String, _ ByVal Valor As Variant) SaveSetting "Gestin De Socios", _ "Valores finales", _ Clave, _ Valor End Sub
Inmediatamente antes de cerrar la aplicacin, en alguna parte del cdigo, se har la siguiente llamada:
19 - 38
Funcin GetSetting
Devuelve el valor de una clave del registro. Su sintaxis es
Public Sub CargarDatosDeEntrada() Dim datUltimaFecha As Date Dim strUltimaFecha As String Dim lngSocio As Long lngSocio = CLng(GetSetting("Gestin De Socios", _ "Valores finales", _ "ltimo Socio", _ "0")) datUltimaFecha = CDate(GetSetting("Gestin De Socios", _ "Valores finales", _ "Fecha Utilizacin", _ CStr(Date))) Debug.Print "ltimo socio grabado " _ & CStr(lngSocio) Debug.Print "ltima fecha de utilizacin " _ & CStr(Date) End Sub
Funcin GetAllSettings
Devuelve el valor de todas las claves de una seccin del registro.
eduardo@olaz.net
Eduardo Olaz
Entrega 19
19 - 39
Su sintaxis es
Public Sub MostrarDatosRegistro() Dim aDatos As Variant Dim i As Long aDatos = GetAllSettings("Gestin De Socios", _ "Valores finales") For i = 0 To UBound(aDatos, 1) Debug.Print "Clave " & CStr(i) _ & " - " _ & aDatos(i, 0) & ": " & aDatos(i, 1) Next i End Sub
La lnea cabecera del bucle, en vez de basarla directamente en el ndice 0 podramos usar la funcin LBound que nos devuelve el ndice ms bajo del array.
Instruccin DeleteSetting
Elimina una seccin completa o una clave en el registro de configuracin de Windows. Su sintaxis es
19 - 40
"Fecha Utilizacin"
Borrara la clave "Fecha Utilizacin". Si hubiramos ejecutado
Estos procedimientos los veremos ms adelante en un captulo especficamente dedicado al API de Windows.
eduardo@olaz.net
Eduardo Olaz
VBA - Access
Entrega
20
Eduardo Olaz
20 - 2
eduardo@olaz.net
Eduardo Olaz
Entrega 20
20 - 3
Como veremos, la gran ventaja que tienen las propiedades es que permite filtrar los datos que van a manejarse pudiendo evitarse la asignacin de datos incorrectos y teniendo la posibilidad de reaccionar y avisar al usuario si se ha cometido una incorreccin.. Una parte de los datos se guardan en el propio objeto sin que sea posible acceder a ellos directamente. Slo es posible manejarlos mediante el uso de las propiedades, teniendo que sujetarse a los filtros que incluya en ellas el programador. A esta capacidad de proteger y ocultar los datos bsicos del objeto es a lo que se llama Encapsulacin. Una clase podemos abordarla desde dos puntos de vista: Como programador de la clase Como usuario de la clase
El programador de la clase es el que define qu propiedades va a tener, de qu tipo y cmo se va a comportar. El usuario de la clase, ya sea el que la ha diseado u otra persona, va a utilizarla para crear objetos que le faciliten una serie de tareas de programacin asignando valores concretos a las propiedades y utilizando sus procedimientos, llamados mtodos de la clase.
20 - 4
empezar a plantearse muy seriamente el pasar a la plataforma Net. La buena noticia es que VB.Net, en su sintaxis, guarda un fuerte parecido con VBA. Los conceptos que rodean a la utilizacin de las clases y objetos, es probablemente el obstculo ms difcil para un programador, digamos estndar de Visual Basic, que apenas hace uso de las limitadas, pero a pesar de ello potentes, posibilidades de VBA en la programacin orientada a objetos. Me atrevera a afirmar que casi todo lo que pueda aprender en esta entrega le servir en el futuro con VB.Net, haciendo que su curva de aprendizaje sea mucho ms suave. El dominio en el manejo de las clases y la Programacin Orientada a Objetos, le tiende un puente que le facilitar el salto a lenguajes como VB.Net y C#. Tenga tambin en cuenta que la plataforma Net trabaja de forma exhaustiva con las clases. Hay que usarlas de forma obligatoria para el desarrollo de cualquier programa. En .Net hasta los mdulos son en s mismos clases, aunque no se declaren como tales. Podra argumentar muchas ms razones, pero prefiero centrarme en los temas que ataen a esta entrega.
Propiedades
Para estudiar qu son las propiedades, veamos la clase CPersona que vimos en el captulo 7 (he eliminado las lneas de control de fecha, de la clase original, para ver mejor el ejemplo didctico). Os recuerdo que para crear una clase, usaremos la opcin de men [Insertar] > [Mdulo de clase], y en este caso, pondremos a la propiedad Name de la ventana Propiedades el valor CPersona.
Option Explicit Public Public Public Public Public Nombre As String Apellido1 As String Apellido2 As String FechaNacimiento As Date Telefono As String
Public Function Edad() As Long Edad = (Date - FechaNacimiento) / 365.2425 End Function Public Function NombreCompleto() As String NombreCompleto = Nombre _ & " " & Apellido1 _ & " " & Apellido2 End Function
Podemos, por ejemplo crear un objeto llamado Empleado.
eduardo@olaz.net
Eduardo Olaz
Entrega 20
20 - 5
Public Sub PruebaCPersona() Dim Empleado As New CPersona With Empleado .Nombre = "Antonio" .Apellido1 = "Lpez" .Apellido2 = "Iturriaga" .FechaNacimiento = #12/24/1980# Debug.Print "Empleado: " & .NombreCompleto Debug.Print "Fecha de nacimiento: " _ & .FechaNacimiento Debug.Print "Edad: " & .Edad & " aos" End With End Sub
El resultado de todo esto aparecer en la ventana Inmediato
.FechaNacimiento = #12/24/1780#
Igualmente podramos haber escrito
.FechaNacimiento = #12/24/2020#
Lo que, a fecha de hoy, nos dara un empleado de -15 aos. Cmo podemos evitar que suceda esto? es decir, que introduzcan una fecha de nacimiento absurda. Aqu es donde vamos a ver qu es y cmo se programa una propiedad. Examinemos este cdigo:
Private Const conEdadMaxima As Long = 100 Public Nombre As String Public Apellido1 As String Public Apellido2 As String Private m_datFechaNacimiento As Date Public Telefono As String Public Property Let FechaNacimiento(ByVal Fecha As Date)
Comencemos a programar con VBA - Access
20 - 6
Dim datFechaMinima As Date ' Ponemos como fecha mnima la de hoy hace 100 aos datFechaMinima = DateAdd("yyyy", -conEdadMaxima, Date) If Fecha < datFechaMinima Or Fecha > Date Then ' Si la fecha introducida es menor que _ datFechaMinima o mayor que la fecha de hoy _ generar el error de rango incorrecto Err.Raise Number:=106, _ Source:="Clase CPersona", _ Description:="Rango de edad inadecuado" End If If m_datFechaNacimiento <> Fecha Then m_datFechaNacimiento = Fecha End If End Property Public Property Get FechaNacimiento() As Date FechaNacimiento = m_datFechaNacimiento End Property
Se supone que el resto del cdigo permanece igual. Vemos que ha desaparecido la variable pblica FechaNacimiento, siendo sustituida por la variable privada m_datFechaNacimiento. Si ahora tratamos de ejecutar el procedimiento PruebaCPersona en el que la lnea de asignacin de fecha sea
.FechaNacimiento = #12/24/2020#
Nos dar un bonito mensaje genrico de error:
Eduardo Olaz
Entrega 20
20 - 7
.FechaNacimiento = #12/24/1880#
Vemos que si la fecha es normal el cdigo se ejecuta como antes. Recordemos que la lnea del procedimiento de prueba:
20 - 8
Un segundo de Escritura, Property Let, que graba un valor en una variable interna de tipo privado. Su forma de escritura es equivalente a la de un procedimiento. Existe un tercer tipo de procedimiento property que se utiliza para grabar propiedades de tipo Objeto. Es el Property Set. Este podra ser un ejemplo de uso:
Public Property Set Objeto(NuevoObjeto As Object) Set m_Objeto = NuevoObjeto End Property
La propiedad FechaNacimiento, del ejemplo decimos que es de Lectura / Escritura, ya que tiene los dos procedimientos Property Get y Property Let. Si quisiramos crear una propiedad de solo lectura, por ejemplo que devolviera el nmero de registros existentes en una tabla, crearamos slo el procedimiento Property Get. Igualmente si quisiramos una propiedad de solo escritura, aunque no hay muchos casos que se me ocurran que lo necesiten, escribiramos slo el procedimiento Property Let. A la hora de crear una clase, lo que se suele hacer es definir los Atributos de la clase. Para todos aquellos atributos sobre los que se quiere tener un control, ya sea porque se quieren controlar los rangos de entrada o salida se vayan a generar eventos cuando cambien, vayan a cambiar o alcancen determinados valores se quiera hacer que sean slo de Lectura o solo de Escritura el mtodo a seguir es el siguiente Se crea una variable de alcance Private para cada uno de los atributos Se genera un Property Let para la asignacin de valores, o un Property Set si el dato puede admitir algn tipo de objeto. De forma paralela se genera un Property Get que devolver el dato de la variable fuera del objeto. En el caso de las funciones (mtodos) Edad y NombreCompleto, podramos haberlas definido como propiedades de slo lectura, de la siguiente manera:
Public Property Get Edad() As Long Edad = (Date - FechaNacimiento) / 365.2425 End Property Public Property Get NombreCompleto() As String NombreCompleto = Nombre _ & " " & Apellido1 _ & " " & Apellido2 End Property
Antes de efectuar este cambio, estos procedimien Si abrimos el examinador de Objetos veremos lo siguiente:
eduardo@olaz.net
Eduardo Olaz
Entrega 20
20 - 9
Despus de hacerlo
Vemos que ha cambiado el smbolo que aparece delante, tanto de Edad como de NombreCompleto, y ha pasado de ser el smbolo de un Mtodo a otro que representa una Propiedad.
Mtodos de clase
A las funciones y procedimientos pblicos de una clase se les llama Mtodos de clase. En el caso de la clase CPersona, tenemos dos mtodos de clase, que devuelven valores, es decir son del tipo Function. Estos mtodos son Edad, que devuelve un nmero del tipo Long y NombreCompleto, que devuelve una cadena String. Podran haberse creado mtodos que no devolvieran ningn valor, es decir del tipo Sub. Todos estos mtodos pblicos podran, a su vez, haber utilizado para su ejecucin interna otros mtodos privados de la propia clase, que no fuesen visibles desde fuera de la misma. Como ya hemos dicho, a la capacidad de ocultar determinados atributos y procedimientos de la clase, es a lo que se llama Encapsulacin.
20 - 10
que tenga, por ejemplo mtodos como Anterior y Siguiente, Derecha e Izquierda, que devuelvan objetos de la misma clase. Vamos crear una clase a la que llamaremos CIndividuo. Esta clase va a ser muy sencilla, y slo va a tener una propiedad llamada Nombre que devolver un String y otra propiedad llamada Pareja que va a devolver un objeto del tipo CPareja. Vamos a ello. Como siempre creamos un nuevo mdulo de clase al que pondremos por nombre CIndividuo. Nota: Lo de poner una C delante del nombre en cristiano de la clase es una convencin de nombres que se haba extendido en el mundillo de Visual Basic. Ahora, en Vb.Net parece que la forma a adoptar es poner delante el prefijo cls. Su cdigo sera el siguiente.
Option Explicit Private m_Pareja As CIndividuo Private m_strNombre As String Public Property Get Pareja() As CIndividuo Set Pareja = m_Pareja End Property Public Property Set Pareja(Persona As CIndividuo) Set m_Pareja = Persona End Property Public Property Get Nombre() As String Nombre = m_strNombre End Property Public Property Let Nombre(NuevoNombre As String) If m_strNombre <> NuevoNombre Then m_strNombre = NuevoNombre End If End Property
En este cdigo podemos observar varias cosas En primer lugar el procedimiento para la escritura del valor de la propiedad, Property Let ha sido sustituido por un procedimiento Property Get. Esto es as porque se va a asignar un objeto a la variable m_strNombre.
eduardo@olaz.net
Eduardo Olaz
Entrega 20
20 - 11
Para probar esta clase vamos a escribir una historia cotidiana en la que se entremezclan tres individuos. Antonio y Mara se conocen desde hace tiempo y como el roce genera el cario, acabaron hacindose novios. Durante una temporada mantienen una relacin estable y feliz. Un buen da Mara conoci a Juan. La atraccin mutua fue instantnea y fulgurante, lo que les llev a vivir un apasionado romance. A todo esto Antonio no se entera de nada.
Qu tenemos aqu? Tenemos tres objetos de la clase CIndividuo, de los que la propiedad Nombre es
Public Sub HistoriaDeTres() Dim Hombre As New CIndividuo Dim Mujer As New CIndividuo Dim UnTercero As New CIndividuo ' Les ponemos nombres Hombre.Nombre = "Antonio" Mujer.Nombre = "Mara" UnTercero.Nombre = "Juan" ' Antonio y Mara son novios Set Hombre.Pareja = Mujer Set Mujer.Pareja = Hombre Debug.Print Hombre.Nombre _ & " lleva mucho tiempo saliendo con " _ & Mujer.Nombre Debug.Print "La pareja de " & Hombre.Nombre _ & " es: " & Hombre.Pareja.Nombre Debug.Print "La pareja de " & Mujer.Nombre _ & " es: " & Mujer.Pareja.Nombre
20 - 12
Debug.Print Mujer.Nombre _ & " conoce a " & UnTercero.Nombre _ & " y ..." Set Mujer.Pareja = UnTercero Set UnTercero.Pareja = Mujer Debug.Print "La & " Debug.Print "La & " pareja de " & Mujer.Nombre _ es: " & Mujer.Pareja.Nombre pareja de " & UnTercero.Nombre _ es: " & UnTercero.Pareja.Nombre
' Antonio no se entera de nada Debug.Print "Tras los cuernos " _ & Hombre.Nombre _ & " ni se ha enterado" Debug.Print Hombre.Nombre _ & " cree que su pareja es " _ & Hombre.Pareja.Nombre End Sub
Si ejecutamos el procedimiento, el resultado, en la ventana de Depuracin ser:
Antonio lleva mucho tiempo saliendo con Mara La pareja de Antonio es: Mara La pareja de Mara es: Antonio Mara conoce a Juan y ... La pareja de Mara es: Juan La pareja de Juan es: Mara Tras los cuernos Antonio ni se ha enterado Antonio cree que su pareja es Mara
Vemos que para acceder al nombre de la pareja, podemos hacerlo de la siguiente forma
Individuo.Pareja.Nombre
Esto es as porque Individuo.Pareja devuelve un objeto del tipo CIndividuo, por lo que podremos acceder a su propiedad Nombre. Supongamos que hubiramos puesto
Debug.Print Mujer.Pareja.Pareja.Nombre
Esto nos devolvera Mara, ya Mujer.Pareja devuelve al Novio de Mara, por lo que la pareja del Novio de Mara, es la propia Mara. De la misma forma
Mujer.Pareja.Pareja.Pareja
Nos devolver al Individuo que es en ese momento Novio de Mara.
eduardo@olaz.net
Eduardo Olaz
Entrega 20
20 - 13
Estableceremos la comunicacin entre Nodos, de forma que ser el nodo el eslabn que servir para desplazarse por el rbol. Definiremos unos mtodos Anterior Izquierda Derecha Devolver el nodo anterior en el organigrama del rbol Devolver el nodo situado en el extremo de la rama izquierda Devolver el nodo situado en el extremo de la rama derecha
Cada nodo podr almacenar un dato en la propiedad Valor y tendr una propiedad Nombre. Vamos a tratar de construir la siguiente estructura:
Nodo_3
d No a rd io r
. _1
Iz
qu
Nodo_1
No
do
_0
z .I
qu
ie
Nodo_0
Valor
No do _2 .A nt No er do io _0 r .D er ec ha
d No
o_
1.
t An
er
Valor
N No do _4 .A nt No er do io _1 r .D er ec ha
o od
_3
.A
e nt
Valor
Nodo_4
Nodo_2
Valor
Valor
Sera equivalente a un rbol con dos ramas principales, de una de las cuales a su vez parten otras dos ramas. Vamos a crear una clase a la que llamaremos CNodoArbolSimple. Primero insertaremos un mdulo de clase con el nombre CNodoArbolSimple.
20 - 14
Option Explicit Private m_varValor As Variant Private m_strNombre As String Private m_ndoAnterior As CNodoArbolSimple Private m_ndoIzquierda As CNodoArbolSimple Private m_ndoDerecha As CNodoArbolSimple Public Property Get Valor() As Variant If Not IsObject(m_varValor) Then Valor = m_varValor Else Set Valor = m_varValor End If End Property Public Property Let Valor(NuevoValor As Variant) m_varValor = NuevoValor End Property Public Property Set Valor(NuevoValor As Variant) Set m_varValor = NuevoValor End Property
En las primeras lneas definimos tres variables privadas del tipo CNodoArbolSimple, que nos servirn para hacer referencia a los nodos Anterior, Izquierda y Derecha. Tambin definiremos una variable de tipo Variant que nos servir para almacenar al Valor asignado al nodo. Y ahora fijmonos en la propiedad Valor. Por qu he puesto una propiedad Property Get, otra Property Let y una tercera Property Set?. La razn es muy sencilla. Al manejar Valor una variable de tipo Variant, puede ocurrir que sta haga referencia a un objeto. No se puede asignar un objeto de la forma m_varValor = NuevoValor, como lo hace Property Let, ya que dara un error. Es necesario hacer Set m_varValor = NuevoValor de lo que se encarga Property Set. Vamos a hora a definir la propiedad Valor para almacenar datos en el nodo:
eduardo@olaz.net
Eduardo Olaz
Entrega 20
20 - 15
Vamos a probar este esbozo de clase para comprobar si lo dicho funciona correctamente. En un mdulo estndar escribimos:
Public Sub PruebaNodo() Dim Nodo As New CNodoArbolSimple Dim col As New Collection col.Add "Lunes" col.Add "Martes" col.Add "Mircoles" Nodo.Valor = 1000 Debug.Print Nodo.Valor Set Nodo.Valor = col Debug.Print Nodo.Valor(1) Debug.Print Nodo.Valor(2) Debug.Print Nodo.Valor(3) End Sub
Si hemos escrito correctamente todas las lneas y ejecutamos el procedimiento vemos que, como era de esperar, nos muestra en la ventana de depuracin:
Option Explicit Option Explicit Private m_varValor As Variant Private m_strNombre As String Private m_ndoAnterior As CNodoArbolSimple Private m_ndoIzquierda As CNodoArbolSimple Private m_ndoDerecha As CNodoArbolSimple Public Property Get Valor() As Variant If Not IsObject(m_varValor) Then Valor = m_varValor Else Set Valor = m_varValor
Comencemos a programar con VBA - Access
20 - 16
End If End Property Public Property Let Valor(NuevoValor As Variant) m_varValor = NuevoValor End Property Public Property Set Valor(NuevoValor As Variant) Set m_varValor = NuevoValor End Property Public Property Get Nombre() As String Nombre = m_strNombre End Property Public Property Let Nombre(NuevoNombre As String) m_strNombre = NuevoNombre End Property Public Property Get Anterior() As CNodoArbolSimple Set Anterior = m_ndoAnterior End Property Public Property Set Anterior(NuevoNodo As CNodoArbolSimple) Set m_ndoAnterior = NuevoNodo End Property Public Property Get Izquierda() As CNodoArbolSimple Set Anterior = m_ndoIzquierda End Property Public Property Set Izquierda(NuevoNodo As CNodoArbolSimple) Set m_ndoIzquierda = NuevoNodo End Property Public Property Get Derecha() As CNodoArbolSimple Set Anterior = m_ndoDerecha End Property
eduardo@olaz.net
Eduardo Olaz
Entrega 20
20 - 17
Public Property Set Derecha(NuevoNodo As CNodoArbolSimple) Set m_ndoDerecha = NuevoNodo End Property
La clase est simplificada al mximo, sin incluir gestin de errores ni comprobaciones de valores previos; esto lo dejo para el lector. Vamos ahora a ponerla a prueba generando un rbol con la estructura del rbol simple que pusimos al principio de esta seccin
Public Sub PruebaNodosArbol() ' La coleccin Nodos _ contendr los sucesivos nodos Dim Nodos As New Collection Dim i As Long Dim Nodo As New CNodoArbolSimple With Nodo .Nombre = "Nodo_0" .Valor = 0 Set .Izquierda = New CNodoArbolSimple Set .Derecha = New CNodoArbolSimple End With ' Aado el nodo con sus datos y la clave "0" _ a la coleccin Nodos.Add Nodo, "0" With Nodo.Izquierda
Comencemos a programar con VBA - Access
20 - 18
.Nombre = "Nodo_1" .Valor = 10 Set .Izquierda = New CNodoArbolSimple Set .Derecha = New CNodoArbolSimple Set .Anterior = Nodo End With Nodos.Add Nodo.Izquierda, "1" With Nodo.Derecha .Nombre = "Nodo_2" .Valor = 20 Set .Izquierda = New CNodoArbolSimple Set .Derecha = New CNodoArbolSimple Set .Anterior = Nodo End With Nodos.Add Nodo.Derecha, "2" ' Ahora trabajo con el nodo Nodo_3 With Nodo.Izquierda.Izquierda .Nombre = "Nodo_3" .Valor = 30 Set .Anterior = Nodo.Izquierda End With Nodos.Add Nodo.Izquierda.Izquierda, "3" With Nodo.Izquierda.Derecha .Nombre = "Nodo_4" .Valor = 40 Set .Anterior = Nodo.Izquierda End With Nodos.Add Nodo.Izquierda.Derecha, "4" Debug.Print Debug.Print " Nodo Base " ' El procedimiento MuestraDatos est a continuacin MuestraDatos Nodos(CStr(0)) For i = 1 To 4 Debug.Print Debug.Print " Nodo n " & CStr(i)
eduardo@olaz.net
Eduardo Olaz
Entrega 20
20 - 19
MuestraDatos Nodos(CStr(i)) Next i ' para comprobar si todo est correcto _ recorremos la estructura usando los enlaces Debug.Print "Ahora la recorro por los enlaces" Debug.Print "Siguiendo las propiedades" Debug.Print "Izquierda, Derecha y Anterior" Set Nodo = Nodos(1) Debug.Print "Nodo Base " & Nodo.Nombre Set Nodo = Nodo.Izquierda Debug.Print "Nodo N 1 " & Nodo.Nombre Set Nodo = Nodo.Anterior.Derecha Debug.Print "Nodo N 2 " & Nodo.Nombre Set Nodo = Nodo.Anterior.Izquierda.Izquierda Debug.Print "Nodo N 3 " & Nodo.Nombre Set Nodo = Nodo.Anterior.Derecha Debug.Print "Nodo N 4 " & Nodo.Nombre End Sub
El procedimiento MuestraDatos es el siguiente
Public Sub MuestraDatos(Nodo As CNodoArbolSimple) With Nodo Debug.Print Tab(5); "Nombre " & .Nombre Debug.Print Tab(5); "Valor " & .Valor End With End Sub
Tras ejecutarse el procedimiento PruebaNodosArbol se mostrar en la ventana Inmediato lo siguiente:
20 - 20
Valor
10
Nodo n 2 Nombre Nodo_2 Valor 20 Nodo n 3 Nombre Nodo_3 Valor 30 Nodo n 4 Nombre Nodo_4 Valor 40 Ahora la recorro por los enlaces Siguiendo las propiedades Izquierda, Derecha y Anterior Nodo Base Nodo_0 Nodo N 1 Nodo_1 Nodo N 2 Nodo_2 Nodo N 3 Nodo_3 Nodo N 4 Nodo_4
Para destruir la estructura podramos ir eliminando cada uno de los elementos de la coleccin Nodos, mediante su mtodo Remove, antes de salir del procedimiento. Ya se que a primera vista no parece muy espectacular, pero hemos creado una estructura tipo rbol, en la que cada nodo puede estar enlazado con otros tres y es capaz de almacenar un valor. En la clase CNodoArbolSimple hemos colocado una propiedad Izquierda, otra Derecha y una Anterior. Si quisiramos crear un rbol ms genrico, en el que un nodo podra tener 0, 1 n ramas, deberamos sustituir las propiedades Derecha e Izquierda, por una propiedad Rama(ndice) que trabajara contra una coleccin privada de la propia clase. Igualmente podramos saltarnos una estructura de rbol y definir una estructura de grafo ms complejo. Podramos haber desarrollado una propiedad Padre(ndice) que enlazara con varios posibles Ancestros, de nivel -1, una propiedad Hermano(ndice) que enlazara con objetos del mismo nivel, y una propiedad Hijo(ndice) que enlazara con objetos de nivel 1. A travs de los objetos devueltos podramos tener acceso a Abuelos, Nietos, etc Adems podramos hacer que cada nodo almacenara diferentes valores de diversos tipos. Las referencias a Hermanos, Padres e Hijos podramos guardarlos en Colecciones Privadas de la propia clase.
eduardo@olaz.net
Eduardo Olaz
Entrega 20
20 - 21
Las aplicaciones en las que una estructura de este calibre, podra servir como elemento clave, son innumerables: Desarrollo flexible de Escandallos de Produccin Elaboracin de Grafos Diseo de una hoja de clculo Definir modelos de datos Diseo de juegos Todas las que tu imaginacin pueda intuir. . . Antes de seguir quiero volver a incidir en un aspecto que hemos tocado. Si una clase incluye referencias a objetos la propia clase, y mtodos para desplazarse entre las referencias, podemos encadenar estos mtodos. Por ejemplo si tenemos una clase que posee una propiedad Siguiente que devuelve otro objeto de esa misma clase, podramos recorrer la estructura de objetos, utilizando el mtodo Siguiente desde un Objeto base de forma reiterativa, hasta que lleguemos a un objeto cuya propiedad Siguiente est sin asignar, es decir su valor sea Nothing.
MiObjeto Is Nothing
Por ejemplo, el siguiente cdigo imprimir primero False, ya que no hemos todava asignado un objeto a la propiedad Anterior, del objeto Nodo1. A continuacin imprimir True, ya que el objeto Nodo2 s tiene asignado el objeto Nodo1 en la propiedad Anterior.
Public Sub PruebaIsNothing() Dim Nodo1 As New CNodoArbolSimple Dim Nodo2 As New CNodoArbolSimple Debug.Print Nodo1.Anterior Is Nothing Set Nodo2.Anterior = Nodo1 Debug.Print Nodo2.Anterior Is Nothing End Sub
Eventos.
Un evento es un procedimiento de la clase que se ejecuta desde dentro del propio objeto creado con la clase, y que es posible captarlo en el objeto que contiene a la clase. Como veremos prximamente, si en un formulario colocamos un botn de comando (CommandButton) cada vez que lo presionemos genera el evento Click. Si quisiramos que cuando se presione el botn de nombre cmdSalir, se cierre el formulario, podramos poner en el mdulo de clase del formulario el siguiente cdigo.
Comencemos a programar con VBA - Access
20 - 22
Private Sub cmdSalir_Click() On Error GoTo HayError DoCmd.Close Salir: Exit Sub HayError: MsgBox Err.Description Resume Salir End Sub
Dentro del mdulo de clase del objeto Botn, algn programador ha definido un evento al que ha puesto por nombre Click() Nosotros podemos programar Eventos dentro de las clases, de forma que esos eventos puedan ser captados por otras clases que contengan a las primeras. Para ello se utiliza las instrucciones Event y RaiseEvent, que veremos ms adelante.
Qu es un Evento
Si vamos a la ayuda de Access, vemos que lo describe de la siguiente forma Un evento es una accin especfica que se produce en o con un objeto determinado. Microsoft Access puede responder a una variedad de eventos: clics del Mouse cambios en los datos formularios que se abren o se cierran muchos otros.
Los eventos son normalmente el resultado de una accin del usuario. No se si aclara mucho, pero como complemento me atrevo a decir que un evento es algo as como una llamada de aviso que efecta un objeto cuando suceden determinadas cosas. Esa llamada puede, o no, ser recogida por un procedimiento, que actuar en consecuencia. Vamos a ver un ejemplo clsico. Pongamos en un formulario un botn de comando. Ojo: desactiva antes el Asistente para controles de la barra de herramientas. A su propiedad Ttulo, le ponemos el texto Saludo. Sin dejar de seleccionar el botn, activamos la hoja Eventos de la ventana de propiedades. Seleccionamos el evento Al hacer clic y pulsamos en el botoncito con tres botones Nos aparece una pequea ventana con tres opciones. Seleccionamos Generador de cdigo y pulsamos en el botn Aceptar. Directamente nos abre el mdulo de clase de ese formulario, probablemente con el siguiente cdigo.
Eduardo Olaz
Entrega 20
20 - 23
Option Compare Database Option Explicit Private Sub cmdSaludo_Click() MsgBox "Hola terrqueos", , "Saludo desde Marte" End Sub
Cerramos la ventana de edicin, y ejecutamos el formulario. Al pulsar en el botn, nos aparece el mensaje:
Por qu pasa esto? Cuando hacemos clic sobre algunos objetos, stos hacen una llamada, buscando en el objeto que los contiene, en este caso el formulario, un procedimiento que tenga como nombre NombreDelObjeto_Click. Como el nombre del botn es cmdSaludo, examina el formulario para ver si existe algn procedimiento de nombre cmdSaludo_Click. Como en este caso s existe, ejecuta dicho procedimiento. Un procedimiento que se vaya a ejecutar como respuesta a un evento, puede incluir parmetros. Vamos a poner en el mismo formulario un cuadro de texto (TextBox), al que llamaremos
20 - 24
el evento es llamado cada vez que se pulsa una tecla en el cuadro de texto, pasndole un valor entero (KeyAscii) que contiene el cdigo ASCII de la letra correspondiente a la tecla pulsada. Por ejemplo, el cdigo ASCII correspondiente a la letra A es el 65, y el de la letra a es el 97. Podramos cambiar el valor de ese cdigo. Por ejemplo, si en el procedimiento pusiramos
NombreDelObjeto_NombreDelEvento([PosiblesParmetros])
Ejemplos de nombres de los procedimientos gestores de los eventos:
Eduardo Olaz
Entrega 20
20 - 25
Instruccin Event
Sirve para declarar un evento definido por un usuario, dentro de una clase. Su sintaxis es la siguiente
20 - 26
Manos a la obra. Clase Depsito Supongamos que ha transcendido al mundo empresarial nuestras habilidades como programadores para todo tipo de terrenos; es slo una hiptesis Un buen da recibimos la visita del ingeniero jefe de una importante refinera petrolfera, y nos cuenta su problema. La empresa tiene su propio equipo de programadores, pero hasta ahora, no han demostrado la capacidad suficiente como para justificar su elevado sueldo. Tal es as, que para cumplir con las nuevas normas de seguridad, necesitan desarrollar unas interfaces que permitan controlar el trasiego de lquidos que se efecte en los diferentes depsitos, pudindose establecer unos niveles de seguridad mnimo y mximo en cuanto al contenido de los mismos, as como su contenido actual y el contenido mximo real. El planteamiento es disear una clase que permita definir y gestionar esos parmetros y que, cuando se pretenda introducir ms cantidad menos de la posible fsicamente genere un error que pueda ser captado por el objeto que maneje el objeto depsito. Adems si se van a superar los lmites de seguridad deber generar los correspondientes eventos de aviso, que permitan interrumpir el proceso de llenado o vaciado. Tambin sera interesante que antes de que se vaya a aadir o a extraer lquido de un depsito, incluso sin que se superen los niveles de seguridad, se genere un evento que permita interrumpir la operacin. Cuando se haya terminado el proceso de llenado y vaciado, se deber generar un evento, informando de la cantidad trasvasada. Manos a la obra. Si analizamos los requerimientos, vemos que para controlar las propiedades del depsito tenemos estos cuatro miembros fundamentales. Capacidad del depsito Contenido actual del depsito Nivel mximo de seguridad Nivel mnimo de seguridad Cantidad a extraer Cantidad a introducir Introduciendo Introducido Extrayendo Extrado TrasvaseImposible VioladoNivelDeSeguridad
eduardo@olaz.net
Eduardo Olaz
Entrega 20
20 - 27
Contenido Actual
Nivel Mnimo
Para controlar cada una de las propiedades definiremos las variables privadas, a las que se acceder mediante las correspondientes propiedades. Tambin definiremos los eventos que hemos definido. Insertamos un nuevo mdulo de clase y escribimos
Public Event Introduciendo( _ ByVal Volumen As Single, _ ByRef Cancelar As Boolean) Public Event Introducido( _ ByVal Volumen As Single) Public Event Extrayendo( _ ByVal Volumen As Single, _ ByRef Cancelar As Boolean) Public Event Extraido( _ ByVal Volumen As Single)
20 - 28
Public Event TrasvaseImposible( _ ByVal ContenidoActual As Single, _ ByVal VolumenATrasvasar As Single) Public Event ViolandoNivelDeSeguridad( _ ByVal ContenidoActual As Single, _ ByVal VolumenATrasvasar As Single, _ ByVal NivelDeSeguridad As Single)
Las variables Private definidas, no deberan representar para nosotros ningn problema por lo que no voy a comentarlas. Para manejar estas variables, escribiremos las correspondientes propiedades de lectura y escritura Property Get y Property Let. En cuanto a la declaracin de los eventos, vemos el proceso que responde a la estructura:
Instruccin RaiseEvent
La instruccin RaiseEvent desencadena un evento declarado a nivel de un mdulo de clase. Este mdulo puede ser un mdulo especfico de clase, o el mdulo asociado a un formulario o a un informe de Access. La forma de hacerlo es una mezcla entre la forma de llamar a una funcin y la de llamar a un procedimiento Sub. Su sintaxis es:
RaiseEvent NombreDelEvento(ListaDeParmetros)
Hay que usar parntesis como en las funciones. Para los procesos de trasvase de lquido, definiremos dos procedimientos Sub pblicos dentro del mdulo de clase Aadir y Extraer. Estos Mtodos de la clase sern los encargados de desencadenar los eventos cuando las condiciones lo requieran. Incluso la propiedad Contenido, cuando haya que variarlo, utilizar estos mtodos para ajustar el contenido actual del depsito. A continuacin desarrollaremos el cdigo completo y comentaremos los diferentes temas.
Eduardo Olaz
Entrega 20
20 - 29
ByVal Volumen As Single, _ ByRef Cancelar As Boolean) Public Event Introducido( _ ByVal Volumen As Single) Public Event Extrayendo( _ ByVal Volumen As Single, _ ByRef Cancelar As Boolean) Public Event Extraido( _ ByVal Volumen As Single) Public Event TrasvaseImposible( _ ByVal ContenidoActual As Single, _ ByVal VolumenATrasvasar As Single) Public Event ViolandoNivelDeSeguridad( _ ByVal ContenidoActual As Single, _ ByVal VolumenATrasvasar As Single, _ ByVal NivelDeSeguridad As Single) ' Capacidad del depsito Public Property Get Capacidad() As Single Capacidad = m_sngCapacidad End Property Public Property Let Capacidad(ByVal Volumen As Single) If m_sngCapacidad <> Volumen Then m_sngCapacidad = Volumen End If End Property ' Nivel Mximo de seguridad Public Property Get NivelMaximo() As Single NivelMaximo = m_sngNivelMaximo End Property Public Property Let NivelMaximo(ByVal Volumen As Single) If m_sngNivelMaximo <> Volumen Then
Comencemos a programar con VBA - Access
20 - 30
Select Case Volumen Case Is < 0 MsgBox "El volumen mximo no puede ser negativo" Case Is > m_sngCapacidad MsgBox "El volumen mximo no puede ser mayor" _ & vbCrLf _ & "que la capacidad del depsito" Case Is < m_sngNivelMinimo MsgBox "El volumen mximo no puede ser menor" _ & vbCrLf _ & "que el volumen mnimo" Case Else m_sngNivelMaximo = Volumen End Select End If End Property ' Nivel Mnimo de seguridad Public Property Get NivelMinimo() As Single NivelMinimo = m_sngNivelMinimo End Property Public Property Let NivelMinimo(ByVal Volumen As Single) If m_sngNivelMinimo <> Volumen Then Select Case Volumen ' Control de casos incorrectos Case Is < 0 MsgBox "El volumen mnimo no puede ser negativo" Case Is > m_sngCapacidad MsgBox "El volumen mnimo no puede ser mayor" _ & vbCrLf _ & "que la capacidad del depsito" Case Is > m_sngNivelMaximo MsgBox "El volumen mnimo no puede ser mayor" _ & vbCrLf _ & "que el volumen mximo" Case Else m_sngNivelMinimo = Volumen End Select End If
eduardo@olaz.net
Eduardo Olaz
Entrega 20
20 - 31
End Property ' Contenido actual del depsito Public Property Get Contenido() As Single Contenido = m_sngContenido End Property Public Property Let Contenido(ByVal Volumen As Single) ' Para hacer que el depsito _ Contenga un determinado volumen _ introducimos lo que falte _ o extraemos lo que sobre Select Case Volumen Case Is > m_sngContenido Introducir Volumen - m_sngContenido Case Is < m_sngContenido Extraer m_sngContenido - Volumen End Select End Property ' Mtodo para introducir en el depsito Public Sub Introducir(ByVal Volumen As Single) On Error GoTo HayError Dim blnCancelar As Boolean ' Si vamos a introducir una cantidad negativa _ asume que se quiere extraer If Volumen < 0 Then Extraer -Volumen Exit Sub End If ' Desencadenamos el primer evento RaiseEvent Introduciendo(Volumen, blnCancelar) ' Si en el gestor del evento _ se ha decidido cancelar la operacin If blnCancelar Then ' Cancelamos el proceso Exit Sub Else ValidaTrasvase Volumen
Comencemos a programar con VBA - Access
20 - 32
m_sngContenido = m_sngContenido + Volumen RaiseEvent Introducido(Volumen) End If Salir: Exit Sub HayError: MsgBox "Se ha producido el error n " & Err.Number _ & vbCrLf _ & Err.Description, _ vbCritical + vbOKOnly, _ "Error en la clase CDeposito " & Err.Source Resume Salir End Sub ' Mtodo para extraer del depsito Public Sub Extraer(ByVal Volumen As Single) On Error GoTo HayError Dim sngVolumenFinal As Single Dim sngVolumenATrasvasar As Single Dim blnCancelar As Boolean ' Si vamos a extraer una cantidad negativa _ asume que se quiere introducir If Volumen < 0 Then Introducir -Volumen Exit Sub End If ' Desencadenamos el primer evento RaiseEvent Extrayendo(Volumen, blnCancelar) If blnCancelar Then ' Cancelamos el proceso Exit Sub Else ' Pasamos Volumen a ValidaTrasvase ValidaTrasvase -Volumen
eduardo@olaz.net
Eduardo Olaz
Entrega 20
20 - 33
m_sngContenido = m_sngContenido - Volumen RaiseEvent Extraido(Volumen) End If Salir: Exit Sub HayError: MsgBox "Se ha producido el error n " & Err.Number _ & vbCrLf _ & Err.Description, _ vbCritical + vbOKOnly, _ "Error en la clase Deposito " & Err.Source Resume Salir End Sub ' Comprobacin de si el trasvase es posible Private Sub ValidaTrasvase(ByVal Volumen As Single) ' Este procedimiento se encarga de comprobar _ si es posible extraer o introducir en el depsito _ y generar los eventos y errores, en su caso. Dim sngVolumenFinal As Single sngVolumenFinal = m_sngContenido + Volumen Select Case sngVolumenFinal Case Is > m_sngCapacidad ' Si no cabe en el depsito _ desencadenamos el evento y generamos un error RaiseEvent TrasvaseImposible( _ m_sngContenido, _ Volumen) Err.Raise 1100, "Proceso de trasvase", _ "Se pretende introducir ms volumen" _ & " del que cabe en el depsito" Case Is < 0 ' Si no hay suficiente volumen en el depsito _ desencadenamos el evento y generamos un error RaiseEvent TrasvaseImposible( _ m_sngContenido, _
Comencemos a programar con VBA - Access
20 - 34
Volumen) Err.Raise 1110, "Proceso de trasvase", _ "Se pretende extraer ms volumen" _ & " que el que hay en el depsito" Exit Sub Case Is > m_sngNivelMaximo ' Si cabra en el depsito _ pero superara el nivel de seguridad mximo RaiseEvent ViolandoNivelDeSeguridad( _ m_sngContenido, _ Volumen, _ m_sngNivelMaximo) Case Is < m_sngNivelMinimo ' Si se puede extraer, pero al final quedara _ una cantidad inferior _ al nivel de seguridad mnimo RaiseEvent ViolandoNivelDeSeguridad( _ m_sngContenido, _ Volumen, _ m_sngNivelMaximo) End Select End Sub
Comentemos este cdigo. En general los procedimientos Property Get, que devuelven valores, tienen una estructura bastante elemental, por lo que no voy a hacer comentarios sobre ellos. Como casi siempre, los procedimientos que presentan algo ms de complicacin son los Property Let, por las validaciones a efectuar antes de admitir los nuevos datos. Suelo tener por costumbre que, si el valor a introducir es idntico al existente anteriormente, simplemente hago salir del procedimiento sin cambiar nada. Este es un tipo de actuacin que no siempre ser la adecuada. En determinadas circunstancias es preciso que se registre un intento de asignar un valor a una propiedad idntico al que tena previamente. Veamos la propiedad NivelMaximo: Esta propiedad devuelve y establece el nivel mximo de seguridad. Normalmente se asignar despus de definir la capacidad del depsito, y lgicamente no debe ser ni negativa ni mayor que la capacidad total del depsito. Tampoco puede ser inferior al nivel de seguridad mnimo del mismo. Para la propiedad NivelMinimo, podemos considerar lo dicho en la propiedad anterior, solo que no debe ser mayor que el nivel mximo de seguridad. Por todo esto, lo normal sera primero definir la capacidad total del depsito, a continuacin las propiedades de los niveles de seguridad Mximo y Mnimo, y finalmente el contenido
eduardo@olaz.net
Eduardo Olaz
Entrega 20
20 - 35
actual, que como inicialmente ser cero, al llamar a la propiedad Contenido ejecutar el procedimiento Introducir. Podramos haber definido eventos que se dispararan en el momento que cambiramos la capacidad del depsito, o sus niveles de seguridad, pero como stas sern propiedades que normalmente se definirn en el momento de la creacin del depsito, y no cambiarn durante su vida til, no he considerado pertinente escribirlos. En una clase desarrollada para su explotacin real, seguramente s habra que considerarlos. La propiedad Contenido devuelve o establece la cantidad contenida ahora en el depsito. Si en el mundo real queremos establecer una cantidad para el contenido de un depsito, realizaramos los siguientes pasos. Comprobaramos el volumen existente en el depsito Si la cantidad es idntica a la que queremos que contenga, se acaba el problema. Si la cantidad que queremos que tenga es mayor que la existente en el depsito, aadiremos la diferencia entre lo que queremos que haya y lo que realmente hay. Si es mayor que la que queremos extraeremos la diferencia.
Select Case Volumen Case Is > m_sngContenido Introducir Volumen - m_sngContenido Case Is < m_sngContenido Extraer m_sngContenido - Volumen End Select
Llama al mtodo Introducir al mtodo Extraer pasndole como parmetro el volumen que ser necesario trasvasar. Es una de las ventajas de trabajar con objetos, ya que podemos aplicar al cdigo una lgica de procesos muy cercana a lo que ocurrira en el mundo real, con lo que el cdigo se hace ms estructurado, modular y comprensible. Vamos a analizar primero el mtodo Introducir. Lo primero que hace es comprobar si el Volumen a introducir es negativo, lo que equivaldra a que tendramos que extraer. Por tanto, si fuera as llamara al mtodo Extraer.
20 - 36
valor True, con lo que se interrumpira el proceso de introduccin. Este proceso se realiza en el segmento de cdigo:
If blnCancelar Then ' Cancelamos el proceso Exit Sub Else ValidaTrasvase Volumen m_sngContenido = m_sngContenido + Volumen RaiseEvent Introducido(Volumen) End If
Si no se ha interrumpido el proceso, llama a procedimiento ValidaTrasvase, que se encarga de comprobar si las cantidades estn en los mrgenes correctos. El procedimiento ValidaTrasvase, comprueba si es posible efectuar la operacin de trasvase en su totalidad, es decir que durante el trasvase no vamos a intentar meter ms volumen del que cabe en el depsito, ni vamos a intentar extraer ms volumen del que actualmente hay. Si ocurriera uno de estos dos casos, disparara el evento TrasvaseImposible informando del contenido actual del depsito y del volumen que se pretende trasvasar. A continuacin, si intentara introducir ms cantidad de la que cabe en el depsito, generara el error n 1100, o el error 1110 si lo que se pretendiera es extraer ms cantidad de la existente. Estos errores seran captados por el procedimiento que ha hecho la llamada de comprobacin Introducir o Extraer. La siguiente tarea es comprobar si volumen final es mayor que el nivel mximo, o menor que el nivel mnimo, caso en el que disparar el evento ViolandoNivelDeSeguridad. A este evento le pasa como parmetros el contenido actual del depsito, el volumen a trasvasar y el nivel de seguridad, Mximo Mnimo, violado. Si no se ha generado ningn error significa que la operacin de trasvase es posible, aunque estuviese fuera de los lmites de seguridad. Por ello, al retomar el control, el procedimiento Introducir, realiza el trasvase y genera el evento Introducido.
Eduardo Olaz
Entrega 20
20 - 37
Vamos ahora a ver cmo podemos llegar a manejar los eventos generados, y cmo les podramos dar una utilidad prctica. A la hora de declarar un objeto de forma que se puedan manejar sus eventos, es preciso hacerlo con la palabra WithEvents.
Tampoco podemos declararlo en un mdulo normal; debe ser dentro de un mdulo de una clase, ya sea una clase normal, o en el mdulo de clase de un formulario o informe. Cuando declaramos un objeto con WithEvents, debemos simplemente declarar su variable, y despus crear el objeto, o asignarle un objeto del tipo declarado. Una forma correcta de hacerlo sera, por ejemplo, usando el evento Al cargar (Load) del formulario. Tambin podramos usar el mismo evento para asignarle los valores iniciales. Por ejemplo abrimos un formulario nuevo y en su mdulo de clase escribimos:
20 - 38
' Le ponemos un ttulo al formulario Caption = " Depsito marca ACME" ' Creamos el objeto Deposito Set Deposito = New CDeposito ' Le asignamos los valores iniciales With Deposito .Capacidad = 1000 .NivelMaximo = 900 .NivelMinimo = 100 End With ' Limitamos la cantidad a trasvasar sngTrasvaseMaximo = 500 End Sub
Vemos que creamos el objeto de tipo CDeposito y se lo asignamos a la variable Deposito en la lnea
Tambin podramos poner otras etiquetas para indicar qu es lo que contienen. Es lo que se ha hecho en el ejemplo. En el modo diseo, el formulario podra adquirir una apariencia semejante a sta:
eduardo@olaz.net
Eduardo Olaz
Entrega 20
20 - 39
La intencin es que al presionar un botn, se realicen las operaciones de Introducir y Extraer en el depsito el volumen definido en el cuadro de texto txtCantidad mostrando en las etiquetas el contenido actual del depsito. Como la capacidad del depsito la hemos definido como de 1000 unidades, una de las restricciones que vamos a poner es que no se puedan trasvasar en ningn sentido ms de 500 unidades de una vez. Requisito solicitado a ltima hora por los tcnicos de la refinera. El cdigo del formulario sera este:
Option Explicit Dim WithEvents Deposito As CDeposito Dim sngTrasvaseMaximo As Single Private Sub Form_Load() ' Le ponemos un ttulo al formulario Caption = " Depsito marca ACME" ' Creamos el objeto Deposito Set Deposito = New CDeposito ' Le asignamos los valores iniciales With Deposito .Capacidad = 1000 .NivelMaximo = 900 .NivelMinimo = 100 ' Mostramos los datos lblCapacidad.Caption = CStr(.Capacidad) lblMaximo.Caption = CStr(.NivelMaximo) lblMinimo.Caption = CStr(.NivelMinimo) ' Limitamos la cantidad a trasvasar sngTrasvaseMaximo = .Capacidad / 2 End With MuestraContenido End Sub ' Creamos los manejadores de los eventos _ del objeto Deposito Private Sub Deposito_Introduciendo( _ ByVal Volumen As Single, _ ByRef Cancelar As Boolean) ControlDeVolumen Volumen, Cancelar End Sub
Comencemos a programar con VBA - Access
20 - 40
Private Sub Deposito_Extrayendo( _ ByVal Volumen As Single, _ ByRef Cancelar As Boolean) ControlDeVolumen Volumen, Cancelar End Sub Public Sub Deposito_ViolandoNivelDeSeguridad( _ ByVal ContenidoActual As Single, _ ByVal VolumenATrasvasar As Single, _ ByVal NivelDeSeguridad As Single) Dim strMensaje As String If ContenidoActual + VolumenATrasvasar _ > NivelDeSeguridad Then strMensaje = "superar el nivel de seguridad" Else strMensaje = "no alcanzar el nivel de seguridad" End If MsgBox "Tras el trasvase, el depsito" _ & vbCrLf _ & strMensaje, _ vbInformation + vbOKOnly, _ "Violacin de niveles de seguridad" End Sub Private Sub ControlDeVolumen( _ ByVal Volumen As Single, _ ByRef Cancelar As Boolean) ' Con este procedimiento controlamos _ que no se trasvase ms de lo indicado If Abs(Volumen) > sngTrasvaseMaximo Then MsgBox CStr(Volumen) & " es demasiado volumen" _ & vbCrLf _ & "para trasvasarlo de una vez", _ vbCritical + vbOKOnly, _ "Cantidad a trasvasar inadecuada" Cancelar = True End If End Sub
eduardo@olaz.net
Eduardo Olaz
Entrega 20
20 - 41
Private Sub cmdExtraer_Click() Dim sngVolumen As Single sngVolumen = Val(Nz(txtVolumen, 0)) If sngVolumen <> 0 Then Deposito.Extraer sngVolumen Else Exit Sub End If MuestraContenido End Sub Private Sub cmdIntroducir_Click() Dim sngVolumen As Single sngVolumen = Val(Nz(txtVolumen, 0)) If sngVolumen <> 0 Then Deposito.Introducir sngVolumen Else Exit Sub End If MuestraContenido End Sub Private Sub MuestraContenido() lblContenido.Caption = CStr(Deposito.Contenido) End Sub
Es interesante fijarse en el procedimiento ControlDeVolumen que comprueba si la cantidad a trasvasar es excesiva. Este procedimiento es llamado desde los manejadores de eventos del depsito
Deposito_Introduciendo y Deposito_Extrayendo.
Los dos manejadores le pasan el parmetro Cancelar, que han recibido desde los correspondientes eventos Introduciendo y Extrayendo. Si en ControlDeVolumen se hace que Cancelar tome el valor True, como as sucede si la cantidad a trasvasar es mayor de lo permitido por la cantidad asignada a la variable sngTrasvaseMaximo, ese valor se transmite al manejador del evento, y desde ste al propio evento, y por tanto a los mtodos Extraer e Introducir del objeto Deposito. Cuando esto sucede se interrumpe el proceso de trasvase. Podemos comprobar que se muestran los correspondientes mensajes de advertencia si los valores de volumen estn fuera de los valores admitidos, o si se violan los lmites de seguridad. Tambin vemos que el propio objeto genera los mensajes de error, como podemos apreciar en los siguiente grficos:
Comencemos a programar con VBA - Access
20 - 42
eduardo@olaz.net
Eduardo Olaz
Entrega 20
20 - 43
Vamos que los mensajes de advertencia funcionan como est previsto. El procedimiento MuestraContenido muestra la cantidad existente tras cada trasvase.
Comencemos a programar con VBA - Access
20 - 44
Como sugerencia, la clase CDeposito, se podra tomar como base, con pequeas modificaciones, por ejemplo para controlar la validez de entradas y salidas de un artculo en un almacn, e incluso para gestionar su stock.
Initialize se genera justo despus de que se cree el objeto, o lo que es lo mismo, tras
crearse un ejemplar de la clase o una instancia de la misma. Esto sucede tras crear un objeto con la palabra clave New, ya sea mediante
Set
Me permito recordar aqu que no se puede crear directamente un objeto precedido por la palabra clave WithEvents, directamente con As New. El evento Terminate se genera justo antes de que se destruya el objeto. El evento Initialize puede usarse para asignar propiedades por defecto al objeto en un inmediatamente despus de haber sido creado. Hay que recordar que VBA, al contrario que otros lenguajes como VB.Net o C#, no posee los llamados Constructores, que permiten crear un objeto asignndole valores concretos a sus propiedades a la vez que stos se crean. Por ejemplo en VB.Net, sera factible asignar las propiedades Nombre, Apellido y FechaDeNacimiento al objeto Alumno de una clase tipo ClsPersona, en el momento de crearse.
Set
Ms adelante veremos cmo podemos superar esta carencia de los objetos de VBA y crear seudo-constructores. Para utilizar el evento Initialize, se hara de la siguiente forma
Eduardo Olaz
Entrega 20
20 - 45
Vamos a programar una sencilla clase a la que pondremos como nombre CTrabajador y a instanciarla, para ver el efecto de los eventos Initialize y Terminate.
Option Explicit Private m_strNombre As String Private m_datFechaContrato As Date Private Sub Class_Initialize() ' Ponemos como fecha de contrato la de hoy FechaContrato = Date MsgBox "Contratado un nuevo trabajador" _ & vbCrLf _ & "Fecha provisional del contrato: " _ & Format(FechaContrato, "d/m/yyyy"), _ vbInformation + vbOKOnly, _ "Class_Initialize" End Sub Public Property Get Nombre() As String Nombre = m_strNombre End Property Public Property Let Nombre(ByVal NuevoNombre As String) If m_strNombre <> NuevoNombre Then m_strNombre = NuevoNombre End If End Property Public Property Get FechaContrato() As Date FechaContrato = m_datFechaContrato End Property Public Property Let FechaContrato( _ ByVal NuevaFecha As Date) If m_datFechaContrato <> NuevaFecha Then m_datFechaContrato = NuevaFecha End If End Property Private Sub Class_Terminate()
Comencemos a programar con VBA - Access
20 - 46
MsgBox "El trabajador " & Nombre _ & vbCrLf _ & "contratado el " _ & Format(FechaContrato, "d/m/yyyy") _ & vbCrLf _ & "ha sido despedido.", _ vbInformation + vbOKOnly, _ "Class_Terminate" End Sub
Para probar la clase y comprobar los efectos de los eventos, escribimos en un mdulo normal el siguiente cdigo:
Public Sub PruebaCTrabajador() Dim Empleado As New CTrabajador Empleado.Nombre = "Antonio" Empleado.FechaContrato = #9/3/2005# End Sub
Al ejecutar el procedimiento PruebaCTrabajador, nos muestra en pantalla primero
Eduardo Olaz
Entrega 20
20 - 47
Public Property Get Nombre() As String Nombre = strNombre End Property Public Property Let Nombre(ByVal NuevoNombre As String) strNombre = NuevoNombre End Property
Con ello podramos escribir desde cualquier parte del cdigo, incluso desde mdulos diferentes, lo siguiente
Nombre, podramos haber hecho Public Sub PruebaProperty() MduloConPropiedades.Nombre = "Pepe" Debug.Print MduloConPropiedades.Nombre End Sub
Por lo tanto, tenemos plena libertad para aprovechar las Property en mdulos y definir propiedades, con las ventajas que stas tienen y sin necesidad de efectuar instancias de clases. No se interprete esto como una sugerencia para no utilizar las clases, sino ms bien todo lo contrario.
VBA - Access
Entrega
21
Eduardo Olaz
21 - 2
Procedimientos Friend
Hasta ahora, los procedimientos de un mdulo los hemos podido definir como Privados mediante la palabra Private o Dim, o pblicos mediante la palabra reservada Public. En las clases hay otro tipo de alcance para definir un mtodo o propiedad; este nuevo alcance viene definido por la palabra clave Friend.
Friend protege a los mtodos y propiedades de una clase, de forma que slo pueden ser
invocados por procedimientos del mismo proyecto en el que est definida la clase. Para los procedimientos del propio proyecto es como si estuviera declarado como Public. Para el cdigo externo al proyecto los mtodos y propiedades declarados como Friend estn ocultos. Podemos incluso declarar el procedimiento Property Get de una propiedad Public y el Property Let, o Property Set como Friend. Un aspecto muy prctico de esta forma de definir las propiedades es que as conseguimos que la aplicacin del propio proyecto pueda leer o establecer una propiedad, mientras que al cdigo externo, de otras aplicaciones o proyectos, esa propiedad sera de slo lectura. En el siguiente ejemplo la propiedad Nombre sera de slo lectura para un cdigo externo, y de Lectura / Escritura para el cdigo del propio proyecto.
Public Property Get Nombre() As String Nombre = m_strNombre End Property Friend Property Let Nombre(NuevoNombre As String) If m_strNombre <> NuevoNombre Then m_strNombre = NuevoNombre End If End Property
eduardo@olaz.net
Eduardo Olaz
Entrega 21
21 - 3
vbWhite ---Constante
vbYellow -vbCyan
21 - 4
128 0 0 128
0 128 0 0
-----
El sistema que usa VBA, permite trabajar con ms de 16 millones de colores diferentes, en concreto 16.777.216 lo que es lo mismo doscientos cincuenta y seis elevado al cubo. Una cantidad ms que razonable de posibles valores para nuestra vida diaria. Para obtener el valor Long de un color, sabiendo las proporciones de cada color primario Rojo, Verde y Azul, VBA nos provee de la funcin RGB.
Funcin RGB
Devuelve un valor de tipo Long, correspondiente a un color expresado en sus colores primarios Rojo (Red), Verde (Green) y Azul (Blue). Sintaxis
15106112 mod 256 64 (rojo) Int(15106112 / 256) mod 256 128 (verde) Int(15106112 / 65536) 230 (azul)
Nota: Los monitores de ordenador, as como en general los monitores de televisin, usan el sistema de color Aditivo, sumndose los valores correspondientes a los tres colores luz primarios.
eduardo@olaz.net
Eduardo Olaz
Entrega 21
21 - 5
Por el contrario, en la industria grfica, as como en las pinturas o las impresoras de color, se usa el llamado Mtodo Sustractivo, en el que los colores primarios son el amarillo, el magenta, el azul cian y el negro. Esto es as en los sistemas que usan el color blanco del papel como un color ms y para tintes no opacos o en distribucin de las tintas mediante pequeos puntos de pigmento. En caso contrario habra que aadir adems el color Blanco como un primario ms.
Mtodo Sustractivo
Mtodo Aditivo
En la imagen superior tenemos los dos tipos de mtodos y los resultados de su uso simultneo
Option Explicit Private Const con8Bits As Long = 256 Private Const con16Bits As Long = 65536 ' 256*256
Comencemos a programar con VBA - Access
21 - 6
Private m_lngColor As Long Public Property Get Color() As Long Color = m_lngColor End Property Public Property Let Color(NuevoColor As Long) Dim blnCancelar As Boolean If m_lngColor <> NuevoColor Then m_lngColor = NuevoColor End If End Property Public Property Get ColorRojo() As Byte ColorRojo = m_lngColor Mod con8Bits End Property Public Property Let ColorRojo(NuevoColor As Byte) Dim BytRojo As Byte Dim lngColor As Long BytRojo = m_lngColor Mod con8Bits If BytRojo <> NuevoColor Then Color = RGB(NuevoColor, ColorVerde, ColorAzul) End If End Property Public Property Get ColorVerde() As Byte ColorVerde = Int(m_lngColor / con8Bits) Mod con8Bits End Property Public Property Let ColorVerde(NuevoColor As Byte) Dim BytVerde As Byte Dim lngColor As Long BytVerde = Int(m_lngColor / con8Bits) Mod con8Bits If BytVerde <> NuevoColor Then Color = RGB(ColorRojo, NuevoColor, ColorAzul) End If End Property
eduardo@olaz.net
Eduardo Olaz
Entrega 21
21 - 7
Public Property Get ColorAzul() As Byte ColorAzul = Int(m_lngColor / con16Bits) End Property Public Property Let ColorAzul(NuevoColor As Byte) Dim BytAzul As Byte Dim lngColor As Long BytAzul = Int(m_lngColor / con16Bits) If BytAzul <> NuevoColor Then Color = RGB(ColorRojo, ColorVerde, NuevoColor) End If End Property
Como se puede apreciar es una clase relativamente sencilla, que no tiene implementado ningn tipo de evento. Posee las propiedades Color, ColorRojo ColorVerde y ColorAzul que acceden a la variable privada m_lngColor. Veamos cmo la podramos utilizar.
21 - 8
En el segundo las coordenadas x e y de la esquina inferior derecha. Hay que tomar en cuenta que la coordenada y crece hacia abajo. Cuando en una entrega ms avanzada estudiemos la programacin de los informes, veremos cmo se puede cambiar todo esto.
vbRed indica que se debe pintar en rojo. B indica que en vez de una lnea se dibujar un rectngulo y F que se dibujar como un
rectngulo slido. Hasta que lleguemos al captulo de los mtodos grficos, en informes, no voy a dar pormenores de esta instruccin, por lo que, como he comentado ms arriba, si desea ms informacin le recomiendo que consulte en la ayuda de VBA, el mtodo Line. Nota importante: En Access slo se pueden usar los mtodos grficos dentro de los informes. Al contrario que en Visual Basic, no se pueden usar ni en formularios ni en otros tipos de objetos, como los objetos Image. Simplemente para abrir boca vamos a ver cmo podemos usar este mtodo. Seleccionamos la seccin Detalle del informe, mostramos la ventana de Propiedades, activamos la pestaa de Eventos, hacemos clic en el evento Al imprimir, pulsamos el botn con los tres botoncitos y seleccionamos el Generador de cdigo. Se nos abrir el mdulo de clase del formulario en el manejador del evento Format
Private Sub Detalle_Format( _ Cancel As Integer, _ FormatCount As Integer) Line(100,200)-(500, 500), vbRed, BF Line(400,800)-(800, 1500), vbBlack, BF Line(2000,2800)-(1000, 1000), vbBlue, BF Line (0, 0)-(5000, 5000), vbRed, B End Sub
Si abrimos el informe, nos mostrar en pantalla lo siguiente:
eduardo@olaz.net
Eduardo Olaz
Entrega 21
21 - 9
Como lo anterior era una prueba, eliminamos las sentencias Line anteriores y escribimos:
Option Explicit Private Sub Detalle_Format( _ Cancel As Integer, _ FormatCount As Integer) ' Lado del cuadrado Const conLado As Long = 200 ' Separacin entre cuadrados Const conSeparador As Long = 60 Dim Color As New ClsColor Dim i As Long, j As Long Dim X As Long ' Distancia al margen izquierdo Dim Y As Long ' Distancia al borde superior For i = 1 To 32 ' Transicin de colores en las lineas de cuadrados Color.ColorRojo = 256 - 8 * (i - 1) - 1 Color.ColorAzul = 8 * i - 1 Y = conLado * i + conSeparador * (i - 1) For j = 1 To 32 ' Transicin en las columnas de cuadrados Color.ColorVerde = 8 * j - 1 X = conLado * j + conSeparador * (j - 1) Line (X, Y)-(X + conLado, Y + conLado), _ Color.Color, BF Next j
Comencemos a programar con VBA - Access
21 - 10
Si recorremos el permetro vemos que se producen estas transiciones de color Del rojo al amarillo Del amarillo al azul turquesa Del azul al azul turquesa Del rojo al azul
Pasando por colores intermedios como los violetas, naranjas, toda una gama de verdes y algunos colores tierra. Este resultado, en cierto modo espectacular, lo hemos conseguido simplemente escribiendo unas pocas lneas de cdigo. Y a m personalmente me gusta. Si hubiramos intentado conseguir este resultado trabajando de la forma convencional, probablemente hubiramos necesitado ms cdigo, o un cdigo menos intuitivo.
eduardo@olaz.net
Eduardo Olaz
Entrega 21
21 - 11
Option Explicit Enum PaletaColores eoColorRojo = vbRed eoColorVerde = vbGreen eoColorAzul = vbBlue eoColorAmarillo = vbYellow eoColorMagenta = 16711935 eoColorCian = vbCyan eoColorNaranja = 33023 eoColorAzulMar = 16744192 eoColorVioleta = 8388736 eoColorBlanco = vbWhite eoColorGrisClaro = 13421772 eoColorGrisMedio = 8421504 eoColorGrisOscuro = 4210752 eoColorNegro = vbBlack End Enum
' ' ' ' ' ' ' ' ' ' ' ' ' '
RGB(255, RGB( 0, RGB( 0, RGB(255, RGB(255, RGB( 0, RGB(255, RGB( 0, RGB(128, RGB(255, RGB(204, RGB(128, RGB( 64, RGB( 0,
Vemos que, por ejemplo la constante eoColorVioleta, de valor 8388736, es un miembro del tipo enumerado PaletaColores, perteneciente a la clase ClsColor. Si pulsamos en PaletaColores, nos informa que es una constante de tipo Enum, miembro de la clase ClsColor y con un alcance Pblico.
Comencemos a programar con VBA - Access
21 - 12
Cmo es posible que se comporte como un conjunto de constantes pblicas si no se ha utilizado delante la palabra clave Public? Si miramos la ayuda de VBA, veremos que indica literalmente lo siguiente: Los tipos Public Enum de un mdulo de clase no son miembros de la clase; sin embargo, se escriben en la biblioteca de tipos. Aparentemente hay una contradiccin con lo que dice el explorador de objetos. En realidad podramos interpretar que es como si pasara a formar parte de la biblioteca de VBA, por lo que no sera necesario crear un ejemplar de la clase ClsColor para tener acceso a los valores de cada una de las constantes. Este comportamiento tiene su lgica, ya que es muy habitual utilizar constantes enumeradas como tipos de parmetro para funciones, procedimientos o variables, tanto pblicas como privadas. Con ello podemos directamente definir parmetros para procedimientos del tipo enumerativo definido en cualquiera de las clases que tengamos en el proyecto, como si estos tipos enumerados hubiesen sido declarados Public en un mdulo estndar. Supongamos que hemos ya escrito la clase ClsColor y que en un mdulo normal escribimos la siguiente funcin:
Public Function NombreColor( _ Color As PaletaColores _ ) As String Select Case Color Case eoColorRojo NombreColor = "Rojo" Case eoColorVerde NombreColor = "Verde" Case eoColorAzul NombreColor = "Azul" Case eoColorAmarillo NombreColor = "Amarillo" Case eoColorMagenta NombreColor = "Magenta" Case eoColorCian NombreColor = "Cian" Case eoColorNaranja NombreColor = "Naranja"
eduardo@olaz.net
Eduardo Olaz
Entrega 21
21 - 13
Case eoColorAzulMar NombreColor = "Azul marino claro" Case eoColorVioleta NombreColor = "Violeta" Case eoColorBlanco NombreColor = "Blanco" Case eoColorGrisClaro NombreColor = "Gris Claro" Case eoColorGrisMedio NombreColor = "Gris Medio" Case eoColorGrisOscuro NombreColor = "Gris Oscuro" Case eoColorNegro NombreColor = "Negro" Case Else NombreColor = "Color sin tabular" End Select End Function
A partir de este momento podemos llamar a esta funcin, y la ayuda en lnea nos mostrar las diferentes opciones de color que podemos seleccionar. Si escribimos unas lneas de cdigo que llamen a la funcin NombreColor veremos lo siguiente:
Un tema que, segn mi criterio, podra haberse mejorado en Visual Basic es que en un caso de estos, el compilador nos obligara a introducir slo valores incluidos en el tipo que se le ha asignado al parmetro; en este caso cualquier valor definido en PaletaColores. Por desgracia no es as. A la funcin PruebaEnumeracion podramos pasarle no slo valores definidos en la enumeracin PaletaColores, definida en la clase ClsColor, sino que tragara cualquier valor del tipo Long.
21 - 14
Supongo que los programadores de Microsoft consideraron que la sintaxis de VB debera tener este comportamiento ya que una constante enumerada no deja de ser una constante numrica de tipo Long. Podemos por lo tanto escribir un procedimiento que llame a la funcin NombreColor y que utilice cualquier valor no definido en PaletaColores.
Public Sub PruebaEnumeracion() Debug.Print "eoColorRojo: " & NombreColor(eoColorRojo) Debug.Print "vbRed: " & NombreColor(vbRed) Debug.Print "vbWhite: " & NombreColor(vbWhite) Debug.Print "vbGreen: " & NombreColor(vbGreen) Debug.Print "-1: " & NombreColor(-1) Debug.Print "0: " & NombreColor(0) Debug.Print "8388736: " & NombreColor(8388736) Debug.Print "RGB(255, 128, 0): " _ & NombreColor(RGB(255, 128, 0)) Debug.Print "eoColorGrisMedio: " _ & NombreColor(eoColorGrisMedio) End Sub
El resultado de ejecutar este procedimiento sera:
eoColorRojo: Rojo vbRed: Rojo vbWhite: Blanco vbGreen: Verde -1: Color sin tabular 0: Negro 8388736: Violeta RGB(255, 128, 0): Naranja eoColorGrisMedio: Gris Medio
Vemos que podemos pasar los valores: Directamente del tipo PaletaColores (eoColorRojo) Un color incluido en PaletaColores y tambin definido en la clase de VBA
ColorConstants (vbRed) Un valor no incluido en PaletaColores (-1) Un color incluido en PaletaColores con su valor numrico (0) (8388736) Lo mismo pero mediante una funcin (RGB(255, 128, 0))
eduardo@olaz.net
Eduardo Olaz
Entrega 21
21 - 15
Un utilidad adicional e interesante de la funcin PruebaEnumeracion() es que nos muestra un ejemplo de cmo podemos construir nuestra propia funcin para devolver un valor que queremos asociar a una constante numrica. Como ya vimos anteriormente, a una clase se le puede aadir una serie de Funciones y procedimientos Sub pblicos a los que llamamos mtodos. Por ejemplo, para aumentar la potencia de la clase clsColor, le vamos a aadir los mtodos :
ConvierteAGris que convertira el color original a un tono gris. Un tono gris tiene
igual cantidad de Rojo, Verde y Azul. Para simplificar el cdigo lo que har ser convertir un color a su promedio de los tres componentes; advirtiendo de antemano que ese no sera el procedimiento ortodoxo para hacerlo correctamente. Podramos crear todo un juego de filtros grficos, al estilo de los de cualquier programa de edicin grfica, pero no es el objeto de este documento. Veamos cul podra ser el cdigo de estos tres mtodos que vamos a aadir a la clase:
Public Sub AclaraColor(Optional ByVal Porcentaje As Single = 0.2) Dim intRojo As Integer Dim intVerde As Integer Dim intAzul As Integer ' Definimos como aclarado mnimo el 2% If Porcentaje < 0.02 Or Porcentaje > 1 Then Exit Sub Else intRojo = ColorRojo + (255 - ColorRojo) * Porcentaje If intRojo > 255 Then intRojo = 255 End If intVerde = ColorVerde + (255 - ColorVerde) * Porcentaje If intVerde > 255 Then intVerde = 255
Comencemos a programar con VBA - Access
21 - 16
End If intAzul = ColorAzul + (255 - ColorAzul) * Porcentaje If intAzul > 255 Then intAzul = 255 End If End If m_lngColor = RGB(intRojo, intVerde, intAzul) End Sub Public Sub OscureceColor( _ Optional ByVal Porcentaje As Single = 0.2) Dim intRojo As Integer Dim intVerde As Integer Dim intAzul As Integer ' Definimos como oscurecimiento mnimo el 2% If Porcentaje < 0.02 Or Porcentaje > 1 Then Exit Sub Else intRojo = ColorRojo * (1 - Porcentaje) If intRojo < 0 Then intRojo = 0 End If intVerde = ColorVerde * (1 - Porcentaje) If intVerde < 0 Then intVerde = 0 End If intAzul = ColorAzul * (1 - Porcentaje) If intAzul < 0 Then intAzul = 0 End If End If m_lngColor = RGB(intRojo, intVerde, intAzul) End Sub Public Sub InvierteColor() Dim bytRojo As Byte Dim bytVerde As Byte Dim bytAzul As Byte bytRojo = ColorRojo
eduardo@olaz.net
Eduardo Olaz
Entrega 21
21 - 17
bytVerde = ColorVerde bytAzul = ColorAzul m_lngColor = RGB(bytRojo, bytVerde, bytAzul) End Sub Public Sub ConvierteAGris() Dim bytRojo As Byte Dim bytVerde As Byte Dim bytAzul As Byte Dim bytGris As Byte bytRojo = ColorRojo bytVerde = ColorVerde bytAzul = ColorAzul bytGris = CByte((ColorRojo + ColorVerde + ColorAzul) / 3) m_lngColor = RGB(bytGris, bytGris, bytGris) End Sub
Ms adelante vamos a realizar un ejemplo en el que utilizaremos alguno de estos mtodos.
21 - 18
Herencia y Polimorfismo
Adems de la Encapsulacin,ocultacin de algunos miembros de una clase, con acceso controlado a los mismos, que ya vimos en la entrega anterior, comentamos que en el paradigma de la Programacin Orientada a Objetos, se utilizan otras dos caractersticas: La Herencia y el Polimorfismo. Hay una cuarta caracterstica, la Herencia Mltiple, pero sta no se considera como estrictamente necesaria. Por herencia se entiende la capacidad de crear una clase, simplemente declarando que una clase nueva hereda de otra previamente existente. La clase creada por herencia, automticamente debera tener los mismos mtodos y propiedades que la clase Padre, o al menos aqullos que no estn especificados como no heredables. Esto ocurre, por ejemplo, con VB.Net. Si tenemos una clase clsPersona que tiene las propiedades
Edad NombreCompleto
Si creramos la clase clsTrabajador de esta manera
Eduardo Olaz
Entrega 21
21 - 19
Por desgracia, algo que est implementado en lenguajes como VB.Net y C#, estrictamente hablando, no lo est en VBA ni en Visual Basic. Es decir, VBA no tiene ni Polimorfismo ni Herencia. La buena noticia es que podemos disear mecanismos que, de alguna manera, emulen el Polimorfismo y la Herencia en VBA. Uno de estos mecanismos es la utilizacin de Interfaces. Pero sin la potencia propia de lenguajes como VB.Net o C#,
Interfaces
Una interfaz es el conjunto de propiedades y mtodos de una clase a los que se puede acceder mediante el cdigo. En otras palabras, aquellos atributos de la clase a los que podemos acceder directamente desde una instancia, o ejemplar de la clase, porque tienen un alcance de tipo Public. En resumidas cuentas es la cara (faz) que nos muestra la propia clase. En el caso concreto de la clase ClsColor, tenemos Propiedad Color de tipo Long de Lectura / Escritura Propiedad ColorRojo de tipo Byte de Lectura / Escritura Propiedad ColorVerde de tipo Byte de Lectura / Escritura Propiedad ColorAzul de tipo Byte de Lectura / Escritura
Si por ejemplo hubiramos definido un mtodo pblico de tipo sub llamado AclaraColor y un mtodo pblico de tipo Function llamado ColorInverso, que devolviera un valor Long, ambos mtodos tambin formaran parte de la interfaz de ClsColor. El respetar la interfaz de las clases, es importante, ya que un programador espera que una clase se comporte de una forma determinada. Por ejemplo, si hemos definido una nueva versin de una clase concreta, deberemos respetar el interfaz de la clase anterior. Un programador espera que si una clase tena la propiedad Nombre, la nueva versin de la clase, la siga manteniendo, y que si esa propiedad tena un parmetro de tipo String, el parmetro de la propiedad Nombre en la nueva versin de la clase, sea tambin de tipo string. Una nueva versin de la clase, debe mantener las propiedades, los mtodos y los parmetros en propiedades y mtodos, que tena la clase anterior. Debe haber una compatibilidad hacia atrs entre una versin y las anteriores.. Si esto no ocurriera as, podran darse un gran nmero de errores en cascada, convirtiendo la labor de depuracin y mantenimiento de la aplicacin en un verdadero infierno. Ha odo hablar del infierno de las dlls, cuando no se han respetado las interfaces entre versin y versin?. Podra llegar a aceptarse que una nueva versin de la clase tuviera ms propiedades y mtodos, o que incluso alguna de las propiedades tuviera ms parmetros, siempre que estos fuesen de tipo opcional, aunque esto, estrictamente hablando, sera romper el contrato de la interfaz. Lo que nunca debera ocurrir es que las llamadas a los procedimientos o propiedades de la clase base, sean incompatibles con las llamadas a la nueva versin de la clase. Por eso se suele decir que la Interfaz de una clase es un contrato que debe respetarse.
Comencemos a programar con VBA - Access
21 - 20
Existe un tipo especial de clases que, en VBA, podramos definir como Clases Abstractas que declaran las propiedades y los mtodos que van a publicar al exterior, pero sin tener desarrollado, ni en las propiedades ni en los mtodos, cdigo para implementarlos. A estas clases se les llama Interfaces. Se suelen grabar con el nombre precedido de la letra I. Por ejemplo IColor, IElementoGrfico, IDireccionable...
Option Explicit Function Tipo() As String ' Cdigo del mtodo a implementar en la clase End Function Sub Comer(Bicho As Object) ' Cdigo del mtodo a implementar en la clase End Sub Function Sonido() As String ' Cdigo del mtodo a implementar en la clase End Function
Como vemos, la interfaz Bicho va a mostrar al resto del mundo, los mtodos Tipo, Comer y Sonido. Los mtodos Tipo, y Sonido devuelven una cadena String. La clase clsGato mostrar la propiedad Nombre, y los mtodos Correr, Cazar y Maullar La clase clsPerro mostrar Nombre, Correr, Cazar y Ladrar La clase clsLeon mostrar Nombre, Correr, Cazar y Rugir Las tres clases implementan la interfaz IBicho. Aunque Access acepta mtodos sin ninguna implementacin, conviene al menos poner una lnea comentada, ya que si quisiramos pasar el cdigo a Visual Basic, su compilador podra eliminar los mtodos sin implementar, considerando que no tienen ninguna utilidad, y dar problemas a la hora de compilar y ejecutar el cdigo.
eduardo@olaz.net
Eduardo Olaz
Entrega 21
21 - 21
IBicho
IBicho
Vamos ahora a crear la clase clsGato, para lo que insertamos el correspondiente mdulo de clase. Para que pueda implementar la interfaz IBicho, en el encabezado de la clase ponemos la instruccin:
Implements IBicho
Tras esto, en el editor de cdigo podemos seleccionar la interfaz IBicho del gato, y el editor nos escribir la estructura del mtodo IBicho_Tipo.
Option Explicit
21 - 22
Implements IBicho Private Sub IBicho_Comer(Bicho As Object) End Sub Private Function IBicho_Tipo() As String End Function Private Function IBicho_Sonido() As String End Function
Como adelanto, dir que cuando hagamos que el objeto clsGato muestre la interfaz IBicho, mostrar slo estos mtodos. Si la interfaz IBicho tuviera alguna propiedad o mtodo Sub, tambin los mostrara. De todo esto veremos un ejemplo ms adelante. Si observamos los mtodos anteriores, tienen la siguiente estructura
Private Property Let Interfaz_Propiedad (parmetros) Private Property Set Interfaz_Propiedad (parmetros) Private Property Get Interfaz_Propiedad (parmetros) _ As TipoDevuelto
Esto nos puede recordar algo a la construccin de los manejadores de eventos que hemos visto con anterioridad. Puede sorprender que a pesar de que stos son los mtodos y propiedades que va a mostrar la interfaz, se declaren como Private. Esto es as para que no sean visibles cuando creamos una instancia de la clase que implementa la interfaz. Para comprobarlo, aadiremos a la clase la estructura correspondiente al resto de los miembros pblicos de clsGato; es decir la propiedad Nombre, y los mtodos Correr, Cazar y Maullido. Tras todo ello lo tendremos el siguiente cdigo:
Eduardo Olaz
Entrega 21
21 - 23
End Property Public Property Let Nombre(ByVal NuevoNombre As String) End Property Public Function Correr() As Long End Function Public Function Cazar(Bicho As Object) As String End Function Public Function Maullido() As String End Function Private Sub IBicho_Comer(Bicho As Object) End Sub Private Function IBicho_Tipo() As String End Function Private Function IBicho_Sonido() As String End Function
Hasta aqu slo tenemos el esqueleto de la clase, que posteriormente completaramos. Para ver los efectos de todo esto, vamos a crear un mdulo estndar con un procedimiento que crear una instancia de la clase clsGato. En ese mdulo creamos el procedimiento ProbarInterfaz(). Conforme vamos escribiendo el cdigo vemos que, como era de esperar, nos muestra los atributos pblicos de la clase
21 - 24
Para ello hemos declarado una variable del tipo de la interfaz IBicho,:
Eduardo Olaz
Entrega 21
21 - 25
Dim gtoMicifuz As New clsGato Dim SerVivo As ISerVivo Dim Bicho As IBicho Set SerVivo = gtoMicifuz Set Bicho = gtoMicifuz
Ahora gtoMicifuz mostrara la interfaz de clsGato, SerVivo mostrara la interfaz de ISerVivo y Bicho la de IBicho. Las interfaces permiten hacer que objetos procedentes de clases distintas, muestren una serie de mtodos y propiedades iguales. Con ello conseguimos emular de una forma coherente el polimorfismo con Visual Basic y VBA. Cmo podramos utilizar esto? Vamos a crear las clases clsGato, clsPerro y clsLeon definitivas Clase clsGato:
Option Explicit Implements IBicho Private Const conlngDistancia As Long = 200 Private m_Nombre As String Private m_lngDistancia As Long Private Sub Class_Initialize() ' Asignamos una capacidad aleatoria _ de desplazamiento al gato Randomize Timer m_lngDistancia = conlngDistancia + Rnd * conlngDistancia End Sub Public Property Get Nombre() As String Nombre = m_Nombre End Property Public Property Let Nombre(ByVal NuevoNombre As String) m_Nombre = NuevoNombre End Property Public Function Correr() As Long Correr = m_lngDistancia
Comencemos a programar con VBA - Access
21 - 26
End Function Public Function Cazar(Bicho As Object) As String Cazar = "El gato " & Nombre _ & vbCrLf _ & "ha cazado al " _ & IBicho_Tipo _ & " " _ & Bicho.Nombre End Function Public Function Maullido() As String Maullido = "Miau!, Miau!" End Function Private Sub IBicho_Comer(Bicho As Object) MsgBox "El gato " & Nombre _ & vbCrLf _ & "se est comiendo a " _ & Bicho.Nombre End Sub Private Function IBicho_Tipo() As String IBicho_Tipo = "gato" End Function Private Function IBicho_Sonido() As String IBicho_Sonido = Maullido End Function
Podemos observar que desde dentro de un procedimiento de la Interfaz se puede llamar a un mtodo de la clase, y a la inversa. Esto ocurre, por ejemplo en
eduardo@olaz.net
Eduardo Olaz
Entrega 21
21 - 27
Clase clsPerro
Option Explicit Implements IBicho Private Const conlngDistancia As Long = 2000 Private m_Nombre As String Private m_lngDistancia As Long Private Sub Class_Initialize() Randomize Timer m_lngDistancia = conlngDistancia + Rnd * conlngDistancia End Sub Public Property Get Nombre() As String Nombre = m_Nombre End Property Public Property Let Nombre(ByVal NuevoNombre As String) m_Nombre = NuevoNombre End Property Public Function Correr() As Long Correr = m_lngDistancia End Function Public Function Cazar(Bicho As Object) As String Cazar = "El perro " & Nombre _ & vbCrLf _ & "ha cazado a " _ & Bicho.Nombre End Function Public Function Ladrido() As String Ladrido = "Guau guau!!" End Function Private Sub IBicho_Comer(Bicho As Object) MsgBox "El perro " & Nombre _
Comencemos a programar con VBA - Access
21 - 28
& vbCrLf _ & "se est comiendo a " _ & Bicho.Nombre End Sub Private Function IBicho_Tipo() As String IBicho_Tipo = "perro" End Function Private Function IBicho_Sonido() As String IBicho_Sonido = Ladrido End Function
Y este ser el cdigo de la Clase clsLeon:
Option Explicit Implements IBicho Private Const conlngDistancia As Long = 1000 Private m_Nombre As String Private m_lngDistancia As Long Private Sub Class_Initialize() ' Asignamos una capacidad aleatoria _ de desplazamiento al gato Randomize Timer m_lngDistancia = conlngDistancia + Rnd * conlngDistancia End Sub Public Property Get Nombre() As String Nombre = m_Nombre End Property Public Property Let Nombre(ByVal NuevoNombre As String) m_Nombre = NuevoNombre End Property Public Function Correr() As Long Correr = m_lngDistancia End Function
eduardo@olaz.net
Eduardo Olaz
Entrega 21
21 - 29
Public Function Cazar(Bicho As Object) As String Cazar = "El len " & Nombre _ & vbCrLf _ & "ha cazado al " _ & IBicho_Tipo _ & " " _ & Bicho.Nombre End Function Public Function Rugido() As String Rugido = "Grrrrrrr Grrrrrrrrrr!!" End Function Private Sub IBicho_Comer(Bicho As Object) MsgBox "El len " & Nombre _ & vbCrLf _ & "se est comiendo a " _ & Bicho.Nombre End Sub Private Function IBicho_Tipo() As String IBicho_Tipo = "len" End Function Private Function IBicho_Sonido() As String IBicho_Sonido = Rugido() End Function
Color, es el color que va a tener la lnea que define el permetro, por ejemplo de un rectngulo o una elipse. En los elementos grficos reales esta propiedad es ForeColor, y la que define el color del relleno o del fondo es BackColor, pero en este ejemplo vamos a simplificar sus atributos.
Comencemos a programar con VBA - Access
21 - 30
Left, es la distancia respecto del borde izquierdo, de la esquina superior izquierda, o del Top, es equivalente a la propiedad Left, pero respecto al borde superior. Whidth, es la anchura del elemento grfico. Height, es su altura.
Estas cuatro ltimas propiedades las poseen todos los controles visibles.
Option Explicit Property Get Color () As Long ' Cdigo de la propiedad a implementar en la clase End Property Property Let Color (NewColor As Long) ' Cdigo de la propiedad a implementar en la clase. End Property Property Get Left() As Integer ' Cdigo de la propiedad a implementar en la clase. End Property Property Let Left(ByVal X As Integer) ' Cdigo de la propiedad . . . End Property Property Get Top() As Integer ' Cdigo de la propiedad . . . End Property Property Let Top(ByVal Y As Integer) ' Cdigo de la propiedad . . . End Property Property Get Whidth() As Integer ' Cdigo de la propiedad . . . End Property
eduardo@olaz.net
Eduardo Olaz
Entrega 21
21 - 31
Property Let Whidth(ByVal NewValue As Integer) ' Cdigo de la propiedad . . . End Property Property Get Height() As Integer ' Cdigo de la propiedad . . . End Property Property Let Height(ByVal NewValue As Integer) ' Cdigo de la propiedad . . . End Property Property Get Canvas() As Object ' Cdigo de la propiedad . . . End Property ' Como la propiedad Canvas devuelve un objeto _ que es el "lienzo" sobre el que se va a pintar _ Ser del tipo Property Set Property Set Canvas(NewCanvas As Object) ' Cdigo de la propiedad . . . End Property Sub Draw() ' Cdigo del mtodo para Dibujar . . . End Sub
Como ya hemos comentado, no hemos desarrollado ninguna propiedad ni mtodo, stos deben ser desarrollados en las clases que implementen esta interfaz. Es tradicional poner una lnea comentada, en las propiedades y mtodos de las interfaces ya que, si estuviramos desarrollando una interfaz con Visual Basic de Visual Studio, el compilador podra llegar a eliminar un procedimiento en una clase que no tuviera escrito nada en su interior. En Access, el nico objeto que admite dibujar en su interior es el objeto Report, pero por ejemplo en Visual Basic de la plataforma Visual Studio, podemos dibujar en un objeto Form, en un control Picture Box, en un control Image, etc. Estos objetos tienen desarrollados mtodos grficos equivalentes en todos ellos, funcionando de una forma polimrfica. Por ejemplo, para dibujar una lnea, tanto en un objeto Report de Access, como en un Picture o un Form de Visual Basic usaremos el mtodo Line. Para dibujar una elipse, un crculo, un sector circular elptico, usaremos el mtodo
Circle.
Hay adems una multitud de objetos ActiveX que tambin implementan esos mtodos, e incluso, utilizando Visual Studio, podramos desarrollar nuestros propios controles ActiveX que implementaran mtodos grficos.
21 - 32
Nota: No entra en el objetivo de este trabajo mostrar cmo desarrollar controles ActiveX con Visual Basic, con C++ o con cualquier otra plataforma de desarrollo de software.. Cuando diseemos una clase que implemente esta interfaz deber incluir todos los mtodos de esta; caso contrario dar un error de compilacin, como podemos ver en la siguiente imagen:
En este caso nos avisa que todava que faltan mtodos por desarrollar: Para desarrollar cada uno de los mtodos, en los cuadros combinados ubicados en la parte superior del editor de cdigo, seleccionamos el objeto IGraficElement y el mtodo a escribir:
Tambin podramos escribirlo de forma directa, poniendo el nombre de la interfaz seguida del carcter lnea inferior y el nombre del mtodo. Vamos a crear tres clases que implementarn la interfaz IGraficElement.
eduardo@olaz.net
Eduardo Olaz
Entrega 21
21 - 33
En cada una de ellas desarrollaremos los mtodos declarados en la interfaz. Lo que realmente va a cambiar entre las tres clases ser el mtodo Draw que las dibuja en el Lienzo (Canvas). Este sera el cdigo bsico de la clase clsRecta:
Option Explicit Implements IGraficElement Private Private Private Private Private Private lngColor As Long intLeft As Integer intTop As Integer intWhidth As Integer intHeight As Integer objCanvas As Object
' Desarrollamos ahora los mtodos de IGraficElement Private Property Let IGraficElement_Color(NewColor As Long) If lngColor <> NewColor Then lngColor = NewColor End If End Property Private Property Get IGraficElement_Color() As Long IGraficElement_Color = lngColor End Property Private Property Get IGraficElement_Left() As Integer IGraficElement_Left = intLeft End Property Private Property Let IGraficElement_Left( _ ByVal NewPosition As Integer) If intLeft <> NewPosition Then intLeft = NewPosition
Comencemos a programar con VBA - Access
21 - 34
End If End Property Private Property Get IGraficElement_Top() As Integer IGraficElement_Top = intTop End Property Private Property Let IGraficElement_Top( _ ByVal NewPosition As Integer) If intTop <> NewPosition Then intTop = NewPosition End If End Property Private Property Get IGraficElement_Whidth() As Integer IGraficElement_Whidth = intWhidth End Property Private Property Let IGraficElement_Whidth( _ ByVal NewValue As Integer) If intWhidth <> NewValue Then intWhidth = NewValue End If End Property Private Property Get IGraficElement_Height() As Integer IGraficElement_Height = intHeight End Property Private Property Let IGraficElement_Height( _ ByVal NewValue As Integer) If intHeight <> NewValue Then intHeight = NewValue End If End Property Private Property Get IGraficElement_Canvas() As Object Set IGraficElement_Canvas = objCanvas End Property
eduardo@olaz.net
Eduardo Olaz
Entrega 21
21 - 35
Private Property Set IGraficElement_Canvas( _ NewCanvas As Object) Set objCanvas = NewCanvas End Property Private Dim Dim Dim Dim Sub IGraficElement_Draw() X0 As Integer Y0 As Integer X1 As Integer Y1 As Integer
' Para dibujar la recta usaremos el mtodo _ Line(Coords. Origen)-(Coords. Final) Color X0 = intLeft If intHeight < 0 Then Y0 = intTop Y1 = intTop - intHeight Else Y0 = intTop Y1 = Y0 + intHeight End If X1 = X0 + intWhidth End Sub
De forma semejante desarrollaramos las clases correspondientes a clsRectangulo y clsElipse. Lo que cambiara en ellas sera el mtodo Draw, aunque en la clase clsRectangulo el cambio sera muy pequeo, ya que bastara con aadirle el parmetro B al mtodo Line.
' Para dibujar el rectngulo usaremos el mtodo _ Line(Coords. Origen)-(Coords. Final), Color, B X0 = intLeft If intHeight < 0 Then Y0 = intTop Y1 = intTop - intHeight
Comencemos a programar con VBA - Access
21 - 36
Else Y0 = intTop Y1 = Y0 + intHeight End If X1 = X0 + intWhidth objCanvas.Line (X0, Y0)-(X1, Y1), lngColor, B End Sub
En el caso de la clase clsElipse los cambios seran ms sustanciales, ya que usar el mtodo Circle, al que se le pasan las coordenadas del centro, el radio, color y aspecto. (Ver el mtodo Circle en la ayuda de VBA) Las coordenadas del centro y el aspecto (relacin entre la altura y la anchura), las podemos extraer de las variables
Public Sub Draw() Dim X0 As Integer Dim Y0 As Integer Dim X1 As Integer Dim Y1 As Integer Dim CentroX As Integer Dim CentroY As Integer Dim sngAspecto As Single ' Para dibujar la Elipse usaremos el mtodo _ Circle(Coords. Centro), Color, Aspecto X0 = intLeft If intHeight < 0 Then Y0 = intTop Y1 = intTop - intHeight Else Y0 = intTop Y1 = intTop + intHeight End If X1 = intLeft + intWhidth CentroX = X0 + intWhidth / 2 CentroY = Y0 + intHeight / 2 sngAspecto = intHeight / intWhidth
eduardo@olaz.net
Eduardo Olaz
Entrega 21
21 - 37
En la cabecera del mdulo de clase del informe declararemos las clases que vamos a utilizar. Lgicamente esas clases, as como la interfaz IGraficElement debern estar en mdulos del mismo fichero Access.
21 - 38
Option Explicit Dim Dim Dim Dim Dim objGrafico As New IGraficElement Recta As New clsRecta Rectangulo As New clsRectangulo Elipse As New clsElipse ColorObjeto As New clsColor
Al declararlas con New, desde ese mismo instante ya hemos creado una instancia de cada una de las cuatro clases. En el evento Al abrir (Open) del informe, asignaremos como lienzo (Canvas) el propio informe (recordemos que Me hace referencia al propio objeto la clase actual que en este caso es el informe en cuyo mdulo de clase est escrito el cdigo que incluye Me):
Private Sub Report_Open(Cancel As Integer) ' Asignamos el Lienzo a cada clase Set Recta.Canvas = Me Set Rectangulo.Canvas = Me Set Elipse.Canvas = Me End Sub
Vamos a utilizar el evento Al imprimir (Print) de la seccin Detalle del informe para dibujar en l
Private Sub Detalle_Print( _ Cancel As Integer, _ PrintCount As Integer) Const Incremento As Integer = 30 Const Bucles As Integer = 40 Const Oscurecimiento As Single = 0.025 Const CambioLuz As Single = 0.25 Dim i As Integer Dim intPuente As Integer ' Primero usamos las clases, propiamente dichas _ para dibujar una lnea, un rectngulo y una elipse With Recta .Top = 0 .Left = 0 .Height = 2500 .Whidth = 5000 .Color = eoColorAzul .Draw End With
eduardo@olaz.net
Eduardo Olaz
Entrega 21
21 - 39
With Rectangulo .Top = 0 .Height = 2500 .Whidth = 5000 .Color = eoColorRojo .Draw End With With Elipse .Top = 0 .Height = 2500 .Whidth = 5000 .Color = eoColorNegro .Draw End With ' Vamos a trabajar ahora con la Interfaz Set objGrafico = Recta With objGrafico ColorObjeto.Color = eoColorVioleta .Left = 1000 .Whidth = 0 .Height = 2500 For i = 1 To Bucles / 4 .Color = ColorObjeto.Color .Left = .Left + Incremento .Draw ColorObjeto.AclaraColor Next i ColorObjeto.Color = eoColorVioleta .Left = .Left + 10 * Incremento For i = 1 To Bucles / 4 .Color = ColorObjeto.Color .Left = .Left - Incremento .Draw ColorObjeto.AclaraColor CambioLuz / 2 Next i End With
21 - 40
Set objGrafico = Rectangulo ColorObjeto.Color = eoColorAzul ColorObjeto.OscureceColor CambioLuz With objGrafico ColorObjeto.AclaraColor CambioLuz .Left = 6000 .Whidth = 2500 .Height = 2500 For i = 1 To Bucles .Color = ColorObjeto.Color .Top = .Top + Incremento .Left = .Left + Incremento .Whidth = .Whidth - 2 * Incremento .Height = .Height - 2 * Incremento .Draw ColorObjeto.AclaraColor 0.05 Next i End With Set objGrafico = Elipse With objGrafico ColorObjeto.Color = eoColorRojo ColorObjeto.AclaraColor 2 * CambioLuz .Left = 2000 .Whidth = 2500 .Height = 2500 For i = 1 To 2 * Bucles .Color = ColorObjeto.Color .Top = .Top + Incremento / 6 .Left = .Left + Incremento / 6 .Whidth = .Whidth - Incremento / 3 .Height = .Height - Incremento / 3 .Draw ColorObjeto.OscureceColor Oscurecimiento Next i End With With objGrafico ColorObjeto.Color = eoColorAzul ColorObjeto.AclaraColor 2 * CambioLuz For i = 1 To 2 * Bucles
eduardo@olaz.net
Eduardo Olaz
Entrega 21
21 - 41
.Color = ColorObjeto.Color .Top = .Top + Incremento / 6 .Left = .Left + Incremento / 6 .Whidth = .Whidth - Incremento / 3 .Height = .Height - Incremento / 3 .Draw ColorObjeto.OscureceColor Oscurecimiento Next i End With With objGrafico ColorObjeto.Color = eoColorAmarillo ColorObjeto.AclaraColor 2 * CambioLuz For i = 1 To 2 * Bucles .Color = ColorObjeto.Color .Top = .Top + Incremento / 6 .Left = .Left + Incremento / 6 .Whidth = .Whidth - Incremento / 3 .Height = .Height - Incremento / 3 .Draw ColorObjeto.OscureceColor Oscurecimiento Next i End With End Sub
Al ejecutar este cdigo, se dibujan estas figuras grficas:
Como ya se comenta en el propio cdigo, podemos apreciar que en la primera parte usamos directamente las clases y sus mtodos, para simplemente trazar una lnea, un rectngulo y una elipse mediante el mtodo Draw De cada una de ellas. En la segunda parte del evento utilizamos una instancia de la interfaz, IGraficElement que asociamos a la variable objGrafico. A este objeto le vamos asignando sucesivamente los sucesivos objetos Recta, Rectangulo y Elipse, que a su vez son instancias de las clases clsRecta, clsRectangulo y clsElipse, y cada una de ellas implementa la interfaz IGraficElement.
Comencemos a programar con VBA - Access
21 - 42
Demasiado cdigo para un resultado tan pobre? No lo voy a negar, pero lo que realmente me interesa es que el lector capte la esencia de la utilizacin de interfaces, que lo que permiten es usar objetos diferentes mediante los mismos mtodos y propiedades. En definitiva aprovechar las ventajas del Polimorfismo.
Herencia
En un punto anterior hemos dicho que las clases en VBA no poseen la capacidad de heredar unas de otras, es decir, la programacin con VBA no tiene posibilidad de implementar la herencia de clases. sta es una de las razones por las que se dice que Visual Basic no es un verdadero lenguaje orientado a objetos. Planteada esta premisa inicial, al igual que con el polimorfismo, existe la posibilidad de emular la herencia mediante la llamada Herencia mediante delegacin. El truco consiste en lo siguiente: Supongamos que queremos crear la clase clsCuadrado, que fcilmente podemos deducir que es un caso particular de la clase clsRectangulo que hemos creado con anterioridad. Si, como explicbamos en el punto anterior, VBA implementara la herencia como s es en el caso de VB Net, sera tan sencillo como declararla as
Inherits clsRectangulo
La clase clsCuadrado tendra todos los mtodos y propiedades del la clase clsRectngulo. Podramos a su vez redefinir el mtodo Draw e incluso eliminar las propiedades Whidth y Height y sustituirlas por una nica propiedad Lado a la que llamaramos Side. Por desgracia con VBA la cosa no es tan fcil, pero como he dicho podemos emular este comportamiento con algo ms de cdigo. Para ello crearemos la clase clsCuadrado, y dentro de ella instanciaremos una clase del tipo clsRectangulo que la declararemos como Private para que no se tenga acceso a ella desde fuera de la clase Cuadrado.. Ese objeto de tipo Rectngulo creado dentro de la clase clsCuadrado ser el que haga todo el trabajo. El cdigo de la clase clsCuadrado podra ser el siguiente: Empezaremos declarando un objeto del tipo clsRectangulo; objeto que instanciaremos en el evento Initialize de la clase
eduardo@olaz.net
Eduardo Olaz
Entrega 21
21 - 43
Private Rectangulo As clsRectangulo Private Sub Class_Initialize() Set Rectangulo = New clsRectangulo End Sub
A continuacin vamos a escribir la destruccin del objeto Rectangulo en el evento Terminate de la clase, y algo que no lo hemos hecho en el cdigo de las clases anteriores, destruir la propiedad Canvas de la clase antes de que sta desaparezca.
Private Sub Class_Terminate() Set Rectangulo.Canvas = Nothing Set Rectangulo = Nothing End Sub
El resto de las propiedades y el mtodo Draw. Se implementaran as:
Public Property Get Color() As PaletaColores Color = Rectangulo.Color End Property Public Property Let Color(NewColor As PaletaColores) Rectangulo.Color = NewColor End Property Public Property Get Left() As Integer Left = Rectangulo.Left End Property Public Property Let Left(ByVal X As Integer) Rectangulo.Left = X End Property Public Property Get Top() As Integer Top = Rectangulo.Top End Property Public Property Let Top(ByVal Y As Integer) Rectangulo.Top = Y End Property Property Get Side() As Integer Side = Rectangulo.Whidth End Property
Comencemos a programar con VBA - Access
21 - 44
Property Let Side(ByVal NewValue As Integer) Rectangulo.Whidth = NewValue Rectangulo.Height = NewValue End Property Property Get Canvas() As Object Set Canvas = Rectangulo.Canvas End Property Property Set Canvas(NewCanvas As Object) Set Rectangulo.Canvas = NewCanvas End Property Sub Draw() Rectangulo.Draw End Sub
Frente a las anteriores clases, sta tiene la peculiaridad de que no implementa las propiedades Whidth y Height, y en cambio implementa una nueva propiedad Side no incluida en las otras clases. Adems tampoco implementa la interfaz IGraficElement aunque podramos llegar a implementrsela con algo ms de cdigo, si ste fuera nuestro propsito. Vamos a ver cmo podemos usar la clase Al igual que en el caso anterior creamos un nuevo informe en el que aumentaremos la altura de la seccin detalle y aprovechamos los eventos del mismo.
Private Sub Detalle_Print( _ Cancel As Integer, _ PrintCount As Integer) Const Escala As Integer = 30 Dim intLado As Integer Dim i As Integer objColor.Color = eoColorRojo For i = 1 To 30 intLado = Escala * i With Cuadrado .Color = objColor.Color .Side = intLado .Left = 2500 - intLado / 2 .Top = .Left
eduardo@olaz.net
Eduardo Olaz
Entrega 21
21 - 45
.Draw objColor.AclaraColor 0.05 End With Next i objColor.Color = eoColorAmarillo For i = 31 To 60 intLado = Escala * i With Cuadrado .Color = objColor.Color .Side = intLado .Left = 2500 - intLado / 2 .Top = .Left .Draw objColor.AclaraColor 0.05 End With Next i For i = 61 To 70 intLado = Escala * i Cuadrado.Color = eoColorVioleta With Cuadrado .Side = intLado .Left = 2500 - intLado / 2 .Top = .Left .Draw End With Next i For i = 71 To 80 intLado = Escala * i Cuadrado.Color = eoColorNaranja With Cuadrado .Side = intLado .Left = 2500 - intLado / 2 .Top = .Left .Draw End With Next i End Sub
Tras ejecutarse este cdigo, el resultado final sera algo as como lo siguiente:
21 - 46
Debo reconocer que siento cierta debilidad por el diseo grfico. Las clases anteriores u otras similares se pueden usar para muchas cosas. Este es otro ejemplo realizado con la clase clsRecta.
Para los que sientan curiosidad les dir que es una figura fractal que representa al Copo de Koch. Es un fractal de superficie limitada pero con un permetro de longitud infinita. Si he utilizado los elementos grficos es simplemente porque me resulta divertido, sobre todo considerando las pocas posibilidades que vienen implementadas en Access. Ms adelante utilizaremos lo aprendido en estos captulos para realizar clases que por ejemplo nos sirvan para gestionar datos de clientes o proveedores.
eduardo@olaz.net
Eduardo Olaz
Entrega 21
21 - 47
Constructor de la clase
De entrada debo decir que en las clases de VBA no podemos definir constructores, al menos de la forma como se entienden en la Programacin Orientada a Objetos. No obstante en el captulo anterior comentamos la posibilidad de emularlos. Primero vamos a definir qu podemos entender por un constructor de una clase: Un constructor ortodoxo debera poder introducir los parmetros que definan completamente la clase en el momento de su creacin. Por ejemplo con un constructor De verdad podramos definir
Public Sub Constructor( _ Optional ByVal X As Integer, _ Optional ByVal Y As Integer, _ Optional ByVal NewColor As PaletaColores, _ Optional ByVal NewSide As Integer, _ Optional NewCanvas As Object) Left = X Top = Y Color = NewColor Side = NewSide If TypeName(NewCanvas) <> "Nothing" Then Set Canvas = NewCanvas End If End Sub
Por cierto, la funcin TypeName, devuelve una cadena con informacin sobre el tipo de variable que se ha pasado como parmetro.
21 - 48
En este caso, si no pasamos ningn valor al parmetro NewCanvas, su contenido ser Nothing, por lo que la funcin TypeName devolvera la cadena "Nothing". Si no es as, significa que se le ha pasado algn objeto, por lo que se lo asignamos a la propiedad Canvas. Siguiendo con el planteamiento anterior podramos hacer
Private Sub Report_Open(Cancel As Integer) Set Cuadrado = New clsCuadrado Cuadrado.Constructor 1200, 1400, vbRed, 1000, Me End Sub
De esta forma hemos inicializado de un golpe y totalmente los valores del objeto Cuadrado. Lstima que no se pueda hacer simultneamente con la creacin del objeto. O s se puede hacer?. Podramos emularlo auxilindonos de un procedimiento externo que admitiera los parmetros del procedimiento Constructor y que al llamarlo creara una instancia de la clase y ejecutara su mtodo Constructor. Veamos cmo se hace: En un mdulo normal creamos una funcin a la que llamaremos clsCuadrado_New , que incluir los mismos parmetros que el mtodo Constructor de la clase. ste sera su cdigo:
Public Function clsCuadrado_New( _ Optional ByVal X As Integer, _ Optional ByVal Y As Integer, _ Optional ByVal NewColor As PaletaColores, _ Optional ByVal NewSide As Integer, _ Optional NewCanvas As Object _ ) As clsCuadrado Dim Cuadrado As New clsCuadrado If TypeName(NewCanvas) <> "Nothing" Then Cuadrado.Constructor X, Y, _ NewColor, NewSide, _ NewCanvas Else Cuadrado.Constructor X, Y, _ NewColor, NewSide
eduardo@olaz.net
Eduardo Olaz
Entrega 21
21 - 49
Option Explicit Dim Cuadrado As clsCuadrado Private Sub Report_Open(Cancel As Integer) Set Cuadrado = clsCuadrado_New(1200, 1400, _ vbRed, 1000, Me) End Sub Private Sub Detalle_Print( _ Cancel As Integer, _ PrintCount As Integer) Cuadrado.Draw End Sub
Con esto simulamos un constructor, ya que funciona como si a la vez que creramos un objeto le asignramos sus atributos.
Factora de Clases
No es una mala costumbre definir un mtodo Constructor cada vez que diseamos una clase, e incluir las respectivas funciones constructoras, que en nuestro caso seran
FactoraDeClases.
21 - 50
El valor de la propiedad Instancing determina si una clase es privada o si est disponible para su uso por parte de otras aplicaciones. En el caso concreto de VBA para Access la propiedad Instancing slo puede tomar los dos valores comentados con anterioridad.
Private hace que no se permita el acceso de otras aplicaciones a la informacin de la biblioteca de tipos de la clase y que no se pueden crear, desde fuera, instancias de la misma. PublicNotCreatable permite que las dems aplicaciones puedan usar los objetos de
la clase slo despus de que hayan sido creadas. En el software de desarrollo Visual Basic 6.0, no en el VBA de Access, se permiten otro tipo de valores para esta propiedad 3 Multiuse 4 GlobalMultiuse 6 SingleUse 7 Global SingleUse Pero no siempre es as para todo tipo de proyectos. Por ejemplo, si se est desarrollando un componente ActiveX, al igual que en VBA de Access, slo se permiten los valores Private y PublicNotCreatable, lo que resulta lgico.
eduardo@olaz.net
Eduardo Olaz