Escolar Documentos
Profissional Documentos
Cultura Documentos
1
INTRODUCCION Antecedentes históricos.
El lenguaje de programación BASIC (Beginner's All purpose Symbolic Instruction Code ) nació
en el año 1964 como una herramienta destinado a principiantes, buscando una forma sencilla
de realizar programas, empleando un lenguaje casi igual al usado en la vida ordinaria ( en
inglés), y con instrucciones muy sencillas y escasas. Teniendo en cuenta el año de su
nacimiento, este lenguaje cubría casi todas las necesidades para la ejecución de programas.
Téngase en cuenta que las máquinas existentes en aquella época estaban estrenando los
transistores como elementos de conmutación, los ciclos de trabajo llegaban a la impensable
cifra de 10.000 por segundo y la memoria no pasaba de unos pocos k´s en toroides de ferrita.
Los autores fueron los científicos John G. Kemeny (Budapest, 1926 – USA 1992) y Thomas E.
Kurtz (Illinois 1928) Su trabajo original se llamó True BASIC.
La evolución del BASIC por los años 70 fue escasa, dado el auge que tomaron en aquella
época lenguajes de alto nivel como el FORTRAN y el COBOL. En 1978 se definió una norma
para unificar los Basics existentes creándose la normativa BASIC STANDARD
Con la popularización del PC, salieron varias versiones del BASIC que funcionaban en este tipo
de ordenadores (Versiones BASICA, GW-BASIC), pero todas estas versiones del BASIC no
hicieron otra cosa que terminar de rematar este lenguaje. Los programadores profesionales no
llegaron a utilizarlo, habida cuenta de las desventajas de este lenguaje respecto a otras
herramientas (PASCAL, C, CLIPPER). El BASIC con estas versiones para PC llegó incluso a
perder crédito entre los profesionales de la informática.
Tal fue ese abandono por parte de los usuarios, que la aparición del Quick-BASIC de Microsoft,
una versión ya potente del BASIC, que corregía casi todos los defectos de las versiones pasó
prácticamente inadvertida, a no ser porque las últimas versiones del sistema operativo MS-
DOS incluían una versión de Quick-BASIC algo recortada (Q-Basic) como un producto mas
dentro de la amplia gama de ficheros ejecutables que acompañan al sistema operativo, y
aprovecha de él el editor de textos (Cada vez que se llama al EDIT estamos corriendo el editor
del Q-Basic).
Esta versión del popular BASIC ya es un lenguaje estructurado, lo que permite crear
programas modularmente, mediante subrutinas y módulos, capaz de crear programas ya
competitivos con otros lenguajes de alto nivel. Sin embargo llegaba tarde, pues los entornos
MS-DOS estaban ya superados por el entorno gráfico Windows.
Sin embargo algo había en el BASIC que tentaba a superarse: su gran sencillez de manejo. Si
a esto se le añade el entorno gráfico Windows, el aprovechamiento al máximo de las
LSB Visual Basic - Guía del Estudiante Capítulo 1 Página 1
posibilidades de Windows en cuanto a intercambio de información, de sus librerías, de sus
drivers y controladores, manejo de bases de datos, etc. el producto resultante puede ser algo
que satisfaga todas las necesidades de programación en el entorno Windows. La suma de
todas estas cosas es VISUAL - BASIC. Esta herramienta conserva del BASIC de los años 80
únicamente su nombre y su sencillez, y tras su lanzamiento al mercado, la aceptación a nivel
profesional hizo borrar por fin el "mal nombre" asociado a la palabra BASIC.
Actualmente (2001) se está comercializando la versión 6.0 de este producto. Desde su salida al
mercado, cada versión supera y mejora la anterior. Dados los buenos resultados a nivel
profesional de este producto, y el apoyo prestado por el fabricante para la formación de
programadores, Visual-Basic se ha convertido en la primera herramienta de desarrollo de
aplicaciones en entorno Windows.
Es obligado decir sin embargo, que sigue siendo BASIC. No se pueden comparar sus
prestaciones con otros lenguajes cuando deseamos llegar al fondo de la máquina y controlar
uno a uno sus registros. No es ese el fin perseguido con VB y si es necesario llegar a esas
precisiones será necesario utilizar otro lenguaje que permita bajar el nivel de programación.
(Visual-C). o realizar librerías (DLLs) que lo hagan. En la mayor parte de las aplicaciones, las
herramientas aportadas por VB son mas que suficiente para lograr un programa fácil de
realizar y de altas prestaciones.
Es por tanto un termino medio entre la programación tradicional, formada por una sucesión
lineal de código estructurado, y la programación orientada a objetos. Combina ambas
tendencias. Ya que no podemos decir que VB pertenezca por completo a uno de esos dos
tipos de programación, debemos inventar una palabra que la defina : PROGRAMACION
VISUAL.
Análisis . Es el studio de las necesidades que han dado origen a la creación de ese
programa. Es lo que se se llama Análisis de la aplicación. Es la primera fase que debe
tener siempre un programa y es tambien la más olvidada entre los programadores
noveles. Una aplicación no se inicia con el teclado, sino sobre un papel.
- Generación del código asociado a los eventos que ocurran a estos controles. A
la respuesta a estos eventos (click, doble click, una tecla pulsada, etc.) le llamamos
Procedimiento, y deberá generarse de acuerdo a las necesidades del programa.
- Generación del código del programa. Un programa puede hacerse solamente con
la programación de los distintos procedimientos que acompañan a cada objeto. Sin
Nombre
Apellido1
Apellido2
Dirección
Teléfono
Salario
La variable Nombre tomará valores distintos según vayamos introduciendo los datos de los
distintos alumnos. Es posible, que a lo largo de la ejecución del programa, esta variable
Nombre valga:
José
Pedro
María
Luis
Espero que su intuición o conocimiento anterior le lleve a conocer el concepto de variable. Mas
adelante lo verá mas claro.
Decíamos que Basic no exige la definición previa de las variables. Otras herramientas exigen
que se haga así. Por lo tanto es normal encontrar, en otros sistemas de programación, que un
programa comienza de la siguiente forma:
Mediante estas declaraciones, el programa sabe de que tipo de dato se trata y por tanto cómo
debe trabajar con él. En otros sistemas de programación distintos de Basic, es necesario
realizar esta declaración antes de introducir una variable.
Basic permite que no se declaren. Cuando a lo largo del programa le introducimos una variable
nueva, asume que es una variable y que el tipo es el adecuado para el valor que le estamos
introduciendo en ese momento.
Salario=50000000
Nombre ="Pedro"
Teléfono = "1234567"
entiende que Salario, Nombre y Teléfono son variables, que Salario es un número (No hemos
metido su valor entre comillas), y que Nombre y Teléfono son sucesiones de caracteres
alfanuméricos (su valor está entre comillas)
Print Salario
Print Nombre
Print Telwfono
Habrá observado en tercer lugar la palabra Telwfono, que por error ha introducido el
programador. Basic interpreta que Telwfono es una variable e irá a leer en memoria el valor
que tiene. No tendrá ningún valor. Por lo tanto no escribirá nada y encima no dará ningún aviso
de que se ha cometido un error. Nada es gratis en esta vida, y la facilidad para introducir
variables se paga con la posibilidad de un error.
Basic ha pensado en ese problema, y se puede solucionar con esta que será la primera
instrucción BASIC que vamos a estudiar:
OPTION EXPLICIT
Las variables pueden ser de los siguientes tipos: (El número indicado en segundo lugar indica
el número de Bytes que ocupa en memoria.)
(*) Una variable tipo String ocupa el mismo número de bytes que caracteres tenga la cadena.
(**) Una variable tipo Variant ocupa 16 bytes si se trata de un número y 22 bytes + longitud de
la cadena si se trata de un dato tipo cadena de caracteres.
Existen también variables definidas por el usuario (Ya verá la sentencia Type). En este tipo de
variables se pueden introducir muchos datos de distinto tipo. Los bytes necesarios para
almacenar esa variable dependerá de los datos que se hayan definido.
Dentro de las variables Objet (variables que se refieren a objetos) existe una gran variedad
que a estas alturas del curso no debemos complicarnos con ellas. Pero para adelantar algo,
veremos que una variable puede ser del tipo Form - Formulario - , tipo Recordset, etc. etc.
Cada tipo de variable ocupa unos determinados bytes. Si no se define una variable, VB toma
como tipo por defecto para la variable el tipo Variant, tal como citábamos anteriormente. Este
tipo ocupa mas bytes que, por ejemplo, un integer. Si el tipo de dato que vamos a introducir en
una variable es un integer, y no la hemos declarado como tal, VB asumirá para esa variable
que es del tipo Variant, lo que le llevará a gastar mas bytes de memoria (16) que los que
necesitaría (2) si la hubiésemos declarado previamente. Si esa variable va a estar en el rango
de 0 a 255, y no declaramos previamente que la variable va a ser del tipo Byte, o la declaramos
como integer, p. e., estamos desperdiciando memoria RAM y posiblemente, retardando la
ejecución del programa. Lo mismo podemos decir del resto de las variables, y lo importante
que es declararlas y declararlas bien.
NOTA. Observe en la lista anterior que un dato Booleano ocupa 2 Bytes, mientras que un dato
tipo Byte ocupa un byte. En muchas ocasiones declaramos variables tipo Boolean con la
intención de que ocupen menos espacio. Paradoja del VB. Si la declaramos como Byte
ocupamos menos espacio en memoria. Declarar una variable como Boolean tiene también sus
ventajas (escribirá menos código por lo general cuando necesite leer o escribir una variable
tipo Boolean), pero tenga presente esta observación respecto al tamaño de los datos Boolean.
Puede declarar el tipo de la variable mediante un carácter después del nombre de la variable.
Esta técnica, obligatoria en Quick-Basic, está en desuso en VB. No es recomendable definir el
tipo de esta forma, pues existe un serio peligro de error. De cualquier forma, eso es potestativo
del programador y de sus costumbres.
En Visual Basic, cuando declaramos una variable como String (Cadena de caracteres), no es
necesario declarar su longitud. VB aceptará cualquier número de caracteres. Si desea evitar
que se puedan introducir más de un determinado número de caracteres, debe declarar su
número. Por ejemplo :
En este segundo caso, si se introduce como Var2 una cadena de caracteres con mas de 15
caracteres, Var2 tomará solamente los 15 primeros. Visual basic no presenta ningún aviso de
que esa variable ha omitido los caracteres que han sobrepasado la cifra de 15. Si desea que el
usuario conozca esa circunstancia, debe introducir el código oportuno para producir el aviso.
La declaración de variables tipo String con número de caracteres predefinido presenta también
inconvenientes a la hora de tratar esa cadena de caracteres con sentencias tales como Mid,
Left y Right, o a la hora de añadir una cadena a otra. La experiencia nos indica que NO
merece la pena declarar variables tipo String con el número de caracteres prefijado,
excepto que sea necesario justamente para definir la longitud de la cadena.
En una gran parte de los casos una variable que se compone de números debe declararse
como una variable de cadena de caracteres (String), y no como numérica. Cuando pensamos
por ejemplo en un número de DNI, compuesto por 7 u 8 cifras, un código postal, el número de
una calle, el piso de un edificio, parecen invitar a que la variable que los contenga sea una
variable de tipo numérico (Byte, Integer, Long, ...). Estas variables, aunque su contenido sean
números, siempre se deben declarar como String, ya que se tratan en realidad de cadenas de
caracteres, aunque estos no sean letras sino números. Para aclarar mas estas ideas, piense
en el número del DNI con la letra del NIF incluido, o que el lugar del DNI se deba rellenar con el
número del permiso de residencia, (lleva letras). Piense en el código postal de una provincia
que comienza por 0 ( 08XXX = Barcelona ) . Si la variable que va a contener ese código postal
se declara como numérica, el cero de la izquierda lo desprecia, por lo que ese código postal
quedaría reducido al 8XXX, número de 4 cifras que el cartero nunca reconocería como un
código postal, que necesariamente ha de tener 5 cifras. Para terminar, piense la forma de
introducir en una variable numérica el número 32Bis de una calle o el piso S2 de un edificio. O
piense en una variable que va a contener un código de un producto. Ese código siempre va a
ser un número, por ejemplo de 9 cifras. ¿Ha pensado como trataría el ordenador una variable
numérica que contiene el código 000100123 ?. El resultado sería que convertiría ese código en
la cifra 100123, y Vd. deberá buscarse la vida para añadir los ceros iniciales a la hora de
sacarlo a una impresora, por ejemplo.
En todos los casos anteriores, el número no representa una cantidad numérica, sino un
nombre. Lo que ocurre es que ese nombre contiene solamente números.
¿Qué variables debemos declarar entonces como numéricas ? La respuesta es bien sencilla :
Aquellas que van a contener datos con lo que vamos a realizar operaciones
matemáticas.
Las variables booleanas (True/False) pueden en muchos casos sustituirse por una variable del
tipo Byte. Si ese datos True / False se va a introducir en una base de datos o en fichero en el
disco, puede ser mas prudente poner 0 en vez de False y 1 en vez de True.
Una variable byte ocupa muy poco, simplemente 1 byte como su nombre indica. Pero no puede
contener números mayores de 255 ni números negativos.
Cada vez que declare una variable numérica piense en los valores que puede tener, sobre todo
cuando esa variable va a ser el resultado de una operación matemática. Recuerde el escaso
Otro tipo de variable es Date. Este tipo de variable representa una fecha o una hora. Pero
debe ser objeto de un estudio un poco mas extenso para tener claro que es una fecha para
Visual Basic, como las trata y como se pueden comparar. Lo verá mas adelante al estudiar las
funciones Format y DateDiff.
Denominamos ámbito de una variable a las partes del programa donde esa variable está
declarada. Para entenderlo mejor, veamos someramente la forma de un programa
desarrollado en VB.
Un programa VB tiene uno o varios formularios. Cada formulario tiene varios controles. Tanto el
formulario como cada uno de sus controles tienen una parte del programa, justamente la parte
relacionada con cada uno de los eventos que pueden suceder bien al formulario o a los
controles. A estas partes las habíamos llamado Procedimientos. Podemos tener
procedimientos que no estén relacionados con ningún evento ocurrido al formulario o a sus
controles. (Los Procedimientos que iremos insertando a lo largo de la aplicación)
Aparte de formularios y controles, un programa puede tener Módulos, y en cada uno de los
módulos podemos insertar cuantos Procedimientos y Funciones queramos. La estructura de
un programa VB puede ser de la siguiente forma:
Si se declara una variable dentro de un procedimiento o Función, esa variable "NO SALE" del
Procedimiento o Función donde se declaró. El procedimiento puede estar en un Formulario
(Cualquier procedimiento de un control o un procedimiento creado por nosotros) o en un
Módulo (En este caso, el procedimiento solo puede ser creado por nosotros)
En un Formulario, una variable puede declararse de dos formas : Privada o Pública. Para
declarar una variable a nivel de formulario debe hacerse en la sección de declaraciones, que
está la ventana de código Objeto = General, Proc. = Declaraciones. Si se declara Privada,
esa variable se puede ver en todo el formulario, (es decir, en todos los procedimientos de todos
los controles del formulario y en los Procedimientos que pudiésemos insertar en ese
formulario), pero no sale de dicho formulario. Si se declara como Pública, esa variable puede
verse por todo el formulario, de la misma forma que lo haría declarada como Privada, y
además puede ser usada desde otro Formulario o Módulo, citándola con el nombre del
Formulario, seguido del nombre de la variable (Formulario.Variable)
En un Módulo una variable puede declararse como Privada, con lo que no saldrá de ese
En VB es posible declarar varias veces las variables, es decir, pueden declararse a nivel de
formulario, en su apartado de declaraciones, y esa variable conservará su valor en todas las
partes de ese formulario. Sin embargo, si se declara una variable con el mismo nombre a un
nivel inferior, por ejemplo al principio de un procedimiento, esa variable será una variable
distinta a la anterior, aunque tenga el mismo nombre, al declararla en un Procedimiento,
solamente será válida en ese Procedimiento. Una vez que hayamos salido de ese
procedimiento, la variable con ese nombre volverá a ser la declarada en el Formulario. En
realidad, lo que tenemos son dos variables distintas, pero con el mismo nombre, una declarada
para todo el formulario excepto para el procedimiento donde se volvió a declarar, y otra para
ese procedimiento concreto.
NOTA.- No es recomendable declarar una variable a dos niveles. Es mucho mejor utilizar otro
nombre para esa variable dentro del procedimiento donde se le declararía por segunda vez. A
esta nota cabe exceptuar cuando declaramos variables para una operación tipo contador
For I = 1 To N
Esa variable I es práctico declararla con el mismo nombre en cada Procedimiento donde se
use.
NombreFormulario.Nombrevariable
Sentencia STATIC
Como se dijo anteriormente, una variable declarada en un procedimiento pierde su valor al salir
de él. Lo peor es que una vez que el programa vuelva a entrar en ese procedimiento, la
variable estará puesta a cero. Afortunadamente, esto último tiene solución. Si declarásemos
una variable en un procedimiento o función, como estática, esa variable, aunque no la
podremos utilizar fuera de ese procedimiento o función, cuando volvamos a él conservará el
valor que tenía cuando lo abandonamos. Esta declaración como estática se realiza mediante la
instrucción Static
El nombre de una variable puede ser tan largo como queramos. hasta un máximo de 40
caracteres. En la versión VB para España se pueden usar incluso la Ñ y vocales acentuadas.
Es indiferente usar mayúscula ó minúsculas. No se sorprenda, si por ejemplo, la ha declarado
con mayúsculas y luego la cita con minúsculas al escribir el código, que automáticamente se
cambie a mayúsculas. El nombre de una variable siempre debe comenzar por una letra.
No hay problema por utilizar variables largas. Al compilar el programa no se lleva el nombre, es
decir, no le va a ocupar mas espacio. Utilice siempre nombres que le definan la variable con
algún sentido. Es muy útil a la hora de acordarse como se llaman, y sobre todo, a la hora de
rehacer un programa que realizó hace seis meses.
Pese a que Visual Basic no obliga a declarar variables, es muy útil hacerlo. De esta forma se
tiene control sobre el programa. La experiencia se lo irá demostrando.
En un Módulo
Creo que esta costumbre viene del lenguaje C. Pero no vale en VB. Se trata de declarar varias
variables juntas en una misma línea :
Esta declaración está MAL hecha. Visual Basic interpretará que Variable1, Variable2 y
Variable3 son del tipo Variant, y solamente Variable4 la supone como tipo String
La forma correcta de hacerlo, si queremos declarar esas variables un una sola línea, es la
siguiente :
Podemos conocer el tipo con el que se ha declarado una variable. Esto se hace mediante la
Función TypeName, que devuelve una cadena con el tipo de una variable.
MiTipo = TypeName(NombreVariable)
NombreVariable puede ser cualquier variable con excepción de las de tipos definidos por el
usuario.
La cadena de caracteres devuelta por TypeName puede ser una de las siguientes:
Byte Un byte
Entero Un entero.
Si NombreVariable es una matriz, la cadena devuelta puede ser cualquiera de las cadenas
posibles con un paréntesis vacío adherido. Por ejemplo, si NombreVariable es una matriz de
enteros, TypeName devolverá "Integer()".
Option Explicit
Dim MiString As String ‘Hemos declarado que MiString es una variable tipo String
Dim MiEntero As Integer
Dim MiObjeto As Control ‘Hemos declarado que MiObjeto es una variable tipo Objeto
Haga doble click sobre el botón de comando. Le aparecerá la ventana anterior, pero referida
ahora al Command1. Teclee el siguiente código :
Label1 = TypeName(MiString)
label2 = TypeName(MiEntero)
Label3 = TypeName(MiObjeto)
End Sub
Esta función tiene mucha utilidad para determinar errores durante el diseño. Cuando aparece el
error “No coinciden los tipos” puede interrogarse al programa para ver qué tipo de variable es el
que hemos declarado. Esta idea es especialmente útil cuando el trabajo a realizar sea retomar
el trabajo comenzado por otro programador.
Sentencias condicionales.
Si no se cumple
Fin de la sentencia.
Así de fácil es programar en Basic. Lo que ocurre es que esta herramienta habla inglés, y lo
descrito anteriormente toma la forma:
If condición Then
Instrucciones
Else
Otras instrucciones
End If
If condición 1 Then
Instrucciones
ElseIf Condición 2
Otras instrucciones
ElseIf Condición 3
Otro juego de instrucciones
Else
Instrucciones que debe realizar caso de no cumplir las condiciones 1, 2 y 3.
End If
Su nombre casi nos define lo que es: Selecciona, dependiendo del caso, un determinado juego
de instrucciones:
Select Case variable ' variable es una variable que puede tomar los valores (p.e.) de
Case 1
Instrucciones a ejecutar en caso de que variable = 1
Case 2
Instrucciones a ejecutar en caso de que variable = 2
Case 3
Instrucciones a ejecutar en caso de que variable = 3
Case 4
Instrucciones a ejecutar en caso de que variable = 4
Case Else
Instrucciones a ejecutar en caso de que variable sea distinta a los valores
anteriores
End Select
Este procedimiento resulta mucho mas sencillo y rápido que las sentencias If Then Else
vistas anteriormente, cuando el margen de elección es mayor que 2.
Puede agrupar varios valores en una misma línea. Por ejemplo, si tiene 100 valores posibles, y
el tratamiento es igual para varios de ellos, puede agruparlos así:
Switch toma una serie de parámetros, todos por parejas. El primer término de cada pareja es
la expresión a evaluar. El segundo es el valor que tiene que devolver. En realidad Switch es
una función (las funciones las veremos muy pronto)
Esta instrucción obtiene un valor para A que dependerá del valor que tome B entre los valores
posibles (1, 2 ó 3)
La sentencia Choose es casi igual, cambia solamente la forma. La misma intrucción anterior
puede realizarse con Choose de la siguiente forma:
A = Choose ( B, 5, 7, 11 )
Las sentencias Switch y Choose no se usan mucho en programación. Parece que los
programadores o no las conocen o se lían (y es comprensible) al utilizarlas. Le garantizo que
puede llegar a ser un gran programador sin necesidad de utilizarlas. Use preferentemente If
End If y Select Case.
Observaciones
IIf siempre evalúa SiEsCierto y SiEsFalso, aunque sólo vaya a devolver una de ellas. Por esta
razón, deberá vigilar que no se produzcan efectos no deseados. Por ejemplo, si al evaluar
SiEsFalso se produce un error de división entre cero, se generará un error aunque Expresión
dé como resultado True.
Al ejecutar el programa, debe introducir un valor numérico en Text1. Si ese valor numérico es
menor que 100, MiVariable tomará el valor “Es menor” (No se cumple la condición expresada
en el primer parámetro Text1.Text > 1000). Si introduce una cantidad mayor, se cumple esa
condición, por lo que MiVariable tomará el valor “Es mayor”. El valor que toma la variable
puede verlo en Label1.
Fíjese que hemos declarado MiVariable como String, ya que va a contener un dato tipo cadena
de caracteres.
Con estas sentencias condicionales es posible realizar bifurcaciones del programa, cambiar las
propiedades de un objeto, obtener resultados de operaciones, ....
Sentencias de bucle.
Es muy común utilizar bucles a lo largo de un programa. Un bucle es una sucesión repetitiva de
instrucciones, que se estarán realizando mientras se cumpla una condición o mientras no se
cumpla otra condición. Es tan sencillo como esto:
Mientras condición
Instrucciones
Existen dos formas de bucle: Una, que realiza un número determinado de recorridos por el
bucle. Es el denominado bucle por contador. Otra, realiza el bucle hasta que se cumpla (o deje
de cumplirse) una condición. Es el llamado bucle por condición.
Realiza el bucle tantas veces como le indiquemos. Por ejemplo, en este bucle nos va a
presentar las 26 letras mayúsculas del alfabeto inglés
For N=65 To 90
Label1.caption = Chr ( N )
Next N
Este "programa" nos presentará en una caja (Label) los caracteres cuyo número ASCII vaya
desde el 65 (A) al 90 (Z) Comenzará presentando el correspondiente al número 65, e irá
presentando sucesivamente el 66, el 67, etc., hasta llegar al 90, donde se parará.
X=0
Do While X < 1000
X=X+1
Loop
El programa toma una variable ( X ) que previamente tuvimos la curiosidad de ponerla a cero,
e incrementa su valor una unidad. Analiza si el valor de X es menor que 1000, y si es cierto,
vuelve a realizar el bucle. Así hasta que X ya no sea menor que 1000. Al dejar de cumplirse
que X sea menor que 1000, sale del bucle. Acabamos de realizar un temporizador, y también
de exponer las sentencias condicionales y los bucles, inicio de cualquier curso de Basic. Como
final de lección, se propone un problema. Con el primer bucle, donde visualizábamos los
caracteres A a la Z, posiblemente no nos diese tiempo de ver cada una de las letras que iban
apareciendo en la pantalla, en la etiqueta Label1, dado que cambiaría con mucha velocidad, y
solamente veríamos la Z, que es donde se detuvo el programa. Podemos poner un
temporizador cada vez que presente una letra, y así nos dará tiempo a verla. Con los dos
bucles vistos anteriormente ya podemos hacerlo.
X=0
Do Until X > 1000
X=X+1
Loop
Observe que la diferencia entre una y otra es la condición, While para indicar Mientras se
cumpla que ... y Until para indicar Mientras no se cumpla que ....
For N=65 To 90
Label1.caption = Chr ( N )
Label1.RefreshN ' Refresca la etiqueta
X=0
Do While X < 1000
X=X+1
Loop
Next N
Este es nuestro primer programa en BASIC. En VISUAL BASIC es idéntico, pero nos falta la
Hemos puesto nuestra primera piedra en Visual Basic. Este ejemplo es muy sencillo. Visual
Basic es tan sencillo como este ejemplo.
(*) Las temporizaciones no se hacen realmente así. El ejemplo anterior no deja de ser
una forma sencilla de detener el programa, para que en los comienzos de su carrera como
programador pueda ver el contenido de una etiqueta. Si lo hiciera de esta forma, su ordenador
estaría detenido realizando una función bastante triste, contar hasta 1000 para luego continuar
el programa. Y lo que es peor, en un ordenador a 300 MHz. Tardaría menos tiempo que en
uno a 66. Esta misma función puede hacerla con Sleep que la verá mas adelante, que es la
forma limpia y elegante de hacer una temporización
Para salir de un bucle, podemos esperar a que se complete, bien porque se ha cumplido la
condición (Bucles por condición) o a que se haya llegado al final de la cuenta (Bucles por
contador). Pero puede ocurrir que interese salir del bucle sin haber terminado la ejecución total
del mismo, por ejemplo, porque ya hemos encontrado lo que estabamos buscando, y sería una
pérdida de tiempo seguir ejecutando el bucle cuando ya hemos obtenido lo que buscamos.
Para ello, basta con ejecutar la sentencia Exit Do (Cuando hemos comenzado el bucle con Do
Until o Do While, o con Exit For cuando lo hemos comenzado con For ... To
Ejemplos
En el siguiente ejemplo pretendemos parar el programa cierto tiempo, a la espera que otro
equipo se conecte a través del puerto serie. (Cuando se conecte, el control MSComm1
detectará la señal DSR que le envía el otro equipo y deberemos salir inmediatamente del bucle
para, por ejemplo, atender a ese puerto serie)
Contador = 0
Do While Contador < 1000
Contador = Contador + 1
DoEvents
If MSComm1.DSRHolding = True Then Exit Do
Sleep (1000)
Loop
En el siguiente ejemplo, deseamos transmitir por el puerto serie una serie de números, del 1 al
For I = 1 To 1000
If MSComm1.DSRHolding = False Then Exit Do
MSComm1.Output = Str(I)
Next I
Función DoEvents
Observará que la variable Valor nunca podrá llegar a tener el valor 100, puesto que cada vez
que llega a 90 le ponemos de nuevo el valor 0. El programa no va a salir nunca del bucle, pero
además no podrá atender a otros eventos que se produzcan en el ordenador (por ejemplo la
pulsación de una tecla) ya que el microprocesador está atendiendo solamente a ese bucle. Si
lo está ejecutando desde el propio entorno de Visual Basic, deberá parar la ejecución mediante
las teclas Ctrl – Pausa. No podrá pararlo mediante el botón Terminar de la barra de
herramientas de VB. En un programa compilado posiblemente tenga que reiniciar el equipo
mediante Alt – Ctrl – Sup, llevándose por delante el resto de los programas que se estén
ejecutando en ese momento en su PC.
No es difícil cometer este error involuntariamente. Esto es lo que se llama un bucle infinito.
Para que no suceda, o al menos, que si nuestro programa entra en un bucle infinito se pueda
salir de una forma no tan agresiva, hay que introducir dentro del bucle la instrucción DoEvents.
Cuando VB encuentra esta función el control del microprocesador al sistema operativo, y si hay
alguna operación en espera (la pulsación de una tecla, por ejemplo) la ejecuta y posteriormente
vuelve a ejecutar el bucle. De esta forma, podemos salvar al menos los datos de las otras
aplicaciones que se están ejecutando al mismo tiempo.
Y ya que explicamos como se sale de un bucle por la vía rápida, veamos como salir de un
procedimiento usando también esa “vía rápida”.
Para salir de un procedimiento basta con ejecutar la sentencia Exit Sub. En el ejemplo
LSB Visual Basic – Guía del Estudiante Capítulo 1 Página 18
siguiente, usamos el botón BotonLeerNombre para introducir el dato Nombre en una base de
datos. Pero ese campo puede tener como máximo, 20 caracteres. Si el usuario ha introducido
mas de 20 caracteres, debemos salir del procedimiento donde se introduce ese dato sin
ejecutarlo de forma íntegra:
NOTA. Los ejemplos anteriores sirven solamente de ilustración para explicar el código que
estamos estudiando. No pretenda ver una forma mejor de realizar la misma función, que
seguro que existe.
Funciones de cadena
Se denomina CADENA a una sucesión de caracteres. Una cadena puede tener uno o varios
caracteres alfanuméricos. Una cadena es también una sucesión de números.
Ejemplo de cadenas:
Hagamos una distinción entre una cadena que contenga números y un número. Un número
tiene un valor, pero también tiene una presentación escrita. El valor es algo que estará en el
ordenador como una sucesión de bits. Dependiendo de como lo hayamos declarado, tendrá
mas o menos bits. Pero esa información no es un número legible por el usuario. Lo que sí es
legible por el usuario es la representación en numeración decimal (u octal, o hexadecimal, o
incluso en binario) de ese número. Esa representación del número en un determinado sistema
de numeración es una cadena.
Así, el número nueve, que en la memoria del ordenador será una sucesión de bits, nos lo podrá
presentar como:
9 En numeración decimal
9 En numeración hexadecimal
11 En numeración octal
1001 En numeración binaria
Esas formas de presentarnos el número son CADENAS de caracteres. El valor del número
dentro del ordenador es un NUMERO.
Cuando introducimos un valor numérico por teclado o pantalla, el ordenador para hacer
operaciones con él, debe transformarlo a un número.
Estas consideraciones nos llevan a las dos primeras funciones con cadenas:
Ejemplos
Nota para recordar: Siempre habrá que convertir un número a una cadena cuando queramos
presentarlo en la pantalla. Siempre habrá que convertir a número la cadena de caracteres
numéricos que hayamos introducido por teclado o por pantalla, cuando queramos operar con
ese número. Un Label, cuando tiene que presentar un único número, no le pedirá que se lo
convierta a cadena, pues VB hace automáticamente esa conversión. Sin embargo, cuando
tiene que presentar un número, y además un texto en el mismo Label, VB no realizará
automáticamente ese cambio, por lo que le dará un error. Le recomiendo que convierta los
números a variables de cadena siempre que los quiera presentar en un Label o TextBox.
Cuando tenga mas experiencia en VB verá que esta observación estará un poco de más. Pero
de momento, convierta cada dato según lo vaya a necesitar.
Existe una función mas amplia que Str. Se trata de Cstr. Esta función no solamente
transforma un número a una cadena, como hace Str, sino que convierte cualquier tipo de
variable a una variable tipo String (cadena).
Esta función transforma, por ejemplo, una variable tipo Booleana en una variable de cadena,
devolviendo la cadena “Verdadero” si el valor de la variable booleana es True, y “Falso” si es
False.
Si se aplica Cstr a una variable tipo Fecha/Hora devuelve la fecha / Hora en formato corto.
Left (cadena, n)
Extrae los n primeros caracteres de una cadena, comenzando por la izquierda.
UCase (cadena) Devuelve otra cadena igual, pero con todos los
caracteres en mayúsculas. (UCase = Upper Case)
LTrim Elimina los posibles espacios que tenga una cadena por
Su izquierda.
Rtrim Elimina los posibles espacios que tenga una cadena por
Su derecha.
Trim Elimina los espacios que tenga una cadena, tanto por su
izquierda como por su derecha. (No elimina los espacios
centrales de la cadena)
Estas tres funciones se emplean para quitar los posibles espacios que pueden resultar
de una entrada de datos. Tienen especial importancia cuando se toman los datos de un archivo
o base de datos, donde fueron introducidos por otro programa.
La función Trim hay que usarla cada vez que convertimos un número a cadena de caracteres.
Cuando usamos Str(Número), la función Str introduce un espacio al comienzo de la cadena
que, en la mayor parte de los casos, produce un error cuando lo vamos a comparar, a
introducir en una base de datos, etc.
No se pone ningún ejemplo de estas funciones, pues sería difícil verlo impreso.
Mid puede usarse también para cambiar el contenido de una cadena. Observe la sutileza entre
Mid como Función de cadena y Mid como Instrucción.
Si la cadena que vamos a introducir tuviera más caracteres, tomará solamente los primeros
Función Asc
Nota Hay otra función (AscB) que puede usarse con bytes contenidos en una cadena. En
lugar de devolver el código del carácter para el primer carácter, AscB devuelve el primer byte.
Función Chr
LSB Visual Basic – Guía del Estudiante Capítulo 1 Página 23
Devuelve el carácter asociado con el código de carácter especificado.
Otros caracteres importantes son, el Backspace (carácter 8), y el espacio (carácter 32)
Nota Se proporciona otra función (ChrB) para su uso con datos de byte incluidos en una
cadena. En lugar de devolver un carácter, que puede ser de uno o de dos bytes, ChrB siempre
devuelve un solo byte.
Visual Basic puede operar con números tal como lo haría cualquier persona. Conoce las 4
reglas, y puede utilizar paréntesis de la misma forma que los escribimos sobre el papel.
+ Suma
- Resta
* Multiplicación
/ División
\ División sin decimales
Mod Resto de una división
^ Eleva a un exponente
Ejemplos
Existen otras operaciones que se pueden realizar con números: comparaciones. Los
operadores que realizan comparaciones se denominan Operadores relacionales. El
resultado de las operaciones realizadas con estos operadores solamente admiten dos
resultados: True (Cierto) o False (Falso) Estos operadores son:
= Igual que
<> No igual que
< Menor que
Estos operadores se suelen utilizar en estructuras de programa donde se tome una decisión.
Operadores Lógicos
Estos operadores podemos usarlos con variables Booleanas, es decir, aquellas que solamente
pueden tomar los valores cero y uno, y con caracteres (un carácter = 1 Byte = 8 bits)
realizando la operación correspondiente bit a bit con los 8 bits de cada carácter. Pero en este
caso, los parámetros deben introducirse en decimal. Por ejemplo, si quiere realizar la función
And entre el carácter 15 (00001111) y el 131 (10000011) , debe ponerlo de la forma
Resultado = 15 And 132. El resultado de esta operación será el byte 00000011, pero como Ud.
ya se habrá dado cuenta, le devolverá el valor 3. En el ejercicio Cap11 puede ver un ejemplo
muy explicativo
Int y Fix eliminan la fracción de un número y devuelven el valor entero resultante. La diferencia
entre Int y Fix es que si el número es negativo, Int devuelve el primer entero negativo menor o
igual a número, mientras que Fix devuelve el primer entero negativo mayor o igual a número.
Por ejemplo, Int convierte -8,4 en -9, y Fix convierte -8,4 en -8.
Si número no es un número entero, se redondea al número entero más cercano antes de ser
evaluado.
Mediante esta función se puede convertir un número de hasta ocho caracteres hexadecimales.
Si número no es entero, se redondea al número entero más cercano antes de ser evaluado.
Recuerde que estas dos funciones devuelven una Cadena de caracteres, no un número.
El resultado deberá tratarlo como una cadena de caracteres.
CLng se diferencia de las funciones Fix e Int en que trunca, en vez de redondear, la parte
fraccionaria de un número. Cuando la parte fraccionaria es exactamente 0,5, la función CLng
siempre la redondea al número par más cercano. Por ejemplo, 0,5 se redondea a 0 y 1,5 a 2.
CInt se diferencia de las funciones Fix y Int en que trunca, en vez de redondear, la parte
fraccionaria de un número. Cuando la parte fraccionaria es exactamente 0,5, la función CInt
siempre la redondea al número par más cercano. Por ejemplo, 0,5 se redondea a 0 y 1,5 a 2.
Debe usar las función CByte, CCur, CLng, CInt en lugar de Val para proporcionar
conversiones que reconozcan las variantes internacionales, cuando se convierte de cualquier
otro tipo de datos. Por ejemplo, los diferentes separadores decimales se reconocen
adecuadamente, dependiendo de la configuración de la información local de su PC.
Si la expresión a convertir queda fuera del intervalo aceptable para el tipo de datos a obtener,
ocurrirá un error.
Debe usar la función CStr en lugar de Str para proporcionar conversiones que reconozcan las
variantes internacionales, cuando se convierte de cualquier otro tipo de datos a String. Por
ejemplo, los diferentes separadores decimales se reconocen adecuadamente, dependiendo de
la configuración de la información local de su sistema.
Función Cdate Convierte una expresión al tipo de datos Date. (Fecha / Hora)
En algunas ocasiones (no siempre, misterios del VB) cuando hacemos una operación entre
números, cuyas variables que los contienen se han declarado de distinto tipo, puede ocurrir
que dé un error. Por ejemplo, queremos obtener una suma que debe ser un Long, a partir de
varios números tipo Byte. Declaramos las variables de la siguiente forma:
Suma = I1 + I2 + I3 + I4 + i5
El resultado de esta operación es que da un error, pues no entiende que sumando varios Byte
(números comprendidos entre 0 y 255) puedan dar un número de otro tipo. Dará seguramente
el error 6, Overflow. Para que esto no suceda, haremos el truco de convertir el primero de los
números que forman parte en la operación matemática a un Long, y de esta forma, VB ya se
da cuenta que el resultado de esa operación es un Long:
Suma = CLng(I1) + I2 + I3 + I4 + I5
Tenga en cuenta esa observación inicial que NO ocurre en todas las ocasiones. (Y me parece
que era un fallo de los muchos que tenía VB5)
Otras Funciones
Una función curiosa
NOTA MUY IMPORTANTE para versiones de VB anteriores a la 6.- Los números aleatorios
generados de esta forma son siempre iguales, eso sí, dependiendo del número que se le
introduzca como parámetro. Esta generación de números no produce números aleatorios pues
como se dijo, dependen del numero que se meta como parámetro, y si este se repite, se repite
la sucesión de números que nos crea el generador aleatorio. (Afortunadamente lo han
corregido en la versión 6)
Randomize Timer
La función Randomize devuelve una variable Rnd con un número comprendido entre 0
y 1 (Nunca será 0 ni 1) Leyendo el valor de la variable sucesivas veces, se puede obtener una
sucesión de números aleatorios. No es necesario ejecutar la instrucción Randomize Timer
cada vez que se quiera obtener un dato de la variable Rnd.
Supongamos que tenemos un formulario con una etiqueta de nombre Label1, un botón de
comando de nombre Command1. Cuando hagamos click sobre el botón de comando deberá
generar un número comprendido entre el 1 y el 49. En el procedimiento click de Command1
pondremos el siguiente código:
Randomize Timer
A = Rnd * 100
A = CInt(A)
LSB Visual Basic – Guía del Estudiante Capítulo 1 Página 28
Do While A > 49
A = A - 49
Loop
Do While A < 1
A = A + 49
Loop
Label1.caption = A
End Sub
Realice este pequeño programa, con la instrucción Randomize Timer y sin ella.
Date Devuelve la fecha de hoy. Esta fecha la toma del reloj del ordenador.
Mediante estas instrucciones podemos obtener el dato necesario de una fecha u hora. Por
ejemplo, para obtener el día de hoy solamente:
La función DatePart merece estudio aparte. Con esta función podemos averiguar el mes,
trimestre, semana, día del año, etc.
Por ejemplo, y tomando como referencia para todos los ejemplos el día 22 de Septiembre de
1998, fecha en la que se ha escrito este texto, (Now = 22/09/1998), tenemos :
Cuidado con como se escriben las fechas. Una posibilidad es la usada en los ejemplos, pero
solamente vale para el día de hoy. Puede expresar la fecha metiéndola entre dos signos #,
pero en ese caso debe estar expresada en el formato mm/dd/yy (mes/día/año). Ejemplo :
Pero lo mas fácil para el formato de fecha usado en España es meter la fecha entre comillas
dobles, y usar el formato especificado para el país :
Con las comillas dobles puede incluso no poner el año. En este caso le pone por defecto, el
año en curso.
Estas tres formas de expresar la fecha funcionan perfectamente, al igual que cuando lo
hacíamos con Now, y el resultado sigue siendo 39.
(No se extrañe que esta explicación la encuentre solamente en esta Guía del Estudiante)
Visual Basic trata las fechas como números. Y precisamente como un número Double. Emplea
por lo tanto 8 bytes para representar una fecha. Por ejemplo, al día 12 de Julio de 1999,
(Fecha en la que el autor de este libro alcanzó la respetable edad de #9 años) le corresponde
el valor de 36563. Ese es el número de días transcurridos desde el origen de tiempos tomado
por VB (Y por muchas otras aplicaciones de Microsoft y de otras marcas) hasta ese fatídico
día. ¿ Cual es ese origen del tiempo? El 30 diciembre 1899. ¿Que porqué ese día?. No lo sé.
Pero es fácil intuir que motivos prácticos para que cualquier fecha del siglo XX tuviese un
número positivo. Porque las anteriores a ese día las trata como números negativos. Por
ejemplo, el día 12 de Octubre de 1492, VB lo trata como el día número - 148.732. Ese
numero puede conocerlo utilizando la función CDbl sobre una variable tipo Date. Puede verlo
en el ejercicio Cap12 del disco.
Una hora también es una variable tipo Date. Y una composición Día / Hora también. ¿Que
como trata los minutos dentro de una variable Date? Pues como decimales de ese número
Double. Por ejemplo, la hora 12:00:00 del día 12 de julio de 1999 es para VB
36.563,5
Es decir, la parte decimal representa la fracción de día transcurrido desde el comienzo del día
(medianoche) hasta la hora en cuestión. No debemos confundir esta fracción del día (0,5 para
las 12:00:00) con el valor de la función Timer para ese instante (45.000)) que representa el
número de segundos transcurridos desde la 12 de la noche de ese día.
Cuando se tratan variables tipo Date, VB que es un lenguaje inteligente, interpreta como tipo
de datos Date determinados datos que no lo son. Por ejemplo, 12 Julio 1999 es tal y como
puede verse, una sucesión de caracteres. Si ejecutamos el código:
Al estar Pepe declarada como variable tipo Date, VB busca todas las posibilidades de extraer
una fecha (o una hora) del dato que le estamos metiendo. Otra cosa ocurriría si la
declarásemos como String o Variant. En cualquiera de estos casos, Pepe contendría el literal
introducido, es decir, “12 Julio 1999”
De lo anterior se desprende una cosa, dado que la fecha la guarda como un número, no
importa en que formato le introduzcamos una fecha. Si ponemos que Fecha1 = “12/7/99” es
idéntico a poner fecha1 = 12/07/1999 ó Fecha1 = 1999-07-12. Eso sí, debe reconocer el
formato, y luego, el dato, lo tratará como un número Double. Lo volveremos a ver cuando
estudiemos las bases de datos. Access trata las fechas de idéntica forma.
Función FORMAT
Esta función permite presentar cadenas numéricas o fechas de una determinada forma.
Permite establecer el Formato de esa cadena.
Si recurre a la ayuda de VB acerca de esta función se va a enterar muy poco de lo que puede
dar de sí. La sintaxis que presenta es :
Lo mejor que puede hacer con esta definición de la sintaxis de Format es olvidarla. No le aclara
mucho lo que se puede hacer con Format. La función Format se usa para poner una fecha en
un determinado formato. Con la expresión :
la variable FechadeHoy tendrá el valor 1998-05-21, que se refiere al día 21 de mayo de 1998,
según el formato recomendado por la norma ISO-8601 para la presentación de fechas. Si
hubiésemos puesto la expresión FechadeHoy = Format (Now, “dd/mm/yy”) , la variable
FechadeHoy contendría el valor 21/05/98 referido al día citado.
Las posibilidades de Format llegan también al campo de las cadenas numéricas. Por ejemplo la
cifra 123456 se transformará en las siguientes expresiones, según como empleemos la función
Format
No se complique la vida con el tema del primer día de la semana ni la primera semana del
año. No lo usará con frecuencia. Centrémonos mas en el parámetro Formato. Puede tomar
muchos valores. Veamos los principales. Primero para números
Observe que usamos la expresión Variable = Format (1234567,”Formato”) para todos los
ejemplos de números.
(Cada carácter # indica que ahí va un número. El separador debe ser una coma, no un punto,
aunque esto depende del idioma que esté usando)
Puede poner una de estas expresiones, eso si, siempre entre comillas dobles :
Para fechas (Observe que usamos el ejemplo Format(Now, “Formato”) siendo Now la fecha
y hora actual (21/07/98 a las 22:16:00 )
Va como cultura general. Esta recomendación dice que el formato de fecha debe ponerse de la
forma yyyy-mm-dd (p.e. 1998-10-05 para referirse al 5 de Octubre de 1998). Para este
formato, pondremos :
Format dispone de mas opciones. Sería muy largo explicarlas aquí. Para mas información, en
la WEB de Microsoft (www.microsoft.com) puede encontrar mas formatos posibles.
Cuando realizamos el Format sobre una fecha, esta fecha puede ser un literal (un string), o una
variable tipo fecha
Si en Text1 tenemos la expresión 12 Julio 1999, y queremos convertir esa fecha al formato
ISO 8601, podemos hacerlo de cualquiera de estas dos formas:
La función Format es suficientemente inteligente para darse cuenta que con el formato yyyy-
mm-dd le estamos pidiendo una fecha. Si le metemos una fecha no hay problema (caso de
meterle Pepe, variable declarada como Date). Pero si le metemos una cadena de caracteres
(caso de Text1.Text) intenta obtener de esa cadena una fecha válida. Si lo consigue lo
presenta. Si no lo consigue, dará un error.
Cuando utilizamos varias variables que tienen un significado similar para nosotros, pero que
son distintas (Por ejemplo, el nombre de los alumnos de una clase) podemos utilizar una
matriz. Esta matriz está formada por tantos elementos como alumnos tenga la clase. El
nombre asociado a cada uno de los elementos de la matriz puede ser:
Las matrices normalmente comienzan a numerar por el número 0. Este comienzo puede no ser
el mas apropiado para la variable que estamos planteando, pues ningún alumno tiene el
número de orden 0. Para hacer que una matriz comience a numerar por el 1 se debe definir
mediante la instrucción:
Option Base 1
que debe colocarse al comienzo del módulo o formulario donde declaremos la matriz.
Para declarar la matriz se hace como con todas las variables, especificando entre paréntesis el
número de elementos que componen la matriz:
Hemos declarado que la variable alumno es una cadena, y que hay 25 elementos en esa
matriz.
Donde le decimos que la variable Alumno tiene 25 elementos, que el primero tiene el índice 1 y
el último tiene el índice 25.
Pero imaginemos que queremos meter en la misma matriz el nombre, primer apellido y
segundo apellido del alumno. Necesitaremos declarar una matriz de 25 por 3. Como todos los
elementos serán cadenas de caracteres, podemos declararla de la siguiente forma:
De esta forma, el primer apellido del alumno que ocupa el puesto número 15 de la clase, será
el elemento:
Alumno (15, 2)
Dim Variable ( 1 To N, 1 To M, 1 To P, 1 To Q, 1 To R)
Una variable ya declarada como una matriz puede redimensionarse a lo largo del programa
mediante la instrucción ReDim
ReDim (25)
le estamos diciendo que tiene 25 elementos, pero que el primero sea el 0 ó el 1 depende de
haber puesto OPTION BASE 0 u OPTION BASE 1.
Una matriz puede redimensionarse cuantas veces se quiera a lo largo de la aplicación, pero
esa redimensión no puede afectar al número de dimensiones de la matriz. Si redimensionamos
la matriz perderá la información existente. Para evitar perder la información presente en la
matriz, debe utilizar la sentencia ReDim Preserve.
Dim MiMatriz()
Observe que no solamente la hemos cambiado dos veces de dimensiones (la primera a 2 y la
segunda a 3), sino que también hemos cambiado el número de elementos en cada dimensión.
Si hubiésemos utilizado ReDim Preserve solamente podríamos haber usado la primera de las
dos líneas anteriores :
pero ya no podríamos volver a cambiar el número de dimensiones con la segunda línea. Nos
daría un error.
Con ReDim podemos cambiar el número de elementos de cada dimensión cuantas veces
queramos. Por ejemplo, podemos redimensionar MiMatriz de las siguiente forma :
ReDim MiMatriz (1 To 5, 1 To 9)
ReDim MiMatriz (1 To 15, 1 To 20)
ReDim MiMatriz (1 To 25, 1 To 30)
...........................
Si hubiésemos empleado ReDim Preserve, podríamos cambiar los elementos de todas las
dimensiones de la matriz una vez :
ReDim Preserve (1 To 5, 1 To 9)
Esto no causa ningún error en una matriz de una dimensión, ya que si la matriz tiene sólo una
dimensión, puede cambiar el tamaño de esa dimensión porque es la única y la última.
Para conocer el índice máximo y mínimo de una matriz se usan las funciones UBound y
LBound.
UBound devuelve el mayor subíndice disponible para la dimensión indicada de una matriz.
LBound devuelve el mayor subíndice disponible para la dimensión indicada de una matriz.
En otro momento queremos tener el control de los índices de esa matriz, y queremos averiguar
el índice menor y mayor de cada una de sus dimensiones :
(IID1= Indice Inferior Dimensión 1, ISD 1 = Indice Superior Dimensión 1, etc. )
Función Split
Una vez visto lo que es una matriz, vamos a ver una función interesante para el tratamiento de
cadenas de caracteres. La función Split
Descripción
Esta función toma una cadena de caracteres compuesta varias subcadenas, separadas entre
ellas mediante un separador, y devuelve una matriz de una dimensión, que contiene esas
subcadenas.
Donde:
Ejemplo.
En el siguiente ejemplo veremos como separar distintos datos que están dentro de un TextBox
(Text1) separados entre ellos por una coma.
Entre en el programa VB. Le aparecerá en pantalla algo similar a esto : (Para VB Vers. 6)
Este es el comienzo del VB . Observe que en esta pantalla existen varias cosas. En la parte
superior , la barra de título del programa Visual Basic, con el texto :
Por debajo de esta barra de Título del VB, encontramos la barra de menú de VB, con las
leyendas :
Por debajo de esta barra de menú tenemos la barra de herramientas, donde podrá ver varios
iconos, que cada uno representa un determinada operación que Vd. puede realizar. Estas
operaciones está todas en la línea de menú, y puede acceder a ellas abriendo los menús
desplegables que existen en cada una de las palabras Archivo Edición Ver ...... de esta línea
de menú. El hecho de colocar las operaciones mas usuales en un icono en la barra de
herramientas se hace para mayor comodidad del usuario.
A la izquierda o derecha de la pantalla tiene una caja rectangular con varias columnas de
iconos. Esa caja es la Caja de Herramientas (No la confunda con la barra de herramientas de
la parte superior)
Posiblemente esta caja de herramientas no le aparezca tal y como la ve en esta figura. Eso
depende de la versión y la personalización. Para añadir un nuevo control haga click en
Proyecto | Componentes y le aparecerá una caja con todos los controles existentes. Puede
seleccionar nuevos controles para añadir a la caja de herramientas, marcando la casilla que
está a la izquierda del nombre del control que quiere introducir. Una vez seleccionados todos
los nuevos controles, haga click en APLICAR . Salga haciendo Click en ACEPTAR, y podrá
observar que esos nuevos controles ya se le han añadido a la caja de herramientas. Esos
controles le aparecerán cada vez que cargue el proyecto actual. No es prudente meter muchos
controles en la caja. Ponga solamente los que necesite normalmente en sus aplicaciones.
Ocupará menos memoria y tardará menos tiempo en cargar el programa VB. Además, cuando
realice una aplicación y la distribuya una vez compilada, Visual Basic entregará en los
disquetes de distribución las DLL’s u OCX’s correspondientes a todos los controles
personalizados que Vd. tenga en la caja de herramientas, los necesite el programa o no los
necesite. Esto le va a suponer que está cargando subprogramas inútiles en el ordenador
destino de su aplicación. A la hora de compilar el programa (Crear archivo .EXE) quite todos
los controles personalizados que no necesite su aplicación. (Sólo los controles personalizados.
Los controles comunes - CommandButton, Label, TextBox, etc.-, no se pueden eliminar de la
caja de herramientas)
Para quitar controles de su caja de herramientas, debe proceder de forma análoga, a lo que
hizo para meterlos en la caja de herramientas, pero al revés. No se preocupe a la hora de
quitarlos. Si está utilizando un determinado control, VB no le dejará quitarlo.
Puede tener dos ventanas más adosadas a la caja de herramientas, una denominada Ventana
de Propiedades, donde puede ver las propiedades del formulario o de cualquier control que
tenga dentro del proyecto, y otra, denominada Ventana de Proyecto, donde puede ver todos
los formularios existentes. Existen otras ventanas, por ejemplo la Ventana de Depuración.
Por cada formulario y cada control que introduzca en el proyecto, le aparecerá otra ventana,
denominada Ventana de código.
No se extrañe de que esta presentación gráfica del Visual Basic coincida con otros sistemas de
desarrollo (Delphi, p.e.). La lógica de desarrollo de una aplicación en Windows ha llevado a
varios fabricantes de software a utilizar un entorno gráfico similar (diríamos idéntico). A Visual
basic le queda el orgullo de ser el primero en utilizarlo. (¡Y el mejor!)
Con lo descrito anteriormente ya tenemos, al menos, fijado el argot con el que expresarnos
para comenzar a estudiar el VISUAL BASIC. Veamos con un poco mas detalle la Ventana de
Código.
Esta figura le muestra un Formulario con su ventana de código. Cada objeto gráfico de VB
tiene su propia ventana de código. Así, si en este formulario hubiésemos introducido un Label y
dos CommandButton, todos ellos tendrían su propia ventana de código. La ventana de código
la podemos ver haciendo doble click sobre cualquier objeto de nuestro proyecto. En este caso
hemos hecho doble click sobre el único objeto que teníamos : el formulario.
Observe las dos cajas de la parte superior, uno con la inscripción Objeto : que en el momento
que le sacamos la foto tenía Form, y el otro con la inscripción Proc : (procedimiento), que en el
momento de la foto tenía Load. A estas cajas les denominamos Lista de Objetos y Lista de
Procedimientos respectivamente.
Haciendo click sobre la flecha de cada lista, se despliega un menú, en la lista de objetos se
desplegará una lista con los nombres de cada objeto existente en ese momento dentro del
formulario. Haciendo click sobre uno de los nombres, nos presentará la ventana de código de
ese objeto. Todos los objetos gráficos (controles) existentes dentro de un formulario y el propio
formulario aparecerán en la misma lista de objetos.
Haciendo click sobre la flecha de la lista de procedimientos, se despliega la lista con todos los
procedimientos posibles para ese objeto. Siempre saldrá uno. Si tenemos escrito código en uno
de los procedimientos, saldrá por defecto ese procedimiento para el cual hemos escrito el
código. Si no hay código en ninguno de los procedimientos, saldrá el que tenga por defecto
cada objeto.
Solamente nos queda por decir, para cerrar este capítulo, que es un procedimiento.
Para ello vamos a explicar lo que es un evento. Un Evento es algo que le puede ocurrir a un
objeto. En una interface gráfica, lo que le puede ocurrir a un objeto es que se le haga click,
doble click, que se pase el cursor del ratón por encima, etc. Este es el Evento. El
Procedimiento es la respuesta por parte de ese objeto, al evento que le está sucediendo.
Esa respuesta, esa forma de Proceder del objeto al evento que le está sucediendo, debemos
programarla según nuestras necesidades, es decir, debemos escribir el código que necesite
nuestra aplicación como respuesta al evento que acaba de ocurrir. Posiblemente, no queramos
ninguna respuesta a muchos de los eventos que pueden acaecer a un objeto. Cada objeto
tiene muchos eventos y solamente queremos aprovechar los que nos interesan. Para que un
evento no produzca ningún efecto, basta con dejar sin código el procedimiento correspondiente
Observará que el primer elemento del menú desplegable de la lista de objetos se denomina
General. Este no es en realidad ningún objeto, sino un apartado existente en cada formulario,
que, al desplegar su lista de procedimientos tiene la sección de declaraciones, donde
debemos declarar las variables que queremos que afecten a todo el formulario y sus controles,
y tendrá además, los nombres de todos los procedimientos que introduzcamos (véase un
poco mas adelante). En este menú desplegable de la lista de procedimientos del General verá
con frecuencia cosas que Vd. no puso allí. Cosas tales como Command1_click, y en la
ventana un determinado código. Esto ocurre cuando se borra algún control que tenía escrito
código en alguno de sus procedimientos. Visual Basic sabe lo mucho que cuesta escribir el
código asociado a un control. Si borramos un control de nuestro formulario accidentalmente,
después de haber introducido todo el código asociado a él, Visual Basic nos sorprende con que
ese código no lo tira inmediatamente, sino que lo reserva como un procedimiento en ese
apartado General del formulario. Si en realidad queríamos borrar el control y todo su código,
debemos quitarlo de ese apartado General de nuestro formulario, pues en realidad, si no lo
queremos, no hará otra cosa que estorbar. Para quitarlo basta con borrar todo el código que
aparece en la ventana de código cuando hacemos click sobre el nombre del control eliminado.
Deberemos borrar todo el código, incluida la cabecera donde figura el nombre del control
eliminado, y la parte final, que siempre termina con End Sub.
El primer estorbo lo observará si crea otro control con el mismo nombre, cosa fácil ya que VB
da un nombre por defecto a cada control (Command1, Command2....). El código asociado al
control eliminado pasará automáticamente al nuevo control con el mismo nombre.
Una aplicación puede tener todo su código escrito en los sucesivos procedimientos del
formulario y de los controles que tenga ese formulario.
Puede ocurrir que un determinado evento no esté entre los posibles eventos de los controles
de nuestra aplicación. Piense por ejemplo, el evento de que la variable A sea igual a la variable
B. No existe en ninguno de los controles ese procedimiento. No se preocupe, puede crear un
procedimiento que se ejecute cuando su programa lo decida. Podemos añadir cuantos
procedimientos queramos. Estos procedimientos se añaden al formulario, y deberán definirse
por un nombre que Vd. debe elegir. Para que se ejecuten las instrucciones (código) incluido en
ese procedimiento, basta simplemente con nombrarlo por ese nombre.
Para insertar un procedimiento debe ir a la barra de menú, hacer click sobre Herramientas, y
en el menú que le desplegará, volver a hacer click sobre Agregar Procedimiento. VB le
presentará un cuadro donde le pedirá el nombre, si quiere que sea un procedimiento, una
función o una propiedad. A lo largo del curso irá viendo que es cada cosa.
Escribiendo el código en los sucesivos procedimientos, bien en los propios de cada objeto, bien
en los procedimientos que vayamos creando, es posible completar la aplicación. Pero en una
aplicación larga esta forma de escribir el código no sería la mas adecuada. Es mas,
posiblemente sería demasiado engorroso escribirla de esta forma, y muy probablemente
deberíamos escribir el mismo código para varios procedimientos, lo que alargaría inútilmente el
programa y el tiempo de desarrollo.
Para disponer de un sitio donde escribir parte (o la mayor parte) de su programa, puede
introducir uno o varios módulos. Expliquemos lo que es un módulo.
Un Módulo es una parte del programa donde solamente puede escribir código. Es igual que un
formulario, sin interface gráfica. Un profesor de Visual Basic lo expresaba diciendo que un
Módulo es un Formulario sin cara. En un módulo pueden existir procedimientos al igual que en
los formularios, pero como un módulo no tiene interface gráfica, esos procedimientos debe
introducirlos el programador tal y como explicamos un poco mas atrás. El módulo tiene su
propia ventana de código, al igual que un formulario, con un objeto único, el apartado General.
Aquí también tenemos la sección de declaraciones, al igual que los formularios. En esta
Los módulos se emplean para la declaración de variables globales, y para escribir el código de
la aplicación que sea común a varios formularios. Esto nos evita tener que repetir código
inútilmente. Ese código común se escribirá en un procedimiento que previamente habremos
insertado en este módulo, y lo citaremos por su nombre desde cualquier parte del programa.
Los procedimientos se pueden introducir en los módulos o en los formularios. Y les pasa lo
mismo que a las variables en cuanto a su ámbito. Para agregar un procedimiento debe abrir
una ventana de código (cualquiera) perteneciente al módulo o formulario donde quiera insertar
ese módulo, y a continuación hacer click sobre Herramientas | Agregar Procedimiento de la
Barra de Menú.
Nos aparecerá un cuadro donde le debemos poner el nombre de ese procedimiento y elegir el
ámbito (Alcance en la figura) que le queremos dar, público o privado.
¿Cual es el nombre por el que podemos llamar a ese procedimiento? Sigue las mismas
normas que para las variables. Si se ha declarado como público en un módulo, se le citará por
su nombre cualquiera que sea la parte del programa desde donde le citemos.
p.e. Formulario1.CalculaGastos
Si se ha declarado como privado, se le llamará por el nombre, pero solamente desde el código
del módulo o formulario donde se insertó, puesto que desde otro módulo o formulario no se
verá.
Fíjese en el punto usado como separador entre el nombre del formulario y el nombre del
procedimiento. VB usa como separador un punto. Usa el separador para separar el nombre de
un control y una de sus propiedades (Label1.Caption), para separar el nombre del formulario
del nombre de uno de sus controles (Formulario1.label1.caption) Se irá familiarizando con la
terminología VB según vayamos avanzando en el curso.
Call
En Visual Basic, para ejecutar un procedimiento no hace falta usar la sentencia Call, muy
propia de otros lenguajes de programación. Hay programadores que la usan no sé si porque
creen que es necesario, o porque piensan que queda más bonito. No hace falta pero VB lo
soporta. Queda a gusto del programador usarla o no usarla.
Funciones
Main
Merece la pena pararse un poco para estudiar el Procedimiento Main. Para verlo con mas
detalle, comentaremos como comienza a trabajar una aplicación realizada en Visual Basic.
Imaginemos una aplicación que tiene 3 Formularios. En cada uno de ellos tiene código.
Lógicamente la aplicación tendrá que presentar uno de ellos en primer lugar. Deberemos
decirle a Visual Basic cual es el formulario inicial, y será ese por el que empiece. En ese
formulario dispondremos el código necesario para que la aplicación se ejecute en el orden
deseado.
Verá que tiene 5 pestañas, y actualmente tiene abierta la pestaña correspondiente a General,
y tiene desplegada una lista donde nos pide el Formulario Inicial. En esa lista figura también la
expresión Sub Main. Si ponemos como formulario inicial uno de los formularios, la aplicación
comenzará por ese formulario. Si en uno de los Módulos existentes en el proyecto, ponemos
un procedimiento llamado Main, podemos comenzar la ejecución de la aplicación justamente
por ese procedimiento. En ese procedimiento pondremos el código necesario para que,
posteriormente, se muestre uno de los formularios. Esto es muy practico cuando queremos
hacer una función previa a mostrar cualquier formulario (abrir una base de datos, por ejemplo).
Para comenzar la aplicación por Main se elige esa opción en la lista Formulario Inicial.
¡ Recuerde que Main debe estar en un Módulo !
El cuadro de diálogo anterior sirve además para otras cosas. Entre ellas poner el nombre del
proyecto (nombre que no aparecerá por ninguna parte, solo en los datos internos de la
aplicación) y su descripción.
Existe otro cuadro parecido en Herramientas | Opciones donde puede terminar de completar
las condiciones de trabajo. Observe el cuadro “Cuando Inicie sus programas”. Aquí le permite
grabar el programa antes de ejecutar, cosa que le recomiendo para evitar que, al ejecutarlo,
por un código mal puesto se le cuelgue el PC y tenga que volver a empezar. En este cuadro
puede elegir también el formato de la ventana de trabajo formato del editor, etc.
Podemos elegir también las ventanas que queremos que estén siempre visibles, y que verifique
automáticamente la sintaxis de una instrucción en el momento de escribirla.
Cambiar el color de la letra y del fondo del código, según el tipo que sea (código, comentario,
error devuelto, etc.). Los alumnos mas aventajados y con ganas de marear al profesor,
conocen la forma de poner como invisible una parte del texto del código. Si observa
comportamientos raros en el texto donde escribe el código, revise el cuadro de Colores de
Código.
En la sección de operadores lógicos puede jugar a aplicar uno de los operadores disponibles a
una pareja de número o a un par de datos booleanos. Observe que para invertir los bits de un
byte basta con hacer el Xor con 255
Este sistema de cifrado puede utilizarlo en sus aplicaciones para, por ejemplo, cifrar el
Password de entrada y poder guardarlo cifrado en el disco duro de su PC. De cualquier forma,
debo indicarle que este sistema de cifrado al tiempo que simple, es tremendamente vulnerable,
pero si lo acompaña de otras características, lo convierte en un cifrador intrínsecamente
seguro (Vigenere). La explicación mas detallada de algoritmos cifradores se sale por completo
de la intención de este curso.
Supongamos que queremos cifrar la palabra Secreto que es el Password de entrada para una
aplicación. Deberemos usar otra palabra que cifrará a la anterior, que llamaremos Clave. La
clave debe tener, al menos, tantos caracteres como la palabra a cifrar. Imaginemos que clave
es ABCDEFGHIJK
Para cifrar usamos la función Xor de cada una de las letras de Secreto con las
correspondientes letras de la clave. (La primera letra de Secreto con la primera letra de la
clave, la segunda con la segunda, etc.) Pero debemos efectuar la función Xor sobre un
número, no sobre una letra. Por lo tanto convertiremos la letra en un número usando la función
Asc Asc (A) = 65, ya que 65 es el número Ascii que corresponde a la A
Para realizar el Xor del carácter introducido en la variable a, con el carácter introducido en la
variable b haremos:
Resultado = 83 Xor 65
Luego el resultado de realizar el Xor entre S y A es el carácter 18, que no tiene representación
como una letra.
65 = 0 1 0 0 0 0 0 1
18 = 0 0 0 1 0 0 1 0
Resultado 2= 0 1 0 1 0 0 1 1 = 83 es decir la letra S
Vamos a ver en este capítulo como podemos realizar una aplicación en Visual Basic.
Comencemos por abajo.
Una aplicación VB es una aplicación que, generalmente, tiene una interface gráfica. Es decir,
es una aplicación de las típicas de Windows. Y esa interface gráfica está formada por un
formulario y dentro de él, controles. Tanto al formulario como a los controles les denominamos
genéricamente Objetos. Hay objetos VB que no los podemos ver en la interface gráfica. No
podemos verlos porque pese a que son objetos VB, no tienen ninguna representación en la
ventana. Son por ejemplo, los objetos de acceso a datos que veremos profusamente más
adelante.
Todos los objetos de Visual Basic tienen Propiedades. (Por ejemplo, el nombre de ese objeto
es una de sus propiedades). Los objetos que tienen parte gráfica tienen además Eventos. Y
muchos de ellos tienen también Métodos
Las propiedades son aquellas características de un objeto que lo define "físicamente", bien por
su forma o color, por su contenido, por la forma en la que va a trabajar… Las propiedades
pueden modificarse cuando estamos diseñando la interface gráfica, mediante lo que llamamos
caja de propiedades, o durante la ejecución del programa. En este caso hay que hacerlo con
código escrito en el propio programa. Veamos ya dos definiciones que se repetirán
profusamente a lo largo del curso
- Tiempo de diseño. Es cuando realizamos una operación durante el diseño. Por ejemplo,
podemos cambiar el color de un control durante el diseño de la aplicación, accediendo a su
propiedad BackColor en la caja de propiedades.
- Tiempo de ejecución. Es cuando esa operación se realiza durante la ejecución del
programa. Si tenemos una línea de código como esta
MiControl.BackColor =RGB(255,0,0)
al ejecutarse esa línea, se cambiará el color del control de nombre MiControl. Hemos cambiado
la propiedad BackColor de ese control en tiempo de ejecución.
Las propiedades pueden ser de lectura y escritura, (se puede cambiar y se puede leer el valor
de la propiedad), sólo de lectura (solamente se puede leer el valor de la propiedad) ó solo de
escritura (hay muy pocas de este tipo). Puede que una propiedad, que es de lectura y
escritura en tiempo de diseño, sea sólo de lectura en tiempo de ejecución (esto es lo que le
pasa por ejemplo, a la propiedad Name - Nombre)
Un evento es todo aquello que le puede ocurrir a un objeto con parte gráfica (Control o
Formulario) Por ejemplo, es un evento el hecho de hacer click sobre ese control, el hecho de
pasar el ratón por encima de él, el hecho de que un control cambie de tamaño…. Los controles
tienen muchos eventos, unos de ellos comunes a casi todos los controles (Evento click, por
ejemplo) y otros exclusivos de un determinado control (El evento Timer solamente lo tiene el
control Timer) Puede ver los eventos de un control haciendo doble click sobre ese control en
tiempo de diseño. Le aparecerá la ventana de código.
La ventana de código es el lugar donde deberá escribir el código de su aplicación. Puede ver
que existen en ella dos listas desplegables, una a la izquierda (sin desplegar) donde se ve el
nombre del control del cual estamos visualizando el código (en este caso Command1) y otra a
la derecha, donde se despliegan todos los eventos que tiene ese control. Haciendo click en la
línea de uno de esos eventos, aparecerá la ventana de código dedicada a ese evento.
Nota. Fíjese en la parte inferior izquierda de la ventana de código. Hay dos botones, uno que
permite visualizar el código correspondiente a un solo evento, (el de más a la izquierda) y otro
que permiten ver en la misma ventana el código de todos los eventos. Cada programador tiene
su costumbre para ver uno o todos. La práctica le dirá lo que es más práctico para Vd.
Estos procedimientos forman parte del programa. Podría hacerse una aplicación que no
tuviese mas código que el introducido en los procedimientos, y muchas veces esa es la
realidad.
Un procedimiento puede pasar parámetros. Se dice que pasa parámetros cuando el sistema
aporta datos automáticamente al procedimiento. Por ejemplo, el procedimiento MouseUp, que
se ejecuta cuando levantamos el botón del ratón (también existe el evento MouseDown), pasa
los siguientes parámetros: Número del botón que se ha pulsado, (1=Izdo, 2=Dcho, 3=Central),
si está pulsada la tecla mayúsculas (Shift, 1 si está pulsada, 0 si no está pulsada) y los valores
X e Y de la posición del cursor del ratón. Podemos ver los parámetros que pasa en la propia
definición del procedimiento, que nos da Visual Basic
End Sub
Observe que los parámetros están entre paréntesis, y que queda definido el tipo de variable
que es cada uno de ellos. Podemos usar ese valor dentro del código del procedimiento como
un dato más.
Un Método es una operación que la realiza Visual Basic sin necesidad de escribir código para
realizarla. Por ejemplo, si queremos dibujar una línea en un formulario o en la impresora
utilizaremos el método Line. Si queremos dibujar una circunferencia usaremos el método
Circle. Si queremos escribir texto, utilizaremos el método Print. No necesitamos decirle como
lo tiene que haver, puesto que eso ya lo sabe hacer VB sin necesidad de que se lo
expliquemos. A los métodos les tenemos que pasar datos. A eso le llamamos también pasarle
parámetros. Los métodos solo permiten introducir los parámetros que necesita el método para
ejecutarse. (En el caso de una línea, el punto inicial y el final, en el caso del circulo, el radio y
las coordenadas del centro. Pueden pedir parámetros optativos, como el puede ser el color de
la línea o circulo.
Espero que le quede claro cada una de estas definiciones. Tendrá tiempo suficiente a lo largo
del curso para verlas, y dentro de muy poco tiempo le serán muy familiares estos conceptos.
EL FORMULARIO
El primer objeto Visual Basic con que nos encontramos es el FORMULARIO. De hecho, cada
vez que iniciamos Visual Basic (VB) nos presenta en pantalla un nuevo formulario, que tiene
por defecto el nombre de Form1
Fig. 2.3 Un formulario que parece un equipo de radio (Programa realizado por el autor)
Ejemplo de un formulario para una aplicación industrial. Este formulario reproduce el panel de
control de un transmisor - receptor de radio. En este caso, parece que el nombre de ventana le
viene mejor que el de formulario. Observe que dentro del formulario existen gran cantidad de
objetos. Botones, que hacen la misma función que el botón real en el equipo de radio, y un par
de displays, que muestran un texto, en este caso las frecuencias de transmisión y recepción.
Vamos a ver las propiedades del formulario, pero solo veremos las que son específicas para un
formulario. El resto las podrá ver al final del capítulo. Aquí haremos referencia a esas
LSB Visual Basic – Guía del Estudiante Capítulo 1 Página 52
propiedades con un asterisco (*) que significa que esa propiedad no tiene una notación
especial para los formularios. Seguiremos esta norma con todos los controles.
PROPIEDADES.
Name Nombre
Caption Título
Es el texto que aparecerá en la barra de Título cada vez que aparezca en pantalla este
formulario. No tiene otra función dentro del programa. El programa no accede a este formulario
por el título, sino por el nombre. Puede cambiarse en tiempo de ejecución.
Control Box Menú de Control en la parte sup. Izda. Valor por defecto : True
Propiedad Booleana que admite los valores de true (verdadero) o False (Falso). Si esta
propiedad es True, aparecerá en la esquina superior izquierda el icono (el "menos" en W-3.11)
para desplegar el menú de control de este formulario. Si esta propiedad se pone como False,
no aparece dicho icono y por tanto no se puede desplegar dicho menú.
MinButton
MaxButton Valor por defecto: True
Nota. En los formularios MDI child, es necesario poner a true las propiedades ControlBox,
MinButton y MaxButton para poder maximizar el formulario hijo. De no ponerlas a true, sí se
pretende maximizar el formulario hijo, (Propiedad WindowState=2) el formulario no aparece.
Define el tipo de borde que tendrá el formulario durante la ejecución. No se puede cambiar en
tiempo de ejecución. Admite los siguientes valores:
1 - Fixed Single
El formulario tendrá un borde fino, y no podrá cambiarse su tamaño
durante el tiempo de ejecución. Con este valor, el formulario puede
tener un menú de control, barra de título y botones de maximizar y
minimizar. Solo podrá cambiarse de tamaño utilizando estos botones.
2-Sizable
El formulario tendrá borde grueso, y se podrá cambiar su tamaño en
tiempo de ejecución mediante los botones de maximizar y minimizar, y
mediante el arrastre de uno de los bordes con el ratón.
3 - Fixed Dialog
El formulario tendrá borde grueso, y no se podrá redimensionar
durante la ejecución. No puede tener los botones de maximizar ni
minimizar.
5 - Sizable ToolWindow
En las versiones de 16 bits se comporta como Sizable. En W95
muestra el botón Cerrar y el texto de la barra de titulo aparece con un
tamaño de fuente reducido. El formulario no aparece en la barra de
tareas de W95.
Admite los valores 0 (=Flat, plano) y 1 (=3D) Si tiene el valor 1 (3D), el formulario aparecerá
con cierto efecto tridimensional, y los controles que le introduzcamos a este formulario
aparecerán como esculpidos dentro de él. Con valor 0 (Flat) en esta propiedad, el formulario
aparecerá durante la ejecución como una superficie plana. El color de fondo se ve afectado al
cambiar esta propiedad. Si se cambia a 3D, el fondo (Backcolor) toma el color definido en
Windows en el Panel de Control. Si se cambia a Flat, toma el color blanco
Propiedad Booleana. Esta propiedad, estando en True, permite actualizar el contenido del
formulario y de sus controles incluso cuando no están visibles. Imaginemos que en este
formulario existe un texto, que se haya cambiado por programa mientras este formulario no
estaba visible. Si esta propiedad Autoredraw está en False, al hacer visible este formulario,
aparecerá sin reflejar ese cambio. Si esta propiedad está en True, aparecerá actualizado.
Establece el color del fondo del formulario. Puede cambiarse en tiempo de ejecución.
Propiedad Booleana. Establece si un evento Paint vuelve a dibujar el objeto entero (True) o si
solamente dibujan las partes que han sufrido cambios (False)
DrawMode
Enabled (*)
Establece el color del primer plano del formulario. Es el color que tendrán las letras si
escribimos en él, o los dibujos, si lo que hacemos es dibujar. En tiempo de diseño, determina el
color de la rejilla,.
Especifica el tipo y tamaño de la letra que se usará en el formulario al utilizar el método Print.
Al seleccionar esta propiedad en la ventana de propiedades, aparece un cuadro de dialogo
donde se eligen ambos parámetros.
Cuando introduzca nuevos controles en el Formulario, la propiedad Font de estos controles
tomará el valor que tenga esta propiedad en el Formulario. Puede servirle este pequeño truco
para utilizar en todos los controles una determinada fuente sin tener que teclearla para cada
control.
Establece si el texto o gráfico de fondo del formulario se muestra (True) o no se muestra entre
los caracteres de texto escritos en el propio formulario.
Icon Icono
Esta propiedad define el icono que va a representar a este formulario cuando esté minimizado.
Si el formulario es el formulario padre o formulario de inicio de una aplicación, este icono es el
que toma el Asistente de Instalación para colocarlo como icono de apertura del programa en el
grupo de programas Windows correspondiente. Como valor de esta propiedad se le puede
asignar directamente el icono o el nombre de un archivo (con su path correspondiente) que lo
contiene, haciéndolo directamente sobre la caja de propiedades.
Propiedad Booleana. Cuando un formulario tiene dentro de sí varios controles, uno de ellos es
el que está activo. En estas condiciones, si se pulsa una tecla, esa pulsación la recibe en
primer lugar el control que esté activo, y si éste no la procesa, pasa esa pulsación al
formulario. Para hacer que esa pulsación pase previamente por formulario, debe ponerse esta
propiedad en True. Esta propiedad la usará frecuentemente cuando quiera realizar alguna
función pulsando una letra. Pone KeyPreview a True, y puede conocer que tecla se ha pulsado
en el procedimiento KeyPress del formulario. Si la tiene a False, ese procedimiento no se
ejecuta ya que la pulsación “no pasa” por el formulario.
Indica la posición del borde izquierdo del formulario respecto a la parte izquierda de la pantalla.
(Lo verá mas adelante, la pantalla será para VB el objeto Screen). Normalmente no se
introduce como valor numérico, sino que lo toma automáticamente de la posición que tenga el
Permite que una aplicación destino inicie una conversación DDE con el formulario (origen de
datos). Puede tomar los siguiente valores:
LinkTopic
Establece el tema al que este formulario va a responder a una conversación DDE, cuando
funciona como origen. Es por este tema por el que se debe llamar a este formulario cuando
actúa de origen en una conversación DDE
Establece que este formulario es un formulario Hijo dentro de un formulario MDI. No se puede
cambiar en tiempo de ejecución. Es una propiedad Booleana
MouseIcon (*)
Picture Gráfico
Mediante esta propiedad podemos poner un gráfico como fondo del formulario. El gráfico
puede ser un bit-map o un fichero .ICO
ScaleLeft, ScaleTop
Estas propiedades, medidas en la unidad de medida elegida para el ancho y alto mediante las
propiedades ScaleMode, ScaleWidth y ScaleHeight anteriores, expresan las coordenadas
iniciales de la parte izquierda y de la parte superior respectivamente del Formulario. Estas
propiedades no afectan a la posición del Formulario en la pantalla (Si está maximizado seguirá
ocupando toda la pantalla, si está en “Normal” ocupará el mismo sitio que se dio en tiempo de
diseño). Supongamos que se le asigna a un Formulario, las propiedades ScaleWidth = 400, y
ScaleHeight = 300. Si colocamos un control justamente en el centro del Formulario tendrá sus
propiedades Top =150 y Left=200. Si ponemos ahora las propiedades del Formulario
ScaleLeft a 30 y ScaleTop a 10, ese control, para seguir en el centro del Formulario deberá
tener sus propiedades Top a 160 (150 + 10) y Left a 230 (200 + 30).
Recuerde que las medidas de un formulario crecen, desde la esquina superior izquierda, según
avanzamos hacia abajo y hacia la derecha.
Como aclaración de las relaciones entre distintas unidades de medida, puede ver en la
siguiente table la correspondencia entre cada una de ellas y la unidad imaginaria Twip.
1 Point=20 Twips ; 1Pixel=15 Twips : 1 Charecter=240 Twips ; 1 Inch (pulgada) =1440 Twips
1mm=56,52 Twips 1 cm=566 Twips
Tag (*)
Esta propiedad establece la posición del borde superior del formulario respecto a la parte
superior de la pantalla (Objeto Screen). Normalmente no se introduce como valor numérico
sino que lo toma automáticamente de la posición que tenga el Formulario durante el tiempo de
diseño Este valor puede cambiarse durante la ejecución para, conjuntamente con Left, variar
la posición del Formulario. Los valores de Top y Left definen la esquina superior izquierda del
Formulario.
WindowState
Este evento ocurre cuando el formulario toma el foco. Cosa un poco difícil, ya que para que el
formulario tome el foco debe ocurrir, o que no tenga ningún control capaz de tomar el foco, o
que todos los controles existentes en el formulario y que puedan tomar el foco, estén
desactivados. No se suele usar este procedimiento.
Se produce al cargar por primera vez el formulario. Esto significa que si en una aplicación
cargamos ese formulario una vez (la primera) se realiza este evento, pero si descargamos el
formulario (con Unload Formxx) y luego lo volvemos a cargar (con Formxx.Show), esta
segunda vez (y sucesivas) no se produce este evento
Estos tres procedimientos son excluyentes y tienen la jerarquía con el orden siguiente:
KeyDown, KeyPress y KeyUp. Si el procedimiento KeyDown tiene código que pueda realizar
una operación, no se ejecutará el procedimiento KeyPress ni el KeyUp. Si es el procedimiento
KeyPress quien tiene el código que ejecuta una operación, no se ejecutará el KeyUp. Para que
realice esta exclusión es necesario que el código pueda realizar alguna operación.
Estos tres procedimientos ocurren cuando el formulario forma parte de un enlace DDE. Vea el
capítulo del DDE para mas detalles.
Cancel es un parámetro que debemos introducir para abortar la descarga del formulario. Si se
pone a un valor distinto de 0, se detiene la descarga.
Constante Valor
vbFormControlMenu 0 El usuario eligió el comando Cerrar del menú Control del
formulario o hizo click en el X del mismo.
vbFormCode 1 Se invocó la instrucción Unload desde el código.
vbAppWindows 2 La sesión actual del entorno operativo Microsoft Windows
está finalizando.
vbAppTaskManager 3 El Administrador de tareas de Microsoft Windows está
cerrando la
aplicación.
vbFormMDIForm 4 Un formulario MDI hijo se está cerrando porque el
formulario MDI padre también se está cerrando.
Se ejecuta cuando cambia el tamaño del Formulario, bien porque le cambia con las flechas del
ratón arrastrando uno de sus bordes, bien porque lo maximizamos, ponemos a tamaño
intermedio o minimizamos. Puede usar este evento para redistribuir o cambiar el tamaño de los
controles. No pasa parámetros.
Evento Terminate
Este evento no se produce si sale de la aplicación con la instrucción End. Solamente si lo hace
mediante Unload (Unload Me, p.e.) o con el comando Cerrar o el X del formulario. Me permito
recomendarle que No termine sus aplicaciones mediante End. No podrá usar los eventos
QueryUnload, Unload ni Terminate.
No se deben introducir mas controles de los necesarios. Sobre todo, cuando vaya a compilar el
proyecto. Si tiene metido en la caja de herramientas un control que no necesita, lo introducirá
en los discos de distribución igual que si lo necesitara.
El Command Button es un objeto que sirve para introducir datos a través de la pantalla. El
Botón de Comando tiene la siguiente forma:
El botón de comando puede usarse para la entrada de datos con el ratón, o para validar
cualquier operación. El tamaño puede cambiarse a voluntad, pero la forma siempre es
rectangular. En la figura anterior vemos dos botones de comando, uno de ellos (el Command2)
marcado con unos puntos en su contorno. Estos puntos nos permiten variar su tamaño en
tiempo de diseño. También puede cambiarse su tamaño y posición en tiempo de ejecución.
(Las propiedades marcadas con (*) no varían de la descripción general, hecha al final del
capítulo)
PROPIEDADES
Name Nombre(*)
Cancel
Establece un valor que indica si un botón de comando es el botón Cancelar de un formulario.
Es una propiedad booleana, y admite los valores True o False. Puede utilizar la propiedad
Cancel para dar al usuario la opción de cancelar los cambios que no se han hecho efectivos y
devolver el formulario a su estado anterior. En un formulario sólo puede haber un botón de
comando con la propiedad Cancel = True.
Causes Validation
Esta propiedad habilita o deshabilita la ejecución del evento Validate del control que tenía el
foco antes de cambiar el foco a este control. El evento Validate de un control se ejecuta
inmediatamente antes de que pierda el foco. (Se dice que un control tiene el foco cuando es
ese control el que está activado). Al hacer click sobre el botón de comando, siempre habrá
algún control que pierda el foco para que lo tome el botón de comando. Si el botón de comando
tiene la propiedad Causes Validation a True, se ejecutará el evento validate de ese control que
acaba de perder el foco. Si está a false, no se ejecutará. Observe que esta propiedad afecta a
un control diferente del que ostenta la propiedad.
Default
Establece un valor que determina el control CommandButton que es el botón de comando
predeterminado de un formulario. Sólo un botón de comando de un formulario puede ser el
botón de comando predeterminado. Cuando Default se define a True para un botón de
comando, se define automáticamente a False para el resto de los botones de comando del
formulario. Cuando la propiedad Default del botón de comando está establecida a True y su
formulario primario está activo, el usuario puede elegir el botón de comando (invocando su
evento Click) presionando ENTRAR. Cualquier otro control que tuviera el enfoque no recibe
evento de teclado (KeyDown, KeyPress o KeyUp) de la tecla ENTRAR a menos que el usuario
haya movido el enfoque a otro botón de comando del mismo formulario. En este caso, al
presionar ENTRAR se elige el botón de comando que tiene el enfoque en lugar del botón de
comando predeterminado.
MaskColor Establece cual es el color de la imagen del botón que actuará como máscara para
producir zonas transparentes. Para ello, la propiedad UseMaskColor debe estar a true. (¡)
Picture. Solamente está disponible cuando la propiedad Style está puesta a Graphical.
Pone un icono sobre el botón.
Una etiqueta es un control que nos permite presentar un texto. La etiqueta debe usarse en
aquellos casos en los que exista una información estática o dinámica que no deba ser
cambiada por el operador.
Puede adoptar estas formas: con borde tridimensional, borde plano o sin borde, y el texto
justificado a la izquierda, a la derecha o centrado.
Se ha incluido la trama para poder observar los límites de la etiqueta sin borde.
PROPIEDADES
Alignment Justificación
Sin borde o con borde. En caso de haber elegido en la propiedad Appearance el modo
tridimensional, y eligiendo con borde en esta propiedad, el aspecto adopta una forma como
incrustada en el formulario.
DataSource DataField
Estas propiedades establecen la forma en que debe llevarse a cabo una conexión DDE con
otra aplicación. Se verán con mas detalle al estudiar los enlaces DDE
UseMneumonic
Es una propiedad Booleana. Devuelve o establece un valor que indica si al incluir el signo (&)
en el texto de la propiedad Caption del control Label se define una tecla de acceso.
WordWrap
Devuelve o establece un valor que indica si un control Label con el valor True en su propiedad
AutoSize se expande vertical u horizontalmente para adaptarse al texto especificado en su
propiedad Caption. Es una propiedad Booleana.
True El control Label se expande o contrae horizontal y verticalmente para adaptarse al texto
y al tamaño de la fuente. Contempla para la expansión horizontal la colocación de los espacios
del texto.
False (Predeterminado) El texto no se ajusta a la siguiente línea; el control Label se expande
o contrae horizontalmente para adaptarse a la longitud del texto y verticalmente para adaptarse
al tamaño de la fuente y al número de líneas.
Las cajas de texto son los controles en los que Visual Basic presenta o introduce textos. Es por
tanto un control bidireccional. Normalmente se usan para introdución de textos, o para la
presentación de aquellos que el operador pueda cambiar. Para cambiar o escribir un texto en
una caja de texto, basta con conseguir que esa caja de texto tenga el foco y teclear el texto en
el teclado. Esto se puede lograr, bien haciendo click con el ratón en esa caja de texto, bien con
la tecla TAB, bien por programa.
La caja de texto no se debe usar nunca para presentar textos que el operador de la aplicación
no deba cambiar. Úsese para ello la etiqueta, control no bidireccional, que además tiene la
ventaja de ocupar menos memoria de programa.
Las cajas de texto pueden tener una o varias líneas, según esté la propiedad Multiline. La
capacidad máxima de una caja de textos es de 64 Kbytes.
PROPIEDADES
Alignment Justificación
Sin borde o con borde. En caso de haber elegido en la propiedad Appearance el modo
tridimensional, y eligiendo con borde en esta propiedad, el aspecto adopta una forma como
incrustada en el formulario.
Enabled Habilitado
Propiedad Booleana que habilita o deshabilita la etiqueta Cuando está deshabilitado (Enabled =
False), no tienen efecto los eventos que se produzcan sobre el TextBox. No se puede escribir
el él ni pasarle el foco, pero sí se le puede cambiar el texto mediante el programa. Puede ser
una buena alternativa para impedir que el usuario pueda cambiar un determinado texto. Esta
propiedad puede variarse en tiempo de ejecución.
Locked
Establece si el texto se puede editar, es decir, cambiar. Cuando se pone esta propiedad a
True, el texto existente en la caja puede resaltarse con el ratón, e incluso copiarlo al
portapapeles, pero no puede variarse tecleando un nuevo texto. Se puede cambiar por
programa, cambiando la propiedad Text.
Si está en False, puede cambiarse el texto mediante teclado.
MaxLenght
Indica, si se establece, la longitud máxima del texto. Si no se establece o si se pone valor 0,
permite cualquier longitud de texto.
Name Nombre(*)
PasswordChar
En ocasiones, es conveniente que no se pueda leer lo que se escribe en la caja de texto, caso
por ejemplo de la entrada de un password o palabra de paso. Esta propiedad nos permite
indicar un carácter que sustituye a cualquier carácter que tenga la caja de texto. (Típicos el *
o ?). El texto que tenga en la propiedad Text no cambia por el hecho de presentar en pantalla
un carácter distinto. Esta propiedad puede cambiarse en tiempo de ejecución. Para quitar el
PasswordChar basta con forzarlo al carácter nulo : Text1.PasswordChar = “”
ScrollBars
Cuando la propiedad Multiline de la caja de texto está a True, se pueden colocar barras de
desplazamiento del texto hacia arriba y abajo, o hacia los lados. Esto nos permite tener una
caja de texto de tamaño reducido y poder leer en ella un texto mayor que la propia caja. Esta
propiedad puede tomar los siguiente valores :
0 - No salen barras
1 - Barras de desplazamiento horizontal
2 - Barras de desplazamiento vertical
3 - Ambas barras.
FRAME RECUADRO
Para agrupar controles, en primer lugar trace el control Frame y, a continuación, meta los
controles dentro de Frame. De este modo podrá mover al mismo tiempo el Frame y los
controles que contiene. Si traza un control fuera del Frame y, a continuación, intenta moverlo
dentro de éste, el control se colocará sobre el Frame, pero no pertenecerá a el. Es decir, si es
un OptionButton este se comportará como si estuviese fuera del Frame, aunque físicamente
esté dentro de el.
Cuando un control Frame tiene dentro otros controles, y hacemos invisible al Frame, mediante
su propiedad Visible = False, los controles interiores al Frame quedan también invisibles. Los
controles Frame se utilizan para poner dentro de ellos diversos controles, que solo aparecerán
en el programa cuando se cumpla una determinada condición. Cuando esa condición se
cumpla, se pone la propiedad Visible del Frame a True y se verán todos los controles que tiene
dentro. Si no se cumple esa condición, se pone la propiedad Visible del Frame a False y no se
verá ni el Frame, ni los controles que contiene.
PROPIEDADES
Las señaladas con (*) no presentan novedades respecto a las ya
comentadas para los controles precedentes.
Enabled
Cuando esta propiedad está a False, tanto los procedimientos asociados al propio control
Frame como todos los controles dentro del Frame estarán inhabilitados. Si esta propiedad está
a True, todos ellos están habilitados.
Font (*)
ForeColor
Color de las letras del título del Frame.
Visible
Cuando un Frame está con la propiedad Visible = False, tanto el propio Frame como todos los
controles interiores a el serán invisibles.
Un control OptionButton muestra una opción que se puede activar o desactivar, pero con
dependencia del estado de otros controles OptionButton que existan en el formulario.
En el formulario también existen tres CheckBox, que como puede verse, se pueden
seleccionar los que se desee, sin ningún tipo de exclusión entre ellos.
PROPIEDADES
Las señaladas con (*) son comunes a ambos controles y no presentan novedades respecto a
las ya comentadas para los controles precedentes.
0 - Left Justify
1 - Right Justify
Cuando se elige el valor 0, justificado a la izquierda, el título del control aparece a la derecha
del botón, pegado a la figura del botón. Cuando se elige el valor 1, justificado a la derecha, el
título (Caption) aparece a la izquierda del botón, comenzando en la parte izquierda del cuerpo
total del control, es decir, no tiene porqué aparecer con su parte derecha pegado al botón, caso
que el cuerpo total del control se haya hecho mas largo que la palabra del título.
Atención. Presenta una diferencia entre uno y otro control respecto a la forma de expresarse
respecto a su valor cuando está seleccionado. Para forzar que el control NO esté seleccionado,
o para leer el Value cuando no está seleccionado, podemos utilizar tanto Value = 0 como
Value = False. Sin embargo, cuando lo que se quiere es poner la propiedad a True hay una
diferencia entre ambos controles.
Para el OptionButton podemos utilizar indistintamente las siguiente formas :
El poner como Value el valor True nos dará en este control un error.
Para la lectura del Value, existe una diferencia entre el valor devuelto por el CheckBox y el
devuelto por el OptionButton.
PROCEDIMIENTOS
Un control ListBox muestra una lista de elementos en la que el usuario puede seleccionar uno
o más. Si el número de elementos supera el número que puede mostrarse, se agregará
automáticamente una barra de desplazamiento al control ListBox.
La lista tiene varios elementos. Cada línea de esta lísta es un elemento de la lista. Como el
número de elementos de la lista tiene mas elementos de los que le podían caber, generó
automáticamente la barra de desplazamiento vertical.
El ComboBox está normalmente sin desplegar. Se despliega cuando se hace click con el ratón
en la flecha que tiene en su parte derecha (véase fig. Anterior). Al desplegarse, muestra la lista
con todos sus elementos. Haciendo click con el ratón en cualquiera de sus elementos, el
elemento elegido pasa a la parte TextBox del Combo y la lista vuelve a replegar.
El ListBox (y por tanto el ComboBox) tiene unas propiedades y métodos particulares que
solamente se pueden aplicar durante el tiempo de ejecución :
Propiedades
ListCount - Indica el número de elementos que tiene la lista
Para seleccionar un elemento de la lista, basta con hacer click con el ratón sobre él. Ese
elemento se resaltará con fondo en azul. Una vez seleccionado un elemento, la propiedad
ListIndex tomará el valor del número de orden que ocupa ese elemento en la lista,
comenzando por el 0 para el elemento que ocupa el primer lugar. Si no se selecciona ningún
elemento, el valor de la propiedad ListIndex será -1. El primer elemento de la lista es
ListIndex 0, y el valor de la propiedad ListCount siempre es uno más que el valor mayor de
ListIndex.
En el ComboBox la propiedad Text contiene el texto que contenga la parte TextBox del
Combo, bien haya sido introducida desde teclado o mediante la recuperación de un elemento la
parte ListBox del mismo.
Ejemplos
Variable = List1.Listcount
Variable contendrá un número con el número total de elementos de la lista
List1.
Variable = List1.ListIndex
Variable contendrá un número con el número de orden del elemento de la lista
seleccionado en ese momento.
Variable = “VISUALBASIC”
List1.AddItem Variable
Añade un elemento a List1. En este caso, el elemento añadido es la palabra
VISUALBASIC.
Variable = List1.Text
Variable contendrá el elemento que estaba seleccionado en List1. (Variable
será una cadena de caracteres)
List1.RemoveItem (n)
Elimina el elemento n de List1.
List1.RemoveItem (List1.ListIndex)
Elimina el elemento que estaba seleccionado en ese momento.
List1.listIndex = n
Selecciona el elemento n de List1 (Se resalta en azul el elemento n)
Las señaladas con (*) son comunes a ambos controles y no presentan novedades respecto a
las ya comentadas para los controles precedentes.
Determina si los elementos se presentan en una sola columna o en varias columnas, y la forma
de presentar los elementos en el ListBox. Si esta propiedad se pone a 0 la lista tendrá
solamente una columna, y presentará los elementos uno debajo de otro. Cuando los elementos
sean mas que los que se pueden presentar en la lista, aparecen automáticamente unas barras
de desplazamiento vertical.
Si la propiedad se pone a un número distinto a 0, el ListBox es de tipo multicolumna,
presentará en cada columna los elementos que le quepan dependiendo de su dimensión
vertical, y tendrá tantas columnas como sea necesario para albergar el número de elementos
que tenga. Presentará en su cuadro tantas columnas como se le indique en el valor de esta
propiedad, y si no puede presentar todos los elementos en las columnas que muestra, le
aparecerán barras de desplazamiento horizontales para poder movernos por todas las
columnas que tenga.
Recuerde : El número que se le asigna a la propiedad Columns no indica el número de
columnas que tendrá el ListBox sino el número de columnas que presenta.
Nombrelista.Columns = número
.
Esta propiedad no puede definirse a 0 o cambiada desde 0 en tiempo de ejecuciones decir, no
se puede convertir en tiempo de ejecución un ListBox de múltiples columnas en un ListBox de
columna única o un ListBox de columna única en un ListBox de múltiples columnas. Sin
embargo, sí es posible cambiar durante la ejecución el número de columnas de un ListBox de
múltiples columnas.
DataField DataSource .
Establecen el control Data asociado y el campo donde están los datos que se llevarán al
TextBox o ComboBox para presentar datos procedentes de una Base de Datos.
La propiedad ItemData es una matriz de valores enteros largos cuyo número de elementos es
el valor de la propiedad ListCount del control. Los números asociados con cada elemento se
pueden usar para cualquier fin. Por ejemplo, se puede usar en la confección de una lista de
teléfonos, el número de identificación de un empleado, etc. Cuando se rellena el ListBox,
también se rellena los elementos correspondientes de la matriz ItemData con los números
correspondientes.
La propiedad ItemData se usa a menudo como índice de una matriz de estructuras de datos
asociados con los elementos de un control ListBox.
Nota Cuando se inserta un elemento en una lista con el método AddItem, el elemento también
se inserta automáticamente en la matriz ItemData. Sin embargo, el valor no se reinicializa a
cero; retiene el valor que estaba en esa posición antes agregar el elemento a la lista. Cuando
se usa la propiedad ItemData, asegúrese de establecer su valor al agregar nuevos elementos a
la lista.
Left (*)
Sorted
Cuando esta propiedad tiene el valor True, Visual Basic se encarga de casi todo el
procesamiento de cadenas necesario para mantener el orden alfabético, incluyendo el
cambio de los números de índice cuando se agregan o eliminan elementos.
Nota El uso del método AddItem para agregar un elemento en una posición
específica de la lista puede romper el orden alfabético, y los elementos agregados con
posterioridad pueden no ordenarse correctamente.
PROCEDIMIENTOS
Mediante estos controles se pueden introducir datos variando la posición del cursor.
Las señaladas con (*) son comunes a ambos controles y no presentan novedades respecto a
las ya comentadas para los controles precedentes.
LargeChange
Esta propiedad establece la variación de la propiedad Value cada vez que se hace click en el
interior de la barra de desplazamiento, en la parte por donde pasa el cursor.
Left (*)
Max
Esta propiedad establece el valor máximo para la propiedad Value, es decir, el valor de esta
propiedad cuando el cursor está en su parte máxima. (Recuerde que el cursor está en el
máximo, cuando está mas a la derecha, caso del HScrollBar, o cuando está en la parte mas
baja, caso del HScrollBar.
Min
Esta propiedad establece el valor mínimo para la propiedad Value, es decir, el valor de esta
propiedad cuando el cursor está en su parte mínima. (Recuerde que el cursor está en el
mínimo, cuando está mas a la izquierda, caso del HScrollBar, o cuando está en la parte mas
alta, caso del HScrollBar.
Value
Esta propiedad lee o establece el valor dado por la posición del cursor. Este valor tiene un
mínimo, establecido por Min y un máximo, establecido por Max. Esta propiedad es la que se
debe leer para conocer la posición del cursor.
TIMER TEMPORIZADOR
Este objeto permite establecer temporizaciones. Presenta una novedad respecto a los
controles estudiados hasta ahora. El control Timer solamente se ve durante el tiempo de
diseño. En tiempo de ejecución, el control permanece invisible.
PROPIEDADES
Interval
El valor de esta propiedad nos dará el intervalo de tiempo (en milisegundos) en que se
producirá un evento Timer y consecuentemente, realizará el procedimiento asociado a este
evento. Si el valor de la propiedad Interval está a 0 (Predeterminado), no se produce el evento
Timer. (El control Timer está deshabilitado cuando la propiedad Interval = 0)
Timer
CONTROL SHAPE
Shape es un control gráfico que se muestra como un rectángulo, un cuadrado, una elipse, un
círculo, un rectángulo redondeado o un cuadrado redondeado.
Un control Shape no actúa como contenedor de controles. (Esto quiere decir que un control
Shape nunca le servirá, por ejemplo, para albergar varios OptionButton y pretender que sean
independientes de otros controles OptionButton que se encuentren fuera del control Shape.
PROPIEDADES
Backcolor (*)
BackStyle
Esta propiedad establece si Shape permite ver a su través (Transparent) o n lo permite
(Opaque)
BorderColor
Establece el color del borde.
BorderStyle
Establece el tipo de borde. Puede ser : Transparent, (No se ve el borde), Solid, (Borde de línea
continua),Dash, (línea a rayas), Dot, (línea a puntos), Dash-Dot, (línea de raya - punto), dash-
Dot-Dot, (línea de raya - punto - punto), InsideSolid, (raya continua)
BorderWidth
Establece el ancho de la línea.
Shape
Establece la forma del control. Puede ser : Rectangular, cuadrado, redondo, ovalado, cuadrado
con esquinas redondeadas y rectangular con esquinas redondeadas.
PROCEDIMIENTOS No tiene.
CONTROL LINE
Line, al igual que Shape, es un control gráfico que solamente sirve para poner una línea en un
formulario. Del mismo modo, no tiene procedimientos, por lo que no sirve para aportar código
al programa. Solo sirve para aportar una característica gráfica, es un adorno.
PROPIEDADES
BorderColor
Establece el color de la línea.
BorderStyle
Establece el tipo de línea : Puede ser : Transparent, (No se ve la línea), Solid, (Línea
continua),Dash, (línea a rayas), Dot, (línea a puntos), Dash-Dot, (línea de raya - punto), dash-
Dot-Dot, (línea de raya - punto - punto), InsideSolid, (raya continua)
BorderWidth
Establece el ancho de la línea.
PROCEDIMIENTOS No tiene.
AÑADIR CONTROLES A LA CAJA DE HERRAMIENTAS
Los controles descritos hasta ahora son parte de los que aparecen en la caja de herramientas
mas elemental de Visual Basic. Estos, otros que se estudiarán en el capítulo 4 (Controles
para mostrar imágenes), el control Data y el contenedor OLE que se estudiarán en la
segunda parte del curso, son los que están incluidos en la caja de herramientas mas
elemental. La razón es que estos controles vienen como lo más elemental de Visual Basic,
como lo que es imprescindible para que funcione una aplicación realizada en VB. La
pregunta es ahora, ¿qué es lo más elemental de VB?
Son tres DLLs, Visual Basic For Aplications (MSVBVM60.DLL), Visual Basic Runtime Objets And
Procedures (MSVBVM60.DLL\3) y Visual Basic Objets And Procedures (VB6.OLB). Una aplicación,
por muy elemental que sea, necesita esos tres ficheros. Pero posiblemente no haya ninguna aplicación
práctica que se pueda realizar con esos controles. Hay más. Lo que ocurre es que hay que introducirlos
en la caja de herramientas. Para ello, basta con desplegar el menú de Proyecto de la barra de menú:
Verá a lo largo del curso que Visual Basic utiliza mucho las colecciones. Una de ellas es la colección
Controls, que es el conjunto de todos los controles existentes en un formulario.
Visual Basic trata a las colecciones como un objeto más. Así, las colecciones tienen sus
propiedades. No vamos a entrar en demasiados detalles de momento, pero una de las
propiedades de todas las colecciones en Count, que es precisamente el número de
elementos que componen esa colección.
La numeración de los elementos de una colección siempre va del 0 al n-1, siendo n el número
de elementos de esa colección.
Vamos a ver una expresión que nos puede ayudar mucho. Sirve para saber a que
tipo de control pertenece un determinado elemento. Esa expresión es TypeOf y
siempre se debe usar con una sentencia condicional If y aplicarla solamente sobre
una variable tipo objeto. (No se preocupe al leer esto, es muy fácil) (No se puede
utilizar con Select Case)
Una variable tipo objeto es una variable que no contiene un dato numérico o string. Contiene un objeto.
Por ejemplo, un control.
For Each Pepe in Controls ‘ Para cada valor que tome Pepe haciedole recorrer todos
‘ los elementos de la colección Controls
If TypeOf Pepe Is TextBox Then ' Si pepe es un TextBox ….
Pepe.Locked = True ' lo bloquea
End If
Next Pepe
Puede usar este código para bloquear todos los TextBox de un formulario. Se mete en un
procedimiento llamado Bloquear (p.e.) y cada vez que queramos bloquear todos los
TextBox del formulario basta con citar a este procedimiento. Un código similar sería
Dim I As Integer
For I = 0 To Controls.Count - 1
If TypeOf Controls(I) Is TextBox Then
Controls(I).Locked = True
End If
Next
Caption Título
Es el texto del control o lo que figura en la barra de menú del formulario. Puede cambiarse en
tiempo de ejecución.
0 Manual
1 Automático
Enabled Activado
Establece el modo de rellenar controles Shape, o figuras (círculos o cuadrados) creados con
los métodos gráficos Circle y Line.
Valores: 0 - Continuo
1 - Transparente
2 - Línea Horizontal
3 - Línea Vertical
4 - Diagonal hacia arriba
5 - Diagonal hacia abajo
6 - Cruzado
7 - Diagonal cruzada
Font Fuente
Es el tipo de letra para el título o el texto del control. Puede cambiarse en tiempo de ejecución.
Height Altura
Establece un número de contexto asociado para este formulario. Este número se aplica para
determinar la ayuda interactiva asociada a este formulario o control. Vea mas adelante, el tema
Ayuda de Windows.
En el caso de que se tengan varios controles que realicen una función similar (Las teclas
numéricas de una calculadora, p.e.) puede hacerse un array con estos controles. Todos
tendrán el mismo nombre, y se diferencian por un índice. La propiedad Index de ese control
toma el número de ese índice.
Indica la posición del control, concretamente de la parte izquierda del mismo. Normalmente
esta propiedad no se introduce numéricamente, sino que la toma automáticamente de la
posición que se le de al control en tiempo de diseño. Puede cambiarse en tiempo de ejecución.
Establece un icono personalizado para el puntero del ratón cuando esté encima del Formulario
o control. Este icono puede ser un Bitmap de los existentes en el directorio Icons de Visual
Basic o cualquiera que tengamos. Si se pone 99 como valor de la propiedad MousePointer
(siguiente), cada vez que el puntero del ratón pase por este Formulario o control, cambiará su
forma y adoptará la del icono elegido.
Determina la forma del puntero del ratón cuando se coloca encima del formulario o control.
Puede elegirse uno de los punteros preestablecidos (15 en total) o el personalizado visto en la
propiedad anterior. Para elegir ese icono personalizado, debemos poner en esta propiedad el
valor 99.
Cuando disponemos de varios controles en un mismo formulario, solamente uno de ellos tiene
el foco. Esta expresión de tener el foco significa que ese control está remarcado y en esa
condición, si pulsamos la tecla ENTER haría el mismo efecto que hacer click con el ratón en
ese control, o, en el caso de un TextBox, las letras pulsadas en el teclado van a ese TextBox.
Propiedad Booleana. Cuando esta propiedad está a False, el botón no tomará el foco cuando
se pulse la tecla del Tabulador. Sin embargo sigue manteniendo el índice de la propiedad
TabIndex descrita anteriormente. Puede cambiarse en tiempo de ejecución. Esto nos permite
descartar algún botón de tomar el foco, cuando por facilidad para el usuario, sea conveniente
en determinados puntos del programa.
Indica la coordenada de la parte superior del control. Puede variarse durante el tiempo de
ejecución. Esta propiedad, juntamente con Left definen la esquina superior izquierda del botón
de comando. Normalmente esta propiedad no se introduce numéricamente, sino que la toma
automáticamente de la posición que se le den al botón en tiempo de diseño. Puede cambiarse,
cambiando el valor a esta propiedad, en tiempo de ejecución.
Visible
Devuelve o establece un número de contexto asociado a un objeto. Se utiliza para dotar a las
aplicaciones de Ayuda interactiva con el menú emergente ¿Qué es esto? de la Ayuda de W 95.
Width Ancho
Define la anchura del formulario o control. Normalmente no se introduce como valor numérico
sino que lo toma automáticamente del tamaño que tenga el formulario o control durante el
tiempo de diseño. Juntamente con Height define el tamaño del objeto. Puede cambiarse
durante el tiempo de ejecución
Lo tienen casi todos los controles y el formulario. Es el evento habitual para introducir el código
que queremos que realice el programa al hacer click sobre un determinado control. No es el
LSB Visual Basic – Guía del Estudiante Capítulo 1 Página 86
único evento donde podemos hacerlo, pues también tenemos otros muy parecidos
(MouseDown, MouseUp), pero que funcionan de distinta forma. El evento Click en el
formulario se efectúa cada vez que pulsamos uno de los botones del ratón. En los controles,
sólo con el botón izquierdo.
Evento KeyDown Pulsar una tecla (Ocurre cuando el control tiene el foco)
Se produce sobre el control que tiene el foco al pulsar una tecla, justo en el instante que llega a
su posición inferior. Pasa como parámetros el KeyCode As Integer y el Shift As Integer. El
KeyCode es el código de la tecla (no confundir con Keyascii, que es el código de la letra. Hay
teclas que no tienen Keyascii y sí tienen KeyCode F1, F2, ...Insert, ) Shift es la posición de la
tecla Mayúsculas (1=pulsada, 0=levantada)
Evento KeyPress Pulsar una tecla (Ocurre cuando el control tiene el foco)
Este evento se produce después del KeyDown, y pasa como parámetro el KeyAscii As Integer,
que es el código Ascii de la letra cuya tecla se ha pulsado.
Evento KeyUp Levantar la tecla pulsada (Ocurre cuando el control tiene el foco)
Ocurre al levantar la tecla, después del KeyPress. Pasa como parámetros los mismos que
KeyDown, KeyCode As Integer, Shift As Integer
LostFocus
Ocurre cuando el control pierde el foco. Este evento es muy importante, por ejemplo en un Text Box,
para dar el formato adecuado al texto contenido en ese Text Box, o para comprobar que es correcto, etc.
Se ejecuta en el instante de apretar el botón de mouse, cuando está el puntero encima del
formulario o control correspondiente. Pasa como parámetros el botón pulsado (1= izquierdo, 2
= derecho, 3 = centro), la tecla mayúsculas (Shift=1 significa pulsada, =0 no pulsada) y las
coordenadas X e Y del puntero del ratón.
Se ejecuta cuando pasa por encima del formulario o0 control el puntero del ratón
Existen mas eventos. No se van a mencionar aquí todos, pues se irán viendo según avance el
curso.
Y no se preocupe si este capitulo le ha resultado muy pesado. Les pasa a todos. El próximo va
a ser mas divertido.
El CommonDialog es un control del que se libran muy pocas aplicaciones. Dada la importancia
de este control, se le dedicaba en versiones anteriores de la Guía del Estudiante un capitulo
LSB Visual Basic – Guía del Estudiante Capítulo 1 Página 88
único. En esta edición se le a anexado el estudio sobre los controles para la búsqueda de
ficheros. El conocimiento de este capítulo es fundamental si desea llegar a ser un buen
programador de VB.
Este control no se presenta en tiempo de diseño mas que con un simple icono :
• Abrir Ficheros
• Guardar Ficheros
• Elegir colores
• Seleccionar Impresora
• Seleccionar Fuentes
CommonDialog mostrando la función Abrir. Nótese el icono para crear nueva carpeta, objeto
del comentario siguiente.
En realidad el cuadro de diálogo permite conocer datos con los cuales, y mediante el código
adecuado, abriremos o guardaremos ficheros, elegiremos colores o seleccionaremos fuentes.
Es decir, el CommonDialog NO realiza mas funciones que mostrar ficheros existentes, fuentes
disponibles, colores, para que, mediante código, abramos esos ficheros o usemos una
Dependiendo de la aplicación para la que vaya a usarse se deberá activar de distintas formas.
Si el cuadro de diálogo se va a usar para seleccionar la impresora y para otras
aplicaciones, es recomendable usar uno exclusivamente para seleccionar la impresora.
Esta última recomendación se debe a que, para el control de la impresora, el CommonDialog
SI realiza las funciones de selección de impresora predeterminada. Esta diferencia operativa
hace que si usamos el mismo CommonDialog para seleccionar impresora y abrir ficheros, por
ejemplo, se “cuelgue” el CommonDialog.
Analizando las propiedades, vemos que tiene gran cantidad de ellas. Esto es porque agrupa
todas las propiedades correspondientes a la cinco funciones que puede desarrollar. Se detalla
a continuación la forma de usar el CommonDialog para cada aplicación.
Se entiende que el nombre - Name - que se ha puesto para el cuadro de diálogo en todos los
ejemplos es CD1)
Para mostrar el cuadro de diálogo correspondiente a Tipos de Letra ( Fonts ), debe ejecutarse
la instrucción:
CD1.ShowFont
CD1.ShowColor
El cuadro de diálogo puede tener una de estas dos formas (Dependiendo de su propiedad
Flags):
(Si la parte derecha del cuadro no sale, se saca haciendo click en Definir colores
personalizados.)
Variablecolor = CD1.Color
Para establecer un color por medio de código, se pondrá un valor de color a la propiedad
correspondiente de ese objeto. El valor del color puede establecerse de tres formas que se
comentan a continuación.
Visual Basic acepta para especificar un color, tres procedimientos: Mediante el número de
color, mediante la sentencia RGB ( rojo, verde, azul ) o mediante la función QBColor.
Por número
El número que representa el color en VB está formado por la suma de la componente roja, la
componente verde y la componente azul. Podríamos verlo muy bien en numeración
Hexadecimal:
Color = Hex XX YY ZZ
Donde ZZ es un número Hexadecimal que representa la cantidad del color rojo. El mínimo
estaría en 0 (H00) y el máximo en 255 (HFF)
YY representaría la cantidad de color verde y XX la de color azul, ambos con los mismos
límites explicados para el rojo.
Una mezcla de un poco de rojo (HB1), otro poco de verde (H56) y otro poco de azul (H1F)
daría el siguiente número:
Hex(1F56B1) = 2053809
Label1.Backcolor = 12345678
Se puede expresar el color, poniendo simplemente RGB (rojo, verde, azul), donde rojo es un
número entre 0 y 255 que indica la cantidad de color rojo que se aporta al color, verde un
número comprendido entre 0 y 255 indicando la cantidad de verde, y lo mismo para azul.
Esta es la forma mas sencilla de poner la propiedad color, y con la que mejor controlaremos el
mismo.
Esta función se ha puesto en Visual Basic para compatibilidad con los colores que se usan en
Quick-Basic y Qbasic. Mediante esta función se obtienen solamente 16 colores.
Sintaxis Objeto.QBColor(color)
0 Negro 8 Gris
1 Azul 9 Azul claro
2 Verde 10 Verde claro
3 Aguamarina 11 Aguamarina claro
4 Rojo 12 Rojo claro
5 Fucsia 13 Fucsia claro
6 Amarillo 14 Amarillo claro
7 Blanco 15 Blanco brillante
CD2.ShowPrinter
(A este cuadro de dialogo le hemos llamado CD2 para evitar los problemas referidos en la
nota).
Nombredelaimpresora = Printer.DeviceName
Para que la impresora quede como impresora por defecto de Windows, debe tener a True la
propiedad PrinterDefault del cuadro de diálogo.
Posiblemente las opciones mas usadas del cuadro de diálogo. Para presentar el cuadro de
diálogo correspondiente a Abrir Archivo, debe ejecutar la instrucción:
CD1.ShowOpen
Si lo que necesita es abrir el cuadro de diálogo para guardar un fichero, debe ejecutar la
instrucción :
CD1.ShowSave
pero antes deberá introducir el filtro o filtros de archivos que necesite. Ojo, ¡ ANTES !
Un filtro sirve para que en el cuadro de diálogo se presenten solamente los ficheros de nuestro
interés. Se pueden seleccionar por el nombre, la extensión o las dos cosas. La sintaxis para
introducir un filtro es la siguiente:
donde "nombre" puede ser un nombre o usar caracteres comodín. Lo mismo para "extensión"
Por ejemplo, para buscar archivos ejecutables, con cualquier nombre y extensión .EXE
Puede buscar varios tipos de ficheros, separando los caracteres de busca por medio del
carácter punto y coma (;)
Puede también introducir dos filtros, separados por el separador | (En una línea solamente).
En este caso, cuando se presente el cuadro de diálogo, solamente presentará uno de los
filtros, y deberá seleccionar manualmente el otro en un cuadro situado en la parte inferior
izquierda del cuadro de diálogo. Para predeterminar cuál de los dos filtros saldrá sin tener que
seleccionarlo, deberá ejecutar esta instrucción, ANTES de invocar el cuadro de diálogo
CD1.FilterIndex = 2
En este caso, aparecerá por defecto el filtro escrito en segundo lugar. El orden de los filtros
comienza por 1.
El cuadro de diálogo deberá presentar un directorio. Este directorio con el que, por defecto,
aparece el cuadro de diálogo, se puede introducir mediante la propiedad InitDir, que deberá
contener el nombre del directorio y su path. Si no se especifica, comenzará por el directorio
actual.
Nombrefichero= CD1.filename
El nombre del fichero, Nombrefichero en la sentencia anterior, viene con su path, es decir,
nombre y dirección completa, por lo que puede emplearse directamente la salida del
CommonDialog para abrir un fichero.
El cuadro de guardar es similar. Observe que en este caso hemos desplegado la segunda
opción de filtro de ficheros :
Mediante el CommonDialog se puede presentar un fichero de ayuda. (Ficheros del tipo .HLP
de Windows)
Para mostrar el fichero de ayuda debe poner el nombre (y Path) del fichero de ayuda en la
propiedad HelpFile del CommonDialog
CD1.HelpFile = "C:\MiCarpeta\MiFicheroAyuda.HLP"
Puede mostrar el índice, o una página concreta de ese fichero. Para ello debe jugar con la
propiedad HelpCommand, que le va a indicar al CommonDialog qué ayuda debe mostrar.
CD1.HelpCommand =Valor
1 &H1 cdlContext
Muestra la Ayuda de un contexto determinado. Cuando se usa esta valor, también se
debe especificar un contexto con la propiedad HelpContext.
2 &H2 cdlQuit
Notifica a la aplicación Ayuda que el archivo de Ayuda especificado ya no se está
utilizando.
3 &H3 cdlContents
Muestra el tema de contenido de ayuda, definido con la opción Contents de la sección
[OPTION] del archivo .HPJ.
3 &H3 cdlIndex
Muestra el índice del archivo de Ayuda especificado. Las aplicaciones sólo deben
utilizar este valor para un archivo de Ayuda con índice único.
4 &H4 cdlHelpOnHelp
Muestra la Ayuda para utilizar la propia aplicación Ayuda.
5 &H5 cdlSetContents
CD1.HelpCommand = cdlHelpIndex
Para mostrar la página que se ha puesto en el fichero de ayuda como Contexto 2 (En Sección
[MAP] del fichero .HPJ (Vea Creación de Ficheros de Ayuda),
CD1.HelpCommand = cdlHelpContext
CD1.HelpContext = 2
CD1.HelpCommand = cdlHelpHelpOnHelp
Una vez establecidas estas propiedades, mostraremos la ayuda con el Método ShowHelp
CD1.ShowHelp
Repitiéndole que no se debe usar el CommonDialog para mostrar ayudas, pero para cumplir el
objetivo de plasmar en este texto la mayor información acerca de cada control, se enumeran a
continuación el resto de propiedades que afectan a la presentación de ayuda :
HelpFile
HelpContext
HelpKey
La Propiedad Action
Valor Descripción
0 Ninguna acción.
1 Muestra el cuadro de diálogo Abrir.
2 Muestra el cuadro de diálogo Guardar como.
3 Muestra el cuadro de diálogo Color.
4 Muestra el cuadro de diálogo Fuente.
5 Muestra el cuadro de diálogo Impresora.
6 Ejecuta WINHELP.EXE.
CancelError
Devuelve o establece un valor que indica si se genera un error cuando el usuario elige
el botón Cancelar.
Debe poner esta propiedad a true cuando quiera detectar que se ha pulsado el botón
CANCELAR. Luego, debe detectar con la rutina de detección de errores adecuada, si
el error producido ha sido el 32755. De ser así, es que han pulsado ese botón, y Vd.
procederá en consecuencia.
Color
Esta propiedad es de lectura / escritura
De escritura :Establece el color predeterminado con que aparecerá el CD para elegir
color. Puede introducirse como RGB o numéricamente, como se explicó mas atrás.
Para que aparezca este color predeterminado, debe poner la propiedad Flags a 1.
De lectura : Entrega el color elegido, bien en el cuadro de elección de color, bien en el
cuadro de elección de fuente.
Copies
Establece el valor predeterminado para número de copias que aparecerá en el CD
cuando se abra para elegir impresora.
DefaultExt
Devuelve o establece la extensión de archivo predeterminada para el cuadro de
diálogo.
DialogTitle
Devuelve o establece la cadena mostrada en la barra de título del cuadro de diálogo.
FileName
Devuelve o establece la ruta y el nombre de archivo de un archivo seleccionado. La
lectura de esta propiedad devuelve el nombre del archivo seleccionado actualmente en
la lista. Si no hay ningún archivo seleccionado, FileName devuelve una cadena vacía.
Filter
(Explicada mas atrás)
Devuelve o establece los filtros que aparecen en el cuadro de lista Tipo de un cuadro
de diálogo.
Nota para los que no tiene teclado de 102 teclas. Para obtener el carácter ASCII 124, pulse la
tecla ALT y, sin dejar de pulsarla, teclee 124 en el teclado numérico.
FilterIndex
Devuelve o establece un filtro predeterminado para un cuadro de diálogo Abrir o
Guardar Como.
Esta propiedad indica el filtro predeterminado cuando se han especificado varios filtros
para un cuadro de diálogo Abrir o Guardar. El índice del primer filtro definido es 1.
Flags Propiedad que tiene distintos significados para cada tipo de cuadro de diálogo.
Tiene la misma sintaxis para todos los cuadros.
1 &H1& cdlRGBInit
Establece como valor de color inicial para el cuadro de diálogo el indicado en su
propiedad Color.
2 &H2& cdlFullOpen
Hace que el cuadro de diálogo se muestre completo al crearlo, incluyendo la sección
que permite al usuario crear colores personalizados. Sin esta opción, el usuario debe
elegir el botón de comando Definir colores personalizados para mostrar tal sección.
4 &H4& cdlPreventFullOpen
Desactiva el botón de comando Definir colores personalizados, evitando que el
usuario defina colores.
8 &H8& cdlShowHelp
Hace que el cuadro de diálogo muestre un botón Ayuda.
Se pueden poner varias de estas condiciones, poniendo como valor Flags la suma de los
valores de cada una de las condiciones a poner.
1 &H1& cdlReadOnly
Hace que la casilla de verificación Sólo lectura aparezca marcada inicialmente al crear
el cuadro de diálogo. Este indicador también señala el estado de la casilla Sólo lectura
cuando el cuadro de diálogo está cerrado.
2 &H2& cdlOverwritePrompt
Hace que el cuadro de diálogo Guardar como genere un cuadro de mensaje si el
archivo seleccionado ya existe. El usuario deberá confirmar que desea sobrescribir el
archivo.
4 &H4& cdlHideReadOnly
Oculta la casilla de verificación Sólo lectura.
8 &H8& cdlNoChangeDir
Hace que el cuadro de diálogo restablezca como directorio actual el que lo era en el
momento de abrirse.
16 &H10& cdlShowHelp
Hace que el cuadro de diálogo muestre el botón Ayuda.
256 &H100& cdlNoValidate
Especifica que el cuadro de diálogo común permita caracteres no válidos en el nombre
de archivo devuelto. Funciona tanto con el cuadro de Abrir como con el de Cerrar.
Tenga cuidado a la hora de crear un archivo con nombre no válido !
512 &H200& cdlAllowMultiselect
Especifica que el cuadro de lista Nombre de archivo admita selecciones múltiples. El
usuario puede seleccionar más de un archivo en tiempo de ejecución presionando la
tecla MAYÚS y utilizando las teclas FLECHA ARRIBA y FLECHA ABAJO para
seleccionar los archivos deseados. Al hacerlo, la propiedad FileName devolverá una
cadena con los nombres de todos los archivos seleccionados. Los nombres están
delimitados en la cadena con espacios.
Se pueden poner varias de estas condiciones, poniendo como valor Flags la suma de los
valores de cada una de las condiciones a poner.
1 &H1& cdlScreenFonts
Hace que el cuadro de diálogo muestre solamente las fuentes de pantalla que admite
el sistema.
2 &H2& cdlPrinterFonts
Hace que el cuadro de diálogo muestre solamente las fuentes que admite la impresora,
especificadas por la propiedad hDC.
3 &H3& cdlBoth
Hace que el cuadro de diálogo muestre las fuentes de impresora y de pantalla
disponibles. La propiedad hDC identifica el contexto de dispositivo asociado a la
impresora
4 &H4& cdlShowHelp
Hace que el cuadro de diálogo muestre un botón Ayuda.
256 &H100& cdlEffects
Especifica que el cuadro de diálogo permita efectos de tachado, subrayado y color.
1024 &H400& cdlANSIOnly
Especifica que el cuadro de diálogo sólo permita seleccionar las fuentes que utilizan el
juego de caracteres de Windows. Si se establece este indicador, el usuario no podrá
seleccionar una fuente que sólo contenga símbolos.
2048 &H800& cdlNoVectorFonts
Especifica que el cuadro de diálogo no permita seleccionar fuentes vectoriales.
Se pueden poner varias de estas condiciones, poniendo como valor Flags la suma de los
valores de cada una de las condiciones a poner.
0 &H0& cdlAllPages
Devuelve o establece el estado del botón de opción Todas las páginas.
1 &H1& cdlSelection
Devuelve o establece el estado del botón de opción Selección. Si no se
especifica cdlPageNums ni cdlSelection, el botón de opción Todas estará
seleccionado.
2 &H2& cdlPageNums
Devuelve o establece el estado del botón de opción Páginas.
4 &H4& cdlNoSelection
Desactiva el botón de opción Selección.
8 &H8& cdlNoPageNums
Desactiva el botón de opción Páginas y el control de edición asociado.
16 &H10& cdlCollate
Devuelve o establece el estado de la casilla de verificación Intercalar.
32 &H20& cdlPrintToFile
Devuelve o establece el estado de la casilla de verificación Imprimir a un
archivo.
Las seis propiedades siguientes nos dan las características especiales de la fuente elegida
(negrita, cursiva, etc.). Una vez seleccionado el tipo de letra, el valor de estas propiedades
contiene la opción elegida para la letra (si FontBold=True es que hemos elegido negrita, etc.)
FontBold
Propiedad Booleana que establece o devuelve que el tipo de letra usado es Negrita.
FontItalic
Propiedad Booleana que establece o devuelve que el tipo de letra usado es Cursiva.
FontUnderline
Propiedad Booleana que establece o devuelve que el tipo de letra usado es Subrayado.
FontName
(Variable toma el valor del nombre de la fuente. Por ejemplo, Variable = “Arial”
Nota En tiempo de ejecución puede obtener información sobre las fuentes disponibles
a través de las propiedades FontCount y Fonts. Lo verá mas adelante
FontSize
Devuelve de la fuente elegida. Debe ser una variable tipo Integer o Long
donde Variable será una expresión numérica que especifica el tamaño de fuente a
utilizar, en puntos.
InitDir
Devuelve o establece el directorio de archivos inicial.
Left y Top
Esto no ocurre para sistemas a 32 bytes, en los que el CD aparece siempre en la parte
superior izquierda de la pantalla, independientemente de donde esté el formulario que
lo contiene.
MaxFileSize
Devuelve o establece el tamaño máximo del nombre de archivo abierto con el control
CommonDialog.
Name
Nombre que define al CommonDialog
PrinterDefault
Utilice esta propiedad, que por defecto está a True, para poner o no poner como
impresora predeterminada, la impresora seleccionada con el CommonDialog. Puede
por ejemplo, querer imprimir un texto en una impresora determinada, pero solo ese
texto, y no quiere cambiar la impresora predeterminada por el hecho de haber
seleccionado en este momento otra impresora. Ponga esta propiedad a False
(recuerde que por defecto está a True) para que la impresora seleccionada no se
quede como impresora por defecto.
Tag
Tal como indica su nombre, estos controles son listas. Repase el control ListBox para recordar
sus propiedades.
DriveListBox
Este control permite elegir una unidad de disco válida en tiempo de ejecución. Utilice este
control para mostrar una lista de todas las unidades válidas del sistema de un usuario. Puede
crear cuadros de diálogo que permitan al usuario abrir un archivo de una lista en cualquier
unidad disponible.
Posee las propiedades típicas de cualquier control VB respecto a su geometría y color. Las
propiedades mas destacables de este control son : Drive, List, ListCount y ListIndex
Este control se trata de una lista como su nombre indica, y posee las propiedades List,
ListCount y ListIndex para permitir al usuario tener acceso a elementos de la lista.
ListCount proporciona información acerca del número de unidades de disco disponibles (En el
ejemplo, List1.ListCount será 3, que corresponde a los tres discos disponibles - disco C :,
disco A : y disco D : )
ListIndex devuelve el índice del disco seleccionado en la lista (0 para el primero, n-1 para el
último)
DirListBox.
Aparte de las propiedades geométricas de todos los controles, el DirListBox presentas las
siguientes propiedades particulares :
Path
Devuelve o establece la ruta de acceso actual. No está disponible en tiempo de diseño.
donde
El valor de la propiedad Path es una cadena que indica una ruta de acceso, como
C:\WINDOWS\SYSTEM. El valor predeterminado es la ruta de acceso actual
Nota El valor que devuelve Path es distinto del de List (ListIndex). Path devuelve siempre un
mismo camino, bien el que se le haya impuesto mediante esta misma propiedad Path, y si no
se le ha impuesto con anterioridad, el que tenga por defecto. List (ListIndex) devuelve el
camino completo del directorio seleccionado, y, como era de esperar, cambia cada vez que
cambiamos la selección del directorio.
Al igual que cualquier lista, el DirListBox tiene las propiedades List, ListCount y ListIndex.
List (n) devuelve una cadena de caracteres con el camino completo del elemento cuyo orden
sea el señalado entre paréntesis. Si en vez de n colocamos el ListIndex nos devolverá,
logicamente, el camino del elemento seleccionado.
FileListBox
Como se puede apreciar, cuando el número de ficheros supera el número que puede
presentar, aparecen automáticamente las flechas de scroll verticales.
Path
ListCount
ListIndex
Devuelve un número con el índice del elemento seleccionado. Como todas las listas, devuelve
-1 si no hay ningún elemento seleccionado. El primer elemento de una lista tiene el índice = 0.
Filename
List (n)
Variable = List1.List (5) Variable = nombre del fichero que ocupa el 5º lugar en la lista.
Pattern
Devuelve o establece un valor que indica los nombres de archivo mostrados en un
control FileListBox en tiempo de ejecución.
valor: Expresión de cadena que indica una especificación de archivo, como *.* o *.FRM. La
cadena predeterminada es *.*, que obtiene una lista de todos los archivos. Además de utilizar
caracteres comodín, también puede especificar varios modelos, separándolos con
caracteres punto y coma (;). Por ejemplo, con *.EXE; *.BAT se obtendría una lista con todos
los archivos ejecutables y todos los archivos por lotes de MS-DOS.
Comentarios
Devuelven o establecen un valor que determina si un control FileListBox presenta los archivos
con los atributos Archive, Hidden, Normal o System.
El atributo Archive de un fichero indica si se ha variado ese fichero después de la última copia
de seguridad.
El atributo Hidden indica si el fichero se trata de un fichero oculto.
El atributo Normal indica que el fichero es un fichero “normal” (No está oculto ni es de sistema).
El atributo System de un fichero indica que el fichero es un fichero de sistema.
Sintaxis
objeto.Archive [= booleano]
objeto.Hidden [= booleano]
objeto.Normal [= booleano]
objeto.System [= booleano]
Las sintaxis de la propiedades Archive, Hidden, Normal y System tiene la siguiente partes:
objeto :Nombre del FileListBox
booleano :Una expresión booleana (True / False) que especifica el tipo de archivos
presentados.
El Directorio Actual
Se llama directorio Actual al directorio que está actualmente en uso, bien porque haya sido
forzado el control correspondiente a tener ese directorio en su propiedad Path, bien porque, al
no haberse forzado ninguno, utilice el directorio por defecto.
El directorio por defecto es el último que se ha seleccionado por algún procedimiento. Por
ejemplo, al arrancar una aplicación, el directorio por defecto será el mismo donde se encuentra
el ejecutable. Pero si a lo largo de la aplicación cambiamos el directorio, seleccionando otro
mediante un CommonDialog, ese directorio es el que queda por defecto. Asegúrese en sus
aplicaciones que siempre elige la propiedad Path antes de presentar ficheros o directorios a
través de los controles anteriores, para evitar que pueda mostrarle ficheros de un directorio no
deseado.
Puede comprobar cual es el directorio actual. La función CurDir nos devuelve el directorio
actual.
Puede darse el caso de que desee conocer el directorio actual de varias unidades de disco. Por
ejemplo, puede estar trabajando en el directorio C:\CursoVB\Ejemplos y ser este el directorio
actual en el disco C :, y tener un disquete en la unidad A : en el que la última vez que trabajó
fue en el directorio A :\ApVB\Tema1 y ese es su directorio por defecto en el disco A. Para
conocer el directorio actual del disco C : no necesitó especificar la unidad, ya que era
precisamente la unidad C la unidad actual. Para conocer el directorio actual de la unidad A
emplearía la expresión :
Decíamos que el disco C era la unidad actual. También podemos cambiar la unidad actual,
mediante la sentencia ChDrive seguida de una letra (entre comillas dobles)
También podemos forzar a que un directorio sea el directorio por defecto. La sentencia que
tendremos que usar en ChDir.
Podemos incluso crear un directorio. Usaremos para ello la sentencia MkDir. Esta sentencia
crea el directorio con el nombre indicado, sobre el directorio actual.
¡ Cuidado ! Visual-Basic no le advertirá que puede borrar algo que no desea. Asegúrese muy
bien mediante un aviso al usuario cada vez que elimine un directorio.
Vamos a ver ahora como podemos ver los nombres de los ficheros existentes en un
determinado directorio. Muy sencillo, con un FileListBox. Efectivamente, así podría ser para
presentarlos en pantalla. Sin embargo, si deseamos imprimir la lista de ficheros existentes,
guardarla en un fichero, etc., tenemos una forma que facilita esta labor. Igual que en DOS. La
función Dir
A esta función le debemos añadir como parámetro el criterio de búsqueda, criterio formado por
los caracteres o comodines que deseemos. Dir devuelve solamente el nombre de un fichero,
por lo que tendremos que repetirla tantas veces como ficheros tenga el directorio. Pero
solamente tendremos que introducir el criterio de búsqueda la primera vez.
En el siguiente ejemplo, sacaremos por impresora los nombres de los ficheros existentes en el
directorio actual : (Este código lo meteremos en el procedimiento click de un botón de
comando)
Ya sabemos sacar el directorio. Vamos a ver que otras cosas se pueden hacer con ficheros.
Función Kill. A poco Inglés que sepa se habrá dado cuenta que esta función sirve para
quitarse algo de enmedio. Esta función borra un fichero del disco.
Kill “C:\CursoVB\Ejemplos\Ejemplo1.txt”
Observe la necesidad de dobles comillas para especificar que es, precisamente lo escrito en el
código, lo que indica el origen y el destino. En el caso anterior no llevaba dobles comillas,
debido a que era el contenido de un TextBox lo que se usaba para indicar el origen y el destino.
Donde nombrerutaantiguo es una expresión de cadena que indica el nombre del fichero (con
su path) al que se le quiere cambiar el nombre, y nombrerutanuevo es una expresión de
cadena que especifica el nombre nuevo y, si se desea mover de directorio, la nueva ubicación
del archivo y la unidad de disco. El nombre y ubicación especificados en nombrerutanuevo no
pueden ser los de un archivo existente.
Si se utiliza Name con un archivo abierto se produce un error. Antes de cambiar el nombre de
un archivo, se debe cerrar.
Existe una función que le permite conocer el volumen de un fichero : FileLen. Mediante esta
instrucción podrá saber, por ejemplo, si un fichero le va a caber en lo que le queda libre de un
disquete, el tiempo previsto para enviarlo por módem, etc.
La sintaxis es la siguiente :
Variable = FileLen("C:\CursoVB\Ejemplos\Ejemplo1")
(Observe que de esta función se obtiene una variable de cadena.) Puede aprovechar esta
función para borrar o hacer Backup de determinados ficheros que tengan mas de un tiempo de
permanencia, sustituir ficheros por versiones mas recientes, etc.
Podemos obtener y modificar los atributos de un fichero. Los atributos de un fichero se refieren
a si son de solo lectura, ficheros ocultos, de sistema, si ha sido modificado desde la última vez
que se ha realizado un Backup,
Para conocer los atributos de un fichero, utilizaremos la función GetAttr. Esta función devuelve
un número, que representa la suma de los atributos de un archivo, directorio o carpeta o una
etiqueta de volumen.
Donde nombreruta es una expresión de cadena que especifica un nombre de archivo, con su
Path y unidad de disco.
El valor devuelto por GetAttr es un número, igual a la suma de los siguientes valores de
atributos:
0 vbNormal Normal.
1 vbReadOnly Sólo lectura.
2 vbHidden Oculto.
4 vbSystem Archivo de sistema.
16 vbDirectory Directorio o carpeta.
32 vbArchive El archivo ha sido modificado después de efectuar la última
copia
de seguridad.
Donde nombreruta es una expresión de cadena que especifica un nombre de archivo, con su
Path y unidad de disco, y atributos es una o varias de las constantes o valores anteriores, cuya
suma especifica los atributos de archivo.
Función FileAttr
1 Entrada
2 Salida
4 Aleatorio
8 Añadir
32 Binario
La Ayuda de VB dice que tipoinformación puede ser =2 para que FileAttr devuelva información
sobre el selector de archivos del sistema operativo. Algunas veces la información de ayuda de
VB promete cosas que, sin dudar de que sean ciertas, son difíciles de comprobar.
Esta función no tiene una gran aplicación práctica, ya que comprueba la forma en la que
hemos abierto un archivo. Información que conocemos desde el mismo momento de abrirlo.
• Formularios
• PictureBox
• Image
Existen mas controles gráficos que se irán viendo a lo largo del curso. Estos tres son los mas
usados y con los que comenzaremos a trabajar.
Para "descargar" esa imagen de ese formulario, basta con ejecutar la misma instrucción sin
fichero alguno:
Form1.Picture = LoadPicture ( )
También pueden tomar la imagen desde otro control de imagen, mediante un procedimiento de
Intercambio Dinámico de Datos (DDE) (Solo el PictureBox)
Puede también meter una imagen en una variable, y luego poner en uno de los controles
citados anteriormente la imagen guardada en la variable. Este método es muy rápido, ya que
no necesita acceder al disco para buscar una imagen, pero emplea mucha memoria RAM, ya
que la variable )o variables conteniendo la(s) imágenes están en la RAM.
Para ello debemos declarar una o varias variables tipo Picture, y meter en ellas una imagen
mediante el método LoadPicture. Vea en el capitulo 7 (Métodos Gráficos - Método LoadPicture)
un ejemplo muy ilustrativo.
EL FORMULARIO
Ya hemos visto lo que es el Formulario. Simplemente podemos añadir a lo ya dicho, que un
formulario tiene la propiedad Picture, con lo que podemos poner como fondo un bit-map, un
fichero de icono ( .ICO) o un Metarchivo (.WMF). Un fondo gráfico sobre el Formulario puede
convertir una aplicación de buena a excelente. No intente nunca mejorar una aplicación
mediocre con un fondo artístico en un Formulario.
Además de presentar imágenes, se puede escribir texto en un formulario. Para ello se utiliza el
método gráfico Print que veremos en el capítulo de métodos gráficos
Este objeto permite presentar todo tipo de ficheros gráficos (.BMP, WMF, .ICO, .CUR)
Las dimensiones del PictureBox se establecen en el diseño o mediante programa, variando las
propiedades Height y Width. También puede cambiarse su posición mediante las propiedades
Top y Left. Dispone de una propiedad, Align que permite colocarlo adosado en la parte
superior. Inferior o a uno de los lados del formulario que lo contiene.
El Control PictureBox puede ser destino de los datos de un enlace DDE. (Los datos serán,
lógicamente, gráficos. Por ello, tiene las propiedades LinkMode, LinkItem, LinkTimeout y
LinkTopic.
También puede tomar el dato del gráfico de una Base de Datos. (A través de un control Data o
un objeto Recordset o rdoResultset)
El control Picture puede ser contenedor de otros controles. Esto quiere decir que puede poner
otros controles dentro del picture, y esos controles tendrán un comportamiento especial en
ciertos casos. (Verá más adelante el control Option)
CONTROL IMAGE
Este control permite presentar todo tipo de ficheros gráficos (.BMP, WMF, .ICO, .CUR)
El control Image no puede ser destino de una comunicación DDE. Puede tomar la imagen de
una base de datos, bien a través de un control Data o a través de un objeto Recordset.
El control Image utiliza menos recursos del sistema y repinta con más rapidez que un control
PictureBox, pero sólo admite una parte de las propiedades, los eventos y los métodos de
PictureBox. Use la propiedad Stretch para determinar si el gráfico se escala para que se
ajuste al control o viceversa. Poniendo esta propiedad a True el gráfico se adaptará a las
dimensiones del control Image. Si se pone a False, el control Image tomará las medidas del
gráfico que contenga. Si el gráfico es un bit-map (Fichero .BMP), con la propiedad Stretch a
True podemos variar el tamaño del bit-map, variando las propiedades Width y Height del
control Image, propiedades que se pueden cambiar en tiempo de ejecución. Si esta propiedad
está a False, el tamaño del bit-map no cambiará, presentándose solamente una porción del bit-
map, caso que el control Image sea menor que el tamaño del bit-map, o sobrará espacio en el
control, en caso contrario.
Un Image es transparente, es decir, deja ver el fondo del formulario en las partes no ocupadas
por su gráfico. Por lo tanto, no tendrían sentido en este control propiedades como Backcolor,
FillColor, o FillStyle.
Sintaxis
NombrePictureBox.Align [= número]
número Un entero que especifica cómo se presenta el control. Puede tomar los
siguientes valores :
Se puede usar propiedad Align para crear rápidamente una barra de herramientas
o una barra de estado en la parte superior o inferior de un formulario. Cuando un
usuario cambie el tamaño del formulario, un control con la propiedad Align
establecida a 1 ó 2 modificará su tamaño de forma automática para ajustarse a la
anchura del formulario.
Permite que el Formulario o PictureBox (El control Image no tiene esta propiedad),
mantenga siempre la imagen o el texto presente. Si Autoredraw está a False, el
control no presentará las imágenes o texto que no se hayan dibujado realmente
(caso por ejemplo de un formulario minimizado) o que se hayan borrado por haber
sido puestas tras otro control.
Verá con mas detalles estas propiedades cuando estudie los Objetos enlazados a
Datos. Le van a permitir presentar una imagen desde una base de datos. Le
permitirá también introducirla de una forma muy sencilla en la base de datos.
DragIcon
DragMode (PictureBox e Image) Igual que otros controles
(Una vez que comenzamos a estudiar las propiedades gráficas, se comenta cómo se puede
cambiar el icono de la propiedad MouseIcon de todos los controles)
Sintaxis
objeto.MouseIcon = LoadPicture(ruta) ó
objeto.MouseIcon = imagen
donde :
objeto Nombre del objeto PictureBox o Image
ruta Expresión de cadena que especifica la ruta y el nombre del archivo que
contiene el icono personalizado. Así podemos cargar un icono de los existentes en
el disco. Ej. Image1.MouseIcon =
LoadPicture("C:\vb\icons\computer\disk05.ico")
ScaleHeight, ScaleWidth
ScaleLeft, ScaleTop
ScaleMode
Si Stretch tiene el valor True, al cambiar el tamaño del control también se cambiará
el del gráfico que contenga. Cuando aumenta el tamaño del control, mantiene las
nuevas medidas aunque se le introduzca un gráfico de menor tamaño. Debe
controlarse por programa el tamaño del mismo.
Tag
Top
Visible
WhatsThisHelpID
Width Comunes a ambos. Igual que resto de controles.
Lo verá mucho mas ampliamente en un capítulo posterior. Se incluye en este capítulo para que
lo pueda ir usando.
Este control no está normalmente en la caja de herramientas. Hay que introducirlo mediante los
Proyecto | Componentes y pertenece al grupo Microsoft Windows Common Controls
(COMCTL32.OCX) (Vea Nota 1)
Este control nos permite almacenar varias imágenes. Es, tal como indica su nombre, como un
ListBox pero que en vez de contener textos, contiene imágenes.
La utilidad de este control es para almacenar en él distintas imágenes, y servirlas desde él a
otro control, un PictureBox o un control Image, sin necesidad de tener que acceder a leerlas en
el disco, que conllevaría una importante pérdida de tiempo si el cambio de imágenes debe ser
rápido.
Este control es necesario para almacenar las imágenes que se mostrarán en los iconos de la
barra de herramientas. Verá mas adelante como se realiza una barra de herramientas (La barra
de herramientas solo está disponible para la versión de 32 Bits)
Un ImageList permite tener varias imágenes dentro de él, pero este control no las presenta.
Solamente sirve de “almacén” para pasarlas rápidamente a otro control (PictureBox, p.e.) que
será el que las presenta en pantalla.
En la siguiente figura, pueden verse las propiedades de un ImageList con varias imágenes
cargadas.
Para introducir imágenes en el control ImageList deberemos acceder a sus propiedades, bien
mediante F4 y luego haciendo click sobre Personalizado, bien pulsando el botón derecho del
ratón sobre el icono del control. Aparecerá un cuadro donde podemos insertar imágenes
El control ImageList tiene una colección llamada ListImages. Esta colección contiene todas las
imágenes introducidas en el ImageList y las propiedades de cada imagen. Como toda
colección, será un array donde podemos elegir uno de sus elementos designándolo por su
nombre seguido de su número (Index) encerrado entre paréntesis. Así la imagen número 3
será :
ImageList1.Listimages (3)
Observe que tras ListImages ( ) es necesario poner la propiedad Picture, ya que Picture es una
propiedad de la colección ListImages
Como cualquier colección, ListImages tiene la propiedad Count, que nos servirá para contar el
número de imágenes que guarda.
Una de las mayores aplicaciones del ImageList es proporcionar imágenes para otros controles.
Entre ellos el TOOLBAR
Como el espacio que ocupan los iconos es grande, es también bastante normal que no estén
representados en la barra de herramientas todas las funciones posibles de la aplicación, sino
como decíamos, solamente las mas frecuentes. Pero ocurre que la función que para un usuario
es muy frecuente, para otro no lo es, por lo que también es habitual poder programar los iconos
que aparecen en la barra de herramientas.
Para introducir el Toolbar es necesario que esté presente en la caja de herramientas el control
personalizado Microsoft Windows Common Controls (COMCTL32.OCX).
El control Toolbar es un contenedor de un array de iconos. Estos iconos forman botones sobre
los que se puede insertar código en sus procedimientos. A cada botón se le denomina Button
y a la colección de botones se le denomina Buttons. Cada Button es un elemento de la
colección Buttons y por lo tanto podemos referirnos a cada uno de ellos por el índice que tiene
dentro de la colección.
Los iconos de cada uno de los botones del Toolbar debe introducirse previamente en un
control ImageList. Como se recordará, un control ImageList es un almacén de imágenes, que
podemos usar en cualquier parte de la aplicación. Una de esas partes es la confección de la
barra de herramientas. Por lo tanto, es necesario introducir un control ImageList en cualquier
aplicación que use un control Toolbar. Todas las imágenes del Toolbar deben estar en un
único ImageList. Y este ImageList debe estar en el mismo formulario que el Toolbar
La barra de herramientas puede realizarse en tiempo de diseño (este sería el caso para cuando
no existiese variación ni elección de botones a lo largo de la ejecución de la aplicación)
o en tiempo de ejecución (caso que correspondería con una aplicación que tuviese una barra
de menú programable o que variase esta barra de menú dependiendo de cada operación que
se esté ejecutando en la aplicación).
Para realizar la barra de herramientas en tiempo de diseño, basta con visualizar el cuadro de
propiedades que aparece pulsando el botón derecho del ratón sobre el control Toolbar.
Aparecerá un cuadro de diálogo como este :
Este cuadro nos permite también introducir la forma que adoptará el puntero del ratón cuando
entre en el Toolbar (MousePointer), si el Toolbar aparece por defecto habilitado o deshabilitado
(Enabled) y si muestra o no un rótulo programable al pasar el ratón por encima de cada botón
del Toolbar. (ShowTips). (El uso de estos pequeños rótulos pueden hacer una aplicación muy
bonita) La propiedad AllowCustomize permite, si es True, cambiar el orden de los botones e
introducir separadores (vea evento DobleClick).
En el cuadro botones podemos introducir los datos deseados para cada uno de los botones
que compondrán la colección Buttons.
Para insertar un nuevo botón hay que hacer click sobre Insertar botón e insertará un nuevo
botón inmediatamente después del que figura en la propiedad Index (en la figura del ejemplo, lo
insertaría en segundo lugar, no en el último lugar como podría creerse). También podemos
eliminar un botón haciendo click sobre Eliminar botón.
El Caption (opcional) es la palabra que figurará debajo del icono del botón.
La propiedad Key es un nombre que se puede añadir a cada botón. Esta propiedad sirve para
identificar el botón que se ha pulsado, pero no para nombrar al botón.
La propiedad Value es el valor por defecto, con el que aparecerá el botón al comienzo de la
aplicación. (Unpressed, no pulsado, Pressed, pulsado).
ToolTipText es el texto que aparecerá al lado del botón cada vez que se ponga el cursor del
ratón sobre el botón. Para que aparezca debe estar habilitada la propiedad ShowTips.
El control Toolbar también puede realizarse en tiempo de ejecución. Para introducir u botón en
el Toolbar hay que crear el botón y añadirlo a la colección Buttons de Toolbar. Podemos
hacerlo en una sola instrucción :
NombreBotón puede ser cualquier nombre (es el nombre del objeto botón)
NombreToolbar es el nombre del Toolbar
Indice es el número de orden de ese botón dentro del Toolbar
Key es el valor de la propiedad Key del botón. Este valor debe ser único para cada botón.
Style es un número del 0 al 4 con el tipo de botón. Vea lista mas arriba
Imagen es el número de la imagen para ese botón, dentro del ImageList que las contiene.
Los botones pueden habilitarse y deshabilitarse usando la propiedad Enabled (True / False).
Recuerde que los botones se identifican por su índice :
Toolbar1.Buttons(Indice).Enabled = False
Con la lectura de la propiedad Key, y la sentencia Case o If ... Then podemos iniciar la parte de
la aplicación asociada a cada botón.
ButtonClick
Este evento se produce cada vez que se hace click sobre un botón del Toolbar. Pasa como
parámetro el índice del botón pulsado. Podremos saber que botón se ha pulsado mediante
sentencias condicionales :
If Button.Index = 1 Then . . . .
If Button.Index = 2 Then . . . .
Click
Este evento se produce cuando se hace click sobre cualquier botón del Toolbar, pero no pasa
parámetros. (No distingue sobre que botón se ha hecho click) Podemos utilizarlo cuando se
quiera realizar una operación común para todos los botones (Que suene un Beep, por ejemplo)
DobleClick
Este evento solamente se produce cuando se hace doble click en la parte del Toolbar donde no
hay botones, y la propiedad AllowCustomize esté a True. Si se hace doble click sobre un
botón se produce solamente el evento click (dos veces). Haciendo doble click en una zona
Change
Se produce solamente cuando se ha realizado algún cambio del orden por el procedimiento
anterior.
DragDrop, DragOver
Igual que el resto de los controles Pasa como parámetro el índice del botón, la tecla de
mayúsculas, y las posiciones x e y del ratón.
Este control solamente funciona en la versión de VB de 32 Bits. Pero para 16 bits puede
hacerse una cosa parecida (y mas sencilla) utilizando un control Picture como contenedor de
tantos controles Image como necesitemos. Dado que el control Picture dispone de propiedad
Align y puede servir como contenedor de otros controles, puede realizarse una barra de
herramientas similar a esta, y posiblemente de forma mas sencilla.
LA BARRA DE ESTADO
La barra de estado puede albergar hasta 16 paneles, que pueden contener texto o gráficos.
También pueden contener información del Hardware, tal como el estado de la tecla Bloqueo
de Mayúsculas, Bloqueo del teclado numérico, tecla Insert activada, tecla Scroll Lock
activada, o mostrar la fecha y la hora
Las dimensiones de cada panel pueden configurarse como fijas o ajustadas al tamaño del texto
o imagen que presentan.
Para añadir un panel en tiempo de ejecución basta con añadirlo a la colección Panels del
control StatusBar mediante la sentencia :
Donde PanelNuevo es el nombre con el que se va a conocer ese panel que acabamos de crear.
PanelNuevo debe declararse como variable objeto Panel donde sea necesario según el ámbito
que le queramos dar. De esta forma, una vez creado, nos podremos referir a ese panel
simplemente citándolo por su nombre (En este caso, PanelNuevo)
Si no se pone alguna de las propiedades opcionales intermedias, deben mantenerse las comas
separadoras.
Set PanelNuevo = statusbar1.Panels.Add(pepe, "Contenido del Key " & Str(pepe), "Texto " &
Str(pepe), , LoadPicture("C:\vb\icons\comm\net09a.ico"))
Estos son los controles gráficos mas importantes. Verá a lo largo del curso que existen otros, y
más que podrá encontrar entre diferentes suministradores de controles, ya que VB ha dejado
una puerta abierta para realizar controles a medida. Existen varias empresas de software
dedicadas a la fabricación de controles personalizados, y en el aspecto de controles gráficos,
podrá encontrar una amplia gama de fabricantes y controles. Puede obtener muchos controles
en el CD-ROM que acompaña a varias revistas especializadas en programación VB, y a través
de Internet.
Los ademdums se fueron introduciendo una vez que la Guía del Estudiante estuvo
“casi” terminada. Los temas tratados en los ademdums no constituyen en sí
mismos materia suficiente para un nuevo capítulo, ni se acoplan lógicamente a
ninguno de los capítulos escritos. Por lo tanto, se ha decidido alojarlos en capítulos
que no eran demasiado “gruesos”, aunque el contenido de los ademdums no
encaje ni por su contenido, ni por los conocimientos anteriores. No se preocupe
que algunos ademdums no los entienda. Posiblemente estemos usando un
lenguaje que no se ha visto todavía en los capítulos estudiados. Los podrá repasar
cuando lo considere oportuno, una vez vaya avanzando en el curso.
Desde mis comienzos en la actividad educativa de esta asignatura, los alumnos siempre me
preguntaron por la posibilidad de crear controles en tiempo de ejecución. Supongo que exista
alguna razón que yo desconozco para ello. Y digo esto porque no he tenido la necesidad de
ello en ninguno de mis proyectos, y no han sido precisamente pocos.
Quiero decir con esto que la creación de controles en tiempo de ejecución es algo muy
llamativo, pero de escaso sentido práctico. Sean los alumnos los que busquen utilidad a ello, y
yo me limito a mi obligación de enseñárselo.
En anteriores versiones de VB era un proceso un poco tedioso. Había que crear una matriz de
controles y añadir en tiempo de ejecución tantos controles nuevos a esa matriz como
necesitemos. Era complicado. Había que meter un control y ponerle Index = 0. Se le pone la
propiedad Visible = False y ya tenemos una matriz de controles. Una matriz de un solo control,
pero al tener la propiedad Index = 0 ya era una matriz. Ya en ejecución, se utilizada el método
Load para crear los controles que necesitamos. Los nuevos elementos de la matriz tendrán
todas las propiedades del elemento inicial, es decir, las del elemnto que tienen indice 0. Al ser
una matriz, todos los controles comparten el código de sus procedimientos.
Para crear por ejejmplo, varios botones de comando, introduciremos primero uno con Index =
0 cmdBotonInicial(0) y cuando queramos crear un botón nuevo, invocamos el método Load.
Recuerde que el nuevo botón tendrá las mismas propiedades que el objeto inicial, por lo tanto
sus mismas propiedades Left y Top. Resultado: el nuevo botón queda completamente
solapado con el inicial, por lo que habrá que cambiarle de posición y coocarlo en las
coordenadas deseadas.
Una de las propiedades que no se pasan al nuevo control es la propiedad Visible. Esta siempre
comienza a False. Por lo tanto también hay que ponerla a True para que veamos el nuevo
control. Este podría ser un código para crear un botón de comando
Load cmdBotonInicial(1)
cmdBotonInicial(1).Visible = True
cmdBotonInicial(1).Left = cmdBotonInicial(0).Left + 1000
Es un poco complicado. Se suele hacer esto para que aparezca un control justamente donde
hacemos clic con el ratón sobre el formulario. Dejo al alumno la posibilidad de jugar con este
método.
Sintaxis
Donde:
NombredelControl Es la referencia del nuevo control devuelta por el método Add. Debe
ser una variable tipo Control declarada previamente. Le
recomiendo que esta variable tipo objeto tenga el mismo nombre que le va a
dar al control en su parámetro Name. Le ahorrará muchas confusiones.
Vamos a ver como se puede declarar una variable tipo Control. Puede hacerlo según estos tres
ejemplos, en el primero se declara que NuevoBoton es un control, (declaración
suficientemente válida ya que posteriormente al crearlo, le diremos que ese control es
precisamente un CommandButton) , en la segunda ya lo declaramos como objeto
CommandButton, y la tercera es la declaración de un Objeto TextBox:
Mediante esta declaración (Que debe ponerse en la sección de declaraciones del formulario),
no solamente estamos declarando que NuevoBoton es un CommandButton, sino que tendrá
los mismos eventos (WithEvents) que un CommandButton.
NuevoBoton.Visible = True
NuevoBoton.Left = 100
NuevoBoton.Top = 120
NuevoBoton.Caption = “Soy un Botón”
NuevoTextBox.Visible = True
NuevoTextBox.Left = 2000
NuevoTextBox.Top = 100
Ejecutamos el programa y vemos que efectivamente, una vez creado el botón, al hacer click
sobre él sale el Mensaje Box. Volvamos a ver el código. Cuando introducimos un procedimiento
nuevo creado por nosotros, ese procedimiento aparece en el desplegable de la derecha de la
ventana de código. En el desplegable de la parte izquierda aparecen solamente los nombres de
los controles existentes en el formulario. Pero ¡Oh sorpresa!, el procedimiento
NuevoBoton_Click no está en el desplegable de la parte derecha. Está en la parte izquierda, y
como si fuese un botón que realmente existe en tiempo de diseño. Ya hemos introducido
código en el botón recién creado.
Hay formas más complicadas de crear controles, pero creo que con esta le basta. E insisto que
nunca me he visto en la necesidad de crear controles (lo cual no demuestra nada, lo
reconozco)
Quitar controles
Para quitar controles agregados dinámicamente, se utiliza el método Remove. Solamente se
pueden quitar los controles agregados con el método Add (a diferencia de los controles
agregados con la instrucción Load, que se quitan mediante Unload ).
Me.Controls.Remove "NuevoTextBox"
El Examinador de objetos es una herramienta que tienen Visual Basic para ver los objetos de
las colecciones, las propiedades, métodos y eventos de los controles, los métodos del proyecto,
las constantes . Lo entenderemos mejor abriéndolo. Para abrirlo, basta con pulsar F2
Este es el analizador de objetos. Puede ver que tienen varias partes. Comencemos por arriba.
El desplegable donde pone Todas sirve para elegir una parte de todo lo que nos puede mostrar.
En este caso está mostrando todo lo que tienen el proyecto. Si observa la figura de la página
siguiente verá que está desplegado, mostrando todas las referencias y controles que tienen en
ese momento. Puede ver que tienen el MSCommDlg, porque a ese proyecto le habíamos
introducido el CommonDialog. Si seleccionamos ese control, nos aparecen en la ventana
Classes todas las colecciones que tiene el CommonDialog, entre ellas el propio control, que si
lo seleccionamos, en la ventana de la derecha, (Miembros) aparecerán todas la propiedades,
los métodos, y los eventos (Claro que un CommonDialog no tienen eventos, pero sí aparecerán
si mostramos cualquier otro control)
Si le añadimos ahora una referencia (El acceso a una base de datos mediante DAO, por
ejemplo) aparecerá esa referencia en la lista desplegable.
Los controles básicos aparecen en la referencia VB. Aparecen concretamente los controles que
figuran en la barra de herramientas al abrir un nuevo proyecto. Si añadimos más componentes
(Haciendo clic en la barra de menú Proyecto | Componentes) veremos que aparecen nuevos
controles en el desplegable.
Parece que es un poco complicado. Nada más lejos de la realidad. Verá que es una
herramienta que le va a ser muy útil, y que la va a comprender una vez se decida a usarla,
mucho antes que el tiempo que necesitaría para leérselo en este libro. Adelante.
El objeto Clipboard se usa para manipular el texto y los gráficos del Portapapeles. Este objeto puede
usarse para permitir que el usuario corte, copie y pegue texto y gráficos en la aplicación.
El objeto Clipboard puede contener varias unidades de datos siempre y cuando cada una tenga un
formato diferente. Por ejemplo, se puede usar el método SetData para poner un mapa de bits en el
Clipboard con el formato CF_BITMAP y después usar el método SetText con el formato CF_TEXT
para poner texto en el Clipboard. Después se puede usar el método GetText para recuperar el texto o el
método GetData para recuperar el gráfico. Los datos del Clipboard se pierden cuando se colocan otros
datos con el mismo formato en el Clipboard desde el código o mediante un comando de menú.
SetText
Pone una cadena de texto en el objeto Clipboard usando el formato del objeto Clipboard especificado.
No acepta argumentos con nombre.
formato Opcional. Una constante o valor que especifica uno de los formatos del Portapapeles
reconocidos por Visual Basic, como se describe a continuación :
Estas constantes se enumeran en la biblioteca de objetos de Visual Basic (VB) del Examinador
de objetos.
SetData
Pone una imagen en el objeto Clipboard usando el formato gráfico especificado. No acepta
argumentos con nombre.
formato Opcional. Una constante o valor que especifica uno de los formatos del
Estas constantes se enumeran en la biblioteca de objetos de Visual Basic (VB) del Examinador
de objetos.
El gráfico que se va colocar en el objeto Clipboard se define mediante la función LoadPicture
o la propiedad Picture de los objetos Form, Image o PictureBox.
GetData
Devuelve un gráfico del objeto Clipboard. No acepta argumentos con nombre.
formato
Opcional. Una constante o valor que especifica el formato gráfico de Clipboard, como se
describe mas adelante. La constante o valor debe ir entre paréntesis. Si formato es 0 o se omite,
GetData usa automáticamente el formato apropiado.
Si en el objeto Clipboard no hay ningún gráfico que coincida con el formato esperado, no se
devuelve nada. Si en el objeto Clipboard sólo hay una paleta de colores, se crea un DIB de
tamaño mínimo (1 x 1).
GetText
Devuelve una cadena de texto del objeto Clipboard. No acepta argumentos con nombre.
formato
Opcional. Un valor o constante que especifica el formato del objeto Clipboard, como se
describe mas adelante. La constante o valor debe ir entre paréntesis.
Si en el objeto Clipboard no hay ninguna cadena de texto que coincida con el formato
esperado, se devuelve una cadena vacía ("").
Sintaxis objeto.Paste
Objeto es el nombre del Panel Contenedor OLE donde se quiere depositar el contenido del
Portapapeles.
Comentario
Para usar este método, defina la propiedad OLETypeAllowed y después verifique el valor de la
propiedad PasteOK. No se puede pegar con éxito a menos que PasteOK devuelva True.
Si se ha ejecutado el método Paste, la propiedad OLEType es vbOLELinked (0) o
vbOLEEmbedded (1). Si no se ha ejecutado el método Paste, la propiedad OLEType es
vbOLENone (3).
Este método puede usarse para implementar un comando Edición Pegar en un menú.
Si el valor de la propiedad PasteOK es True y Visual Basic no puede pegar el objeto, el control
contenedor OLE elimina cualquier objeto existente en el control.
PasteOK
Devuelve un valor booleano que determina si el contenido del Portapapeles del sistema se
puede pegar en el control contenedor OLE.
Objeto es el nombre del contenedor OLE donde se quiere pegar el contenido del portapapeles.
Cuando el valor de esta propiedad es True, el contenido del Portapapeles del sistema se puede
pegar en el control contenedor OLE.
La propiedad OLETypeAllowed se usa para especificar el tipo del objeto (vinculado o
incrustado) que se quiere pegar en el control contenedor OLE. Una vez que un objeto se ha
pegado en el control contenedor OLE, se puede verificar el valor de la propiedad OLEType
para determinar el tipo de objeto que ha sido creado.
Esta propiedad puede usarse si se quiere que la aplicación implemente un comando Pegar en
un menú Edición. Si PasteOK es False, el comando del menú se deshabilita; si no, puede
habilitarse. Los comandos de menú se habilitan y se deshabilitan estableciendo su propiedad
Enabled a True o False, respectivamente.
GetFormat
Devuelve un entero que indica si un elemento del objeto Clipboard coincide con un formato
especificado. No acepta argumentos con nombre.
formato
Requerido. Un valor o constante que especifica el formato del objeto Clipboard, como se
describe mas adelante. La constante o valor debe ir entre paréntesis.
El método GetFormat devuelve True si algún elemento del objeto Clipboard coincide con el
formato especificado. Si no, devuelve False.
Para los formatos vbCFDIB y vbCFBitmap, la paleta de colores que se encuentre en el
Clipboard se usa para presentar el gráfico.
Clear
Sintaxis Clipboard.Clear
Copy
Copia el objeto de un control contenedor OLE al Portapapeles del sistema.
Sintaxis objeto.Copy
Cuando se copia un objeto en el Portapapeles del sistema, todos los datos y la información de
vinculación asociados con el objeto se copian en el Portapapeles del sistema. En el Portapapeles del
sistema se pueden copiar objetos vinculados y objetos incrustados.
Este método puede usarse para implementar un comando Edición | Copiar en un menú.
EJERCICIO
Se propone el siguiente ejercicio, donde pueden verse todas las posibilidades del Portapapeles.
Option Explicit
El intercambio dinámico de datos es una utilidad de Windows que utiliza Visual Basic, y nos permite
crear aplicaciones que tomen datos una de otras.
Para pasar datos de una aplicación a otra se necesitan al menos, dos aplicaciones (lógico), una que se
deje leer, y la otra (u otras), que quieran leer la información en aquella.
Puede darse el caso que una aplicación esté recibiendo datos de otra aplicación, y a su vez envíe datos a
una tercera.
La aplicación servidor debe estar funcionando antes de que la aplicación cliente le pida la información.
Si no es así, se generará un error.
El origen de un intercambio DDE siempre es un formulario. Dentro de este formulario origen estará
el Label, TextBox o PictureBox que contiene la información a enviar.
Para indicar que un formulario es origen de información para un intercambio DDE debemos decírselo
en sus propiedades LinkMode y LinkTopic.
Cuando queremos que uno de estos controles sea el destino de un intercambio DDE debemos indicarlo
en sus propiedades LinkMode, LinkTopic, LinkItem, e indicarle el tiempo de espera para un
intercambio en la propiedad LinkTimeout.
Propiedad LinkMode
En tiempo de ejecución, para un control, esta propiedad es de lectura y escritura, es decir, se puede usar
para saber que valor tiene esta propiedad en un determinado control, o para forzar esa propiedad a un
determinado valor.
objeto.LinkMode [= número]
donde número es un entero que especifica el tipo de conexión. (0, 1, 2 ó 3 según se ha visto)
y objeto es el nombre del control.
Propiedad LinkTopic
Esta propiedad es de lectura y escritura, tanto para los controles como para los formularios.
Cuando un control destino quiere establecer una comunicación DDE “llama” a la aplicación origen, y
dentro de ella, al formulario que contiene el control cuya información debe ser traspasada. (Nótese que
la aplicación origen puede tener varios formularios). Al ese formulario podría llamarle por su nombre,
(por su Name), pero no lo hace así. Le llama por un nombre que le debemos poner al formulario en su
propiedad LinkTopic. A este nombre se le denomina Tema.
Para un control destino, La propiedad LinkTopic contiene el nombre de la aplicación y el tema, ambos
separados mediante el carácter | (carácter 124)
El nombre de la aplicación origen será el nombre del de proyecto de Visual Basic sin la extensión .VBP
(si la aplicación se está ejecutando en el entorno de desarrollo de Visual Basic), o el nombre de la
aplicación de Visual Basic sin la extensión .EXE (si se ejecuta como un archivo ejecutable
independiente).
El tema será el mismo al que “responde” el formulario donde se encuentra el control que contiene la
información a traspasar.
Esta propiedad es de lectura y escritura, es decir, tanto sirve para saber el nombre del control origen de
los datos, como para establecerlo en tiempo de ejecución.
donde cadena es el nombre del control origen que tiene los datos y objeto es el nombre del control al
que le estamos poniendo la propiedad LinkItem.
Propiedad LinkTimeout
Devuelve o establece la cantidad de tiempo que un control espera una respuesta a un mensaje DDE.
donde número es una expresión numérica que especifica el tiempo de espera en décimas de segundo.
El tiempo de respuesta DDE desde aplicaciones origen varía. Use esta propiedad para ajustar el tiempo
que un control destino espera la respuesta de una aplicación origen. Si se usa LinkTimeout de forma
correcta se puede evitar la generación de un error por Visual Basic si una aplicación origen tarda mucho
en responder.
Nota La plazo mayor de tiempo que un control puede esperar es 65.535 décimas de segundo, es decir,
sobre 1 hora y 49 minutos. Al establecer LinkTimeout a 1 se indica al control que espere la respuesta
en una conversación DDE durante el mayor plazo de tiempo. El usuario puede forzar que el control deje
de esperar presionando la tecla ESC..
Evento LinkNotify
Ocurre cuando el origen ha cambiado el dato definido por el vínculo DDE si la propiedad LinkMode
del control destino está establecido a 3 (Notificar).
End Sub
En este procedimiento se puede escribir el código necesario para nuestra aplicación, a sabiendas de que
este evento se produce cuando cambia la información en origen. Posiblemente no quiera depositarlo de
inmediato en el control destino, puesto que si fuese así habría puesto la propiedad LinkMode a 1.
Cuando quiera colocar el dato en el control destino, puede utilizar el método LinkRequest para obtener
el nuevo dato de la fuente.
Método LinkRequest
Pide a la aplicación origen de una conversación DDE que actualice el contenido de un control Label,
PictureBox o TextBox.
Sintaxis objeto.LinkRequest
Objeto es el nombre de un control Label, PictureBox o TextBox involucrado en una conversación DDE
como destino. LinkRequest provoca que la aplicación origen envíe los datos actuales al objeto,
actualizando la propiedad Caption si objeto es un control Label, la propiedad Picture si objeto es un
control PictureBox o la propiedad Text si objeto es un control TextBox.
Método LinkSend
Sintaxis objeto.LinkSend
Objeto debe ser un control PictureBox de un objeto Form que sea origen de una conversación DDE.
Cuando otras aplicaciones establecen vínculos automáticos con un objeto Form de su aplicación, Visual
Basic las notifica cuando el contenido de un control TextBox o Label origen cambia. Sin embargo,
Visual Basic no notifica automáticamente a una aplicación destino DDE cuando el valor de la
propiedad Picture de un control PictureBox origen cambia. Como la cantidad de datos de un gráfico
pueden ser muy grande y como no tiene sentido actualizar la aplicación destino por cada cambio de
píxel de la imagen, Visual Basic requiere el uso del método LinkSend para notificar explícitamente a
las aplicaciones destino DDE cuándo cambia el contenido de un control PictureBox.
Método LinkPoke
Sintaxis objeto.LinkPoke
Objeto es el nombre del control Label, PictureBox o TextBox involucrado en la conversación DDE
como destino. Si objeto es un control Label, LinkPoke transfiere el contenido de la propiedad Caption
al origen. Si objeto es un control PictureBox, LinkPoke transfiere el contenido de la propiedad Picture
al origen. Si objeto es un control TextBox, LinkPoke transfiere el contenido de la propiedad Text al
origen.
Normalmente, en una conversación DDE la información fluye de origen a destino. Sin embargo,
LinkPoke permite que un objeto destino suministre datos al origen. No todas las aplicaciones origen
aceptan información de esta forma; si la aplicación origen no acepta los datos, se produce un error.
El Drag & Drop es una forma visual de representar el movimiento de algo. Para ello se toma algo de
una parte de la interface gráfica, mediante la operación de colocar el puntero del ratón y pulsar su botón
izquierdo sobre ese algo que se quiere tomar. Sin dejar de pulsar el botón izquierdo del ratón se desliza
(se arrastra) el puntero del ratón hasta el punto de la interface gráfica donde lo queremos dejar. Ese
punto puede ser un control o un formulario. Una vez en el punto de destino se suelta el botón del ratón
y se “deja caer” lo que habíamos tomado en el punto de origen. El efecto de “tomar” algo de un control
le denominaremos Drag. Si durante la operación de arrastre pasamos por encima de un control, se
producirá en ese control el evento DragOver Al efecto de dejar caer le denominaremos DragDrop.
El Drag & Drop tiene un efecto visual muy didáctico. Pero NO HACE otra cosa. Es decir, no toma
nada del control origen ni lo suelta al llegar a destino. Esa acción de tomar algo en el origen o soltarlo
al llegar al destino debemos realizarla mediante código en los eventos adecuados (MouseDown,
DragOver , DragDrop), dando los valores adecuados a las propiedades DragMode y DragIcon, y
utilizando el método Drag.
Propiedad DragMode
Es una propiedad de lectura y escritura. Devuelve o establece un valor que determina si se usa el modo
de arrastre manual o automático en una operación de arrastrar y soltar.
Cuando esta propiedad está a 0 (Manual) se debe emplear el Método Drag para iniciar una operación
Drag & Drop. Si está a 1, la operación de arrastrar y soltar se inicia automáticamente cada vez que
hacemos click sobre el botón izquierdo del ratón, teniendo el puntero del mismo sobre el control. Los
controles contenedores OLE sólo se arrastran automáticamente cuando no tienen el enfoque.
Parece en principio mas práctico tener esta propiedad a 1. Sin embargo la realidad es distinta. Se
controla mucho mejor el Drag & Drop poniendo esta propiedad a 0 (Manual), pese a que en este caso
deberemos utilizar el método Drag para iniciar el proceso.
El poner esta propiedad en automático conlleva también el hecho de que el control no toma el foco
haciendo click sobre él, ya que no sabe si lo que quiere hacer es llevarle el foco o iniciar una operación
de Drag & Drop.
Propiedad DragIcon
Devuelve o establece el icono que se presenta como puntero del ratón durante una operación de
arrastrar y soltar.
Esta propiedad va a marcar el icono que aparezca durante la operación Drag & Drop desplazándose a lo
largo de la ventana. Si no se especifica esta propiedad, el desplazamiento se expresa mediante un
rectángulo del tamaño del control origen, cosa que resulta bastante desagradable en cualquier
aplicación. Debe ponerse por lo tanto un icono en esta propiedad.
El valor de esta propiedad puede establecerse en tiempo de diseño, eligiendo el icono en el cuadro de
Propiedades del control origen, o en tiempo de ejecución. En este caso, la referencia a un icono válido
puede darse igualando el valor de esta propiedad a un icono ya existente en la aplicación
(NCO.DragIcon = Form1.Icon pondría como icono el del formulario Form1, NCO.DragIcon =
Para Nota. Cuando quiera colocar un icono animado en la propiedad DragIcon (una hoja de papel que
oscila al moverse, una hoja de papel que se destruye - ejemplos tomados de W95-), debe usar varios
iconos en secuencia. Por lo tanto debe cambiar la propiedad DragIcon a lo largo del tiempo que dura el
arrastre. Puede utilizar para ello un temporizador (control Timer) o basarse en las coordenadas del
formulario por donde se mueve (para eso introducen en el evento DragOver del Formulario). Dado que
el cambio debe ser rápido, no es conveniente acceder al disco (mediante la función LoadPicture) cada
vez que tiene que cambiar la imagen del icono. Como para simular un movimiento tan sencillo son
suficientes pocas imágenes, (8 por ejemplo) puede crear otras tantas variables tipo Picture, cargar las
imágenes al comienzo de la aplicación (con la función LoadPicture) y cuando necesite el movimiento
animado del icono, cargar las 8 imágenes secuencialmente desde esas variables, para simular el
movimiento
NCO.DragIcon = Variable1
NCO.DragIcon = Variable2
......
NCO.DragIcon = Variable8
Esto le ocupará mas memoria. Es el precio a pagar por la rapidez y la buena presentación de una
aplicación.
Método Drag
Inicia, termina o cancela una operación de arrastre de cualquier objeto excepto los controles Line,
Menu, Shape o Timer.
NombreControlOrigen es el nombre del control donde se inició la operación Drag & Drop.
TipoAcción es un valor o una constante que especifica la acción a realizar, según se describe a
continuación :
El uso del método Drag para controlar una operación de arrastrar y soltar sólo se requiere cuando la
propiedad DragMode del control origen tiene el valor Manual (0). Sin embargo, Drag puede usarse con
objetos cuya propiedad DragMode tenga el valor 1 (o vbAutomatic).
En versiones anteriores de Visual Basic, Drag era un método asíncrono y las instrucciones siguientes se
ejecutaban incluso aunque la acción de arrastre no hubiera terminado. En Visual Basic versión 4.0,
Drag es un método síncrono y las instrucciones siguientes no se ejecutan hasta que la acción de arrastre
no haya terminado.
Usando el método Drag puede controlar exactamente cuando quiere que se produzca el inicio del Drag
& Drop y el final. El autor de esta Guía del Estudiante vuelve a recomendar que ponga la propiedad
DragMode = 0 (Manual). Entre otras razones por la siguiente :
Cuando tenemos la propiedad DragMode de un TextBox a 1 (automático) ese TextBox no podrá coger
el foco, (al menos de forma fácil) y si lo consigue no puede seleccionar el texto que tiene actualmente,
Como decíamos al principio, el Drag & Drop solamente es una manifestación visual de algo que se está
produciendo, pero ese algo deberemos programarlo. Por ejemplo, es muy típico llevar un dato de una
casilla a otra (de un TextBox a otro p.e.) mediante una operación D & D.
El dato debemos llevarlo a una variable que nos permita, al final de la operación, introducir ese dato en
el TextBox final. (No olvide declarar la variable en el lugar correspondiente para que sea válida en
ambos controles). La acción de llevar el dato a la variable debemos hacerla en el procedimiento mas
adecuado. Puede ser por ejemplo, el procedimiento MouseDown del control origen, ya que cualquier
operación de D & D comienza haciendo click en el control origen. Si tiene la propiedad DragMode de
ese control en Manual, en el momento de hacer Click, ese control toma el foco y, caso de un TextBox,
puede arrastrar con el ratón para cambiar el texto, cosa que no podría hacer si pone DragMode=1.
Evento DragOver
Cuando una operación de arrastrar y soltar está en progreso y el cursor del ratón pasa por encima de un
control o un formulario, se produce el evento DragOver de ese control o formulario.
Donde :
origen = Control que se está arrastrando. O dicho de forma mas ortodoxa, control en el que se inició la
operación de Drag & Drop. Dentro de este procedimiento puede hacer referencia a sus propiedades y
métodos con este argumento. Por ejemplo, Source.Visible = False. Caso de que el Control Origen fuese
parte de un array de controles, el Index de ese control se tiene en índice
x, y Número que especifica la posición horizontal (x) y vertical (y) actual del puntero del mouse
dentro del control o formulario destino. Estas coordenadas se expresan siempre en términos del sistema
de coordenadas del destino tal y como se establece en las propiedades ScaleHeight, ScaleWidth,
ScaleLeft y ScaleTop.
estado Entero que nos indica el estado de transición del control que se está arrastrando en relación al
formulario o control destino:
0 = Entra. Este valor se produce en el instante en el que entra el cursor del ratón a
este control o Formulario. Se genera también en el control origen en el instante que se
pulsa el botón del ratón, iniciándose de esta forma el D & D.
1 = Deja Este valor se produce cuando el cursor sale del control o Formulario.
2 = Sobre Este valor se produce cuando el cursor se está moviendo sobre el control o
el Formulario destino.
Mediante el parámetro origen (completado con índice si el origen se trata de un array) podemos
conocer el control desde el que se inició la operación de Drag & Drop. Debemos tener siempre presente
que el procedimiento DragOver se realiza cada vez que pasamos por encima de un control o formulario
durante una operación de Drag & Drop, independientemente que ese control o formulario sea o no sea
origen ni destino de esa operación. Puede emplearse el procedimiento DragOver para comprobar si ese
valor que transportamos puede depositarse sobre el control o formulario por el que estamos pasando, si
el valor que llevamos está dentro de los márgenes que admite la aplicación, etc. Es muy llamativo por
ejemplo, cambiar el color del control que va a recibir el valor traspasado si este valor se sale de los
márgenes aceptables. O cambiar la propiedad DragIcon del control origen cuando ese dato no se puede
depositar en ese control. Es típico poner la señal de trafico de Prohibido cuando el dato transportado no
se puede soltar sobre el control sobre el que estamos pasando el cursor del ratón. (Como el control
LSB Visual Basic – Guía del Estudiante Capítulo 1 Página 153
origen se pasa como parámetro al procedimiento DragOver con el nombre Origen basta con poner
Origen.Dragicon = ..... ) Recuerde en este caso que al abandonar ese control debe restaurar el icono
original. Para ello es útil cargar varias variables con los distintos iconos que se van a usar y luego
igualar la propiedad DragIcon del control origen a una u otra variable, dependiendo de los valores u
otros factores de la aplicación.
En el siguiente ejemplo, se declaran 6 variables tipo Picture que van a almacenar seis iconos distintos :
En General - Declaraciones
Dim pepe as string
Dim icono1 As Picture, Icono2 As Picture, Icono3 As Picture
Dim Icono4 As Picture, Icono5 As Picture, Icono6 As Picture
El Form1.Load se cargan los valores de estas variables con seis iconos existentes en el disco. El icono
asignado al control origen (Text1) es Icono4.
Set icono1 = LoadPicture("C:\vb\icons\dragdrop\drag1pg.ico")
Set Icono2 = LoadPicture("C:\vb\icons\dragdrop\drag2pg.ico")
Set Icono3 = LoadPicture("C:\vb\icons\dragdrop\drag3pg.ico")
Set Icono4 = LoadPicture("C:\vb\icons\dragdrop\drop1pg.ico")
Set Icono5 = LoadPicture("C:\vb\icons\dragdrop\dragfldr.ico")
Set Icono6 = LoadPicture("C:\vb\icons\dragdrop\dropfldr.ico")
Text1.DragIcon = Icono4
En el control destino se cambia el icono si el valor de la variable transportada (pepe) es mayor de 999 :
En el procedimiento DragOver
If Val(pepe) > 999 Then Source.DragIcon = icono1
‘Al salir de Text1 se recupera el icono original
If State = 1 Then Source.DragIcon = Icono4
Evento DragDrop
Ocurre cuando se completa una operación de arrastrar y soltar como resultado de arrastrar un control
sobre un formulario o control y liberar el botón del mouse o utilizar el método Drag con su argumento
acción establecido a 2 (Drop).
Dependiendo de si soltamos sobre un Formulario convencional, Formulario MDI o Control, este evento
captura los siguientes parámetros :
Utilice un procedimiento de evento DragDrop para controlar qué ocurre tras completarse una
operación de arrastrar. Por ejemplo, puede mover el contenido del control origen a un nuevo lugar o
copiar un archivo de un lugar a otro, depositar un valor sobre un control etc.
Recuerde que la operación Drag & Drop no hace otra cosa que lo que se puede ver en la interface
gráfica. Debe poner en este Procedimiento (DragDrop) el código necesario para que se realice la
operación deseada.
Cuando veíamos las propiedades de los controles relacionadas con el Drag & Drop, veíamos
otras que hablaban de Drag y Drop, que eran OLEDragMode y OLEDropMode, y en el
Formulario veíamos solamente esta última: OLEDropMode. Entre los procedimientos
observábamos algo parecido: OLECompleteDrag, OLEDragDrop, OLEDragOver,
OLEGiveFeedback, OLESetData y OLEStartDrag. También existe el Método OLEDrag.
Mediante estas propiedades, procedimientos y método, podemos hacer que desde una
aplicación (Word por ejemplo) pase información a una aplicación hecha por nosotros, utilizando
la misma interfase gráfica para “ver” el movimiento de los datos. Lo mismo ocurre en sentido
contrario. Desde una aplicación nuestra podemos llevar texto o imágenes a Word. A ese
mecanismo se le denomina OLE Drag & Drop. Parece como si el OLE Drag & Drop reuniera
las dos ventajas de DDE y de Drag & Drop. Esa fue la idea con la que Microsoft creo esta
herramienta.
Mediante el OLE Drag & Drop ya podemos enlazar nuestras aplicaciones a las aplicaciones
compradas. Pero piense que lo que va a conseguir con el OLE Drag & Drop ya lo tiene
prácticamente igual con el portapapeles. Por lo tanto, no se haga demasiadas ilusiones. Y
además recuerde que el mecanismo de arrastrar y soltar no es tan fácil como copiar y pegar, al
menos para personas poco hábiles.
El Objeto DataObjet
El objeto DataObjet es un contenedor que utiliza OLE para transportar datos. Recuerde,
cuando hacíamos Drag & Drop normal, en el Drag teníamos que meter lo que queríamos
transportar (Texto o imagen) en una variable y en el Drop poníamos el contenido de esa
variable en el control final. Esto puede hacerse cuando se trata de una misma aplicación, en la
que el valor de la variable se mantiene mientras estuviésemos dentro del ámbito donde se ha
declarado. ¿Cómo haríamos esto para pasar un texto a Word? Debe haber una “variable”
que mantenga su contenido entre dos aplicaciones. Esa “variable” debe ser un objeto superior
a las dos aplicaciones que van a intercambiar datos. Es concretamente el DataObjet
Método SetData
Inserta datos en un objeto DataObject con el formato de datos especificado.
objeto es un objeto tipo DataObjet. Este objeto no es necesario declararlo, ya que aparece en todos los
procedimientos donde es posible introducir o sacar datos del DataObject.
Data es un objeto tipo DataObject donde se ha metido el texto que contienen el Text1. Ese objeto
llamado Data lo ha pasado el procedimiento (Data As DataObject) por lo que podemos meterle los
datos sin necesidad de crearlo previamente. Este objeto es único para Windows, por lo que a partir de la
ejecución de ese procedimiento OLEStartDrag el texto introducido en Data está disponible para
cualquier aplicación Windows que sepa recogerlo.
datos es el dato a introducir en el DataObject. En el ejemplo anterior, era el texto contenido en Text1
formato es un valor o constante que especifica el tipo de datos introducidos. Este argumento es
opcional. Vea valores posibles en el cuadro más abajo.
Método GetData
Devuelve datos de un objeto DataObject en forma de Variant.
formato es un valor o constante, que determina el formato de los datos que se van a obtener. Los
valores para formato se pueden ver en el cuadro más abajo.
RTB1.SelText = Data.GetData(vbCFText)
End Sub
Método Clear
Elimina el contenido del objeto DataObject.
Sintaxis objeto.Clear
Puede ejecutar este método antes de introducir nuevos datos en el DataObject para
asegurarse que ha borrado los datos existentes
Este método sólo está disponible para los orígenes de operaciones de arrastre de componentes. Si se
invoca Clear desde un destino de la operación de colocación de componentes, se producirá un error.
Método GetFormat
Devuelve un valor booleano que indica si un elemento del objeto DataObject coincide con un formato
especificado.
Ejemplo Puede asegurarse que el formato contenido en el DataObject es el deseado antes de proceder a
colocarlo en un control en la operación de Drop:
Propiedad Files
Devuelve una colección DataObjectFiles, que a su vez contiene una lista con todos los nombres de
archivo utilizados por un objeto DataObject (por ejemplo los nombres de los archivos que un usuario
arrastra hacia o desde el Explorador de archivos de Windows).
Sintaxis NombredelObjetoDataObject.Files(índice)
La colección Files sólo se puebla con nombres de archivo cuando el objeto DataObject contiene datos
de tipo vbCFFiles. La colección Files puede rellenarse para permitir a las aplicaciones de Visual Basic
actuar como origen de operaciones de arrastre de una lista de archivos.
Colección DataObjectFiles
Es la colección de cadenas con los nombres de ficheros de la propiedad Files del objeto DataObject.
Sintaxis NombredelObjetoDataObject.DataObjectFiles
Aunque se ha pretendido realizar un ejemplo de esta propiedad, el autor cede ese honor al
alumno interesado en el tema. No he podido obtener los elementos de la colección
DataObjectFiles.
Propiedad OleDragMode
Si se pone esta propiedad a Manual es necesario iniciar el drag mediante el método OLEDrag,
que ejecuta el procedimiento OLEStartDrag, y colocar en este el código correspondiente. No es
práctico, pero cada aplicación y cada programador es una caso distinto.
Propiedad OLEDropMode
Esta propiedad va a determinar la forma en la que el componente destino toma los datos
transportados medainte OLE Drag & Drop. Acepta tres valores:
Es normal poner esta propiedad a manual, para poder controlar lo que ocurre con la
información tanto en destino como en origen. Estando en Automático, la información en el
origen se mueve al destino, es decir, desaparece del origen al tiempo que se pone en el
destino. Esto puede que no sea lo deseado por el programador. Si pone esta propiedad en
Manual, puede controlar lo que hace la información tanto en destino como en origen.
Una cosa que podemos advertir en todos los eventos relacionados con el OLE Drag & Drop es
la ausencia del parámetro Source que teníamos en los eventos DragDrop y DragOver del
Drag&Drop convencional. Parece normal, ya que no tenemos porque saber el nombre del
control origen si éste es de una aplicación externa (Word por ejemplo). Ese parámetro Source
lo empleábamos para saber si un control podía recibir o no la información, dependiendo del
origen de la misma. En el OLE Drag & Drop solamente podemos basarnos para eso en el tipo
de información que transporta el DataObject, tipo que podemos determinar mediante el método
GetFormat.
Evento OLEStartDrag
Se produce cuando se ejecuta el método OLEDrag de un componente o cuando un componente inicia
una operación OLE de arrastrar y colocar, y su propiedad OLEDragMode está establecida a
Automatic.
Sintaxis
Private Sub objeto_OLEStartDrag(datos As DataObject, efectosPermitidos As Long)
El parámetro EfectosPermitidos va a determinar lo que ocurre con el dato en el control origen. Acepta 3
valores:
Evento OLEDragOver
Se produce cuando el puntero del ratón pasa por encima de un control durante una operación de OLE
Drag & Drop.
Sintaxis
Evento OLEDragDrop
Se produce cuando se termina la operación de OLEDrag&Drop sobre el control. Para que se
pueda terminar la operación de OLEDrag&Drop el componente destino tienen que permitirlo.
Nota Este evento sólo se produce cuando OLEDropMode está establecido a 1 (Manual).
Sintaxis
En este evento podemos escribir el código necesario para que el dato se coloque de la forma
deseada (por ejemplo, puede analizar el tipo de dato y proceder de acuerdo al contenido del
DataObject).
Los parámetros pasados por este evento son iguales al del evento anterior.
Evento OLECompleteDrag
Se produce en el origen cuando se ha completado la operación de arrastre (cuando se levanta
el botón del ratón), e informa al componente de origen de que se ha completado o cancelado
dicha operación.
En el ejemplo siguiente, el control origen (Text1) se pone rojo cuando se termina el OLEDrag&Drop:
Evento OLEGiveFeedback
Este evento se produce en el control Origen. Se produce después de cada evento OLEDragOver.
OLEGiveFeedback permite al componente de origen proporcionar al usuario una indicación visual,
como cambiar el cursor del mouse para indicar lo que ocurrirá si coloca el objeto o señalar visualmente
la selección (en el componente de origen) para indicar lo que va a ocurrir.
Sintaxis
En este capítulo hemos visto tres formas de transvasar información, bien de una aplicación a otra, bien
dentro de la misma aplicación.
Es posible que le haya llamado la atención estas herramientas. Y posiblemente piense en un enorme
número de posibilidades de aplicación. La realidad le va a demostrar que se aplican muy pocas veces.
Exceptuando el OLEDrag&Drop, que le permite exportar texto a / desde Word, y eso le vienen muy
bien al usuario porque le ahorra tiempo de teclear, los demás no se usan con la profusión que se
debería, vistas a priori las ventajas que tienen.
El Menú o Barra de Menú es la segunda línea de cualquier ventana Windows, donde pueden verse
distintos nombres. La Barra de Menú es un componente de un Formulario.
La Barra de Menú puede tener tantas palabras como se desee, y sobre cada palabra, puede desplegarse
un Menú desplegable con mas palabras. Sobre cada una de estas puede a su vez desplegarse otro
conjunto de palabras, etc. con lo que se puede conseguir una cantidad de palabras tan grande como
necesitemos.
Para cada palabra se produce el evento click. Cada palabra de la Barra de Menú lleva anexo un único
procedimiento, que se ejecutará al hacer click sobre la palabra. Pero en las palabras que sirven para
desplegar un menú no tiene sentido que se ponga ningún código en su procedimiento, ya que están ahí
justamente para desplegar otras palabras. Será en las palabras finales donde se colocará el código
correspondiente.
En las figuras anteriores pueden verse tres formas distintas de ver el mismo Menú. En la primera figura
el Menú está sin desplegar. En la segunda figura puede verse un árbol de Menú largo, desplegado en su
totalidad. En la tercera puede verse el Menú desplegable de la palabra Archivo de la Barra de Menú.
Puede apreciarse en este menú desplegable una línea que separa las palabras Guardar Como y SALIR.
Esta línea separadora es muy práctica para separar dos temas distintos dentro de un Menú desplegable.
Tan distintos como las operaciones de Abrir y Guardar respecto a la última orden del menú desplegable
que es SALIR de la aplicación.
Para crear un menú debe usarse el Editor de Menú, que se encuentra en la Barra de Menú de VB en
Herramientas | Editor de menús. Le aparecerá el siguiente cuadro. (En principio vacío)
Existen otras propiedades aparte del Caption y Name que se introducen directamente en las casillas al
efecto.
HelpContextID Igual que esta propiedad en el resto de los controles, permite introducir un número de
contexto para la ayuda de Windows.
Enabled. Habilitado. Si se quita la marca de esa casilla, esa palabra aparecerá por defecto
deshabilitada. Esta propiedad puede leerse y cambiarse en tiempo de ejecución, por lo que esta
propiedad puede usarse para deshabilitar ciertas funciones de la aplicación, dependiendo de la
evolución de la propia aplicación. La presentación en el menú cuando está deshabilitada es con color
pálido.
Visible. Como su nombre indica, que sea visible o no lo sea. Puede cambiarse en tiempo de
ejecución.
WindowList Indica si esa palabra es la que va a mostrar todos los formularios abiertos en una
aplicación de documentos múltiples (MDI). Al hacer click sobre esta palabra, se desplegará un
submenú con todos los Caption de los Formularios hijo abiertos en ese momento. Solamente puede
existir una palabra en un menú con esta propiedad activada. Esta propiedad no puede variarse en
tiempo de ejecución.
Para cambiar una de estas propiedades en tiempo de ejecución, basta con citar por su nombre a esa
palabra (por su Name, no por su Caption), e igualar a True o False su valor :
nmuDocumentos.Visible = False
El nombre usado para nombrar a una de las palabras del menú puede ser cualquiera. Sin embargo es
prudente usar un nombre que nos pueda identificar, por una parte, que ese nombre corresponde a una de
las palabras del menú, y por otra, a que palabra se refiere. La solución puede ser cualquiera. El autor
Otras propiedades
Shortcut Atajo. Esta propiedad admite varias combinaciones de teclas para acceder al
procedimiento click de esa palabra sin necesidad de usar el ratón. Las combinaciones de teclas posibles
se muestran haciendo click en la flecha hacia abajo que tiene esa casilla. Se desplegará una lista con
todas ellas. Para elegir una de estas combinaciones, basta con hacer click sobre la línea que la contiene.
Esta combinación de teclas aparecerá en el menú, al lado de la palabra (Véase figura siguiente, SALIR
Ctrl + X)
NegotiatePosition. Establece un valor que determina si los controles Menu de nivel superior se
muestran en la barra de menús mientras un objeto vinculado o incrustado de un formulario está activo y
mostrando sus menús. No está disponible en tiempo de ejecución. Puede tener los valores 0
(Predeterminado) Ninguno. El menú no se muestra en la barra de menús cuando el objeto está activo.
1 Izquierda. El menú se muestra a la izquierda de la barra de menús cuando el objeto está activo.
2 Medio. El menú se muestra en medio de la barra de menús cuando el objeto está activo.
3 Derecha. El menú se muestra a la derecha de la barra de menús cuando el objeto está activo.
El menú editado con los datos de la figura anterior tendrá esta forma en tiempo de ejecución :
Observe que tiene cuatro palabras en la línea superior, y que “colgando” de la primera aparece un menú
desplegable. Para conseguir un menú desplegable fíjese en la figura del Editor, en la parte de abajo
donde están todos los Caption de las palabras del menú. Observará que algunas de ellas (&Edicion,
Documentos, Ayuda - Hay otra que no se vé, &Archivo) están completamente a la izquierda de la
ventana, y las demás están separadas de la parte izquierda por unos guiones. Las palabras que están
completamente a la izquierda son las que figurarán en la barra de menú de forma permanente. Las que
están separadas (tabuladas) corresponden a las que aparecerán en los menús desplegables. Como es
lógico, un menú desplegable debe colgar de una palabra de las de la barra de menú. La palabra de la
que cuelga el submenú es la palabra sin tabular inmediatamente anterior.
Para tabular las palabras, hay que hacer click en el botón en forma de flecha a la derecha que está sobre
la ventana de edición:
Situandonos sobre una de las líneas ya editadas, y haciendo click sobre el botón con flecha hacia la
izquierda, quitamos la tabulación.
Los botones con flecha hacia arriba o abajo nos permiten variar el orden de las líneas ya editadas.
Podemos hacer varios niveles de submenús a base de introducir varias tabulaciones. Al comienzo de
este capítulo puede ver un ejemplo de ello.
Otra cosa. Puede verse en el menú desplegado de la figura anterior una línea entre Guardar Como y
SALIR. Esta línea, que solamente tiene efectos estéticos, se logra introduciendo como Caption un
guión ( - ). El editor de menús solamente le permite el guión en una palabra de menú desplegable
(Tabulada). No se olvide de poner el Name incluso para este guión.
Siempre se recomienda poner la orden de SALIR en la primera palabra de la barra de Menú. Bien que
esa primera palabra sea Salir o que esté en el menú desplegable de la primera palabra de la Barra de
Menú, en este último caso, separada mediante una línea separadora. (Vea The Windows Interface
Guidelines for Software Design, pág.124)
POPUP MENÚS
Para crear un menú emergente o PopUpMenu es necesario tener en el menú de ese formulario una
palabra de la que se despliegue un submenú con las mismas palabras que queremos que aparezcan en el
PopUpMenu.
Por ejemplo, en el menú que hemos editado como ejemplo anteriormente, existía una palabra Edición,
de la que se desplegaba el Submenú Copiar, Cortar y Pegar. Si analizamos la edición realizada, las
palabras tenían el siguiente Caption y Name :
Edición mnuEdicion NO
Copiar mnuCopiar SI
Cortar mnuCortar SI
Pegar mnuPegar SI
Recuerde que las palabras del menú que estaban tabuladas son las que aparecían en el menú
desplegable. Esas mismas serán las que aparezcan en el PopUpMenu.
Para ello utilizaremos el Método PopupMenu. Este método pesenta un menú emergente en un
objeto MDIForm o Form en la posición actual del mouse o en las coordenadas especificadas.
Para combinar la los valores de indics de posición y comportamiento basta con sumar los
valores.
Nota El parámetro indics no tiene efecto en las aplicaciones que se ejecuten bajo Microsoft
Windows versión 3.0 o anteriores.
negrita. Parámetro opcional que especifica el nombre de un control menú del menú emergente
para presentar su título en negrita. Si se omite, ningún control del menú aparece en negrita.
Nota. Este argumento funciona solamente en las aplicaciones que se ejecuten bajo Windows
95. La aplicación ignora este argumento cuando se ejecute bajo versiones de 16 bits de
Windows o Windows NT 3.51 y anteriores.
La forma mas habitual de presentar un menú emergente es mediante el botón derecho del
ratón. Concretamente en el editor Word que estoy utilizando para escribir estos apuntes ocurre
cuando se levanta el botón derecho del ratón. Es decir, en el evento MouseUp, con la
condición de que Button=2. Imaginemos que queremos presentar en un menú emergente las
palabras Cortar, Copiar y Pegar del ejemplo anterior, que colgaban en el menú de la palabra
Edición. (Vea mas atrás). Queremos también que la palabra Copiar salga resaltada en negrita.
Iríamos al procedimiento MouseUp del Formulario y escribiríamos :
Observe que se ha omitido el nombre del Formulario (Opcional), el parámetro indics, y las
coordenadas x e y (el menú emergente aparecerá donde esté situado el cursor del ratón).
Observe también que las comas separadoras de los parámetros hay que colocarlas.
Si queremos presentar un menú emergente es necesario tener ese menú en el menú del
formulario. El problema puede ser que no queramos que esté en la barra de menú. No es
problema. Ponga la propiedad Visible del elemento del menú desde el que se va a desplegar el
submenú a False. No se verá ya en la barra de menú ni esa palabra ni el submenú que de ella
se despliega. Eso sí, las palabras del submenú deben tener la propiedad Visible = True. Si les
pone esa propiedad a False, no se verán en el menú emergente. Puede aprovechar esta
circunstancia para hacer menús emergentes con mas o menos elementos, según las
necesidades de su aplicación. También puede poner la propiedad Enabled a False si así lo
precisa. No puede activar la propiedad WindowList en un elemento que forma parte de un
menú emergente.
Ejercicio propuesto: un editor de texto, con un menú que diga Copiar | Cortar | Pegar
donde pueda utilizar el portapapeles. Ahora es posible que le cueste un poco de
trabajo. Pero este ejercicio lo va a repetir con casi todas las aplicaciones que realice en
su vida profesional.
- Secuenciales
- Aleatorios (Random)
- Binarios
FICHEROS SECUENCIALES
Para abrir un fichero secuencial para guardar información, debemos ejecutar la siguiente
instrucción:
Numerocanal debe ser un número comprendido entre 1 y 255. Representa el número del canal
por donde introduciremos los datos. Normalmente se llama también número de archivo. No
puede haber mas de un archivo abierto con un número de canal determinado.
Close # Numerocanal
Si no se especifica Numerocanal (la instrucción Close a secas) se cerrarán todos los ficheros
abiertos actualmente.
Para introducir la información, pueden emplearse dos métodos: Print y Write Nos
centraremos de momento solamente en Print.
Print
Introduce la información de forma secuencial, byte tras byte tal como se
comentó. Sirve para guardar textos. Por ejemplo si deseamos guardar en ese
fichero el contenido de una caja de texto llamada Text1, basta con ejecutar la
siguiente instrucción:
Cuando los datos se han introducido con la instrucción Print, la forma de acceder a ese texto
es muy sencilla:
Se utiliza la instrucción Input o Line Input para sacar los datos del fichero.
Verá un poco mas adelante la propiedad LOF de los ficheros secuenciales. LOF devuelve un
valor igual a la longitud total del fichero. Si ejecutamos la instrucción :
Para no emplear una palabra tan larga como Numerodecanal, utilizaremos de ahora en
adelante el número 1 como número de canal. Recuerde que ese número puede ser un número
comprendido entre el 1 y el 255
leeremos el fichero de una única vez. Este procedimiento puede ahorrarle cierto tiempo a la
hora de la lectura. (Por lo dicho anteriormente, este procedimiento de leer todo el fichero de un
golpe solamente se podrá hacer si la longitud del fichero (LOF(1)) es inferior a 65.534 bytes.
Tenga esto presente cuando vaya a leer un fichero que no sabe que longitud tiene. Para
curarse en salud, le recomiendo que lea los ficheros secuenciales carácter a carácter.
Input(1,#X) pues tarda poco mas que leyéndolo de un golpe. Claro que para leer un fichero
carácter a carácter debe poner un bucle en el que se van leyendo los caracteres hasta que
llaga al final del fichero..
Do Until EOF(1) El programa ejecutará este bucle hasta que se cumpla que EOF(1) sea
True. EOF(NumerodeCanal) es una propiedad de los ficheros
cuando están abiertos, que es True cuando la posición del puntero de
lectura apunta al carácter Fin De Fichero (End Of File) que es el
carácter siguiente al último carácter de texto de ese fichero.
A = Input (1, #1) Estrae un carácter del fichero abierto por el canal número 1. Al
leer este carácter, el puntero de lectura avanza
automáticamente tantos caracteres como haya leído, colocándose sobre
el primer carácter no leído.
MiVariable = MiVariable & A Con esta línea vamos anexando el carácter leído a la variable
MiVariable. En vez de utilizar una variable, podríamos
poner ese texto sobre un TextoBox o un RichTextBox (RTB) (Lo
verá proximamente). Si lo ponemos en un RTB esta línea
sería:
RTB.SelText = A
Sólo se utilizará la instrucción Input con archivos abiertos en modo Input o Binary. (Los
Binary Se verán a continuación) A diferencia de la instrucción Input #, (la veremos a
continuación, pues es la instrucción que deberá utilizar para leer archivos escritos con la
instrucción Write ), Input devuelve todos los caracteres que lee, incluyendo puntos y coma,
retornos de carro, avances de línea, comillas y espacios iniciales.
Nota Se proporciona otra función (InputB) para su uso con datos de byte incluidos en archivos
de texto. En lugar de especificar el número de caracteres a devolver, número especifica el
número de bytes.
La instrucción Line Input se utiliza para extraer una línea completa. Tiene la siguiente
sintaxis:
Mediante esta instrucción se extrae una línea completa (los caracteres delimitados entre dos
retornos de carro), y se le asigna esa cadena a VariableString
Una línea es un trozo de texto delimitado por los caracteres 13 (Retorno de carro) y 10 (Avance
de línea) La instrucción Line Input # lee un carácter cada vez en un archivo hasta que
encuentra un retorno de carro (Chr(13)) o una secuencia retorno de carro - avance de línea
(Chr(13) + Chr(10)). Las secuencias de retorno de carro - avance de línea no se tienen en
cuenta y no se añaden a la cadena de caracteres extraída mediante esta instrucción. Cuando
lea un archivo mediante Line Input # y lo quiere presentar en un TextBox o en el Printer,
deberá introducirlos para evitar que le salga todo el texto en una única línea.
Con lo que se ha explicado, ya puede realizar un pequeño edito de textos. Este sencillo editor
tiene un TextBox llamado TBTexto, donde podemos escribir el texto que queramos (con la
propiedad MultiLine = True y ScrollBars = Vertical), tres botones llamados BAbrir (1), (2 a y b)
y (3), para abrir el fichero y poner su contenido en TBTexto, y dos BGuardar para guardar,
uno abriendo el fichero For Append y el otro abriéndolo For Output. En este último, al guardar
el texto borramos el contenido que del fichero, si ya existiera. En el primero, lo anexamos al
final del mismo . Para conocer el nombre del fichero, ponemos un CommonDialog llamado
CD1, con un filtro CD1.Filter = “Ficheros de Texto |*.txt”
Para abrir el fichero, y depositar su contenido en TBTexto pondremos este código en BAbrir :
Cuando el fichero es mayor de 64 kilobytes, el leerlo de un golpe puede dar problemas. Mejor
dicho, no funciona. Deberemos leerlo en ese caso, carácter a carácter o línea a línea.
INSTRUCCIÓN WRITE
La otra forma de introducir datos en un fichero secuencial es Write. Mediante esta instrucción,
no se puede introducir texto, sino datos, aunque esos datos sean de texto. Mediante la
instrucción Write puede crear una pequeña base de datos. Eso sí, una base de datos de
escasa calidad. Pero no es momento de cuestionar la calidad de esta base de datos, sino de
explicarla.
Lo que estamos haciendo en realidad al escribir datos mediante la instrucción Write es escribir
estos datos en un fichero secuencial, utilizando una coma como separador entre los distintos
datos. Un fichero secuencial con datos introducidos mediante la instrucción Write tendrá esta
forma :
Observe que el contenido de este fichero se diferencia algo de una simple sucesión de
caracteres. Tiene varias partes separadas por una coma. Cada una de ellas es un dato.
Imaginemos una aplicación, con la que introducimos datos de libros. La aplicación es un único
formulario donde podemos introducir la información mediante varios TextBox. Existe un Botón
de comando con el siguiente código:
Si introduce los datos del ejemplo anterior, se creará un fichero que tiene esta apariencia:
Si realizamos otra introducción de datos, con la segunda línea del ejemplo anterior, los datos
existentes en ese fichero no se borrarán, dado que lo hemos abierto con Append. Quedará de
la siguiente forma:
¿Que pasaría si leemos este fichero con la instrucción Input o Line Input # vista
anteriormente ? Pues simplemente que lo leeríamos tal como está, con sus comas y comillas
dobles. No sería la forma mas adecuada, ya que lo que queremos es obtener sucesivos datos
Para sacar estos datos debemos leerlos con la instrucción Input #. Esta instrucción saca los
datos que hemos metido, es decir, elimina las comillas y la coma que servían de separadores.
En nuestro ejemplo, podríamos hacerlo de esta forma, aprovechando los mismos TextBox:
'Abrimos el fichero
LIBRETBTITULO.Text = TIT
LIBRETBAUTOR.Text = AUT
LIBRETBEDITORIAL.Text = NED
LIBRETBPREC.Text = PRE
LIBRETBEX.Text = EXS
' Refrescamos los TextBox
Refresh
Loop
Close #1
End Sub
Observe que los nombre de las variables en el proceso de lectura tienen distintos nombres a
los utilizados durante la escritura. Como en el fichero no se guarda ninguna referencia al
respecto, lo único que importa es que el número de variables para la lectura sea igual al
número de variables que se utilizó para la escritura, y que el orden de las variables sea
correcto. Si por ejemplo, el número de variables usadas en la introducción de datos fuese
distinto al número de variables usado en la lectura, Visual Basic nos daría un error.
Observe también que se ha introducido un temporizador. Sirve para ver los sucesivos títulos
que tenemos en nuestra base de datos tan sui generis. Se ha pretendido presentar unos datos
de esta pequeña base de datos sin complicarnos mucho la vida. Mas adelante veremos como
presentarlos de una forma correcta. Este tipo de ficheros no es el mas adecuado para construir
bases de datos, es complicado manejar ficheros de mas de unas pocas líneas, por lo que para
tener muchos datos, este tipo de ficheros no es recomendable. No es aconsejable ni para
muchos datos ni para pocos datos. Pero es obligación del autor explicarlo … y de advertirlo.
En esta pequeña aplicación mostrábamos las informaciones sucesivamente según las íbamos
leyendo del fichero. Interrumpíamos momentáneamente la lectura para presentar cada
Pero volvamos a nuestra aplicación de librería, donde hemos hecho una variación, los campos
PRECIO y EXISTENCIAS los vamos a tratar ahora como números (Integer). También se ha
introducido otro campo tipo texto, para introducir la edición. El fichero creado tras introducir
varios libros quedó de la siguiente forma :
Observe que los dos últimos campos (correspondientes a PRECIO y EXISTENCIAS) no están
entre comillas, pues no se trata de texto, sino de números.
(Observe también la gran ventaja de tener una base de datos con datos completamente
legibles)
Se han introducido alguna variaciones en el programa. Entre ellas, se crea una matriz de
variables de 5 x n (n=número de registros). También se le añaden unos botones para poder
recorre toda la base de datos.
Donde las variables TITULO, AUTOR, EDITORIAL y EDICION serán del tipo String y PRECIO
y EXISTENCIAS serán de tipo numérico. Podemos saberlo, ya que en una línea cualquiera del
fichero anterior :
los cuatro primeros parámetros van entre comillas dobles, ya que son datos tipo texto, y los
dos últimos van sin comillas, como corresponde a datos numéricos.
Pero existe otra forma mejor (al menos un poco mas complicada) para leer estos datos, y evitar
el problema que teníamos antes de tener que hacer un temporizador para poder ver, aunque
solo sea un momento, los datos. Podemos meter los datos a una matriz que tenga tantas
columnas como datos (campos) (en el ejemplo 6, cuatro String y dos numéricos), y tantas filas
como apuntes (registros) tengamos.
Para ello, antes de nada debemos definir un tipo de variable, mediante la instrucción Type.
Parece que no nos basta con los tipos de variable que trae VB (Long, String, Integer ...) y es
cierto. La variable que necesitamos no es de ningún tipo de esos. Está compuesta por varias
secciones, y cada una de ellas puede ser de un tipo distinto. Este tipo de definiciones debe
hacerse en un módulo (Véase instrucción Type en la ayuda de VB), por lo que se ha creado un
módulo llamado LIBREMD2.BAS, con el siguiente código :
Type REGISTROLIBRO
AUTOR As String
TITULO As String
EDITORIAL As String
Esto es lo que se llama DEFINIR una variable. Es como hacerse un traje a medida. Y dese
cuenta de que es un tipo de variable (REGISTROLIBRO) formado por varias partes (AUTOR,
TITULO, Etc.) Observe que DEFINIR una variable NO es lo mismo que DECLARAR
En el apartado de Declaraciones del General del formulario declaramos las variables, entre
ellas una variable, REGISTROLIBROS, de la cual decimos que va a ser una matriz mediante
los paréntesis que la acompañan, y además declaramos que va a se similar a
REGISTROLIBRO ya definida en el módulo.
Dim NR As Integer
Dim REGISTROLIBROS() As REGISTROLIBRO
Dim NRP As Integer
En el ejemplo preparado podemos escribir y leer datos. Veamos de nuevo como se escriben
El código del procedimiento click del botón BIntroducir1 es :
‘(Observe que para introducir el precio y las existencias se transformó el contenido del TextBox
a ‘un valor numérico mediante la instrucción Val.)
Al leer los datos, lo primero es que no conocemos el número de registros existentes. Como
cada registro va en una línea del fichero, si contamos el número de retornos de carro (Chr(13))
obtendremos el número de registros. Esta operación es la primera que se realiza en el
procedimiento click del botón LEER :
‘Leemos los registros del 1 al NR, y cada campo del fichero lo vamos asignando a los
elementos ‘que componen la matriz.
For I = 1 To NR
Input #1, REGISTROLIBROS(I).TITULO
Input #1, REGISTROLIBROS(I).AUTOR
Input #1, REGISTROLIBROS(I).EDITORIAL
Input #1, REGISTROLIBROS(I).EDICION
Input #1, REGISTROLIBROS(I).PRECIO
Input #1, REGISTROLIBROS(I).EXISTENCIAS
Next I
Close #1
‘NRP = Número del Registro Presentado. Lo hacemos igual a 1 para presentar, de momento, el
‘número 1
NRP = 1
LIBRETBTITULO.Text = REGISTROLIBROS(NRP).TITULO
LIBRETBAUTOR.Text = REGISTROLIBROS(NRP).AUTOR
LIBRETBEDITORIAL.Text = REGISTROLIBROS(NRP).EDITORIAL
LIBRETBEDICION.Text = REGISTROLIBROS(NRP).EDICION
LIBRETBPREC.Text = REGISTROLIBROS(NRP).PRECIO
LIBRETBEX.Text = REGISTROLIBROS(NRP).EXISTENCIAS
End Sub
Observe que es muy sencillo trabajar con ficheros secuenciales para bases de datos. El
inconveniente es la cantidad de memoria necesaria para la matriz de datos. También tienen un
gran inconveniente: solamente se pueden leer de hacia adelante. Un fichero secuencial hay
que leerlo de una sola vez. Esto que no es ningún inconveniente para un fichero de texto, o
incluso para un fichero de datos que leemos una única vez, es un problema cuando leemos un
dato situado en un punto de ese fichero, y luego tenemos que leer un dato colocado en una
posición anterior. En este caso, deberemos terminar de leerlo, y volver a comenzar por el
principio. Este es un gran inconveniente para hacer bases de datos con ficheros secuenciales.
No solamente hemos visto una forma de crear una base de datos, sino que hemos visto como
movernos a lo largo de los registros. Verá esto con mucho mas detalle cuando demos bases de
datos. Este tipo de bases de datos son la Tipo Texto que verá mas adelante.
EOF
Indica el fin del fichero (End Of File). EOF será False hasta que encuentre el final del
fichero secuencial. Habrá observado instrucciones tales como:
LOC
Devuelve la posición de lectura/escritura actual en un archivo abierto.
LOF
Devuelve la longitud de un fichero (Lenght Of File) abierto mediante Open.
Sintaxis LOF(numeroarchivo)
SEEK
Devuelve la posición actual de lectura/escritura de un archivo abierto con la
instrucción Open.
Sintaxis Seek(númeroArchivo)
Seek devuelve un valor entre 1 y 2,147,483,647 (equivalente a 2^31-1) inclusive. Para archivos
abiertos en modo Output, Append o Input, Seek devuelve la posición de byte en la que se va a
ejecutar la siguiente operación. El primer byte de un archivo está en la posición 1, el segundo
en la posición 2 y así sucesivamente.
FICHEROS ALEATORIOS (RANDOM)
Un fichero aleatorio es un conjunto de registros, todos ellos de la misma longitud, que nos
permite guardar varias colecciones de datos.
Tal como habíamos visto en los ficheros secuenciales, el almacenar una serie de colecciones
de datos en un fichero secuencial era muy fácil, pero bastante difícil de almacenarlas en la
memoria cuando el número de estas colecciones superaba una cierta cantidad.
Los ficheros aleatorios permiten almacenar información en registros que pueden ser fácilmente
leídos, pudiendo leer los registros uno a uno, sin necesidad de leerlos todos. En los archivos
secuenciales, la información de varios datos podíamos introducirla mediante la instrucción
Write, y conseguíamos un registro de la forma: (Recuérdese el ejemplo de los datos de un
libro)
"Título","Autor","Editorial","Edición","Precio","Existencias"
Los ficheros aleatorios nos permiten guardar una información similar a la anterior, referida a
cualquier número de libros, y para leerla no es necesario leer todo el fichero, sino simplemente
acceder a los registros que nos interesen. También permite realizar el cambio de un registro de
una forma sencilla, sin alterar los demás.
Todo esto tiene un precio: En los archivos secuenciales, podíamos introducir informaciones de
cualquier longitud. En los archivos aleatorios cada dato tiene una longitud asignada, longitud
que no se puede sobrepasar, y si la información que debemos almacenar tiene menos que la
longitud preestablecida, perderemos esa capacidad sobrante.
Un fichero aleatorio (Random), una vez abierto, puede utilizarse para leer o escribir datos.
Para escribir datos en un fichero aleatorio, primero debemos definir el registro, es decir, en el
caso de los libros visto anteriormente, un registro va almacenar los datos del titulo, autor,
editorial, etc. Para "saber" como se colocan estos datos dentro del registro será necesario
definirlo.
En realidad lo que vamos a hacer es definir una variable de las denominadas “Definidas por el
usuario” Con esto lo que hacemos es crear un nuevo tipo de variable, en el ejemplo, la variable
tipo Registro. Se debbe hacer en la sección de declaraciones de un módulo o de un
formulario, y siempre antes de declarar una variable como variable de ese tipo. (La definición
de la variable debe hacerse en la sección de declaraciones del módulo o formulario con ámbito
suficiente para que sea válida en todas las partes de la aplicación donde se necesite esa
variable)
Type Registro
Titulo As String * 30 Asignamos 30 caracteres para el título
Autor As String * 30 Otros 30 para el autor
Editorial As String * 15 Asignamos 15 caracteres para editorial
Edición As String * 6 Asignamos 6 caracteres para Edición
Precio As String * 4 Asignamos 4 caracteres para el precio
Existencias As String * 3 Tres caracteres para existencias.
End Type
Observe la diferencia de esta definición con la que hacíamos en los ficheros secuenciales. En
aquellos no poníamos la longitud de cada sección. Aquí es necesario, ya que la longitud de un
registro (y también de cada campo) es fija.
Ya una vez definido como es Registro, podemos decir que una variable es del tipo definido
para Registro, es decir :
Entonces MiVariable ya puede almacenar los datos de Titulo, Autor, Editorial, Edición, Precio y
Existencias, colocando un dato tras otro, sin ninguna separación, en el mismo orden que lo
habíamos definido para Registro. No es necesario utilizar separaciones ya que el programa
sabe que longitud tiene cada dato y el orden de colocación. Lo sabe porque se lo hemos dicho
al definir Registro. Si los datos a introducir son :
el registro correspondiente a este libro tendrá una forma mas o menos así :
Imagínese que introducimos otro libro. El primer campo de ese nuevo registro se colocará en el
fichero inmediatamente después del último campo existente, sin ningún tipo de separación. No
hace falta esa separación, pues VB conoce donde finaliza un registro y comienza otro, ya que
la longitud total del registro se le ha introducido en la instrucción para abrir el fichero, que
repetimos aquí por comodidad :
(Si se omite el dato LongitudRegistro VB colocará la longitud por defecto, 128 caracteres)
LongitudRegistro deberá ser igual o mayor que la suma de caracteres de cada uno de los
campos declarados en la instrucción Type, que también reproducimos :
Type Registro
Titulo As String * 30 Asignamos 30 caracteres para el título
Autor As String * 30 Otros 30 para el autor
Editorial As String * 15 Asignamos 15 caracteres para editorial
Edición As String * 6 Asignamos 6 caracteres para Edición
Precio As String * 4 Asignamos 4 caracteres para el precio
Existencias As String * 3 Tres caracteres para existencias.
End Type
¿Deberá ser igual o mayor, o estrictamente igual ? La respuesta es igual o mayor. Pero si
declaramos en la instrucción Open que el registro es mayor que la suma de los caracteres de
todos los campos que lo componen, estaremos perdiendo espacio de disco duro, tanto como la
diferencia entre lo declarado en la instrucción Open menos la suma de los caracteres de cada
uno de los campos. Y esa cifra, multiplicada por el número de registros existentes. Por lo tanto
debe declararse en la instrucción Open exactamente la suma de los caracteres de todos los
campos. En el ejemplo anterior era bastante fácil, ya que todos los campos eran del tipo String
(cadena de caracteres) y cada carácter ocupa un byte. La cosa se complica si uno o varios de
los campos son de tipo numérico, porque nos obligará a recordar cuantos bytes ocupa un
integer, un Long, etc. Si hubiésemos declarado en la instrucción Type Precio as Long,
Existencias as Integer deberíamos tener en cuenta que un Long (número entero entre
-2.147.483.648 y 2.147.483.647, inclusive) ocupa 4 bytes y un integer (número entero entre
-32.768 y 32.767) ocupa 2 bytes. Se reproduce a continuación la longitud en Bytes de cada uno
de los tipos de datos. No se moleste en aprendérsela de memoria, pues en la Ayuda de Visual
Basic puede encontrarla como Resumen de tipos de datos. Pero fíjese en algo tan curioso
como que un dato tipo Boolean que solamente puede tomar 2 valores (Sí / No) ocupa 2 bytes
frente a un dato tipo Byte, que puede tomar 256 valores y ocupa solamente un Byte.
Ya le estoy viendo tomando buena nota de cuanto ocupa cada variable. Y echando números
para saber la longitud exacta de la variable y no perder ningún byte del disco duro
desaprovechándolo sin información. ¿No habrá algo que nos lo facilite ? Sí, lógicamente. La
instrucción LEN
LEN nos da la longitud de un texto (Var=Len (“Hola que tal”), o de una variable
Var=Len(Variable)
Podemos usarla para conocer la longitud total de la variable MiVariable
Ni que decir tiene que si se abre ese fichero en varias partes del programa con distintas
instrucciones Open el valor de LongitudRegistro debe ser igual en todas ellas.
A partir de este momento, el programa sabe que REGLIBROS es una variable que tiene 6
campos (Titulo, Autor, Editorial, Edición, Precio y Existencias) y que cada campo tiene los
caracteres especificados en la instrucción Type.
Ahora nos cabe una pregunta ¿Cuantos registros tiene un fichero Random? La respuesta es
sencilla. Basta conocer la longitud del fichero mediante la instrucción LOF si el fichero ya está
abierto, o con la instrucción FILELEN si no lo está, y dividir el dato obtenido con cualquiera de
las dos instrucciones anteriores por el valor LongitudRegistro.
Una vez abierto el fichero Random podemos leer y escribir datos en él. Para escribir datos
utilizaremos la instrucción Put. La sintaxis de Put es la siguiente:
Type Registro
Titulo As String * 30 Asignamos 30 caracteres para el título
Autor As String * 30 Otros 30 para el autor
Editorial As String * 15 Asignamos 15 caracteres para editorial
Edición As String * 6 Asignamos 6 caracteres para Edición
Precio As String * 4 Asignamos 4 caracteres para el precio
Existencias As String * 3 Tres caracteres para existencias.
End Type
Luego REGLIBROS tendrá 6 campos (Titulo, Autor, Editorial, Edición, Precio y Existencias),
cada uno de una longitud determinada, la definida en la instrucción Type.
Antes de introducir REGLIBROS en el fichero habrá que decir que valor tiene. Pero cada
campo que lo compone tendrá un valor. Podríamos hacer una aplicación en la que, a través de
varios TextBox, le introdujésemos los valores de los campos, y el número de registro en el cual
queremos escribir. El nombre de cada uno de los TextBox para cada uno de los datos es el
siguiente :
La aplicación deberá introducir en cada campo el valor (string) existente en cada uno de esos
TextBox. El valor de la variable REGLIBROS lo compondremos de la siguiente forma :
REGLIBROS.Titulo = TBTITULO.Text
REGLIBROS.Autor = TBAUTOR.Text
REGLIBROS.Editorial = TBEDITORIAL.Text
REGLIBROS.Edicion = TBEDICION.Text
REGLIBROS.Precio = TBPRECIO.Text
REGLIBROS.Existencias = TBEXISTENCIAS.Text
De esta forma, REGLIBROS ya tiene un valor que se puede escribir en el fichero mediante la
instrucción Put.
Observe que ya se le han introducido otros controles (TextBox para introducir el nombre del
fichero, otro para el número de registro, botones para leer, escribir, abrir el fichero
(EXAMINAR), un par de botones para subir o bajar el número de registro y un botón para salir
de la aplicación. El TextBox para introducir el nombre del fichero se llama
TBNOMBREFICHERO y en el que debemos introducir el número de registro a leer o escribir
TBLEERESCR.
El Botón EXAMINAR cierra cualquier fichero que pudiese estar abierto, abre el fichero indicado
en TBNOMBREFICHERO, calcula el número de registros y escribe este número en el Label
con nombre LNUMFICH. El código de su procedimiento Click es el siguiente :
Veamos como se escribe un registro. Analicemos el código del procedimiento click del botón
ESCRIBIR
Para leer los datos de un fichero Random utilizaremos la instrucción Get. Su sintaxis es la
siguiente :
Puede omitirse NumeroRegistro. En este caso, el número de registro que se leerá será el
siguiente al último registro leído.
La Instrucción Get leerá un registro completo. Ese registro contendrá varios campos, y
seguramente nos interesará conocer el valor de cada campo dentro de ese registro. Variable
es una variable que contendrá todos los campos. En nuestra aplicación de biblioteca, Variable
tiene por nombre REGISTROLIBRO (El mismo que tenía para la instrucción Put de escribir. Es
pura comodidad del programador. Puede tener cualquier otro nombre)
Para obtener el contenido de cada campo, realizaremos un proceso similar al empleado para la
escritura, pero al revés. En nuestra aplicación, pretendemos poner el contenido de cada
campo en los mismos TextBox que se utilizaron para escribirlos. Veamos el contenido del
procedimiento click del botón LEER
En los ficheros Random tienen especial importancia las funciones Seek y Loc.
Mediante la Función Loc podemos conocer la el último registro manipulado, bien por lectura,
escritura. Si abrimos el fichero y no se ha hecho ninguna lectura o escritura de registros, el
número devuelto por la función Loc es 0.
Variable tomará un valor igual al número del registro escrito o leído por última vez.
Mediante la Función Seek podemos conocer el próximo registro que será manipulado en una
operación de lectura o escritura. Si abrimos el fichero y no se ha hecho aún ninguna operación
de lectura o escritura, Seek devuelve el valor 1.
Instrucción Seek
Sintaxis
EOF
Devuelve un valor que indica si se ha llegado al final de un archivo.
Sintaxis EOF(Numerocanal)
En archivos Random, EOF devuelve False hasta que se haya ejecutado una instrucción Get y
no haya podido leer el registro completo, en cuyo caso devolverá True.
La función EOF no suele emplearse en ficheros Random, ya que en estos nos movemos a
base de registros, y es muy fácil controlar cuantos registros existen en el fichero y en que
registro nos encontramos o nos vamos a mover, mediante las funciones LOF, LOC y SEEK
Recuerde Para obtener la longitud de un archivo que no está abierto utilíce la función
FileLen.
Un fichero binario es una sucesión de bytes, uno tras otro, que puede almacenar cualquier tipo
de información. Cuando se explicaban los ficheros secuenciales, decíamos que eran los mas
adecuados para introducir información de un texto, con los Random podíamos realizar una
base de datos de forma sencilla, a base de controlar los registros y sus campos. Con un
fichero binario podemos almacenar cualquier información. (texto y cualquier tipo de datos) .
Como siempre en VB, Numerocanal puede ser un número comprendido entre 1 y 255, que
define ea fichero. No pueden existir al mismo tiempo 2 ficheros abiertos con el mismo Numero
de canal.
Una vez abierto un fichero se binario, podemos leer o escribir datos en él.
Para escribir uno o varios caracteres en un fichero binario, usaremos la instrucción Put.
y en este caso siempre escribirá el número de bytes declarado. ¡ Cuidado ! Si los datos a
introducir sobrepasan el número de bytes declarados para Variable, los datos sobrantes no se
escribirán en el fichero. Si los datos a escribir en el fichero fuesen menos que los declarados
para Variable, la diferencia se rellenará con el byte nulo ( 0 ).
Pero en este caso debemos volver a decir ¡ Cuidado !, si no conocemos el número de bytes
que vamos a escribir, puede que “machaquemos” parte de la información que ya tenemos en el
fichero, pues la instrucción Put va a colocar los bytes que componen Variable en la posición
especificada por Posición y siguientes, hasta que quepa toda la cadena de bytes que le
queremos introducir. Si la posición en la que introducimos esos bytes es una posición
intermedia, y no controlamos bien el número de bytes a introducir y las informaciones que ya
existen en el fichero en las posiciones colindantes con las que vamos a introducir los datos, es
muy probable que perdamos esa información al introducir la nueva.
¿Qué pasará si el dato Posición indica una posición mayor que la que tiene realmente el
fichero ? Simplemente que rellenará las posiciones intermedias que se formarán con un byte
(puede verlo con el Block de Notas ÿÿÿ)
Un fichero binario es, como se decía al principio, una sucesión de bytes, que no tiene ningún
tipo de separación entre bytes. Cada dato (byte o conjunto de bytes) que introduzcamos en un
fichero binario se escribirá en el fichero tal y como se introducen. Machacando información ya
existente si no se controla bien donde se mete. Por lo tanto, aunque parece que los ficheros
binarios pueden ser mucho mas versátiles que los secuenciales y los Random, exigen mucho
mas control que los anteriores. Limite el uso de este tipo de ficheros a las aplicaciones en que
sea realmente imprescindible.
Donde Posición es el número del primer byte leído, que como en el caso de Put, si se omite,
tomará como valor el byte siguiente al usado en la última instrucción Get. Variable es el
nombre de una variable que contendrá los datos leídos.
Pueden leerse uno o varios Bytes, pero ahora surge un pequeño problema. ¿Como le decimos
cuantos bytes debe leer ? Sencillamente los especificados al declarar la variable . Imaginemos
que la declaración de la variable fue :
Con Variable declarada de esta forma, leerá 10 bytes a partir del byte Posición. (Incluido)
De esta forma, siempre leeremos un número determinado de caracteres (10 en el caso del
ejemplo). Esta es una limitación para el uso de Get. Esta función viene muy bien en aquellas
aplicaciones en las que tenemos que extraer un número fijo de bytes (En la práctica, en la
mayor parte de las aplicaciones se extrae de uno en uno)
Pero pueden existir aplicaciones en las que sea necesario leer una vez un número de
caracteres, y otra vez otro. Ese problema lo tenemos resuelto con la instrucción Input.
Mediante la instrucción Input podemos leer el número de caracteres que queramos, pero no
podemos controlar el byte de comienzo. Por lo tanto deberemos ayudarnos de la instrucción
Seek para posicionar el puntero de lectura encima del primer byte que queramos leer :
Por ejemplo, si queremos leer 35 bytes de un archivo binario, abierto con el número de canal 1,
comenzando por el byte 48 (el 48 será el primer byte leído), ejecutaremos las dos siguientes
instrucciones :
Seek (1), 48
Variable = Input (35, #1)
Al igual que se hizo para los ficheros secuenciales y Random, vamos a ver con un ejemplo
Con el botón ABRIR se abre el fichero deseado. Si no existe en el disco, lo crea, ya que la
instrucción
Open Nombrefichero For Binary As # Numerocanal
intenta abrir un fichero existente llamado Nombrefichero, y si este no existe, lo crea. Si existe el
fichero, presenta todo su contenido en el TextBox inferior, para poder tener una referencia de
que lugar ocupan los distintos caracteres (un fichero binario puede guardar cualquier byte, por
lo que si abre un fichero generado con cualquier programa puede ser que muchos de los bytes
no contengan información de un carácter, por lo que le recomendamos haga esta práctica con
un fichero creado por la misma práctica)
El botón CERRAR cierra el fichero. SALIR sale de la aplicación. El TextBox superior (variable)
sirve para introducir la variable a escribir en el fichero, o para presentar la variable leída en
caso de lectura . El TextBox posición sirve para indicar la posición del primer byte. Posición por
defecto presenta la posición que se extrae mediante la función Seek cada vez que se hace una
lectura o escritura en el fichero. Longitud de la variable permite introducir esa longitud, para
leer mediante la instrucción Input.
El botón ESCRIBIR escribe el dato Variable en el fichero, LEER (GET) lee un único byte, y
LEER (INPUT) lee una cadena de caracteres, de longitud la especificada en el TextBox
Longitud de la variable.
Se enumera a continuación el código de cada uno, dejando para las explicaciones del profesor
en clase, o el estudio del alumno, la interpretación de cada una de susu partes.
FORMULARIO. DECLARACIONES
Option Explicit ‘Obliga a declarar todas las variables
Dim LONGVAR As Integer ‘Se declara la variable LONGVAR
Dim COMIENZA As Long ‘Se declara la variable COMIENZA
Dim TESTO As String ‘Se declara la variable TESTO
Dim pospordef As Long ‘Se declara la variable pospordef
BOTON CERRAR
Private Sub BCERRAR_Click()
Close ‘La instrucción Close cierra todos los ficheros abiertos
End Sub
BOTON ESCRIBIR
Private Sub BESCRIBIR_Click()
TBVAR2.BackColor = RGB(255, 0, 0) ‘Pone el TB long. De la variable en rojo
Dim escribe As String ‘Declara la variable escribe como string, sin limitación
escribe = TBVAR1.Text ‘Pasa el contenido de TBVAR a la variable escribe
Put #1, Val(TBVAR3), escribe ‘Instrucción Put. Val(TBVAR3) es la posición del 1er
byte
pospordef = Seek(1) ‘Analiza donde quedó el puntero del fichero
Lvar5 = Str(pospordef) ‘y pone este valor en la etiqueta Lvar5
End Sub
BOTON SALIR
Private Sub BSALIR_Click()
End ‘Sale de la aplicación.
End Sub
DIR
Devuelve el nombre de un archivo, directorio o carpeta que concuerda con el patrón o atributo
de archivo especificado o la etiqueta de volumen de una unidad de disco.
vbNormal 0 Normal.
vbHidden 2 Oculto.
vbSystem 4 Sistema
vbVolume 8 Etiqueta de volumen; si se especifica se ignoran todos los atributos
vbDirectory 16 Directorio o carpeta.
En Microsoft Windows, Dir permite el empleo de los caracteres comodín '*' (múltiples
caracteres) y '?' (un solo carácter) para especificar varios archivos.
La primera vez que se llama a la función Dir se debe especificar el nombreruta, de lo contrario
se produce un error. Si además se especifican atributos de archivo, se debe incluir el
nombreruta.
Dir devuelve el primer nombre de archivo que coincide con el nombreruta. Para obtener más
nombres de archivo que coincidan con el nombreruta, se debe volver a llamar a Dir sin
argumentos.
Cuando no hay más nombres de archivo coincidentes, Dir devuelve una cadena de caracteres
de longitud cero. Cuando se devuelve una cadena de longitud cero, en las siguientes llamadas
se debe especificar nombreruta o se producirá un error. Se puede cambiar el nombreruta sin
haber obtenido todos los nombres de archivo que coinciden con el nombreruta actual. Sin
embargo, no se puede llamar a la función Dir.
FILECOPY
fuente Expresión de cadena que especifica el nombre de un archivo a copiarse puede incluir
el directorio o carpeta y la unidad de disco..
destino Expresión de cadena que especifica el nombre del archivo de destino se puede incluir
el directorio o carpeta y la unidad de disco.
FILEDATATIME
Devuelve una fecha que indica la fecha y hora en que un archivo fue creado o modificado por
última vez.
El argumento con nombre nombreRuta es una expresión de cadena que especifica un nombre
de archivo. Se puede incluir el directorio o carpeta y la unidad de disco.
FILELEN
Si el archivo especificado está abierto cuando se llama la función FileLen, el valor devuelto
representa el último tamaño de ese archivo cuando se guardó la ultima vez en el disco.
Para obtener la longitud de un archivo abierto, utilice la función LOF.
GETATTR
Devuelve un número, que representa los atributos de un archivo, directorio o carpeta o una
etiqueta de volumen.
El argumento con nombre nombreRuta es una expresión de cadena que especifica un nombre
de archivo se puede incluir el directorio o carpeta y la unidad de disco.
Valores devueltos
0 vbNormal Normal.
1 vbReadOnly Sólo lectura.
2 vbHidden Oculto.
4 vbSystem Archivo de sistema.
16 vbDirectory Directorio o carpeta.
32 vbArchive El archivo ha sido modificado después de efectuar la última copia de
seguridad.
Las constantes y valores de atributos son los mismos que para la instrucción GetAttr
FREEFILE
Devuelve el siguiente número de archivo disponible para ser usado con la instrucción Open.
Sintaxis FreeFile[(númerodeintervalo)]
METODOS GRAFICOS
MANEJO DE LA IMPRESORA. El objeto PRINTER
Los métodos gráficos se aplican sobre aquellos objetos que permiten dibujar o escribir sobre ellos. No
son muchos estos objetos. Son el Formulario, el Picture Box y el Printer.
Se denominan métodos gráficas a aquellos métodos que nos permiten dibujar o representar
gráficos ya existentes en un objeto, o que nos permiten analizarlos o borrarlos. Los métodos
gráficos de que dispone Visual Basic son :
METODO Line
Dibuja líneas y rectángulos en un objeto. El objeto puede ser un Formulario, un control PictureBox o
el objeto Printer.
(Con esta sintaxis trazará una línea desde las coordenadas absolutas x1,y1
(origen) a las coordenadas también absolutas x2,y2. El color de la línea será el
especificado en color. Los parámetros BF se explican mas adelante.
(Con esta sintaxis trazará una línea desde las coordenadas absolutas x1,y1
hasta las coordenadas relativas a (x1,y1), x2,y2. Es decir, al poner la palabra
Step (paso) antes de las coordenadas finales, le estamos indicando que las
coordenadas que siguen a Step son relativas. Relativas ¿respecto a quien?.
Relativas a las coordenadas de comienzo de la línea. Esta última expresión
haría lo mismo que la siguiente expresión :
Podemos darnos cuenta por la descripción anterior que la palabra Step (palabra reservada de Visual
Basic) indica que las coordenadas que le siguen son relativas a algo. Veamos la tercera forma de
colocar una línea :
Con esta expresión trazará una línea entre las coordenadas relativas (x1, y1),
y las coordenadas relativas (x2,y2). En principio es fácil entender que (x2,y2)
son relativas al punto (x1,y1), pero ¿Respecto a quién son relativas las coordenadas
(x1,y1) ? Son relativas a la posición del puntero de dibujo en el instante anterior a
ejecutar esta sentencia, es decir al CurrentX, CurrentY que
existiese antes de ejecutar la sentencia.
En estas expresiones, las coordenadas estarán especificadas en las unidades de medida determinadas por
las propiedades ScaleMode o ScaleWidth / ScaleHeight del objeto.
En las expresiones anteriores objeto es el nombre del Formulario, PictureBox u objeto Printer sobre
el que queremos dibujar. Si no se especifica objeto se entiende por defecto que el objeto sobre el que
queremos dibujar es el formulario que tiene el enfoque en ese momento.
Color es el número de color, que se puede poner en cualquiera de las posibles formas que permite VB.
Si no se especifica color, pondrá el color por defecto, que es el valor de la propiedad ForeColor del
objeto.
dibuja un rectángulo relleno del mismo color de la línea (en este caso verde) con esquinas en las
coordenadas (x1,y1) y (x2,y2)
Para dibujar líneas unidas, comience la línea siguiente en el punto final de la línea anterior.
Cuando se ejecuta Line, las propiedades CurrentX y CurrentY toman el valor del punto final de la línea.
METODO Circle
En la expresión anterior, objeto es el objeto donde se dibujará el circulo. Puede ser, como en el caso de
la línea, un Formulario, un PictureBox o el objeto Printer. Si se omite, se asume que se refiere al
Formulario que tenga el enfoque en ese momento.
(x, y) son las coordenadas del punto central del círculo, elipse o arco. Las unidades de medida
vendrán determinadas por las propiedades ScaleMode o ScaleWidth / ScaleHeight del objeto.
Radio Este parámetro es requerido. Indica el radio del círculo, elipse o arco, en el mismo sistema de
unidades de medida.
Color Este parámetro es opcional. Es el número del color, expresado en cualquiera de las formas
aceptadas por Visual Basic. Si se omite, se utiliza el valor de la propiedad ForeColor.
Inicio, Final Valor opcional. Cuando se dibuja un arco o parte de un círculo o elipse, inicio y fin
especifican (en radianes) la posición inicial y final del arco. El rango de ambas es de - 2 pi radianes a 2
pi radianes. El valor predeterminado de inicio es 0 radianes; el de final es de 2 pi radianes.
Aspecto Este valor nos convertirá la circunferencia en una elipse. Recuerde que una elipse es una
circunferencia generalizada. Lógicamente, este parámetro es opcional. El valor predeterminado es 1.0,
lo que crea un círculo perfecto (no elíptico) en la pantalla. Si utilizamos en esta propiedad el valor 2
obtendremos una elipse con el radio horizontal de doble valor que el vertical. Si empleamos 0.5
obtendremos otra elipse, con el radio vertical doble respecto al horizontal.
LSB Visual Basic – Guía del Estudiante Capítulo 1 Página 197
Para rellenar la circunferencia y conseguir un círculo, establezca las propiedades FillColor y FillStyle
del objeto en el que se dibuja el círculo o elipse. Sólo se puede rellenar una figura cerrada. La figuras
cerradas son círculo, elipses o porciones de tipo circular (arcos con líneas de radio dibujadas en ambos
lados).
Los valores de Inicio y Final son siempre positivos. El sentido de cuenta de los ángulos es en sentido
contrario a las agujas del reloj. Si le añadimos a Inicio o a Final un signo menos delante, lo que hace es
dibujar un radio desde el centro del circulo hasta el inicio de la circunferencia (si le hemos puesto un -
a Inicio) o hasta el final del arco (si le hemos puesto un - a Final). Se le pueden poner a ambos y
cerramos la figura. Es decir, si queremos que además de hacer el arco, dibuje los radios, pondremos el
signo menos ( -) delante de Inicio y de Final, a sabiendas que VB siempre entenderá los valores de
Inicio y Final como positivos.
NOTA. No se líe con la información que aporta VB para este tema. ¡¡¡Está mal !!!
Lo expuesto anteriormente NO FUNCIONA para valores de Inicio o Final iguales a 0. Por lo tanto, si
queremos hacer un radio en 0 radianes (línea recta desde el centro a la derecha) deberemos poner una
cantidad muy cercana a 0 (0.00000001, p. e.) para que ponga el radio.
Puede omitir un argumento opcional en medio de la sintaxis, pero debe incluir la coma del argumento
antes de poner el argumento siguiente. Si omite un argumento adicional del final, no utilice comas tras
el último argumento que especifique.
Cuando se ejecuta Circle, las propiedades CurrentX y CurrentY toman el valor del punto central.
METODO Cls
Borra los gráficos y el texto generados en tiempo de ejecución de los controles Form, Image o
PictureBox. Observe que el objeto Printer no tiene método Cls.
Sintaxis objeto.Cls
Donde objeto representa un formulario, control Image o PictureBox. Si objeto se omite, se asume que el
objeto es el control Form que tenga el enfoque.
Cls borra el texto y los gráficos generados en tiempo de ejecución por instrucciones gráficas y de
presentación. Los mapas de bits de fondo definidos usando la propiedad Picture y los controles
dispuestos en un Form en tiempo de diseño no se ven afectados por Cls. Los gráficos y el texto
colocados en controles Form, Image o PictureBox mientras la propiedad AutoRedraw es True no se ven
afectados si AutoRedraw se establece a False antes de llamar a Cls. Es decir, el texto y los gráficos de
los controles Form, Image o PictureBox pueden mantenerse manipulando la propiedad AutoRedraw
del objeto con el que se trabaja.
Después de llamar a Cls, las propiedades CurrentX y CurrentY del objeto se restablecen a 0.
METODO PaintPicture
Presenta el contenido de un archivo gráfico (.BMP, .WMF, .EMF, .ICO o .DIB) en un objeto Form,
PictureBox o Printer. La imagen deberá estar en un Formulario o en un control Image. NO puede
usarse este método para dibujar una imagen contenida en un fichero (Vea mas adelante la función
LoadPicture)
Puede sacar mucho partido a la propiedad PaintPicture. Piense solamente que puede volcar una imagen
.BMP al objeto Printer (a la impresora). Esto puede permitirle dibujar un logotipo en un listado, factura,
etc.
Sintaxis
objeto.PaintPicture Imagen, x1, y1, anchura1, altura1, x2, y2, anchura2, altura2, Opecod
objeto es el objeto donde queremos colocar el gráfico. Es opcional. Si se omite, se asume que el objeto
es el formulario que tenga el enfoque.
x1, y1 Parámetro requerido. Indican las coordenadas destino (eje x y eje y) del objeto en las que se
ponemos el origen de la imagen. Las propiedad ScaleMode, o ScaleWidth y ScaleHeight del objeto
determinan las unidades de medida que se usan. Recuerde que las coordenadas en VB crecen desde la
esquina superior izquierda. Los valores x1 e y1 determinan el punto del objeto donde se colocará la
esquina superior izquierda del gráfico que vamos a introducir.
anchura1 Opcional. Indica la anchura destino de la imagen. La propiedad ScaleMode del objeto
determina las unidades de medida que se usan. Si anchura destino es mayor o menor que la anchura
origen (anchura2), imagen se amplía o se comprime respecto al original. Si se omite, se usa la anchura
origen.
altura1 Opcional. Indica la altura destino de la imagen. La propiedad ScaleMode del objeto
determina las unidades de medida que se usan. Si altura destino es mayor o menor que la altura origen
(altura2), imagen se amplía o se comprime respecto al original. Si se omite, se usa la altura origen.
x2, y2 Parámetro opcional. Indican las coordenadas (eje x y eje y) de la zona de recorte dentro de la
imagen origen. Esto de la zona de recorte significa que podemos cortarle a la imagen origen un trozo,
tanto en sentido vertical como en horizontal.
anchura2 Opcional. Indica la anchura de la imagen origen. Este parámetro se usa para establecer la
relación entre la anchura de la imagen final y la anchura de la imagen origen.
Si al realizar la copia de la imagen a Objeto, esta nos sale muy grande, podemos, bien rebajar el
parámetro anchura1 o aumentar anchura2, puesto que la medida real del ancho de la imagen final
estará en relación directa con el cociente anchura1/anchura2
altura2 Opcional. Lo mismo que para anchura2, pero referido a la altura en este caso.
Opecod Opcional. Valor Long o código que se usa sólo con mapas de bits. Define una operación bit a
bit (por ejemplo, operador Not o Xor) que se realiza sobre imagen al dibujarla sobre objeto. Para
obtener la lista completa de los operadores bit a bit, busque el tema BitBlt en el archivo de Ayuda de
Windows SDK (WIN31WH.HLP).
NOTA Como caso práctico, el autor de este texto suele guardar la imagen en una variable tipo Picture.
Se desconoce el ancho de esa imagen, que vendrá dado por la anchura real del Bitmap. Puede
conocerse la anchura y altura de la imagen consultando la anchura y altura de la variable :
AnchodelaImagen = VariableImagen.Width
AlturadelaImagen = VariableImagen.Height
Muchas veces, la imagen que se introduce en la variable tipo Picture es distinta en una ocasión u otras,
por lo que desconocemos a priori que dato debemos poner a anchura1 y a anchura2. Todo tiene
solución. Con el código siguiente el ancho y alto de la imagen se mantendrá constante
independientemente de la anchura y altura que tenga el Bitmap original.
donde hemos omitido x2, y2 de forma intencionada, pero observe que hemos seguido respetando su
sitio con las comas como separadores. El último parámetro, Opecod se ha omitido, pero como es el
último, no hace falta dejarle las comas.
Pueden omitirse tantos argumentos finales como se desee. Si se omite uno o varios argumentos finales,
no se usan comas a partir del último argumento especificado. Si se quiere especificar un argumento
opcional, se deben especificar todos los argumentos opcionales que aparecen antes en la sintaxis.
Devuelve, como entero Long, el color rojo - verde - azul (RGB) del punto especificado de un objeto
Form o control PictureBox.
Sintaxis objeto.Point(x, y)
donde
objeto Opcional. Nombre del Formulario o PictureBox donde se va a analizar el color. Si objeto se
omite se asume que el objeto es el formulario que tenga el enfoque.
x, y Parámetro requerido. Valores Single que indican las coordenadas horizontal (eje x) y vertical (eje
y) del punto según la propiedad ScaleMode del objeto Form o control PictureBox. Deben colocarse
entre paréntesis.
Estas coordenadas se refieren a las coordenadas del objeto que contiene el gráfico, con origen (0,0) en
la esquina superior izquierda del mismo.
Si el punto definido por las coordenadas x e y está fuera de objeto, el método Point devuelve el valor
-1.
METODO Pset
objeto Opcional. Nombre del objeto (Formulario, PictureBox o Printer). Si se omite objeto, se asume
como objeto el Formulario que tenga el enfoque.
(x, y) Requeridos. Valores de simple precisión que indican las coordenadas horizontales (eje x) y
verticales (eje y) del punto a establecer.
Estas coordenadas se refieren a las coordenadas del objeto destino, con origen (0,0) en su esquina
superior izquierda.
Color Parámetro opcional. Valor entero largo que indica el color RGB especificado para el punto. Si se
omite, se utiliza el valor de la propiedad ForeColor. Puede utilizar la función RGB o la función
QBColor para especificar el color.
El tamaño del punto dibujado depende del valor de la propiedad DrawWidth. Cuando DrawWidth es
1, PSet establece un píxel al color especificado. Cuando DrawWidth es mayor que 1, se centra el
punto en las coordenadas especificadas.
La forma en que se dibuja el punto depende de los valores de las propiedades DrawMode y DrawStyle.
Cuando se ejecuta PSet, las propiedades CurrentX y CurrentY toman el valor del punto especificado
en los argumentos.
Como complemento a los Métodos gráficos, bueno será comentar las formas posibles de introducir
colores en VB y otras propiedades que tienen relación con los métodos gráficos.
METODO Print
Donde
Objeto es el nombre del Formulario, PictureBox o el Printer, donde se desea escribir
Expresión Es una cadena de caracteres o variable que contiene el texto a escribir
El tamaño, tipo, color y otras propiedades de la letra serán las que tenga el objeto sobre el que
se va a escribir en ese momento. El lugar donde se inicia la escritura será el de la propiedad
CurrentX y CurrentY actuales.
Veamos un ejemplo para excribir sobre un formulario. El Formulario, desde el código que está dentro
de el se le nombra con Me.
Me.FontName = "Arial"
Me.FontSize = 12
Me.FontBold = True
Me.Print "Hola Mundo"
SavePicture (Instrucción)
imagen es el nombre del objeto que contiene los gráficos que se van a guardar en el archivo, mas su
propiedad Picture o Image y expcadena es el nombre del archivo gráfico que se va a guardar.
En este ejemplo, guardamos en el fichero LUIS.BPM que está en el directorio C:\SUAREZ el gráfico
que contiene el PictureBox denominado Picture1
LoadPicture (Función)
Usando LoadPicture sin argumento se borran los gráficos de los formularios y los controles PictureBox
e imagen.
Los formatos gráficos reconocidos por Visual Basic incluyen archivos de mapas de bits (.BMP),
archivos de icono (.ICO), archivos de longitud codificada (.RLE) y Metarchivo (.WMF).
Para cargar gráficos para presentarlos en un control PictureBox, Image o como fondo de un formulario,
el valor devuelto por LoadPicture debe ser asignado a la propiedad Picture del objeto en el se quiere
presentar la imagen. Por ejemplo:
Para asignar un icono a un formulario, se asigna el valor devuelto por la función LoadPicture a la
propiedad Icon del objeto Form:
Los iconos también pueden ser asignados a las propiedades DragIcon de todos los controles
excepto los controles Timer y Menú. Por ejemplo:
Para cargar gráficos en el Portapapeles del sistema se usa LoadPicture de la forma siguiente:
Puede también meter una imagen en una variable, y luego poner en uno de los controles citados
anteriormente la imagen guardada en la variable. Este método le permite presentar una imagen muy
rápidamente, ya que no necesita acceder al disco para buscar una imagen, pero emplea mucha memoria
RAM, ya que la variable (o variables) conteniendo la(s) imágenes están en la RAM.
Option Explicit
Dim MiVariable1 As Picture, MiVariable2 As Picture
Mediante este programa lo que hemos hecho fue guardar dos imágenes en sendas variables, en el
momento de cargar el formulario, imágenes que se pasan posteriormente al control Picture1 con los
botones de comando Command1 y Command2
DrawMode (Propiedad)
Devuelve o establece un valor que determina el aspecto de la salida de un método gráfico o el aspecto
de un control Shape o Line.
Donde objeto es el nombre del Formulario, PictureBox, objeto Print, o los controles Shape o Line, y
número es un entero que especifica el aspecto, según la siguiente lista:
1 Blackness.
2 Not Merge PenInverso del valor 15 (Merge Pen).
3 Mask Not PenCombinación de los colores comunes del color de fondo y el inverso del
Pen.
4 Not Copy PenInverso del valor 13 (Copy Pen).
5 Mask Pen NotCombinación de los colores comunes al Pen y al inverso de la muestra.
6 InvertInverso del color de muestra.
7 Xor PenCombinación de los colores en el Pen y en el color de la muestra, pero no de
ambos.
8 Not Mask PenInverso del valor 9 (Mask Pen).
9 Mask PenCombinación de los colores comunes al Pen y a la presentación.
10 Not Xor PenInverso del valor 7 (Xor Pen).
11 NopNadano hay cambios. De hecho, este valor desactiva el dibujado.
12 Merge Not PenCombinación del color de muestra y el inverso del color del Pen.
13 Copy Pen (Predeterminado)Color especificado por la propiedad ForeColor.
14 Merge Pen NotCombinación del color del Pen y el inverso del color de muestra.
15 Merge PenCombinación del color del Pen y el color de muestra.
16 Whiteness.
Use esta propiedad para producir efectos visuales con controles Shape o Line o al dibujar con métodos
gráficos. Visual Basic compara cada píxel de la plantilla de dibujo con el píxel correspondiente del
fondo existente y después aplica operaciones a nivel de bit. Por ejemplo, el valor 7 (Xor Pen) usa el
operador Xor para combinar un píxel del dibujo con un píxel del fondo.
El efecto exacto de un valor DrawMode depende del modo en el que el color de una línea dibujada en
tiempo de ejecución se combina con los colores de la pantalla. Los valores 1, 6, 7, 11, 13 y 16 producen
los mejores resultados.
DrawStyle (Propiedad)
Devuelve o establece un valor que determina el estilo de línea de la salida de métodos gráficos.
Donde objeto es el nombre del Formulario, PictureBox, objeto Print sobre el que se va a dibujar, y
número es un entero que especifica el estilo de línea, tal como se describe a continuación:
0 (Predeterminado) Continuo.
1 Rayas.
2 Puntos.
3 Raya - punto.
4 Raya - punto - punto.
5 Transparente.
6 Continuo interior.
Si DrawWidth se define con un valor mayor que 1, los valores de DrawStyle entre 1 y 4 producen una
DrawWidth (Propiedad)
Donde objeto es el nombre del Formulario, PictureBox, objeto Printer sobre el que se va a dibujar, y
tamaño es una expresión numérica comprendida entre 1 y 32.767 que representa la anchura de la línea
en pixeles. El valor predeterminado es 1, es decir, un píxel de ancho.
Puede incrementar el valor de esta propiedad para aumentar la anchura de la línea. Si el valor de la
propiedad DrawWidth es mayor que 1, los valores de 1 a 4 en la propiedad DrawStyle producirán una
línea continua (el valor de DrawStyle no se modifica). Si se establece 1 en DrawWidth, DrawStyle
producirá los resultados mostrados en la tabla de esta propiedad.
FillColor (Propiedad)
Devuelve o establece el color usado para rellenar formas. FillColor también se usa para rellenar
círculos y cuadros creados con los métodos gráficos Circle y Line.
Donde objeto es el nombre del Formulario, PictureBox, objeto Printer sobre el que se va a dibujar, y
valor es un valor o constante que determina el color de relleno, con cualquiera de los criterios de VB
para definir el color.
De forma predeterminada, FillColor está definido como 0 (Negro).
Excepto en el objeto Form, cuando la propiedad FillStyle se define con su valor predeterminado, 1
(Transparente), el valor de FillColor se ignora.
FillStyle (Propiedad)
Devuelve o establece el modelo usado para rellenar controles Shape así como los círculos y los cuadros
creados con los métodos gráficos Circle y Line.
Donde objeto es el nombre del Formulario, PictureBox, objeto Printer sobre el que se va a dibujar, y
número es un entero que especifica el estilo de relleno, tal como se describe a continuación:
0 Continuo.
1 (Predeterminado) Transparente.
2 Línea horizontal.
3 Línea vertical.
4 Diagonal hacia arriba.
5 Diagonal hacia abajo.
6 Cruzado.
7 Diagonal Cruzada.
Esta propiedad es aplicable a los formularios y el PictureBox. Si está a true, hace que el Formulario o
Picture guarden el gráfico aunque estén minimizados o pase por encima de ellos otro objeto. Por regla
general, esta propiedad debe ponerse a False cuando no se van a utilizar métodos gráficos sobre ese
objeto (se ahorra memoria) y a True cuando se van a utilizar métodos gráficos
Donde objeto es el nombre del Formulario o PictureBox sobre el que se va a dibujar, y booleano es
una expresión booleana que especifica la forma en la que objeto es vuelto a dibujar, tal como se
describe a continuación:
True Activa el redibujado automático de un objeto Form o control PictureBox. Los gráficos
y el texto se escriben en la pantalla y en una imagen almacenada en memoria. El objeto no recibe
eventos Paint; se vuelve a dibujar cuando es necesario, usando la imagen almacenada en memoria.
Esta propiedad es importante cuando se trabaja con los siguientes métodos gráficos: Circle, Cls, Line,
Point, Print y PSet. Al establecer AutoRedraw a True la salida de estos métodos se vuelve a dibujar
automáticamente en un objeto Form o en un control PictureBox cuando, por ejemplo, se cambia de
tamaño al objeto o se vuelve a presentar después de haber estado oculto por otro objeto.
Se puede establecer AutoRedraw en el código en tiempo de ejecución para alternar entre dibujar
gráficos persistentes (como color de fondo o cuadrícula) y gráficos temporales. Si se define
AutoRedraw a False, la salida anterior se convierte en parte del fondo de la pantalla. Cuando
AutoRedraw se define a False, los gráficos de fondo no se eliminan si se borra el área de dibujo con el
método Cls. Al volver a establecer AutoRedraw a True y después usar Cls se borran los gráficos de
fondo.
Cuando mencionamos la sintaxis de los métodos gráficos todos ellos hablan de medidas. Por
ejemplo, en el método Line:
los valores x1, y1, x2, y2 se refieren a valores de las coordenadas x e y del objeto donde se va
a ejecutar ese método gráfico.
Le sugiero una forma mucho mas práctica de poner las medidas a un objeto soporte de
métodos gráficos. Use las propiedades ScaleWidth y ScaleHeight para determinar cuanto
quiere que le mida de ancho y de alto respectivamente. No se preocupe de cuanto va a medir
ahora la unidad de medida. Si pone para un formulario o PictureBox rectangular que
ScaleWidth = 12000 y ScaleHeight = 8000 sabe que ese formulario o PictureBox mide esas
cantidades que ha puesto. Ahora ya será mucho mas fácil definir los valores de x1, y1 y x2 e
Estas propiedades, aplicables a todos los objetos que aceptan métodos gráficos, definen el
punto de inicio para los siguientes métodos gráficos o de impresión. Es decir, donde se va a
comenzar a escribir mediante el método Print o el punto de inicio del método gráfico Line, si no
se especifica el origen del mismo. La instrucción
Dibuja una línea desde el punto donde se encontrase en ese momento el CurrentX y CurrentY
hasta el punto 2000,2000
Visual Basic acepta para especificar un color, tres procedimientos: Mediante el número de color,
mediante la sentencia RGB ( rojo, verde, azul ) o mediante la función QBColor.
Por número
El número que representa el color en VB está formado por la suma de la componente roja, la
componente verde y la componente azul. Podríamos verlo muy bien en numeración Hexadecimal:
Color = Hex XX YY ZZ
Donde ZZ es un número Hexadecimal que representa la cantidad del color rojo. El mínimo estaría en 0
(H00) y el máximo en 255 (HFF)
YY representaría la cantidad de color verde y XX la de color azul, ambos con los mismos límites
explicados para el rojo.
Una mezcla de un poco de rojo (HB1), otro poco de verde (H56) y otro poco de azul (H1F) daría el
siguiente número:
Hex(1F56B1) = 2053809
Se puede expresar el color, poniendo simplemente RGB (rojo, verde, azul), donde rojo es un número
entre 0 y 255 que indica la cantidad de color rojo que se aporta al color, verde un número comprendido
entre 0 y 255 indicando la cantidad de verde, y lo mismo para azul.
Sintaxis Objeto.QBColor(color)
0 Negro 8 Gris
1 Azul 9 Azul claro
2 Verde 10 Verde claro
3 Aguamarina 11 Aguamarina claro
4 Rojo 12 Rojo claro
5 Fucsia 13 Fucsia claro
6 Amarillo 14 Amarillo claro
7 Blanco 15 Blanco brillante
Ejemplo. Tengamos un Label llamado Label1 y un botón de comando, en cuyo procedimiento click le
introducimos el código:
Static i As Integer
i=i+1
If i = 16 Then i = 0
Label1.caption = i
Label1.BackColor = QBColor(i)
El objeto Printer gestiona el envío de información a la impresora, tanto para imprimir textos como
gráficos. Para Visual Basic el objeto Printer es como otro objeto cualquiera. Si queremos imprimir
algo en la impresora, debemos escribir el código VB necesario para escribirlo en el objeto Printer, lo
mismo que si estuviésemos escribiendo en un formulario.
Form1.Print "ABCD"
Printer.Print “ABCD”
Para enviar gráficos al objeto Printer se procede de igual forma que si estuviésemos dibujando en un
formulario
Form1.Circle (200, 150), 50, , , , 0.5 Esta instrucción dibuja una elipse centrada en
el punto x=200, y=150 del formulario Form1
Printer.Circle (200, 150), 50, , , , 0.5 Esta instrucción dibuja una elipse centrada en
el punto x=200, y=150 del objeto Printer
Parece en principio algo raro hablar de las coordenadas x e y del objeto Printer. Posiblemente porque
el objeto Printer es un objeto que no podemos “ver”. Pero imaginemos que el objeto Printer es una
hoja de papel. La misma hoja que aparecerá escrita una vez que le enviemos el texto (o los gráficos)
que queremos imprimir. Esa hoja de papel tendrá unas dimensiones que deberemos indicar al objeto
Print. La forma de indicarle las dimensiones del papel varían dependiendo del driver de impresora
usado.
El driver de la impresora sabrá que el papel que tiene es un DIN A4, DIN A3, etc., que tiene unas
medidas prefijadas. Centrémonos en lo mas habitual, una impresora que tenga un papel DIN A4 cuyas
medidas son 210 x 297 mm.
El driver de impresora “sabe” que ese es el tamaño de su papel. Ahora solamente nos falta que nuestra
aplicación lo sepa también. Para ello vamos a indicarle mediante un par de sentencias las medidas de
ese papel : ScaleWidth y ScaleHeight
Si le decimos :
le estamos diciendo a nuestro programa que el papel mide 2100 unidades de ancho y 2970 unidades de
alto. (Estamos suponiendo que el papel está colocado en posición vertical). Si tiene 2100 unidades de
ancho, y el ancho real del papel es de 210 mm, nuestra unidad de medida será de 0,1 mm. Es decir, le
decimos a nuestro programa que el papel tiene 2100 décimas de milímetro de ancho, y 2970 décimas de
milímetro de alto. La precisión con la que podemos colocar un punto sobre el papel será por tanto 0,1
mm. Podríamos hacerla mayor (0,01 mm.) si pusiésemos Printer.ScaleWidth = 21000 y
Printer.ScaleHeight = 29700.
Para el trabajo ordinario de imprimir listados o dibujar gráficos tipo barras, es mas que suficiente una
precisión de 0,1 mm. Si lo que queremos es un dibujo mas exacto (Dibujar fotolitos de circuitos
impresos, p.e. ) esta precisión de 0,1 mm. no nos bastaría, debiendo llegar a una precisión del orden de
LSB Visual Basic – Guía del Estudiante Capítulo 1 Página 209
0,01 mm. Pero todo ello está condicionado por el número de p.p.i.
(puntos por pulgada) que nuestra impresora es capaz de dar.
Nota.- Una impresora tiene unos márgenes sobre los que no puede escribir. Por lo tanto, cuando
decíamos que el papel tiene 210 mm. de ancho, en realidad ya estamos cometiendo un pequeño error,
ya que la impresora no puede escribir en todo el ancho, pues los 2 - 3 mm de cada lado no lo imprime.
Deberemos entonces poner las propiedades ScaleWidth y ScaleHeight del Printer de acuerdo con la
superficie real de escritura de nuestra impresora. Le adelanto que no le va a ser fácil enterarse de qué
márgenes deja sin imprimir. Le recomiendo que imprima una línea desde Printer.CurrentX=0 a
Printer.CurrentX= (un número superior a Printer.Scalewidth). Mida con un escalímetro el ancho real
de la impresión.
El objeto Printer almacenará toda la información sin pasarla a la impresora hasta que se le envíe la
instrucción Printer.EndDoc o se le envíe un salto de página mediante la instrucción
Printer.NewPage.
El objeto Printer, al igual que otros objetos de Visual Basic tiene sus propiedades y métodos.
Nota El efecto de las propiedades del objeto Printer depende del controlador suministrado por el
fabricante de la impresora. Algunos valores de la propiedad pueden no tener efecto, o varios valores
distintos de la propiedad pueden tener todos el mismo efecto. Los valores fuera del rango aceptado
pueden producir error. Para obtener más información, vea la documentación del fabricante del
controlador concreto.
ColorMode
Devuelve o establece un valor que determina si una impresora de color imprime en color o en blanco y
negro. No disponible en tiempo de diseño.
Copies
Devuelve o establece un valor que determina el número de copias que se van a imprimir.
Donde número debe ser una expresión numérica que especifique el número de copias que se van a
imprimir. Este valor debe ser un entero. El valor por defecto es 1.
CurrentX CurrentY
Devuelven o establecen las coordenadas horizontal (CurrentX) o vertical (CurrentY) para un método
gráfico o de impresión. No disponible en tiempo de diseño.
Sintaxis
Printer.CurrentX = x Printer.CurrentY =y
Para conocer las coordenadas del cursor de escritura del objeto Printer
Comentarios
Las coordenadas se miden a partir de la esquina superior izquierda del objeto. El valor de la propiedad
CurrentX es 0 en el borde izquierdo de un objeto, y el valor de la propiedad CurrentY es 0 en el borde
superior. Las coordenadas se expresan en las unidades de medida definidas por las propiedades
ScaleHeight y ScaleWidth. Si se han introducido estas propiedades (como recomendábamos mas
atrás), la propiedad ScaleMode se pone automáticamente a User. Si no se establecen, ScaleMode puede
estar en Twips, Point, Pixeles, caracteres, .... unidades que supongo le serán mucho mas difícil de
controlar que si Vd. dice desde el principio que el papel de su impresora tiene unas medidas de 2100
por 2970 (Printer.ScaleWidth = 2100, Printer.ScaleHeight = 2970) Haciéndolo de esta última forma,
cuando queramos dibujar una línea entre dos puntos de nuestro papel DIN A4 bastará con medir las
coordenadas de inicio y final de línea, eso sí, usando como unidad de medida la décima de milímetro.
Por ejemplo, una línea a 5 mm del borde superior, que comience a 20 mm. Del borde izquierdo y
termine a 10 mm del borde derecho, se dibujaría con la siguiente instrucción :
Si queremos escribir datos en una columna, que está a 30 mm. de la parte izquierda de la hoja,
comenzaremos a escribir cada fila de esa columna con un CurrentX de 300. Para ello primero
fijaremos el punto de inicio mediante CurrentX y a continuación usaremos el método Print :
CurrentX y CurrentY se quedan con los valores establecidos por el último método gráfico o método
Print usado.
Cuando se está dibujando sobre el objeto Printer, los métodos gráficos Circle y Line establecen los
siguientes valores para CurrentX y CurrentY :
Cuando se envía la instrucción EndDoc establece los valores de estas propiedades a 0, 0. NewPage
establece igualmente los valores 0, 0.
DeviceName
Devuelve el nombre del dispositivo permitido por un controlador. Esta propiedad es solo de lectura
Cada controlador de impresora acepta uno o más dispositivos por ejemplo, HP LaserJet III es un
nombre de dispositivo.
DrawMode
Devuelve o establece un valor que determina el aspecto de la salida de un método gráfico. Vea la
Ayuda de VB para mayor detalle.
DrawStile
Devuelve o establece un valor que determina el estilo de línea de la salida de métodos gráficos.
0 (Predeterminado) Continuo.
1 Rayas.
2 Puntos.
3 Raya - punto.
4 Raya - punto - punto.
5 Transparente.
6 Continuo interior.
Observaciones
Si DrawWidth se define con un valor mayor que 1, los valores de DrawStyle entre 1 y 4 producen una
línea continua (el valor de la propiedad DrawStyle no cambia). Si DrawWidth se define como 1,
DrawStyle produce el efecto para cada valor descrito en la tabla anterior.
DrawWidth
tamaño es una expresión numérica comprendida entre 1 y 32.767 que representa la anchura de la línea
en pixeles. El valor predeterminado es 1, es decir, un píxel de ancho.
Comentarios
Puede incrementar el valor de esta propiedad para aumentar la anchura de la línea. Si el valor de la
propiedad DrawWidth es mayor que 1, los valores de 1 a 4 en la propiedad DrawStyle producirán una
línea continua (el valor de DrawStyle no se modifica). Si se establece 1 en DrawWidth, DrawStyle
producirá los resultados mostrados en la tabla de esta propiedad.
DriverName
Devuelve el nombre del controlador de un objeto Printer. Esta propiedad es solo de lectura
Cada controlador tiene un nombre único. Por ejemplo, el DriverName de varias impresoras Hewlett-
Packard es HPPCL5MS. El DriverName es normalmente el nombre de archivo del controlador sin la
extensión.
Duplex
Devuelve o establece un valor que determina si las páginas se imprimen por los dos lados (si la
impresora tiene esta característica). No disponible en tiempo de diseño.
Donde valor es un valor o constante que especifica el tipo de impresión, tal como se describe a
continuación
Con impresión a doble cara horizontal, la parte superior de ambas caras de la hoja están en el mismo
borde de la hoja. Con impresión a doble cara vertical, la parte inferior de una página está en el mismo
borde de la hoja que la parte superior de la página siguiente. Véase el gráfico existente en la ayuda de
VB para la propiedad Duplex
FillColor
Devuelve o establece el color usado para rellenar formas; FillColor también se usa para rellenar
círculos y cuadros creados con los métodos gráficos Circle y Line.
Donde valor es un valor o constante que determina el color de relleno. El valor puede introducirse como
RGB o con las constantes QB color
ejemplos
FillColor = QBColor(8)
FillColor = RGB (255,0,0)
El color predeterminado es el 0 (Negro).
FillStyle
Devuelve o establece el modelo usado para rellenar dibujos (círculos y los cuadros creados con los
métodos gráficos Circle y Line.
Donde número Un entero que especifica el estilo de relleno, tal como se describe a continuación
Valor Descripción
0 Continuo.
1 (Predeterminado) Transparente.
2 Línea horizontal.
Font
Devuelve o establece el objeto Font (letra) del objeto Printer. Este objeto Font tiene a su vez sus
propiedades (Name, Size, Bold, Italic ... )
Podemos forzar la fuente a usar por el objeto Printer con un CommonDialog, igualando la propiedad
Name del objeto Font del Printer a la propiedad FontName del CommonDialog
Printer.Font.Name = CD1.FontName
Tipofuente = Printer.Font.Name
FontCount
FontName
(El Objeto Printer admite esta propiedad como Font.Name o FontName indistintamente)
Fonts
Devuelve todos los nombres de fuente disponibles para el dispositivo de presentación actual o la
impresora activa.
Sintaxis Printer.Fonts(índice)
La propiedad Fonts funciona de forma conjunta con la propiedad FontCount, que devuelve el número
de nombres de fuente disponibles para el objeto. Las fuentes disponibles en Visual Basic varían de
acuerdo con la configuración del sistema, y los dispositivos de presentación y de impresión. Use las
propiedades Fonts y FontCount para obtener información sobre las fuentes disponibles para pantalla o
impresora.
Por ejemplo, si queremos ver en Label1 todas las fuentes disponibles para el objeto Printer,
estableceremos el siguiente código :
For i = 1 To Printer.FontCount
Label1.Caption = Label1.Caption + " " + Printer.Fonts(i)
FontSize
Devuelve o establece el tamaño de la fuente a utilizar para el texto mostrado en un control o en una
operación de dibujo o impresión en tiempo de ejecución.
Donde puntos es una expresión numérica que especifica el tamaño de fuente a utilizar, en puntos.
Puede utilizar esta propiedad para dar al texto el tamaño que desee. El valor predeterminado lo
determina el sistema. Para cambiar este valor, especifique el nuevo tamaño de la fuente en puntos.
El valor máximo de FontSize es 2160 puntos.
Nota Las fuentes disponibles en Visual Basic varían dependiendo de la configuración del sistema y de
los dispositivos de presentación e impresión. En las propiedades relacionadas con las fuentes sólo
pueden establecerse valores para los que exista una fuente.
Devuelve o establece los estilos de fuente en los siguientes formatos: Negrita, Cursiva, Tachada y
Subrayada.
Para el objeto Printer, el establecimiento de estas propiedades no afecta al texto ya escrito, solamente
para lo que se escriba después de cambiar la propiedad. Aprovecha esta facilidad para cambiar de letra
a lo largo de un documento.
En general, deberá modificar la propiedad FontName antes de establecer los atributos de tamaño y
estilo con las propiedades FontSize, FontBold, FontItalic, FontStrikethru y FontUnderline. Sin
embargo, cuando especifique un tamaño inferior a 8 puntos para una fuente TrueType, primero deberá
establecer el tamaño en puntos con la propiedad FontSize, luego especificar la propiedad FontName y
después establecer de nuevo el tamaño con FontSize. El entorno operativo Microsoft Windows utiliza
una fuente distinta para las fuentes TrueType con un tamaño inferior a 8 puntos.
FontTransparent
Devuelve o establece un valor Booleano que determina si el texto y los gráficos de fondo de un objeto
Printer, se muestran en el espacio situado entre los caracteres.
ForeColor
Devuelve o establece el color de escritura para impresoras en color. El cambio de color afecta
solamente al texto que se envíe después de realizar el cambio.
hDC
Devuelve un controlador proporcionado por el entorno operativo Microsoft Windows para el contexto
de dispositivo de un objeto.
Sintaxis Printer.hDC
Esta propiedad es un controlador de contexto de dispositivo del entorno operativo Windows. El entorno
operativo Windows se encarga de la presentación del sistema asignando un contexto de dispositivo al
objeto Printer. Se puede usar la propiedad hDC para referirse al controlador del contexto de dispositivo
de un objeto. Así se obtiene un valor que se pasa en las llamadas al API de Windows.
Nota El valor de la propiedad hDC puede cambiar en tiempo de ejecución, así que no se debe
almacenar en una variable. En su lugar, use la propiedad hDC cada vez que lo necesite.
Height Width
Devuelven o establecen las dimensiones del objeto Printer. No están disponibles en tiempo de diseño.
Donde número indica las dimensiones del papel configurado para el dispositivo de impresión. No
disponibles en tiempo de diseño. Si se establecen en tiempo de ejecución, los valores de estas
propiedades se utilizarán en lugar del de la propiedad PaperSize.
Para el objeto Printer, estas propiedades se miden siempre en twips. Si establece las propiedades Height
y Width para un controlador de impresora que no admite su uso, no se producirá ningún error, y el
tamaño del papel continuará siendo el mismo.
¡¡¡ Es mas recomendable utilizar la propiedad PaperSize para establecer las dimensiones del
papel. !!!
Orientation
Devuelve o establece un valor que indica si los documentos se imprimen con orientación vertical u
horizontal. No disponible en tiempo de diseño.
Visual Basic mantiene un contador de páginas impresas desde el inicio de la aplicación o desde la
última vez que se utilizó la instrucción EndDoc en el objeto Printer. Este contador comienza por uno y
se incrementa en uno cada vez que:
Nota La salida de los métodos gráficos que no quepa en la página actual no genera una página
nueva, sino que se recorta para ajustarla a la zona de impresión de la página.
PaperBin
Devuelve o establece un valor que indica la bandeja predeterminada para la alimentación de papel
durante la impresión. No disponible en tiempo de diseño.
No todas las opciones están disponibles en todas las impresoras. Consulte la documentación de la
impresora para obtener descripciones más específicas de estas opciones.
PaperSize
Devuelve o establece un valor que indica el tamaño de papel para la impresora actual. No disponible en
tiempo de diseño. El valor predeterminado es el establecido en la configuración de la impresora en
Windows. Con esta propiedad no se debe jugar alegremente. El valor que se le dé a esta propiedad será
el tamaño de ese papel “ficticio” que comentábamos mas atrás.
valor es un valor o constante que especifica el tamaño del papel, según se describe a continuación
Constante Valor Descripción
Port
Devuelve el nombre del puerto a través del cual se envía un documento a una impresora.
El sistema operativo determina el nombre del puerto, como por ejemplo LPT1: o LPT2:.
PrintQuality
valor es un valor o una constante que especifica la resolución de la impresora, según se describe a
continuación :
Además de los valores negativos predefinidos, también puede indicarse en valor un número positivo de
puntos por pulgada, como por ejemplo 300.
ScaleHeight, ScaleWidth
Donde valor es una expresión numérica que especifica la medida horizontal o vertical. Este valor va a
determinar el número de unidades de medida que tiene el papel, NO su medida. Es decir, podemos
decir, mediante estas propiedades, que el papel tiene 2100 unidades de ancho y 2970 unidades de alto.
Si el papel es un DIN A4 (Medidas 210 x 297 mm) esa unidad de medida sería precisamente una
décima de milímetro. Podríamos haber elegido otros valores, con lo que esa unidad de medida ya no
serían décimas de mm. Sería una unidad de medida cualquiera, pero el papel seguiría siendo DIN A4 y
seguiría teniendo las mismas medidas reales.
ScaleLeft, ScaleTop
donde valor es una expresión numérica que especifica la coordenada horizontal o vertical. El valor
predeterminado es 0.
Con estas propiedades y las relacionadas ScaleHeight y ScaleWidth, puede configurar un sistema de
coordenadas completo, con coordenadas positivas y negativas. Estas cuatro propiedades de escala se
relacionan con la propiedad ScaleMode de la siguiente forma:
Resumiéndolo en palabras mas sencillas. Si, con las medidas de los ejemplos anteriores (ScaleWidth =
2100, ScaleHeight = 2970) ponemos Printer.ScaleLeft = - 1050 estamos diciendo que la coordenada
horizontal absoluta en la parte izquierda de la hoja es de -1050. Si el ancho del papel es de 2100, la
coordenada X absoluta del eje central del papel será entonces 0. Y la coordenada X de la parte derecha
del papel será + 1050. Por lo tanto, si ejecutamos el método gráfico :
dibujará un circulo de radio 300, con el centro en el eje vertical de la hoja (Coordenada X=0) a una
altura de 1000 (Coordenada Y=1000) de la parte superior del papel.
ScaleMode
Devuelve o establece un valor que indica la unidad de medida de las coordenadas de un objeto al
utilizar métodos gráficos, o al situar controles.
donde valor es un número entero que especifica la unidad de medida, según se describe a continuación
0 User. Indica que una o más de las propiedades ScaleHeight, ScaleWidth, ScaleLeft y
ScaleTop tienen valores personalizados.
1 (Predeterminado) Twip (1440 twips por pulgada lógica; 567 twips por centímetro
lógico).
2 Punto (72 puntos por pulgada lógica).
3 Píxel (la unidad mínima de la resolución del monitor o la impresora).
4 Carácter (horizontal = 120 twips por unidad; vertical = 240 twips por unidad).
5 Pulgada.
6 Milímetro.
7 Centímetro.
Recomendación del autor de esta Guía del Estudiante. Una vez mas recomiendo que controle la
posición de los métodos gráficos y método Print fijando las unidades de medida mediante ScaleHeight,
ScaleWidth, (Si le es práctico, emplee ScaleLeft y ScaleTop para que le coincida el punto central del
papel con las coordenadas 0,0 y así usaría coordenadas positivas y negativas) y cada vez que escriba o
dibuje, coloque el “cursor” de escritura mediante las propiedades CurrentX y CurrentY
TrackDefault
Devuelve o establece un valor booleano que determina si el objeto Printer apunta siempre a la misma
impresora, o si la impresora a la que apunta cambia cuando se modifica la impresora predeterminada en
el Panel de control del sistema operativo. No disponible en tiempo de diseño.
TwipsPerPixelX, TwipsPerPixelY
Sintaxis Printer.TwipsPerPixelX
Printer.TwipsPerPixelY
En general, las rutinas del API de Windows requieren las medidas en píxels. Puede utilizar estas
propiedades para convertir rápidamente las dimensiones sin cambiar el valor de la propiedad
ScaleMode de los objetos.
Zoom
Devuelve o establece el porcentaje en que se amplía o reduce el resultado impreso. No está disponible
en tiempo de diseño.
Donde numero es una expresión numérica que evalúa en el porcentaje de ajuste de la salida impresa. El
valor predeterminado es 0, que especifica que la página impresa aparece con su tamaño normal.
Comentarios
La colección Printers le permite consultar las impresoras disponibles de forma que pueda especificar la
impresora predeterminada de la aplicación. Por ejemplo, se puede querer saber cual de las impresoras
disponibles usa un controlador de impresoras determinado. El siguiente código comprueba todas las
impresoras disponibles para saber la primera impresora cuya orientación de página sea vertical:
Dim X As Printer
For Each X In Printers
If X.Orientation = vbPRORPortrait Then
' la define como predeterminada.
Set Printer = X
' Sale del bucle.
Exit For
End If
Next
Nota Si la colección Printers se usa para especificar una impresora concreta, como Printers(3), sólo se
puede tener acceso a sus propiedades de modo lectura. Para leer y escribir las propiedades de una
impresora concreta, primero se tiene que definir como impresora predeterminada de la aplicación.
Circle
Dibuja un círculo, elipse o arco sobre un objeto. No acepta argumentos con nombre.
Sintaxis Printer.Circle Step (x, y), radio, color, inicio, final, aspecto
EndDoc
Termina una operación de impresión enviada al objeto Printer, liberando el documento al dispositivo de
impresión o a la cola.
Sintaxis Printer.EndDoc
Si EndDoc se invoca inmediatamente después del método NewPage, no se imprime la página en blanco
adicional.
KillDoc
Sintaxis Printer.KillDoc
Line
NewPage
PaintPicture
Presenta el contenido de un archivo gráfico (.BMP, .WMF, .EMF, .ICO o .DIB) en un objeto Printer.
No acepta argumentos con nombre.
Sintaxis
Printer.PaintPicture imagen, x1, y1, anchura1, altura1, x2, y2, anchura2, altura2, opecod
Puede ver mas información acerca de este método en el capítulo 7 Métodos Gráficos
Donde lista_salida es una expresión o lista de expresiones a imprimir. Si se omite, se imprimirá una
línea en blanco.
En el argumento lista_salida puede poner un texto directamente (Printer.Print “Hola”) o una variable
(Printer.Print Textoaescribir). Puede también introducir algunas funciones de cadena como la
siguiente:
Spc(n) Se utiliza para insertar caracteres espacio en la salida, donde n es el número de espacios a
insertar.
Nota.- El uso de los tabuladores puede traerle mas problemas que ventajas. Los pasos de tabulación
dependen de cada impresora, y lo que es peor, de la programación de la propia impresora. Por lo tanto,
al usar Tab no controlamos del todo la posición de las columnas. Es mucho mas práctico (y seguro)
usar Printer.CurrentX y Printer.CurrentY para posicionar el “puntero” de escritura del Objeto Printer.
Las instrucciones :
Printer.Print “Hola”
Printer.Print “¿Qué tal ?” generarán la siguiente salida por impresora :
Hola
¿Qué tal ?
Printer.Print “Hola ” ;
Printer.Print “¿Qué tal ?” la salida por impresora sería :
Nota Debido a que el método Print imprime normalmente con caracteres de espaciado proporcional,
es importante recordar que no hay relación entre el número de caracteres impresos y el número de
columnas de anchura fija que tales caracteres ocupan. Por ejemplo, una letra ancha, como W, ocupa
más de una columna de anchura fija, mientras que una letra estrecha, como I, ocupa menos. Para tener
en cuenta los casos en el que se utilizan caracteres con una anchura mayor que la media, deberá
asegurarse de que las columnas de las tablas se encuentren lo bastante lejos unas de otras. Como
alternativa, puede utilizar en la impresión una fuente de anchura fija (como Courier) para hacer que
cada carácter utilice sólo una columna.
Para controlar perfectamente el punto donde se escribirá el siguiente carácter, utilice las propiedades
CurrentX y CurrentY. Es mucho mas práctico que usar, por ejemplo, el Tabulador Tab(n)
Si usa Tab(n) con una determinada impresora, puede verse con la desagradable sorpresa que en otra
impresora no funciona de la misma forma. Como al desarrollar una aplicación nunca sabe sobre que
impresora va a imprimir, es mejor que no utilice Tab(n), sustituyéndolo por CurrentX.
Método TextWidth
Donde AnchodelTexto es una variable tipo Long que indicará el ancho (en las unidades de medida
definidas para el objeto Printer - Twips, Pixels, mm, o como siempre recomendaré, las establecidas
mediante ScaleWidth y ScaleHeight) y Textoaescribir es una variable tipo String que contiene el texto a
escribir.
Si la cadena de texto cuya longitud de impresión queremos analizar contiene retornos de carro,
TextWidth devuelve la anchura de la línea más larga.
Mediante este método puede escribir columnas de datos, mucho mejor que usando el Tab(n).
Suponga que quiere escribir en una columna, los datos Dato(1), Dato(2), ....Dato(N) que son números.
Quiere escribirlos de tal forma que la parte derecha del número quede alineada, como hacemos casi
siempre para poder sumarlos con facilidad. Supongamos que hemos establecido las Propiedades
Printer.ScaleWidth = 21000 y Printer.ScaleHeight = 29700. Damos por hecho que el papel es un DIN
A-4 (210 x 297 mm) por lo que estamos usando, como unidad de medida en ambas coordenadas la
centésima de mm.
Si queremos que las cifras queden alineadas en una columna cuya parte final sea 5000 (la parte final de
esa columna quedará a 50 mm. del margen izquierdo del papel), deberemos calcular la longitud de cada
cifra, y poner, como inicio de escritura de esa cifra 5000 - ancho :
Observe que el ancho de una determinada cadena de caracteres no tiene porqué ser la misma para un
Formulario que para el Printer. Dependerá del tipo de fuente y del tamaño de esa fuente que usemos en
cada caso. Por eso, debe poner siempre Printer.TextWidth, Form1.TextWidth, etc.
Método TextHeight
Este método es idéntico al anterior, pero relativo a la altura. No vamos a alargar la explicación dada la
similitud con el anterior. Este método nos permite separar adecuadamente las líneas de un escrito. Es
muy útil sobre todo cuando utilizamos distintos tipos de letra dentro del mismo escrito.
Método PSet
Asigna a un punto de un objeto Printer un color especificado. No acepta argumentos con nombre.
Step Opcional. Palabra reservada que especifica que las coordenadas son relativas a la
posición gráfica actual proporcionada por las propiedades CurrentX y CurrentY.
(x, y) Requeridos. Valores de simple precisión que indican las coordenadas horizontales (eje
x) y verticales (eje y) del punto a establecer.
color Opcional. Valor entero largo que indica el color RGB especificado para el punto. Si se omite,
se utiliza el valor de la propiedad ForeColor. Puede utilizar la función RGB o la función QBColor para
especificar el color.
El tamaño del punto dibujado depende del valor de la propiedad DrawWidth. Cuando DrawWidth es
1, PSet establece un píxel al color especificado. Cuando DrawWidth es mayor que 1, se centra el
punto en las coordenadas especificadas.
La forma en que se dibuja el punto depende de los valores de las propiedades DrawMode y DrawStyle.
Cuando se ejecuta PSet, las propiedades CurrentX y CurrentY toman el valor del punto especificado
en los argumentos.
Vacíe un píxel con el método PSet especificando las coordenadas del píxel y utilizando el valor de la
propiedad BackColor como argumento color.
LA FUNCION SHELL
LA FUNCION COMMAND PARA PASAR PARAMETROS
LA FUNCION DoEvents
Cajas de mensaje (Mensaje Box)
Cajas de entrada de datos (Input Box)
El Objeto APP (La Aplicación)
La función Shell se utiliza para ejecutar un programa ajeno a la aplicación que se está ejecutando.
Imaginemos que tenemos una aplicación Visual Basic que necesita, por ejemplo, establecer una
comunicación telefónica, y que esa comunicación telefónica nos la realiza un programa desarrollado en
C++ , llamado MARCADOR.EXE que funciona perfectamente y no queremos desaprovechar.
Imaginemos que ese programa tiene la posibilidad de introducirle el número telefónico que debe marcar
como un parámetro. Este parámetro se le introduce, supongamos, añadiendo el número al nombre del
programa ejecutable. Por ejemplo:
MARCADOR.EXE 1234567
En nuestra aplicación Visual Basic introduciremos una línea invocando la función Shell seguida del
nombre (y Path) del ejecutable y del parámetro que le vamos a introducir al ejecutable:
Mediante esta línea, lo que hacemos es ejecutar el programa MARCADOR.EXE e introducirle como
parámetro el número a marcar. Resultado: el programa MARCADOR.EXE llama al número 080
(Bomberos), y una vez establecida la llamada podemos pasarle a ese Organismo datos o lo que nuestra
aplicación haga.
donde:
rutaDeAcceso es el nombre del programa por Ejecutar (con su Path) y cualquier argumentos o
conmutadores (switches) de línea de comando requeridos; puede incluir directorio o carpeta y unidad de
disco. También puede ser el nombre de un documento que se ha asociado con un programa ejecutable.
Si la función Shell ejecuta con éxito el archivo nombrado, devuelve la identificación de la tarea (ID)
del programa iniciado. La ID de la tarea es un número exclusivo que identifica el programa en
ejecución. Este número debe ser un Long. Si la función Shell no puede iniciar el programa nombrado,
ocurrirá un error. Si desea conocer el ID de la tarea, realice una aplicación con un botón
(cmbCalculadora) y un label (label1). Ponga en ese botón en su procedimiento click, el siguiente
código. - Observe que esta aplicación inicia la calculadora de Windows -
Posiblemente lo único que le importe sea el ejecutar esa aplicación, sin dar mayor importancia al ID de
la tarea. Utilice una línea con la siguiente expresión :
Shell ("C:\windows\calc.exe"), 1
(Observe en las dos formas de ejecutar la función Shell, que la colocación de los paréntesis y la coma
separadora es distinto.
El programa a ejecutar puede ser un programa Windows caso anterior de la calculadora) o un programa
DOS.
Nota La función Shell ejecuta otros programas de manera asíncrona. Esto quiere decir que no se puede
esperar que un programa iniciado con Shell termine su ejecución antes de que se ejecuten las
instrucciones que siguen a la función Shell en la aplicación. Esto es un gran inconveniente de la función
Shell. Excepto en contadas ocasiones, siempre es necesario conocer cuando se ha terminado de ejecutar
el programa iniciado mediante Shell. Y no es ese el único problema. La mayoría de los programas DOS
que se ejecutan con Shell no se cierran automáticamente. Esto significa que si podemos evitar el uso de
Shell debe evitarse. Pero si es completamente necesario, tampoco pasa nada. Pero hay que controlar,
tanto la terminación del proceso DOS como su cierre.
Podemos usar para ello APIs. No las hemos explicado todavía. Por eso, y adelantar un poco como se
trabaja con ellas, vamos a presentar, sin grandes explicaciones, lo que hay que hacer para poder detectar
que se ha terminado de ejecutar el programa DOS y para cerrarlo. Verá mas APIs mas adelante.
El programa DOS elegido para este ejemplo es el popular ARJ.EXE, un compresor de datos que, sin
ánimo de publicidad, es uno de los mejores que existen. Pero trabaja solamente en DOS. Se utiliza un
Procedimiento que he llamado ExecCmdNoFocus, para detectar que el proceso abierto para comprimir
o descomprimir ha finalizado, y proceder a cerrarlo.
Dim hProcess As Long 'handle del proceso donde se invoca la función Shell
Dim RetVal As Long 'Valor donde la función GetExitCode coloca el resultado
Dim winHwnd As Long ' manipulador de la ventana que contenga el Caption
‘Finalizado - ARJ
Dim RetValls As Long 'valor de retorno de PostMessage
Do
GetExitCodeProcess hProcess, RetVal
Sleep 100
'en este apartado comprueba si está abierta la ventana "Finalizado - ARJ"
winHwnd = FindWindow(vbNullString, "Finalizado - ARJ")
If winHwnd <> 0 Then
RetValls = PostMessage(winHwnd, WM_CLOSE, 0&, 0&)
End If
Loop While RetVal = STILL_ACTIVE
End Sub
La ventana DOS del ARJ tiene el Caption Finalizado - ARJ cuando ya ha terminado el proceso. En el
procedimiento ExecCmdNoFocus se analiza si esa ventana está presente (prueba de que ARJ ya ha
terminado, pues antes de terminar tiene otro Caption).
Para llamar a ese procedimiento hay que citarle por su nombre (ExecCmdNoFocus) que como lo
hemos declarado Public en un Módulo, podemos llamarle desde cualquier parte de la aplicación.
Debemos pasarle el parámetro CmdLine, que será el programa que vamos a ejecutar con Shell y los
parámetros adicionales que este programa necesite (En este caso, el programa es ARJ.EXE y a
continuación debe indicársele el nombre del archivo ya comprimido, a continuación a para que
añada mas ficheros a ese archivo, y a continuación el nombre de ese fichero o ficheros a añadir :
En el ejemplo anterior usábamos un ejecutable realizado en C++ para marcar un número telefónico que
le introducíamos como parámetro. ¿Podemos hacer eso en una aplicación VB? La respuesta debe ser SI.
Usaremos para ello la función Command. Esta función nos devuelve el parámetro introducido tras el
nombre del ejecutable realizado en VB, cuando iniciamos la aplicación VB mediante la línea de
comandos de Windows (línea Archivo | Ejecutar del Menú de Windows 3.xx o línea Ejecutar de
W95), o desde otra aplicación utilizando la función Shell.
Command (Función)
Devuelve parte del argumento de la línea de comandos utilizada para lanzar Microsoft Visual Basic o
un programa ejecutable desarrollado con Visual Basic.
Cuando se inicia Visual Basic desde la línea de comandos, la parte de la línea de comandos que sigue
a /CMD se pasa al programa como un argumento de la línea de comandos. En el siguiente ejemplo,
cmdlineargs representa la información de argumento devuelta por la función Command.
VB /CMD cmdlineargs
En las aplicaciones desarrolladas con Visual Basic y compiladas en un archivo .EXE, Command
devuelve los argumentos que aparezcan en la línea de comandos tras el nombre de la aplicación. Por
ejemplo:
MyApp cmdlineargs
En la ventana Código, puede usted cambiar el texto devuelto por Command eligiendo Opciones del
proyecto en el menú Herramientas.
Veamos con un par de ejemplos como se pueden usar estas dos funciones:
Marcador Telefónico
Esta aplicación debe marcar un número telefónico. El número a marcar se le pasará como parámetro
tras el nombre de la aplicación. La aplicación ya compilada se llamará MARCADOR.EXE. Para
marcar un número debemos poner, en la línea de Comando de Windows:
Marcador.exe 1234567
o como ya sabemos, introducirle el parámetro mediante una llamada con la función Shell desde otra
aplicación:
Como no vamos a realizar llamada alguna, sino comprobar que esto puede funcionar, nuestra pequeña
aplicación tendrá solamente un Label llamado FRMARCADORL1 donde presentaremos el número a
marcar. Todo el código necesario se lo metemos en el procedimiento Activate del formulario:
También podemos pasar parámetros a un ejecutable, mediante el Drag & Drop de Windows.
Hemos visto mas atrás como podemos hacer Drag & Drop en una aplicación VB. Pero como
comentábamos, el D&D no es una función de VB, sino de Windows. Y Windows lo utiliza en su
Explorador de Windows para pasar como parámetro a una aplicación el nombre del fichero que
arrastremos hacia el icono que representa a esa aplicación. Como estoy seguro que se ha liado, vamos
con un ejemplo.
Sintaxis DoEvents
Cede el control al sistema operativo
Sintaxis variable=DoEvents
variable contendrá un número indicando el número de formularios abiertos en
este momento.
La función DoEvents es una instrucción obligada en todos los bucles por condición para evitar
que, en caso de meterse en un bucle infinito, podamos salir de el aunque sea teniendo que
pulsar las teclas Ctrl-Alt-Sup. Si no lleva esa línea DoEvents, es posible que tenga que
resetaear el ordenador. El siguiente bucle se pone para esperar a que el programa envíe un
mensaje a través del control de comunicaciones. Este, una vez terminada la comunicación,
pone la variable Transmitido a True. Imagínese que se corta la comunicación y Transmitido
nunca llega a ser True.
La función DoEvents devuelve también el número de formularios abiertos por una versión única de
Visual Basic, como la versión estándar de Visual Basic. DoEvents devuelve 0 en el resto de las
aplicaciones.
El control no se devuelve hasta que el sistema operativo haya terminado de procesar los eventos en cola
y que (sólo para Microsoft Windows) se hayan enviado todas las teclas en la cola SendKeys.
Si partes de su código consumen demasiado tiempo de procesamiento, use periódicamente DoEvents
para ceder el control al sistema operativo, de manera que eventos como la entrada por el teclado o los
clics del mouse (ratón) se puedan procesar sin grandes retrasos. Utilice esta función sobre todo, cuando
tenga bucles demasiado largos que puedan interrumpir la entrada de datos por teclado o ratón.
Precaución Asegúrese de que el procedimiento que ha cedido el control con DoEvents no se ejecute
de nuevo desde una parte diferente del código antes de que regrese la primera llamada a DoEvents. Esto
podría causar resultados impredecibles. Además, no use DoEvents si existe la posibilidad de que otras
aplicaciones interactúen con el procedimiento, de formas imprevistas, durante el tiempo en éste ha
cedido el control.
EJEMPLO
Para explicar la función DoEvents se ha preparado un pequeño ejercicio con un formulario principal
(Form1) y dos formularios auxiliares, estos últimos solamente a efectos de contar, mediante DoEvents
el número de formularios abiertos.
El botón COMENZAR introduce un bucle que no pararía nunca. También pone Label1 de color Verde.
Si no hubiésemos puesto DoEvents en una parte del bucle del contador, nunca se podría acceder al
Procedimiento KeyPress del formulario, pues el programa lo único que haría será dar vueltas en el
bucle indefinidamente.
Lo mismo ocurriría con el botón ROJO. Al estar el programa haciendo el bucle continuamente, nunca
podríamos entrar el Procedimiento Click de este botón.
Por último SALI nos saca del programa. Observe que si no hubiésemos puesto DoEvents en el medio
del bucle, tampoco podríamos salir del programa, puesto que el sistema operativo no podría comprobar
que hemos hecho Click en este botón.
Realice esta pequeña práctica con la línea DoEvents del botón COMENZAR activada y desactivada
(Con una comilla simple). Prepárese para, en este último caso, detener la aplicación pulsando Ctr-
Pausa, pues de otra forma no la podrá detener.
Para ver esto, cree una aplicación con dos formularios. El primero (Form1) puede ser el formulario
donde se realizan todas las operaciones de la aplicación. El segundo (Form2) puede ser el típico
formulario donde se presenta la información del fabricante de la aplicación, al que se accede
normalmente desde el menú, con la palabra Acerca de...
Cree un menú en el primer formulario con esa palabra y ponga este formulario como formulario inicial
de la aplicación. En el procedimiento click de este menú ponga el siguiente código :
Form2.Show 1
El 1 detrás de la expresión Show indica que el formulario Form2 debe mostrarse de forma Modal, lo
que significa que no se podrá volver a operar con ningún elemento de Form1 hasta que se oculte o
descargue el formulario Form2. Ponga en este Form2 el típico botón de Aceptar, cuyo código puede ser
simplemente :
Me.Hide
Lo dicho para un formulario puede aplicarse para otro tipo de ventanas. El MessageBox y la ventana
del CommonDialog son dos ejemplos de ventanas modales. Veamos la primera :
1 - El aviso es sí, que tiene por objetivo mostrar una información de interés.
2 - El aviso con espera de respuesta, que muestra una información esperando que el usuario seleccione
una de las respuestas posibles para que el programa la trate.
Una caja de mensaje, puede ser por ejemplo, la instrucción MsgBox "Hola". Por defecto, la caja de
mensaje será similar a esta:
El resultado es:
Habrá observado en la expresión anterior que se ha utilizado vbCrLf (Visual Basic Carriage Return
Line Feed, VB retorno de carro y avance de línea) Vea mas adelante la aclaración de esta expresión.
Con ella logramos introducir un salto de línea.
Supongo que se habrá percatado de que entre el mensaje y el título de la ventana, hemos escrito dos
comas, esto es porque entre las comas, debe ir un número que representará el icono a mostrar. Existen
cuatro iconos diferentes además de la posibilidad de no mostrar ninguno. Los iconos son:
(Sólo en W32. En Windows 3.xx dispone de otros diferentes, aunque con el mismo significado)
Para mostrar el icono en cuestión o para que Visual Basic lo entienda, es necesario escribir lo siguiente:
El resultado es:
Aceptar vbOKOnly ó 0
Aceptar y Cancelar vbOKCancel ó 1
Anular, Reintentar, Ignorar vbAbortRetryIgnore ó 2
Sí, No y Cancelar vbYesNoCancel ó 3
Sí y No vbYesNo ó 4
Reintentar y Cancelar vbRetryCancel ó 5
Aplicación modal vbApplicationModal ó 0 (Es la caja de mensaje sin
icono)
La forma de hacer esto es sumar al parámetro del icono que queremos mostrar el valor de los botones
que deseamos que aparezcan.
Así por ejemplo:
MsgBox "Hola" & vbCrLf & "Esto es un ejemplo.", VbQuestion + vbYesNo , "Ejemplo"
El resultado es:
Aún así, es posible que deseemos que el Focus lo adquiera otro un botón determinado. Por ejemplo, en
este caso el Focus lo tiene el botón Sí, pero es posible que deseemos que lo tenga el botón No por
ejemplo. Esto se consigue con los siguientes parámetros:
Primer botón predeterminado vbDefaultButton1 ó 0
Segundo botón predeterminado vbDefaultButton2 ó 256
Tercer botón predeterminado vbDefaultButton3 ó 512
Por ejemplo: MsgBox "Hola" & vbCrLf & "Esto es un ejemplo.", VbQuestion + vbYesNo +
vbDefaultButton2, "Ejemplo"
El resultado es:
Ahora bien, si decidimos mostrar un mensaje esperando una respuesta, o queremos saber que botón ha
pulsado el usuario, esto lo podemos conseguir mediante el siguiente código de respuestas:
Aceptar vbOK ó 1
Cancelar vbCancel ó 2
Anular vbAbort ó 3
Reintentar vbRetry ó 4
Ignorar vbIgnore ó 5
Sí vbYes ó 6
No vbNo ó 7
If Resp = 6 Then
MsgBox "Ha pulsado SI"
Else
MsgBox "Ha pulsado NO"
End If
Si pulsamos el botón Sí obtendremos una acción, y si pulsamos el otro botón otra acción.
Ahora bien, para elegir o seleccionar un evento o acción, el usuario debe saber combinar los códigos,
sabiendo que un MsgBox posee la siguiente sintaxis principal:
MsgBox Mensaje, Botones, Título de la ventana
InputBox
El InputBox o caja de entrada es otra de las partes más utilizadas para la interacción del usuario con la
aplicación. Es importante que el usuario interactúe con la aplicación para ser el protagonista de esta.
El InputBox nos permite sacar una caja donde el usuario pasará un parámetro, valor o dato para que el
programa lo trate y lo ejecute.
El mensaje que quiere que aparezca se realiza de forma casi idéntica al MessageBox. Puede escribirse
varias líneas de texto seguidas por la constante de Visual Basic vbCrLf o salto de línea o párrafo.
Por ejemplo:
Dim Val As String
(El usuario teclea el nombre - Mundo Visual - y hace click en Aceptar. A continuación se muestra el
MsgBox - 2ª línea del código anterior )
Ahora bien, podemos determinar un texto predeterminado a la caja de entrada, como por ejemplo:
Es importante determinar que si el usuario elige el botón Cancelar, el programa devolverá una cadena
de caracteres igual a 0, es decir, Val ="".
La caja de entrada puede ser sin embargo más personalizada mediante dos parámetros como son la
posición de la ventana de entrada de datos en la pantalla. Estos parámetros se ponen a continuación del
ValorPredeterminado.
Por ejemplo:
Estas informaciones son las Propiedades del objeto App. Este objeto no tiene Métodos ni Eventos.
Comments (Propiedad)
Devuelve o establece una cadena que contiene comentarios sobre la aplicación en ejecución. Es de sólo
lectura en tiempo de ejecución.
CompanyName (Propiedad)
Devuelve o establece un valor de tipo String que contiene el nombre de la empresa o del creador de la
aplicación en ejecución. Es de sólo lectura en tiempo de ejecución.
Se establece esta propiedad en tiempo de diseño usando el cuadro de Opciones de EXE. Visual Basic
toma por defecto el nombre de la compañía con que se cargó VB.
EXEName (Propiedad)
Devuelve el nombre raíz (sin la extensión) del archivo ejecutable que se está ejecutando actualmente. Si
se utiliza en el entorno de desarrollo, devuelve el nombre del proyecto.
Esta propiedad se establece bien en el cuadro de Opciones, bien al guardar el archivo .EXE
FileDescription (Propiedad)
Devuelve o establece un valor de tipo String que contiene información de los archivo de la aplicación
en ejecución. Es de sólo lectura en tiempo de ejecución.
HelpFile (Propiedad)
Ya comentada mas atrás. Especifica el nombre (con su Path) del fichero de ayuda. Se introduce en
tiempo de diseño en el cuadro de Opciones. Puede cambiarse en tiempo de ejecución.
hInstance (Propiedad)
LSB Visual Basic – Guía del Estudiante Capítulo 1 Página 241
Devuelve el controlador de la instancia de la aplicación. Es un Long
Cuando se trabaja en un proyecto dentro del entorno de desarrollo de Visual Basic, la propiedad
hInstance devuelve el controlador de la instancia de Visual Basic.
LegalCopyright (Propiedad)
Devuelve o establece un valor de tipo String que contiene información de derechos de autor sobre la
aplicación en ejecución. Es de sólo lectura en tiempo de ejecución.
LegalTrademarks (Propiedad)
Devuelve o establece un valor de tipo String que contiene información de marca registrada sobre la
aplicación en ejecución. Es de sólo lectura en tiempo de ejecución.
Establezca esta propiedad en tiempo de diseño usando el cuadro de diálogo Opciones de EXE.
Devuelven o establecen el número mayor (menor) de la versión del proyecto. Es de sólo lectura en
tiempo de ejecución.
Revision (Propiedad)
Devuelve o establece el número de revisión de la versión del proyecto. Es de sólo lectura en tiempo de
ejecución.
Mediante las propiedades Major, Minor y Revision obtenemos la versión del programa. Es muy
util conocer la version del programa para saber si lo hay que actualizar. Por ejemplo, la
instrucción:
MiVariable = App.Major & “.” & App.Minor & “.” & App.Revision
MiVariable = 2.1.4
Así conocemos que la versión del programa es la 2.1.4. Estos datos de Major, Minor y Versión hay
que introducirlos en el programa durante el tiempo de diseño. Se usa para ello la caja de Propiedades
del Proyecto
Path (Propiedad)
Esta propiedad es sumamente útil. Cuando se realiza una aplicación, no es prudente obligar al usuario a
meterla dentro de un determinado directorio impuesto por el programador. Si no es así, no sabremos en
qué directorio está el ejecutable, y es muy conveniente saberlo, sobre todo cuando se utilizan ficheros
auxiliares de inicialización, que deberían estar en el mismo directorio de la aplicación. Si queremos
abrir el fichero MiAplicacion.Cfg que estará obligatoriamente en el mismo directorio de la aplicación
(aunque no conocemos su nombre), solamente tenemos que poner la línea de código : Open App.Path
& “\MiAplicacion.ICfg” For Input as # 1, sin importarnos cual es el nombre real de ese directorio.
PrevInstance (Propiedad)
Devuelve un valor booleano que indica si hay ya en ejecución una instancia anterior de una aplicación.
Es muy utilo saber que hay una instancia del programa en ejecución para impedir que se vuelva a abrir
otra.
Puede utilizar esta propiedad en un procedimiento de evento Load para determinar si el usuario ya está
ejecutando una instancia de una aplicación. Dependiendo de la aplicación, puede ser conveniente que
sólo haya una instancia de la misma en ejecución al mismo tiempo en el entorno operativo Microsoft
Windows.
ProductName (Propiedad)
Devuelve o establece un valor de tipo String que contiene el nombre de producto de la aplicación en
ejecución. Es de sólo lectura en tiempo de ejecución.
Establezca esta propiedad en tiempo de diseño usando el cuadro de diálogo Opciones de EXE.
El valor de la propiedad Revision está dentro del rango 0 a 9999.Esta propiedad proporciona
información sobre la versión de la aplicación en ejecución. Esta propiedad se establece en tiempo de
diseño usando el cuadro de diálogo Opciones de EXE.
StartMode (Propiedad)
Devuelve o establece un valor que determina si una aplicación se inicia como proyecto independiente o
como servidor de automatización OLE. De sólo lectura en tiempo de ejecución.
TaskVisible (Propiedad)
Devuelve o establece un valor que determina si la aplicación aparece en la lista de tareas de Windows.
La propiedad TaskVisible sólo puede establecerse a False en aplicaciones que no presenten interfaz,
como servidores OLE que no contengan o presenten objetos Form. Mientras la aplicación disponga de
interfaz gráfica, la propiedad TaskVisible se establece automáticamente a True.
Title (Propiedad)
Valor será una expresión de cadena que especifica el título de la aplicación. La longitud máxima de
valor es 40 caracteres.
Esta propiedad está disponible en tiempo de diseño en el cuadro de diálogo del comando Crear EXE del
menú Archivo.
Todos estos datos figuran en el archivo de proyecto (.VBP). Puede verse editándolo con el Block de
Notas de Windows, e incluso pueden modificarse estas propiedades directamente sobre ese archivo.
Lo que se va a explicar en este capítulo le será bastante familiar aunque nunca haya reparado en ello.
Posiblemente haya utilizado un procesador de texto en el que está escribiendo una carta, y antes de
terminar de escribir esa carta, comienza a escribir otro documento, y posiblemente otro, y tenga los tres
documentos en la pantalla al mismo tiempo, bien en ventanas escalonadas, (cascada), bien en ventanas
en forma de mosaico, o simplemente tapando unas a otras completamente. Los tres documentos están
en su procesador de textos, y puede actuar sobre uno u otro simplemente eligiendo el deseado mediante
el mecanismo que le proporciona su procesador de textos. Este sistema no es ni mas ni menos que una
interface de documentos múltiples. En programación, a este tipo de aplicaciones las denominamos
MDI
Para crear una aplicación MDI debe hacerse mediante un Formulario Padre al que se le añaden tantos
Formularios Hijo como documentos tengamos. Al formulario padre le denominamos Formulario
La única diferencia entre un formulario normal y un formulario Hijo es que éste tiene la propiedad
MDIChild a True.
Para realizar una aplicación MDI, lo primero que hay que hacer es introducir en ella un formulario
MDI. Para introducirlo, basta con hacer click en Insertar | Formulario MDI de la barra de menú.
Solamente se puede tener un formulario MDI en una aplicación. Puede observar que una vez que ha
introducido uno, la palabra Formulario MDI del submenú Insertar queda deshabilitada.
Una vez que tiene un formulario MDI puede introducir tantos formularios hijo como desee. Para que un
formulario sea formulario hijo basta con poner a True su propiedad MDIChild.
Aparte de las propiedades de un formulario normal, un formulario MDI tiene las siguiente propiedades :
Load NombreFormularioHijo
ActiveForm Mediante esta propiedad podemos conocer el formulario activo dentro de una aplicación
de documentos múltiples. (El formulario activo es aquel que tiene el foco)
MiVariable = ActiveForm.caption
ScrollBars Hace que el Formulario MDI (padre) muestre barras de Scroll para presentar en toda
su extensión a un formulario hijo, cuando las dimensiones de áste superan las de aquel.
Aparte de estas propiedades que diferencian un Formulario MDI de un Formulario normal, los
Formularios MDI presentan otras particularidades.
Inserción de Controles Solamente podrán introducirse en un formulario MDI aquellos controles que
tengan la propiedad Align. (Picture, Data, DBGrid) y solo permiten que se presenten con alineación a
uno de los lados del Formulario (Top, Bottom, Left o Right)
El control Picture puede trabajar como contenedor de otros controles. Por lo tanto, para poder introducir
cualquier control (TextBox, Label, CommandButton ...) será necesario introducir un control Picture, y
sobre el, poner los controles que se necesiten.
Línea de Menú. Cuando existe la línea de Menú en un Formulario MDI y en el Formulario Hijo que
introduzcamos en él, la línea de menú del Formulario MDI se sustituye por la línea de menú del
Formulario Hijo introducido.
Barra de Título. La Barra de Título del Formulario MDI se conserva siempre. Pero si el Formulario
Hijo insertado dentro de él está maximizado (ocupa toda la extensión del Formulario MDI), a la barra
de título se le añadirá la barra de Título del Formulario Hijo entre paréntesis.
Puede preparar los formularios hijo uno a uno e introducirlos dentro del formulario MDI según las
necesidades de la aplicación. Este sería el caso de una aplicación con varias pantallas, todas ellas
colocadas sobre una pantalla fija (Formulario MDI) del que se aprovecha quizás alguna parte como
parte común de toda la aplicación (Menú, Título, Barra de herramientas montada sobre un Picture, etc.)
Puede también darse el caso de introducir un número indeterminado de ventanas iguales para realizar
varias veces la misma función, pero sobre ventanas diferentes. (Caso del procesador de texto que tiene
varias ventanas de texto, cada una con un documento. Lo que desconocemos a priori es el número de
documentos que vamos a editar)
Para el primer caso, será necesario crear cada una de las ventanas, e introducirlas y quitarlas según pida
la aplicación.
En segundo caso, bastará con crear un formulario hijo con todas las partes necesarias para su correcto
funcionamiento, y luego, realizar tantas “fotocopias” de ese formulario como ventanas necesitemos. Lo
que introducimos en la aplicación son precisamente esas “fotocopias”, pero no el original, que lo
seguimos manteniendo intacto para volver a copiarlo si fuese necesario.
En realidad una Clase es la definición de un objeto Visual Basic. Un objeto Visual Basic puede ser un
Formulario, un control, un objeto de acceso a datos.
La Instancia es la réplica de una clase. Puede ser la réplica de un Formulario, de un control o de otro
objeto. La Instancia lleva las mismas Propiedades que la clase. Se dice que hereda las propiedades.
(Excepto la propiedad Visible, que siempre, por defecto, aparece a False). Cuando se varía una
propiedad de una Instancia, no se altera el valor de esa propiedad en la Clase ni en ninguna de las
restantes Instancias.
Después de toda esta teoría, ¿podemos saber como se crea una Clase de un formulario ? O dicho de
manera mas coloquial, ¿Cómo se crea un Formulario para poder hacer varias “fotocopias” de él ?
La respuesta es obligatoriamente mas sencilla que la teoría. Con el formulario vacío que tengamos en el
proyecto (Insertemos un Formulario si fuese necesario) pongámosle todos los controles que deseemos.
Le podemos poner un Menú y cambiar a nuestro antojo todas sus propiedades. Entre ellas, la propiedad
MDIChild. Si vamos a introducir las Instancias de ese Formulario en un Formulario MDI esa
propiedad debe estar a True, y por lo tanto sus Instancias saldrán igualmente con esa propiedad a True.
Pongámosle un nombre y ya está creada la clase. Supongamos que ese nombre es FormularioHijo
Para crear ahora Instancias de ese Formulario podemos hacerlo de dos formas :
Declarar una variable tipo Objeto. No se asuste. Para declarar que una variable es un Formulario basta
con declararla de la siguiente forma :
Esta variable debe declararla en sitio adecuado para su aplicación, y el ámbito de esa variable objeto
será el mismo que para cualquier tipo de variable. (Vea Ambito de las Variables) La sentencia a utilizar
para la declaración será Dim, Private, Public o Global tal como se explicó para las variables.
Una vez declarada como variable puede hacerla igual a un objeto existente que servirá de modelo (Una
Clase) que estará definida por un nombre: (P.e. FormularioHijo)
Una vez creado la instancia del formulario debemos cargarlo en el Formulario Padre. Para cargarlo
debemos emplear la sentencia Load MiVentana, con lo que quedará cargado en la memoria, pero,
Es muy práctico poner un Caption distinto a cada formulario que se introduzca, caso de introducirse
varios formularios hijo iguales. El Caption es una propiedad y por lo tanto todas las instancias heredan
el Caption de la Clase. Sería prudente distinguir un formulario de otro mediante su Caption, es decir
mediante su barra de título.
Para ello podemos crear un contador en el mismo procedimiento en el que creamos una nueva instancia,
y poner el Caption de cada nuevo Formulario siguiendo un orden numeral.
Si el código donde vamos a nombrar ese formulario está fuera de él (P.e. en el Formulario Padre)
deberemos referirnos al formulario hijo mediante ActiveForm. ActiveForm nos va a indicar cual es
el formulario que está actualmente activo. Un formulario está activo cuando estamos trabajando sobre
él. En ese momento tiene el foco. Permanece activo desde que hacemos click con el ratón sobre
cualquiera de sus partes, hasta que activamos otro formulario. Es sencillo reconocer cual es el
formulario activo pues tiene su barra de título con el color vivo.
Cada vez que hacemos una operación sobre una parte de un formulario éste se pondrá activo. Por
ejemplo, si el formulario es un documento de texto, y contiene el texto en un RichTextBox de nombre
RTB1, si queremos hacer una operación con el texto desde un botón colocado en el formulario padre
(poner en negrita el texto seleccionado), haríamos lo siguiente :
ActiveForm.RTB1.SelBold = True
ya que siempre estaremos seguro de que el Formulario Activo es aquel en el que acabamos de
seleccionar el texto.
Si el botón donde hemos puesto el botón no es el formulario padre, sino el hijo, tenemos un problema
similar. Su nombre será (con los ejemplos anteriores) MiVentana, y pueden existir varios formularios
con ese nombre, tantos como documentos hayamos introducido. No podemos por tanto nombrarlo con
su nombre, pues hay (o puede haber) varios. Tampoco lo podemos nombrar con ActiveForm, ya que
esta propiedad corresponde al Formulario Padre. La solución es nombrarle mediante Me. Me siempre
se refiere al formulario que contiene al procedimiento donde está esa palabra. Por lo tanto, si tenemos
un botón en el formulario hijo con la instrucción :
Me.RTB1.SelBold = True
donde
Las ventanas o los iconos se pueden distribuir incluso si el objeto MDIForm está minimizado. Los
resultados son visibles cuando el objeto MDIForm se maximiza.
ZOrder es un método que no solamente se puede emplear con formulario hijo, sino con cualquier
instancia. Puede emplearlo también con cualquier control. Pero lea detenidamente la Ayuda de este
método, ya que no todos los controles la admiten.
El control RichTextBox es una caja de texto con mas prestaciones que el TextBox. No está
normalmente en la caja de herramientas, por lo que habrá que ir a Proyecto | Componentes y elegir
Microsoft RichTextBox Control. El icono que presenta en la caja de herramientas es el siguiente :
Frente a la rigidez del TextBox, este control nos permite escribir un texto utilizando distintos tipos de
fuentes en el mismo texto, e introducir mas de 65.536 caracteres, límite máximo del TextBox. Aparte
de estas, tiene otras características respecto a la forma de guardar y leer el texto en un fichero, que le
convierten en una herramienta muy útil para el diseño de aplicaciones en las que haya que introducir
documentos de texto.
El RichTextBox puede utilizar indistintamente formato de texto Ascii (que le llamaremos Texto Plano)
o formato RTF (RichTextFormat, que llamaremos Texto enriquecido) El formato de Texto enriquecido
es un formato de intercambio entre procesadores de texto. Vea al final del capítulo una explicación más
detallada de este formato de texto. De momento basta con decir que permite poner tipos distintos de
letras, de tamaños, de colores, introducir gráficos, y toda una serie de ventajas que le van a permitir
realizar procesadores de texto casi tan perfectos como los editores comerciales mas conocidos. Esto nos
permite por ejemplo, guardar estrictamente las letras que componen el texto (Propiedad Text de
RichTextBox) o guardar el texto, con sus letras y todos los adornos que queramos ponerles (Tipos
distintos de fuente, negrita, cursiva, …). Esto lo logramos con la propiedad TextRTF del RichTextBox.
Cuando hablamos de guardar, nos estamos refiriendo lógicamente a guardarlo en un fichero en el disco,
y también a guardarlo en el portapapeles. Si tenemos un RichTextBox de nombre RTB (así le
llamaremos en todos los ejemplos) con un texto tal como
Te creías muy listo Flanahan, pero tus vacas no pasarán por mis tierras
RTB.Text = Te creías muy listo Flanahan, pero tus vacas no pasarán por mis tierras
La propiedad TextRTF contiene toda la información, pero eso sí, en formato RTF
(Puede que el texto anterior tenga alguna diferencia sobre el real, debido a que hubo que introducirle
algún retorno de carro para poder presentarlo)
En el ejemplo, el texto seleccionado es ejemplo nos permitirá, texto que hemos seleccionado con el
ratón. Las propiedades anteriores tomarán estos valores para ese texto seleccionado :
Recuerde que estas propiedades son tanto de lectura como de escritura. Es decir, puede seleccionar un
texto con el ratón y analizar ese texto seleccionado, o seleccionar el texto dándole valores a estas
propiedades.
Las diferencias entre uno y otro control comienzan ahora. En un RichTextBox, con un texto
seleccionado, podemos cambiar el tipo de letra, su tamaño, su color, etc.
RTB.Font.Name = CD1.FontName
RTB.Font.Size = CD1.FontSize
RTB.Font.Bold = False
RTB.Font.Italic = False
Con la propiedad Font podemos escribir en el RTB usando la misma letra para todo el texto. Sin
embargo verá que esta propiedad prácticamente no se va a usar, ya que el RTB tiene la gran ventaja que
puede usar varios tipos de letra y tamaños dentro de un mismo texto. Puede usar también varios colores.
Para poder cambiar de letra no usaremos Font.Name, sino SelFontName, (Fíjese que esta propiedad no
lleva ningún punto intermedio) que cambia el tipo de letra en el texto que hayamos seleccionado, y si
no hemos seleccionado ningún texto, y el cursor de escritura se encuentra al final del texto escrito, lo
que hará será cambiar el tipo de letra a partir de ese punto.
Si el texto seleccionado está en la parte final del texto, o si el cursor de escritura está al final del escrito
y no se ha seleccionado ningún texto, la propiedad elegida permanecerá vigente para la escritura que se
realice a partir de ese punto.
Al igual que las otras propiedades, si el texto seleccionado está al final del texto, o si el cursor de
escritura está al final del escrito, la propiedad elegida permanecerá vigente para la escritura que se
realice a partir de ese punto.
Estas propiedades son de lectura y escritura, de forma que pueden tener estas dos sintaxis:
Esta instrucción sustituye todo el contenido del RTB por el texto Siempre nos quedará París
La propiedad TextRTF es similar, pero con texto en formato enriquecido. Si la usamos como lectura:
MiVariable contendrá el texto, más los caracteres que definen el tipo de letra, tamaño, etc, como vimos
al principio.
RTB.TextRTF = TuVariable
Si TuVariable contiene un texto en formato RTF, el contenido de RTB será justamente ese texto, con
todas sus florituras de tipo de letra, tamaño, negrita, etc. Si TuVariable contuviese un texto en formato
de texto plano, lo escribirá tal cual, con el tipo y tamaño de letra que tenga en su propiedad Font.
SelText
Devuelve o establece el texto seleccionado en formato de texto plano. No está disponible en tiempo de
diseño.
Al ser una propiedad de lectura y escritura, nos permite:
Escritura: añadir texto (que se colocará en la posición en la que esté el curso) o cambiar el texto
seleccionado por otro. La instrucción
MiVariable = RTB.SelText
MiVariable tomará el valor del texto que estuviese seleccionado en formato de texto plano.
SelRTF
Devuelve o establece el texto seleccionado en formato .RTF. Al igual que SelText es de lectura y
escritura. No está disponible en tiempo de diseño.
Si el contenido de MiVariable está en formato RTF, sustituirá el texto que tuviésemos seleccionado por
el contenido de MiVariable, y lo escribirá con todos los detalles de tipo de letra, tamaño, etc., que
contuviese el formato RTF. Si no hubiésemos seleccionado previamente ningún texto, escribirá ese
texto en el lugar donde estuviese colocado el cursor.
Si el contenido de MiVariable fuese un texto plano, escribirá ese texto, usando las mismas propiedades
para la letra que tuviese el texto seleccionado previamente, o las del punto donde se encontrase el
cursor.
Esta propiedad es equivalente a la propiedad SelText y funciona de forma idéntica, pero en este caso el
texto reemplazado o devuelto mediante la propiedad SelRTF está en formato RTF. Esta propiedad
devuelve una cadena de longitud cero ("") si no hay texto seleccionado en el control.
La medida de estos márgenes y sangrías se realiza en las unidades de medida del formulario
que contiene al RichTextBox. Veamos un ejemplo comentado de estas propiedades.
RTB.SelIndent = Val(TBSangria)
Separa el párrafo correspondiente a la línea donde está en ese momento al cursor. (Que a
partir de ahora la llamaremos Línea en curso) El espacio que separa ese párrafo del borde
izquierdo del RichTextBox es un valor igual al que hayamos introducido en el TextBox
TBSangría, medido en las unidades de medida del formulario que contiene a RTB. Recuerde,
sólo hace la sangría con el párrafo que contiene la línea en curso.
Si queremos separar varios párrafos, debemos seleccionar esos párrafos antes de ejecutar la
instrucción anterior. Para poner una separación desde el borde izquierdo de todas los
párrafos del texto deberemos primero, seleccionar todo el texto, y luego ejecutar la
instrucción anterior. Esto podemos lograrlo combinando estas tres instrucciones:
RTB.SelStart = 1
RTB.SelLength = Len(RTB.Text)
RTB.SelIndent = Val(TBSangria)
RTB.SelRightIndent = Val(TBMargenDcho)
Separamos el final del párrafo donde está la línea en curso una distancia igual al valor de
TBMargenDcho. Esta propiedad es completamente equivalente a la anterior, esta referida al
margen derecho y aquella al margen izquierdo. Es aplicable todo lo que se dijo para SelIndent.
RTB.RightMargin = Val(TBAncho)
La instrucción anterior fija como distancia máxima que puede ocupar una línea de texto la
cantidad introducida en TBAncho. Por supuesto, expresada como siempre en las unidades de
medida del formulario. La línea puede ser mas ancha que el ancho del RichTextBox. En este
caso la línea no cabe en el RTB, pero puede visualizar la línea completa usando baraas de
scroll horizontales (Vea propiedad ScrollBars más adelante)
Puede hacer lo que parece mas lógico, separar la primera línea un poco respecto a las demás,
dando un valor negativo a la propiedad SelHangingIndent. Pero para que esto resulte, debe
poner un valor al menos igual a la propiedad SelIndent. En el ejemplo siguiente, hacemos una
sangría solamente a la primera línea del párrafo donde está la línea en curso:
RTB.SelIndent = Val(TBSangriaEsp)
RTB.SelHangingIndent = -Val(TBSangriaEsp)
(Observe el signo menos en la segunda línea) El valor de la sangría de la primera línea será el
contenido en TBSangriaEsp
Viñetas
El RichTextBox es, como decíamos al principio, un control que nos permite realizar editores de textos
muy potentes. Incluso podemos poner viñetas
Una viñeta es un párrafo marcado con un punto en su comienzo, y que lleva una cierta sangría
respecto al borde izquierdo del RichTextBox. Para poner una viñeta es necesario jugar con
dos propiedades:
SelBullet, propiedad Booleana que si la hacemos True convertimos el párrafo donde está la línea en
curso en una viñeta. Si está en False, ese párrafo será un párrafo normal.
BulletIndent, propiedad a la que le pondremos un valor numérico igual a la separación que queremos
poner en el texto de la viñeta.
Para que tenga efecto la propiedad BulletIndent, la propiedad SelBullet debe estar puesta a True. No
tendrá efecto alguno si SelBullet = False. Por ejemplo, para poner una viñeta podemos poner
RTB.SelBullet = True
RTB.BulletIndent = 500
Una vez puesta una viñeta en el RichTextBox, insertará otro cada vez que pulsemos ENTER. Para
quitarlo, basta con ejecutar la sentencia
SelBullet = False
Null. La selección abarca más de un párrafo y contiene una mezcla de estilos de viñeta y no viñeta.
True. Los párrafos de la selección tienen estilo de viñeta.
False. La párrafos de la selección no tienen estilo de viñeta.
El primer problema es que si estamos escribiendo sobre el RichTextBox y pulsamos Tabulador, el foco
se nos escapa hacia el siguiente control, según el orden de la propiedad TabIndex. Existe una solución,
que es pulsar la combinación de las teclas Ctrl + Tab. Sin embargo esa posibilidad no es recomendable,
dado que un usuario normal no está acostumbrado a realizar esa combinación para forzar una
tabulación.
La solución está en poner la propiedad TabStop de todos los controles a False, cada vez que el
RichTextBox toma el foco. Es decir, en su procedimiento GotFocus. De esta forma, al dar el tabulador
ningún control se “querrá” quedar con el foco, y por lo tanto el foco seguirá en el RichTextBox. En
estas condiciones el resultado es que el cursor de escritura pasará a la siguiente tabulación.
Vamos a ver como se puede poner la propiedad TabStop a False en todos los controles del formulario.
Para ello vamos a explicar un nuevo objeto Visual Basic, el objeto Controls. Este objeto es una
colección, y está formada por todos los controles del formulario. Como cualquier colección tendrá una
propiedad, la propiedad Count que toma el valor igual al número de controles existentes en el
formulario. Como cualquier cosa de VB formada por varios elementos, cada uno de ellos se distingue
por su índice. Y este índice comienza por el 0 y termina por el n-1, siendo n = número de elementos de
esa colección.
Para poner la propiedad TabStop a False en todos los controles de la colección Controls del formulario,
basta con poner este código en el procedimiento GotFocus del RTB
Podemos hacerlo de otra forma. Declaremos una variable tipo Objeto Control
Nota. Si va a la información del RichTextBox podrá ver un ejemplo de esto, con el siguiente código:
For Each Control In Controls
Propiedad SelTabCount
RTB.SelTabCount = 5
Propiedad SelTabs
Establece el valor numérico (separación desde el borde izquierdo) de las tabulaciones. Dado que pueden
existir varias tabulaciones, deberemos distinguirlas entre ellas mediante un índice. El índice para la
primera tabulación es el 0
RTB.SelTabs(0) = 1000
RTB.SelTabs(1) = 2000
RTB.SelTabs(2) = 3000
RTB.SelTabs(3) = 4000 Con estas líneas determinaríamos la posición de tabulación de
RTB.SelTabs(4) = 5000 los 5 tabuladores.
Antes de darle valor a la propiedad SelTabs deberemos haber creado los tabuladores
mediante SelTabCount. Si pretende darle valor a un tabulador mayor que el número de
tabuladores real (por ejemplo, si pone en el caso anterior RTB.SelTabs(5) = 5000) le dará un
error.
Recuerde que los valores de los tabuladores debe darlos en las unidades de medida del formulario.
OTRAS PROPIEDADES
Propiedad SelCharOffset
Nos permite crear subíndices y superíndices. La sintaxis es:
RTB.SelCharOffset = Número
Esta forma de escribir subíndices y superíndices se debe aplicar cada vez que queramos escribir uno de
ellos, e inmediatamente, poner esa propiedad a 0, ya que si no lo hacemos así, escribiría como
subíndice o superíndice el resto del texto. P.e. para poner un superíndice:
TamIni = RTB.SelFontSize
RTB.SelFontSize = TamIni - 4
RTB.SelCharOffset = 40
RTB.SetFocus
RTB.SelFontSize = TamIni
Propiedad SelProtected
Es una propiedad Booleana, que nos permite proteger contra cualquier cambio a una parte del
texto (o todo el texto) que contiene el RTB. Para proteger una parte del texto basta con
seleccionarla y a continuación ejecutar la instrucción
RTB.SelProtected = True
Una vez protegida una parte del texto, esa parte no se puede variar. Puede desprotegerse, volviendo a
seleccionarla y ejecutando la instrucción:
RTB.SelProtected = False
Propiedad ScrollBars
Pone barras de desplazamiento al RichTextBox.
Esta propiedad puede establecerse a 0 (None, ninguna), a 1 (Horizontal), 2 (Vertical) o 3 (Both, ambas).
Cuando las barras de desplazamiento no son necesarias, bien porque hay pocas líneas, bien porque hay
pocos caracteres por línea, las barras de desplazamiento están desactivadas.
Es frecuente pensar que las barras de desplazamiento horizontal no funcionan. Y eso ocurre porque
siempre se ven desactivadas. En realidad lo que pasa es que solamente se activan cuando la línea de
texto es mas ancha que el ancho del RichTextBox. Puede ocurrir eso cuando la propiedad RightMargin
tiene un valor superior a la anchura del control, circunstancia que nos permite escribir saliéndonos del
control, y es en solamente en ese caso en el que se activa la barra de scroll horizontal.
Propiedad DisableNoScroll
Devuelve o establece un valor que determina si están desactivadas las barras de desplazamiento en el
control RichTextBox.
Propiedad Appearance
Plano o tridimensional, como en el resto de los controles.
Propiedad AutoVerbMenu
Propiedad Booleana que indica si se presenta un menú emergente cuando el usuario hace click con el
botón derecho del ratón. El menú emergente muestra los comandos de Deshacer (Ctrl-Z) cortar (Ctrl-
X), copiar (Ctrl-C), pegar (Ctrl-V) y Eliminar (Supr) Pero no es necesario escribir código en ninguna
parte para que se realicen estas operaciones (Por una vez, VB nos regala unas operaciones. No es VB,
es Windows directamente)
Propiedad BorderStyle
Sin borde o con borde (None o Fixed Single)
Propiedad Enabled
Propiedad Booleana. Activa o desactiva el RichTextBox
FileName
Variable = RTB1.Filename
obtendremos en Variable el nombre (y Path) del fichero que tenemos cargado en el RTB
HideSelection
Devuelve o establece un valor que determina si el texto seleccionado aparece resaltado cuando el
RichTextBox pierde el enfoque. Esto es justamente lo que ocurre cuando seleccionamos un trozo de
texto (Por ejemplo para pasar ese texto a negrita) y hacemos click sobre otro control (Por ejemplo,
sobre un botón de comando para cambiar a Negrita) Si tenemos esta propiedad a False el texto
seleccionado sigue seleccionado. Si está a True, el texto se deselecciona.
NOTA Le recomiendo que cuando tenga un control para cambiar el tipo de letra, hágalo sobre un
control que no acepte el foco (Label, p.e.)
Propiedad Locked
Igual que le ocurre al TextBox, si ponemos esta propiedad a True impedimos que se pueda cambiar el
texto existente en el RichTextBox mediante el teclado.
MaxLength
Esta propiedad marca el número máximo de caracteres que puede contener. Si se pone a 0
(Predeterminado) admite cualquier número de caracteres.
MousePointer
Igual que para el resto de los controles.
Multiline
Igual que para el TextBox. Si está a True (predeterminado) el RichTextBox puede contener varias
líneas. Si está a False, una solo.
OLEDragMode
OLEDropMode
Estas dos propiedades son similares a la DragMode de otros controles. Se verán con mas detalle al
estudiar el Drag & Drop.
El control RichTextBox cuenta con unos métodos especiales para abrir un fichero y guardar el texto que
contiene un poco especiales. Estos métodos (SaveFile y LoadFile) se pueden usar solamente cuando
queremos guardar o leer el texto en formato .RTF. Con ellos no es necesario abrir el fichero (con Open
Nombrefichero .....) ni cerrarlo, pero siempre para guardar o leer texto en formato RTF. Podemos leer o
guardar el texto de un RichTextBox como texto plano (Como los ficheros ASCII .TXT). Para ello
debemos utilizar los métodos Open Nombrefichero For Input / Output vistos en el capítulo de ficheros.
Donde nombre_ruta (Parámetro requerido) es una expresión de cadena que define la ruta de acceso y el
nombre del archivo que va a recibir el contenido del control, y tipo_archivo es un entero o una
constante que especifica el tipo de archivo cargado, como se describe a continuación :
Ejemplo
RTB1.SaveFile “C :\CursoVB\mitexto.rtf”, 0
Guarda el contenido del RichTextBox RTB1 en un fichero llamado mitexto.rtf que está en el directorio
CursoVB, con formato RTF
Aparte del método SaveFile, puede utilizar la función Print de Visual Basic y las propiedades
TextRTF y SelRTF del control RichTextBox para escribir archivos .RTF. Por ejemplo, puede guardar
el contenido de un control RichTextBox en un archivo .RTF de este modo:
METODO LoadFile
Donde nombre_ruta (Parámetro requerido) es una expresión de cadena que define la ruta de acceso y el
nombre del archivo que se va a cargar en el control, y tipo_archivo es un entero o una constante que
especifica el tipo de archivo cargado, como se describe a continuación
Al cargar un archivo con el método LoadFile, el contenido del archivo cargado reemplaza a todo el
contenido del control RichTextBox. Esto hace que cambien los valores de las propiedades Text y
rtfText.
También puede usar la función Input de Visual Basic y las propiedades TextRTF y SelRTF del control
RichTextBox para leer archivos .RTF. Por ejemplo, puede cargar el contenido de un archivo .RTF en el
control RichTextBox de este modo:
METODO Find
Si se encuentra el texto buscado, el método Find resalta el texto especificado y devuelve un número con
la posición del primer carácter resaltado. Si no se encuentra el texto especificado, el método Find
devuelve –1.
Si utiliza el método Find sin la opción rtfNoHighlight aunque la propiedad HideSelection sea True y el
control RichTextBox no tenga el enfoque, el control seguirá resaltando el texto encontrado. Los usos
posteriores del método Find sólo buscarán el texto resaltado hasta que se mueva el punto de inserción.
El comportamiento de búsqueda del método Find varía según la combinación de valores especificados
para los argumentos inicio y fin. Esta tabla describe los comportamientos posibles:
METODO GetLineFromChar
Devuelve el número de la línea que contiene una posición de carácter especificado en un control
RichTextBox.
Donde pos_carácter (Requerido) es un entero largo que especifica la posición del carácter cuya línea
desea identificar. El índice del primer carácter del control RichTextBox es 0.
Utilice el método GetLineFromChar para averiguar qué línea del texto de un control RichTextBox
contiene una determinada posición de carácter. Es posible que necesite hacerlo porque puede variar el
número de caracteres de cada línea, lo que hace muy difícil averiguar qué línea del texto contiene un
determinado carácter, identificado por su posición en el texto.
METODO SelPrint
Sintaxis NombreRTB.SelPrint(hdc)
Si hay texto seleccionado en el control RichTextBox, el método SelPrint sólo enviará el texto
seleccionado al dispositivo de destino. Si no hay texto seleccionado, se enviará el contenido completo
del control RichTextBox al dispositivo.
El método SelPrint no imprime texto desde el control RichTextBox. En su lugar, envía una copia del
texto con formato a un dispositivo que pueda imprimirlo. Por ejemplo, puede enviar el texto al objeto
Printer utilizando código como éste:
RichTextBox1.SelPrint(Printer.hDC)
Observe que la propiedad hDC del objeto Printer se utiliza para especificar el argumento de contexto de
dispositivo del método SelPrint.
Nota Si utiliza el objeto Printer como destino del texto desde el control RichTextBox, deberá
inicializar en primer lugar el contexto de dispositivo del objeto Printer. Esto es necesario, ya que
Visual Basic no conoce el hDC del Printer hasta que se imprime algo. La información de Microsoft
recomienda imprimir una cadena de longitud cero.
(Ejecutar la instrucción Printer.Print “”) Sin embargo esa no es buena solución ya que da un error de
impresora. Vale mas forzar la posición del papel, aunque no hiciese falta
Printer.Orientation = 1
Mediante SelPrint nos podemos ahorrar la tediosa programación del Printer, pero tiene también
inconvenientes: No controlamos el cambio de página, si tenemos papel preimpreso es muy difícil
ajustarlo, etc.
METODO Span
donde :
juego_caracteres (Requerido) Una expresión de cadena que especifica el juego de caracteres que se va
a buscar al ampliar la selección, basándose en el valor de negado.
hacia_adelante (Opcional) Una expresión booleana que determina en qué sentido se mueve el punto de
inserción, como se describe mas adelante.
Negado (Opcional) Una expresión booleana que determina si los caracteres de juego_caracteres
definen el conjunto de caracteres de destino o se excluyen del conjunto de caracteres de destino, como
se describe mas adelante.
True (Predeterminado) Selecciona texto desde el punto de inserción actual o desde el principio de la
selección actual hacia delante, hacia el final del texto.
False Selecciona texto desde el punto de inserción actual o el principio de la selección actual hacia
atrás, hacia el principio del texto.
True Los caracteres incluidos en la selección son los que no aparecen en el argumento
juego_caracteres. La selección se detiene en el primer carácter encontrado que aparece en el argumento
juego_caracteres.
False (Predeterminado) Los caracteres incluidos en la selección son los que aparecen en el
argumento juego_caracteres. La selección se detiene en el primer carácter encontrado que no aparece
en el argumento juego_caracteres.
METODO Upto
Mueve el punto de inserción hasta el primer carácter (sin incluirlo) que sea miembro del conjunto de
caracteres especificado en un control RichTextBox.
Donde :
juego_caracteres (Requerido) Una expresión de cadena que especifica el conjunto de caracteres que se
va a buscar al mover el punto de inserción, basándose en el valor de negado.
hacia_adelante (Opcional) Una expresión booleana que determina en qué sentido se mueve el punto
de inserción, como se describe en Valores.
negado (Opcional) Una expresión booleana que determina si los caracteres de juego_caracteres
definen el conjunto de caracteres de destino o se excluyen del conjunto de caracteres de destino, como
se describe en Valores.
Valores
True (Predeterminado) Mueve el punto de inserción hacia delante, hacia el final del texto.
False Mueve el punto de inserción hacia atrás, hacia el principio del texto.
El Portapapeles y el RichTextBox
Imagínese que seleccionamos un texto en un RichTextBox y ese texto lo metemos al portapapeles.
Dado que el texto está escrito en RTF, ¿Como nos lo guarda el Portapapeles ?
La solución es que puede guardarlo en las dos versiones. En formato de texto plano (Guarda
estrictamente los caracteres ASCII del texto seleccionado) o como texto enriquecido (RTF), guardando
en este caso, además del texto limpio y puro, la información del tipo de letra, tamaño, color, etc. típicas
del formato RTF.
Para ello debemos indicarle al portapapeles en qué formato queremos guardarlo. La línea de código :
guarda en el portapapeles todo el contenido del RichTextBox (llamado RTB1 en los ejemplos) en
formato RTF
Las líneas :
guardarán, respectivamente, el texto seleccionado y todo el texto de RTB1, en formato de texto plano
ClipBoard.SetText RTB1.SelRTF
Clipboard.SetText RTB1.TextRTF
Clipboard.SetText RTB1.Text
APENDICE
Propiedad Appearance
rtfFlat 0 Uniforme. Pinta sin efectos visuales.
rtfThreeD 1 (Predeterminado). 3D. Pinta con efectos tridimensionales.
Método Find
rtfWholeWord 2 Determina si una coincidencia se basa en una palabra completa o en
parte de una palabra.
rtfMatchCase 4 Determina si una coincidencia se basa en el uso de mayúsculas y
minúsculas de la cadena especificada además del texto de la cadena.
rtfNoHighlight 8 Determina si una coincidencia aparece resaltada en el control
RichTextBox.
Propiedad Selalignment
rtfLeft 0 (Predeterminado) Izquierda. El párrafo se alinea a lo largo del margen
izquierdo.
rtfRight 1 Derecha. El párrafo se alinea a lo largo del margen derecho.
rtfCenter 2 Centro. El párrafo se centra entre los márgenes izquierdo y derecho.
Propiedad Scrollbars
rtfNone 0 (Predeterminado) Ninguna.
rtfHorizontal 1 Sólo barra de desplazamiento horizontal.
rtfVertical 2 Sólo barra de desplazamiento vertical.
rtfBoth 3 Barras de desplazamiento horizontal y vertical.
El FORMATO RTF
Cuando se edita un texto mediante un procesador de textos, el fichero resultante se guarda don un
formato distinto para cada procesador. De esta forma, un texto editado en WP no es compatible con el
P.T. AmiPro, con Word o con cualquier otro. Los fabricantes de estos procesadores de textos han tenido
que incluir una herramienta capaz de convertir un formato a otro para poder alcanzar la compatibilidad
entre ellos que el mercado exigía.
El Formato de Texto Enriquecido pretende ser un nexo de unión entre todos los procesadores de texto,
para poder intercambiar ficheros editados en uno u otro. De hecho, las últimas versiones de los mas
importantes procesadores de textos incluyen la posibilidad de guardar y buscar el texto en este formato.
(WP, Word)
Este formato consiste en guardar mediante caracteres ASCII plenamente legibles tanto el texto escrito
como los tipos de letra, tamaño, saltos de carro, etc. Veamos un ejemplo comparativo del mismo texto
escrito en Word, guardado en RTF y en ASCII :
Texto1
Este texto está escrito en Word. Observe que podemos poner letra negrita, letra cursiva, letra
subrayada. Podemos cambiar el color de las letras, rojo, verde, azul. Podemos cambiar el tamaño de las
letras a tamaño mas grande, mas pequeño, etc.
Fin Texto 1
Texto1
Este texto est escrito en Word. Observe que podemos poner letra negrita, letra cursiva, letra
subrayada. Podemos cambiar el color de las letras, rojo, verde, azul. Podemos cambiar el tamaño de las
letras a tamaño mas grande, mas pequeño, etc.
Fin Texto 1
Y ahora el mismo texto en formato RTF. En este formato hubo que seccionar las líneas para poder
mostrarlas en una hoja, ya que RTF utiliza líneas sin retornos de carro. Se han seccionado las líneas
terminándolas con un guión bajo y comenzando en la línea siguiente también con un guión bajo.
Como puede observar, el RTF incluye el texto escrito casi en ASCII, pero añadiendo una serie de datos
respecto al tipo de letra, codifica los acentos, las eñes, y hasta incluye, tomándolo del ordenador, el
nombre del operador que lo ha escrito. Estas informaciones también se guardan cuando se archiva un
texto en el formato propio del procesador de textos, pero lo hace en binario, por lo que no lo podemos
visualizar. El formato RTF, dentro de que mete toda esa información adicional, lo archiva con
caracteres ASCII.
MUY IMPORTANTE
Observe que el fichero .RTF comienza por {\rtf Cuando tenga que importar un texto hacia
un RichTextBox, puede que ese texto esté en formato RTF o como Texto Plano
(Fichero ASCII puro) Para saber si el texto está en RTF analice los Cinco primeros
caracteres del texto a importar. Si son {\rtf es que está en presencia de un texto
RTF.
Para saber si un fichero contiene texto enriquecido, basta con abrirlo como un
fichero secuencial, (recuerde que un fichero .RTF tiene solamente caracteres ASCII)
y leer los cinco primeros caracteres.
Procedimientos y funciones en VB
CORRECCION DE ERRORES Y DEPURACION DE PROGRAMAS - EL
OBJETO ERROR
LA AYUDA DE WINDOWS
Procedimientos
Un Procedimiento en Visual Basic es un trozo de código que realiza una determinada tarea. Un
procedimiento es el código que asociamos a un evento de un control (CommandButton_Click,
Form_Load, ...). Un control puede tener por lo tanto, muchos procedimientos asociados. Uno a cada
uno de sus eventos.
Si queremos realizar una determinada tarea en un programa, y esta tarea se repite muchas veces en ese
programa, podemos, por ejemplo, repetir el código tantas veces como sea necesario en los puntos del
programa que así lo pidan. Esto nos llevaría a escribir líneas y líneas repetidas en nuestras aplicaciones,
con el consiguiente incremento de trabajo y del volumen de la aplicación.
Podemos hacer otra cosa mas práctica y elegante. Escribir ese código una sola vez, creando con él un
Procedimiento. Este Procedimiento tendrá un nombre, y cada vez que queramos que se ejecute ese
código bastará con nombrar por ese nombre al Procedimiento.
En esta ventana debe teclear el nombre que quiere dar al Procedimiento. En tipo debe elegir la opción
Procedimiento (Veremos mas adelante la Función y en otro capítulo las opciones Propiedad y Evento)
y en el Ambito debe elegir Public o Private dependiendo del ámbito que quiera darle :
Si se ha insertado en un Módulo, puede citarse solamente por su nombre. Puede citarse también por el
nombre del módulo seguido por el nombre del procedimiento, separando ambos por un punto. Si el
procedimiento tiene por nombre NombreProcedimiento y se ha insertado en el Módulo1 puede citarse
de las dos formas siguientes en cualquier parte del programa:
NombreProcedimiento
Modulo1.NombreProcedimiento
Si se ha insertado en un Formulario, desde ese Formulario basta con citarle por su nombre. Desde otro
Formulario o Módulo, hay que citarlo mediante el nombre del Formulario donde está insertado, seguido
del nombre del procedimiento, separados por un punto.
Private. Si elige este ámbito, sólo se podrá acceder a ese Procedimiento desde el Formulario o Módulo
donde se haya insertado.
La caja de opción (Check) que pone Todas las variables locales como estáticas nos va a poner todas
las variables declaradas dentro del procedimiento como estáticas (No ponen a cero o nulo su valor
cuando salimos y volvemos a entrar en ese procedimiento). Puede ahorrarnos un poco de código.
El código de los Procedimientos se guarda en el General del Formulario o Módulo donde se han
insertado :
fpublico
Call fpublico
No hace falta poner Call, aunque Visual Basic lo admite. Algunos programadores me dicen que al
poner Call, siempre se enteran mejor de que están llamando a un procedimiento. Me parece muy bien, y
creo que es una buena razón. Pero no existe otra razón para ello.
Los datos que se puedan generar en un procedimiento debe extraerlos de ese procedimiento mediante
variables. Verá que con las funciones es distinto.
RutErr:
End Sub
Esto puede producirse serios problemas a la hora de depurar su programa. Para evitar que suceda eso, y
que se pare la ejecución del programa en la línea del procedimiento Calcula_Dietas en la que se
produjo el error, inicie este procedimiento con una instrucción que anule la condición de tratamiento de
errores:
Funciones
Una función es una forma especial de realizar un procedimiento. En realidad es un procedimiento al
que le pasamos una o varios parámetros con los que realizará una operación (cualquier operación, no
tiene porqué ser matemática) y obtendrá un resultado de esos parámetros. Este resultado puede leerse
desde otra parte de la aplicación en una variable que tienen el mismo nombre que la función. La forma
de obtener los datos de la función es llamar directamente a esa función, como verá un poco más
adelante.
Para insertar una Función se procede de igual forma que para un Procedimiento, pero marcando el
botón de opción Tipo Función en la caja de diálogo de la figura anterior.
El ámbito de una Función es el mismo que el un Procedimiento. Si se declara Pública puede usarse en
toda la aplicación. Si se declara Privada, solamente en el Formulario o Módulo donde se haya insertado.
Para llamar a una Función son válidos igualmente los criterios expuestos para los Procedimientos en
cuanto a la sentencia Call.
Se dijo anteriormente que a una Función se le pasan uno o varios parámetros con los que va a realizar
alguna operación. Al declarar la Función, hay que decirle el nombre de los parámetros que se le van a
pasar, de que tipo son (String, Integer, Boolean, ...) y cómo se le van a pasar (ByVal, ByRef,
ParamArray). Esto hay que introducírselo en la propia declaración
End Function
La función MiFuncion sabe que debe recibir dos parámetros, y que el primero será una cadena de
caracteres y el segundo un integer. Los nombres Variable1 y Variable2 son los nombres que usa la
Función internamente. No tienen porqué coincidir con los nombres que tengan las variables que
contienen esos valores en otras partes del programa.
Una función siempre da un resultado. Este resultado se le introduce en una variable que tiene el mismo
nombre que la función. Esta variable no hace falta declararla, ya que la declaración de la función lleva
implícito que exista una variable con ese nombre.
Imaginemos que lo que va a hacer la función de este ejemplo es tomar una cadena de caracteres
(Variable1) y obtener de ella otra cadena con los caracteres iniciales de la primera, tantos caracteres
como nos indique la segunda variable (Variable2)
Ya se habrá dado cuenta de que debemos emplear Left para obtener los caracteres iniciales de una
cadena. Nuestra función quedará de la forma :
Donde MiFuncion es una variable que se ve en todo el ámbito de la función. Para llamar a la función,
basta con citarla por su nombre y ponerle los parámetros necesarios. Cuando esté tecleando el código,
Visual Basic le invitará a introducir los parámetros citándole su nombre. (En nuestro ejemplo, ByVal
Variable1 as String, ByVal Variable2 as Integer
Pruebe esto con una pequeña aplicación. En un formulario, ponga un TextBox (Text1) donde va a
introducir la cadena original, otro TextBox donde va a introducir el número de caracteres a tomar, y un
Label (Label1) donde va a ver el resultado. Ponga un Botón de comando (Command1) donde llamará a
la función. Inserte una función (MiFuncion) en ese formulario :
Puede pensar que para hacer esta cosa tan elemental no merece la pena hacer una función.
Efectivamente. Bastaría con poner en el botón Command1_Click el siguiente código :
y funcionaría igual. Lógicamente, una función debe introducirse cuando vaya a realizar un código un
poco mas complejo, y sobre todo, cuando se va a repetir en muchos procedimientos.
Hemos visto que los parámetros de la función pueden pasarse Por Valor (ByVal), caso del ejemplo
anterior, y Por Referencia (ByRef). ¿Cuál es la diferencia ? La diferencia es que si le pasa un valor
por valor (ByVal) ese valor, aunque lo cambie la función internamente, ese cambio no se manifiesta
fuera de ella. Si se pasa por referencia (ByRef), y la función cambia el valor de esa variable, ese
cambio se mantiene fuera de la función.
Veamos esto de una forma muy sencilla : Vamos a hacer una función que multiplica dos números. Pero
dentro de la función vamos a cambiar uno de esos números, sumándole 2. Una vez realizada la
operación presentamos el valor de los dos factores en dos Label a ver si ha cambiado. Insertemos dos
funciones, MultiplicaA y MultiplicaB. En MultiplicaA le introducimos los datos Por Valor y en
MultiplicaB por Referencia.
Las dos funciones son idénticas, excepto en la forma de pasarle los parámetros. Pongamos un Botón de
Comando para multiplicarlo con MultiplicaA y otro con MultiplicaB. Al final del procedimiento click
de cada uno de ellos presentamos las dos variables que se pasan a la función en sendos Label. Cuando
Tras esto, se pone en Label2 el valor pepe + 2 (se mantienen el cambio realizado en la función
MultiplicaB x1 = x1 + 2
No queda ahí la cosa. Un valor puede pasarse también por ParamArray. En principio parece que esto
ya es para nota. No es para tanto.
Vamos a ver que sucede cuando queremos realizar una suma. La suma de los importes de varios
productos de un ticket de compra. En principio no sabemos cuantos productos va a comprar un cliente,
por lo tanto no sabemos a priori cuantos parámetros le tenemos que pasar. Para pode pasar un número
indeterminado de parámetros se los pasamos como PamArray :
Cuando se declara una función con un determinado número de parámetros, es necesario pasárselos
todos. Si no se hace así, VB nos dará un error. Pero puede que algún parámetro no exista siempre.
Cuando uno o varios de los parámetros que se pasan a una función son opcionales, se le pueden pasar
como Optional.
Se pueden pasar uno o mas parámetros como Optional. Las condiciones para los parámetros opcionales
es que ocupen los últimos lugares y que sean del tipo Variant
Los parámetros los tomamos de tres TextBox (TB1, TB2 y TB3, siendo este último el que es opcional.
En un botón de comando ponemos este código :
Se puede salir de una función antes de que termine. Por ejemplo, si se cumple una determinada
condición, se puede salir de la función mediante la sentencia Exit Function
Una vez que conocemos gran parte del Visual Basic, y que seguramente habremos hecho alguna
aplicación, es momento de pararnos en una de las cosas que un programador nunca debe olvidar: Un
programa no solo debe funcionar, sino que debe funcionar bien.
Parece que esta afirmación carece de sentido o que al menos es una afirmación gratuita. Sin embargo es
un defecto muy común entre programadores noveles, quizás por la alegría que da el trabajo
terminado, olvidarse de algo tan fundamental como la calidad del programa, y perder por ello la
alegría que da el trabajo bien hecho.
Cuando terminamos un programa, lo normal es que no funcione bien. Mucho antes de terminarlo ya
habremos tenido la sorpresa de que Visual Basic ha detectado un error y detiene la ejecución del
programa. Iremos corrigiendo los errores elementales que van apareciendo, errores de sintaxis, ficheros
que ya estaban abiertos, End If que nos faltaban, etc. Y el programa parece que ya puede funcionar
hasta el final. El programa debe entrar entonces en la fase de corrección de errores y optimización.
Corrección de Errores.
Denominamos Corrección de errores al proceso de la programación en la que se analizan y corrigen los
errores existentes en el código o en el funcionamiento del programa.
Podemos por tanto dividir los errores en errores de código y errores de funcionamiento.
Habrá notado el error de bulto consistente en que A:\MiFicher.Txt debe ir entre comillas dobles. Este
tipo de error se detecta generalmente al ejecutar el código. En este caso, Visual Basic nos dará el
mensaje de error “Error de Sintaxis” nada mas terminar de escribir la línea. Errores como este, o el
típico If sin End If seguro que se los ha encontrado repetidas veces y no vamos a ahondar mas en ellos.
Se corrige el error sintáctico en el momento y el problema queda resuelto. Pero volvamos a la línea de
código anterior, esta vez ya bien escrita:
Al estar escrita correctamente, esta línea no nos puede dar error de sintaxis. Pero, ¿Que ocurre cuando
estamos ejecutando el programa que contiene esa línea, y en el momento de ejecutarla no tenemos
metido un disquete en la unidad A:? O piense lo que ocurriría si tenemos un disquete en A:, pero no
existe en él el fichero MiFicher.Txt. No existe ningún error de sintaxis. El programa puede funcionar
correctamente si, en el momento de la ejecución de esa línea, tenemos un disco en la unidad A: y éste
contiene un fichero llamado MiFicher.Txt. Sin embargo no puede funcionar si no tenemos metido el
disquete, o si este no tiene el mencionado fichero.
Existen dos tipos de errores de ejecución: Errores Interceptables y Errores que no se pueden
interceptar.
Los errores interceptables son aquellos que Visual Basic conoce. Los dos errores anteriores son de este
tipo. Son errores no interceptables, aquellos que Visual Basic no puede detectar. No porque no los
conozca, sino porque el propio error deja sin trabajar a Visual Basic o incluso al sistema operativo.
Afortunadamente el sistema operativo está muy protegido, pero no por ello dejan de ocurrir estos
errores. ¿Recuerda la frase “El programa xxx ha causado un error de protección general en el
Módulo..... “ Bueno, acaba de producir un error No Interceptable.
No solamente existen estos errores, también hay errores no catalogados y aplicables solamente al
programador. Piense en un bucle infinito. Un bucle infinito es una secuencia de instrucciones tal como:
Podrá advertir que este bucle no se termina nunca, ya que n no podrá alcanzar nunca el valor 2000 ya
que cada vez que llega a 1000 le cambiamos su valor a 0. Este tipo de error, más normal de lo que
parece, ni lo detecta VB ni produce una caída del sistema operativo. Sin embargo es un error, pero en
este caso será el propio programador quien se tenga que dar cuenta de él, ya que VB no puede
ayudarnos debido a que el código, línea a línea, está perfectamente escrito.
Vemos por tanto que VB solamente nos ayudará en los errores de sintaxis, durante el tiempo de diseño,
y los errores interceptables en tiempo de ejecución.
Errores Interceptables.
Los errores interceptables pueden producirse cuando está ejecutando una aplicación, tanto en el
entorno de Visual Basic como en modo autónomo. Algunos de estos errores pueden ocurrir también en
tiempo de diseño o en durante la compilación.
Un error interceptable es, como decíamos anteriormente, un error que Visual Basic conoce. Para
conocerlos, VB dispone de un objeto que vamos a ver a continuación. El Objeto Error
Objeto Error
Contiene información sobre errores en tiempo de ejecución.
El Objeto Error tiene Propiedades y Métodos. Las propiedades del objeto Error las establece quien
genera el error. Por ejemplo, si el error se genera durante la ejecución, será VB quien genera las
propiedades del objeto Error. Pero puede ser también el programador quien genere el error. Lo veremos
mas adelante
Propiedad Description
Devuelve o establece una cadena descriptiva asociada a un error.
Propiedad HelpContext
Propiedad HelpFile
Devuelve o establece la ruta completa del archivo de Ayuda de Microsoft Windows que debe mostrarse
al producirse el error.
Variable = Err.HelpFile
Propiedad LastDLLError
Devuelve un código de error de sistema producido por una llamada a una biblioteca de vínculos
dinámicos (DLL). Esto ocurre cuando falla la ejecución de una función API.
Propiedad Number
Esta es la propiedad mas importante del Objeto Error. Por ello es la propiedad predeterminada. Permite
conocer el número del error producido. También nos permite establecer el número de error, cuando nos
interesa provocar un error mediante código, con el método Raise
Variable = Err
Source
Devuelve o establece el nombre del objeto o aplicación que ha generado el error originariamente.
Método Clear
Borra los valores de todas las propiedades del objeto Err.
Sintaxis Err.Clear
De aquí se desprende que un error no “sale” del procedimiento en el que se generó. Para salir de un
procedimiento el programa debe pasar necesariamente por una instrucción Exit Sub. En ese momento,
desaparecen todas las propiedades del Objeto Error.
Método Raise
Genera un error en tiempo de ejecución.
Número Requerido. Entero de tipo Long que identifica la naturaleza del error.
Origen Opcional. Expresión de cadena que indica el objeto o aplicación que ha generado
originariamente el error.
Descripción Opcional. Expresión de cadena que describe el error.
ArchivoAyuda Opcional. Ruta del archivo de Ayuda de Microsoft Windows en el que se encuentra la
información sobre el error.
ContextoAyuda Opcional. Identificador de contexto que especifica el tema del archivo indicado en
ArchivoAyuda que contiene la información de ayuda del error.
Todos los argumentos son opcionales, excepto Número. Sin embargo, si utiliza Raise sin especificar
algunos argumentos, y si los valores de las propiedades del objeto Err no se han borrado, tales valores
se conservarán para el error actual.
Decíamos al principio del Objeto Error que sus propiedades las establece quien las generó. Con el
Método Raise se genera un error. Y vea que se pueden establecer sus propiedades.
Una Rutina es un trozo de código escrito en un procedimiento, que tiene un nombre. El nombre de la
Rutina puede ser cualquiera, y debe terminar con el carácter dos puntos (:) Por ejemplo:
RutinaErrores:
El nombre de la rutina debe ocupar él solo una línea. Se accede directamente a la rutina cuando citamos
su nombre en el código del procedimiento donde está la Rutina.
Para citarla, debemos usar ese nombre, quitando el carácter dos puntos final. Podemos llamarla
mediante la sentencia GoTo. Veamos un pequeño ejemplo. Es un botón de comando, y un TextBox. Si
el TextBox (Text1) mantiene su texto original (Text1) hacemos que el programa pase por la rutina. Si
lo hemos variado, hacemos que no pase:
La rutina es en realidad un trozo de código del procedimiento que se ejecutará, bien porque hayamos
dirigido allí la ejecución del programa (mediante GoTo) o bien porque el programa pase por la rutina al
ejecutarse. Observe el Exit Sub que tiene en la línea inmediatamente anterior a la Rutina. Si no lo
ponemos, el programa pasará por la rutina una vez ejecutada la sentencia condicional If - Else - End
If, igual que si se tratase de cualquier otra parte del programa. Para evitar que pase por ella, ponemos
ese Exit Sub que hará que el programa salga del procedimiento sin llegar al final.
En el ejemplo anterior, la condición para que pasase por la rutina era que se cumpliese una determinada
condición en Text1. Para que pase por una rutina, al darse la condición de que ha ocurrido un error,
usaremos la instrucción On Error GoTo
Rutina:
MsgBox "Sí pasó por la Rutina"
End Sub
Cuando insertamos la línea On Error GoTo Rutina, el programa sabe que nada mas producirse un
error, debe ir a la rutina señalada. En esa rutina tomaremos las medidas adecuadas para que se subsane
el error producido. Así por ejemplo, si en una línea le decimos al programa que vaya a leer un fichero
de la unidad A:, puede ocurrir que la unidad no tenga metido el disco. O que lo tenga metido pero que
no exista el fichero en ese disco.
Si estuviese metido el disco, pero no tuviera un fichero llamado Mificher.Txt, daría este error:
En realidad estos errores no son de programa, sino de una utilización incorrecta del programa, ya que
debería estar metido el disco, y que contuviese un fichero llamado Mificher.Txt. Pero en programación
hay que prever estas eventualidades. Y lo lógico sería poner un aviso diciéndole al usuario que la
unidad A: no está preparada (caso 1) o que el disco no contiene el fichero buscado (caso 2)
Instrucción Resume
Hemos visto en el apartado anterior la sentencia On Error Resume Next, de la que decíamos que
“pasaba” del error y continúa en la línea siguiente a la que provocó el error. Es así ya que la
Instrucción Resume hace desaparecer el error mediante código (Pone Err.Numbber = 0). Si le añadimos
Next el programa sigue ejecutándose en la línea siguiente a la que generó el error.
La Instrucción Resume puede tener las siguientes sintaxis:
Resume Rutina La ejecución continúa en la rutina citada en la instrucción. La rutina debe encontrarse
en el mismo procedimiento que el controlador de errores.
Ya que conocemos las instrucciones On Error GoTo y Resume, veamos en el ejemplo anterior como
resolvemos el problema de que no exista disco en la unidad A: o que en el disco no exista el fichero
buscado:
EJEMPLO
RutinaErrores:
If Err.Number = 71 Then ‘No hace falta poner Err.Number. Basta con Err
MsgBox "La Unidad A no tiene ningún disco. Introduzca uno"
Resume ‘Al poner Resume (sin mas) el programa seguirá ejecutándose
‘en la misma línea que provocó el error. Como hemos
‘intercalado un MsgBox, que lleva implícita una espera a
que se ‘le haga click en su botón, durante esa espera el usuario puede
‘cambiar el disco y al reiniciarse el programa en esa línea,
ya ‘puede tener un disco para que no se repita el error.
End If
Es momento ahora de hablar de la ventana de depuración. Esta ventana permite que Visual Basic se
comunique con nosotros en tiempo de ejecución. Es la ventana de Debug. No está presente
normalmente en la pantalla, pero se presenta haciendo click en Ver | Ventana de Depuración de la barra
de menú de Visual Basic. En esta ventana se presenta lo que escribamos en un objeto preparado para
ayudarnos en la corrección de errores, que se llama precisamente Objeto Debug:
En esta ventana podemos presentar otro tipo de datos, por ejemplo el valor de una variable en un
determinado momento:
Debug.Print MiVariable
Veremos en el ejemplo de este capítulo una aplicación de la ventana de depuración que le dejará claras
las ideas.
Si queremos que cuando se produzca un error se nos presente en esa ventana, basta con insertar esta
línea de código en la Rutina de Error:
Hemos visto la forma de detectar un error y tomar las medidas oportunas para que el programa siga
funcionando. Aquí lo hemos hecho solicitándole una operación al usuario. En otros casos nos interesará
que el programa “pase” del error, en otras que repita un procedimiento... El programador deberá ver
en cada caso la respuesta que debe dar el programa a un determinado error.
Un programa perfecto es aquel que contempla todas las posibilidades de error y les da solución a todas.
Esto quiere decir, que un programa debería tener en cada procedimiento una llamada a la detección de
errores (On error GoTo ... ) y una rutina de corrección. El trabajo es grande. La recompensa también.
Toda aplicación bien terminada debe tener una ayuda. Cualquier aplicación realizada en Visual Basic
puede tenerla, usando para ello los recursos que brida Windows.
El recurso de Windows es un programa capaz de presentar ficheros de ayuda. Estos ficheros son un
poco especiales y solamente se pueden presentar mediante ese presentador de ficheros de ayuda. Ese es
el programa WinHelp.Exe para las versiones de 16 bits y las primeras versiones de Windows 95, y el
WinHlp32.Exe para Windows 98 y Windows NT4. Como en todas las aplicaciones de Microsoft (MS)
tienen compatibilidad hacia arriba. Por lo tanto no se extrañe que una ayuda que le abre perfectamente
el WinHlp32.Exe, si la pretende abrir con el WinHelp.exe (que es más antiguo) le diga que no se trata
de un fichero de ayuda.
Windows dispone de otra utilidad, el programa HC.EXE (Help Compiler) que puede adaptar el
fichero .RTF donde escribirá la ayuda, a un formato capaz de ser interpretado por el WINHELP.EXE
Lo vamos viendo todo paso a paso.
Antes de comenzar a explicar como se realiza una ayuda Windows vamos a comentar el formato de
archivos RTF. Este formato posiblemente le sea familiar debido a que el control RichTextBox puede
guardar y leer ficheros en ese formato. Las siglas RTF vienen precisamente de Rich Text Format, en
castellano, Formato de Texto Enriquecido.
Cuando se edita un texto mediante un procesador de textos, el fichero resultante se guarda don un
formato distinto para cada procesador. De esta forma, un texto editado en WP no es compatible con el
P.T. AmiPro, con Word o con cualquier otro. Los fabricantes de estos procesadores de textos han tenido
que incluir una herramienta capaz de convertir un formato a otro para poder alcanzar la compatibilidad
entre ellos que el mercado exigía.
El Formato de Texto Enriquecido pretende ser un nexo de unión entre todos los procesadores de texto,
para poder intercambiar ficheros editados en uno u otro. De hecho, las últimas versiones de los mas
importantes procesadores de textos incluyen la posibilidad de guardar y buscar el texto en este formato.
(WP, Word)
Este formato consiste en guardar mediante caracteres ASCII plenamente legibles tanto el texto escrito
como los tipos de letra, tamaño, saltos de carro, etc. Veamos un ejemplo comparativo del mismo texto
escrito en Word, guardado en RTF y en ASCII :
Texto1
Este texto está escrito en Word. Observe que podemos poner letra negrita, letra cursiva, letra
subrayada. Podemos cambiar el color de las letras, rojo, verde, azul. Podemos cambiar el tamaño de las
letras a tamaño mas grande, mas pequeño, etc.
Fin Texto 1
Texto1
Este texto est escrito en Word. Observe que podemos poner letra negrita, letra cursiva, letra
subrayada. Podemos cambiar el color de las letras, rojo, verde, azul. Podemos cambiar el tamaño de las
letras a tamaño mas grande, mas pequeño, etc.
Fin Texto 1
Y ahora el mismo texto en formato RTF. En este formato hubo que seccionar las líneas para poder
mostrarlas en una hoja, ya que RTF utiliza líneas sin retornos de carro. Se han seccionado las líneas
terminándolas con un guión bajo y comenzando en la línea siguiente también con un guión bajo.
Como puede observar, el RTF incluye el texto escrito casi en ASCII, pero añadiendo una serie de datos
respecto al tipo de letra, codifica los acentos, las eñes, y hasta incluye, tomándolo del ordenador, el
nombre del operador que lo ha escrito. Estas informaciones también se guardan cuando se archiva un
texto en el formato propio del procesador de textos, pero lo hace en binario, por lo que no lo podemos
visualizar. El formato RTF, dentro de que mete toda esa información adicional, lo archiva con
caracteres ASCII.
La utilidad que tiene Windows para presentar las ayudas utiliza precisamente este formato RTF. Por
ello, debemos disponer de un procesador de textos que pueda guardar el texto escrito en este formato.
(Word, WP5, WP6 y otros) Los ejemplos de estos apuntes se han realizado en Word.
Antes de decidirnos a escribir el fichero de ayuda debemos pensar muy bien lo que vamos a poner en él.
Pensemos ante todo a que personas va dirigida la aplicación, sus posibles conocimientos de informática
Para acceder a la ayuda, Windows ofrece la posibilidad de pulsar F1 . Nuestras aplicaciones deben
sacar la ayuda pulsando F1. Lo podrán hacer mediante otros procedimientos. Comencemos por lo mas
sencillo, una ayuda de una única página.
Escriba el texto de ayuda con Word y guárdelo en formato RTF, en cualquier directorio, pero
preferentemente en uno que no se mezcle con otros ficheros para poder localizarlo mejor. Cree si es
necesario un directorio exclusivo para la ayuda. Imaginemos que lo creamos y es el C :\DIRAYUDA,
donde guardamos el fichero de ayuda con el nombre AYUDA1.RTF
Este archivo no lo puede utilizar directamente el programa WINHELP.EXE. Hay que compilarlo. Para
ello utilizamos el compilador HC.EXE. Este compilador no es una herramienta Windows, por lo que
tendrá que ir al DOS y ejecutarlo. El programa HC.EXE se encuentra en el directorio C :\ ..... \VB\HC
Pero al compilador HC.EXE hay que suministrarle la información para que pueda trabajar. Esa
información se la damos en un fichero que le pasaremos como parámetro, que debe tener extensión
.HPJ y que meteremos en el mismo directorio donde tengamos el fichero de ayuda AYUDA1.RTF
Este fichero estará editado en ASCII puro. Puede editarlo con el EDIT de MS-DOS o con el Block de
Notas de Windows. Imaginemos que lo vamos a llamar FICHAYUD.HPJ y como se dijo, se meterá en
el mismo directorio donde está el fichero de ayuda. (C :\DIRAYUDA) El nombre que decida para este
fichero con extensión .HPJ será el que tenga el fichero de ayuda (Que se obtendrá de la compilación y
tendrá por extensión .HLP) Tendrá, de momento, dos líneas.
[FILES]
AYUDA1.RTF
HC C:\DIRAYUDA\ FICHAYUD.HPJ
La compilación no suele dar problemas sobre todo en un fichero de ayuda tan simple de una única
página. Al compilar, obtenemos un fichero con el mismo nombre que el fichero .HPJ y extensión .HLP,
(FICHAYUD.HLP en nuestro caso) que lo dejará en el directorio donde estuviera el programa HC.EXE.
(Posiblemente el C :\VB\HC) Debe moverlo a otro directorio donde no estorbe y lo podamos localizar
sin problemas. Movámoslo al C :\DIRAYUDA Vayamos a nuestra aplicación VB y abramos el menú
de Herramientas | Opciones para sacar el cuadro de Opciones. Vaya a la pestaña de Proyecto y busque
el directorio y nombre del fichero .HLP haciendo click sobre el cuadrado con tres puntos (...) que está a
la derecha del TextBox de Archivo de Ayuda. Se le abrirá un CommonDialog para buscarlo.
NOTA : Para evitar liarse con los directorios, es medida siempre prudente llevarse el compilador de
ayudas HC.EXE al directorio donde tengamos el fichero .RTF y el .HPJ.
Sin embargo este procedimiento no sería el mas indicado para una información de ayuda extensa.
Deberemos poner varias páginas. Si lo hacemos así, al pulsar F1 siempre aparecerá la página 1. Si
aparece siempre la página 1, pongamos en esta primera página el índice de temas, y en las páginas
sucesivas cada uno de los temas. Ya veremos como acceder a cada una de las páginas haciendo click
sobre la línea del índice que contiene el título del tema deseado. Tal y como lo hace con cualquier
aplicación Windows.
Volvamos al editor Word y abramos el fichero que habíamos creado de una página, y añádale mas
páginas introduciendo avances de página manuales (Se introducen con Control + Intro)
INDICE
1 - Introducción a la aplicación
2 - Acceso a la Base de Datos
3 - Introducción de datos
4 - Lectura de datos
Introducción a la Aplicación
Introducción de datos
Lectura de datos
Ya tenemos hecho un fichero de ayuda con varias páginas. No es suficiente esto, ya que
WINHELP.EXE , pulsando F1, solamente nos mostrará la página 1. Debemos hacer algo para, una vez
en la página 1, que nos estará mostrando el índice, poder acceder a cada una de las páginas haciendo
click sobre la línea del índice que contiene el tema de interés.
Para ello debemos poner un identificador a cada una de las páginas. Este identificador debe ser único,
es decir, no pueden existir dos páginas con el mismo identificador.
Para introducir un identificador en una página, una vez que tenga el fichero de ayuda terminado, sitúese
con el cursor al comienzo del título de la primera página (después del índice), justamente al lado de
Introducción a la Aplicación . Inserte en ese punto una Nota al pié (Abra el Menú Insertar | Nota al
Pié del Word) Le aparecerá esta ventana :
Introduzca una almohadilla (#) en el TextBox Marca personal. Haga click sobre Aceptar.
Le aparecerá en la parte inferior de la pantalla un cuadro para introducir el identificador de esa página.
Repita el proceso para todas las páginas insertándoles una Nota al pié a cada una. Al final obtendrá este
resultado :
INDICE
1 - Introducción a la aplicación
2 - Acceso a la Base de Datos
3 - Introducción de datos
4 - Lectura de datos
- - - - - - - - - - - - - - - - - - - - - salto de página manual - - - - - - - - - - - - - - - - - - - - - - - -
#
Introducción a la Aplicación
#
Introducción
LSB Visual Basic – Guía del Estudiante Capítulo 1 Página 286
#
Acceso a la Base de Datos
(Observe que estamos simulando la ventana de Word. Donde ve - - salto de página manual - -
entienda que estamos simulando lo que Vd. ve en la ventana real)
En la parte inferior puede ver los identificadores que se han asociado a cada página. ¿Qué podemos
hacer para asociar estos identificadores a las líneas correspondientes del índice ? Muy sencillo, y es el
siguiente paso que debe hacer :
INDICE
1 - Introducción a la aplicación
2 - Acceso a la Base de Datos
3 - Introducción de datos
4 - Lectura de datos
Habrá observado en las aplicaciones Windows que para seleccionar un tema hay que poner el puntero
del ratón sobre la línea deseada, que está en color verde, y en ese momento el puntero se convierte en
una mano. En nuestra aplicación ocurrirá lo mismo. Seleccione todas las líneas que quiere asociar a
cada uno de los identificadores (Una a una o todas a la vez, dependiendo de como las tenga dispuestas),
y vaya al menú Formato | Fuentes . Le aparecerá el cuadro de diálogo. Vaya al TextBox Subrayado
de ese cuadro de diálogo y elija Doble.
#
Acceso
#
Meterdatos
#
Leerdatos
LSB Visual Basic – Guía del Estudiante Capítulo 1 Página 287
Al ponerles doble subrayado, le aparecerán en la ayuda de color verde, y el puntero se le transformará
en una mano cuando lo ponga encima de una de esas líneas. Pero todavía queda asociarlas a cada uno
de los identificadores. Para ello tiene que llevar el puntero del ratón justamente al final de cada una de
las líneas. Al hacer click el puntero de escritura se coloca en ese punto. Tiene que escribir a
continuación de cada línea el nombre del identificador que corresponda. Pero lógicamente no querrá
que se vea en la pantalla de ayuda. Por lo tanto debe hacerlo invisible. Para escribir un texto invisible
en Word hay que pulsar las teclas Control + Mayúsculas + O. Pulse las tres al mismo tiempo y a
continuación escriba el nombre del identificador que corresponda. No verá lo que escribe pues para eso
está escribiendo con texto invisible. Puede visualizarlo mediante el botón
del editor Word. Verá también todos los caracteres de control del documento. Si lo prefiere, vaya al
menú Herramientas | Opciones y sobre la pestaña Ver seleccione Texto oculto.
Proceda de igual forma con todas las líneas del índice. Cuando termine, guarde el documento en
formato RTF y compílelo de la forma explicada mas atrás. Posiblemente ahora le salgan errores de
compilación, pues el compilador comprueba que todos los identificadores se corresponden en el índice
y en las páginas. Si le falta algún pie de página o un salto de carro manual le dará error. También le
dará un error, mejor dicho una observación, indicándole que hay párrafos ocultos. Es lógico, ha
detectado los textos ocultos que contiene el fichero. Ni caso. Le habrá creado el archivo .HLP y le
sugiero que lo pruebe, moviéndolo al directorio C :\DIRAYUDA y ejecutando la aplicación. Pulse F1 y
le saldrá la página con el índice y las líneas del índice en verde. Si lo ha hecho todo perfectamente, al
seleccionar una y otra línea le aparecerá la página correspondiente.
Ventanas Emergentes
Es posible que dentro de la información presentada en cualquiera de las páginas anteriores, exista una
palabra o frase que quisiera explicar con mas detalles. Habrá observado en las ayudas de Windows que
algunas frases dentro de las páginas de ayuda están en verde, y al acercar el puntero del ratón a ellas se
convierte en una mano. Para poder hacer lo mismo en nuestra ayuda, volvamos al documento Word.
INDICE
Observe unas palabras que están subrayadas. Imagínese que esas palabras tienen un significado que
queremos explicar con mayor detalle. Puestos en nuestro caso, queremos explicar con mas detalle lo
que es una aplicación, una Base de Datos, introducir y leer.
Pretendemos que, al hacer click sobre una de estas palabras se abra, dentro de la ventana de ayuda, otra
ventana con la explicación de lo que es esa palabra concreta. Por lo tanto, debemos introducir esas
informaciones a base de introducir nuevas páginas en el documento Word donde editamos el fichero de
ayuda. Añádalas de la misma forma insertándoles un pie de página y el identificador deseado, de la
misma forma que se explicó mas atrás. Recuerde que el identificador debe ser único.
Una vez terminado de introducir todas las nuevas páginas, vaya a las palabras o frases que quiere
explicar con el texto de esas páginas, selecciónelas y deles el atributo de subrayado simple (Hágalo de
forma similar a cuando hizo lo del subrayado doble, pero esta vez SIMPLE) Introduzca
inmediatamente después de la palabra o frase un texto oculto con el nombre del identificador
seleccionado para la página deseada en esa palabra. Guarde el documento Word y vuelva a compilar
como anteriormente. Ejecute la aplicación y compruebe como vamos avanzando en el tema de las
ayudas de Windows.
Si repasamos las propiedades de los controles, seguro que se acuerda de la propiedad HelpContextID
que tenían la mayoría de los controles VB. En aquel momento decíamos que esa propiedad que
establece un número de contexto asociado para este control. Este número se aplica para determinar la
ayuda interactiva asociada a este control. Ha llegado el momento de sacarle partido a esa propiedad.
Podemos usar la tecla F1 para acceder al índice de la ayuda de una aplicación y movernos a lo largo de
la ayuda seleccionando una u otra información. A veces resulta práctico seleccionar directamente la
ayuda correspondiente a la función de un control. Para poder hacer esto, asociaremos una de las
páginas del documento Word anterior al número que figura en la propiedad HelpContextID. Cuando ese
control tenga el foco, al pulsar F1 saldrá como página por defecto la página asociada mediante la
propiedad HelpContextID.
#
Introducción
#
Acceso
#
Meterdatos
#
Leerdatos
LSB Visual Basic – Guía del Estudiante Capítulo 1 Página 289
Para establecer esta relación vayamos al fichero con extensión .HPJ que servía para introducir los datos
al Help Compiler. En nuestro caso se llamaba FICHAYUD.HPJ y tenía por el momento, solamente
dos líneas.
[FILES]
AYUDA1.RTF
En estas dos líneas le introducíamos el nombre del fichero de ayuda [FILE]. Añadamos ahora en otro
apartado [MAP] la relación entre los identificadores de página y los números de contexto de la
propiedad HelpContextID de cada uno de los controles que la tengan activada (valor distinto a 0). Se
escribirá el nombre del identificador de página, y separado por un espacio o un tabulador, el número de
contexto :
[MAP]
Identificador1 1
Identificador2 2
Identificador3 3
Identificador4 4
En este caso, cuando un control que tiene en su propiedad HelpContextID el número 3 tiene el foco, al
pulsar F1 aparecerá como página por defecto la correspondiente a la explicación con identificador
nombre Identificador3. Lo mismo ocurrirá con el resto de los identificadores.
Puede que las ayudas que quiera aportar a cada uno de los controles sea muy breve, caso por ejemplo de
la que puede introducir en los TextBox para indicar al usuario lo que se va a hacer con el dato concreto
que se va a meter en ese TextBox. Y que esas ayudas breves sean muy numerosas y tal vez cambiantes
de un usuario a otro. Esto complicaría su fichero de ayuda original, al que debería introducir muchas
páginas nuevas. Puede editar estas pequeñas ayudas en otro documento Word, de la misma forma que
se ha descrito, y darle un determinado nombre (AYUDA2.RTF para nuestro ejemplo) y añadirlo como
otro fichero en la lista [FILES]. Colóquelo tras el fichero anterior, pues el que lleva el índice debe ser
el primero que figure en la lista. Debe añadir la relación entre los nombres de identificador que haya
puesto en ese nuevo fichero y los números de contexto de cada control.
[FILES]
AYUDA1.RTF
AYUDA2.RTF
[MAP]
Identificador1 1
Identificador2 2
Identificador3 3
Identificador4 4
Este procedimiento de acudir rápidamente a la ayuda de un determinado control puede que no sea el
ideal, ya que en una aplicación, siempre tiene el foco algún control. Si ese control que ahora mismo
tiene el foco tiene activado el HelpContextID, y el usuario pulsa F1, con la intención de ir a ver
cualquier cosa no relacionada con el control que tiene actualmente el foco, se verá sorprendido con que
aparece una información que nada tiene que ver con la esperada (índice). En cualquier caso, siempre
podrá ir al índice haciendo click en el botón CONTENIDO de la ventana de ayuda, pero en principio no
está bien que le aparezca en pantalla una información no esperada. Tiene solución, incluya en el menú
(palabra Ayuda) o en un control Image con el símbolo ? una llamada al programa WINHELP.EXE,
pasándole como parámetro el nombre del fichero de ayuda. Esto lo podemos hacer mediante la función
SHELL
También puede utilizar para este fin el CommonDialog. Introduzca un CommonDialog en su aplicación
y ponga en su propiedad HelpFile el nombre del fichero de ayuda. La propiedad HelpCommand de
este CommonDialog debe estar puesta a 3 para que comience mostrando el índice de la ayuda. En estas
condiciones, en vez de acudir a la función Shell para ejecutar el WINHELP.EXE, ejecute
CommonDialog.ShowHelp. Vea con mas detalles las propiedades del CommonDialog, pues le permite
presentar otras funciones de la ayuda de Windows (Ayuda de la Ayuda, etc.)
Habrá observado que la ventana de ayuda tiene, en la barra de título el siguiente texto : Ayuda de
Windows. Si deseamos personalizarlo y poner el nombre de nuestra aplicación, basta con añadir un par
de líneas al fichero FICHAYUD.HPJ. Las correspondientes al apartado [OPTIONS]. Este apartado
debe ir en primer lugar del fichero, y puede llevar la información de la barra de título y el CopyRight.
Estos dos parámetros se introducen de la siguiente forma :
[OPTIONS]
TITLE= Nuestra Aplicación VB - Fichero de Ayuda
COPYRIGHT= Nombre del Autor(a)
[FILES]
AYUDA1.RTF
AYUDA2.RTF
[MAP]
Identificador1 1
Identificador2 2
Identificador3 3
Identificador4 4
Para introducirlo por el primer procedimiento, inserte el gráfico en el texto Word y proceda como
siempre.
Para introducirlo por el segundo método, tiene tres posibilidades : Introducir el gráfico en el centro de
la ventana de ayuda, introducirlo a la izquierda con texto a su derecha o a la derecha con texto a su
izquierda.
Se han resaltado en negrita las tres opciones. Recuerde la llaves donde se encierra la posición y el
nombre (y path) del gráfico.
Debe tener cuidado al introducir gráficos, ya que el compilador (HC.EXE) trabaja en DOS, y me da la
impresión de que no utiliza mas que los primeros 640 Ks. de memoria. Con un gráfico de 470 Ks. usado
en la preparación de estos apuntes fue incapaz de compilarlo, dando error de memoria insuficiente. (Y
no es que el Ordenador no tenga RAM suficiente)
Historial
La Ayuda de Windows nos permite recordar todas las páginas sobre las que se hizo una consulta. Esto
puede ser muy útil, pues tenemos muy accesible los temas que hemos consultado, para poder realizar
una nueva lectura de los mismos si fuese necesario.
Para obtener la ventana de historial, basta con hacer click en la barra de menú de la ventana de Ayuda
en Opciones | Mostrar Ventana de Historial.
Pero para que se pueda mostrar el historial, es necesario darle un nombre a las páginas, y será ese
nombre el que figure en la ventana de historial. Ese nombre es distinto del identificador de página,
aunque no hay ningún problema porque sea el mismo. Ponga como nombre de la página una palabra
que identifique esa página de una forma única.
Para poner el nombre a la página, vaya al comienzo de cada página e introduzca una nota al pié de la
misma forma que cuando ponía el identificador. En este caso el símbolo a introducir en la Marca
personal es el $. Proceda a introducir el nombre en la parte de abajo de la pantalla de Word
Palabras Clave
La Ayuda de Windows nos permite accede a una página usando como criterio de búsqueda ciertas
palabras que ponemos como clave. Por ejemplo, si queremos que aparezca nuestra página de ejemplo
que habla de Acceso a la Base de Datos, cuando el usuario busca una de las siguientes palabras :
Tablas, Base, Registro, y a la de Lectura de Datos cuando busque Obtener, Leer y Visualizar
Volvamos al editor Word, a las dos páginas nombradas y al lado de las notas al pié anteriores,
introduzca otra, esta con la Marca personal K. En la parte inferior de la pantalla, donde pone el nombre
de la nota al pie, ponga introduzca las palabras clave para cada una de ellas, separadas con una coma.
Salve el fichero RTF y vuelva a compilar. Ejecute la aplicación y saque la Ayuda. Observará que el
botón Búsqueda de la ventana de ayuda está habilitado. Haga click en este botón y verá que esas
palabras ya figuran en la lista.
Lo mismo que hacíamos con las palabras, bien las del índice, a las que con un subrayado doble les
introducíamos un enlace a una página, o con cualquier palabra o frase de una página, que mediante un
subrayado simple les introducíamos un enlace a una ventana emergente, lo mismo podemos hacer con
la referencia a un gráfico. (Vea un poco mas arriba, las referencias a los gráficos del tipo {bmc
c:\dirayuda\grafico1.bmp} ) Si seleccionamos esa referencia y le ponemos el atributo de doble
subrayado, al acercar el puntero del ratón a ese gráfico (en la ayuda) nos mostrará una mano, y
haciendo click sobre el gráfico sacará la página correspondiente. Si le ponemos el atributo de subrayado
simple, nos sacará una ventana emergente. Eso sí, en cualquiera de los dos casos, deberemos poner, a
continuación de la referencia al gráfico, con texto invisible, el indicador de página que especifique la
página o ventana emergente que queremos mostrar.
Páginas en secuencia
Si tenemos una ayuda muy larga y la queremos leer toda, con lo que sabemos deberíamos ir al índice y
desde allí acceder a la página 1, leerla, volver al índice, acceder a la página 2, leerla, volver al
índice ....
Para asignar este código de secuencia deberemos introducir otra nota al pié para cada página, en este
caso con la marca personal + (signo mas). También deberemos indicarle al WinHelp que deseamos
sacar los botones (<<) y (>>) para desplazarnos por las páginas. Esto se consigue añadiendo una nueva
sección al archivo .HPJ que se denomina [CONFIG] y le introduciremos en esta sección la expresión
BrowseButtons()
[OPTIONS]
TITLE= Nuestra Aplicación VB - Fichero de Ayuda
COPYRIGHT= Nombre del Autor(a)
[FILES]
AYUDA1.RTF
AYUDA2.RTF
[MAP]
Identificador1 1
Identificador2 2
Identificador3 3
Identificador4 4
[CONFIG]
BrowseButtons()
Encabezado de páginas
Habrá observado en la ayuda de Visual basic que una o dos líneas en la parte superior de la ventana de
ayuda se conservan allí continuamente aunque nos desplacemos hacia abajo mediante los cursores de
desplazamiento. Esas líneas son el Encabezado de página. Para conseguir esto en nuestra ayuda
deberemos escribir las líneas que queramos que se conserven como encabezado de página al principio
de cada página, seleccionarlas con el cursor del ratón y abrir el menú Formato | Párrafo (Se supone
que está utilizando el procesador de texto Microsoft Word) donde le aparecerá un cuadro de diálogo
con dos solapas, Sangría y Espacio y Presentación. Elija Presentación y dentro de esta solapa, active
la casilla Conservar con el Siguiente. Las líneas que hubiera seleccionado se le conservarán como
Encabezado de página.
Con frecuencia es útil emplear varias ventanas para mostrar la información de una aplicación. El
ejemplo que tenemos mas a mano es el de la Ayuda de Visual Basic. Cuando tenemos seleccionada la
ayuda de un control, se pueden observar al menos dos palabras que nos llevan a una segunda ventana :
Propiedades y Eventos. Cuando elegimos una de estas informaciones aparece una segunda ventana que
nos muestra la información pedida, sin perder la información que tenemos en la primera ventana.
Esta segunda ventana se comporta de manera idéntica a la primera, en cuanto a llamadas a otras páginas
o a menús emergentes.
Para poder presentar esta segunda ventana es necesario primero definirla. Para ello volvamos a nuestro
fichero .HPJ y le añadiremos un nuevo apartado : WINDOWS, donde introduciremos el nombre de la
nueva ventana, el título de la misma (El título que aparecerá en su parte superior), y, entre paréntesis,
su posición y dimensiones.
El fichero .HPJ quedará de la siguiente forma, tras introducirle dos nuevas ventanas :
[OPTIONS]
TITLE= Nuestra Aplicación VB - Fichero de Ayuda
COPYRIGHT= Nombre del Autor(a)
[FILES]
AYUDA1.RTF
AYUDA2.RTF
[MAP]
Identificador1 1
Identificador2 2
Identificador3 3
Identificador4 4
[CONFIG]
BrowseButtons()
[WINDOWS]
Ventana2 = “Titulo de esta Ventana”, (PosiciónX, PosiciónY, DimensiónX, DimensiónY)
Ventana3 = “Titulo de esta Ventana”, (PosiciónX, PosiciónY, DimensiónX, DimensiónY)
El Objeto App
Introducimos aquí un nuevo objeto Visual Basic. El objeto App es un objeto global al que se accede
con la palabra clave App. (No se líe. El objeto App no es ni mas ni menos que un conjunto de datos
acerca de la aplicación) Determina o especifica información sobre el título de la aplicación, la ruta de
acceso de su archivo ejecutable y los archivos de Ayuda, y si está ejecutándose una instancia anterior de
la aplicación. Este objeto tiene solamente propiedades. No tiene ni Eventos ni Métodos. Una de sus
propiedades es HelpFile, propiedad que es de lectura y escritura. Es decir, podemos obtener el nombre
del fichero de ayuda de una aplicación consultando el valor de esa propiedad del Objeto App. En el
ejemplo siguiente introducimos el nombre del fichero de ayuda en el Label1
Label1.caption= App.HelpFile
En este capítulo hemos citado como compilador del fichero de ayuda al programa HC.EXE que
habitualmente se encuentra en el directorio C :\ . . . . \ VB\HC. Este compilador trabaja perfectamente
con ficheros .RTF creados con las versiones primeras del Word.
Si Ud. realiza los ficheros .RTF con el Word para Windows95 (Word 6.0), no habrá tenido ningún
problema con lo descrito anteriormente. Si tiene Word de office97 (W97) habrá observado que al
compilar la ayuda con el HC.EXE le sale un error. No se preocupe. Es que el fichero RTF creado por
W97 es distinto del creado por W95, lo cual, aparte de dar una idea de como se trabajan los temas de
compatibilidad entre procesadores que deberían ser compatibles, nos obliga a buscar otro compilador de
ayudas.
No lo intente con el HCP.EXE. Tampoco vale. Busque por Internet un fichero llamado HCW.EXE,
compilador que tiene además la ventaja de que trabaja en Windows. Usa el mismo fichero .HPJ y
acepta los ficheros .RTF creados con W95 y W97
El aspecto de este compilador de ayudas es el siguiente :
A modo de introducción
Todo lo visto hasta ahora en este libro es el Visual Basic elemental que debe conocer
necesariamente para realizar una aplicación. Sin embargo hay algo que falta. El 90 % de las
aplicaciones que va a realizar van a llevar una base de datos. Es lógico. Una aplicación se
hace normalmente para presentar datos, datos que habrá que introducir y mostrar de forma
conveniente. Lo de introducir y mostrar datos son las materias que va a ver en los capítulos
siguientes, pero no olvide lo de mostrarlos de forma conveniente, y es en eso donde tendrá
que aplicar todo lo que lleva aprendido hasta ahora. Se observa con mucha frecuencia que el
alumno tiene una prisa desmesurada por llegar a los temas que tratan las bases de datos,
dejando un poco de lado la interface necesaria para su introducción y presentación, que es lo
que da ergonomía y elegancia a una aplicación. El conocimiento de bases de datos es
necesario. Lo explicado hasta ahora, imprescindible.
Visual Basic nos permite trabajar directamente con distintas bases de datos (ACCESS,
dBaseIII, dBaseIV , dBase 5, Excel3, Excel4, Excel5, Excel7, FoxPro2.x, Foxpro3.0,
LotusWK1, LotusWK3, LotusWk4, Paradox3.x, Paradox4.x y Paradox5.x Esto lo logra
mediante el Motor de Bases de Datos Jet, herramienta de Microsoft para administrar los
datos en bases de datos Access. Tiene un nombre que mas parece de una materia de
ingeniería aeronáutica, pero en realidad no es mas que un conjunto de programas que se
cargan en el disco duro cuando instala Access o Visual Basic. E esta forma de acceder a las
bases de datos se le llama Acceso mediante objetos DAO. Lo de DAO viene de Data
Access Objet. Y es la forma más sencilla y rápida de acceder a una base de datos Access
instalada en el propio disco duro o en un disco de red de área local rápida. (Lo de facilidad de
acceso a una base de datos Access viene implícito en la política de Microsoft de facilitar la
compatibilidad entre sus aplicaciones)
Pero esto se quedaría muy corto si solamente se pudiese conectar con las bases de datos
citadas. No se puede concebir un sistema de desarrollo que no pueda acceder a bases como
Oracle, Informix, SQL Server, etc. Estas bases de datos, aparte de tener su propia interface
para acceso a datos, disponen de una forma de acceso común a todas : ODBC
Los objetos DAO pueden acceder también a bases de datos a través de ODBC. Esto podemos
decirlo con la versión DAO 3.5, no podemos decir lo mismo con la 2.5, procedimiento previsto
pero que no funcionaba.
Este pobre funcionamiento de DAO con ODBC llevó a Microsoft a crear otro tipo de acceso a
datos: el RDO (Remote Data Objet), y los objetos de acceso a datos RDO. Esto consiste en
objetos parecidos a los DAO, pero que en vez de atacar directamente a la base de datos como
lo hace DAO, lo hacen a través de una conexión ODBC previamente establecida en Windows.
Así por ejemplo, en un objeto DAO hablamos del nombre de la base de datos, refiriéndonos al
nombre del fichero que contiene los datos (C:\Mis Documentos\MiBase.Mdb), y en RDO nos
referimos al nombre de la conexión ODBC (Connection) refiriéndonos al nombre de una
conexión ODBC ya establecida, que apunta a una base de datos que es donde vamos a leer o
escribir. Este método tiene la gran ventaja de que podemos establecer hoy una conexión con
una base determinada, y si queremos cambiar mañana la base de datos sobre la que vamos a
trabajar, basta con cambiar esa conexión apuntando hacia otra base de datos. Así no es
necesario realizar ningún cambio a nuestro código
Pero RDO no tiene las prestaciones que tiene DAO para trabajar con bases de datos Access.
De hecho, RDO pierde grandes facilidades que aporta DAO para este tipo de bases de datos.
RDO trabaja sobre una conexión ya instalada, que apunta a una base de datos ya creada. Con
RDO no podemos crear esa base de datos. Esto es obvio, con RDO podemos entrar en una
base de datos Oracle, por ejemplo. Nadie puede pretender que Visual Basic cree una base de
datos de esa marca. Sin embargo, con ADO sí podemos crear una base de datos Access.
Piense en la facilidad para distribuir una aplicación que tenga una base de datos Access cuyos
campos puedan tener un tamaño definido por el propio usuario. Bastaría poner una
herramienta en la propia aplicación donde el usuario introdujese los tamaños de los campos
tipo texto, para adaptar la base de datos a sus necesidades. Esto puede hacerlo con DAO
puesto que nos permite crear bases de datos Access, pero no lo podemos hacer mediante
RDO.
RDO utiliza una terminología ligeramente distinta de DAO. Por ejemplo, sonde en DAO
ponemos OpenRecordset, en RDO debemos poner OpenResultset. Es una pena que no se
pueda trabajar con la misma terminología y con todas las prestaciones que tiene DAO a través
de ODBC. Esto mismo pensó Microsoft, y esa fue la razón de implementar en los objetos DAO
3.5 la conexión ODBCDirect.
ODBCDirect nos permite trabajar con los mismos objetos DAO pero a través de una conexión
ODBC, que en este caso, funciona. Puede ser un poco más lenta que DAO, pero en esto hay
opiniones para todos. De esta forma es posible seguir utilizando los viejos métodos aprendidos
para DAO (Y lo que es mejor, reutilizar el código ya escrito en anteriores aplicaciones) a través
de una conexión ODBC. Lo veremos mas adelante.
Pero no debemos pensar que aquí se acaban los métodos de acceso a datos. Existe otro mas,
muy reciente denominado ADO. ADO quiere sustituir a DAO y RDO.
En este punto ponía hace dos años que eso lo va a determinar el mercado. Hoy (Nov.2002) se
puede decir que ADO ha sustituido en todas las aplicaciones nuevas a RDO, y ha dejado a
DAO solamente el mercado de las pequeñas aplicaciones domésticas. ADO es una maravilla.
Por eso, la Guía del Estudiante dedica un nuevo capítulo y un amplio ejemplo a ADO. Pero eso
no implica que no haya que ir paso a paso. Y primero hay que aprender DAO. Pero ahora todo
lo que aprenda de DAO le va a servir para ADO, pues en ADO sí coincide el código.
Aprendamos pues DAO, y luego aplicaremos nuestros conocimientos a ADO. La gran ventaja
de ADO es que en aquellas aplicaciones que leen una base situada en un servidor, ocupa
menos la red de área local, ya que trabaja una aplicación Cliente – Servidor.
Comenzaremos el estudio de bases de datos con los objetos DAO. Y para DAO el control que
organiza el trabajo al Motor Jet es el Control Data. Existen controles similares para RDO y
para ADO. Pero no corramos y centrémonos sobre DAO y nuestro motor Jet
El Control Data
El control Data puede tomarse directamente de la caja de herramientas. Al contrario que los
controles similares RDO y ADO, este está siempre en la caja de herramientas. En el formulario
tiene el aspecto de una barra deslizante :
Decíamos que el control Data sirve de enlace entre la base de datos y los controles que
pueden presentar datos. Estos controles a los que nos referimos son los llamados Controles
Enlazados a Datos, y que son viejos conocidos nuestros, al menos algunos de ellos.
Un control Label puede presentar un dato. Si queremos que ese dato sea un campo de una
tabla de una base de datos, basta que enlacemos la base de datos al control data, y que
enlacemos luego el control Label con el control Data. Si hacemos lo mismo con el TextBox, no
solamente podremos presentar datos de la BD, sino que los podemos introducir, al ser el
TextBox un control bidireccional.
Veamos como se enlaza un control data a una base de datos. Se supone que el alumno
conoce la estructura de una base Access, BD con la que vamos a iniciar este estudio. De
cualquier forma, y para los que han suspendido la asignatura de base de datos Access, citar
solamente que una base de datos Access contiene dentro de un fichero (P.e.
C:\MiCarpeta\MiBase.Mdb) varias Tablas (P.e. Tabla1, Tabla2 y Tabla3) Cada tabla es en sí
una base de datos en el sentido estricto. Tiene un número indeterminado de registros, (filas)
que guardan la información en varios Campos.
Con esto ya podría trabajar, pero le faltan aún ciertos detalles. Por ejemplo, el tipo de recordset
que debe crear. (Dynaset, Snapshot, Table) Esto se lo indicamos en la propiedad
RecordsetType, que por defecto le va a poner Dynaset. (Ya veremos que es cada uno de
ellos) Y ya tenemos casi todas las propiedades del control Data cubiertas. Las demás son las
típicas de todos los controles. Casi todas las propiedades, porque hay una que se ha
introducido en la versión 6 de VB para permitir las dos formas de atacar a la base de datos,
con el motor Jet o a través del citado ODBCDirect.. Esa propiedad es DefaultType y nos
permite elegir entre usar el motor de base de datos Jet (Poniendo a esta propiedad el valor 2 o
dbUseJet) o usar ODBCDirect (Poniéndole el valor 1 ó dbUseODBC) El valor por defecto es
usar el motor Jet y así trabajaremos en principio.
Ya tenemos enlazado el control Data a la base de datos. Falta ahora enlazar una etiqueta y un
TextBox al control Data para tener el enlace completo. Eso es aún más sencillo. Si
desplegamos las propiedades del TextBox por ejemplo, veremos que tiene unas propiedades
que cuando lo estudiamos, las habíamos pasado un poco por alto: DataSource y DataField.
En la figura puede ver que la propiedad DataSource puede desplegarse, mostrando en este
caso el nombre del único control Data que tenemos en el formulario: Data1 Si tuviésemos mas
controles Data, aparecerían los nombres de todos ellos. Se elige uno.
A continuación debemos señalarle qué campo queremos que nos presente. Podemos
desplegar la lista, donde podemos ver los campos de la tabla elegida para la propiedad
RecordSource del control Data. Elegimos uno y ejecutamos la aplicación.
El control Data proporciona acceso a datos almacenados en bases de datos usando uno de los
tres tipos de objetos Recordset. El control Data le permite ir de registro en registro y presentar
y manipular los datos de los en controles enlazados. Sin un control Data, los controles
enlazados con datos de un formulario no pueden tener acceso automáticamente a los datos.
Los controles enlazados solamente pueden tener acceso a un control Data si este está en el
mismo Formulario.
Avance de términos.
Objeto Recordset (conjunto de registros)
Es un conjunto lógico de registros. Los tres tipos de objetos Recordset son Dynaset, (Permite
la lectura y escritura de un registro) Snapshot (Realiza una lectura instantánea de los
registros, no permitiendo modificarlos) y Table. (Representación en el código de una tabla
base que puede utilizarse para agregar, modificar o eliminar registros de una sola tabla).
Controles enlazados
Son los controles que pueden presentar directamente datos de uno o varios campos de una
Base de Datos. Los controles DBList, DBCombo y DBGrid tienen la posibilidad de presentar un
conjunto de registros cuando se asocian con un control Data. Los controles CheckBox,
TextBox, Label, Picture, Image, ListBox y ComboBox también son controles enlazados con
datos y pueden asociarse a un único campo de un Recordset administrado por un control Data.
La mayoría de las operaciones de acceso a datos se pueden realizar usando el control Data sin
escribir ningún código. Los controles enlazados con un control Data presentan de forma
automática los datos de uno o más campos del registro actual o, en algunos casos, de un
conjunto de registros a ambos lados del registro actual. El control Data realiza todas las
operaciones sobre el registro actual.
Avance de términos
Registro Actual. Un registro es un conjunto completo de campos. Una base puede tener
muchos registros, pero el “puntero” de la base de datos apunta a un único registro en cada
momento. Ese registro al que apunta el puntero se llama registro actual.
Si el control Data recibe instrucciones de moverse a un registro diferente, todos los controles
enlazados pasan automáticamente los cambios al control Data para ser guardados en la base
de datos. El control Data se sitúa después en el registro requerido y pasa los datos del registro
actual a los controles enlazados donde son presentados. Esto significa que se pueden
modificar los datos de una base de datos simplemente cambiando los datos en los controles
enlazados que lo permitan, y moviendo el puntero de la base de datos, es decir, cambiando el
registro actual.
Una vez iniciada la aplicación, Visual Basic usa las propiedades del control Data para abrir la
base de datos seleccionada, abrir un objeto Database y crear un objeto Recordset. Las
propiedades Database y Recordset del control Data hacen referencia a los objetos Database y
Recordset recién creados que pueden ser manipulados por el control Data. Siempre podremos
conocer el Recordset usado por el control Data leyendo esa propiedad
Y si tenemos otro control Data en la aplicación (Puede estar en otro formulario) siempre
podemos hacer que el recordset de este segundo control Data sea igual al del primero
Cuando se usa un control Data para crear un objeto Recordset o cuando se crea un objeto
Recordset en el código y se asigna al control Data, el motor de base de datos Jet de Microsoft
puebla automáticamente el objeto Recordset. Como resultado, los marcadores (y en los objetos
Recordset de tipo snapshot, los datos del conjunto de registros) se guardan en la memoria
local; el usuario no necesita manipular el control Data y no es necesario invocar el método
MoveLast en el código para conocer el número total de registros. Los bloqueos de página
usados para crear el Recordset se liberan más rápidamente, haciendo posible que otros
objetos Recordset accedan a los mismos datos. Los objetos Recordset creados en el código
pero que no se asignan a un control Data no son poblados automáticamente por el motor Jet.
Se deben poblar desde el código.
El control Data crea un objeto Database y un objeto Recordset automáticamente. Estos objetos
de acceso a datos son idénticos a los creados mediante código, y tienen las mismas
propiedades y métodos. Podemos referirnos a ellos usando el nombre del control Data seguido
del nombre del objeto (Database o Recordset). Por ejemplo :
Data1.Database Data1.Recordset
El objeto Database creado por un control Data no se cierra aunque se cambie la propiedad
DatabaseName del control Data. Lo mismo ocurre con el objeto Recordset. Solamente
podemos cerrarlos utilizando el método Close :
Data1.Database.Close Data1.Recordset.Close
Los objetos para acceso a datos Database y Recordset creados por el control Data tienen
cada uno sus propiedades y métodos propios y se pueden escribir procedimientos que usen
estas propiedades y métodos para manipular los datos.
Por ejemplo, el método MoveNext de un objeto Recordset mueve el registro actual al siguiente
registro del Recordset. Para invocar este método, se podría usar el siguiente código:
Data1.Recordset.MoveNext
El control Data puede crear cualquiera de los tres tipos de objetos Recordset (Dynaset,
Snapshot, Table) Si no se indica el tipo a crear, se crea un Recordset de tipo Dynaset.
Nota. Las constantes usadas para requerir un tipo específico de Recordset cuando se usa un
control Data son diferentes de las constantes usadas para determinar el tipo de Recordset
creado o que se va crear usando el método OpenRecordset.
En la pequeña aplicación realizada al comienzo de este tema ha visto que los controles
enlazados a datos permiten visualizar e introducir datos en la base de datos a través del control
Data. Efectivamente, no tendría sentido poner un control data sin enlazarlo a otros controles
para que estos nos sirvan de elementos de presentación y captura de datos. Veremos mas
adelante en este capítulo los controles enlazados a datos.
Consultas almacenadas
Otra opción importante cuando se usa el control Data es la posibilidad de presentar una
consulta realizada previamente en Access. Si se ha creado previamente una consulta, el
control Data nos mostrará esa consulta como si se tratase de una tabla más de la base de
datos, al desplegar la lista de tablas para cubrir la propiedad RecordSource. Puede por lo tanto
presentar solamente los campos que necesite en su aplicación, tomados de una tabla (o de
varias tablas si ha establecido las relaciones oportunas) y de esta forma su aplicación va a
Es mucho más rápida una consulta utilizando una consulta ya creada en Access que
introduciendo la consulta en SQL. La razón es muy sencilla. Al crear una consulta es Access
quien crea una especie de “tabla nueva” en la propia base de datos. Esta tabla nueva no
contiene datos, sino referencias a registros de una tabla. Por lo tanto, el motor de bases de
datos se limita a recorrer esa “tabla nueva”, tomar el número del registro que debe presentar, ir
a ese registro y tomar el dato que contiene. Si lo que hace es una consulta SQL, se debe
obtener la información registro a registro según las condiciones establecidas en la cláusula
SQL. Esta segunda opción tarda logicamente más. Y si está leyendo la base de datos a través
de una red de área local, la ocupación de esta red es mucho menor si el recordset se crea con
la consulta de Access.
Estamos hablando de una consulta SQL establecida en el control Data. ¿Dónde? Justamente
en la propiedad RecordSource del control data. Hasta ahora habíamos puesto en esa
propiedad el nombre de una tabla o de una consulta ya hecha en Access. Si en vez del nombre
de la tabla ponemos una consulta SQL, la cosa también funciona:
Con una Tabla
Data1.RecordSource = “Autores”
Esta consulta obtiene de la tabla Autores, solamente los registros en los que el campo
Apellidos sea igual a “Cervantes Saavedra” Funciona, pero para averiguar el número de
registros que tienen esos apellidos deberá recorrerlos todos, comprobar si son iguales a los
expresados en la sentencia SQL y en caso afirmativo pasarlos al recordset creado. Si se
hubiera creado una consulta previamente en Access, y pusiésemos el nombre de la consulta
en la propiedad RecordSource del control Data, éste iría directamente a los registros que
gozan de tan ilustres apellidos, ya que Access habría hecho una tabla con los números de los
registros que cumplen esa condición (esa tabla no contiene los datos, sino el número de los
registros que los contienen), el control data leería esos números e iría a los registros indicados
en esos números, evitando de esta forma tener que leer el contenido del campo Apellidos del
resto de los registros. De cualquier forma, si la base de datos está en el mismo ordenador que
la aplicación, esto empieza a ser importante cuando trate tablas con muchos registros. Si está
en una red de área local no hace falta tener muchos registros para comprobar que se ralentiza
la aplicación.
Align El control Data puede programarse para que se ajuste automáticamente a la parte
superior o inferior de su formulario primario usando la propiedad Align. En cualquier caso, el
control Data ajusta su tamaño horizontal al de su formulario primario cuando el tamaño de éste
cambia. Esta propiedad permite situar un control Data en un formulario MDI sin requerir un
control Picture que lo contenga.
BOF (Begin Of File). Se produce el BOF cuando el control Data se posiciona sobre el registro
inmediatamente anterior al primero (No es un juego de palabras). Este registro será el -1.
EOF (End Of File) Se produce la condición EOF cuando el control Data se posiciona en el
registro inmediatamente posterior al último. Este registro será también el -1.
La propiedad BOFAction permite seleccionar el comportamiento del Data cuando nos hemos
pasado de registros por abajo. Tiene las opciones MoveFirst (se mueve al primer registro) o
BOF (se queda donde está)
Connect Muy Importante. En esta propiedad debemos indicarle al control Data el tipo de
base de datos a la que va a conectarse. Admite todas las bases enumeradas al principio de
este capítulo.
Esta propiedad es sólo de lectura. Devuelve una referencia a un objeto Database subyacente
de un control Data.
El objeto Database creado por el control Data se basa en las propiedades DatabaseName,
Exclusive, ReadOnly y Connect del control.
Los objetos Database tienen propiedades y métodos que puede utilizar para administrar los
datos. Puede utilizar cualquier método de un objeto Database con la propiedad Database de un
control Data, como Close y Execute. También puede examinar la estructura interna de la
Database empleando su colección TableDefs y, a su vez, las colecciones Fields e Indexes de
objetos TableDef individuales.
DefaultType
Devuelve o establece un valor que indica el tipo del origen de datos (Motor Jet u ODBCDirect)
que se usan en el control Data.
Puede tomar los valores 1 (Usa ODBCDirect) y 2 (Usa el Motor Jet) Pueden usarse también
las constantes dbUseODBC o dbUseJet respectivamente
EditMode
Solo lectura en tiempo de ejecución. Devuelve un valor que indica el estado de modificación del
registro actual.
Exclusive Devuelve o establece un valor (True / False) que indica si la base de datos está
abierta para acceso de un único usuario o de múltiples usuarios.
Font, ForeColor, Height, Index, Left, MouseIcon, MousePointer, Name, igual que el resto
de los controles.
Negotiate Propiedad característica de los controles que tienen la propiedad Align. Establece
un valor que determina si se muestra un control que puede alinearse cuando un objeto activo
del formulario muestra una o más barras de herramientas. No está disponible en tiempo de
ejecución.
Options Devuelve o establece un valor que especifica una o más características del objeto
Recordset de la propiedad Recordset del control Data. Puede ponerse en el cuadro de
propiedades o en tiempo de ejecución. Puede tomar los siguientes valores o nombres de
constantes (estos nombres solo si los introduce en tiempo de ejecución)
Para establecer mas de un valor de los descritos, basta con sumar sus valores. También
puede establecer más de un valor para esta propiedad, combinando opciones sumando valores
entre sí. Por ejemplo, para establecer dbReadOnly y dbInconsistent puede utilizar este
código:
Para determinar si la propiedad contiene un valor específico, puede utilizar el operador And.
Por ejemplo, para averiguar si el Recordset está abierto para acceso de sólo lectura, podría
usar este código:
ReadOnly Devuelve o establece un valor que determina si la Database del control está abierta
para acceso de sólo lectura.
RecordsetType
Devuelve o establece un valor que indica el tipo de objeto Recordset que desea que cree el
control Data.
Los valores o nombre de la constante que puede adoptar son los siguientes:
Recordset
Devuelve o establece un objeto Recordset definido por las propiedades de un control Data o
por un objeto Recordset existente.
Veremos mas adelante las propiedades de un objeto Recordset, propiedades que son en todo
aplicables al Recordset del control Data.
RecordSource
NombreDelControlData.RecordSource = “NombredelaTabla
Donde NombredelaTabla es una expresión de cadena que especifica el nombre de una Tabla o
una Consulta, de las que componen la base de datos especificada en la propiedad
DatabaseName, o una consulta SQL válida que utiliza sintaxis apropiada para la base de datos
especificada en la propiedad DataBaseName. La propiedad RecordSource especifica el
origen de los recursos accesibles a través de controles enlazados del formulario. Si establece
la propiedad RecordSource como el nombre de una tabla existente en la base de datos, todos
los campos de esa tabla serán visibles a los controles enlazados adjuntos a este control Data.
El orden de los registros recuperados lo establece el objeto Index que selecciona mediante la
propiedad Index del Recordset. Si no establece la propiedad Index, los datos se devolverán sin
ningún orden concreto.
Si establece la propiedad RecordSource como una instrucción SQL que devuelve registros,
todos los campos devueltos por la consulta a SQL serán visibles a los controles enlazados
adjuntos al control Data. Esta instrucción puede incluir una cláusula ORDER BY para cambiar
el orden de los registros devueltos por el Recordset creado por el control Data o una cláusula
WHERE para filtrar los registros.
Nota. Asegúrese de que cada control enlazado tiene un valor válido para su propiedad
DataField. Si cambia el valor de la propiedad RecordSource de un control Data y, a
continuación, utiliza Refresh, el Recordset identificará el nuevo objeto. Esto puede invalidar los
valores de DataField de controles enlazados y producir un error interceptable.
(Se explican aquí los métodos que inciden directamente en el tratamiento de bases de datos.
No se comentan los métodos Drag, Move y ZOrder que son idénticos a los del resto de
controles)
Método Refresh
De momento es aplicable al control Data. Veremos que también es aplicable a otros objetos de
acceso a datos (QueryDef). El método Refresh no puede utilizarse con colecciones que
no sean persistentes, como Databases, Recordsets o Workspaces.
Actualiza los datos del recordset del control data. Imagínese que el control data accede a una
base de datos compartida. Mediante este método actualizamos el contenido del recordset del
Data y por lo tanto los datos presentados a través de los controles enlazados. El método
Refresh también se utiliza para cerrar y volver a generar el objeto Recordset o las estructuras
de datos creadas por un control Data.
Sintaxis NombredelcontrolData.Refresh
Puede utilizar el método Refresh sobre un control Data para abrir o reabrir la base de datos (si
han variado las propiedades DatabaseName, ReadOnly, Exclusive o Connect) y volver a
generar el objeto Recordset indicado por la propiedad Recordset del control.
Método UpdateControls
Actualiza los datos presentes en los controles enlazados a datos vinculados al control Data.
Sintaxis Nombredelcontroldata.UpdateControls
Utilice este método para restablecer en los controles enlazados sus valores originales, por
ejemplo cuando un usuario modifica los datos y luego decide cancelar los cambios.
Este método produce el mismo efecto que hacer actual de nuevo al registro actual, excepto en
que no se produce ningún evento ni introduce en la Base de Datos los posibles valores que se
hubiesen cambiado en los controles enlazados.
Método UpdateRecord
Sintaxis NombredelcontrolData.UpdateRecord
Puede utilizar este método para guardar el contenido actual de los controles enlazados en la
base de datos. Los cambios introducidos en los controles enlazados a datos se pasan a la
base de datos al cambiar el registro actual, bien mediante código (Data1.Recordset.MoveNext,
p.e.) o usando las flechas del control data, o cambiando el registro actual en un DBGrid. Sin
embargo hay circunstancias en las que no es apropiado hacer esto. Mediante el método
UpdateRecord introducimos los cambios en la BD. Este método no desencadena el evento
Validate.
En algunos casos, la actualización puede no tener lugar, debido a que la operación vulnere las
restricciones de integridad referencial, o que la página que contiene el registro esté bloqueada,
o que la base de datos u objeto Recordset no sean actualizables, o a que el usuario no cuente
con el permiso adecuado para la operación. En cualquiera de estas circunstancias, se
producirá un error interceptable.
Error
Se produce solamente como resultado de un error de acceso a datos que tiene lugar cuando
no está ejecutando código Visual Basic. Lo explicamos.
El control data carga los datos durante la carga del formulario que lo contiene, abriendo la base
indicada en su propiedad DataBaseName. Imagínese que no existe esa base de datos en el
disco. En ese caso no se producirá ningún interceptable ya que no se ejecuta ningún código
escrito. Tampoco se ejecuta ningún código escrito cuando el usuario hace click en uno de los
botones del control data.
El procedimiento Error del control Data se ejecuta cada vez que ocurre un error por una
maniobra de este tipo, y pasa el código de error como parámetro. Analizando el código de error
podemos escribir código en este procedimiento para paliar el error. La ayuda de VB tiene un
buen ejemplo del uso de este procedimiento.
Este ejemplo presenta un cuadro de diálogo Abrir si no se ha podido encontrar la base de datos
especificada en la propiedad DataBaseName del control Data después de haber terminado el
evento Form_Load.
Reposition
Este evento se puede usar para realizar cálculos basándose en los datos del registro actual o
para cambiar el formulario en respuesta a los datos del registro actual.
Validate
Se produce antes de que un registro diferente se convierta en el registro actual; antes del
método Update (excepto cuando los datos se guardan con el método UpdateRecord); y de los
métodos Delete, Unload o la operación Close.
El evento Validate se usa para realizar las últimas comprobaciones sobre los registros que se
van a escribir en la base de datos.
Los controles enlazados a datos son aquellos que pueden presentar datos de una base de
datos, a través de un control Data. Los controles enlazados a datos permiten crear aplicaciones
con acceso a datos con muy poco código, o incluso ninguno. Para utilizar cualquiera de estos
controles enlazados conectables a datos debe incluir uno o más controles Data en un
formulario. El control Data establece un enlace entre la base de datos y los controles enlazados
para la manipulación de los datos. El control Data que sirve de enlace entre la Base de Datos y
los controles enlazados debe estar obligatoriamente en el mismo formulario que los controles.
Los controles asociados a datos tienen todos la propiedad DataSource, que es la propiedad
donde se debe poner el nombre del control Data asociado a ellos. Existen en Visual Basic trece
controles enlazados a datos, además del control Data :
Data Ya comentado, se utiliza para tener acceso a los datos de las bases a través de
controles enlazados de un formulario. Crea y administra los objetos Database y Recordset
para su uso por parte de los controles enlazados. Requerido para su uso con todos los demás
controles enlazados.
DBCombo Se utiliza para obtener una combinación enlazada formada por un cuadro de lista
y un cuadro de texto. La lista puede llenarse automáticamente a partir de un control Data. El
DBList Se usa para mostrar una lista generada a partir de un control Data en la que el usuario
puede elegir un elemento. La lista puede rellenarse automáticamente desde un control Data, y
puede proporcionar acceso de lectura / escritura a un campo de texto específico seleccionado
en ella.
DBGrid Se utiliza para mostrar a la vez todos los registros del recordset del control Data. El
DBGrid se rellena automáticamente con todos los registros, y muestra todos los campos del
recordset de control Data, formando una cuadrícula con filas y columnas. El usuario puede
elegir un elemento de la cuadrícula para variar el valor en ese campo y registro o introducir un
nuevo registro. El hecho de colocarse sobre un determinado registro de la cuadrícula, fuerza a
ese registro a convertirse en el registro actual del control Data al que está asociado.
Label Se usa para el texto que el usuario no debe modificar. Puede utilizarse para ofrecer
acceso de sólo lectura a un campo de texto específico.
TextBox Se utiliza para almacenar texto que el usuario puede introducir o modificar. Puede
utilizarse para proporcionar acceso de lectura / escritura a un campo de texto específico.
CheckBox Se utiliza para crear un cuadro que el usuario puede elegir de forma sencilla para
indicar si algo es verdadero o falso, o para mostrar varias opciones entre las que el usuario
pueda elegir más de una. Puede utilizarse para proporcionar acceso de lectura / escritura a un
campo booleano o de bit específico.
ListBox Se utiliza para mostrar una lista en la cual el usuario puede elegir un elemento. La
lista se rellena con el método AddItem. Puede usarse para proporcionar acceso de lectura /
escritura a un campo de texto específico seleccionado en la lista. Consulte el control DBList
PictureBox Se usa para mostrar una imagen gráfica de un mapa de bits, un icono o un meta-
archivo en un formulario. Puede utilizarse para proporcionar acceso de lectura / escritura a un
campo de imagen o binario específico.
Image Se utiliza para mostrar una imagen gráfica de un mapa de bits, un icono o un meta-
archivo en un formulario. Las imágenes mostradas en un control Image utilizan menos recursos
que las de los controles PictureBox. Puede usarse para proporcionar acceso de lectura /
escritura a un campo de imagen o binario específico.
Los controles enlazados a datos tienen unas propiedades para el acceso a datos basadas en el
enlace con la base de datos a través del control Data. Las propiedades comunes a todos ellos
son :
Vamos a estudiar cada uno de los controles enlazados a datos con un poco mas de detalle.
Control Label
Posiblemente el control Label es el control más sencillo para mostrar el contenido de un campo
de una base de datos. Como todos los controles enlazados a datos, el Label permite presentar
los datos e introducirlos en la base a través del control Data. Lo que ocurre con el Label es que
su propiedad Caption no se puede introducir directamente por teclado, y deberá cambiarse por
código. Esto puede ser una ventaja (no hay posibilidad de introducirlo accidentalmente) y un
inconveniente, al tener que escribir código para hacerlo.
El control Label, al poder ser origen (a través del formulario que lo contiene) y destino de un
enlace DDE, esto nos puede resolver muchos problemas de introdución de distintos datos de
otras aplicaciones que no tengan acceso directo a una base de datos.
Control TextBox
Todo lo dicho del control Label es aplicable al TextBox, que además presenta la particularidad
de que en este control sí se puede escribir directamente desde el teclado. De esta forma
podemos introducir datos o cambiar los existentes en la base de datos.
Control CheckBox
El control CheckBox permite presentar e introducir datos de tipo Booleano. Tiene las
propiedades DataSource y DataField en lo relativo a acceso a datos.
Estos controles tienen una característica especial respecto a su comportamiento con el enlace
a la base de datos. La lista no se puede cargar directamente desde la base de datos, sino a
través de un control intermedio, por ejemplo un Label, donde presentaremos un campo de la
base de datos. El texto de la propiedad Caption de este Label se introduce en el ListBox o
ComboBox mediante el método AddItem. Una vez introducidos todos los elementos de ese
campo que nos interesen, cada vez que la base de datos se sitúa sobre el registro
correspondiente a uno de los elementos que está en el ListBox o ComboBox, éste cambia su
ListIndex para seleccionar el elemento correspondiente al registro actual del control Data.
Para agregar estos controles debe insertar el OCX Microsoft Data Bound List Controls 6.0
Estos controles tienen dos enlaces a controles data. Uno para rellenar la lista, que se lo
debemos indicar en la propiedad RowSource, acompañado del nombre del campo con el que
la va a rellenar, que introduciremos en la propiedad ListField, y otro enlace a otro control Data,
que se lo debemos indicar en la propiedad DataSource. El campo de este segundo control
Data se lo indicaremos en la propiedad DataField. El comportamiento de estos dos controles
es el siguiente: Mediante el enlace a través de la propiedad RowSource se rellena la lista,
utilizando los datos del campo elegido en la propiedad ListField. Imaginemos que la tabla a la
que nos conectamos con estas propiedades es la Tabla A. La tabla a la que enlazamos el
DBList mediante la propiedad DataSource, le llamamos Tabla B. Con este invento vamos a
pasar un dato desde la tabla A a la tabla B. El dato que vamos a pasar está en la tabla A en el
campo señalado en la propiedad BoundColumn del DBList. El registro de la tabla A de donde
cogemos el dato será el seleccionado en el DBList. El registro de la tabla B donde vamos a
meter el dato será el registro actual de ese control data, control actual que habremos
seleccionado mediante cualquier método. El campo de esa tabla B que vamos a variar será el
campo indicado en la propiedad DataField. (Esto se vuelve a explicar mas abajo, pero para
entenderlo no hay mas remedio que realizar una práctica)
Pueden mostrar una imagen almacenada en una Base de Datos. Mediante el control Data, se
puede introducir la imagen presente en uno de estos controles en la Base de Datos.
El campo que contenga una imagen en una BD debe ser tipo Objeto OLE (Binario Largo en
versiones anteriores de Access), y el tipo de imágenes que se pueden introducir son los mapas
de bits (Archivos con extensión .BMP), los archivos de icono, (Extensión .ICO) y los
metaarchivos. (Metafiles, extensión .WMF)
Para introducir un gráfico en una BD es mas práctico introducirlo mediante un control Data y un
control Picture o Image que creando por código un Recordset. En realidad deberíamos decir
que es el único método práctico de introducir / sacar imágenes de una Base de Datos
Control DBGrid
Es posiblemente el control que más se use para presentar y modificar datos de una B.D. El
control DBGrid presenta todos los registros y todos los campos de la Base de Datos. Por eso,
necesita obligatoriamente un control Data para poder presentar datos. Otros controles (Label,
TextBox, Picture, etc.) que solamente presentan un dato (un campo de un registro) pueden
trabajar sin necesidad de un control data, creando un Recordset mediante código. (Lo veremos
un poco mas adelante). Sin embargo el control DBGrid, al presentar todos los datos de la base
de datos necesita un control Data. Veamos porqué.
Cuando creamos un objeto Recordset mediante la instrucción : (se verá mas adelante)
lo que estamos haciendo es seleccionar, de todos los campos que pueda tener la tabla llamada
Mitabla, los denominados campo1 y campo2. Cada vez que seleccionemos un registro, es ese
registro solamente el que se mantiene en la memoria del ordenador, (el registro actual) y de
ese registro, solamente metemos los datos del campo1 y campo2.
Cuando creamos un Recordset mediante un control Data, se meten en la memoria TODOS los
registros de la tabla especificada en el control data. Por lo tanto, al permanecer todos los
registros de esa tabla en memoria, podremos presentar sus valores en el control DBGrid. No lo
podremos hacer con un Recordset creado mediante código, que solamente mantiene un
Con el Recordset creado con la instrucción anterior, podemos forzar a un control Data que su
Recordset sea igual al ya creado mediante la instrucción :
En este caso, el control Data1 tomará todos los registros con los campos campo1 y campo2 de
la base de datos y los meterá en la memoria RAM. Así ya podemos rellenar las cuadrículas del
control DBGrid.
Observe que una aplicación de acceso a datos ocupará mucha mas memoria RAM si
establecemos el enlace con la base de datos mediante un control Data que si lo hacemos
creando Recordsets a medida. Pero si necesitamos presentar los datos en un DBGrid, no
quedará mas remedio que usar un control Data. Si nuestra aplicación no tiene que presentar en
el DBGrid todos los campos de la tabla de la B.D. podemos crear previamente un Recordset
mediante código y a continuación forzar que el Recordset del control Data sea igual al
Recordset creado, utilizando la expresión anterior.
El control DBGrid tendrá tantas columnas como campos tenga el Recordset. El número de filas
será igual al número de registros que tiene la tabla. Si se sobrepasa el espacio físico del
DBGrid para poder presentarlos, aparecerán automáticamente flechas de deslizamiento
vertical. El ancho de las columnas puede cambiarse mediante la propiedad Width del objeto
Columns del DBGrid.
DBGrid1.Columns(n).Width = Valor
Donde n es el número de la columna (la primera es la 0) y el valor debe expresarse según las
unidades de medida (ScaleWidth) del Formulario que lo contiene.
AllowAddNew
Devuelve o establece un valor que indica si el usuario puede agregar nuevos registros al objeto
Recordset subyacente a un control DBGrid.
La última fila que se muestra en el control DBGrid se deja en blanco para permitir a los
usuarios introducir nuevos registros. Si la propiedad AllowAddNew es False, los usuarios no
pueden establecer el foco en dicha fila.
El Recordset subyacente puede, por otras razones, no permitir inserciones incluso en el caso
de que la propiedad AllowAddNew sea True. En este caso, se producirá un error si el usuario
intenta agregar un registro.
AllowDelete
Devuelve o establece un valor que indica si el usuario puede eliminar registros del objeto
Recordset subyacente a un control DBGrid.
Utilice la propiedad AllowDelete para impedir que los usuarios eliminen registros del conjunto
de registros a través de la interacción con el control DBGrid.
El objeto Recordset subyacente puede, por otras razones, no permitir eliminaciones incluso en
el caso de que la propiedad AllowDelete sea True. En este caso, se producirá un error si el
usuario intenta eliminar un registro.
AllowRowSizing
Si la propiedad AllowSizing es True, el puntero del mouse se convierte en una flecha de doble
cabeza (Size N S) cuando se sitúa sobre el divisor de filas entre selectores de registro, y el
usuario puede modificar el tamaño de las filas mediante arrastre. Cualquier cambio de tamaño
de columna provoca un evento RowResize.
AllowUpdate
Devuelve o establece un valor que indica si un usuario puede modificar datos del control
DBGrid.
Cuando la propiedad AllowUpdate es False, el usuario puede aún desplazarse a través del
control DBGrid y seleccionar datos, pero no puede modificar ninguno de los valores; cualquier
intento de hacerlo se ignora.
Puede también hacer uso de las propiedades del objeto Columns para hacer que columnas
individuales del control DBGrid sean de sólo lectura, pero los valores de la propiedad
AllowUpdate tienen prioridad sobre los valores establecidos para las columnas (sin modificar
éstos).
ColumnHeaders
DataMode
DefColWidth
RecordSelectors
Los selectores de registros aparecen a la izquierda de las filas en el control DBGrid. Cuando el
usuario elige el selector, el registro completo (fila del control DBGrid) se selecciona.
El objeto Columns es un objeto no privativo del control DBGrid, que contiene todas las
columnas y las propiedades de las columnas de un control. Podemos cambiar las propiedades
de cada una de las columnas de un DBGrid mediante las propiedades del objetos Columns
asociado a él. Por ejemplo, el encabezamiento de una columna en un DBGrid es, por defecto,
el nombre del campo que se va a presentar en esa columna. Si queremos poner otro
encabezamiento a una columna, ejecutaremos la expresión :
DBGrid1.Columns(0).Caption = "Cabecera"
donde el 0 entre paréntesis significa que estamos afectando a la columna número 0 (la primera
por la izquierda).
DBGrid1.Columns(3).Width = 1000
En este caso estamos fijando la anchura de la columna cuarta por la izquierda a 1000 unidades
de medida de las del Formulario que contiene al DBGrid.
Los dos controles DBList y DBCombo se implementan de la misma manera. Las dos únicas
diferencias estriban en la forma en que se presenta la información al usuario y la presencia de
la porción del control DBCombo en el cuadro de texto, que se emplea para introducir valores.
Los controles DBList y DBCombo tienen dos modos que pueden utilizarse individualmente o
al mismo tiempo:
Autollenado: Llena automáticamente la lista con un campo seleccionado de entre todos los
registros administrados por el control Data especificado por la propiedad RowSource del
control DBList o DBCombo.
En control DBList o DBCombo puede trabajar sobre dos controles Data. Uno para rellenar la
lista. El control Data y el campo que rellena la lista son los especificados en las propiedades
RowSource y ListField del control DBList o DBCombo. Respecto a este control Data estos
controles funcionan solamente como receptores de datos : No pueden cambiar el contenido de
los registros con los que rellenan su lista. (Llamemos a esta base de datos Base A en esta
explicación)
El otro control Data es el que estos controles usan para introducir datos en su BD asociada. El
control Data y el campo de la BD asociados a estos controles DBList y DBCombo, son los
especificados en las propiedades DataSource y DataField. Es sobre esta base de datos y el
campo correspondiente sobre los que estos controles DBList y DBCombo actúan cambiando o
introduciendo datos. (Llamemos a esta otra base de datos Base B)
Basta con seleccionar un elemento de la lista (que pertenece a la base A) y un campo (X) del
registro de la base A al que pertenece ese elemento se colocará en el campo correspondiente
(el indicado en la propiedad DataField del control DBList o DBCombo) de la base B. En el
caso del DBCombo, podemos escribir directamente el dato en su caja de texto en vez de
seleccionarlo de la lista.
Ese campo (X) cuyo dato pasamos de la base A a la base B no tiene porqué ser el elemento
que vemos en la lista. Puede ser otro campo de la base A. Será el que introduzcamos en la
propiedad BoundColumn del DBList o DBCombo. Es la propiedad que viene a continuación.
Léasela con la atención que se merece.
Propiedad BoundColumn
Devuelve o establece el nombre del campo de origen de un objeto Recordset que se utiliza
para suministrar un valor de datos a otro Recordset.
nombredelDBList.BoundColumn = nombredelcampo
Con estas ideas expresadas aquí, puede comenzar a leer el texto de ayuda de esta propiedad.
No se desespere si no entiende algo de lo allí expresado.
BoundText
Esta propiedad es de lectura y escritura. Es sencilla de usar para conocer el contenido del
campo especificado en BoundColumn. (lectura del valor)
Cuando la queramos utilizar para forzar el valor de esta propiedad a un valor determinado,
debemos utilizar la siguiente sintaxis :
objeto.BoundText [= valor]
Es decir, si hay coincidencia con algún valor de ese campo, BoundText seguirá con el valor
especificado. Si no la hay, BoundText se pone a Nulo.
MatchWithList
Si Variable = True el contenido de la propiedad BoundText coincide con uno de los registros de
la lista. Si es False, el contenido de la propiedad BoundText no coincide con ninguno de los
registros de la lista.
DataChanged
Devuelve o establece un valor que indica que han cambiado los datos del control enlazado por
algún proceso distinto de la recuperación de datos del registro actual. No está disponible en
tiempo de diseño.
Variable puede ser True, indicando que los datos que hay actualmente en el control no son
iguales que los del registro actual, y False (Predeterminado) que indica que los datos que hay
actualmente en el control (si los hay) son iguales que los del registro actual.
Comentarios
Cuando un control Data se mueve de un registro a otro, pasa datos desde los campos del
registro actual a controles enlazados al campo específico o el registro completo. Cuando se
MatchEntry
Devuelve o establece un valor que indica cómo el control DBCombo o DBList realiza
búsquedas basándose en la entrada del usuario.
Donde valor es una constante o un valor que define el comportamiento de un control cuando
tiene el enfoque y el usuario introduce uno o más caracteres.
Sintaxis DBCombo1.SelectedItem
SelText
SelText devuelve o establece una cadena con el texto actualmente seleccionado, o es una
cadena de longitud cero () si no hay caracteres seleccionados.
VisibleCount
Devuelve un valor que indica el número de elementos visibles del control DBCombo o DBList.
Sintaxis objeto.VisibleCount
Sintaxis objeto.VisibleItems
Estos marcadores pueden emplearse para obtener registros individuales del conjunto de
registros empleado para rellenar la lista.
Y aquí se terminan las propiedades de DBList y DBCombo. ¡La lata que dan y lo poco
que se usan!
Controles Picture e Image para acceso a datos a través del control Data.
Merece la pena hablar del almacenamiento de imágenes en una base de datos. Es posible
introducir imágenes en una base de datos Access. El campo debe ser del tipo Objeto OLE y
puede manejarse de dos formas, una de forma sencilla, mediante un control Data, y otra de
forma un poco complicada, mediante los métodos AppendChunk y GetChunk. Estos dos
métodos los veremos un próximo capítulo. Pero adelanto que es complicado usarlos.
Es muy sencillo sin embargo introducir y presentar imágenes residentes en una base de datos
mediante los controles Picture e Image. Estos controles son enlazados a datos y pueden
guardar una imagen o presentarla. Basta para ello poner los valores adecuados en las
propiedades DataSource y DataField para elegir un campo tipo Objeto OLE de la base de
datos y está el problema resuelto. Sobre todo para presentar la imagen, que lo hace
automáticamente. No es lo mismo para introducir los datos, pero también es muy sencillo.
Basta para ello disponer de un CommonDialog, por ejemplo, para buscar una imagen dentro
del disco. El fichero puede ser un BMP, JPG o WMF. Abrimos el CommonDialog (CD1) y
buscamos la imagen deseada. A continuación la cargamos en el Control Picture o Image que
está enlazado a datos mediante el método LoadPicture. Para introducir la imagen en la base
de datos basta con cambiar de registro, o utilizar el método UpdateRecord del control Data.
Para presentar una imagen es preferible usar el control Image con la propiedad Stretch = False
a usar el control Picture. Con el Image y esa propiedad puesta a False, siempre veremos la
imagen con el tamaño diseñado en el formulario. Hacerlo con el control Picture se puede, pero
es algo mas complicado.
Pese a que, como se ha visto, se pueden meter imágenes en la base de datos Access, no es
aconsejable hacerlo debido al volumen tan grande de datos que genera. Es preferible guardar
la imagen en el fichero con la imagen, y meter en la base de datos la dirección completa del
fichero. Para abrirlo no hay mas que leer la dirección del fichero, y presentarlo en el Picture o
Image mediante el método LoadPicture.
Control MSFlexGrid
El control MSFlexGrid es un control similar al DBGrid, pero pensado fundamentalmente para
enlazarlo a datos a través del control Adodc1 (El control similar al Data en ADO). Sin embargo
también funciona con el control Data, aunque con prestaciones reducidas. Estando enlazado al
control Data, muestra los datos solo para lectura. Tampoco permite cambiar el registro actual
del control Data haciendo click sobre una de las líneas del MSFlexGrid. Estas dos condiciones
le hacen el control ideal para presentar todos los datos del control Data para aquellos casos en
los que no se permite cambiarlos al usuario.
En el capítulo anterior hemos visto los controles capaces de acceder a un Base de Datos,
enlazados mediante un control Data. Se comenzó a exponer que no es necesario usar un
LSB Visual Basic – Guía del Estudiante Capítulo 1 Página 322
control Data para acceder a leer datos, añadir registros o cambiar su contenido. Y es más.
Comenzaremos ahora a ver que el control Data pese a que puede evitarnos gran cantidad de
líneas de código, nos hace perder el control respecto al programa. Es normal. El control Data
se ha desarrollado para realizar un trabajo muy estándar. Si nuestra aplicación se separa un
poco de lo normal, lo mas probable es que necesitemos realizar las operaciones mediante
código. Esto no quiere decir que el Data haya que dejarlo en desuso. Será necesario en
aquellas aplicaciones en las que se va a usar un control DBGrid, pues como se recordará,
mediante el uso de un control Data metemos en memoria RAM todo el contenido de la base de
datos relativo al Recordset que hemos creado. El control Data será necesario en aquellas
aplicaciones donde utilicemos un DBGrid, un DBList o un DBCombo. Veremos mas
adelante que también será necesario cuando queramos guardar imágenes en una Base de
Datos.
El control Data también permite consultas más rápidas a la BD. El hecho de guardar el
contenido completo del Recordset en la memoria hace que cualquier consulta sea más rápida.
Eso sí, estamos empleando mucha más memoria RAM.
Pero en este capítulo vamos a ver como se pueden manejar bases de datos utilizando otros
objetos de acceso a datos. Concretamente los objetos DAO.- (Data Access Objet).
Los objetos DAO utilizan el Motor de Bases de Datos Jet de Microsoft y trabajan
directamente sobre el fichero que contiene la base de datos. Existen otros objetos de acceso a
datos, como ha podido ver en el capítulo anterior, que no trabajan directamente sobre el
fichero, sino sobre una conexión ODBC que enlaza con la base de datos. Son los objetos RDO
y ADO, cuyo estudio se realizará en capítulos posteriores. Estos últimos tipos son mas
modernos, pero no tienen algunas prestaciones que tienen los DAO, debido precisamente a
que no trabajan directamente sobre el fichero. Centrémonos sobre los objetos DAO
Estos objetos, pese a que no tienen representación en la interface gráfica, son objetos Visual
Basic como los demás, y nos podremos referir a ellos por su nombre como hacíamos con
todos los controles. Eso sí, debemos declararlos como se declaran las variables, y siguen
siendo válidos los criterios de declaración de variables en cuanto al ámbito de aplicación. Si
declaramos un DAO en un procedimiento, no nos podremos referir a él fuera de ese
procedimiento. Si queremos que sea válido en toda la aplicación deberemos declararlo en la
sección de declaraciones de un módulo, o en la sección de declaraciones de un formulario si
fuese suficiente ese ámbito para nuestra aplicación.
La primera sorpresa suele ocurrir a la hora de declarar un objeto. Por ejemplo, para declarar un
objeto tipo DataBase debemos hacerlo con la siguiente declaración:
Lo que le está ocurriendo es que su programa no conoce el tipo de variable DataBase. Para
que la conozca, debe agregarle una referencia. Vaya a Proyecto | Referencias … de la barra
de menú y seleccione Microsoft DAO 3.51 Objet Library Haga click en Aceptar y ya tiene
agregada esa referencia a su programa. Agregar una referencia significa que le ha dicho a su
programa que puede hacer uso de una colección de DLLs donde podrá encontrar la definición
del objeto o la variable que no encuentra. Ahora ya no le dará el error anterior, pues en esa
DLL que acaba de agregarle a su programa está la explicación al secreto de lo que es un
objeto DataBase.
Verá que hay mas referencias parecidas a Microsoft DAO 3.51. (Las versiones 2.0, 2.1,
2.5/3.5, y si ha instalado Access2000 tendrá la 3.6) Existen tantas versiones distintas como
versiones de Access. En realidad esta DLL no es mas que un componente de Access que
podemos usar, al igual que lo hace Access, para gestionar una base de datos. Debe elegir la
La versión Microsoft DAO 3.6 corresponde a Access 2000. Si ha instalado este programa le
aparecerá esa referencia en la lista de referencias. No se sorprenda si antes de instalar Access
2000 no tenía esa referencia y después de la instalación sí. Si crea una BD con la versión 3.6
no podrá abrirla con Access 97 ¡Esta es la compatibilidad hacia delante de Microsoft!
Hay que señalar que las colecciones de estos objetos DAO son a su vez objetos de acceso a
datos. Esto hay que explicarlo un poco mejor. Por ejemplo, un objeto Database es un objeto
DAO que representa una base de datos abierta. Al objeto que agrupa a todas las bases de
datos abiertas en ese momento le llamamos Objeto Databases. Si tenemos dos bases de
datos abiertas en un determinado momento, el objeto Databases contendrá dos elementos.
Todos los objetos DAO excepto el dbEngine tienen colecciones. Es lógico. El dbEngine, como
verá mas adelante, es precisamente el Motor de Bases de Datos Jet y solamente existe uno.
Comenzaremos precisamente por él la explicación de los objetos DAO.
El dbEngine es el motor Jet. Y como vimos en el capítulo anterior, la versión 3.5 puede
trabajar directamente sobre el fichero de la base de datos o a través de una conexión ODBC.
En el primer caso decimos que está trabajando en el espacio de trabajo Microsoft Jet Si le
hacemos trabajar a través de ODBC decimos que estamos trabajando en el espacio de
trabajo ODBCDirect
Los objetos que emplea en cada uno de los espacios de trabajo pueden verse en las figuras
20.1 y 20.2. Puede observarse que tiene muchos mas objetos en el espacio de trabajo
Microsoft Jet que en el de ODBCDirect. Es lógico. Con el primero nos permite CREAR bases
de datos, y por lo tanto necesita tener objetos tales como el Tabledef, Index o Relation. Lo verá
un poco mas adelante.
En esta figura pueden verse los objetos y sus colecciones. Las colecciones llevan el nombre en
plural. Parece un poco complicado, y posiblemente lo será. Lo cierto es que para el trabajo que
se hace normalmente con bases de datos este diagrama queda bastante reducido. No se
extrañe tampoco de ver que objetos como el Fields y el Field existen en varios niveles. Lo
verá mucho mejor mas adelante.
Este modelo parece un poco mas asequible. La razón es que no hace todo lo que hace el
espacio de trabajo Microsoft Jet.
Objeto Workspace
Un objeto Workspace define una sesión de trabajo para un usuario específico. Una sesión de
trabajo es precisamente eso, una sesión de trabajo en el más puro estilo informático. Pueden
existir varias sesiones de trabajo, pero en la mayoría de los casos eso no es lo normal. Será
necesario crear varias sesiones cuando necesitemos imponer restricciones de acceso a una
base de datos, cuando tengamos que usar Transacciones (*) en un sistema multiusuario, y en
algún caso más. Las sesiones de trabajo tienen dueño (Usuario) y una palabra clave para
acceder a ellas.
Pero lo normal es tener solamente una sesión abierta. Y visual Basic nos facilita este caso
abriendo una sesión de trabajo automáticamente. Cuando se inicia Visual Basic, se crea un
Workspace con palabra clave y nombre de usuario Admin. Este Workspace es precisamente
el que ocupa el número cero de la colección de Workspaces. Es decir, es el Workspaces(0).
No se preocupe de que ahora mismo no lo entienda. Ya lo entenderá. Pero hay que exponerlo
ahora. El objeto Workspace contiene:
Al objeto Workspace se le puede dar un nombre definido por el usuario. Ese nombre habrá que
declararlo como nombre de una variable objeto Workspace :
La declaración de la variable Objeto tiene las mismas características que cualquier variable en
cuanto al ámbito en el que se puede usar. Para que pueda usarse en toda la aplicación
deberemos declararla en un Módulo con la sentencia Public
Una vez declarado el nombre del objeto Workspace, hay que crearlo. En realidad, y tal como
citábamos mas atrás, cada vez que se inicia una sesión de Visual Basic, se crea
automáticamente un Workspace. El número 0 A este Workspace no podemos ponerle ningún
tipo de palabra de acceso, ya que se la ha puesto VB : Admin. Utilicemos este Workspace, el
número 0 de la colección Workspaces, para comenzar a trabajar. Tiempo tendremos mas
adelante de ver como se crea un Workspace. Para hacer que Misesion sea ese Workspace
que crea automáticamente VB basta con ejecutar la siguiente línea de código
Pero si no queremos aprovechar este Workspace creado automáticamente por Visual Basic, y
queremos usar otro, usemos el método CreateWorkspace. Se verá al final del capítulo.
Logicamente el término Aquí .... alguna cosa va a depender de cada método y de lo que Vd.
quiera hacer, pero la estructura Set DAOInferior = DAOSuperior.Método ( - - - - - - - - - - )
se mantendrá en todas las operaciones de creación y manipulación de objetos DAO. Esta
sintaxis es tan simple que un profesor de Visual Basic decía que es como un “juego de
niños” No lo olvide y se le quitará el miedo al manejo de bases de datos mediante código.
Posiblemente hasta ahora le haya parecido muy difícil y haya optado por usar el control Data
para todas sus aplicaciones. Si recuerda este Juego de niños verá que es más sencillo crear
objetos DAO que poner un control Data en un formulario.
Objeto Database
Un objeto Database puede crearse, bien porque hemos creado una base de datos mediante el
procedimiento CreateDataBase, o porque hemos abierto una base de datos existente
mediante el procedimiento OpenDatabase. En cualquiera de los dos casos, el objeto
Database existe hasta que lo cerremos (mediante el método Close) o hasta que cerremos la
aplicación.
Al objeto Database se le debe dar un nombre definido por el usuario. Eso sí, hay que
declararlo como una variable objeto Database
El nombre que le demos al objeto DataBase no tiene nada que ver con el nombre del fichero
que alberga esa base de datos. El objeto DataBase es la base de datos que creamos en la
memoria RAM del ordenador.
podremos usar el método del Workspace CreateDatabase para crear ese objeto Database.
Recuerde que el objeto Database NO es el fichero que va a contener la base de datos sino que
es una estructura de base de datos que está de momento en la memoria RAM del ordenador.
El fichero se creará posteriormente cuando cerremos el objeto Database. El nombre del objeto
Database tampoco tiene porque coincidir con el nombre del fichero que se va a crear. En el
ejemplo que veremos mas adelante, el nombre del objeto Database es MiBaseDatos y el
nombre del fichero es MiBase.Mdb.
En nuestro caso el Objeto DAO superior es el Workspace Misesion, y los parámetros que hay
que pasar en este caso son:
Vayamos al ejemplo:
El Método CreateDataBase Crea un nuevo objeto Database, guarda la base de datos en disco
y devuelve un objeto Database abierto.
MiBaseDatos es el nombre del objeto Database por el cual nos referiremos a esa base de
datos, NO el nombre del archivo con el que quedará guardada en el disco.
Misesion es el nombre del objeto Workspace existente que contendrá la base de datos. Si se
omite este argumento, se utilizará el objeto Workspace predeterminado - Workspaces(0) – e
incluso, si no se pone nada, usará ese Workspace predeterminado.
escenario es una expresión de cadena utilizada para especificar dos cosas: el orden alfabético
que se va a usar en esta base de datos, (Obligatorio) que denominaremos inf_local y el
Password o palabra clave que quiere usar para restringir su uso. Este Password es opcional.
Debe especificar el argumento inf_local o se producirá un error. Consulte la tabla de
constantes para inf_local incluida más adelante en este tema.
dbLangGeneral
Si desea introducir una palabra clave para restringir el acceso a la base de datos, debe
indicarlo a continuación. Deberá concatenarlo con la inf_local mediante el signo & y
separarlo mediante punto y coma. Hay que poner pwd antes de la contraseña
Constante Descripción
(Advertencia sobre las bases de datos codificadas. No piense que al codificar la base de
datos va a mantener sus datos confidenciales en secreto. Access se los guardará codificados,
pero se los va a presentar de forma clara. Utilice para ello el Password, ya que no podrá abrir
la base si no se conoce ese Password. Pero tampoco se fíe mucho. Existen infinidad de
craqueadores de contraseñas de Access. El Password vale para proteger la BD de usuarios no
“piratas”. No emplee este procedimiento en aplicaciones en las que necesite verdadera
confidencialidad.
El Objeto TableDef es una tabla de una base de datos ACCESS. El Objeto TableDefs es la
colección que contiene todas las tablas de la base de datos
Un objeto Field representa un campo dentro de una tabla Access. La colección Fields contiene
todos los campos de una tabla. Verá mas adelante que también hay objetos Field en otros
objetos DAO (Index, QueryDef, Recordset, Relation), pero de momento vamos a fijarnos
solamente en las tablas.
Declaremos ahora el nombre que quiere ponerle al objeto Tabledef que va a crear: (MiTabla1)
Si acude a la información de VB para ver los parámetros que hay que pasar en el método
CreateTableDef verá que son muchos: ([nombre[, atributos[, origen[, conexión]]]]) Se explicará
para que sirven todos ellos, pero de momento nos quedamos únicamente con el primero:
Nombre, que es el nombre que podrá ver en Access como nombre de la tabla que va a crear.
De momento solamente hemos creado uno o varios objetos Tabledef. Pero como siempre,
vacíos. Una tabla tiene campos. Un objeto Tabledef tiene Fields. Debemos crear objetos Field
(Campos) para poder meterlos en los objetos Tabledef que acabamos de crear.
El objeto Field debe crearlo el objeto DAO superior a él: el Tabledef. Previamente
declararemos los nombres de los Objetos Field a introducir.
Ejemplos
Set MiCampo11 = Mitabla1.CreateField (“ID_Alumno”, dbText, 8)
Set MiCampo12 = Mitabla1.CreateField (“NombreAlumno”, dbText, 20)
Set MiCampo13 = Mitabla1.CreateField (“Apellidos”, dbText, 25)
Set MiCampo14 = Mitabla1.CreateField (“Edad”, dbInteger)
Set MiCampo15 = Mitabla1.CreateField (“Fecha_Ingreso”, dbDate)
Se crean todos los campos que se quieren introducir en las tablas. Observe que cada objeto
Field debe ser creado por el objeto Tabledef que lo va a contener. (MiTabla1 crea todos sus
campos, MiTabla2 los suyos, etc)
Ya están todos los campos creados, pero todavía no están metidos en las tablas. Tenemos
que añadirlos a la colección de campos de la tabla que los creó. Esa colección de campos es el
Objeto Fields de la tabla. Se añade mediante el método Append.
MiTabla1.Fields.Append Micampo11
MiTabla1.Fields.Append Micampo12
MiTabla1.Fields.Append Micampo13
MiTabla1.Fields.Append Micampo14
MiTabla1.Fields.Append Micampo15
MiTabla2.Fields.Append Micampo21
Ya tenemos campos formando parte de la colección Fields de las tablas. Ahora debemos
añadir las tablas a la colección de tablas de la base de datos. Esa colección de tablas es el
objeto Tabledefs de la base de datos, es decir, del objeto DataBase. Lo haremos también
mediante el método Append
MiBaseDatos.TableDefs.Append Mitabla1
MiBaseDatos.Tabledefs.Append Mitabla2
MiBaseDatos.Close
Ya tenemos la base de datos creada en el disco de la misma forma que lo hubiera hecho
Access. Pero puede que le falte algo respecto a una base creada directamente con
Access: los Indices y las Relaciones.
Un índice es una marca que le podemos poner a cada uno de los registros en un campo. Esa
marca puede servir por ejemplo, para ordenar los registros de la BD por ese campo. Puede
servir también para evitar que dos registros tengan el mismo valor para un determinado campo.
En el ejemplo que estamos preparando, el Campo11 (ID_Alumno) queremos que sea un índice,
y además que no se pueda repetir el mismo valor para dos registros distintos, de forma que no
puedan existir dos registros con el mismo valor en ese campo.
Una Relación es una correspondencia entre un campo de una tabla y otro campo de
características similares en otra tabla. Esto nos lleva al concepto de Base de Datos Relacional
que seguramente ya sabe de que se trata. En Access se puede establecer una relación de una
forma muy sencilla. En Visual Basic también. Pero antes de seguir reflexionemos y recordemos
lo que hemos hecho hasta ahora.
Observe que, cada vez que creamos un objetos (DataBase, Tabledef, Field) usamos el
mencionado “juego de niños”. El método correspondiente para crear un objeto DAO pertenece
al objeto DAO inmediatamente superior en jerarquía, es decir, CreateDatabase es un método
del objeto Workspace, CreateTableDef es un método del objeto Database, CreateField es un
método del objetos TableDef.
Hemos visto que después de crear un objeto, debemos añadirlo a la colección a la que debe
pertenecer con el método Append. El procedimiento es siempre el mismo :
Sigamos ahora perfeccionando la base de datos. Vamos a ver como se crea un Indice.
Primer debemos crear el Objeto Index. Se hace mediante el método del objeto Tabledef,
CreateIndex. El índice debe crearlo el Tabledef al que pertenece el campo que queremos que
sea índice. La sintaxis de CreateIndex es:
Donde NombreIndice es el nombre de una variable declarada como tipo de dato objeto Index.
NombreTabledef es el nombre de variable del objeto TableDef que se desea usar para crear el
nuevo objeto Index.
Nombre es una variable de tipo String que da un nombre único al nuevo objeto Index. Este
nombre lo puede ver si abre la base de datos con Access y en la vista de Diseño de la tabla,
abre la función Ver | Indices de la barra de menú.
En nuestro ejemplo:
Creamos el índice
Ya tenemos creado el Objeto Index. Ahora, (y aquí empieza la incongruencia citada) este
objeto Index debe crear el campo que queremos que sea índice. Pero ese campo ya debe
existir en el objeto Tabledef con el que hemos creado el índice (en este caso, en MiTabla1)
El nombre del campo, tipo y tamaño deben coincidir con los datos que sirvieron para
crear el campo en el objeto Tabledef.
Ya tenemos el campo o los campos creados por el índice. Ahora debemos añadirlo (o
añadirlos) a la colección Fields del objeto Index recién creado. Lo hacemos mediante el
método Append
MiIndice.Fields.Append MiCampo11
MiIndice.Fields.Append MiCampo11
MiIndice.Fields.Append MiCampo12
No crea que ya hemos terminado. Un objeto Index tiene propiedades. Una de ellas ya la hemos
visto sin querer, la propiedad Name (nombre del índice, en nuestro ejemplo, Indice1). Otras
propiedades son:
(Existen además las propiedades Clustered, Foreign, DistinctCount que no se explican para
no complicar mas el tema. Cuando los necesite no tendrá inconveniente en estudiarlos
partiendo de la ayuda de VB)
Para introducir el valor de una de estas propiedades se procede con la siguiente sintaxis:
MiIndice.Uniuqe = True
MiIndice.Primary = True
Ya está creado el índice y tiene ya metido uno o mas campos y todas sus propiedades. Ahora
debemos añadir ese índice a la colección Indexes del Objeto Tabledef.
MiTabla1.Indexes.Append MiIndice
Lo confieso. Cada vez que hago esto en la vida real tengo que volver a leer este procedimiento
en la Guía del Estudiante. Es complicado, pero alguna vez se terminará aprendiendo.
Ya tenemos la base de datos completamente creada. Sin embargo alguien dirá que le falta
algo: Relacionar dos tablas
Una Relación es una asociación establecida entre dos campos del mismo tipo ubicados en dos
tablas distintas. Se pueden establecer relaciones uno a uno ó uno a varios. Para relacionar un
Para crear una relación, usaremos un nuevo objeto DAO : El objeto Relation. Este objeto
forma parte de una colección, que es a su ves otro objeto DAO : el objeto Relations.
Para crear una relación usaremos el Método CreateRelation, que es un método del objeto
Database. (Lógico, una relación se establece entre dos tablas. Por lo tanto, la relación debe
pertenecer al objeto DAO superior jerárquicamente a las tablas: el Objeto Database. Como
para cualquier otro objeto DAO, es necesario declararlo :
Una relación se hace entre dos campos. Una relación debe tener campos. Por lo tanto,
deberemos hacer una cosa similar a la que hacíamos para el método CreateIndex, crear los
campos mediante el Objeto Relation que acabamos de crear. Pero esos campos ya deben
estar creados en las tablas que se van a relacionar.
Supongamos que queremos crear una relación en la base de datos creada en el ejemplo
anterior, y queremos relacionar el campo Campo11 que está en MiTabla1 y que lo habíamos
hecho clave primaria, con el campo Campo21 de MiTabla2. MiTabla1 y MiTabla2 son los
nombres reales de las tablas, NO los nombres de los objetos Tabledef.
El ejemplo que trae la ayuda de VB puede ser muy aclaratorio, pero le advertimos lo mismo
que para los índices, paciencia. Una vez creada la relación, podrá comprobarlo visualizándola
con el visor de relaciones del Access
Creamos el objeto Relation, que tendrá por nombre RelacionUno, pero este nombre NO debe
confundirse con el nombre del objeto DAO Relation, que es MiRelacion
MiRelación.Attributes = dbRelationUpdateCascade
Le decimos cual es el nombre del campo de la tabla principal que vamos a relacionar, mediante
el método CreateField. Deberemos declarar el nombre del objeto Field que vamos a crear para
la relación
MiCampo.ForeignName = "Campo21"
MiRelación.Fields.Append MiCampo
Y ahora añadimos el objeto Relation recién creado a la colección Relations del objeto Database
MiBaseDatos.Relations.Append MiRelación
Solamente nos falta ver que valores puede tener la propiedad Attributes del objeto Relation
Ahora ya casi podemos decir que tenemos la base de datos creada. Puede que sea así o que
le falte alguna cosa. Puede faltarle una o varias consultas. Las consultas también se pueden
crear mediante objetos DAO. Precisamente con un objeto QueryDef
Antes de utilizar el método CreateQueryDef debe declarar el nombre de los objetos a crear,
declarándolos como Variables Objeto tipo QueryDef . El ámbito es igual que para cualquier
variable:
Ahora podemos utilizar el método CreateQueryDef para crear el nuevo objeto QueryDef en la
base de datos.
Donde
MiConsulta1 es una variable del tipo QueryDef que previamente se ha declarado como tal.
Será el nombre por el que llamemos al Objeto QueryDef en el código de nuestra aplicación.
MiBaseDatos es el nombre del objeto Database abierto en el que vamos a introducir el nuevo
objeto QueryDef.
Nombre es una expresión de cadena que representa el nombre de la nueva consulta que
vamos a crear. Este nombre será el que veamos al abrir la base de datos con Access en la
pestaña Consultas. Puede omitirlo a la hora de crear la consulta, pero deberá añadirselo
posteriormente.
Texto_sql es una expresión de cadena (instrucción SQL válida) que define el objeto
QueryDef.
Lógicamente una consulta nos debe suministrar una serie de datos de una o mas tablas. Esos
datos no tienen porqué ser todos los datos de las tablas. Texto_sql es precisamente el filtro de
esos datos (expresado mediante una cláusula SQL).
MiBaseDatos.QueryDefs.Append MiConsulta1
Ahora ya tenemos la base de datos creada con todas las posibilidades. Ha llegado el momento
de crear una base real para comprobar todo lo expuesto.
Otra tabla necesaria será la tabla Peliculas, donde introduciremos todos los datos relativos a
las películas (existentes o no en el videoclub), tal como título, director, artistas, resumen,
calificación, precio, etc. Tendrá un campo, ID_Pelicula que identificará a esa película. Pero
puede haber versiones en varios idiomas, por lo tanto, existirá otro campo Idioma de tipo texto,
para introducir ese dato. Por lo tanto, una película deberemos definirla por el conjunto formado
por su ID_Pelicula y por su Idioma. El conjunto de esos dos campos será la clave primaria.
(Si cree que hay campos que no tienen sentido en esta tabla (Idioma), piense que esto es un
ejemplo para poder explicar de la forma más didáctica todas las posibles variaciones de una
instrucción)
Existe otra tabla denominada Cintas, donde figurarán todas las cintas existentes en el
Existirá una tercera tabla, Alquileres, que relacionará al cliente con la cinta que ha
alquilado. Tendrá un campo llamado ID_Cliente y otro ID_Cinta. Aparte tendrá otros dos
campos, fecha de alquiler y fecha de devolución.
La base de datos deberá tener dos relaciones, una, entre el campo ID_Cliente de la tabla
Clientes y el campo ID_Clientes de la tabla alquileres (Será uno a infinito) y otra
relación, entre el campo ID_Cinta de la tabla Cintas y el campo ID_Cinta de la tabla
Alquileres (Relación 1 a 1 ya que solamente existe una cinta con esa ID_Cinta). Para
darle más alegría al ejercicio le pondremos una relación entre los campos ID_Pelicula e
Idioma de las tablas Peliculas y Cintas.
(Se ha puesto el nombre de Pelicula al campo relacionado con ID_Pelicula para que se
vea que dos campos relacionados no tienen porqué tener el mismo nombre. Eso sí,
deben tener las mismas características)
Como colofón a todo esto, crearemos una consulta en la que utilizaremos todas las
relaciones.
Para llevar a cabo este ejercicio se ha partido de una interface gráfica en la que pueden
verse tres botones (Borrar la base de datos, crearla y salir) y un TextBox donde se ha
puesto el nombre del fichero de la base de datos en su propiedad Text. Veamos el
código de cada uno de los botones (por orden inverso de complejidad del código)
Fig. 20.3 Interface gráfica de la parte de crear bases de datos para la aplicación del
Videoclub
CODIGO
‘Se crea el Objeto DataBase (Se toma el nombre del fichero del TextBox TBNombreBase
Set MiBaseDatos = Workspaces(0).CreateDatabase(TBNombreBase, dbLangGeneral)
‘Una vez creados los campos se les ponen las peopiedades que se estime oportuno
‘En este caso, se ha puesto la propiedad AllowZeroLength (Permitir valores nulos en
ese ‘campo) a lo que interesa en cada uno de los campos. Nota Tenga presente que por
defecto le ‘va a dejar el campo que NO permite valores nulos, circunstancia que le va a
crear problemas.
MiCampo12.AllowZeroLength = True
MiCampo13.AllowZeroLength = False
MiCampo14.AllowZeroLength = True
MiCampo15.AllowZeroLength = True
MiTabla2.Fields.Append MiCampo21
MiTabla2.Fields.Append MiCampo22
MiTabla2.Fields.Append MiCampo23
MiTabla2.Fields.Append MiCampo24
MiTabla2.Fields.Append MiCampo25
MiTabla3.Fields.Append MiCampo31
MiTabla3.Fields.Append MiCampo32
MiTabla3.Fields.Append MiCampo33
MiTabla3.Fields.Append MiCampo34
MiTabla3.Fields.Append MiCampo35
MiTabla3.Fields.Append MiCampo36
MiTabla4.Fields.Append MiCampo41
MiTabla4.Fields.Append MiCampo42
MiTabla4.Fields.Append MiCampo43
MiTabla4.Fields.Append MiCampo44
‘Se declaran las variables tipo objeto Index y tipo objeto Field para crear los índices
Dim MiIndice1 As Index, MiIndice2 As Index, MiIndice3 As Index
Dim CampoIndiceA As Field
Dim CampoIndiceB As Field
‘Se declaran los objetos Relation y un par de objetos Field para crear las relaciones. Por
‘claridad se han declarado objetos Field distintos para la creación de los índices y de las
‘relaciones, pero podrían haber sido los mismos
Dim MiRelacion1 As Relation, MiRelacion2 As Relation, MiRelacion3 As Relation
Dim CampoRelacionA As Field
Dim CampoRelacionB As Field
‘Se crea la primera relación entre el campo ID_Clientes de la tabla Clientes (Tabla ‘primaria) y
el campo ID_Cliente de la tabla Alquileres (Tabla relacionada)
Set MiRelacion1 = MiBaseDatos.CreateRelation("RelClientes", "Clientes", "Alquileres")
Set CampoRelacionA = MiRelacion1.CreateField("ID_Cliente", dbText, 8)
CampoRelacionA.ForeignName = "ID_Cliente"
MiRelacion1.Fields.Append CampoRelacionA
MiBaseDatos.Relations.Append MiRelacion1
‘Se comienza a crear una consulta (La sentencia SQL está cortada dado que no cabe en ‘una
End Sub
El resultado de todo esto podemos verlo si abrimos la base de datos con Access
Fig. 20.6 Clave Primaria formada con los dos campos de la tabla Películas
Set MiBaseDatos =
= Workspaces(0).CreateDatabase(TBNombreBase, dbLangGeneral & ";pwd=LSB")
Cuando lleguemos a la parte de abrir bases de datos, explicaremos cómo se abre una
base de datos con contraseña. Y no crea que ha conseguido la confidencialidad total de
sus datos. Existen programas que puede bajarse de Internet que le leen la contraseña
con la que ha protegido su base. No es una protección total, pero sí suficiente para que
un usuario “normal” no se la pueda abrir.
Anexo1
Ha visto mas atrás que puede ser necesario cambiar las propiedades de los campos una
vez creados (Por ejemplo, MiCampo13.AllowZeroLength = False) Alargaríamos
demasiado este ya largo capítulo si se explican todos los las propiedades que puede
tener un campo. Añada un poco de esfuerzo a su estudio y vea las propiedades de los
objetos Field en la ayuda. Le reseño aquí las que he considerado mas importantes
DefaultValue Es el valor que le pone a ese campo si no introduce ninguno. Puede indicar un
valor a la hora de crear el campo:
Campo14.DefaultValue = “Madrid”
Required Si/No Indica si el dato es requerido. En caso de que tome el valor SI (True) es
necesario introducir un dato en ese campo
Value Es justamente el dato que almacena en ese campo. Es la propiedad por defecto del
objeto Field.
En el método CreateField debe introducir el tipo del campo que desea crear. (Como
puede ver mas atrás, con esta sintaxis p.e.: CreateField("Fecha_Alq", dbDate) Ese tipo
coincide con la propiedad Type aplicada a los campos. La propiedad Size solamente
tendrá que aplicarla cuando vaya a crear un campo tipo Texto.
Puede ver la ayuda de VB para mas detalles. Le enumero los mas usuales
Propiedad Type
Propiedad Attributes
Constante Descripción
Propiedad Size
Devuelve o establece un valor que indica el tamaño máximo, en bytes, de un objeto Field que
contiene texto o el tamaño fijo de un objeto Field que contiene texto o valores numéricos
Variable = Micampo11.Size
Para abrir una base de datos existente deberemos usar el método OpenDatabase. Pero
previamente deberemos declarar el nombre que se le va a dar a ese objeto Database mediante
la instrucción Dim si queremos que el ámbito de ese Database sea un formulario, o Global o
Public, (en la sección de declaraciones de un Módulo o Formulario) si queremos que el ámbito
sea toda la aplicación.
Por ejemplo, si queremos abrir una base de datos y poder referirnos a ella en toda la
aplicación, debemos declararla de esta forma en la sección de declaraciones de un módulo :
MiBaseDatos Variable de tipo de dato objeto Database que representa el objeto DAO
Database que se va a abrir.
Misesion Variable de tipo de dato objeto Workspace que representa el objeto
Workspace existente que va a contener a la base de datos.
nombre_bd Expresión de cadena con el nombre de un archivo (y su Path) de una base de
datos existente. Si el nombre de archivo tiene extensión, es necesario
especificarla. Si la red lo admite, también puede especificar una ruta de red,
como por ejemplo "\\MISERVID\MICOMP\MIDIR\MIBD.MDB". nombre_bd
también puede ser un origen de datos OBDC. Lo veremos en otro capítulo.
Si se refiere a una base de datos ya abierta por otro usuario con acceso
exclusivo, se producirá un error.
exclusivo Valor de tipo Boolean que es True si la base de datos se va a abrir con acceso
exclusivo (no compartido) o False si se va a abrir con acceso compartido. Si se
omite este argumento, la base se abrirá con acceso compartido.
sólo_lectura Valor de tipo Boolean que es True si la base de datos se va a abrir con acceso
de sólo lectura o False si se va a abrir con acceso de lectura/escritura. Si se
omite este argumento, la base se abrirá para lectura/escritura.
origen Expresión de cadena utilizada para abrir la base de datos. Esta cadena
constituye los argumentos de conexión ODBC. Para especificar una cadena de
origen deberá especificar también los argumentos exclusivo y sólo_lectura.
Consulte la sintaxis en la propiedad Connect.
Nota para todo este capítulo. No es necesario cambiar el nombre del Workspace. Si
Misesion = Workspaces (0), la sentencia anterior podemos ponerla también :
Ya tenemos la base de datos abierta. Pero no crea que nuestro programa ha hecho trabajo. Se
ha limitado a ver si existía el fichero indicado y a “apuntar” el nombre y path de ese fichero que
contiene la base de datos. El trabajo comienza cuando cree el recordset.
También hay una colección Recordsets. La colección Recordsets contiene todos los objetos
Recordset abiertos de un objeto Database.
Al utilizar objetos de acceso a datos, casi toda la interacción con los datos se produce a través
de objetos Recordset. Todos los objetos Recordset están formados por registros (filas) y
campos (columnas). Existen tres tipos de objetos Recordset:
Recordset de tipo tabla: Representación en código de una tabla base de datos que puede
utilizarse para agregar, modificar o eliminar registros de una sola tabla de base de datos. Un
Recordset tipo Tabla contiene todos los campos de una tabla y no puede contener campos que
no pertenezcan a esa tabla.
Recordset de tipo hoja de respuestas dinámica: Resultado de una consulta que puede
tener registros actualizables. Un Recordset de tipo hoja de respuestas dinámica es un conjunto
dinámico de registros que puede utilizarse para agregar, modificar o eliminar registros de una o
más tablas de una base de datos subyacente. Este tipo de objeto Recordset puede contener
campos de una o más tablas de una base de datos.
Como cualquier objeto DAO, debemos declararlo como variable tipo objeto.
Origen en la primera expresión es una variable de tipo String que especifica el origen de los
registros del nuevo objeto Recordset. El origen puede ser un nombre de tabla, un nombre de
consulta o una instrucción SQL que devuelva registros. En el caso de los objetos Recordset de
tipo tabla, el origen sólo puede ser un nombre de tabla.
El parámetro opciones permite especificar las características del nuevo objeto Recordset tales
como las restricciones de edición y consulta para otros usuarios. Vea la Ayuda de VB para
mayor detalle.
Si tenemos abierta una base de datos llamada MiBaseDatos, podemos crear el objeto
MiRecordset eligiendo de la tabla MiTabla de esa base de datos los campos Campo1, Campo2
y Campo3, y que sea del tipo de hoja de respuestas dinámica, de la siguiente forma :
Si deseamos que el Recordset contenga todos los campos de esa misma tabla :
Veamos lo que decíamos antes. Crear un Recordset desde otro recordset. Se puede usar
solamente para variar sus propiedades. Si desde el Recordset anterior, queremos crear un
nuevo Recordset denominado MiRecordset1, que tenga la condición de que sea solo lectura,
usaremos la sentencia :
Este nuevo Recordset contendrá los mismos campos que el Recordset origen, pero no
podremos cambiar datos en él.
Pero en la mayoría de los casos, necesitaremos crear un Recordset donde se elijan varios
campos de una o varias tablas, seleccionando de esos campos unos determinados valores.
Por ejemplo, en una base con las direcciones de los clientes, a lo mejor queremos seleccionar
todas aquellas direcciones en las cuales el código postal sea el 28700 (San Sebastián de los
Reyes). Imaginemos que hemos abierto la base de datos con el nombre CLIENTES (Recuerde
que este es el nombre del objeto DAO usado para abrir la B.D., no el nombre que pueda tener
esa B.D. en el disco), y esta base de datos tiene una tabla llamada DIRECCIONES (Este sí es
el nombre real de la tabla dentro de la B.D.) Vamos a abrir un Recordset con todos los clientes
de San Sebastián de los Reyes :
El Recordset Mirecordset2 contiene todos los campos de todos los registros de la tabla
DIRECCIONES que cumplan la condición de que el código postal (campo COD_POSTAL en el
ejemplo) sea igual a 28700.
Observe en esta y anteriores expresiones, que la sentencia SQL está entre doble comilla.
Podemos introducir cualquier sentencia SQL para determinar qué registros introducimos en el
Recordset. Por ejemplo, si queremos seleccionar todos los clientes de Madrid (su código postal
comenzará necesariamente por 28 y le seguirán tres cifras) :
A la hora de crear un recordset podemos pensar que tipo es el más adecuado. Todo
dependerá de lo que necesitemos de nuestro recordset y de cómo nos queramos mover por él.
Cuando decimos movernos por él queremos decir cambiar de un registro a otro, buscar
registros, etc.
Vamos a prescindir del recordset tipo Snapshot si lo que queremos es leer y escribir datos. Un
recordset Snapshot solamente sirve para realizar informes (leer) de los datos en un instante
determinado. Veamos la elección entre Dynaset y Table
Si queremos seleccionar parte de los registros de una tabla, o ver registros de varias tablas al
mismo tiempo (lo que podemos ver en Access en una consulta), debemos elegir directamente
el tipo Dynaset, ya que el tipo Table debe contener TODOS los registros de una UNICA
TABLA.
Si estamos en ese caso es el único en el que tendremos dudas respecto al tipo elegido.
Si creamos un recordset tipo Table se nos puede complicar un poco el código a la hora de
movernos a lo largo del recordset, ya que no podemos usar ciertos métodos como los Find
(FindFirst, FindLast, etc.) debiendo utilizar para realizar la misma función los métodos Move
(MoveFirst, MoveLast, MovePrevious, MoveNext), o el método Seek, un poco más complicado
y que exige un índice. (Lo que ganamos con la complicación del Seek es velocidad). Los
desplazamientos a lo largo de un recordset tipo tabla son mucho más rápidos que sobre un
recordset tipo Dynaset. Esa rapidez se nota fundamentalmente cuando va a manejar miles de
registros, en cuyo caso es indispensable usar recordsets tipo tabla y moverse sobre un índice.
Si no va a manejar miles de registros, no apreciará mucha diferencia entre uno y otro. Y si usa
Dynaset dispone de más recursos, sobre todo de búsqueda. Veremos casos de uno y otro tipo.
Sintaxis NombredeMiRecordset.RecordCount
(NombredeMiRecordset puede ser del tipo Dynaset, Snapshot Table. Pero en el caso de que
sea Dynaset se va a encontrar con una sorpresa. Si le pide ese dato a un recordset tipo
Dynaset recién creado, le devolverá el valor 1. ¡Parece que solamente tiene un registro! No es
así. Un recordset tipo Dynaset tiene la ventaja (y el inconveniente) de que solamente guarda en
memoria un registro. Por lo tanto no sabe cuantos registros tiene realmente hasta que no los
recorre todos. Para que el método RecordCount le devuelva el número de registros
existentes, tiene que acceder previamente al primero y al último. Para ello basta con ejecutar
estas dos líneas de código:
NombredeMiRecordset.MoveLast
NombredeMiRecordset.MoveFirst
Método AddNew
Sintaxis MiRecordset.AddNew
El método AddNew crea un nuevo registro donde puede introducir nuevos datos, y
posteriormente agregarlo al conjunto de registros del objeto Recordset. Este método establece
en los campos el valor Null (predeterminado para los objetos Recordset de tipo tabla) o los
valores predeterminados, si existen. El registro creado queda en la memoria, y ahí se puede
modificar simplemente asignando a cada campo el valor deseado. Para asignar un valor a un
campo simplemente tenemos que poner la expresión :
Una vez que se hayan introducido los datos en el nuevo registro, debe utilizar el método
Update para guardar los cambios y agregarlo al conjunto de registros. No se modificará la base
de datos hasta que se utilice el método Update.
El registro que era actual antes de utilizar el método AddNew continúa siéndolo después. Esto
puede comprobarlo asignando a un Label el contenido de un campo, añadir un registro con un
valor para ese campo distinto al que está presente en el Label y comprobar que el contenido
del Label no se ve afectado por haber introducido un registro nuevo. Si desea hacer que el
nuevo registro sea el actual, puede establecer en la propiedad Bookmark el marcador
identificado por el valor de la propiedad LastModified. En la práctica anterior observará tras
este proceso que se cambia el contenido del Label al nuevo valor.
Método Edit
Copia el registro actual de un objeto Recordset de tipo hoja de respuestas dinámica o tabla en
el búfer de copia para su edición.
Sintaxis MiRecordset.Edit
Una vez invocado el método Edit, los cambios efectuados en los campos del registro actual se
copian en el búfer de copia. Al terminar de realizar los cambios deseados, utilice el método
Update para guardarlos. Como en el caso del método AddNew este registro modificado está
en la memoria y es necesario introducirlo en la BD.
El registro actual después de utilizar Edit es precisamente el registro que acabamos de editar.
Para poder usar Edit debe existir un registro actual. Si no es así o si MiRecordset no se refiere
a un objeto Recordset de tipo tabla u hoja de respuestas dinámica, a un objeto Table o a un
objeto Dynaset abierto, se producirá un error.
Una vez añadido el registro, o cambiados los datos de un registro, debemos utilizar el Método
Update para guardar los datos en la BD.
Método Update
Guarda el contenido del búfer de copia en un objeto Recordset de tipo hoja de respuestas
dinámica o tabla especificado.
Es decir, mete en la Base de Datos el contenido del registro que estaba en la memoria, bien
por haber utilizado el método Update, bien por haber utilizado el método Edit.
Sintaxis MiRecordset.Update
Uso del método Edit o AddNew y desplazamiento a otro registro sin utilizar antes Update.
Uso de Edit o AddNew y utilización de nuevo de Edit o AddNew sin especificar antes Update.
Establecimiento de otro registro en la propiedad Bookmark.
Cierre del conjunto de registros indicado en MiRecordset sin utilizar antes Update.
Cada vez que se crea o edita un registro se cambia el valor de la propiedad LastModified, que
tomará el marcador de ese registro creado o editado.
Método CancelUpdate
Sintaxis recordset.CancelUpdateTipo
Método Delete
Este método elimina el registro actual de un objeto Recordset de tipo hoja de respuestas
dinámica o tabla. Para eliminar un registro, debe haber un registro actual en el Recordset antes
de utilizar Delete, pues de lo contrario se producirá un error interceptable. Una vez eliminado,
este registro sigue siendo el registro actual. Puede observar, que si a continuación de Delete
utiliza AbsolutePosition para conocer en que registro está, la respuesta será -1, prueba de que
está sobre un registro inexistente.
Propiedad Bookmark
Devuelve o establece un marcador que identifica de forma única el registro actual de un objeto
Recordset o define el registro actual de un Recordset como marcador válido.
Esto merece una pequeña aclaración. Bookmark en inglés significa ese papel que introducimos
en un libro para saber en qué página hemos dejado la lectura. En Visual Basic, significa el
registro en el que estamos actualmente (registro actual) Podemos conocer en que registro
estamos mediante la siguiente expresión:
Variable = MiRecordset.Bookmark
Pero tenemos que tener en cuenta que Variable es una variable tipo String (Sí, string, aunque
parezca que para conocer la posición de un registro debería ser un numérico, pero es así). Por
lo tanto deberíamos haber declarado la variable previamente como una variable tipo String
Pero esta propiedad sirve para colocarnos en el registro que deseemos. Eso sí, previamente
deberíamos haber obtenido el Bookmark de ese registro. Imagínese que se está moviendo a lo
largo del recordset y hemos visto un registro donde tenemos un dato importante (por ejemplo,
un máximo del valor de un campo) No sabemos si habrá otro registro que tenga un valor mayor
que este. Deberemos seguir buscando, pero antes anotamos el Bookmark de ese registro
Variable = MiRecordset.Bookmark
Seguimos buscando moviéndonos por todo el recordset, y comprobamos que no hay otro
registro con un valor mayor. Queremos volver a aquel registro. Para ello forzamos a que el
registro cuyo Bookmark sea igual a Variable se convierta en registro actual:
MiRecordset.BookMark = Variable
La propiedad Bookmark se almacena internamente como matriz de Byte. Por esta razón, si se
intenta usar la propiedad Bookmark en una operación de comparación, se producirá un error
interceptable. Antes de tener acceso a la propiedad Bookmark, copie los valores de los
marcadores a variables cadena y efectúe las comparaciones usando dichas variables cadena.
Por ejemplo, el siguiente código compara marcadores en dos objetos Recordset:
No hay límite en el número de marcadores que pueden establecerse. Para crear un marcador
para otro registro distinto del registro actual, muévase al registro deseado y asigne el valor de
la propiedad Bookmark a una variable String que identificará el registro.
Para asegurarse de que el Recordset acepta marcadores, inspeccione el valor de su propiedad
Bookmarkable antes de usar la propiedad Bookmark. Si Bookmarkable es False, el Recordset
no acepta marcadores, y el uso de la propiedad Bookmark produce un error interceptable.
Propiedad LastModified
Sintaxis NombreRecordset.LastModified
El valor devuelto por esta propiedad es un tipo de datos Variant o String. (Similar al devuelto
por Bookmark) LastModified se puede usar para colocarse en el registro más recientemente
agregado o actualizado.
Esta propiedad puede usarse para volver al último registro que ha sido modificado. Basta para
ello igualar la propiedad Bookmark a la propiedad LastModified :
NombreRecordset.Bookmark = NombreRecordset.LastModified
Al abrir el conjunto de registros indicado en Recordset, el primer registro pasa a ser el registro
actual y en la propiedad BOF se establece False. Si el conjunto no contiene ningún registro, se
establecerá en BOF el valor True y no habrá registro actual.
No es posible utilizar los métodos MoveFirst ni MovePrevious en los Recordset tipo snapshot
de desplazamiento hacia delante.
Para desplazar la posición del registro actual en un objeto Recordset un número de registros
determinado hacia adelante o hacia atrás, utilice el método Move.
Método Move
Desplaza la posición del registro actual en un objeto Recordset.
Donde :
filas es un valor de tipo Long con signo que especifica el número de filas (de registros) que se
desplaza la posición. Si filas es mayor que 0, la posición se desplaza hacia adelante (hacia el
final del archivo). Si es menor que 0, la posición se desplaza hacia atrás (hacia el principio del
archivo).
Inicio (opcional) es un valor de tipo String que identifica un marcador. Si se especifica inicio, el
desplazamiento será relativo al marcador indicado. Si se omite, Move comenzará por el registro
actual. El marcador que debe utilizarse para definir el registro Inicio debe ser un Bookmark o
similar (LastModified, por ejemplo)
Si se especifica una posición anterior al primer registro, la posición del registro actual se situará
al principio del archivo (BOF). Si se especifica una posición posterior al último registro, la
posición del registro actual se situará al final del archivo (EOF).
Recordset.FindFirst "Nombre = 'Luis' " ' Busca un nombre. Recuerde siempre las
comillas dobles para la expresión de búsqueda
y las comillas simples si se trata de un dato
string.
Al buscar campos que contengan fechas, deberá utilizar el formato de fecha de los Estados
Unidos (mes-día-año), incluso cuando no utilice la versión para este país del motor de base de
datos Jet, pues de lo contrario es posible que no se encuentren los datos buscados. Puede
utilizar la función Format para convertir la fecha. Por ejemplo:
Observe que las fechas, aparte de ponerlas en americano, hay que presentarlas entre
almohadillas (#). Observe lo dicho mas atrás para las comillas dobles en la expresión de
búsqueda.
Método Seek
El método Seek busca el primer registro de un objeto Recordset indexado de tipo Table que
cumple el criterio especificado para el índice activo y lo convierte en el registro activo. Sólo
funciona en espacios de trabajo Microsoft Jet.
Donde MiRecordset es un recordset de tipo Table que tiene definido un índice en el campo por
el que se va a realizar la búsqueda. Como podemos tener varios índices en una tabla,
deberemos indicarle cual es el índice de búsqueda. Una vez que se lo indiquemos, ese índice
será el Indice activo.
clave1, clave2...clave13 Son uno o más valores que corresponden a los campos en el índice
activo del objeto Recordset. Puede utilizar un argumento de hasta 13 claves.
Antes de usar Seek se debe establecer el índice activo. Todo índice tiene un nombre.
Habíamos visto cuando creábamos un índice que debía tener un nombre. Recuerde el ejemplo:
Puede ver el nombre de ese índice en la Fig. 20.7 En este caso habíamos creado el índice
mediante código y hemos podido controlar su nombre. Si lo hubiésemos creado directamente
en Access, el nombre que le pone por defecto es el mismo que el nombre del campo.
Ese nombre del índice es el que debemos usar para crear el índice activo. Por ejemplo, si
quisiéramos que el índice activo fuese el IndicePeliculas lo haríamos índice activo mediante la
siguiente instrucción:
MiRecordset.Index = "IndicePelículas"
A partir de ahora, el campo (o campos) de ese índice será sobre el que realizaremos la
búsqueda mediante Seek. Para encontrar el registro que tenga por valor 00000012 usaremos
la expresión
Ese registro será ahora el registro actual. Si hubiese mas de un registro con ese valor, el
registro actual será el primero que cumpla esa condición
En el ejemplo hemos utilizado el comparador = para buscar un registro cuyo valor en el campo
indicado por el índice activo sea igual al indicado en el siguiente parámetro (00000012). Si
quisiésemos encontrar un registro cuyo valor sea superior a 00000012 usaríamos la expresión
El método Seek busca en los campos clave especificados y localiza el primer registro que
cumpla el criterio especificado por comparación y clave1. Cuando lo encuentra, convierte ese
registro en activo y la propiedad NoMatch se establece en False. Si el método Seek no
consigue localizar ninguna coincidencia, la propiedad NoMatch se establece en True y el
registro activo es indefinido.
Si comparación es igual (=),mayor o igual (>=) o mayor que (>), Seek empezará al principio del
índice y buscará hacia adelante.
Si comparación es menor que (<) o mayor o igual que (<=), Seek empezará al final del índice y
buscará hacia atrás, a menos que haya entradas de índice duplicadas al final. En tal caso,
Seek empezará en una entrada cualquiera entre las entradas duplicadas existentes al final del
índice.
Debe especificar valores para todos los campos definidos en el índice. Si utiliza Seek con un
índice de múltiples columnas y no especifica un valor de comparación para cada campo del
índice, no podrá usar el operador de igual (=) en la comparación. Esto se debe a que algunos
de los campos de criterio(clave2, clave3, etc) estarán predeterminados en Null, lo que
posiblemente no concordará. Por tanto, el operador de igual sólo funcionará correctamente si
tiene un registro que sea Null en su totalidad, excepto la clave que está buscando. Es
aconsejable usar el operador mayor que o igual en su lugar.
En el ejemplo siguiente se toman los nombres de las calles y otros datos de una tabla llamada
Calles_Nombre, cuyo campo NombreVia esta indexado. El índice tiene el mismo nombre que el
campo porque se creó directamente con Access. El procedimiento BBuscaCalle_Click busca la
primera calle cuyo nombre coincida con las letras tecleadas en el TextBox TBBuscaCalle
Método Clone
En muchas ocasiones es necesario crear un Recordset que sea copia exacta de otro. Las
ocasiones en las que es necesario hacer esto pueden ser variadas, pero vamos a destacar
una : crear un Recordset idéntico al Recordset de un control Data, para trabajarlo con código
durante una parte de la ejecución del programa. Otras aplicaciones pueden ser copiar el
Recordset de otra máquina a través de la Red de Area Local, ... y donde podamos llegar con
nuestra imaginación.
Puede utilizar el método Clone cuando desee realizar en un conjunto de registros una
operación que requiera varios registros actuales. Este método es más rápido y eficiente que
crear un nuevo Recordset.
Inicialmente, un Recordset creado con Clone carece de registro actual. Para hacer que un
registro sea el actual antes de utilizar el Recordset copia, puede utilizar cualquiera de los
métodos Move, Find o Seek (solo para Recordsets tipo Tabla), o establecer su propiedad
Bookmark
Método Requery
El método Requery actualiza los datos de un objeto Recordset, volviendo a ejecutar la consulta
con la que se ha creado ese Recordset. Este método debe usarse cada vez que se sospeche
que los datos de la Base de datos han cambiado, y se quieran presentar los datos
actualizados. Es un método típico de una BD que se está usando desde varios puestos a
través de una Red de Area Local.
No es posible utilizar el método Requery en objetos Recordset tipo Snapshot o en los Dynaset
que tengan la propiedad Restartable a False, ni tampoco en los objetos Recordset de tipo
Tabla.
Si los valores de las propiedades BOF y EOF del objeto Recordset son ambos True después
de utilizar el método Requery, la consulta no habrá devuelto ningún registro y el objeto
Recordset no contendrá datos.
Transacciones
Veamos estos tres métodos que, dadas sus funciones, deben estudiarse conjuntamente.
Supongamos un Banco. Debe hacer una transferencia entre dos cuentas corrientes que están
en la misma base de datos. La operación es sencilla : Busca la cuenta origen y crea un nuevo
registro. Apunta en el campo OPERACIÓN una T de Transferencia, en el campo IMPORTE
apunta el valor del dinero a transferir, y en el campo SALDO pone la diferencia entre lo que
había en ese campo en la última operación, menos el importe del dinero transferido.
A continuación hace un proceso similar con la cuenta destino, pero en este caso, sumándole el
importe de la transferencia. No hay problemas. Pero que pasa si, una vez sacado el dinero de
Para evitar estas situaciones usamos lo que se denomina una Transacción, que es una
combinación de estos tres métodos. Con el método BeginTrans iniciamos la Transacción. Con
CommitTrans terminamos la transacción y se guardan los cambios realizados (En ambas
cuentas a la vez, en el caso del ejemplo). Con Rollback se termina la transacción sin llegar a
guardar los cambios, quedando el Objeto Workspace afectado por las operaciones internas a
esa transacción tal y como estaba antes de comenzar dicha operación.
Dado que una transacción pertenece a un Workspace, deberemos aplicar estos métodos al
Workspace que ese usuario tenga abierto. Es decir, en un sistema con varios usuarios que
están trabajando simultáneamente sobre una Base de Datos, un determinado usuario deberá
entrar con un Workspace propio (una sesión de trabajo solo para él). En estas condiciones
podemos crear una transacción. Y aquí comenzamos a ver la necesidad de crear Workspaces
distintos para distintos usuarios. Veremos un poco más adelante como se crean los
Workspaces.
Sintaxis MiSesión.BeginTrans
MiSesión.CommitTrans
MiSesión.Rollback
Dentro de un objeto Workspace, las transacciones son siempre globales y no se limitan sólo a
la base de datos o al conjunto de registros. Si realiza operaciones en más de una base de
datos o conjunto de registros durante una transacción en un objeto Workspace, el método
Rollback deshará todas las operaciones en todos ellos. Quiere esto decir que una transacción
debe iniciarse al comenzar una determinada operación, realizar esa operación sin realizar
ninguna otra durante ese tiempo, terminar la operación y finalizar la transacción, bien con
CommitTrans o con Rollback.
Si desea tener transacciones simultáneas, lo mas indicado es crear varios objetos Workspace
para usar uno con cada transacción.
Puede anidar transacciones. Es posible tener hasta cinco niveles de transacciones abiertos a
un tiempo en un mismo objeto Workspace utilizando múltiples combinaciones anidadas de
BeginTrans y CommitTrans o Rollback. En este caso, el orden de finalización de una
transacción debe ser siempre de menor a mayor nivel jerárquico, es decir, se deberá cerrar
primero la transacción que esté mas interior dentro del anidamiento, y así sucesivamente. Si
cierra una transacción anidada mediante CommitTrans, y posteriormente cierra una
transacción que abarque a esta última con Rollback, los cambios de la primera transacción NO
quedarán guardados.
(Cuando se utilizan bases de datos SQL ODBC externas no es posible anidar las
transacciones).
Si cierra un objeto Workspace sin guardar o deshacer las transacciones pendientes, éstas se
desharán automáticamente.
Algunas bases de datos pueden no admitir las transacciones. En este caso la propiedad
Transactions del objeto Database o Recordset tendrá el valor False. Lea detenidamente la
Ayuda de estos métodos antes de trabajar con ellos.
El hecho de usar transacciones, aparte de lo que significa para asegurar la integridad de los
datos, ahorra accesos al disco (Importantísimo en algunas redes LAN y WAN), ya que los
cambios a introducir se van almacenando en un búfer en la memoria, y se vuelcan al disco
solamente en el momento de terminar la transacción de modo afirmativo con CommitTrans.
Acabamos de ver que es necesario poder crear Workspaces para cada usuario. Hasta ahora, y
para no complicar el estudio de las bases de datos, habíamos usado solamente el
Workspaces(0) que VB crea automáticamente. Vamos a entrar ahora en la creación de
Usuarios (Users) y Workspaces. El método para crear Workspaces es el CreateWorkspace,
método del dbEngine. Comencemos con una mirada a este objeto desde el punto de vista de
DAO. No olvide que el dbEngine es el motor de bases de datos Jet.
Tiene como cualquier objeto DAO sus propiedades y métodos. Vamos a ver alguna propiedad,
en orden ascendente de importancia práctica.
Propiedad Version
Devuelve la versión actual de DAO en uso. El tipo de datos es String (p.e., "3.5" )
Propiedad DefaultType
Establece o devuelve un valor que indica qué tipo de espacio de trabajo (Microsoft Jet u
ODBCDirect) utilizará el próximo objeto Workspace que se cree. Puede tomar los valores:
dbUseJet Crea objetos Workspace conectados al motor de base de datos Microsoft Jet.
dbUseODBC Crea objetos Workspace conectados a un origen de datos ODBC.
El DefaultUser es un tipo de datos String, que puede tener entre 1 y 20 caracteres de longitud
en espacios de trabajo Microsoft Jet y cualquier longitud en espacios de trabajo ODBCDirect.
El DefaultPassword es un tipo de datos String que puede tener hasta 14 caracteres de longitud
en bases de datos Microsoft Jet y cualquier longitud en conexiones ODBCDirect. Puede
contener cualquier carácter excepto 0 ASCII.
Para que estas propiedades tenga efecto, debe establecerla antes de llamar a cualquier
método DAO.
Donde User es la variable tipo objeto User que desea crear. Debe declararse como User)
Objeto El nombre del Workspace (o del objeto objeto Group) que quiere utilizar para crear el
nuevo objeto User.
Nombre Nombre del nuevo usuario
PID. Identificador de Usuario. Debe contener de 4 a 20 caracteres alfanuméricos
Contraseñal. Contraseña para el nuevo usuario. La contraseña puede tener hasta 14
caracteres de longitud y puede incluir cualquier carácter excepto el carácter ASCII 0
Esto merece algún comentario. El nuevo usuario debe crearlo un Workspace (o un Group, pero
de eso no nos vamos a ocupar por ahora) Lo normal es crear el nuevo usuario con el
Workspaces(0) que como sabe, lo crea automáticamente Visual Basic.
Recuerde que antes de crear un nuevo usuario, debe indicar donde está la base de datos con
la información del systema, mediante la propiedad SystemDB
DBEngine.SystemDB = "C:\Windows\System\System.Mdw")
ListUsers.Clear
For I = 0 To Workspaces(0).Users.Count - 1
NomUsuario = Workspaces(0).Users(I).Name
ListUsers.AddItem NomUsuario
Next I
Creación de nuevos Workspaces. Método CreateWorkspace
La sintaxis es la siguiente:
Veamos un ejemplo de cómo crear un Workspace para el usuario Luis creado anteriormente:
Podemos ver todos los Workspaces existentes. En las siguientes instrucciones podemos ver el
código para listarlos en ListWS
ListWS.Clear
For I = 0 To DBEngine.Workspaces.Count - 1
NomWS = DBEngine.Workspaces(I).Name
ListWS.AddItem NomWS
Next I
Debe tenerse en cuenta que el objeto Workspace es un objeto que solamente existe
mientras está ejecutándose la aplicación. Cuando salimos de la aplicación, ese
Workspace desaparece. No ocurre lo mismo con el Usuario, que queda en la base de
datos del sistema, es decir, es un objeto persistente.
Las partes de código expuestas se han sacado de un ejemplo creado para ver el número y
nombre de los usuarios existentes, Workspaces y DataBases. Es un ejemplo para explicar
estos conceptos. No tiene otra finalidad.
RutErr:
If Err = 3028 Then
MsgBox "No se puede abrir la base de datos del sistema. Compruebe que su Path y nombre
son correctos. Vealo en Ver Ini"
End If
End Sub
El fichero Cap20Usr.INI que debe estar necesariamente en la misma carpeta que el programa,
en el caso del PC del autor tiene esta forma
REM Visual Basic - Guía del Estudiante. Cap. 20. Creación de Usuarios y WorkSpaces
DBEngINI=C:\WinNT\System32\System.mdw
Método CompactDatabase
Sintaxis
BaseDatosNva es el nombre del fichero (con su Path completo) de la base de datos nueva,
creada al copiar la BaseDatosAnt, ya compactada. No es posible especificar en el argumento
BaseDatosNva el mismo archivo de base de datos que en BaseDatosAnt.
inf_local es una expresión de cadena utilizada para especificar el alfabeto usado a la hora de
ordenar datos de esa Base de Datos. El parámetro a introducir es el mismo que para el
argumento similar usado en la creación de la Base de Datos (dbLangGeneral para el caso de
España). Este argumento es opcional. Si se omite, la información local de BaseDatosNva será
la misma que la de BaseDatosAnt.
Opciones nos permite cambiar alguna característica de la Base de Datos. Puede elegirse
entre cifrarla o no cifrarla y cambiar la versión del motor de bases de datos que va a usar la
nueva Base de Datos.
Se puede usar una constante (solamente) de encriptación y una (solamente) de Versión. Sólo
se puede compactar BaseDatosNva con una versión igual o posterior a la de BaseDatosAnt.
Método RepairDatabase
Donde NombreBase es el nombre (Y path) del fichero que contiene la Base de Datos a reparar.
Puede especificar una ruta de red. P.e. : "\\MISERVID\ MIDIR\NombreBase.MDB".
Para poder reparar la base debe estar Cerrada. Recuerde, si está en un entorno multiusuario,
que los demás usuarios tampoco pueden tenerla abierta mientras la repara.
El método RepairDatabase también intenta validar todas las tablas del sistema y todos los
índices. Los datos que no puedan repararse se pierden. Si la base de datos no puede
repararse, se producirá un error interceptable.
Método Execute
Este Método es para el Objeto DataBase y para el Objeto QueryDef.
Ejecuta una consulta de acciones o una instrucción SQL en el objeto Database especificado.
Donde NombreQuerydef es el nombre del objeto QueryDef cuya propiedad SQL especifica la
instrucción SQL a ejecutar.
Opciones igual que para el Objeto Database.
El método Execute sólo es válido para las consultas de acciones. Si utiliza Execute con otro
tipo de consultas, se producirá un error. Debido a que las consultas de acciones no devuelven
registros, Execute no devuelve un conjunto de registros.
Ejemplo. En el siguiente ejemplo, usamos EXECUTE para cambiar el campo Nombre en una
tabla llamada CLIENTES de una Base de Datos abierta, cuyo Objeto DataBase se llama
BaseDatos. Para poder “jugar” con el nombre a cambiar y el nombre cambiado, se introduce el
nombre que queremos cambiar en TBNombre2 y el nuevo nombre en TBNombre1
MiSQL = "UPDATE CLIENTES SET NOMBRE = '" & TBNombre2 & "' WHERE NOMBRE= '" &
TBNombre1 & "'"
Dada una instrucción SQL sintácticamente correcta y teniendo los permisos adecuados, el
método Execute no fallará, aún cuando no pueda modificarse ni eliminarse una línea. Por lo
tanto, debe especificar siempre la opción dbFailOnError cuando utilice el método Execute
para ejecutar una consulta de actualización o eliminación. Esta opción generará un error
interceptable y deshará todos los cambios realizados con éxito cuando alguno de los registros
afectados se encuentre bloqueado y no pueda actualizarse o eliminarse.
Propiedad RecordsAffected
Para determinar el número de registros afectado por el último método Execute, puede utilizar
la propiedad RecordsAffected del objeto Database o Querydef. Por ejemplo, RecordsAffected
contienen el número de registros eliminados, actualizados o insertados al ejecutar una consulta
de acciones. Al utilizar el método Execute para ejecutar un objeto Querydef, en la propiedad
RecordsAffected del Querydef se establece el número de registros afectados.
Una imagen (la fotografía de una persona por ejemplo) puede guardarse en una base de datos
tipo ACCESS y presentarse en un control Picture. ¡¡ Imagine una aplicación que sea una
agenda de teléfonos y pueda insertar la foto de la persona !!
Para introducir una imagen en una BD, el campo de esa BD donde se va a introducir la imagen
debe ser LongBinary ( si esa versión de ACCESS lo tiene) u Objeto OLE.
La asociación de la Base de datos al Control Data puede hacerse, bien mediante sus
propiedades DatabaseName y RecordSource, bien creando un Recordset con código e
igualando la propiedad Recordset del Control Data a ese Recordset.
Es posible que se pueda introducir y presentar un bit-map en un control Picture o Image de otra
forma, sin usar el control Data. Eso sí, complicando el código. No merece la pena liarse con
esto. Lo mismo que decíamos que necesitamos un Control Data cuando vamos a usar un
DBGrid, debemos usar un Control Data cuando vayamos a presentar una imagen.
Hasta ahora todos los accesos realizados a una base de datos los hemos realizado directamente a través
del Motor de bases de Datos Microsoft JET. Este motor de bases de datos abre, lee y graba el
fichero .MDB (o el dBase, FoxPro, Paradox, etc.) de la misma forma que un editor de textos puede
abrir, leer o escribir un fichero creado por el mismo o por otros editores conocidos.
El ODBC es una herramienta que nos permite ver a todas las bases de una forma única. No es un
lenguaje común a todas las Bases de datos. Ese es el SQL. Es la interface que adapta a una base de
datos para que nos podamos entender con ella en SQL. Por lo tanto es fácil pensar que esa interface
deberá fabricarla el mismo fabricante que ha realizado la base de datos. El lenguaje SQL es conocido
por todo el mundo, pero la forma interna de trabajar una base de datos solamente la conoce su
fabricante. Por eso, el Driver ODBC que es el que nos permite adaptar las particularidades de esa base
de datos al lenguaje común, deberá suministrarlo el fabricante de la base.
Microsoft desarrolló varios drivers para otras tantas bases de datos y hojas de cálculo. Entre ellas para
Access y Excel, dBase y FoxPro. También lo desarrolló para bases de datos tan populares como Oracle.
Ese driver hay que instalarlo en el ordenador, aunque Windows instala algunos por defecto.
Veamos ahora otro elemento fundamental para el trabajo con ODBC: la conexión. La conexión ODBC
es el conjunto de datos que hay que aportarle a Windows para que pueda enlazar nuestra aplicación con
la base de datos. Estos datos son, al menos:
La conexión ODBC no la realiza Visual Basic. La realizará Windows. Veamos como podemos
establecerla.
Vaya a Inicio | Configuración | Panel de Control | ODBC de 32 Bits. Haga doble click y le aparecerá
un cuadro como el de la figura 13-1. En este cuadro figuran todos los enlaces que están establecidos.
Estos enlaces puede establecerlos para un usuario (DSN de usuario), de sistema o de archivo. La
elección de uno u otro dependerá de los permisos de acceso que quiera establecer (y eso es, de
momento, para nota). En cualquier caso, todas los enlaces ODBC que establezca funcionarán del mismo
modo, exceptuando que puede otorgar unas prerrogativas distintas de uso, dependiendo de como lo
haya abierto.
Observe uno que figura en la lista: Luki. En esa línea figura otro dato: Microsoft Access Driver.
(*.mdb) Esto significa que el enlace de nombre Luki está usando el driver ODBC de Access. Y
significa también que si queremos llamar a esa conexión deberemos llamarla por su nombre : Luki
Sigamos con la creación del enlace ODBC. Para crear uno nuevo, haga click en el botón Agregar de la
figura 13-1. Le aparecerá un cuadro donde le pide el driver que quiere utilizar.
Haciendo click en Finalizar, le pedirá el nombre del nuevo enlace, un comentario, (no es necesario
introducirlo), y la base de datos con la que quiere enlazar
Haciendo click en Base de Datos | Seleccionar nos muestra un cuadro de diálogo donde `podemos
buscar la base de datos. Se elige la base de datos deseada. Hacemos click en ACEPTAR y ya está
creada la nueva conexión.
La base de datos del sistema deberá elegirla para poder introducir usuarios con distintas atribuciones de
acceso a la base de datos. Si no introduce esta base de datos de sistema, deberá trabajar sin restricción
de acceso.
Una vez creada la conexión podemos utilizarla para conectar nuestra aplicación con la base de datos. El
proceso para ello consiste en algo parecido a lo que hacíamos trabajando con el motor Jet en el espacio
de trabajo Microsoft Jet, pero de distinta forma. En ese entorno se manejaba la base de datos creando
un objeto Database, que manipulaba el fichero de la base de datos directamente. En este caso, la base
de datos se maneja mediante un objeto Connection.
El trabajar sobre una conexión nos aporta mayor versatilidad a las aplicaciones. Piense por ejemplo un
caso en el que se emplea una base de datos Access. Si quisiésemos ampliar las prestaciones de la base
de datos de esa aplicación posiblemente pensaríamos en migrar la BD a Oracle o SQLServer. Si
atacamos a la base de datos a través de ODBC solamente deberíamos cambiar la conexión, tal como
vimos más atrás. Nuestro programa seguiría llamando a la misma conexión ODBC, pero esta conexión,
ahora, abriría una base Oracle o SQLServer en vez de la Access que abría originalmente. No
necesitaríamos realizar ninguna modificación en nuestro programa.
Este razonamiento es válido para cualquier sistema de acceso a bases de datos a través de ODBC. (RDO
ó ADO) Sin embargo el ODBCDirect tiene una ventaja `para los que trabajamos normalmente con
DAO: usa los mismos nombres para casi todos los métodos y funciones, por lo que el cambio de código
es mínimo. El cambio se limita prácticamente a crear el objeto Connection en vez del objeto Database.
Comencemos a ver esto con u ejemplo. Es un ejemplo uy simple, en el que abrimos una base de datos
Access llamada Alumnos.Mdb que tiene una tabla llamada Personas. Creamos la conexión Luki tal
como se describió mas atrás. La tabla personas tiene solamente tres campos: ID_Alumno, Nombre y
Apellidos. Creamos una interface gráfica sencilla
Mediante esta sencilla aplicación queremos visualizar los datos existentes, añadir nuevos registros y
borrar el registro actual. Veamos el código con los comentarios oportunos.
General/Declaraciones
Option Explicit 'Declaramos las variables tipo objeto
Dim MiSesion As Workspace
Dim MiConexion As Connection
Dim RsODBC As Recordset
Observe que casi todo el código es igual que el que escribiríamos si hubiésemos utilizado el espacio de
trabajo Microsoft Jet. Solamente hemos utilizado código distinto el procedimiento
BCrearConexion_Click. Vamos a analizar las instrucciones de ese procedimiento que son distintas a
las que estamos acostumbrados a manejar
DBEngine.DefaultType = dbUseODBC
Vea la propiedad DefaultType en el Capítulo 20. Tenga en cuenta que el ODBCDirect lo ejecuta el
motor Jet (que como recordará es el objeto DBEngiine). La propiedad DefaulType debemos igualarla a
dbUseODBC para que trabaje en el espacio de trabajo ODBCDirect. Si no le diésemos ningún valor a
esa propiedad, tomaría el otro valor posible (dbUseJet) que es su valor por defecto. Por eso, cuando
utilizábamos los métodos CreateDataBase u OpenDatabase no necesitábamos darle ningún valor a esa
propiedad.
Vea mas abajo la información de OpenConnection para ver los parámetros que hay que pasarle a este
método.
Método OpenConnection
Es un método del Workspace que solamente es aplicable cuando el DBEngine está trabajando en el
espacio de trabajo ODBCDirect. Abre un objeto Connection usando un origen de datos ODBC ya
creado.
El objeto Connection sería el equivalente en DAO al objeto Database. Debemos declararlo antes de
usarlo
En el ejemplo:
Set MiConexion = MiSesion.OpenConnection("Conexion1", dbDriverNoPrompt, False,
"ODBC;DSN=Luki")
Nombre es el nombre de la conexión (no se debe confundir con el nombre del objeto Connection que es
MiConexion, y que es el nombre por el que nos tendremos que referir a esa conexión en toda la
aplicación. Nombre no lo usará normalmente)
El parámetros Nombre puede ser una cadena cualquiera, o el nombre del enlace ODBC establecido en
Windows (que en nuestro ejemplo sería Luki). Si opta por poner una cadena cualquiera (como hicimos
en el ejemplo, Conexión1) deberá poner el nombre del enlace ODBC en el parámetro Conectar. Si
opta por poner el nombre del enlace ODBC, Visual Basic interpreta que debe conectar a través de ese
enlace y ya no necesita poner ningún valor al parámetro Conectar.
Opciones Este parámetro es para definir que tipo de respuesta va a dar el administrador del controlador
de ODBC para solicitar al usuario información sobre la conexión (Nombre del origen de datos (DSN),
nombre del usuario y contraseña). Puede tomar uno de los siguientes valores:
Una vez seleccionada la conexión, aparecerá otro cuadro solicitando el nombre de inicio de sesión y la
contraseña. En resumen, con este parámetro DbDriverPrompt forzamos al programa a que utilice los
recursos Windows para seleccionar el enlace ODBC
Sólolectura (Opcional). True si la conexión se va a abrir con acceso de sólo lectura y False si la
conexión se va a abrir para acceso de lectura/escritura (predeterminado)
Conectar (Opcional si se ha puesto el nombre del enlace en el parámetro Nombre). Este parámetro está
formado por varias partes. Una, obligatoria, que contiene la expresión "ODBC;" Otra parte obligatoria,
si no ha puesto en el parámetro Nombre el nombre del enlace ODBC es "DSN =
NombredelaConexión;" Si desea especificar el usuario propietario de esa conexión debe añadir "UID
= usuario;" y como ese usuario tendrá una contraseña, deberá añadirla también "PWD = contraseña;"
También puede indicar cuanto tiempo debe esperar el administrador del controlador de ODBC para
generar un error en caso de que no conteste el sistema a la petición ODBC. Este tiempo deberá
introducirlo con la expresión "LOGINTIMEOUT = segundos;" Observe que cada una de estas
partes lleva un identificador y un dato, ambos separados por un signo =, y siempre terminan con el
signo punto y coma (;)
Si omite los parámetros UID y/o el PWD, estos datos se obtendrán de las propiedades UserName y
Password del objeto Workspace.
Set MiRecordset =
= MiConexion.OpenRecordset (Origen, Tipo, Opciones, Bloquearmodificaciones)
Origen Puede ser un nombre de tabla, de una consulta o una instrucción SQL que devuelva registros.
Tipo Indica el tipo de recordset que queremos crear. El espacio de trabajo ODBCDirect no permite
recordsets tipo Tabla. Los tipos que se pueden elegir son:
Opciones Debe indicar uno de estos dos valores (si no pone alguno de ellos da error):
DbRunAsync Ejecuta una consulta asíncrona, que es la forma normal de trabajar del
ODBC. Esto permite a su aplicación seguir procesando otras instrucciones mientras se ejecuta
la consulta en segundo plano (Vea mas adelante la propiedad StillExecuting
dbExecDirect Ejecuta una consulta saltando el método SQLPrepare y llamando
directamente al método SQLExecDirect. Utilice esta opción sólo cuando no se abra un objeto
Recordset basándose en una consulta de parámetros. Para obtener más información, consulte la
"Referencia del programador de Microsoft ODBC 3.0."
DbReadOnly (Predeterminado) No permite que los usuarios hagan cambios en los registros del
Recordset
DbPessimistic Permite cambiar datos y añadir registros en el recordset, utilizando el criterio
de bloqueo pesimista. Esto significa que bloquea la página donde se encuentra el registros
desde que se ejecuta el método Edit hasta que se ejecuta el Update. Es el criterio mas seguro
de bloqueo de datos, pero debe tener en cuenta que tiene la base bloqueada desde el Edit hasta
el Update. Si hace bloqueo pesimista, procure que las instrucciones entre uno y otro no tengan
ningún tiempo de espera, por ejemplo a que el usuario introduzca un dato. (El tamaño de una
página depende de la base de datos que esté usando. En Access es de 2048 Bytes. Esto
significa que solamente tendrá bloqueada una parte relativamente pequeña de la base de datos,
pudiendo modificar otros registros que estén fuera de ese segmento de 2 Kbytes. Lógicamente
este bloqueo solamente tendrá efecto cuando esté trabajando en un entorno multiusuario.
Actualización por lotes. Modelo de cursor para clientes que trabajan con cursores, pero no mantienen
bloqueos en el servidor o ejecutan actualizaciones por fila. En su lugar, el cliente actualiza muchas filas
que están almacenadas en el búfer local y después ejecuta una actualización por lotes. Este modelo de
cursor también permite al cliente cancelar la conexión con el servidor y volverla a establecerla con el
mismo servidor o con otro diferente.
Para utilizar la actualización por lotes en DAO 3.5, debe utilizar un espacio de trabajo de ODBCDirect,
la propiedad DefaultCursorDriver se debe establecer a dbUseClientBatchCursor en el momento de abrir
la conexión y se debe abrir el Recordset con el argumento de tipo de bloqueo del método
OpenRecordset establecido a dbOptimisticBatch.
Propiedad StillExecuting
Cuando abrimos una conexión mediante OpenConnection, un QueryDef o creamos un recordset y le
ponemos en Opciones DbRunAsync, la operación se realiza de forma asíncrona, por lo tanto no
sabemos cuando tendremos disponible el objeto a crear o los datos de la consulta. Para poder controlar
si la operación se está realizando todavía leeremos la propiedad StillExecuting, que devolverá True si
todavía se está ejecutando, y False si ha terminado. Basta con hacer un bucle del tipo
Mientras la propiedad StillExecuting sea True, no se puede tener acceso a ningún objeto devuelto. Por
ejemplo, no pretenda crear un recordset con MiConexion Mientras que StillExecuting sea True, pues
MiConexión no existe aún.
Esta propiedad puede ser muy útil cuando tiene consultas encadenadas y necesita esperar a que termine
una para comenzar con la siguiente.
Objeto Connection, para controlar que aún se está ejecutando uno de los métodos Execute u
OpenConnection
Objeto QueryDef, para comprobar su método Execute
Recordset, donde podrá comprobar si aún se están realizando los métodos Move, NextRecordset u
OpenRecordset
Para ver el valor que tiene la propiedad StillExecuting deberá poner el nombre del objeto que quiere
comprobar (Connection, QueryDef o Recordset) después de haber iniciado uno de los métodos citados
anteriormente. Por ejemplo, para esperar a que se haya creado el objeto Connection MiConexion,
haremos un bucle de la siguiente forma
Método Cancel
Cancela la ejecución de un método asícrono de llamada. Sólo funciona espacios de trabajo
ODBCDirect.
Sintaxis objeto.Cancel
El método Cancel solamente sirve para cancelar la ejecución de una llamada asíncrona de los métodos
Execute, OpenConnection u OpenRecordset que se hayan invocado con la opción dbRunAsync. Cancel
devolverá un error de tiempo de ejecución si en el método que está intentando finalizar no se utilizó
dbRunAsync.
Fig. 13-6 Estructura jerárquica de los objetos de acceso a datos en el espacio ODBCDirect
En el espacio de trabajo ODBCDirect no se pueden crear bases de datos. Se pueden crear objetos
QueryDef, que en este caso serán objetos efímeros, que solamente existirán en la memoria RAM,
desapareciendo en el momento que cerremos ese QueryDef o cerremos la aplicación.
Puede crear un QueryDef para crear posteriormente a partir de él un Recordset con el que puede
presentar o escribir el contenido de los registros. En el siguiente ejemplo hemos abierto la conexión, y
como paso previo a la creación del recordset hemos creado un QueryDef con todos aquellos registros
cuyo campo Apellidos sea igual a Alvarez Pérez. Una vez creado ese QueryDef, hemos creado un
recordset a partir de él. No tiene mucho sentido hacer esto ya que podríamos crear directamente el
recordset imponiendo esa condición en el campo Apellidos. Se expone aquí con fines didácticos, no
prácticos.
DBEngine.DefaultType = dbUseODBC
Para indicarle el enlace ODBC que debe usar puede hacerlo de las dos formas que venimos viendo para
los parámetros Nombre y Conexión. Si indica el nombre del enlace en el parámetro Nombre lo
aceptará y será necesario poner el parámetro Conexión. Si `pone un nombre cualquiera en el parámetro
Nombre deberá indicar el nombre del enlace en el parámetro Conexión.
Esta posibilidad de crear un objeto Database nos aproxima aún más al código empleado en el espacio
de trabajo Microsoft Jet. Por lo tanto puede ser muy útil usar este objeto en aquellos casos en los que ya
se había escrito el código, y pasamos posteriormente a usar ODBC.
DBEngine.DefaultType = dbUseODBC
Set MiSesion = Workspaces(0)
'En la siguiente línea le pasamos el nombre del enlace ODBC en el parámetro Nombre
Set MiBaseDatos = MiSesion.OpenDatabase("Luki", dbDriverNoPrompt)
'Pero podríamos haberselo pasado en el parámetro Conexión y ponerle como parámetro 'nombre
cualquier cadena de caracteres
Set MiBaseDatos =
MiSesion.OpenDatabase("BaseDatos1", dbDriverNoPrompt, False, "ODBC;DSN=Luki")
'Ahora creamos el recordset, como en los otros casos
Set RsODBC = MiBaseDatos.OpenRecordset("Personal", dbOpenDynaset, dbRunAsync, dbOptimistic)
If RsODBC.AbsolutePosition <> -1 Then
RsODBC.MoveLast
RsODBC.MoveFirst
LNumRegs = RsODBC.RecordCount
LNumReg = RsODBC.AbsolutePosition + 1
PresentaDatos
Else
MsgBox "La base de datos no tiene ningún registro"
End If
Mediante los ejemplos expuestos en este capítulo espero que el alumno comprenda como trabaja DAO
a través de ODBC, mediante el ODBCDirect.
No olvide que sigue tratándose de DAO, y que por lo tanto deberá poner la misma referencia (En
Proyecto | referencia) que en el caso del DAO trabajando en el espacio de trabajo Microsoft Jet. En la
LSB Visual Basic – Guía del Estudiante Capítulo 1 Página 384
siguiente parte de este capítulo se estudiarán los objetos RDO, objetos que trabajan exclusivamente a
través de ODBC. Con las ideas captadas hasta aquí verá que es sencillo abordar estos nuevos objetos.
Visual Basic - Guía del Estudiante Cap. 13 Continuación
Acceso a Bases de Datos remotas mediante objetos RDO
En la primera parte de esta capítulo hemos visto como acceder a bases de datos a través de ODBC
usando el motor JET. El acceso se realizaba mediante ODBCDirect, modo de operación del motor
JET, y podíamos usar la misma sintaxis y objetos que habíamos visto con los objetos DAO. También
vimos como crear un enlace ODBC, enlace que vamos a utilizar en este capítulo. Como ya se explicó
en el capítulo anterior, damos por supuesto que el alumno conoce como crear ese enlace.
Los objetos RDO se crearon para cubrir el hueco que tenía VB para conectar con bases de datos
distintas a las que trabaja el motor JET. (Access, dBase, FoxPro, etc.). Esta forma de trabajar nos
permite enlazar con bases de datos tipo Oracle o SQLServer, pero también nos permite trabajar con
Access, dBase o FoxPro, que también tienen su propio driver ODBC.
Lo primero que nos encontramos al trabajar con RDO es que es más lento que DAO. Si creamos dos
aplicaciones que trabajen sobre una base Access, una directamente a través de DAO y otra a través de
RDO, observaremos que la primera accede a la BD con una rapidez mayor que la segunda. Normal, no
es lo mismo abrir un fichero y leerlo (que es lo que hace DAO) que pasar unos parámetros a otro
programa (Driver ODBC de Access) para que este abra el fichero, obtenga los datos y nos los pase. Lo
mismo podemos decir cuando tenemos que contar registros, movernos de un registro a otro, editar o
añadir nuevos registros. Esa falta de rapidez es el coste de la tecnología de bases abiertas.
Lo segundo que nos va a llamar la atención es el nombre de los objetos de acceso a datos. Una vez que
nos habíamos familiarizado con palabras como Recordset, DataBase o Dynaset, nos las cambian por
rdoResultset, rdoConnection o Keyset respectivamente. Y lo peor no es solamente tener que aprender
sus nombres, sino que en una aplicación hecha en DAO que sea necesaria cambiarla a RDO, nos vemos
obligados a retocar la mayor parte de las líneas de código (Cosa que no ocurría con ODBCDirect)
Y lo tercero, RDO no tiene la posibilidad de crear bases de datos como hacíamos en DAO. Normal, ya
que en este caso no trabajamos directamente sobre la BD sino sobre la conexión ODBC que Windows
realizó a una base de datos. Podemos añadir mas desventajas de RDO: no puede contar los registros que
tiene, tiene un comportamiento muy irregular con la propiedad AbsolutePosition, y varios detalles que
procuraremos explicar en este capítulo para ahorrar al alumno el trabajo de tener que experimentarlos
por sí mismo. (Aunque, consejo de viejo profesor, es la forma más segura de aprenderlos).
Tras estos inconvenientes parece lógico encontrar alguna contrapartida positiva. Por ejemplo poder
conectar con cualquier tipo de base de datos, pudiendo incluso realizar la aplicación atacando una base
Access, para luego trabajar con una base SQLServer u Oracle sin variar ni una línea de código. Esa
debería ser la gran ventaja de RDO. Pero lamentablemente, y en contra de toda teoría de ODBC, no es
cierto. Si preparamos una aplicación en RDO trabajando con una base Access y pretendemos cambiar la
base por otra SQLServer, observaremos que lo que funciona perfectamente en la primera no funciona en
la segunda. (Y esto no es teoría. Es simplemente experiencia del autor). ¡Y Access y SQLServer son del
mismo fabricante!. La razón está en que los drivers de una base y otra no trabajan exactamente igual.
Tampoco son iguales las protecciones y los permisos de acceso. Consejo de viejo profesor: Si va a
trabajar con una determinada base de datos a través de RDO comience el proyecto usando esa base de
datos. Y puestos a dar consejos, si piensa usar SQLServer u Oracle y si la aplicación va a escribir datos
en la base no use RDO. Si solamente los va a leer no tendrá problemas. Y si tiene problemas nadie se
los va a resolver, argumentando que deje RDO y use ADO. De hecho RDO ya se ha quedado obsoleto
al nacer ADO. Pero eso será objeto de otro capítulo de esta Guía del Estudiante y hoy nos toca aprender
RDO. Comencemos.
Al igual que DAO, RDO tiene objetos de acceso a datos, que deberemos declarar y crear. Mediante
estos objetos podemos leer y escribir datos en una BD mediante código. También, al igual que en DAO
existía el Control Data, con el que podíamos acceder a la base de datos sin escribir ni una línea de
código, en RDO existe un control similar: el Control RemoteData. El funcionamiento es similar, pero
las propiedades son distintas y tienen distintos nombres. Mediante el control RemoteData podemos
Pero antes vamos adelantar la equivalencia entre los objetos DAO y los objetos RDO. La lista siguiente
está copiada literalmente de la información de Microsoft MSDN Library Visual Studio, información
que merece la pena instalarla en su ordenador pese a lo que ocupa.
(*) En RDO, los usuarios y grupos de usuarios son precisamente los que lleve implícitos la conexión
ODBC.
(**) Al no trabajar directamente sobre la base de datos, no se pueden crear relaciones en ella.
DatasourceName
Devuelve o establece el nombre del origen de datos (DSN). El DSN No es más que el nombre de la
conexión ODBC.
Esta propiedad se puede dejar en blanco si la propiedad Connect del control identifica un nombre de
origen de datos (DSN) registrado en el Registro de Windows.
Puede cambiarse en tiempo de ejecución. En este caso, inmediatamente debe utilizar el método Refresh
para abrir la nueva conexión con la base de datos.
Puede leer el valor de esta propiedad. Le devolverá precisamente el DNS que está utilizando. Esta
propiedad sólo le devolverá un valor si ha introducido previamente algún valor en la propiedad
DatasourceName. No le devolverá valor alguno si ha introducido la conexión a través de la propiedad
Connect.
Propiedad Connect
Esta propiedad cumple la misma función que la anterior, pero por otro camino. Mediante la propiedad
Connect le introducimos al control RemoteData la información necesaria para abrir la conexión,
incluyendo en esta información, no solamente el nombre de la conexión, como hacíamos con la
propiedad DatasourceName, sino ampliarla con otros datos de la conexión, tal como el nombre del
usuario, su contraseña, nombre del controlador ODBC a usar, el nombre de la base de datos, el servidor
donde se ubica esta base de datos, nombre de la estación de trabajo desde la que vamos a trabajar e
incluso el nombre de la aplicación en la que vamos a usar los datos de esa base. Esta propiedad es
mucho más completa que la anterior, pero un poco más complicada de usar.
Para introducir los datos de la propiedad Connect deberá usar una palabra para definir el dato, seguido
del signo = y del dato a introducir. Como final del dato debe introducir necesariamente el signo punto y
coma ;
NOTA - Si conoce SQLServer, DATABASE es el nombre de la base de datos que quiere utilizar una vez
realizada la conexión. Para los que no conocen SQLServer, dentro de un fichero de esta base de datos
pueden existir varias bases de datos. Y cada una de ellas puede tener varias tablas. Es decir, la
configuración de esta BD no es tan simple como la de Access. Cada una de estas bases de datos tiene
un propietario y varios usuarios. Cada usuario tiene habilitadas unas funciones (p.e. un usuario puede
leer y escribir y otro solamente leer) Este parámetro le indica cual de esas bases de datos que contiene
el fichero de SQLServer (para hablar con mas propiedad, el sistema de ficheros de SQLServer)
Observe que tras cada parámetro existe un separador ; No es necesario introducir todos los datos.
Dependerá del driver, de cómo haya creado la base de datos, los atributos que le dio a cada uno de los
usuarios. Y como no, dependerá del tipo de base de datos que esté usando (SQLServer, Oracle, etc.).
Recuerde lo mencionado más atrás. No se fíe nunca de que su aplicación trabaja perfectamente sobre
una base de datos. Lo más probable es que si cambia de BD ya no le trabajará tan perfectamente.
El hecho de que podamos abrir una base de datos mediante DatasourceName o Connect tiene su
explicación. Si tenemos creada una conexión ODBC con todos los datos necesarios para que pueda
abrir una base de datos, es mucho más simple usar la propiedad DatasourceName y el Control
RemoteData le abrirá perfectamente la BD. Pero cuando usamos una BD a través de Red de Area
Local, lo normal es que esa base esté compartida por varios usuarios, que cada uno tendrá un nombre
y un Password, que puede estar habilitado para trabajar desde un puesto o desde varios puestos, e
incluso puede estar habilitado para trabajar sobre una BD utilizando una determinada aplicación, y no
estarlo para utilizar esa misma BD con una aplicación distinta. Por eso este control nos brinda las dos
posibilidades, una sencilla, DatasourceName, para trabajar con los datos ya introducidos en la
conexión ODBC, y la otra, para variar los datos de esa conexión ODBC ya existente y lograr con ello
LSB Visual Basic – Guía del Estudiante Capítulo 1 Página 388
todas las ventajas que nos ofrece ODBC respecto a restricciones de usuarios, contraseñas, etc. (la
conexión ODBC en este caso es muy normal que tenga solamente el nombre, y que no apunte a ninguna
base de datos concreta). Puede incluso utilizar ambas propiedades. Es muy típico por ejemplo, que la
conexión apunte a una determinada base de datos, (el nombre de la conexión se lo pasamos en la
propiedad DatasourceName) y que en la propiedad Connect le pasamos el nombre del usuario y la
contraseña.
Puede utilizar el control, RemoteData incluso sin tener una conexión preestablecida. Pero deberá
establecer mediante el programa, utilizando los métodos OpenConnection o EstablishConnection. Se
sale del contenido deseado para este curso comentar estos métodos, pero el alumno aventajado puede
intentar obtener información en la escasa bibliografía existente. No le recomiendo que se complique la
vida rizando el rizo, pudiendo establecer previamente la conexión ODBC.
Propiedad SQL
Mediante las dos propiedades estudiadas, DatasourceName y Connect hacemos que el control
RemoteData sepa la conexión sobre la que va a trabajar - y por lo tanto la base de datos que utilizará.
Ahora nos falta indicarle los datos que deseamos leer o escribir. Nos falta lo que sería en DAO, darle
los datos para crear el Recordset (Por ejemplo el nombre de una tabla o una sentencia SQL, tal como
hacíamos en la propiedad RecordSource del control Data). Para el control RemoteData esta
información se le introduce en la propiedad SQL
La propiedad SQL establece o devuelve una instrucción SQL válida para crear un conjunto de registros
a partir del origen de datos establecido en las propiedades DatasourceName o Connect. Esta
instrucción SQL debe comenzar necesariamente por SELECT, En tiempo de ejecución, podemos
asignar a esta propiedad el nombre de una Consulta ya almacenada en la BD, pero en este caso,
debemos anteponer la palabra EXECUTE. También podemos introducir en esta propiedad un
rdoQuery, un rdoResultset o un rdoTable. Eso lo veremos más adelante.
Para establecer esta propiedad en tiempo de diseño, basta con escribir la instrucción SQL en la ventana
de propiedades. Una instrucción típica sería
Si la BD tiene una consulta (la típica consulta de Access) llamada C_Suarez, donde hemos seleccionado
todos los registros cuyo campo Apellido1 sea Suárez, podemos poner:
(No intente hacer esto mismo con el nombre de una tabla. Solamente sirve para consultas)
Con los registros seleccionados por la propiedad SQL, formamos lo que en DAO era un recordset, pero
en este caso adopta otro nombre: rdoResultset. En RDO se ha buscado otra terminología,
posiblemente para diferenciarlo claramente de DAO. Por ejemplo, en vez de registros es habitual hablar
de Filas, y en vez de campos, hablamos de Columnas.
Si pudiésemos crear un rdoResultset mediante algún procedimiento, (y seguro que podremos hacerlo),
podemos introducir directamente ese rdoResultset como rdoResultset del control RemoteData, al igual
que lo hacíamos con el Recordset del Control Data en DAO:
Propiedad Connection
Veamos previamente que es un objeto rdoConnection. Un Objeto rdoConnection es un objeto de
acceso a datos remotos. Es el equivalente al objeto Database en DAO (Vea cuadro página 2) El control
Label1 = MSRDC1.Connection.Connect
Si deseamos saber si el control RemoteData sigue conectado para realizar una determinada operación:
If MSRDC1.Connection.StillConnecting Then ….
MSRDC1.Connection.Close
MSRDC1.BOFAction = valor
MSRDC1.EOFAction = valor
Los valores (o constantes) que puede tomar son los siguientes:
La propiedad EOFAction sólo tiene efecto cuando se manipula el cambio de filas mediante el ratón,
sobre los botones del control RemoteData. No tiene efecto si se llega a la fila posterior a la última
mediante código. (Por ejemplo, mediante la instrucción MSRDC1.resultset.MoveNext)
Propiedad CursorDriver
Devuelve o establece un valor que especifica el tipo de cursor que se va a crear. Veamos primero qué es
un cursor. Según la definición de Microsoft:
Conjunto lógico de filas administrado por el origen de datos o por el administrador de controladores
ODBC. Los cursores reciben dicho nombre porque indican la posición actual dentro del conjunto de
resultados, igual que el cursor de la pantalla indica la posición actual. (#G!*¡&@%)
El crear un cursor de lado cliente o de lado servidor puede hacer que su código
funciones o no funcione. Los cursores lado cliente suelen ser de lectura / escritura, que
permiten avanzar hacia a delante y hacia atrás. Los cursores lado servidor suelen ser
solamente de lectura y de avance solamente hacia delante. Hemos empleado una
palabra no muy exacta: suelen ser. Y es que eso dependerá de la base de datos (Oracle
no se comporta como SQLServer, y ninguna de ellas se comporta como Access) y
depende también del controlador ODBC que utilice (Hay controladores de varias marcas
para la misma base de datos) Es muy frecuente realizar un código que funciona
perfectamente con un controlador ODBC, y cuando se cambia de controlador – o de
versión – ya no funciona. Le recomiendo mucho cuidado.
El control RemoteData permite elegir el crear un cursor en el servidor, en el puesto, o crearlo solamente
si es necesario. Para ello usamos la propiedad CursorDriver. Puede tomar los siguientes valores:
Método UpdateRow
Es equivalente al método UpdateRecord del Control Data. Guarda los valores actuales de los controles
enlazados en la base de datos. El método UpdateRow tiene el mismo efecto que ejecutar el método
Edit, modificar una columna y después ejecutar el método Update, excepto que no ocurre ningún
evento.
Nota Cuando usa una biblioteca de cursores ClientBatch, todas las actualizaciones a las tablas base se
retrasan hasta que use el método BatchUpdate. En este caso, el método UpdateRow actualiza el
rdoResultset local, pero no actualiza las tablas base. Estos cambios pueden perderse si la aplicación
termina antes de que se haya completado el método BatchUpdate.
De igual forma que un control data tenía asociado un recordset, un RemoteData tiene asociado un
Resultset. El Resultset es el objeto rdoResultset del control RemoteData (Colección de registros, o si lo
prefiere, de filas ya que estamos en RDO), y que como cualquier objeto de acceso a datos, tiene sus
métodos, y este es uno.
Este método es un método del rdoResultset. Si tenemos un rdoResultset creado con código (Ahora
veremos como se hace) la sintaxis sería
Con esta introducción ya podemos pensar que el alumno tiene cierta idea respecto a lo que es el control
RemoteData. Vamos a comenzar a explicar lo que son los objetos de acceso remoto a datos (Objetos
RDO) y podremos seguir viendo cosas acerca del control RemoteData como aplicación de estos nuevos
objetos.
Los objetos de datos remotos nos permiten manipular componentes de un sistema de base de datos
ODBC remoto. Lo de remoto no implica que la base de datos deba estar en un ordenador distinto al que
tiene la aplicación. (Aunque esta sea la disposición más usual en aplicaciones que usan RDO). El
significado de RDO es que se accede a la base de datos a través de una conexión ODBC.
RDO solamente funciona en plataformas de 32 bits. (Windows 95/98/2000 o Windows NT). Para usar
objetos de datos remotos, debe establecer una referencia a Microsoft Remote Data Object 2.0 en
Proyecto | Referencias.
Al igual que en DAO, los objetos RDO tienen una estructura jerárquica que se puede ver en la siguiente
figura:
Los objetos RDO siguen la misma regla para su creación que los objetos DAO: el objeto
jerárquicamente superior crea al objeto inferior.
El Objeto rdoEngine
El objeto rdoEngine representa el origen de datos remoto. Es el equivalente al dbEngine de DAO, es
decir, el motor de bases de datos. Es el objeto de nivel jerárquico superior, por lo tanto no se crea por
otro objeto, sino que está creado simplemente al introducir la referencia.
La característica del rdoEngine es que trabaja siempre a través del Administrador de controladores. El
objeto rdoEngine contiene al objeto rdoEnvironments (Colección de objetos rdoEnvironment) y el
rdoErrors.
El objeto rdoErrors contiene todos los mensajes de error enviados desde el origen de datos remoto.
Cada vez que se recibe uno de estos mensajes, se produce el evento InfoMessage del rdoEngine
Por defecto, esta propiedad toma el valor 0, que pone la configuración establecida en Windows.
Si el archivo DLL del idioma especificado no está presente en el equipo del usuario, RDO se establece
como rdLocaleEnglish, lo cual no requiere un archivo DLL independiente. Cuando esto ocurre, se
coloca un mensaje informativo en la colección rdoErrors para indicar que RDO no pudo cargar el
archivo DLL de recursos para la configuración regional especificada.
Cuando distribuya la aplicación, asegúrese de incluir el archivo DLL del lenguaje apropiado.
Donde:
Nombre es la propiedad Name del nuevo objeto rdoEnvironment. (En el código, para nombrar a este
rdoEnvironment debemos hacerlo con MiSesion). Debe suministrar un nombre, ya que si no lo hace,
este rdoEnvironment creado no se suma a la colección rdoEnvironments.
Usuario es el nombre del usuario.
Contraseña es la contraseña usada en esa sesión. Puede tener hasta 14 caracteres.
Hasta aquí las propiedades y métodos del rdoEngine. Vamos a bajar un nivel jerárquico y ver el
siguiente objeto RDO
El Objeto rdoEnvironment
Un objeto rdoEnvironment es una sesión de trabajo en RDO. Equivale al Workspace de DAO. En un
rdoEnvironment podemos tener varios objetos Connection (varias conexiones) de la misma forma que
en un Workspace podíamos tener varias objetos Database.
La colección de todos los objetos rdoEnvironment es el objeto rdoEnvironments. Visual Basic crea
automáticamente un rdoEnvironment, de la misma forma que creaba un Workspace. El objeto
rdoEnvironment creado es el rdoEnvironments(0) y el nombre de usuario será el que tenga el
rdoEngine en su propiedad rdoDefaultUser y la contraseña igual a la propiedad rdoDefaultPassword
Propiedad LoginTimeout
Devuelve o establece el número de segundos que el Administrador de controladores ODBC espera antes
de que se produzca un error de espera al abrir una conexión.
Si establece un valor para esta misma propiedad en uno de sus objetos rdoConnection, este valor es
prioritario al establecido en el objeto rdoEnvironment.
Método OpenConnection
Abre una conexión con un origen de datos ODBC. En otras palabras, crea un objeto rdoConnection,
objeto que deberá declarar antes de abrirlo:
Nombre puede ser una conexión ya creada, en cuyo caso se abrirá esa conexión. Si este parámetro es
una cadena vacía, deberá obtener los datos de la conexión, o bien del argumento Conectar, o mediante
el cuadro de dialogo de abrir la conexión.
SóloLectura Determina si la conexión se abre como sólo lectura o para lectura / escritura. Si no se
especifica nada, se abre para lectura / escritura.
Conectar Este es el argumento que lleva los datos completos de la conexión. Es similar a la propiedad
Connect del control RemoteData, cuyo contenido repetimos aquí por comodidad.
Para introducir los datos de la propiedad Connect deberá usar una palabra para definir el dato, seguido
del signo = y del dato a introducir. Como final del dato debe introducir necesariamente el signo punto y
coma ;
El funcionamiento en RDO es distinto al de DAO. Además, es distinto para cada base de datos. La
forma de trabajar de SQLServer es distinta de cómo lo hace Oracle. Y muy distinta de cómo lo hace
Access. Le recomiendo que estudie las transacciones en el manual de su gestor de base de datos.
Hay algunas bases de datos que no aceptan transacciones. Puede comprobarlo analizando la propiedad
Transactions del objeto Connection.
Método Close
Cierra un el rdoEnvironment y todas las conexiones que tenía abiertas. Las modificaciones
pendientes de los objetos RDO inferiores que estuviesen abiertos se deshacen.
Sintaxis MiSesion.Close
El objeto rdoConnection
Un objeto rdoConnection representa una conexión abierta con un origen de datos a través de ODBC.
Es el equivalente al Objeto DataBase de DAO
Un objeto rdoConnection (es decir, una conexión a una base de datos a trvés de ODBC) se crea o con
un control RemoteData o mediante el método OpenConnection del objeto rdoEnvironment.
Puede también crear un nuevo objeto rdoConnection que no esté vinculado de forma inmediata con
una conexión física específica a un origen de datos. Por ejemplo, el siguiente código crea un objeto
rdoConnection independiente:
Propiedad Connect Devuelve o establece un valor que proporciona información sobre el origen de
un objeto rdoConnection abierto. La propiedad Connect contiene la cadena de conexión ODBC. Esta
propiedad puede leerse siempre, pero no puede modificarse una vez establecida la conexión.
No es necesario aportar estos datos para crear la conexión, ya que son los que se han
introducido en Windows al crear la conexión ODBC. Estos datos solamente son necesarios si
la conexión ODBC se creó sin aportarle datos acerca de la base de datos.
Propiedad hDbc
Devuelve el controlador de conexión ODBC.
Sintaxis MiConexion.hDbc
La propiedad hDbc devuelve un valor de tipo Long. Este valor lo utilizan las APIs de Windows. Y es
similar a hDc o hWnd
Propiedad LastQueryResults
Devuelve un objeto rdoResultset, precisamente el que se ha generado la última consulta, si la ha
habido. Esta propiedad podemos utilizarla para crear un nuevo Resultset, clónico del ultimo que se ha
generado.
Sintaxis
Dim MiRs as rdoResultset
Set MiRs = MiConexion.LastQueryResults
Propiedad QueryTimeout
Devuelve o establece un valor que especifica el número de segundos que espera el
Administrador de controladores ODBC antes de que se produzca un error de tiempo de espera
al ejecutarse una consulta. El valor predeterminado es de 30 segundos.
Sintaxis MiConexion.StillConnecting
Esta propiedad devuelve True si la conexión no ha terminado de establecerse, y False si ya
está establecida. Es fundamental saber que la conexión ya está establecida antes de realizar
ninguna operación con ella, por ejemplo, crear un Resultset
Propiedad StillExecuting
Devuelve un valor que indica si una consulta está aún ejecutándose. Esta propiedad se usa cuando
creamos un rdoResultset o un rdoQuery, para conocer si ya se ha finalizado el proceso de selección de
filas que esa operación implica. Esta propiedad puede aplicarse al rdoConnection, rdoQuery y
rdoResultset. Devuelve True cuando la consulta está ejecutándose todavía, False si ya se ha finalizado.
Sintaxis objeto.StillExecuting
Propiedad Transactions
Devuelve un valor que indica si se pueden realizar transacciones con un objeto rdoConnection ó
rdoResultset. Esta propiedad le permite asegurarse de esta circunstancia antes de ejecutar la instrucción
BeginTrans.
La propiedad Transactions llama a la función SQLGetInfo de ODBC para determinar si el controlador
ODBC es capaz de permitir transacciones, no si el conjunto de resultados actual es actualizable.
Propiedad AsyncCheckInterval
Devuelve o establece un valor que especifica el número de milisegundos que espera RDO entre dos
comprobaciones para ver si se ha completado una consulta asíncrona.
Comentarios Al usar la opción rdAsyncEnable para ejecutar una consulta de forma asíncrona, RDO
comprueba periódicamente el origen de datos para determinar si la consulta se ha completado. Puede
modificar la duración del intervalo entre comprobaciones mediante la propiedad AsyncCheckInterval.
RDO también comprueba el estado de las consultas asíncronas cuando se examina la propiedad
StillExecuting.
Propiedad UpdateOperation
Esta propiedad afecta a la forma en la que se realiza la modificación de una fila en una
actualización optimista por lotes. Si a esta propiedad se le pone el valor 0 (Predeterminado) la
modificación de la fila se realiza mediante una instrucción Update. Si el valor es 1, la operación
de modificación se realiza mediante dos instrucciones, primero una instrucción Delete (borra la
fila) y a continuación otra instrucción Insert (Crea la fila con los nuevos valores).
El Objeto rdoResultset
El Objeto rdoResultset es el conjunto de filas que devuelve la ejecución de una consulta. Es el
equivalente al Recordset de DAO
Ejemplo:
‘Creamos el objeto rdoEnvironment utilizando el objeto creado automáticamente por VB
LSB Visual Basic – Guía del Estudiante Capítulo 1 Página 399
Set GepaWs = rdoEnvironments(0)
‘El objeto rdoEnvironment crea el objeto rdoConnection
Set GepaConex = GepaWs.OpenConnection(“Gepa”, rdDriverNoPrompt, False)
‘(Gepa es el nombre de la conexión ODBC creada en el ordenador)
‘El objeto rdoConnection crea el objeto rdoResultset
Donde
MirdoResultset = variable tipo rdoResultset que debe estar declarada.
MiConexión = Objeto Connection con el que se crea el rdoRsultset
Nombre = Nombre de una tabla o consulta de la base de datos, o una sentencia
SQL que pueda devolver filas.
Tipo = Tipo de cursor que se va a crear
TipoBloq = Tipo de bloqueo de la base de datos. Por defecto, lo crea solo lectura.
Opciones = Opciones que puede tener el rdoResultset
El objeto rdoResultset tiene algunas particularidades que no tienen los recordsets de DAO, por
ejemplo, poder obtener resultados múltiples, es decir, se pueden introducir varias sentencias
SELECT y cada una de ellas crea un conjunto de registros. Esto no significa que haya mas de
un rdoResultset, sino que existe un único rdoResultset con resultados múltiples. Se puede ir
accediendo a cada uno de los conjuntos de registros mediante el método MoreResults.
AbsolutePosition. Igual que en RDO, pero no siempre funciona, ya que depende del tipo de cursor.
ActiveConnection Devuelve una referencia a la conexión con la que está asociado el rdoResultset
BatchCollisionRows
Devuelve una matriz de marcadores que indica las filas que han provocado colisiones en la
última operación de actualización por lotes. Devuelve un Variant con una matriz de las filas que
han provocado una colisión la última vez que se invocó el método BatchUpdate. El número de
elementos de esta matriz es el que indica la propiedad BatchCollisionCount
BatchSize (Propiedad)
Devuelve o establece un valor que especifica el número de instrucciones enviadas al servidor
en cada lote. De forma predeterminada se envían 15 instrucciones al servidor en cada lote.
Esta propiedad puede modificarse en cualquier momento. Si un DBMS no admite lotes de
instrucciones, puede establecer esta propiedad a 1, con lo que cada instrucción se enviará por
separado.
BOF, EOF Son idénticas a las mismas propiedades del Recordset de DAO
Bookmark Funciona igual que en el Recordset de DAO. Pero en RDO es posible que esa propiedad
no se pueda usar. Depende del tipo de cursor. Para asegurarse de que el objeto rdoResultset admite
marcadores, examine el valor de su propiedad Bookmarkable antes de usar su propiedad Bookmark.
Si Bookmarkable es False, el objeto rdoResultset no admite marcadores y el uso de Bookmark
producirá un error
La propiedad Bookmark no se aplica a los objetos rdoResultset de tipo forward-only.
EditMode Devuelve un valor que indica el estado de edición de la fila actual. Devuelve un
integer o una constante de acuerdo con la tabla siguiente:
LastModified Devuelve un marcador que indica la última fila modificada o agregada más
recientemente. Este marcador es el Bookmark de esa fila. Devuelve un Variant.
LockEdits Devuelve un valor de tipo Booleano que indica el tipo de bloqueo en vigor. Si
devuelve True utiliza bloqueo pesimista. Si devuelve False utiliza bloqueo optimista.
El bloqueo se realiza sobre una página de datos. La página suele se de 2 K (Ese es el tamaño que
utiliza Microsoft SQL Server)
LockType Devuelve o establece un valor entero de tipo Long que indica el tipo de tratamiento
de concurrencia. Los valores admitidos son:
Constante Valor
Descripción
(Predeterminado) El cursor es de sólo lectura. No se admiten
rdConcurReadOnly 1
actualizaciones.
rdConcurLock 2 Concurrencia pesimista.
rdConcurRowVer 3 Concurrencia optimista basada en el identificador de fila.
rdConcurValues 4 Concurrencia optimista basada en los valores de las filas.
Concurrencia optimista con actualizaciones por lotes. Se obtienen valores de
rdConcurBatch 5
estado para cada fila actualizada correctamente.
Lea la ayuda de VB para obtener mayor información de cada uno de los tipos.
Restartable Devuelve un valor que indica si un objeto rdoResultset admite el método Requery,
que vuelve a ejecutar la consulta en la que está basado el objeto rdoResultset. Debe usarse
antes de utilizar el método Requery para evitar que se produzca un error.
PercentPosition
Devuelve o establece un valor que indica o modifica la ubicación aproximada de la fila actual en el
objeto rdoResultset, basándose en el porcentaje con respecto al total de filas de dicho objeto. El valor
devuelto es un Single entre 0,0 y 100,0
Puede usar la propiedad PercentPosition con una barra de desplazamiento de un control Form o
TextBox para indicar la ubicación de la fila actual en un objeto rdoResultset. Esta propiedad
solamente se aplica a los rdoResultset tipo Keyset y Dynamic.
Status Devuelve o establece el estado de la fila o columna actual. El valor de esta propiedad indica si
la fila o la columna estarán implicadas en la próxima actualización optimista por lotes y de qué modo lo
estarán.
StillExecuting Devuelve un valor de tipo Booleano que indica si una consulta está aún
ejecutándose.
Esta propiedad es muy útil (muy necesaria) para saber si la consulta ya está disponible, antes de
presentar los datos de esa consulta. La propiedad debe ser False para poder presentarlos. Es típico crear
un bucle parecido a este:
El rdoResultset tiene algunos métodos comunes con el Recordset de DAO, sin embargo no se
puede esperar que funcionen siempre de la misma forma. En DAO era Visual Basic quien abría
la base de datos, y una base muy concreta, Access, dBase, etc., bases que controla
directamente Visual Basic a través de la dll correspondiente a la versión de la BD que vamos a
abrir. Esto no es exactamente igual en RDO. Aquí quien abre el fichero que contienen la base
de datos no es Visual Basic, sino el driver de ODBC. Y no solamente eso, dependiendo de la
base de datos, unas permiten hacer unas operaciones y otras no. Lo mismo podemos decir del
tipo de rdoResultset, del cursor y de si es lado cliente o lado servidor. Por lo tanto no se asuste
si pretende ejecutar un método y no funciona. Probablemente es que Visual Basic ha puesto
por defecto unas características a los objetos que no son las adecuadas. Y si no fuese posible,
en última instancia nos queda realizar directamente la operación que pretendíamos realizar con
el método, es decir, mediante las operaciones que nos permite el propio driver de ODBC. Eso
sí, en este caso estamos en sus manos. Lo veremos más adelante.
Sintaxis MirdoResultset..AddNew
Funciona igual que el mismo método de DAO, se ejecuta el método AddNew, se introducen los datos
en los campos y se termina la operación con el método Update, que es cuando los datos entran en la
base de datos. Si se está usando cursores de tipo Client Batch los datos se escribitrán en la BD cuando
se ejecute el método BatchUpdate.
Hay que tener cuidado, pues el método AddNew no devuelve ningún error si se intenta añadir una
nueva fila a un rdoResultset no actualizable. Ese error va a salir cuando ejecutemos el método Update.
Para evitar este error hay que comprobar la propiedad Updatable del rdoResultset.
Debe tener cuidado también con asegurarse que tras el método AddNew ejecuta el método Update
antes de cambiar de fila, ya que de no hacerlo se pierden los cambios realizados y VB no avisa de ese
error.
Si desea interrumpir la entrada de datos, una vez ejecutado el método AddNew puede anularse usando
el método CancelUpdate
MirdoResultset. CancelUpdate
La fila recién añadida no pasa a ser la fila actual. Sigue siendo fila actual la que era anteriormente. Para
que sea la nueva la fila actual basta con poner el siguiente código:
MirdoResultset.BookMark = MirdoResultset.LastModified
Pero eso sí, el rdoResultset debe aceptar marcadores. Para comprobarlo, se usa la propiedad
Bookmarkable.
Método BatchUpdate
Realiza una actualización optimista por lotes. Veamos que significa esto.
Este método solamente se puede aplicar a un rdoResultset del tipo Client Batch (Cuando se crea el
rdoResultset usando como tercer parámetro rdUseClientBatch) El tipo de rdoResultset puede ser
Keyset o Dynamic. En estas condiciones, cuando usamos el método Update para guardar los datos,
solamente se guardan en el rdoResultset local, pero no se meten en la base de datos. Cuando se ejecuta
este método, se envían a la base de datos todos los cambios pendientes.
El parámetro filaÚnica es un Booleano que si es True, hace que la actualización sea solamente de la fila
actual. Si es False, se actualizan todas las filas pendientes.
forzar es tambiénun Booleano que si es True fuerza a escribir los valores, aunque puedan causar
colisiones.
Si usa el método CancelBatch, se descartan los cambios guardados en el objeto rdoResultset local.
Método Close
Cierra un rdoResultset abierto. Es igual al método Close del Recordset de DAO
Sintaxis MirdoResultset.Close
Sintaxis MirdoResultset.Delete
Método Edit
Inicia la operación de cambiar los valores de los datos de la fila actual o en un objeto rdoResultset
actualizable. Funciona de la misma forma que el método del mismo nombre del Recordset de DAO. Al
igual que en el método AddNew, es necesario terminar la operación con el método Update.
Sintaxis MirdoResultset.Edit
Si cambia la fila actual antes de ejecutar el método Update, se perderán los cambios. Si desea anular el
cambio que está realizando, basta con ejecutar el método CancelUpdate.
Cuando la propiedad LockEdits del objeto rdoResultset es True (bloqueo pesimista), todas las filas
del conjunto de filas del objeto rdoResultset se bloquean en cuanto se ejecuta Edit, y se mantienen
bloqueadas hasta que se ejecuta Update.
Método GetRows
Recupera múltiples filas de un rdoResultset y las introduce en una matriz.
Método Requery
Actualiza los datos de un objeto rdoResultset volviendo a ejecutar la consulta en la que está basado el
objeto.
El valor admitidi para Opciones es rdAsyncEnable (32) que ejecuta la operación de forma asíncrona
Método Update
Termina una operación de modificación de datos o de añadir una nueva fila. Ya se ha comentado su
funcionamiento con los métodos AddNew y Update.
No vamos a profundizar más en los objetos RDO. Su comportamiento es muy parecido a DAO,
exceptuando los nombres de los objetos, y las particularidades del ODBC en cuanto a la
situación de los cursores.
ODBC es una tecnología ya obsoleta (En el año 2002) y no debe emplearse para nuevos
proyectos. Lógicamente un curso de Visual Basic debe incluir RDO, pero siempre para aplicarlo
al mantenimiento de aplicaciones ya existentes. No se debe emplear para nuevos proyectos,
ya que si se quiere emplear ODBC es mucho más práctico y sencillo emplear DAO en su
versión de ODBCDirect. Tendrá código compatible con DAO, y más rapidez que con RDO.
Además RDO es una tecnología considerada obsoleta por Microsoft, con lo que ello conlleva.
Microsoft dice, y este autor recomienda: USE ADO EN TODOS SUS NUEVOS PROYECTOS
Pero si usa Access con la base de datos instalada en el mismo ordenador que el programa,
use directamente DAO. Si usa Access olvídese de nuevas tecnologías. Con Access DAO
significa: Rapidez, sencillez, eficacia, control sobre el programa, independencia de drivers.
ADO es lo último de Microsoft en acceso a bases de datos. No se porqué, pero desde su inicio,
ADO está como metido en una aureola de dificultad a la que solamente pueden acceder
informáticos especialmente elegidos. Quizás sea el uso de palabras de argot muy rebuscadas,
que más que facilitar el estudio, atemorizan al principiante. La Guía del Estudiante pretende
quitar esos velos que ocultan la sencillez de lo cotidiano y mostrar la facilidad de esta técnica.
Con el estilo didáctico que caracteriza a este libro, pasaremos de las definiciones gloriosas e
iremos a lo verdaderamente importante: saber programar con ADO
O
L Bases de datos
E Relacionales
D
A
B
D
Aplicación que accede a los datos
O
O
D
B Texto
C
R
D Bases de datos
O relacionales
O D
B
C
D
A
O
Modelo DAO/RDO
Las características específicas que proporciona ADO para entornos de Cliente/Servidor (C/S)
son:
• Creación de los objetos de forma independiente. No se necesita navegar por ninguna
jerarquía de objetos para poder crearlos. La mayoría de los objetos se pueden
instanciar de forma independiente. De esta forma crearemos solo los objetos que
necesitemos.
• ADO nos permite utilizar los procedimientos almacenados en el sistema gestor de la
base de datos (si este soporta esta funcionalidad), pudiendo recoger los resultados
devueltos por dichos procedimientos como parámetros de salida. Esta característica
permite mejorar el rendimiento y la rapidez de las aplicaciones.
• Diferentes tipos de cursores.
• Soporte para limitar el número máximo de registros devueltos de una sola vez en un
recordset. Esta característica mejora el rendimiento tanto de la aplicación como de la
red.
• Soporte para recibir varios recordset como resultados devueltos de un procedimiento
almacenado.
Hay que tener en cuenta que todas estas características están limitadas por el servidor de los
datos. Es decir, si el servidor de datos no soporta procedimientos almacenados, no podremos
utilizar con él las características de ADO que se refieren a dichos tipos de procedimientos.
Connection*
Errors Error*
Command*
Parameters Parameter *
Recordset
*
Fields Field*
* Todos los objetos marcados con un asterisco contienen la colección Properties con un
subconjunto de objetos Property.
Properties
Property
Como puede verse en la figura, existen tres objetos principales dentro de ADO: El objeto
Connection, el objeto Command y el objeto recordset. Luego veremos algunas de las
características principales de cada uno de estos objetos.
Antes de proseguir con estos objetos vamos a explicar donde y porqué se deben utilizar
objetos ADO en vez de objetos DAO u objetos RDO
Hasta ahora habíamos utilizado bases de datos Access, y también otras bases de datos
sencillas como dBase. Acceder a Access es extremadamente fácil. Y ello es debido a que
Access es una base de datos sin grandes aspiraciones en cuanto a seguridad. Es una gran
base de datos, y tiene sus dispositivos de seguridad en cuanto a permisos de acceso (Vea El
dbEngine. Visión desde DAO y la propiedad SystemDB en el Capítulo 12) sin embargo estas
posibilidades se usan en muy pocas ocasiones, y estos mecanismos de seguridad de Access
tampoco son una maravilla. Por lo tanto Access se ha quedado como una gran base de datos
Cuando queremos empezar a tener una seguridad en los accesos, disponer de privilegios
distintos para cada usuario, trabajar en una red de área local con muchos usuarios, hay que
recurrir a bases de datos tipo Oracle o SQLServer. Ya empezamos a tener problemas: Visual
Basic no puede acceder directamente a abrir estas bases de datos. Podemos acceder a través
de ODBC, pero como ya se dijo en el, capítulo correspondiente, ODBC se ha quedado
obsoleto. Y Microsoft ha sacado para ello ADO. Y ADO permite abrir la base de datos usando
para ello un dispositivo intermedio que es el proveedor OLE DB. Este no es más que una DLL.
Mejor dicho, un juego de DLLs que puede ver en la carpeta:
Estas DLLs permiten conectar con las bases de datos más conocidas (Oracle, SQLServer,
Access y las demás BD controladas por el motor Jet). ADO funciona de forma diferente a
ODBC. Con ODBC se preparan conexiones permanentes en el ordenador, y cualquier
programa puede acceder a la BD a través de esas conexiones. Con ADO no hay que preparar
previamente ninguna conexión. Es el propio programa el que llama al proveedor de datos OLE
DB y le pasa como parámetros los datos necesarios para que este realice la conexión y abra la
BD. Si hubiese dos programas ejecutándose simultáneamente y accediendo a la misma base
de datos a través de ADO, cada programa prepara una conexión a esa BD. En ODBC
podríamos ver las conexiones existentes en el PC a través del Panel de Control | Fuentes de
Datos ODBC. En ADO no existe esa posibilidad ya que, como se ha dicho, es el propio
programa quien crea esa conexión al ajecutarse.
Una particularidad de ADO frente a lo ya visto con DAO o RDO es que ADO se salta la
jerarquía a la hora de crear nuevos objetos. En DAO, el objeto DAO superior creaba al objeto
DAO inferior (Recuerde aquello del juego de niños). En ADO podemos crear cada objeto sin
que exista el objeto inmediatamente superior. Por ejemplo podemos crear un recordset sin que
exista el objeto Connection. Claro que en este caso, a la hora de crear el objeto recordset
deberemos indicarle, mediante los parámetros que debemos aportar en la sintaxis de creación
del recordset, todos aquellos datos que le aportaríamos a la creación del objeto Connection.
Como ve no tiene ventajas. Solamente que nos desentendemos un poco de abrir y cerrar el
objeto Connection.
EL OBJETO CONNECTION
El objeto Connection representa una sesión con el origen de los datos. Dependiendo de la
funcionalidad del proveedor de los datos podremos utilizar determinadas propiedades, métodos
y colecciones de este objeto. La función de este objeto es recoger toda la información del
proveedor de los datos que se va a utilizar para crear un objeto recordset.
Para crear un objeto Connection, previamente debemos declararlo como variable objeto
Connection:
El sitio donde se debe declarar depende como siempre, del ámbito que deseamos que tenga
ese objeto.
Nota: En el caso de que ejecutemos la aplicación y nos salga un error diciendo que el tipo no
está definido por el usuario, es que no hemos añadido la referencia de “Microsoft Actives Data
Objects Library x.x”
La conexión ¿está creada?. Sí, pero de momento es completamente inútil ya que no sabe ni
siquiera que base de datos debe abrir, ni con que usuario, ni las condiciones en las que debe
abrir esa base (Solo lectura, etc.) Esta información se la pasamos mediante la propiedad
ConnectionString (Cadena de conexión)
La propiedad ConnectionString
Es la propiedad más importante del objeto Connection. Se basa en encadenar una serie de
argumentos en una cadena de caracteres. Los diferentes argumentos son (dependiendo del
proveedor OLE DB y de la configuración de la red, se necesitarán todos o parte de ellos)
Como ejemplo para la conexión a una base de datos Oracle, con el usuario que esa BD tiene
por defecto (scott) y el Password de este usuario (tiger) tendremos que especificar en el
ConnectionString la siguiente cadena de caracteres.
donde el valor de Source es la cadena de conexión de Oracle que previamente tiene que estar
configurada con el SQL*Net o Net8 de Oracle.
Si lo que queremos es crear una conexión con una base de datos Access 2000
‘Provider=Microsoft.Jet.OLEDB.4.0;Data
Source=C:\GuiadelEstudiante\PruebaADO.mdb;’
Si la base de datos es de Access 97 entonces tendremos que especificar otro provider:
‘Provider=Microsoft.Jet.OLEDB.3.5;Data Source=C:\GuiadelEstudiante\db1.mdb;’
Como puede comprobar, resulta una tanto complicado construir la cadena de conexión y
mientras que seamos novatos en ADO podemos tener problemas para crear esta cadena.
¿Pero es que no existe una manera más fácil de construir esta cadena? Efectivamente. Hay un
pequeño truco mediante el cual no solo podemos crearla con un asistente, sino que además
probaremos si la conexión es satisfactoria o no.
1) Primero tenemos que incluir el componente “Microsoft ADO data control 6.0 (OLEDB)”.
Podemos hacerlo pulsando Ctrl.-T o en el menú Proyecto Componentes.
2) Metemos en nuestro formulario el control y pulsamos F4 para ver sus propiedades.
3) Hacemos Click en la propiedad
ConnectionString y nos
aparecerá el botón de puntos
suspensivos. Hacemos Click en
el botón.
5) Al pulsar el botón generar, nos aparece otra ventana en la que tenemos cuatro
pestañas aunque únicamente necesitaremos dos de ellas para crear una cadena de
Prueba satisfactoria
Para seguir un poco el ejemplo que acompaña a este capítulo, vamos a ver el código utilizado
para crear la conexión. En el ejemplo usamos una base de datos Access (No es la mejor para
demostrar como funciona ADO, pero es la que los alumnos van a tener con mayor facilidad.
Una base Oracle o SQL no se instala fácilmente en un ordenador personal)
Ahora nuestro programa ya está en contacto con la base de datos. Lo que falta ya lo puede
suponer: crear un recordset.
Aquí vamos a ver la primera diferencia con DAO. El recordset no lo crea el objeto Connection.
Se crea él a sí mismo. Para que pueda existir un objeto Recordset primero hay que declararlo:
Set MiRecordset = New ADODB.Recordset (Ya está creado. Pero no está abierto, ahora
hay que abrirlo)
Ya tenemos abierto el recordset. En este caso el recordset está formado por todos los registros
con todos sus campos de la tabla Alumnos, que está en la base de datos definida en la
conexión MiConexion, es del tipo Dynamic y el bloqueo de escritura es optimista.
Podríamos elegir ciertos registros, y solamente unos campos. Utilizaríamos una sentencia SQL
Hagamos una pequeña pausa. Observe que hay diferencias de trabajar con ADO o hacerlo con
DAO.
Con DAO.
Declarar MiRecordset
Dim MiRecordset As ADODB.Recordset
Crear el Recordset
Set MiRecordset = New ADODB.Recordset
Abrir el Recordset
MiRecordset.Open "Alumnos", "Provider=Microsoft.Jet.OLEDB.4.0;Data
Source=C:\GuiaDelEstudiante\ADO\PruebaADO.mdb;Persist Security Info=False",
adOpenDynamic, adLockOptimistic
Observe siempre que tanto en las declaraciones del objeto Connection y del objeto Recordset,
como en su creación, debemos anteponer siempre la palabra ADODB
MiRecordset.Close
MiSesion.Close (Recuerde que debe cerrar antes el recordset que la conexión)
Cuando resumíamos en la página anterior el código a usar si usábamos DAO o ADO, parecía
(aunque no fuese cierto) que podíamos asimilar estos objetos
DAO ADO
Recordset Recordset
DataBase Connection
WorkSpace ¿
Efectivamente el Workspace no tiene algo que pudiésemos decir “equivalente” en ADO. Esto
es debido a que ADO ya considera que el acceso desde los usuarios lo gestiona directamente
la base de datos. Recuerde que el Workspace en DAO era una “sesión de trabajo” de la base
de datos, donde podíamos asociar un Workspace a cada usuario. ADO ya le pasa a la BD el
nombre del usuario y su Password para que la propia base de datos quien autorice a ese
usuario.
Los recordsets abiertos mediante una consulta SQL (aquellos en los que seleccionamos parte
de los registros mediante Select … Where van a crearse solo de lectura. Independientemente
de los parámetros que le pasemos a la hora de crearlos. La descripción del error va a ser algo
así como que el cursor es solamente de lectura. También observará con frecuencia, cuando
intente ir a un registro anterior al actual, MiRecordset.MoveFirst, p.e.) que le enviará un error
diciendo que el cursor es solamente de avance hacia a delante. Estos efectos los observará
también cuando use la instrucción SQL Order By. No hay que arrojar la toalla solamente por
esto. Cuando eso ocurra, lo que hay que hacer es volver a crear el recordset de forma que
acepte escritura y movimiento hacia atrás. Eso generalmente se logra abriendo el recordset
con todos los registros de la tabla, e incluyendo en él todos los campos.
En vez de :
MiRecordset.Open "Select Alumno_Nombre, Alumno_Ape1, Alumno_Ape2 From Alumnos " _
& “Where Alumno_Nombre = ‘Luis’”, MiConexion, adOpenDynamic, adLockOptimistic
Ahora ya nos permitirá usar el método AddNew. Basta con añadir las líneas de código
adecuado para introducir los valores en cada campo:
Mirecordset.AddNew
MiRecordset!Alumno_ID = Valor numérico
MiRecordset!Alumno_Nombre = Valor string
Etc.
….
MiRecordset.Update
No renuncie nunca a intentar hacer esto. No siempre funciona. Pero esto es mucho más
sencillo que introducir los datos “a capón” utilizando la instrucción SQL Insert que ya veremos
más adelante. SQLServer es bastante más dócil que Oracle para usar el método AddNew.
A este respecto, hay que citar la propiedad CursorLocation que veremos más adelante. Si
creamos un cursor lado cliente, seguramente nos permitirá utilizar más opciones (entre ellas
AddNew) que si lo establecemos de lado servidor. Tenga en cuenta que si usa un cursor lado
cliente, el recordset no se actualiza cuando otro usuario realiza modificaciones en la base.
En ADO no hace falta usar Edit para modificar los datos del registro actual. Edit no existe en
ADO. Para modificar un dato basta con poner el mismo código que utilizábamos en DAO, pero
sin comenzar por la línea Mirecordset.Edit. Colocándonos en el registro a modificar pondremos:
MiRecordset!Alumno_Nombre = “Pedro”
MiRecordset!Alumno_Ape1 = “Perez”
MiRecordset.Update
De cualquier forma, esto tampoco tiene porque funcionar en todas las bases de datos. En ese
caso tendremos que recurrir, al igual que para un registro nuevo, a las instrucciones SQL, que
en este caso será Update. Pero ya lo veremos más adelante. Hasta ahora solamente he
querido usar el código más simple para que pueda empezar con ADO, y sobre todo, para que
vea que esta es una técnica completamente accesible, aunque, dadas las grandes
prestaciones que tiene, un poco más adelante se va a complicar algo.
Ya ha visto que podemos trabajar perfectamente con recordsets, tal como lo hemos hecho con
el DAO de toda la vida. Pero ya lo comentábamos al principio, parece que hay que adornar lo
obvio para que no lo parezca tanto. Y ahí viene el Objeto Command, que como habrá podido
ver en la figura de la Jerarquía de los objetos ADO, es un objeto que aún no sabemos lo que
hace.
MiRecordset.AddNew
ConexBDPrensa.Execute StrIntroducir
Con la instrucción anterior, lo que hacíamos era añadir un registro. Si lo que queremos hacer
es crear un recordset, la instrucción SQL comenzaría por “Select * From ….” Y el método
EXECUTE devolvería un Recordset
Podemos crear un recordset (MiRecordset, previamente declarado como tal) con esta
instrucción
Ahora, tras la explicación informal de cómo se crea una conexión y un recordset en ADO, y que
es un Command, vamos a entrar a conocer sus propiedades y métodos. Al final, y con un
buen ejemplo realizado de varias formas, entenderá perfectamente la forma de trabajar con
ADO.
visto como crear un objeto Connection, por lo que vamos a pasar directamente a ver sus
objetos, colecciones, propiedades y métodos.
Como puede verse en la figura, el Objeto Connection tienen los objetos Command y Recordset,
y la colección Errors.
Propiedades del Objeto Connection (Lea esto de las propiedades sin complicarse
demasiado la vida. Las realmente importantes verá que están advertidas debidamente)
Las propiedades del objeto Connection dependen de cada proveedor. No todos se comportan
de igual forma, por lo que cada propiedad debe condicionarse a si el proveedor es capaz de
ofrecerla.
Propiedad Attributes
adXactCommitRetaining
adXactAbortRetaining
La cadena de conexión tiene varios argumentos, todos ellos separados por un punto y coma (;)
de la forma
argumento1 = valor; argumento2 = valor; argumento3 = valor etc.
ADO procesa solamente cuatro argumentos: Provider, File Name, Remote Provider y Remote
Server. Los demás argumentos los pasa al proveedor para que el los procese (Usuario,
Password, etc)
Los cursores son, por decirlo de alguna manera, los mecanismos de la base de datos donde se
crean los recordsets. Se estará dando cuenta que las bases de datos que tienen cursores son
ya bases de datos con mecanismos propios para la creación de recordsets. (SQL Server u
Oracle, p.e.) Estas bases de datos trabajan como aplicaciones cliente – servidor. Tienen en el
servidor, aparte de los datos, la mayoría de sus recursos. En el cliente tienen prácticamente los
recursos de conectividad, y poco más. Esta conectividad permite enviar desde el cliente una
petición a la base de datos alojada en el servidor. Esa petición será el resultado por ejemplo,
de una sentencia SQL. Al recibirla el servidor, gestionará la obtención de los resultados y una
vez que los haya conseguido viene su primera duda: ¿Dónde los almaceno? Puede
almacenarlos en el servidor, y cada vez que necesitemos un nuevo registro, por ejemplo al
ejecutar la sentencia MiRecordset.MoveNext, el cliente se lo indica al servidor y este le envía el
nuevo registro. También puede almacenar los datos obtenidos en el cliente, y de esta forma el
cliente puede moverse con entera libertad a lo largo de los registros del recordset. Puede hasta
contarlos y saber en que posición está. La diferencia entre uno y otro sistema es que en el
primer caso el tráfico por la red es mínimo (solamente se envía la información estrictamente
necesaria) y en el segundo caso, se envía mucha información de un golpe, la correspondiente
a todos los registros del recordset, independientemente de si se va a usar en el servidor o no.
Considerando el tráfico generado, parece que es mejor crear los cursores en el lado servidor.
Pero esto trae también sus dificultades. Nos priva de muchas propiedades del recordset. Una
propiedad muy usada, AbsolutePosition, no la tienen los cursores de lado servidor,
circunstancia que no nos debe sorprender, ya que al no estar todos los registros en el cliente,
éste, aunque disponga de todos los datos guardados en todos los campos de un determinado
registro, no puede saber que posición ocupa ese registro dentro de la totalidad de registros del
recordset. Por lo tanto, usar cursores de lado cliente o lado servidor será siempre una decisión
a tomar dependiendo de la velocidad de la red, de la memoria disponible en el cliente, de la
complejidad del programa, etc.
Recuerde que por defecto, ADO crea cursores de lado servidor. Para que la conexión le cree
todos los cursores de lado cliente basta ejecutar la instrucción:
MiConexion.CursorLocation = adUseClient
Este valor debe establecerlo antes de crear la conexión. De esta forma, todos los cursores
creados con esa conexión serán de lado cliente o lado servidor, según haya elegido. Pero
muchas veces sería ideal que unos recordsets tuvieran el cursor de un tipo y otros de otro. Los
recordsets también tienen esta propiedad, por lo que puede elegir el lado deseado para cada
recordset utilizando esta propiedad aplicada no a la conexión, sino al recordset.
El número de registros que el servidor envía al cliente, cuando el cursor es de lado servidor, se
controla mediante la propiedad CacheSize del recordset. Lo verá más adelante.
Propiedad DefaultDatabase
Establece la base de datos predeterminada para un objeto Connection. Es un string con el
nombre de la base de datos por defecto de esa conexión. Solamente es válida con aquellos
proveedores que permiten varias bases de datos por conexión.
IsolationLevel
Esta propiedad afecta al comportamiento de un objeto Connection durante una transacción.
Una vez establecida esta propiedad, solamente será efectiva cuando se invoque el método
BeguinTrans. Vea la ayuda para mayor información de los valores posibles.
Propiedad Mode,
Establece los permisos disponibles para modificar datos en un objeto Connection.
AdModeShareDenyNone Impide que otros abran una conexión con cualquier tipo de
permiso.
Sólo puede establecer la propiedad Mode cuando el objeto Connection está cerrado. Cuando
se usa en un objeto Connection del lado del cliente, la propiedad Mode sólo se puede
establecer a adModeUnknown.
Propiedad Provider
Es un string que indica el nombre del proveedor de un objeto Connection. Si no se especifica
ningún proveedor, la propiedad tendrá el valor predeterminado MSDASQL (Proveedor de
Microsoft OLE DB para ODBC).
Propiedad State
Devuelve el estado del objeto Connection: abierto (adStateOpen = 1) o cerrado (adStateClosed
= 0).
Método Cancel
Cancela la ejecución de una llamada asíncrona pendiente a un método Execute u Open.
Sintaxis NombreConection.Cancel
El método Cancel se utiliza para terminar la ejecución de una llamada asíncrona a un método
Execute o Open (es decir, el método fue invocado con la opción adAsyncConnect,
adAsyncExecute o adAsyncFetch). Cancel devolverá un error de ejecución si no se utilizó
adAsyncExecute en el método que quiere terminar.
Close, método
Cierra el objeto Connection.
Sintaxis NombredelObjetoConnection.Close
Constante Descripción
Indica que el proveedor tiene que evaluar CommandText como
adCmdText
definición textual de un comando, como una instrucción SQL.
Indica que ADO tiene que generar una consulta SQL para devolver
adCmdTable
todas las filas de la tabla mencionada en CommandText.
Indica que el proveedor tiene que devolver todas las filas de la tabla
adCmdTableDirect
mencionada en CommandText.
Indica que ADO tiene que generar una consulta SQL para devolver
adCmdTable
todas las filas de la tabla mencionada en CommandText.
Indica que el proveedor tiene que evaluar CommandText como
adCmdStoredProc
procedimiento almacenado.
adCmdUnknown Indica que el tipo de comando en CommandText es desconocido.
adAsyncExecute Indica que el comando se tiene que ejecutar de forma asíncrona.
Indica que el resto de las filas siguientes a la cantidad inicial
adAsyncFetch especificada en la propiedad CacheSize tiene que ser recuperada
de forma asíncrona.
MiConnection.Open
Cuando se usa un objeto Connection del lado del cliente, el método Open no establece
realmente una conexión con el servidor hasta que se abre un Recordset del objeto Connection.
Colecciones del Objeto Connection
Colección Errors
Contiene todos los objetos Error creados en respuesta a un único fallo relacionado con el
proveedor. Cada objeto Error representa un error específico del proveedor, no un error ADO.
Los errores ADO se exponen al mecanismo de control de excepciones en tiempo de ejecución.
Cuando otra operación ADO genera un error, se borra la colección Errors y el nuevo conjunto
de objetos Error se coloca en la colección Errors. Las operaciones ADO que no generan un
error no tienen ningún efecto sobre la colección Errors. Para borrar manualmente la colección
Errors utilice el método Clear.
Cada objeto Error de la colección, nos dará información sobre un error producido en la
conexión. Sus propiedades son las siguientes:
• Description: Contiene la descripción del error producido (String).
• NativeError: Indica el error devuelto por la base de datos (Long).
• Number: Es el código de error (Long).
• Source: Indica el nombre del objeto o aplicación que ha generado el error (String).
• SQLState: Indica el error SQL estándar asociado.(String).
Debido a que este objeto será de gran utilidad a continuación se incluye un ejemplo de su uso.
Ejemplo del tratamiento de errores:
El resultado de la ejecución es el siguiente:
Private Sub CBConexion_Click()
Dim MiConex1 As ADODB.Connection
Dim MiError As ADODB.Error
On Error GoTo RutErr
Set MiConex1 = New ADODB.Connection
MiConex1.ConnectionString = "Provider=MSDAORA.1;Password=tiger;User
ID=EScot;Data Source=MADRID;Persist Security Info=True"
MiConex1.Open
Exit Sub
RutErr:
For Each MiError In MiConex1.Errors
MsgBox ("Error VB:" & MiError.Number & vbCrLf _
& "Error Oracle:" & MiError.NativeError & vbCrLf _
& "Error SQL:" & MiError.SQLState & vbCrLf _
& "Generado Por:" & MiError.Source & vbCrLf _
& "Descripción:" & MiError.Description)
Next
End Sub
Como puede observarse, algunas de las propiedades no devuelven los valores esperados:
NativeError vale 0 cuando debería valer –1017, que es el error asociado de Oracle. Aún así
podemos capturar el código del error desde la cadena de caracteres description. Con este
comentario se pretende sugerir que se realicen pruebas con el proveedor de datos que
vayamos a utilizar para conocer su comportamiento respecto a este objeto error y dónde nos
devuelve los códigos.
Colección Properties
Es el conjunto de objetos Property
LSB Visual Basic – Guía del Estudiante Capítulo 1 Página 424
Objeto Property
Un objeto Property representa una característica dinámica de un objeto ADO que está definida
por el proveedor.
Las propiedades dinámicas están definidas por el proveedor de los datos y aparecen en la
colección Properties del objeto ADO apropiado. Por ejemplo, una propiedad específica del
proveedor puede indicar si un objeto Recordset acepta transacciones o actualizaciones. Estas
propiedades adicionales aparecerán como objetos Property en la colección Propiedades de
dicho objeto Recordset. A las propiedades dinámicas se les puede hacer referencia sólo a
través de la colección, utilizando la sintaxis Objeto.Properties(0) u Objeto.Properties("Name").
Propiedad Bookmark
Devuelve un marcador que identifica unívocamente al registro actual de un objeto Recordset o
establece el registro actual de un objeto Recordset al registro identificado por un marcador
válido. El valor del Bookmark hay que introducirlo en una variable tipo Variant.
Propiedad CacheSize
Indica el número de registros del objeto Recordset que están en la memoria caché local. Es un
Long de lectura y escritura. El valor predeterminado es 1.
Esta propiedad nos permite conocer cuantos registros están en la memoria local. Si ponemos
esta propiedad por ejemplo a 10, al abrir la primera vez el objeto Recordset, el proveedor
recupera los 10 primeros registros en la memoria local. A medida que se desplaza por el objeto
Recordset, el proveedor devuelve los datos desde el búfer de memoria local. Tan pronto como
pasa del último registro de la memoria caché, el proveedor recupera los 10 registros siguientes
desde el origen de datos y los carga en la memoria caché.
Los registros recuperados desde la memoria caché no reflejan los cambios concurrentes que
hagan otros usuarios en el origen de datos. Para forzar una actualización de todos los datos en
la memoria caché, debe usarse el método Resync.
Propiedad MaxRecords
Indica el número máximo de registros que se devuelven a un Recordset desde una consulta.
Es un Long y el valor predeterminado es 0, que significa sin límite (Obtienen todos los
registros). Esta propiedad es de lectura y escritura cuando el recordset está cerrado, y
solamente de lectura cuando está abierto.
Esta propiedad debe usarse solamente cuando se prevé que se pueden obtener un número
muy grande de registros. Es prudente tomar medidas frente a aquellas operaciones en las que
el ordenador pueda meterse en un proceso excesivamente largo o que ocupe más recursos de
los disponibles. Estas cosas son las que suelen “colgar” al ordenador y en las que es muy
frecuente echarle luego la culpa a Windows Si estamos frente a una base de datos con todos
los datos de los afiliados a la Seguridad Social, no es del todo prudente crear un recordset
mediante esta sentencia:
A nadie se le ocurriría, pero puede surgir involuntariamente cuando ese acceso se realiza
desde un puesto de operación público (Acceso desde Internet por ejemplo). Con la propiedad
MaxRecords puede limitar el número de registros obtenidos. Posiblemente no llegue a obtener
el dato deseado, y se vea en la obligación de realizar la misma consulta sucesivas veces hasta
encontrarlo.
Propiedad CursorType
Indica el tipo de cursor que se usa en un objeto Recordset. Es de lectura y escritura si el
recordset está cerrado, y solo de lectura si está abierto. Puede tomar uno de los siguientes
valores:
Propiedad LockType
Indica el tipo de bloqueo que se pone en los registros durante el proceso de edición. Establece
también si el recordset se actualiza registro a registro o por lotes. Este último sistema permite
realizar varios cambios en el recordset y mantenerlos en la caché durante cierto tiempo, y
proceder a la actualización de todos los cambios pendientes en una sola operación. Esto es
muy importante cuando se está trabajando con una base de datos situada en un servidor con
un acceso lento (conexión vía Internet, por ejemplo)
Propiedad EditMode
Indica el estado de modificación del registro actual. Es solamente de lectura. Devuelve uno de
las siguientes constantes:
Propiedad Filter
Esta propiedad solamente la tienen los recordset ADO. Y es que ADO presupone que está
obteniendo datos de una base de datos alojada en un servidor y que la comunicación entre
servidor y cliente puede ser especialmente lenta. Mediante Filter puede descartar registros que
no cumplan una determinada condición. En realidad lo que hacemos mediante Filter es crear
un nuevo Recordset a partir de otro Recordset.
La propiedad Filter se utiliza también para actuar sobre determinados registros en varios
métodos del recordset (Resync, Save, …) pero no se va a explicar en este manual la
explicación de la utilización de esta propiedad en esos métodos, ya que el nivel del
programador que utiliza esos recursos debe ser elevado. Le reservamos por tanto la posibilidad
de conocerlo directamente desde la ayuda de Visual Basic, que en este caso es bastante
buena.
De momento lo que vamos a hacer con Filter es crear un nuevo recordset ¿Por qué no
creamos directamente el nuevo recordset utilizando una sentencia SQL que lleve implícito ese
filtro?
Personalmente no me gusta utilizar la propiedad Filter, y prefiero crear un nuevo recordset.
Creo que solamente lo he usado para rellenar un MSHFlexGrid con parte de los datos del
recordset que uso en una parte de la aplicación. Veamos un ejemplo:
‘Este es el recordset que uso para muchas cosas dentro de la aplicación. Ahora quiero
presentar en un MSHFlexGrid solamente los datos de los autores de nacionalidad española.
Filtro el recordset anterior utilizando un criterio de igualdad de un campo: Nacionalidad
‘Y cuando queremos presentar todos los autores, basta con poner la línea:
La propiedad Filter puede mostrar también aquellos registros que han sido manipulados
recientemente, mediante las constantes siguientes:
Propiedad Index
Indica el nombre del índice que se utiliza actualmente en el Recordset. Es un String con el
nombre del índice.
El índice se utiliza para moverse a lo largo del recordset mediante el método Move. El índice ya
debe estar creado en la tabla de la base de datos.
Al utilizar un índice, el orden de los registros se cambia al orden establecido en ese índice. Por
lo tanto, los valores obtenidos anteriormente por la propiedad AbsolutePosition cambiarán
completamente.
No todos los proveedores de datos aceptan la propiedad Index. Puede comprobarlo mediante
el método Supports.
Propiedad MarshalOptions
Esta propiedad se usa cuando estamos trabajando con cursores lado cliente. En este caso,
como vimos más atrás, los registros del recordset están en el equipo cliente. Todas las
operaciones realizadas sobre el recordset se realizan en el cliente, por lo tanto llegará el
momento en que habrá que actualizar en el servidor los datos que hayamos modificado en el
recordset que está en el equipo cliente. Mediante esta propiedad podemos hacer que se envíen
al servidor todos los registros (filas) o solamente los que han cambiado. Acepta estas dos
constantes:
Esta propiedad puede mejorar el rendimiento de la aplicación para aquellos casos en los que
se use un canal de comunicación lento.
Propiedad PageSize
Indica cuántos registros constituyen una página del objeto Recordset. Es de lectura y escritura.
Devuelve un Long y su valor predeterminado es 10.
Esta propiedad permite determinar cuántos registros componen una página lógica de datos. Al
establecer un tamaño de página, puede utilizar la propiedad AbsolutePage y se moverá al
primer registro de una página específica. Esto es útil en las situaciones de servidor Web
cuando se desea permitir que el usuario pase páginas de datos y vea cierto número de
Propiedad PageCount
Indica cuántas páginas de datos contiene el objeto Recordset. Es solamente de lectura.
Devuelve un Long. Si el objeto Recordset no admite esta propiedad, el valor será -1 para
indicar que no se puede determinar el valor de PageCount.
Propiedad AbsolutePage
Especifica en qué página reside el registro actual. Es de lectura y escritura. Devuelve un Long
o una de las siguientes constantes:
AdPosBOF El puntero del registro actual está al comienzo del archivo (es decir, la
propiedad BOF tiene el valor True).
AdPosEOF El puntero del registro actual está al final del archivo (es decir, la
propiedad EOF tiene el valor True).
Propiedad RecordCount
Indica el número actual de registros de un objeto Recordset. Devuelve un Long
Es posible que esta propiedad no la permita el proveedor de datos, o que no pueda llegar a
averiguarse, ya que dependiendo del tipo de cursor utilizado puede que no suministre ese dato.
Propiedad Source
Devuelve la tabla, consulta o sentencia SQL utilizado en el método Open para crear el
recordset. Es un String.
Propiedad State
Indica el estado (abierto, cerrado, proceso de ejecución) en el que se encuentra el recordset.
Devuelve uno de los siguientes valores:
Constante Descripción
Valor predeterminado. Indica que el objeto está
adStateClosed
cerrado.
adStateOpen Indica que el objeto está abierto.
Indica que el objeto Recordset se está
adStateConnecting
conectando.
Indica que el objeto Recordset está ejecutando
adStateExecuting
un comando.
Indica que se está obteniendo el conjunto de filas
adStateFetching
del objeto Recordset.
Puede tener una combinación de valores. Por ejemplo, si se está ejecutando una instrucción,
esta propiedad tendrá un valor combinado de adStateOpen y adStateExecuting.
Propiedad Status
Esta propiedad se refiere al registro actual. Indica el estado de este registro respecto a las
operaciones de actualización por lotes u otras operaciones masivas. No es una propiedad que
se use todos los días. Vea la ayuda para mas detalles.
Unique Table especifica la tabla sobre la que se permite realizar modificaciones de datos
(Insertar o actualizar), en el caso de que el recordset se haya creado mediante una operación
JOIN
Estas funcionalidades se le pasan como parámetro mediante una de las siguientes constantes:
Nota Aunque el método Supports puede devolver True para una funcionalidad determinada,
eso no garantiza que el proveedor pueda hacer que la característica esté disponible bajo
cualquier circunstancia. El método Supports devuelve simplemente si el proveedor puede
admitir la funcionalidad especificada, dando por supuesto que se reúnen determinadas
condiciones. Por ejemplo, el método Supports puede indicar que un objeto Recordset admite
El valor devuelto por el método Supports dependerá del tipo de recordset elegido. En la
siguiente tabla puede ver el tipo de recordset y las constantes para las que va a devolver True:
AdOpenForwardOnly Ninguna
AdOpenKeyset adBookmark, adHoldRecords, adMovePrevious, adResync
AdOpenDynamic AdMovePrevious
AdOpenStatic adBookmark, adHoldRecords, adMovePrevious, adResync
Método Open
Es el método que ABRE el recordset. Este método es el que busca los registros que han de
rellenar el recordset.
Un recordset puede abrirse partiendo de una conexión ya abierta. Pero también puede crearse
directamente, sin abrir previamente la conexión. Al final, deberá aportar todos los datos
necesarios para determinar en que base de datos se abre ese recordset. Sobre que tabla,
consulta o sentencia SQL., que tipo de cursor va a ser, etc. Lo que ocurre es que ADO es muy
flexible y nos permite hacerlo de varias formas, aunque todas ellas conducen a lo mismo.
Source (Opcional) Es el nombre de una tabla, consulta o sentencia SQL de la cual se obtienen
los registros del Recordset.
CursorType (Opcional). Un valor que determina el tipo de cursor que el proveedor debe usar
al abrir el Recordset. Puede ser una de las siguientes constantes
LockType (Opcional). Un valor que determina el tipo de bloqueo que debe usar el proveedor al
abrir el Recordset. Con este parámetro se le indica también si debe hacer las actualizaciones
registro a registro o en bloque. Puede ser una de las siguientes constantes
Habrá observado que hemos remarcado que el método Open ABRE un recordset. No lo crea.
El recordset debe estar creado previamente. ¿Qué cuando se crea? Mediante la instrucción:
Set MiRecordset = New ADODB.Recordset
Metodo Requery
Actualiza los datos de un objeto Recordset volviendo a ejecutar la consulta utilizada para
abrirlo. Se obtiene el mismo resultado que si se cerrara el recordset y se volviera a abrir.
Optiones Opcional. Máscara de bits que indica opciones que afectan a esta operación. Si el
valor de este parámetro está establecido a adAsyncExecute, esta operación se ejecutará de
forma asíncrona y se emitirá un evento RecordsetChangeComplete cuando concluya.
Método Resync
Actualiza los datos del objeto Recordset actual. Este método, a diferencia del método
Requery, no vuelve a ejecutar el comando de creación del recordset, sino que lee los registros
existentes en el recordset para actualizar su valor, pero no presenta aquellos registros que
hubieran sido creados con posterioridad a la apertura del recordset. Es más rápido que el
método Requery, y en muchos casos solamente nos interesa actualizar los registros sobre los
que estamos trabajando.
METODO AddNew
Crea un nuevo registro en un objeto Recordset actualizable.
Los parámetros FieldList y Values son opcionales. En caso de ponerlos, FieldList serán los
nombres de los campos a los que se les va a poner un valor, y Values son los valores de cada
uno de estos campos. El orden nombre - valor debe mantenerse estrictamente.
NombreDelRecordset!NombredelCampo = ValorDelCampo
ó
NombreDelRecordset(“NombredelCampo”) = ValorDelCampo
Esta segunda forma es necesaria cuando el nombre del campo tiene espacios. Nunca es
recomendable poner espacios en los nombres de campos, pero en caso de que existan, debe
optar por utilizar la sintaxis segunda, con los paréntesis y comillas dobles.
No siempre se puede utilizar el método AddNew. Puede comprobar si se puede utilizar, usando
el método Supports visto anteriormente.
El método AddNew convierte a este registro recién creado en registro actual. Pero este registro
solamente existe en el recordset. Para introducir los datos en la base de datos, (Y por lo tanto
en el disco duro) es necesario invocar el método Update una vez introducidos todos los valores
de los campos que deseamos introducir. Con algún tipo de cursor es necesario también utilizar
el método Requery para poder acceder al registro recién creado.
Si se invoca el método AddNew mientras se está editando el registro actual, o durante otra
operación AddNew, ADO invoca automáticamente el método Update para guardar los
cambios. Vea más adelante los métodos Update y UpdateBatch.
Método Update
Guarda los cambios realizados en el registro actual de un objeto Recordset. Se utiliza tanto
para “rematar” una operación de creación de un registro iniciada con AddNew, como para
guardar los nuevos datos del registro actual (Recuerde que en ADO no existe el método Edit,
tal como ocurría en DAO)
.
Sintaxis NombreDelRecordset.Update Fields, Values
En esta sintaxis, Fields y Values son opcionales, y solamente tienen aplicación cuando se trata
de cambiar los valores del registro actual, no de terminar una operación de creación de un
nuevo registro mediante AddNew.
Vuelvo a recomendar lo anterior. Para cambiar los valores de varios campos de un registro,
nos colocaremos sobre ese registro, y sin invocar ningún método, ejecutaremos este código
MiRecordset!Campo1 = Valor1
MiRecordset!Campo2 = Valor2
……….
MiRecordset!CampoN = ValorN
MiRecordset.Update
Pero en ADO pasa una cosa que no pasaba en DAO. Si cambiamos de registro una vez
modificado el valor de un registro, ADO invoca automáticamente el método Update. Por lo
tanto, el código siguiente:
MiRecordset!Campo1 = Valor1
MiRecordset!Campo2 = Valor2
……….
MiRecordset!CampoN = ValorN
Tendrá el mismo resultado que el anterior cuando cambiemos de registro actual. Esto puede
ser bueno a malo, pero personalmente pienso que no es práctico porque implica tener mucho
más cuidado que en DAO. Hace lo mismo que cuando tenemos unos controles enlazados a
datos mediante un control Data.
MiRecordset!Campo1 = Valor1
Queremos que ese nuevo valor no entre en la base de datos, debemos cancelarlo mediante el
método CancelUpdate.
Método UpdateBatch
Escribe en disco todas las actualizaciones pendientes de proceso por lotes. En ADO un
recordset puede actualizarse registro a registro o por lotes. Todo dependerá de cómo se ha
abierto (Si el valor LockType se ha puesto a AdLockBatchOptimistic)
Método Cancel
Cancela la ejecución del método Open.
Sintaxis NombreDelRecordset.Cancel
El método Cancel solamente puede usarse si el método Open fue invocado con la opción
adAsyncConnect, adAsyncExecute o adAsyncFetch.
Método Delete
Elimina el registro actual o un grupo de registros. Por defecto elimina solamente el registro
actual. Vea la ayuda para ampliar los detalles respecto al parámetro opcional AffectRecords
Método CancelUpdate
Habíamos visto más atrás que el ADO no existe el método Edit. Para modificar un registro
basta con poner una instrucción tal como esta:
MiRecordset!MiCampo = MiNuevoValor
MiRecordset.Update
Sintaxis MiRecordset.CancelUpdate
Recuerde que ADO funciona de forma distinta a DAO con estos métodos de modificación de
los registros. Recuerde que si está en proceso de modificación de un registro (Ha ejecutado la
Método CancelBatch
Cancela una actualización por lotes pendiente. Es similar a la anterior, pero para actualización
por lotes.
Método Clone
Crea un objeto Recordset duplicado a partir de un objeto Recordset existente. Opcionalmente,
puede especificarse que el nuevo recordset sea solamente de lectura
(El objeto rstDuplicate debe estar declarado previamente como objeto ADODB.Recordset)
Método Move
Mueve la posición del registro actual de un objeto Recordset
.
Sintaxis MiRrecordset.Move NumRecords, Start
NumRecords Un valor Long con signo que especifica el número de registros que debe
moverse a partir de la posición del registro actual o del registro especificado en el parámetro
Start, si es que se especifica.
Start Opcional. Un String o Variant cuyo resultado sea un marcador del tipo Bookmark.
Puede utilizar también una de las siguientes constantes:
Tenga en cuenta a la hora de usar los métodos Move que pueden existir Recordsets que no
permiten el movimiento hacia atrás.
Método Find
Busca el primer registro del recordset que satisfaga los criterios especificados en el criterio de
búsqueda. Si se cumple el criterio de búsqueda, la posición del recordset se establece en el
primer registro encontrado; si no, la posición se establece al final del recordset.
start (Opcional) Un marcador tipo BookMark que se utiliza como posición inicial de
la búsqueda.
El operador de comparación de criterio puede ser ">", "<", "=", ">=" , "<=", "<>" (distinto de) o
"like" (coincidencia parcial de cadenas). El valor de comparación puede ser una cadena, un
número en coma flotante o una fecha. Los valores de cadena están delimitados con comillas
sencillas (por ejemplo, "Pais = 'España'"). Los valores de fecha están delimitados con signos
"#" y con formato mm/dd/yy (por ejemplo, "fecha_inicial > #7/22/97#"). Si el operador de
comparación es "like", el valor de la cadena puede contener los caracteres comodín "*" o "_".
(Funcionan de forma idéntica, sustituyendo a cualquier sucesión de caracteres.) No acepta el
carácter “?” (Por ejemplo, "Pais like ‘Es_’" o “Pais Like ‘Es*’ encuentra España y Estonia)
ADO no tiene los métodos FindFirst, FindNext, FindPrevious y FindLast. Deberá emplear el
método Find de forma inteligente para implementar estos otros.
Método Save
Este método permite guardar el contenido de un Recordset en un fichero. Puede ser muy útil
cuando queremos exportar ese recordset hacia otra aplicación.
Si ya existe el fichero le dará un error. Cerciórese que no existe (Mediante la función Dir) y
bórrelo antes de volver a utilizar el método Save.
Para poder usar el método Index es necesario que el proveedor acepte índices en el objeto
recordset. Esto ha llevado al autor a no poder presentar ningún ejemplo de este método, ya
que todas las bases de datos ensayadas no permitían índices. Puede ver si acepta índices
mediante el método Supports:
Método NextRecordset
Para entender este método es necesario explicar previamente como se puede crear un
recordset compuesto.
MiRecordset es un recordset compuesto. Toma datos de dos tablas distintas, pero podría
haberlo hecho solamente de una tabla con una condición distinta en la SELECT.
Puede usar también este recordset para crear un nuevo recordset a partir del primero
MiRecordset2 debe estar declarado como objeto recordset, y creado con la instrucción New:
Set MiRecordset2 = New ADOBD.Recodset
Vamos a ver aquí un truco que nos permite ADO. Podemos crear un recordset ¡Sin necesidad
de una base de datos!
Cuando estudiamos el objeto recordset vimos que el recordset se crea con una instrucción
como esta:
Set MiRecordset1 = New ADODB.Recordset
Ahora ya podemos abrir el recordset leyendo los datos desde una base de datos. Utilizamos
para ello el método Open del recordset:
Mediante el método Open lo que hace el recordset es conocer su estructura (Que campos
tiene, propiedades de estos campos, etc) y el valor de cada uno de los campos de sus
registros. ¿Qué pasaría si en vez de abrir el recordset le vamos añadiendo objetos Field a su
colección Fields? Lo hacemos mediante el método Append:
Lo que ocurre es que el recordset ya tiene tres campos, el primero de nombre MiCampo1,
numérico Long, el segundo, un string de 25 caracteres de nombre MiCampo2, y el tercero, de
nombre MiCampo3, una cadena de caracteres de longitud indefinida, terminada en un carácter
nulo. Ya tenemos una estructura de un recordset sin necesidad de haber leído la base de
datos. No es necesario por lo tanto que exista una tabla o consulta almacenada con esa
estructura. Ahora podemos abrir el recordset:
MiRecordset1.Open
Lo abrimos sin pasarle ningún parámetro. Ahora ya está abierto y podemos trabajar con el
como con un recordset cualquiera. Recuerde que al principio está vacío.
MiRecordset1.AddNew
MiRecordset1!MiCampo1 = 34
MiRecordset1!MiCampo2 = "Guía del Estudiante"
MiRecordset1!MiCampo3 = "Hola mi amor" & vbCrLf & "Yo soy tu lobo"
MiRecordset1.Update
Ya tenemos un registro dentro del recordset. Podemos introducirle tantos registros como
queramos, y luego movernos por el recordset mediante los métodos Movexxxx. Ahora
podremos leer el contenido del registro actual:
Label1 = MiRecordset1!MiCampo1
Label2 = MiRecordset1!MiCampo2
Label3 = MiRecordset1!MiCampo3
El resultado del código de el ejemplo se traducirá en algo como esto:
Y ahora vienen lo mejor. ¿Para que queremos un recordset que no tienen datos leídos desde
una base de datos? Las aplicaciones de esto solamente está limitadas por la imaginación.
LSB Visual Basic – Guía del Estudiante Capítulo 1 Página 439
Pienso, por ejemplo, en introducir en un recordset todos los datos leídos de un fichero de
configuración, y así tener todos esos datos disponibles durante toda la aplicación, en vez de en
variables, como MiRecordset!MiDato1
Es una idea, pero piense que esto no regala nada. El espacio de memoria consumido será
similar a si usa variables. Pero posiblemente estará más cómodo y más inteligible su código.
Método Append
Agrega un campo a una colección Fields de un Recoprdset.
Name Un String con el nombre del nuevo objeto Field, que tiene que ser único dentro de la
coleccón Fields.
Type Tipo de datos que va a contener ese campo. Puede tomar uno de estos valores:
Se une en una instrucción OR lógica con otro tipo para indicar que los
adArray
datos son una matriz segura de ese tipo (DBTYPE_ARRAY).
adBigInt Un entero con signo de 8 bytes (DBTYPE_I8).
adBinary Un valor binario (DBTYPE_BYTES).
adBoolean Un valor Boolean (DBTYPE_BOOL).
Se une en una instrucción OR lógica con otro tipo para indicar que los
adByRef
datos son un puntero a los datos del otro tipo (DBTYPE_BYREF).
Una cadena de caracteres terminada en nulo (Unicode)
adBSTR
(DBTYPE_BSTR).
adChar Un valor de tipo String (DBTYPE_STR).
Un valor de tipo Currency (DBTYPE_CY). Un valor Currency es un
adCurrency número de coma fija con cuatro dígitos a la derecha del signo decimal.
Se almacena en un entero con signo de 8 bytes en escala de 10.000.
Un valor de tipo Date (DBTYPE_DATE). Un valor Date se almacena
como un valor de tipo Double; la parte entera es el número de días
adDate
transcurridos desde el 30 de diciembre de 1899 y la parte fraccionaria
es la fracción de un día.
adDBDate Un valor de fecha (aaaammdd) (DBTYPE_DBDATE).
adDBTime Un valor de hora (hhmmss) (DBTYPE_DBTIME).
Una marca de fecha y hora (aaaammddhhmmss más una fracción de
adDBTimeStamp
miles de millones) (DBTYPE_DBTIMESTAMP).
Un valor numérico exacto con una precisión y una escala fijas
adDecimal
(DBTYPE_DECIMAL).
adDouble Un valor de coma flotante de doble precisión (DBTYPE_R8).
adEmpty No se ha especificado ningún valor (DBTYPE_EMPTY).
adError Un código de error de 32 bits (DBTYPE_ERROR).
adGUID Un identificador único global (GUID) (DBTYPE_GUID).
Un puntero a una interfaz Idispatch de un objeto OLE
adIDispatch
(DBTYPE_IDISPATCH).
adInteger Un entero firmado de 4 bytes (DBTYPE_I4).
Un puntero a una interfaz Iunknown de un objeto OLE
adIUnknown
(DBTYPE_IUNKNOWN).
adLongVarBinary Un valor binario largo (sólo para el objeto Parameter).
adLongVarChar Un valor largo de tipo String (sólo para el objeto Parameter).
Un valor largo de tipo String terminado en nulo (sólo para el objeto
adLongVarWChar
Parameter).
Un valor numérico exacto con una precisión y una escala exactas
adNumeric
(DBTYPE_NUMERIC).
adSingle Un valor de coma flotante de simple precisión (DBTYPE_R4).
adSmallInt Un entero con signo de 2 bytes (DBTYPE_I2).
adTinyInt Un entero con signo de 1 byte (DBTYPE_I1).
DefinedSize Un Long que define el tamaño del campo si fuese necesario. (Por ejemplo
para un string)
Attrib (Opcional). Especifica los atributos del campo a añadir. Puede tomar estos
valores:
Método Delete
Teóricamente elimina un objeto de la colección Fields.
Field Un Variant que designa el objeto Field que se va a eliminar. Este parámetro tiene
que ser el nombre del objeto Field; no puede ser una posición ordinal o el propio objeto Field.
Comentarios
La llamada al método Fields.Delete en un Recordset abierto provoca un error de ejecución.
(Como ha ocurrido en otras ocasiones, al autor le ha sido imposible ejecutar este método con
los resultados esperados)
Método Refresh
Aunque la colección Fields tiene este método, la verdad es que no tienen efectos visibles sobre
esa colección.
Propiedad Count
Devuelve el número de campos de la colección Fields. Es un Long.
Utilice un objeto Command para consultar una base de datos y obtener registros en un objeto
Recordset, para añadir o eliminar registros, ejecutar una operación de manejo masivo de datos
o para manipular la estructura de una base de datos.
Propiedad ActiveConnection
Indica a qué objeto Connection pertenece actualmente el objeto Command. Es un string con el
nombre de la conexión.
Propiedad CommandText
Contiene el texto del comando que se quiere emitir al proveedor. Es un string. Suele ser una
instrucción SQL, un nombre de tabla o un procedimiento almacenado, o cualquier otra
instrucción que reconozca el proveedor. El valor predeterminado es una cadena vacía.
Propiedad CommandTimeout
Especifica el intervalo de espera para que se ejecute un comando antes de que finalice el
intento y se genere un error. Es un Long que indica en segundos ese tiempo. El valor
predeterminado es 30.
Valores posibles
Propiedad State
Describe el estado del objeto: abierto o cerrado. Devuelve un Long o una constante:
adStateClosed o adStateOpen
Sintaxis
Para un Command que devuelva filas:
Parameters Opcional. Una matriz Variant con los valores de los parámetros pasados con una
instrucción SQL. (Los parámetros de salida no devuelven valores correctos cuando se pasan
en este argumento).
Método Cancel
Cancela la ejecución de una llamada asíncrona pendiente al método Execute
LSB Visual Basic – Guía del Estudiante Capítulo 1 Página 445
Sintaxis NombredelObjetoCommand.Cancel
Utilice el método Cancel para terminar la ejecución de una llamada asíncrona a un método
Execute (es decir, el método fue invocado con la opción adAsyncExecute o adAsyncFetch).
Comandos parametrizados.
Los objetos Command pueden utilizar parámetros. De esta forma, podemos construir una
consulta utilizando la interrogación (?) como comodín. Así cada vez que ejecutemos el
comando, V.B. se encargará de sustituir el comodín por los parámetros asociados a dicho
command. Además podemos utilizar los parámetros para recoger los valores devueltos por un
Procedimiento almacenado en la base de datos. Para entender mejor este tipo de consultas
primero veremos qué es el objeto parameter.
Declaraciones
Dim ConexBDPrensa As ADODB.Connection
Dim RsBDPrensa1 As ADODB.Recordset
Dim StrIntroducir As String, NumeroDocumento as long
Rem Se introduce en una variable tipo String una instrucción para añadir un nuevo registro
Rem Esa instrucción se va a ejecutar desde el objeto Connection mediante su método Execute
Rem Observe que los valores tipo string introducidos (TbXXX) van entre comilla simple
Rem Se ejecuta esa instrucción desde el objeto Connection ConexBDPrensa
ConexBDPrensa.Execute StrIntroducir
ConexBDPrensa.Close
Rem Aquí termina la operación de crear un nuevo registro
MsgBox "Documento enviado"
Este es el código real de una aplicación que introduce los datos el resumen de prensa diario en
una base de datos. Se introduce un nuevo registro en la tabla INT_DOCUMENTOS. El
resumen de prensa es un fichero .Tiff de nombre TbNombFichTIF que se mete en una carpeta
del servidor mediante FTP, y para su loccalización y presentación en una página Web es
necesario introducir su nombre y Path y otros datos en una base de datos. Los campos que se
introducen y los valores de cada uno son:
Campo Valor
Esta forma de introducir los datos, método que los lingüistas especializados en la jerga
informática llaman “a capón”, es la que nunca falla. Exige un poco de código, con muchas
probabilidades de equivocarse, pero muchas veces se prefiere esta forma de meter datos a
utilizar el método AddNew. Tiene la gran ventaja de que la acepta cualquier base de datos,
independientemente de donde esté el cursor. Habrá observado que utilizamos el método
Execute del objeto Connection. Y así funciona perfectamente. Vamos a hacer lo mismo, pero
introduciendo la cadena de caracteres un objeto Command, (Que pertenece al mismo objeto
Connection sobre el que ahora hemos ejecutado el Execute) y vamos a ejecutar el Execute de
ese Command. ¿Se da cuenta que estamos haciendo lo mismo? El nuevo código creado a
partir del anterior es el siguiente:
MiComando.Execute
ConexBDPrensa.Close
ADO le permite hacer las cosas de maneras muy distintas, pero siempre debe aportar la misma
información. En este caso ha visto que el objeto Command no hace falta para nada.
¿Entonces, para que existe? La respuesta es sencilla: facilita la comprensión del código. Pero
Vamos a ver ahora cómo haríamos esto mismo con el método AddNew.
Luego lo creamos:
Set RsBDPrensa = New ADODB.Recordset
ConexBDPrensa.CursorLocation = adUseClient
Abrimos el Recordset:
RsBDPrensa.Open "Int_Documentos", ConexBDPrensa, adOpenDynamic, adLockOptimistic
Cerramos el recordset.
RsBDPrensa.Close
Cerramos la conexión
ConexBDPrensa.Close
Nota 1. – Muchos programadores se rinden cuando ven que no les funciona los métodos
AddNew / Update para introducir nuevos datos. Cierto es que cada base de datos se comporta
de forma distinta respecto a este método. Pero antes de rendirse y usar el código “a capón”
intente ver como tienen la propiedad CursorLocation, el tipo de recordset abierto y el tipo de
bloque elegido (Recuerde que por defecto es de solo lectura). Eso sí, el método “a capón”
funciona siempre (Excepto que sea solo lectura), independientemente del bloqueo y del tipo de
cursor
El control Data para ADO es el “Microsoft ADO data control 6.0 (OLEDB)” que se encuentra
en Proyecto | Componentes. La apariencia es similar a la del Data de DAO
Hagamos un repaso de sus propiedades. Veremos solamente aquellas que son específicas de
ADO:
Propiedad BOFAction.
Establece la forma de proceder cuando llega a la fila anterior a la primera. Toma uno de estos
valores:
Propiedad CommandType
Es idéntica a la misma propiedad del objeto Command. Acepta los valores:
Propiedad ConnectionString
Esta propiedad ya le hemos visto más atrás. Es la cadena de conexión con la base de datos.
Propiedad CursorLocation
Propiedad CursorType
Es la misma que para el objeto Recordset. Acepta los valores:
1 – adOpenKeyset
2 - adOpenDynamic
3 – adOpenStatic
Propiedad EOFAction
Propiedad LockType
Tipo de Bloque. Igual a la misma propiedad del Objeto Recordset
Propiedad MaxRecords
Es similar a la del Objeto Recordset. Establece el número de filas que obtiene en su recordset
asociado.
Propiedad Mode.
Igual a la misma propiedad del objeto Recordset
Propiedades Password
Establece la contraseña para crear la cadena de conexión durante la creación del objeto
Recordset asociado. Esta contraseña es la contraseña del usuario en la base de datos.
Propiedad UserName
Establece el nombre del usuario. Debe ser uno de los usuarios registrados en la base de datos.
Esta propiedad es de lectura y escritura. El valor de la propiedad Password debe ser el
asociado a este usuario.
Propiedad RecordSource
Es una cadena de caracteres con el nombre de una tabla o una sentencia SQL que devuelve
filas.
UpdateControls
Actualiza la información de los controles enlazados a datos. No existe el método
UpdateRecords.
Refresh
Vuelve a construir el recordset. Es idéntico al del control Data de DAO.
Vistas ya las propiedades y métodos del control Adodc, vamos a ver un poco como funciona y
cuales son sus diferencias con el control data de DAO
El Adodc puede enlazar una base de datos a los típicos controles enlazados (Label, TextBox)
usando la tecnología ADO. Los demás controles enlazados no tienen un comportamiento igual
con el Adodc, Control Data o RDODataControl. En la siguiente lista puede ver que controles
trabajan con uno u otro control Data.
Con este capítulo creo que ya tienen conocimientos suficientes para empezar a trabajar con
ADO, y por la tanto, ya tiene permiso para aprender a programar con esta tecnología. Le
recomiendo paciencia, y sobre todo no tener miedo a esta tecnología. Y como se dijo al
principio, úsela siempre que tenga que usar una base de datos instalada en un servidor y
conectada al usuario a través de una red de área local. En el próximo capítulo verá como se
enlazan un cliente con el servidor SQL Server.
De cualquier forma no olvide la tecnología DAO para sus pequeñas aplicaciones, con la base
de datos en el mismo ordenador que la aplicación. Personalmente creo que es mucho más
rápida y más sencilla.
Suerte.
Agradecimientos. En este capítulo participó activamente mi alumno del CEU César Moreno
Fernández.
Una base de datos de esta categoría va a estar instalada con toda seguridad en un servidor, y
accederemos a ella desde otro ordenador, que será el cliente. Puede estar instalada si lo desea en el
mismo ordenador, pero en ese caso deberá tratarlo como si su ordenador trabajase al tiempo como
servidor y como cliente.
La instalación de la base de datos dentro del servidor es bastante compleja, y no se va a explicar en este
curso. Se supone que el suministrador de la BD explicará como hacerlo. La instalación de SQL Server
exige que se haga sobre un PC o Net Server que tenga Windows 2000 Server (o NT Server), sistema
operativo que no se suele instalar en los PCs de cliente y mucho menos en los PC domésticos. Oracle
dispone de una versión con prestaciones algo reducidas que le permite instalarla en un puesto con
Windows 98, en configuración monousuario, circunstancia que permite hacer pruebas de
funcionamiento en PCs particulares, pero que no es la instalación típica que puede existir en una
organización o empresa. Nos referiremos siempre en este capítulo a la instalación convencional, de un
servidor donde se aloja la base de datos, y una serie de puestos clientes conectados a él mediante Red
de Area Local.
En la base de datos es necesario introducir los nombres de usuario que están permitidos, y la clave de
acceso para cada usuario. Lógicamente para que un puesto pueda acceder a la BD debe conocer su
nombre de usuario y la clave de acceso. Estos datos normalmente se introducen en el cliente en su
configuración, y no es necesario teclearlas cada vez que se inicia una sesión.
El servidor tiene un programa que es el Gestor de la Base de Datos. La base de datos propiamente
dicha estará formada por uno o más ficheros con los datos que tienen introducidos y la estructura de
tablas, consultas e índices que tenga. El programa gestor es quien controla estos ficheros y puede
obtener de ellos los datos en una forma adecuada.
El cliente necesita tener un programa que será el que se comunique con el programa gestor que está en
el servidor. Este programa lo realiza el mismo fabricante de la base de datos y se distribuye en el
mismo disco que el programa servidor y el resto del sistema. A esta parte de la aplicación cliente –
servidor que se instala en cada cliente se le llama Componentes de Conectividad. Y la misión de estos
componentes de conectividad es servir de intermediario entre el gestor de la base de datos instalado en
el servidor, y el programa que en el equipo cliente va a manejar los datos de esa base de datos. Este
programa que va a manejar los datos puede ser el Administrador Corporativo de SQL Server, el
Developper 2000 de Oracle, o una aplicación realizada por nosotros en Visual Basic. Pero el
funcionamiento es siempre el mismo, el programa que va a manejar los datos en el cliente conecta con
el programa Componentes de Conectividad y es este quien conecta con el Gestor de la Base de Datos
para que realice la operación que tenga que realizar y le devuelva, si ha lugar, la información extraída
de la base de datos. Por lo tanto para poder trabajar desde un cliente con la base de datos, es necesario
que en el servidor esté corriendo el programa gestor de la base de datos, y en el cliente, que estén
instalados los componentes de conectividad.
Los componentes de conectividad, tal como se dijo más atrás, están fabricados por el mismo fabricante
de la BD, pero no se extrañe si encuentra algún programa fabricado por otro fabricante. Para Oracle hay
varios fabricantes que han realizado un producto de este tipo. No es difícil, ya que al tratarse de una
aplicación cliente – servidor, lo único importante es el protocolo de comunicación entre el programa
gestor de la BD y los componentes de conectividad. Vea el capitulo correspondiente a aplicaciones
cliente –servidor para más detalles.
Se le indica que instale el servidor de bases de datos. Al darse cuenta que no tiene Windows 2000, dice
que no puede instalar más que los componentes del cliente.
SQL Server tiene dos discos, Standard y Profesional. El Profesional le permite hacer instalaciones en
equipos remotos, y en ese caso estaría la opción Equipo Remoto habilitada. No es nuestro caso, ya que
solamente debemos instalar en el equipo local. Siga la instalación en la que en sucesivas ventanas le
aparecerá la opción: “Crear una nueva instancia de SQL Server o instalar herramientas de cliente”.
Acepte esa opción. A continuación le pide el nombre y contraseña. Se refiere a la del equipo, no al de
la base de datos. Acepte. Llegará a la ventana siguiente:
Una vez instalada la conectividad debe asegurarse que desde el cliente ve al servidor y que no está
cerrado el paso al puerto 1433 mediante algún Firewall u otro dispositivo. Ya puede trabajar con la
base de datos SQL Server.
Posiblemente le surja una pregunta ¿Contra que base, en qué servidor, con qué usuario y clave de
acceso? Con lo Ud. ponga en el programa de Visual Basic en la propiedad ConnectionString del objeto
Connection de ADO. Vea un ejemplo real:
Aquí tienen todos los datos necesarios para conocer todas las preguntas que se hacía antes:
Para instalar la conectividad de Oracle ocurre algo parecido a SQL Server. Le recomiendo que
se instruya adecuadamente con los manuales de Oracle para su correcta instalación. No es
complicado, pero este autor no conoce Oracle como SQL Server.
Le pide el idioma:
Le van apareciendo
sucesivas ventanas en
una le pide el directorio
donde quiere instalar el
programa, en otra le pide
permiso para cambiar
una línea del Autoexec,
etc. Acepte los valores
hasta que la instalación
se haya realizado por
completo.
Una vez terminada verá que le ha creado una carpeta llamada Orant que
puede ver expandida en esta figura.
AUTOMATIC_IPC = OFF
TRACE_LEVEL_CLIENT = OFF
names.directory_path = (TNSNAMES)
names.default_domain = world
name.default_zone = world
Fíjese en la línea
names.directory_path = (TNSNAMES)
La parte que afecta a la aplicación en la que me conecto con Oracle es la que comienza por la linea:
Intranet.world =
Los datos son reales. Pero he de manifestar que no conozco Oracle tan bien como para explicarlo, y que
la instalación la hizo personal de una distribuidora de Oracle. De cualquier forma puede verse que está
definiendo la conexión Intranet , que tienen el Host en la dirección 10.3.22.4 y el puerto de
comunicación es el 1521, con una segunda alternativa en el puerto 1526.
Le recomiendo que si su caso es una conexión a Oracle se informe mejor de la configuración de sus
clientes. Es de suponer que los técnicos de Oracle le resuelvan las grandes dudas que le pueden surgir
por las escasa explicaciones de este curso…de Visual Basic.
Visual Basic con el que se escribió estas líneas para crear una conexión ADO con esa base de datos:
ConexBDPrensa.Open
Puede darse cuenta que tanto en SQL Server como en Oracle estamos empleando conexiones ADO.
Podría hacerse también mediante RDO, creando un enlace ODBC (Para poder crear ese enlace ODBC
también es necesario instalar previamente la conectividad). Pero ODBC es una tecnología que ya está
considerada obsoleta, por lo que prescindimos de cualquier ejemplo.
Se sale necesariamente de este curso una explicación más profunda de estas dos bases de datos.
Este capítulo pretende ampliar el conocimiento de ciertos controles introducidos con la versión
6 de VB, que, aunque ya son de uso habitual en cualquier aplicación, se salen un poco de los
conceptos estudiados en los controles de los capítulos 1 y 2. Vienen generalmente como un
paquete OCX que contiene varios de ellos.
Se estudian algunos de los paquetes distribuidos por Microsoft, sean o no fabricados por esta
compañía. Sería interminable comentar los paquetes realizados por casas especializadas,
controles de gran calidad, normalmente caros y para aplicaciones específicas dentro del ámbito
comercial en el que se mueve la empresa que lo realiza. Existen también gran cantidad de
Paquete MSCOMCTL.OCX
Este paquete se denomina Microsoft Windows Common Controls 6.0 Contiene los siguientes
controles:
ToolBar Este es el control que presenta una barra de tareas. Es configurable, pudiendo
poner los botones que queramos con el icono apropiado. Es el control que el
alumno introduce por norma en sus primeras aplicaciones. Luego se va dando
cuenta de que hay cosas mejores y más simples. (Un Picture Box con varios
controles Image).Se coloca automáticamente en la parte superior del
formulario.
StatusBar Similar al anterior, pero para la barra de estado en la parte inferior del
formulario. Personalmente, creo que es más útil que el anterior. Puede
presentar automáticamente algunos parámetros del PC (Bloq. Mayúsculas,
Bloq. Números, Hora, Fecha) o el texto que se desee.
Fig. 16.3
StatusBar
ProgressBar Es la típica barra de progreso de Windows.
Fig. 16.4
Fig. 16.5
Los controles anteriores se parecen a otros similares ya explicados en los capítulos anteriores,
y la profundización en sus propiedades y métodos no va a ser problema para el alumno sin
más explicaciones. Reservemos el tiempo de estudio para lo interesante, que es lo que viene
ahora
ListView Es un control que permite presentar datos en una lista, utilizando para ello
gráficos y texto, con cuatro disposiciones distintas.
Control ImageList
En esta se pueden introducir las imágenes que vamos a utilizar en los controles que tengan
asociado este ImageList. Es importante citar la propiedad Key de cada imagen, que sirve para
elegir la imagen. Una imagen de un ImageList se puede elegir mediante su índice (El índice va
de 1 a n, siendo n el número de imágenes), pero eso puede llevarnos a error, ya que dice muy
poco el número de la imagen dentro del ImageList, además de que ese número puede variar si
introducimos posteriormente una imagen nueva en una posición central. Podemos utilizar para
definir la imagen el texto que introduzcamos en la propiedad Key. Por ejemplo, en la figura
anterior, la imagen que presenta una interrogación tiene el valor Question, valor que expresa
de forma inconfundible el contenido de esa imagen, y que es inalterable sea cual sea la
posición que ocupa esa imagen en el ImageList. De esta forma, para poner esa imagen en el
nodo número 1 del TreeView1 basta con poner:
TreeView1.Nodes(1).Image = "Question"
Propiedad MaskColor
Devuelve o establece el color que se usa para crear las máscaras de un control ImageList.
Método Overlay
Dibuja una imagen utilizando dos imágenes de la colección ListImages de un control ImageList,
y pone esa imagen dentro de un control
Control TreeView.
Es un control que permite presentar datos que están estructurados de forma jerárquica. Los
datos se pueden presentar como un texto, acompañados de un icono que determina el tipo de
dato. Puede por ejemplo presentar el organigrama de una empresa, mostrando los
departamentos que la componen, y dentro de estos, las personas que pertenecen a cada
departamento, y para cada persona, su número de teléfono, fax, E-Mail y domicilio, y todos
ellos,
representados con
un icono. Al hacer
doble clic sobre un
icono se despliegan
los elementos que
existen bajo ese
icono. Volviendo a
hacer doble clic, se
ocultan. Es lo más
parecido al
funcionamiento del
explorador de
Windows.
Fig. 16.9 TreeView mostrando seis nodos, a la izquierda con iconos grandes y a la derecha
con iconos pequeños.
La vista de la Fig. 16.9 nos lleva a la primera idea acerca del TreeView: los Nodos. En
cualquier estructura jerárquica, un elemento tiene contenido en sí mismo, pero de él pueden
depender otros elementos. Esto nos lleva al concepto de Nodo. En el TreeView los datos se
El parámetro Número puede ser un número del 0 al 7. El estilo correspondienta a cada valor
es:
Con lo visto hasta ahora ya podemos ver cómo introducimos nodos en un TreeView. Para
poder introducir nodos con gráficos, es necesario poner en el mismo formulario, un control
ImageList con todas las imágenes que queremos poner en los nodos (Observe en la Fig. 16.8
que contiene todos los iconos que aparecen en la Fig. 16.9 Izda.) En ese ImageList hay que
poner a cada imagen un valor en su propiedad Key (En la Fig. 16.8, la imagen correspondiente
a la carpeta cerrada tiene en su propiedad Key la cadena “Cerrado”, la imagen con una carpeta
abierta, “Abierto”, la correspondiente al sobre tiene “Sobre” y la cara, “Cara”. Ya podemos
introducir los nodos mediante el método Add:
El Index de la colección Nodes comienza a numerar por el 1. Cada vez que se añade un
nodo, el Index de ese nodo será el Index del último + 1, independientemente de la
posición que va a ocupar ese nodo dentro del TreeView. No controlamos directamente
esta propiedad. Por eso, es mucho más práctico definir a los nodos mediante su
propiedad Key.
Ya estamos en condiciones de rellenar un TreeView con varios nodos. El código para colocar
los nodos de la figura 16.9 es: (El nombre del TreeView es TreeV1)
Dim MiNodo As Node ‘Declaramos la variable tipo Node
TreeV1.Style = 7 ‘Hacemos que el estilo sea Líneas, +/-, Imagen y Texto
Set MiNodo = TreeV1.Nodes.Add(, , "R", "Raíz") ‘No tienen parámetro RelativoA
‘Los siguientes nodos,dependen del nodo Raiz, que tiene Key = “R”
Set MiNodo = TreeV1.Nodes.Add("R", tvwChild, "C1", "Secundario 1", "Cerrado")
Set MiNodo = TreeV1.Nodes.Add("R", tvwChild, "C2", "Secundario 2", "Cerrado")
Set MiNodo = TreeV1.Nodes.Add("R", tvwChild, "C3", "Secundario 3", "Sobre")
Set MiNodo = TreeV1.Nodes.Add("R", tvwChild, "C4", "Secundario 4", "Cerrado")
‘El siguiente nodo depende de Secundario 4, que tienen Key = “C4”, y además tiene un
‘icono distinto (Question) para cuando está seleccionado.
Set MiNodo = TreeV1.Nodes.Add("C4", 4, "C4b", "Terciario C41" , "Cara", "Question")
‘Ahora se introduce una propiedad a cada uno de los nodos todavía no explicada: la
‘imagen que va a tener cuando ese nodo esté expandido, es decir, cuando se ven los
‘nodos que dependen de él. Vea que se le pone un valor a esta propiedad incluso a los
‘nodos que no tienen otros nodos que dependan de ellos.
TreeV1.Nodes(1).ExpandedImage = "Abierto"
TreeV1.Nodes(2).ExpandedImage = "Abierto"
TreeV1.Nodes(3).ExpandedImage = "Abierto"
TreeV1.Nodes(4).ExpandedImage = "Abierto"
TreeV1.Nodes(5).ExpandedImage = "Abierto"
TreeV1.Nodes(6).ExpandedImage = "Abierto"
‘Y ahora otra propiedad no explicada. Con esto hacemos que se vean todos los nodos.
MiNodo.EnsureVisible
El resultado de este código es la Fig. 16.9. Y la
Fig. 16.10 es lo que pasa cuando se selecciona
el nodo cuyo texto es Terciario C41. Observe en
el código que al introducir este nodo habíamos
puesto un parámetro más, precisamente, el de la
imagen que tendrá cuando esté seleccionado.
Fig. 16.10
Propiedades de la colección Nodes del
TreeView
Propiedad ExpandedImage
Establece la imagen del nodo cuando está
Propiedad Expanded
Devuelve o establece un valor que determina si un objeto Node de un control TreeView se
encuentra expandido o contraído. Es una propiedad Booleana
Esta propiedad puede usarse para expandir por programa un objeto Node. Si un objeto
Node no tiene nodos secundarios, el valor de esta propiedad se pasa por alto.
Propiedad Children
Devuelve el número de objetos Node secundarios contenidos en un objeto Node. Esta
propiedad es solamente de lectura.
Propiedad FullPath
Devuelve la ruta completa de un objeto Node. El valor devuelto es una cadena de caracteres
que es el resultado de concatenar el texto de la propiedad Text del objeto Node referenciado
con los valores de las propiedades Text de todos sus predecesores. El valor de la propiedad
PathSeparator del TreeView determina el delimitador utilizado para separar el Text de uno y
otro nodo.
Sintaxis NombredelTreeView.Nodes(Index).FullPath
Esta propiedad puede usarse en el procedimiento NodeClick del TreeView, que ya pasa
como parámetro el nodo sobre el que hemos hecho click:
Propiedad Count
Devuelve el número de elementos de la colección Nodes
Estas propiedades devuelven una referencia a otro objeto Node. Con esa referencia que nos
devuelve, podremos conocer los valores de las propiedades del nodo cuya referencia nos
devuelve (Text, Key, FullPath, etc)
Sintaxis NombredelTreeView.Nodes(Index).Xxxx
Propiedad Key
Esta propiedad establece o devuelve una cadena de caracteres que identifica unívocamente a
un nodo. Esta propiedad se le asigna normalmente durante la creación de ese nodo mediante
el método Add, pero puede cambiarse posteriormente por programa.
La propiedad Key puede usarse para nombrar al nodo, al tratarse de una cadena única. No
puede haber dos nodos con el mismo Key. Si la cadena no es única, se produce un error.
Es muy prudente usar la propiedad Key para "autodocumentar" su proyecto de Visual Basic si
asigna nombres significativos a los objetos de una colección.
Propiedad Sorted
Esta propiedad la tiene tanto el control TreeView como la colección Nodes. Si esta propiedad
está a True en el TreeView, se ordenan alfabéticamente los elementos de nivel superior de la
colección Nodes.
Cada elemento de la colección Nodes puede tener su propiedad Sorted a True. En este caso,
los nodos que dependen de ese nodo estarán ordenados por orden alfabético.
Puede tener nodos con la propiedad Sorted = True, y otros con esa propiedad = False.
Propiedad Image
Establece la imagen que presenta un nodo. El valor de esta propiedad es, o bien el
Index de la imagen dentro del ImageList asociado al TreeView, o su propiedad Key.
Propiedad SelectedImage
Es lo mismo que la propiedad anterior, pero en este caso se establece la imagen que presenta
el nodo cuando está seleccionado.
Recuerde que estas propiedades son de la colección Nodes del TreeView. Veremos ahora las
propiedades del TreeView.
Propiedades del Control TreeView
Ya se han visto algunas más atrás. Se comentan solamente aquellas propiedades que no son
comunes al resto de los controles.
Propiedad Checkboxes
Establece si se muestran las casillas de verificación. (True/False)
Propiedad HideSelection
Establece si el texto seleccionado aparece resaltado cuando un control pierde el enfoque.
Propiedad HotTracking
Determina si está activado el resalte sensible al puntero del ratón. Una línea por debajo del
texto cuando pasamos el ratón por encima.
Propiedad ImageList
Establece el control ImageList que está asociado al TreeView. El ImageList debe estar en el
mismo formulario que el TreeView al que se asocia, pero un mismo ImageList puede estar
asociado a más de un TreeView u otros controles.
El TreeView necesita un ImageList para tomar de él las imágenes que va a colocar en los
nodos. Esta propiedad se puede cambiar en tiempo de ejecución, y es este precisamente, el
truco que nos permite cambiar en el TreeView el tamaño de los iconos.
En la Fig. 16.9 (Página 5) puede ver el mismo TreeView con dos tipos de iconos. El TreeView
no nos permite cambiar de tipo de iconos, (tal como lo hace el ListView) por lo tanto hay que
inventar algo que permita cambiarlos y así obtener la misma estética que en el ListView. Esto
se logra colocando dos ImageList en el formulario con las mismas imágenes e idénticos Key
para ellas. En uno de ellos se pone la resolución de las imágenes a 32 x 32 y en el otro a 16 x
16 (Vea Fig. 16.7 Página 3) Cuando quiera usar iconos pequeños, se asocia al TreeView el
ImageList con la resolución de 16 x 16, mediante la sintaxis:
NombredelTreeView = ImageList1 ó Set NombredelTreeView = ImageList1
Y a continuación se añaden los nodos mediante el método Add. Si quiere cambiar los iconos
durante la ejecución del programa (es muy típico colocar en una línea del menú el tipo de
iconos a presentar) observará que al cambiar el ImageList desaparecen los iconos. No hay otro
remedio más que volver a cargarlos, usando los métodos de la colección Nodes Clear (para
vaciarla) y Add. Este es el código que he usado en el ejemplo que ilustra este capítulo:
Propiedad LabelEdit
Devuelve o establece un valor que determina si el usuario puede modificar la propiedad Text de
los objetos Node de un control TreeView.
Propiedad LineStyle
Establece el estilo de las líneas que aparecen entre los objetos Node.
Acepta los valores 0 (tvwTreeLines) con lo que presentará solamente las líneas entre los
nodos de un mismo nivel y su nodo primario, y 1 (tvwRootLines) que presenta, además
de las anteriores, líneas entre los nodos raiz.
Propiedad Nodes
Devuelve una referencia a la colección de objetos Node de un control TreeView
Parent (Propiedad)
Devuelve una referencia al formulario que contiene un control u otro objeto o colección.
Propiedad PathSeparator
Establece el carácter delimitador utilizado en la ruta devuelta por la propiedad FullPath. El
carácter predeterminado es "\".
Scroll (Propiedad)
Devuelve o establece un valor que especifica si se muestran barras de desplazamiento.
Propiedad SingleSel
Establece si un nodo debe expandirse cuando se selecciona. Un nodo, con otros nodos
dependiendo de él, se expande al hacer doble clic sobre él, o al hacer clic sobre el + asociado
a ese nodo. Se contrae volviendo a realizar la misma operación. Si esta propiedad está a True,
se expande o contrae cuando se hace clic sobre él.
Propiedad Sorted
Ya explicada para la colección Nodes.
Método GetVisibleCount
Devuelve el número de objetos Node que caben en el área interna de un control TreeView.
El número de objetos Node viene determinado por la cantidad de líneas que caben en una
ventana. El número total de líneas posibles lo determina el alto del control y la propiedad Size
del objeto Font. En este número se cuenta el elemento situado al final de la lista y que sólo es
visible parcialmente.
Método HitTest
Devuelve la referencia del objeto Node situado en las coordenadas x e y. Se utiliza sobre todo
en las operaciones de arrastrar y colocar para determinar si hay disponible en la ubicación
actual un elemento de destino de colocación. Si no hay ningún objeto en las coordenadas
especificadas, el método HitTest devuelve Nothing.
El método HitTest se utiliza casi siempre junto con la propiedad DropHighlight para resaltar
un objeto cuando se arrastra el mouse sobre él durante una operación de Drag & Drop. La
propiedad DropHighlight requiere una referencia a un objeto específico para resaltarlo. Para
determinar el objeto, se usa el método HitTest junto con un evento que devuelva las
coordenadas x e y, como DragOver, con el siguiente código:
Método StartLabelEdit
Inicia el proceso de modificación de la propiedad Text de un nodo, cuando la propiedad
LabelEdit está puesta a 1 (Manual).
Sintaxis NombredelTreeView.StartLabelEdit
Evento BeforeLabelEdit
Se produce cuando el usuario intenta modificar la etiqueta (Propiedad Text) de un Node.
Para comenzar a modificar una etiqueta, el usuario debe hacer clic primero en el objeto para
seleccionarlo y hacer clic una segunda vez para iniciar la operación. El evento BeforeLabelEdit
se produce después del segundo clic.
Para determinar a qué objeto pertenece la etiqueta que se va a modificar, utilice la propiedad
SelectedItem. En el ejemplo siguiente se comprueba el índice de un objeto Node antes de
permitir su edición. Si el índice es 1, se cancela la operación.
Private Sub TreeView1_BeforeLabelEdit(Cancel As Integer)
If TreeView1.SelectedItem.Index = 1 Then
Cancel = True ' Se cancela la operación
Evento AfterLabelEdit
Se produce cuando se termina de modificar la etiqueta (Propiedad Text) de un Node
Sintaxis
Private Sub NombredelTreeView_AfterLabelEdit(cancelar As Integer, nuevaCadena As String)
Para cancelar una operación de edición de etiqueta, establezca True o cualquier valor distinto
de cero en cancelar. Al cancelar la operación, se restaurará la etiqueta existente anteriormente.
El argumento nuevaCadena puede usarse para comprobar una condición antes de cancelar
una operación. Por ejemplo, el código siguiente cancela la operación si nuevaCadena es un
número:
Private Sub TreeView1_AfterLabelEdit(Cancel As Integer, NewString As String)
If IsNumeric(NewString) Then
MsgBox "No se permiten números"
Cancel = True
End If
End Sub
Evento Collapse
Se genera al contraer cualquier objeto Node de un control TreeView, es decir, cuando sus
nodos secundarios se ocultan.
Evento Expand
Se produce al expandir un objeto Node de un control TreeView, es decir, cuando sus nodos
secundarios se hacen visibles.
Evento NodeClick
Se produce al hacer clic en un objeto Node.
Cuando el usuario hace clic en cualquier punto de un control TreeView, fuera de un objeto
nodo, se genera el evento Click estándar. El evento NodeClick se genera cuando el usuario
hace clic en un objeto Node determinado y devuelve una referencia al objeto Node que puede
usarse para validarlo antes de realizar alguna otra acción.
El evento NodeClick se produce antes del evento Click estándar.
Evento Validate
Ocurre cuando el foco cambia a otro control que tiene su propiedad CausesValidation
establecida a True. (Este evento es común a todos los controles ActiveX)
ListView1.Icons = IMList1
ListView1.SmallIcons = IMList2
Las propiedades Icons y SmallIcons deben estar puestas antes de añadir elementos a la
colección ListItems del ListView. Si no es así, dará un error que dice que es necesario
inicializarlas.
Esta propiedad es la equivalente a la propiedad Nodes del TreeView. Pero en este caso, un
elemento de esta colección no solamente contiene un dato, sino que además puede tener
dentro de sí otra colección de datos. Por ejemplo, pensando en una agenda, un elemento de la
colección puede tener como propiedad Text el alias de una persona, y como subitems de ese
elemento, el nombre y apellidos de la persona a la que se refiere el alias, su teléfono y su
domicilio. Un elemento de la colección ListItems puede tener tantos subitems como queramos.
Los elementos de la colección ListItems se añaden mediante el método Add, con la siguiente
sintaxis
Donde
Donde
Devuelve un valor que determina si un elemento ListItem está seleccionado. Es una propiedad
solamente de lectura, por lo que no puede usarse para seleccionar un elemento.
Add
Clear
Quita todos los objetos que hay en una colección.
Sintaxis ListView1.ListItems.Clear
Remove
Quita un miembro específico de una colección.
CreateDragImage
Crea una imagen de arrastre con una versión difuminada de la imagen asociada a un elemento
de la colección ListImages. Normalmente, esta imagen se utiliza como DragIcon en las
operaciones de arrastrar y colocar.
EnsureVisible
Asegura que el objeto ListItem especificado sea visible.
Propiedad Arrange
Establece cómo se organizan los iconos en la vista Iconos o Iconos pequeños de un control
ListView.
Propiedad Checkboxes
Establece si se muestran las casillas de verificación.
Propiedad ColumnHeaderIcons
Es similar a la propiedad Icons o SmallIcons, pero ésta se refiere al ImageList que contiene los
iconos que se van a poner en las cabeceras de las columnas (cuando está en vista Report)
El número de columnas del ListView debe ser igual al número de ListSubItems de cada
elemento ListItems. De esta forma, cada ListSubItem se presentará en una columna. Para
añadir columnas al ListView se usa el método Add aplicado a la colección ColumnHeaders
Sintaxis
La colección ColumnHeaders tienen también sus propiedades y métodos. Vamos a citar las
más destacables:
Propiedad Alignment
Establece la alineación del texto en la cabecera de un ColumnHeader. Acepta 3 valores:
0 = izquierda, 1 = Derecha, 2 = Centrado
Propiedad Position
Devuelve o establece la posición de una columna.
El control ImageCombo
Los elementos ComboItem se añaden con el método Add, que hemos visto en los controles
TreeView y ListView, con una sintaxis idéntica. No va a ser difícil para el alumno trabajar con
este control, una vez conocida la forma de trabajar con los dos controles citados anteriormente.
- MonthView
- Animation
- DTPicker
- UpDown
- FlatScrollBar
Control MonthView
El control MonthView permite crear aplicaciones donde los usuarios pueden elegir una fecha
mediante una interfaz parecida a un calendario.
Para permitir a los usuarios del programa seleccionar un intervalo continuo de fechas, asigne a
la propiedad MultiSelect el valor True y especifique con MaxSelProperty el número de días
que podrán seleccionar. Las propiedades SelStart y SelEnd devuelven las flechas del principio
y el final de una selección.
Existen muchas formas de personalizar la apariencia de un control MonthView. Existen varios
atributos de colores, como MonthBackColor, TitleBackColor, TitleForeColor y
TrailingForeColor, que le permiten crear combinaciones de colores exclusivas para el control.
Establezca las propiedades MonthRows y MonthColumns para mostrar más de un mes a la
vez (hasta 12 meses) en un control MonthView. El número total de propiedades MonthRows
y MonthColumns debe ser inferior o igual a 12.
Es un control enlazado a datos, por lo que puede presentar o introducir una fecha de una base
de datos a través de este control, previamente enlazado a la base de datos mediante un control
Data.
Propiedad DayBold
Devuelve o establece un valor Booleano que determina si la fecha indicada se muestra en
negrita
Propiedad DayOfWeek
Devuelve o establece un valor que especifica el día de la semana actual.
La propiedades MaxDate y MinDate sirven para definir los límites superior e inferior del
calendario.
Propiedad MaxSelCount
Devuelve o establece el número máximo de días consecutivos que se pueden seleccionar de
una vez.
Obviamente esta propiedad solamente tiene sentido cuando la propiedad MultiSelect tiene el
valor True. Además, el valor asignado a la propiedad MaxSelCount debe ser superior a la
diferencia entre las propiedades SelStart y SelEnd. Por ejemplo, si selecciona el periodo del
15/9 al 18/9, MonthView.SelEnd - MonthView.SelStart = 3. Sin embargo, son cuatro los días
seleccionados realmente, por lo que MaxSelCount debe tener asignado el valor 4.
El valor predeterminado de la propiedad es una semana (7 días).
Propiedad Month
Devuelve o establece un valor que especifica el mes actual.
Valores admitidos.
La propiedad Month puede tomar cualquier valor o constante de las expresadas en la tabla
siguiente:
Los valores admitidos para número son:
Constante Valor Descripción
mvwJanuary 1 Enero
Propiedad MonthBackColor
Devuelve o establece un valor que especifica el color de fondo mostrado para un mes.
Color es una variable numérica Long comprendida entre el entre 0 y 16.777.215, o la expresión
del color mediante el esquema de color RGB.
Proiedad MultiSelect
Devuelve o establece un valor que determina si es posible realizar una selección múltiple de
fechas.
Propiedad Parent
Devuelve el formulario, objeto o colección que contiene un control u otro objeto o colección.
Sintaxis objeto.Parent
Utilice la propiedad Parent para tener acceso a las propiedades, los métodos o los controles
del primario de un objeto. Por ejemplo:
MiBotón.Parent.MousePointer = 4
Propiedad ScrollRate
Devuelve o establece un valor que especifica el número de meses que se desplazará cuando el
usuario haga clic en uno de los botones de desplazamiento. La propiedad ScrollRate permite
al usuario realizar desplazamientos de más de un mes cada vez.
Las propiedades SelStart y SelEnd definen el límite inferior y superior del intervalo de fechas
seleccionado. El intervalo de fechas seleccionado puede abarcar varios meses. Es posible
incluir fechas que no están visibles actualmente.
Para que la selección múltiple de fechas funcione correctamente, asigne a la propiedad
MaxSelCount un valor superior a la diferencia de los valores de las propiedades SelStart y
SelEnd.
Los valores SelStart y SelEnd sólo son válidos cuando la propiedad MultiSelect tiene el valor
True.
Propiedad ShowWeekNumbers
Devuelve o establece un valor que determina si los números de semana aparecen junto a la
semana.
Propiedad StartOfWeek
Devuelve o establece un valor que especifica el primer día de la semana.
Los valores admitidos para entero son iguales que para la propiedad DayOfWeek
Propiedad TrailingForeColor
Devuelve o establece un valor que especifica el color de primer plano de las fechas restantes
visibles actualmente.
Propiedad Value
Devuelve o establece la fecha que se muestra actualmente. El valor devuelto es de tipo Date
Propiedad Week
Devuelve o establece un valor que especifica el número de la semana actual.
VariableNumerica = NombredelControl.Week
NombredelControl.Week = VariableNumerica
Propiedad Year
Devuelve o establece un valor que especifica un año de calendario.
VariableNumerica = NombredelControl.Year
NombredelControl.Year = VariableNumerica
Es posible asignar a la propiedad Year cualquier entero comprendido entre 1601 y 9999.
Método HitTest
Devuelve la fecha ubicada en conjunto de coordenadas determinado. Se suele usar en
operaciones de arrastrar y colocar para determinar si existe un elemento en la ubicación de
destino de la operación.
Mediante el método HitTest puede obtener una fecha del control MonthView simplemente
haciendo clik encima de ella. El método HitTest devuelve Null si no existe ninguna fecha en
las coordenadas especificadas.
DateClick (Evento)
Ocurre cuando se hace clic en una fecha del control.
DateDblClick (Evento)
Ocurre cuando el usuario hace doble clic en una fecha del control.
Tiene la misma aplicación que la anterior, pero esta vez haciendo doble click
Este control es una especie de ComboBox, pero en vez de mostrar cuando está expandido una
lista, muestra el control MonthView. Funciona de forma análoga al MonthView, y cuando
seleccionamos una fecha en el calendario, deposita la fecha en la casilla desplegable y se
cierra. Este control nos permite ahorrar un buen espacio en el formulario sin perder
prestaciones. Estas son las formas de presentarse recogido y ampliado.
Se deja al alumno el estudio exahustivo de este control, que no se separa mucho del
MonthView visto anteriormente.
Control Animation
Este control nos permite introducir en nuestra aplicación un elemento muy elegante:
visualización de ficheros .avi Existen en Windows muchos fichero .avi aplicados para distintas
funciones: copiar ficheros, bajar ficheros de Internet, y otros empleados como elemento
tranquilizador en procesos lentos. Veamos un par de ejemplos:
Para probar este control basta con poner un controlen el formulario, un CommonDialog (CD1) y
dos botones, uno para arrancar y otro para parar la presentación del fichero avi. El tamaño que
se le de en el formulario debe ser suficientemente grande para que quepa la presentación en
su tamaño real. No acepta zoom.
En el procedimiento click del botón de arrancar basta con poner este código:
Propiedad Center
Establece si el fichero .avi se presenta en el centro del control (Center = True) o si se
presenta en las coordenadas 0,0 del mismo (Center = False)
Propiedad AutoPlay
Devuelve o establece un valor que determina si el control Animation empezará a reproducir un
archivo .avi cuando éste se cargue en el control.
Control UpDown
Este control sirve para introducir saltos de un paso en otros controles o en cualquier
parte del código de la aplicación.
Este control es tan sencillo que no se va a explicar. Solamente citar que sus dos
procedimientos más importantes son el DownClick y el UpClick
Control FlatScrollBar
Es un control de scroll. Tiene como todos los controles de este tipo, una propiedad Max para
darle el valor máximo, Min para ajustar el valor mínimo, SmallChange y LargeChange para fijar
las variaciones al hacer click en la fkecha o en el cuerpo respectivamente, y unna propiedad
Value para leer el valor actual.
Solamente tienen el procedimiento Scroll que se produce al variar la posición del cursor.
Dejamos aquí el estudio de controles avanzados. No es que no haya más, sino que el curso
debe tener un final. Para conocer a fondo los controles lo mejor es tomar un control de la lista
que sale del menú de VB Proyecto | Componentes, y ponerse con el. No voy a decir que sea
más fácil que estudiarlos con la ayuda de la Guía del Estudiante. Eso sí, le va a resultar
bastante más divertido. Le recomiendo que si estudia a fondo algún control interesante para
Vd. apunte todo lo que vaya aprendiendo de él. Por experiencia puedo decirle que se olvida.
Créese su propia Guía del Estudiante. A final de cuentas esta comenzó así y ya va bastante
adelantada.
Dado que este libro tiene lógicamente un alcance limitado, y el tema de APIs es enorme,
se recomienda recurrir a un libro específico de APIs. Este no puede ser otro que el
siguiente :
Nota introducida en el 2001 - Aparte de este libro, existe un recurso en Internet que incluso le
supera, y que tiene la gran ventaja de que se trata de un sistema informático donde puede
copiar y pegar código. Puede encontrarlo en http://www.allapi.net/
Desde que lo he descubierto he dejado el Libro de Appleman un poco aparcado. Sin embargo
las explicaciones aportadas en ese libro son difícilmente sustituibles.
Para usar una función API lo primeros que tenemos que hacer es declararla en nuestra
aplicación. La declaración debe hacerse en la sección de declaraciones de un formulario o
módulo. Si la declaramos en un formulario, necesariamente debemos declararla como privada.
Declare Sub Sleep Lib "kernel32" Alias "Sleep" (ByVal dwMilliseconds As Long)
Public Declare Sub Sleep Lib "kernel32" Alias "Sleep" (ByVal dwMilliseconds As Long)
Private Declare Sub Sleep Lib "kernel32" Alias "Sleep" (ByVal dwMilliseconds As Long)
En esta declaración lo que le estamos diciendo es que, en la librería kernel32 está escrita una
función llamada Sleep (Es el nombre que figura entre comillas en la declaración) y que le
tenemos que pasar un parámetro, el tiempo que queremos que se pare la ejecución de la
aplicación, expresado en milisegundos. Nos dice la declaración que el parámetro se le pasa
Por Valor (ByVal) y que ese dato debe ser un Long, es decir, si se lo pasamos como una
variable, esa variable debe ser del tipo Long. Una vez declarada esta función, en la sección de
declaraciones de un módulo o de un formulario, podremos acceder a ella en cualquier parte de
la aplicación (las partes de la aplicación donde se puede usar dependerá del ámbito de la
declaración, que es idéntica que para las variables) usando una línea de código como esta :
Sleep (500) y la aplicación se detendrá medio segundo cuando llegue a esa línea
ó Sleep (tiempo) donde tiempo es una variable tipo Long que contiene el tiempo (en
milisegundos) que queremos detener el programa.
Aquí ya se ha complicado un poco la cosa. Pero tras un análisis detenido veremos que esa
complicación es sólo aparente.
En primer lugar vemos que la librería donde está esta función es, como en la función Sleep, el
kernel32 . Esto quiere decir que la librería kernel32 contiene varias funciones. Pero ¿qué es la
librería kernel32 ? Ni mas ni menos que una DLL llamada kernel32.dll que puede encontrar
en el directorio C :\WINDOWS\SYSTEM, y que es el alma de Windows. (Kernell significa, como
muy bien sabrá, núcleo)
En segundo lugar, vemos que el nombre de esta función dentro de la DLL kernel32.dll es
GetVolumeInformationA, que es lo que figura entre comillas en la declaración. El nombre
GetVolumeInformation que figura como nombre de la función, al principio de la declaración,
es el nombre por el que nos vamos a referir a la función en nuestra aplicación. Ese nombre
puede cambiarse, cambiando también el nombre con el que vamos a llamar a esta función a lo
largo de nuestra aplicación. Esto se lo digo solamente a nivel informativo. No lo haga. Su
aplicación no podría ser interpretada por otra persona. No es profesional y quien mas
perderá por ello es Vd. Le hago especial hincapié en esto, porque es una forma de proteger
sus programas por parte de algunos programadores. Pero un analista experto encuentra
enseguida el truco. Y algunos no perdonan. Seamos profesionales
En tercer lugar, vemos que la declaración de esta función termina con la expresión As Long.
Esto significa que esta función devuelve un dato, y es concretamente, un Long. Por lo tanto, si
ese dato nos sirve para algo, podemos obtenerlo. Verá que no es necesario, pero en muchas
ocasiones, ese dato nos va a indicar si la función se ejecutó correctamente. Concretamente,
esta función devuelve un 0 si ha existido algún problema para obtener el número del disco, o
un número distinto de 0 si lo ha obtenido. Las demás constantes deberemos declararlas en el
procedimiento donde vamos a usar la función (o en otro lugar, si así lo exige el ámbito que les
queramos dar, pero generalmente, en el mismo procedimiento), e invocar la función pasándole
los parámetros correctos.
La sintaxis de las Apis va a ser distinta si deseamos obtener el valor que devuelve o no. Por
ejemplo, para la función anterior podemos poner perfectamente estas dos expresiones
En el ejercicio realizado para hacer estos apuntes, este código se metió en el procedimiento
click de un botón de comando.
‘Declaramos las variables. Observe que no tienen por qué tener el mismo nombre que en la
‘declaración de la función.
Estas variables son las que se van a pasar como parámetros a la función. La correspondencia
entre el nombre del parámetro y cada una de las variables es la siguiente :
Función GetVolumeInformation
Declare Function GetVolumeInformation Lib "kernel32" Alias "GetVolumeInformationA" (ByVal
lpRootPathName As String, ByVal lpVolumeNameBuffer As String, ByVal nVolumeNameSize
As Long, lpVolumeSerialNumber As Long, lpMaximumComponentLength As Long,
lpFileSystemFlags As Long, ByVal lpFileSystemNameBuffer As String, ByVal
nFileSystemNameSize As Long) As Long
lpRootPathName
Es un string que contiene el directorio raíz del disco a analizar. Si es te parámetro es Null, se
toma el directorio raíz del directorio actual. Esta parámetro puede indicar el disco de un
servidor, en cuyo caso debe indicarse con dos backslash. (\\Servidor\Disco)
lpVolumeNameBuffer
Apunta a una variable que va a recibir el nombre del Label del disco. Hay que declararla con un
tamaño predeterminado, siempre mayor que el que va a tener el dato
p.e. Dim VolBuf As String * 255
nVolumeNameSize
Especifica la longitud de la variable anterior. No importa que la hayamos declarado ya con
determinado tamaño. Hay que poner aquí otra vez ese tamaño, que será el mismo que tenía
declarado la variable.
lpVolumeSerialNumber
Apunta a una variable tipo Long, donde se va a meter el número del disco.
lpFileSystemFlags
Apunta a una variable tipo Long, (Igual que la anterior, concatenación de dos bytes) que
especifican los Flags asociados al sistema de ficheros. Puede ser la combinación de dos de los
siguientes parámetros (Excepción: FS_FILE_COMPRESSION y FS_VOL_IS_COMPRESSED
son mutuamente excluyentes).
FS_CASE_IS_PRESERVED
If this flag is set, the file system preserves the case of filenames when it places a name on
disk.
FS_CASE_SENSITIVE
El sistema de ficheros diferencia mayúsculas y minúsculas.
FS_UNICODE_STORED_ON_DISK
FS_PERSISTENT_ACLS
FS_FILE_COMPRESSION
FS_VOL_IS_COMPRESSED
El disco especificado se trata de un disco comprimido. Por ejemplo, es un disco al que se le ha
aplicado DoubleSpace.
lpFileSystemNameBuffer
Apunta a una variable tipo string, donde se mete el sistema de ficheros soportado (FAT or
NTFS). Esta variable debe declararse con un número prefijado de caracteres, siempre superior
al que vaya a tener realmente (p.e. Dim SysName As String * 255)
nFileSystemNameSize
Especifica el número de caracteres de la variable anterior. Debe introducirse el mismo número
con el que se ha declarado la longitud de esa variable. (255 en el ejemplo).
Ya vamos viendo que las APIs no son tan difíciles de entender. Vamos a ver otra, la inversa
de la anterior, que pone el valor al parámetro Label del disco. Observe que el resto de los
parámetros no pueden variarse ya que vienen marcados en el disco (número) o implícitos en el
sistema operativo.
Función SetVolumeLabel
Private Declare Function SetVolumeLabel Lib "kernel32" Alias "SetVolumeLabelA" (ByVal
lpRootPathName As String, ByVal lpVolumeName As String) As Long
lpRootPathName
Variable tipo string donde se introduce el directorio raíz del disco al que se le va a poner o
cambiar el Label. Si este parámetro es Null, se entiende que es el raíz del directorio actual.
lpVolumeName
Variable tipo string que contienen el Label a poner. Si es Null, borra el Label actual.
Esta chuleta no es otro que el Visor de Texto API (API Text Wiever en Inglés). Este es un
programa que se distribuye con Visual Basic y que se instala al tiempo que este, formando
parte del mismo grupo de programas. Haciendo clic en su icono aparece esta ventana :
Haciendo Click sobre la palabra Archivo de la Barra de Menú, aparecen unos ficheros que
contienen las declaraciones de las funciones API :
Win32Api.txt
Winmmsys.txt
Estos dos ficheros son los que suministra Microsoft con VB6. El primero contiene las
declaraciones de las funciones API no relacionadas con el tema multimedia. El segundo
contiene las declaraciones de las API relacionadas con este tema de multimedia.
Si ha adquirido el libro de Appleman puede tener otro fichero : Api32.txt. El autor de este libro
asegura que es mucho mas completo que el fichero que entrega Microsoft. De hecho contiene
bastantes mas declaraciones.
API-Guide presenta también la declaración del API para ser copiada directamente en el
portapapeles y pegada en nuestra aplicación. Últimamente es la única que uso, ya que al
tiempo, se obtienen una explicación de cada uno de los parámetros.
Para obtener una o varias declaraciones, seleccione las funciones en la ventana de arriba del
visor, haga click en Agregar y esa función le pasará para la ventana de abajo. Una vez que
tenga en esa ventana todas las funciones que necesita, haga click en el botón Copiar y las
declaraciones completas le pasarán al portapapeles.
Una vez que ya sabemos donde se pueden copiar las declaraciones de las APIs, veamos una
que nos permitirá obtener la hora desde el sistema operativo :
Ahora nos surge una duda ¿Qué es SYSTEMTIME ? Es una variable que hay que declararla
con la instrucción Type, igual que hacíamos con las variables con varios campos en los
ficheros tipo Random. Repase este capítulo si no lo tiene claro.
Para poder declarar esta variable, podemos obtener su declaración del mismo Visor de Texto
API
Para ello, en la ventana Tipo API en vez de figurar Declaraciones debe poner Tipos. Busque
esta opción desplegando la ventana con la flecha que tiene a la derecha. Busque ahora la
variable cuya declaración quiere conocer. Repitiendo el proceso anterior, se llevará en el
portapapeles la declaración de la variable :
Type SYSTEMTIME
wYear As Integer
wMonth As Integer
wDayOfWeek As Integer
wDay As Integer
wHour As Integer
wMinute As Integer
wSecond As Integer
wMilliseconds As Integer
End Type
Haga un pequeño ejercicio para obtener la fecha y hora usando un API :
Para ello debemos introducir un módulo donde definiremos la variable SYSTEMTIME y donde
podemos declarar la función GetSystemTime :
Módulo 1 Declaraciones
Option Explicit
Type SYSTEMTIME
wYear As Integer
wMonth As Integer
wDayOfWeek As Integer
wDay As Integer
wHour As Integer
wMinute As Integer
wSecond As Integer
wMilliseconds As Integer
End Type
Ahora en un Formulario, ponemos 8 TextBox (uno para cada datos) y un botón de comando,
donde ponemos el siguiente código:
Este programa nos mostrará la hora (hasta milésimas de segundo), la fecha y el día de la
semana.
Cuando necesite un API nuevo para su programa no se conforme con salir del paso. Eso se
logra con el libro de Appelman o con el API-Guide. Por experiencia propia, le recomiendo que
haga una pequeña aplicación para emplear esa o esas APIS que necesita. Estúdielas,
documéntelas y guárdelas donde guarda sus tesoros más preciados. Hágalo, pues al cabo de
una año sin volver a usarlas se le van a olvidar, y tendrá que ponerse nuevamente al día. Hay
APIs caprichosas, declaraciones que tienen que ser así, sin ninguna razón ni criterio para ello.
No duplique su trabajo. Créese una libreta de APIs y con el tiempo, llegará a dominarlas como
el mismísimo Appleman. Por mi parte, dejo como botón de muestra las que presento a
continuación.
Es muy cómodo, sobre todo para los programas que se deben estar ejecutando
continuamente, poner su icono en esta parte de la barra de tareas, ya que ocupan menos
espacio que un programa minimizado normalmente. Solamente se ve el icono representativo
del programa. Por ello es necesario elegir un icono que defina de forma sencilla e inequívoca
la aplicación. Generalmente se le pone un PopUp menú que muestra las opciones de restaurar
(Muestra la aplicación en su tamaño normal) y Salir. También se suele poner un TextToolTip
para mostrar de forma literal el nombre del programa minimizado en ese icono.
Fig. 1 - Icono del programa Wsk colocado en el System Tray con su ToolTipText y PopupMenú
Este sistema permite comunicar la aplicación con el icono creado en el System Tray.
Lógicamente, primero debe crearse el icono (Se crea normalmente en el Load del
formulario inicial de la aplicación), y permanece ahí mientras dura la ejecución de la
aplicación. Si se minimiza la aplicación, no aparece su icono en la barra de tareas. Por
lo tanto se necesitará algún artilugio para poder poner otra vez la aplicación en su
estado normal. Esto se hace mediante una comunicación entre el icono del System Tray
y la aplicación. Esa comunicación es lo que llamaremos mensaje de retorno. Al recibir
ese mensaje de retorno, el formulario va a tratarlo en uno de sus procedimientos. El
procedimiento donde lo va a tratar se le especifica en la llamada a la API como se verá
mas adelante. El mensaje de retorno va a depender de lo que se haga sobre el icono del
System Tray, (Tecnología Windows) y es en principio un poco complicado, pero verá
también más adelante una breve explicación sobre este valor devuelto. Para que el
icono no siga en el System Tray una vez hayamos salido de la aplicación, es necesario
eliminarlo en el procedimiento Unload del formulario inicial.
Vemos que la declaración incluye una variable definida por el usuario: NOTIFYCONDATA. Se
define de esta forma
El segundo parámetro (pnid) es una variable tipo NOTIFYICONDATA tal como se definió más
atrás. Vemos a continuación cada componente de esta variable:
UId Es el identificador del icono del System Tray. Puede ser cualquier número Long.
Solamente será necesario usar un número si hace falta identificar ese icono para una operación
posterior. Si no se va hacer ninguna operación con él, caso más habitual, basta con poner vbNull
como valor de esta parte.
valores (Se especifica el valor, la constante con la que se le suele denominar y el resultado de
usarla):
Uflags puede contener los tres valores, validando de esta forma las tres condiciones anteriores.
Es muy tipico ver que UFlags toma el valor:
Resumiendo: Uflags es en realidad un conjunto de tres banderas, que puede tomar el valor 1, 3
ó 7, según se haya aplicado NIF_MESSAGE (Pesa 1), NIF_ICON (Pesa 2) y NIF_TIP (Pesa 4)
El valor del mensaje de retorno va a depender de lo que se haga sobre el icono del
System Tray. Y es concretamente, el valor que corresponde al evento
que se realice sobre el icono (Mouse down, Mouse Up, doble clic, etc)
según los valores de la tabla siguiente. Eso sí, estos valores se
multiplican por el valor de la propiedad TwipsPerPixelX del objeto
Screen. Insisto, tecnología Windows que queda fuera de este manual
de Visual Basic.
Los valores de la siguiente tabla son aplicables para establecer en que procedimiento
va a procesar la información del mensaje de retorno, como para
conocer el valor de ese mensaje.
HIcon Icono que presentará en el System Tray. Es muy normal poner el icono del formulario
inicial de la aplicación. Si el código de creación del icono está es ese
formulario (cosa muy normal) basta con poner aquí, Me.Icon
SzTip Aquí debe poner el ToolTipText que quiere que aparezca cuando mantiene el cursor
del ratón unos instantes sobre el icono del System Tray.
EJERCICIO PRACTICO
Declaramos otra API, con la que podemos hacer que un formulario tome el foco, y se coloque
en primer plano: SetForegroundWindow
Public Declare Function SetForegroundWindow Lib "user32" (ByVal hwnd As Long) As Long
Ahora declaramos una variable llamada Nid, que será del tipo NOTIFYICONDATA
Ya hemos terminado con el código del módulo. Vayamos ahora al procedimiento Load del
formulario inicial. Además de todo lo que debamos hacer en ese procedimiento, le añadiremos
esta parte, correspondiente a la creación del icono en el System Tray
Con estas dos líneas garantizamos que se va a presentar el formulario, si se hubiese abierto
con la instrucción Load
Me.Show
Me.Refresh
‘Damos los valores apropiados a cada una de las partes de la variable Nid
With Nid
.cbSize = Len(Nid)
.hwnd = Me.hwnd
.uId = vbNull
.uFlags = NIF_ICON Or NIF_TIP Or NIF_MESSAGE
.uCallBackMessage = WM_MOUSEMOVE
.hIcon = Me.Icon
.szTip = "SEAE - Conexión vía IP" & vbNullChar
End With
Llamamos a la función para crear el icono (Parámetro NIM_ADD) y le pasamos la variable Nid
con las características del icono
hwnd = Me.hwnd Le pasa el .hwnd del formulario. Así Windows ya sabe a qué formulario
debe enviar el mensaje de retorno.
NIF_ICON es una constante que vale 2, NIF_TIP es otra constante que vale 4 y
NIF_MESSAGE es otra que vale 1. Por lo tanto, esta línea es exactamente igual a otra
que pusiese
uFlags = 7
La razón de poner una constante en vez del valor es solamente a efectos didácticos durante la
programación. Es más sencillo acordarse de un neumónico que de un número. Es costumbre
de programación solamente. Y recuerde que esas constantes deben estar declaradas, cosa
que hicimos en el módulo, cuya declaración se repite aquí por facilidad de comprensión:
hIcon = Me.Icon Con esta línea le decimos que el icono que debe presentar en el
System Tray es precisamente el icono de este formulario. Podríamos indicarle otro icono,
pasándole la propiedad Icon de otro objeto, o la propiedad picture de un picture box o control
image, siempre y cuando en esa propiedad hayamos puesto un icono.
szTip = "SEAE - Conexión vía IP" & vbNullChar Este es el ToolTipText que va a
aparecer cuando dejemos unos instantes el puntero del ratón encima del icono. Esta variable
tiene 64 caracteres, pues así se declaró – y no funciona si se declara de otro tamaño – en la
definición de la variable tipo NOTIFYICONDATA. Para evitar que aparezcan como espacios los
caracteres no usados, cosa que deja muy fea la presentación del ToolTipText, basta con poner
& vbNullChar tras el texto deseado.
Con el código introducido en el Load del formulario inicial ya hemos puesto el icono en el
System Tray. Ahora ya podemos hacer clic sobre este icono (y otros eventos) para que el
icono envíe al formulario el mensaje de retorno. Ese mensaje será tratado en el procedimiento
Este procedimiento espera recibir 4 parámetros, sin embargo solamente va a recibir 3. Por lo
tanto, el valor de Y será siempre nulo a lo largo de este procedimiento.
El mensaje recibido del icono contiene un número. Si la propiedad ScaleMode del formulario
estuviese en Pixels, ese número coincidiría con el que genera el icono. Si ScaleMode no está
en Pixels, el valor generado lo multiplica por el valor de la propiedad TwipsPerPixelX. Las
líneas anteriores detectan el valor de la propiedad ScaleMode y actúan en consecuencia. Al
final tenemos un número que es el que metemos en la variable msg. Hacemos un Select Case
para obtener un resultado distinto en función del valor de msg
End Select
End Sub
Solamente nos queda ver que números genera el icono para el mensaje de retorno. Son unos
valores que Windows ya tiene predispuestos, y que para mayor facilidad de programación ya
hemos introducido en unas constantes. Repetimos aquí el código de declaración de esas
constantes:
(Recuerde que estos valores están en Hexadecimal). Por ejemplo, cuando hacemos clic en el
icono con el botón derecho (R), en el instante de bajar el botón (ButtonDown) se genera como
mensaje de retorno el número 204 en hexadecimal. Ese valor, que no depende de la
programación, sino que es un valor que Windows tiene prefijado, se lo asociamos a una
constante llamada WM_RBUTTONDOWN (La podríamos haber llamado de otra forma, pero
Microsoft la llama así, y si todos la llamamos así existirá cierta semejanza entre el código de
todos los programadores). Luego, en el procedimiento MouseMove del formulario, en vez de
preguntar si el mensaje de retorno vale &H204, preguntamos si vale WM_RBUTTONDOWN
Podemos hacer que el icono del System Tray cambie en determinadas circunstancias. Por
ejemplo, si el programa minimizado es un correo electrónico, podemos hacer que cambie de
color intermitentemente cuando se ha recibido un mensaje nuevo. Esto podemos hacerlo con
un control Timer, donde pondríamos un código parecido a esto:
Picture1 es un array de Pictures con Index del 0 al 3, y cada uno con una imagen del icono
distinta, para producir un efecto agradable. El código siguiente cambia el icono con 4 imágenes
distintas.
Va a encontrar en este capítulo una palabra muy repetida: Clave del registro. (Key) Una clave
del registro es una de las muchas informaciones que existen en el registro. Por ejemplo:
Las claves se expresan con una estructura similar a la de las carpetas (directorios) del
explorador de Windows. No es que sea así, ya que el registro es un fichero único, pero su
estructura jerárquica es similar. Por eso, hablaremos de Claves y Subclaves, lo mismo que
hablamos de directorios y subdirectorios (Carpetas y Subcarpetas). Una clave tendrá
subclaves si hay más claves por debajo de su nivel jerárquico. En el ejemplo anterior, la clave
Podríamos clasificarlas en dos grupos, uno la de aquellas que sirven para leer y guardar
valores, y otro, con aquellas Apis que sirven para mantenimiento del registro.
La primera operación que debemos hacer para trabajar sobre una clave es abrirla
(Exceptuando lógicamente la operación de crearla). Para abrir una clave se usa el API
RegOpenKeyEx. Al final, una vez realizadas todas las operaciones deseadas, hay que
cerrarla. Se cierra mediante el API RegCloseKey
Handle = Manejador en español, pero esta es una de estas palabras que es mejor no
traducirlas. Utilizaremos la expresión Handle durante todo este capítulo.
Función RegOpenKeyEx
Abre la clave especificada. Es la primera operación que hay que hacer para trabajar con una
clave. (Es similar, en ficheros, a Open NombreFichero For xxx As #n)
Declaración:
Declare Function RegOpenKeyEx Lib "advapi32.dll" Alias "RegOpenKeyExA" (ByVal hKey As
Long, ByVal lpSubKey As String, ByVal ulOptions As Long, ByVal samDesired As Long,
phkResult As Long) As Long
HKey es el Handle de la clave de nivel superior. Hkey acepta cualquiera de los valores
predefinidos:
Función RegCloseKey
Cierra el Handle de la clave que estamos utilizando. Es la operación que
finaliza cualquier operación de lectura o escritura en el registro. Si
hacemos un símil con los ficheros secuenciales, sería el Close #n
Declaración
Declare Function RegCloseKey Lib "advapi32.dll" Alias "RegCloseKey" (ByVal hKey As Long)
As Long
HKEY_CLASSES_ROOT
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USERS
(Estos valores son las constantes citadas en la función anterior.)
Función RegCreateKeyEx
Crea la clave especificada. Si existe ya esa clave, la abre.
Declare Function RegCreateKeyEx Lib "advapi32.dll" Alias "RegCreateKeyExA" (ByVal hKey
As Long, ByVal lpSubKey As String, ByVal Reserved As Long, ByVal lpClass As String, ByVal
dwOptions As Long, ByVal samDesired As Long, lpSecurityAttributes As
SECURITY_ATTRIBUTES, phkResult As Long, lpdwDisposition As Long) As Long
LpSubKey es una variable tipo string que contiene el nombre de la subclave a crear.
La subclave no debe empezar por el carácter \. Este parámetro no puede ser nulo.
LpClass Variable tipo string que especifica el tipo de esta clave. Puede poner una
cadena vacía y el tipo de la clave se especificará cuando le introduzca el valor. Este
parámetro se ignora si la clave ya existe.
dwOptions Especifica las opciones especiales de la clave. Puede ser uno de los
siguientes valores:
PhkResult Apunta a una variable que devuelve un Long con el Handle de la clave
creada o abierta, en caso de que ya existiera.
lpdwDisposition Apunta a una variable que devuelve un Long con uno de los
siguientes valores:
Función RegDeleteKey
Borra una clave del registro de Windows. Funciona de forma distinta en W95
que en WNT. En W95 borra esa clave y todas sus subclaves. En WNT
no permite borrar una clave que tenga subclaves.
HKEY_CLASSES_ROOT
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USERS
Para crear una clave, primero hay que abrir todas las claves jerárquicamente superiores a esa
clave a crear. En este ejemplo vamos a crear una clave de la siguiente forma:
HKEY_CURRENT_USER/Software/GuiadelEstudiante/Colores
End If
'Cierra la clave HKEY_CURRENT_USER/Software/GiadelEstudiante/Colores
RegCloseKey Manejador3
'Cierra la clave HKEY_CURRENT_USER/Software/GiadelEstudiante
RegCloseKey Manejador2
'Cierra la clave HKEY_CURRENT_USER/Software
RegCloseKey Manejador1
End Sub
Para que todas estas funciones puedan trabajar es necesario haberlas declarado previamente.
La declaración puede hacerse, o bien en la sección de declaraciones de un módulo, donde las
declararemos como Públicas, o en la sección de declaraciones de un formulario, donde deben
declararse como privadas. Las constantes también se declararán en el mismo sitio que las
funciones. En este caso, se declararon en el formulario:
Option Explicit
Const HKEY_CLASSES_ROOT = &H80000000
Const HKEY_CURRENT_USER = &H80000001
Const HKEY_LOCAL_MACHINE = &H80000002
Const HKEY_USERS = &H80000003
Const HKEY_CURRENT_CONFIG = &H80000005
Const ERROR_NO_MORE_ITEMS = 259&
Const REG_OPTION_NON_VOLATILE = 0
Const KEY_SET_VALUE = &H2
Const REG_OPTION_BACKUP_RESTORE = 4
Const REG_OPTION_VOLATILE = 1
Const STANDARD_RIGHTS_ALL = &H1F0000
Const SYNCHRONIZE = &H100000
Const READ_CONTROL = &H20000
Const STANDARD_RIGHTS_READ = (READ_CONTROL)
Const STANDARD_RIGHTS_WRITE = (READ_CONTROL)
Const KEY_CREATE_LINK = &H20
Const KEY_CREATE_SUB_KEY = &H4
Const KEY_ENUMERATE_SUB_KEYS = &H8
Const KEY_NOTIFY = &H10
Const KEY_QUERY_VALUE = &H1
Const KEY_READ = ((STANDARD_RIGHTS_READ Or KEY_QUERY_VALUE Or _
KEY_ENUMERATE_SUB_KEYS Or KEY_NOTIFY) And (Not SYNCHRONIZE))
Const KEY_WRITE = ((STANDARD_RIGHTS_WRITE Or KEY_SET_VALUE Or _
KEY_CREATE_SUB_KEY) And (Not SYNCHRONIZE))
Const KEY_EXECUTE = (KEY_READ)
Const KEY_ALL_ACCESS = ((STANDARD_RIGHTS_ALL Or KEY_QUERY_VALUE Or _
KEY_SET_VALUE Or KEY_CREATE_SUB_KEY Or KEY_ENUMERATE_SUB_KEYS Or _
KEY_NOTIFY Or KEY_CREATE_LINK) And (Not SYNCHRONIZE))
Vamos a ver ahora como se introducen los datos en las claves. Para introducir cada dato,
debemos introducir el nombre y el valor de cada uno de ellos.
Función RegSetValueEx
Guarda un dato en una clave previamente abierta. Con esta función, puede también modificar
el valor y el tipo de información que almacena ese dato.
Declaración
Declare Function RegSetValueEx Lib "advapi32.dll" Alias "RegSetValueExA" (ByVal hKey As
Long, ByVal lpValueName As String, ByVal Reserved As Long, ByVal dwType As Long, lpData
As Any, ByVal cbData As Long) As Long
(Si declara el parámetro lpData como String, debe pasarlo por valor - By Val lpData As String)
Declaración
Declare Function RegQueryValueEx Lib "advapi32.dll" Alias "RegQueryValueExA" (ByVal hKey
As Long, ByVal lpValueName As String, ByVal lpReserved As Long, lpType As Long, lpData As
Any, lpcbData As Long) As Long
(Si se declara el parámetro lpData como String, debe pasarse por Valor -By Val).
lpType Apunta a una variable que va a recibir el tipo de dato que contiene. Es un
Long, y devolverá uno de los valores descritos para el valor dwType de la función
RegQueryValueEx. Este valor debe ponerse a Null si no se necesita conocer el tipo de
dato.
lpData Apunta a una variable que recibirá el valor del dato. Esta variable, cuando es
tipo string, hay que declararla con un tamaño prefijado, siempre mayor que el tamaño
del dato que va a recibir (Por ejemplo, Dim MiVariable as String * 100 Se entiende que
el dato que se va a meter en MiVariable nunca será superior a 100 caracteres)
También puede declararse como String sin tamaño, y ponerle posteriormente el
tamaño igual a lpcbData. Vea la explicación de esto en el ejemplo. Si no se necesita
conocer el valor del dato, debe ponerse Null
lpcbData Apunta a una variable que guardará el tamaño del copiado en lpData.
Si la variable lpData no tienen tamaño suficiente para almacenar el valor del dato,
lpcbData devolverá el valor ERROR_MORE_DATA ( = 234)
NOTA: Esta función tiene un comportamiento un poco irregular. Cuando se ejecuta la primera
vez no lee el valor de lpData. Sí lee los demás parámetros. Hay que ejecutarla 2 veces
seguidas para que pueda leer ese parámetro.
Sigamos con el ejemplo práctico. Vamos a introducir y leer valores en una clave.
Para poner el valor a un dato de una clave, esa clave debe estar abierta. Se irán abriendo las
claves de forma jerárquica hasta llegar a la clave deseada. Una vez abierta esa clave, se
utilizará la función RegSetValueEx. Luego se cierran todas las claves en orden inverso a la
apertura.
RegCloseKey Manejador3
RegCloseKey Manejador2
RegCloseKey Manejador1
End Sub
Para leer un dato hay que proceder de forma similar, abriendo jerárquicamente las claves hasta
llegar a la clave a leer, y una vez abierta, proceder con la función RegQueryValueEx.
Recuerde que esta función hay que ejecutarla 2 veces para conseguir el valor del dato.
RegCloseKey Manejador3
RegCloseKey Manejador2
RegCloseKey Manejador1
LValorClaveRet = Trim(Var_lpData)
LTamanoValor = Var_lpcbData
End Sub
Podemos aprovechar la primera ejecución de la función para leer el dato lpcbData que nos
indica el tamaño de lpData. A continuación hacemos que la variable Var_lpData tenga ese
tamaño, rellenando tantos caracteres con espacios. Es otra forma de hacer lo mismo.
Vamos a ver ahora como podemos borrar un valor. Se usa para ello la función RegDeleteValue
Declaración
Declare Function RegDeleteValue Lib "advapi32.dll" Alias "RegDeleteValueA" (ByVal hKey As
Long, ByVal lpValueName As String) As Long
RegEnumValue
Esta función devuelve el nombre y el valor de cada uno de los datos contenidos dentro de una
clave previamente abierta. Esta función devuelve el nombre y valor de uno solo de los datos,
por lo que será necesario recurrir a una serie de llamadas en un bucle para obtenerlos todos.
Declaración
Declare Function RegEnumValue Lib "advapi32.dll" Alias "RegEnumValueA" (ByVal hKey As
Long, ByVal dwIndex As Long, ByVal lpValueName As String, lpcbValueName As Long,
lpReserved As Long, lpType As Long, lpData As Any, lpcbData As Long) As Long
lpcbData Apunta a una variable Long que va a recibir el tamaño del dato a
devolver en lpData.
Do While Resp = 0
Var_lpValueName = Space(255)
Var_lpData = Space(255)
Var_lpcbValueName = 255
Var_lpcbData = 255
Resp = RegEnumValue(Manejador3, I, Var_lpValueName, Var_lpcbValueName, 0, Var_lpType,
ByVal Var_lpData, Var_lpcbData)
Var_lpValueName = Left(Var_lpValueName, Var_lpcbValueName)
Var_lpData = Left(Var_lpData, Var_lpcbData)
List1.AddItem I & " - " & Trim(Var_lpValueName) & " - " & Trim(Var_lpData)
I=I+1
Loop
RegCloseKey Manejador3
RegCloseKey Manejador2
RegCloseKey Manejador1
End Sub
Devuelve los nombres de las subclaves de una clave del registro previamente abierta.
Esta función obtiene el nombre de una única subclave cada vez que se le llama, por lo
que para leerlos todos, es necesario recurrir a llamadas en bucle.
Declaración
Declare Function RegEnumKeyEx Lib "advapi32.dll" Alias "RegEnumKeyExA" (ByVal
hKey As Long, ByVal dwIndex As Long, ByVal lpName As String, lpcbName As Long,
lpReserved As Long, ByVal lpClass As String, lpcbClass As Long, lpftLastWriteTime As
FILETIME) As Long
Hay más APIS para el control del registro. En este curso creemos que ya hay materia
suficiente para tener una idea del manejo del registro (Creación de claves, eliminación, Lectura
y escritura) Con los ejemplos descritos en este capítulo hay materia suficiente para poder
realizar todas las funciones que una aplicación normal pueda hacer sobre el registro.
El uso de Apis en una aplicación la hace generalmente más rápida ya que aprovecha recursos
ya existentes en Windows y además compilados. Tiene un pequeño inconveniente cuando se
hace un desarrollo que va a funcionar sobre distintas versiones de Windows. No es normal que
ocurra, pero hay que comprobar que un programa diseñado en W95 corre perfectamente en
W2000. Y es que algunas Apis no son exactamente iguales.
Función EnumPrinters
Declaración
Declare Function EnumPrinters Lib "winspool.drv" Alias "EnumPrintersA" (ByVal flags As Long,
ByVal name As String, ByVal Level As Long, pPrinterEnum As Long, ByVal cdBuf As Long,
pcbNeeded As Long, pcReturned As Long) As Long
Next C
' Presenta el nombre de las impresoras
For C = 0 To NumPrinters - 1
LbPrinters.AddItem PrintInfo(C).pName
Next C
End Sub
Pero vamos a ver otro código que hace lo mismo sin usar Apis.
De las APIs queda mucho por estudiar. Pero ya debe ser el alumno quien ahonde en ello
según sus propias necesidades. Creo que con lo expuesto hay suficiente para empezar.
Internet es la tentación en la que actualmente todos los estudiantes quieren caer. Y tienen
motivos para ello. Internet ofrece unas posibilidades de comunicación como nunca las ha
habido, lo que brinda unas oportunidades inmensas al programador si analiza de forma
pausada lo que la red le puede ofrecer.
Y digo si las analiza de forma pausada porque esa tentación en la que caen todos los
estudiantes es pretender crear un navegador similar al Internet Explorer o al Netescape, un
correo que supere al Eudora y un programa de FTP que sea capaz de bajarle la Enciclopedia
Británica en menos de media hora. Pero hay que ponerse con los pies en la tierra y
comprender que esos programas llevan mucho trabajo de muchos programadores, mucho
tiempo de prueba y unos medios económicos y comerciales tras ellos que hacen que esos
productos tengan que ser necesariamente mejores que lo que puede hacer un estudiante, por
mucho interés e ilusión que ponga.
Sin embargo sí puede ser muy ilustrativo que en este curso nos propongamos la meta de hacer
un programa que sea al tiempo el mejor navegador, el mejor correo (por dos procedimientos), y
el mejor FTP que se pueda concebir, y encima que pueda hacer chat, ping y Telnet. Será el
mejor, sin duda, porque será el único que nos permita aprender a manejar los controles que
Visual Basic nos tienen reservados para los entornos IP.
Nota para alumnos expertos. Si de verdad es Ud. un experto y se lo sabe todo sobre IP
puede saltar directamente al tema Controles de Visual Basic para las redes IP. La colección
Guía del Estudiante está escrita pensando en quien no sabe y quiere saber. Por eso no puedo
pasar pos alto conceptos de nomenclatura de redes IP que se repetirán a lo largo del capítulo,
y que seguro que a más de un alumno le aportarán nuevos conocimientos. Si pese a ser un
experto, su humildad le aconseja leérselo, verá que a lo mejor se le aclara más de un
concepto.
El hecho de que la información original se trocee en paquetes que se envían a la red implica
poner una dirección de destino a cada uno de esos paquetes (para saber a quien van dirigidos)
y la dirección del origen, (para saber quien lo envía). De esta forma, la red sabe a quien debe
entregar el paquete y el destino sabe de quien procede. Todo esto lo hace el protocolo IP
(Internet Protocol) Esto es como si IP trabajase solamente de cartero. Coge paquetes en un
buzón y los envía a su destinatario sin importarle el contenido del paquete. Luego veremos lo
de la dirección. Lo cierto es que el paquete llega al destino.
Lo que no sabe el destino es el orden en el que tiene que colocar el paquete dentro del bloque
total de información. Debido a que el camino seguido por los paquetes no tiene porqué ser el
mismo para todos, es posible que se reciba primero un paquete que se ha transmitido con
posterioridad. Por lo tanto será necesario poder establecer el número de orden de cada uno de
los paquetes dentro del grupo total de paquetes que se han generado para la transmisión.
También será necesario en ciertos casos conocer si el destino ha recibido correctamente el
paquete. Estas cosas las hace el Protocolo de Transporte.
Veamos ahora lo de la dirección. Decíamos que IP pone la dirección del destinatario y del
remitente. Esas direcciones son un conjunto de cuatro números del 0 al 255, separados por un
punto. Por ejemplo, podría ser 195.236.12.56 Así de frías son las direcciones de Internet.
Como cada número puede tomar los valores del 0 al 255, la numeración total de Internet puede
llegar a 4.228.250.625 No son mucho esos cuatro mil doscientos millones. Piense que somos
seis mil millones de personas en el mundo. Por eso ha habido que buscar formas de
aprovechar al máximo las direcciones IP. No es objeto de este curso su estudio, pero digamos
que se logra a base de que las redes de área local conectadas a Internet se conectan
solamente con un número, e internamente tienen otro (el número interno no coincide con
ninguno de Internet, pero se repite en todas las redes de área local). Otra forma de aprovechar
la numeración es asignar números dinámicamente. Eso ocurre cuando nos conectamos a
Internet desde nuestras casas. Mientras dura la conexión, nuestro ISP (Proveedor de Servicio
de Internet) nos cede un número IP de los que le han asignado a él. Cuando nos
desconectamos, ese número se lo da al siguiente que se conecte. Por eso, en nuestras casas,
cuando nos conectamos a través de la conexión telefónica a redes, no tendremos siempre el
mismo número IP.
Esta Dirección IP la usa el Protocolo de Red, es decir El Internet Protocol, que como decíamos
antes, era como un cartero que recoge una carta del Origen y la pone en el Destino sin saber
que es lo que contiene. Ese origen y destino son máquinas, es decir, ordenadores que están
conectados a la red.
Nota acerca de las direcciones IP. Existe una serie de direcciones IP que no se pueden usar
en Internet debido a que están reservadas para redes de área local. Esto se hace así para que
nunca pueda haber error entre direcciones. Si asignamos a un equipo interior a una red un
número posible en Internet, un Router que esté separando la red de área local del mundo
Internet podría equivocarse y enviar hacia fuera un paquete interno a nuestra red. Por eso se
han reservado esas direcciones. Son estas
De la 10.0.0.0 a la 10.255.255.255
De la 172.16.0.0 a la 172.31.255.255
De la 192.168.0.0 a la 192.168.255.255
IP Versión 6
Como esto de Internet lo único que puede hacer es subir, y los 4.200 millones de direcciones
se van a quedar cortas tarde o temprano, se está definiendo una nueva versión de direcciones
IP, la esperada versión 6, que consistirá en direcciones de 128 bits, frente a los 32 actuales. Si
pensamos que cada bit que ampliemos doblamos la capacidad, vemos que la cifra de
direcciones posibles sobrepasa las necesidades actuales. (Por sobrepasar, sobrepasa la
capacidad de mi calculadora y el resultado es Overflow). Esta solución de 128 bits permitirá
conectar cualquier ordenador de una RAL con un número IP verdadero, es decir, un número de
la red Internet. Pero eso ya lo veremos en la próxima Guía del Estudiante.
Puertos de Comunicación.
Vamos a ver un concepto nuevo: el Puerto de Comunicación. Hasta ahora hemos hablado de
direcciones de máquina que son las que ve el protocolo IP. Dentro de una máquina podemos
tener varios servicios (Correo, Ftp, www, o cualquier otro programa que nosotros hagamos)
Cuando llega uno de esos paquetes que contienen información, la máquina debe saber
reconocer a que servicio está destinado. Eso lo sabe mediante el Puerto de Comunicación al
que va dirigido el paquete.
Los 1024 primeros puertos están reservados para distintos servicios de comunicación dentro
de la máquina. No los use para sus aplicaciones. Los servicios más conocidos utilizan los
siguientes puertos:
Observe que las palabras van separadas uno de otro mediante un punto. Existe un dominio
para cada país. (es para España, cu para Cuba, ar para Argentina, etc.) Pero aparte existe
una serie de dominios que no corresponden a ningún país, que se refieren a organizaciones.
Por ejemplo, com para organizaciones comerciales, edu para organismos de educación, gov
para organizaciones gubernamentales, mil para organizaciones militares, int para organismos
internacionales, org para otras organizaciones, net para organizaciones que manejan recursos
de la red. Lo de que no pertenecen a ningún país concreto es solo teórico. La mayoría de ellas
(edu, gov, mil) pertenecen a USA. Tampoco se merecen ninguna crítica por ello. Han sido los
artífices de Internet.
Buzones de correo. Un dominio identifica a una máquina. (Un ordenador conectado a la red)
Imagínese que ese ordenador es un servidor de correo. En el caso del ejemplo,
correo.laguiadelestudiante.es es el DNS del ordenador que gestiona el correo de empresa
La Guía del Estudiante. En ese ordenador hay varios buzones para varios usuarios. Uno de los
usuarios es Luis Suárez. Parece lógico que su buzón se llame suarez. Para saber que
pertenece a la máquina de correo citada, el nombre de ese buzón deberá llevar el DNS de esa
máquina. No se podrá poner como nombre del buzón suarez.correo.laguiadelestiante.es ya que
suarez no es un dominio, es un buzón. El nombre de un buzón se separa del DNS mediante el
carácter @. Suarez@correo.laguiadelestudiante.es es el nombre de un buzón único en el
mundo. Vamos a avanzar un poco más. Imagínese que ese servidor de correo tiene varios
clientes cuyo apellido es suarez. Deberemos distinguir uno de otro. Para ello basta con seguir
el mismo método que con los dominios. Encadenar varios nombres separados con un punto.
Por ejemplo, podríamos poner cono nombre de buzón luis.suarez@laguiadelestudiante.es Ya
puede enviar tranquilamente sus mensajes a ese buzón. Los enrutadores de internet no se
preocuparán de lo que hay a la izquierda de la @. Buscarán la máquina
correo.laguiadelestudiante.es. Una vez que la encuentren, depositarán el mensaje y será esa
máquina quien se encargue de meter ese mensaje en el buzón luis.suarez de su propiedad.
El protocolo TCP/IP no se utiliza solamente en Internet. Las redes de Area Local en su gran
mayoría lo utilizan. Por lo tanto lo que se va a explicar en este capítulo no solamente le va a
servir para Internet. Puede hacer programas que trabajen sobre una red de área local que usa
los mismos controles.
ADSL (Asimetric Data Suscriber Line). Aprovechando una línea telefónica existente, se envía
superpuesto al servicio telefónico unas señales de datos, que no interfieren para nada el uso
del teléfono conectado a esa línea. Esta técnica supone una modulación de los datos sobre
dos portadoras a varios cientos de kilociclos (la frecuencia de la portadora depende del ancho
de banda contratado) por lo que las pérdidas son elevadas. Esto implica que el módem ADSL
instalado junto al terminal telefónico deberá estar muy cerca de la central telefónica desde la
que envían los datos (Creo que el máximo son unos 2 kilómetros) Por lo tanto, es difícil instalar
esta opción en abonados rurales, o en zonas urbanas con instalación de cables muy viejas.
Pueden contratarse velocidades de 128 a 512 Kbytes en sentido Usuario -> Internet, y de 256
a 2048 en sentido Internet -> Usuario. Esta asimetría se debe a que normalmente son mas los
datos que se “bajan” que los que se envían, y de esta forma se optimizan los recursos del
sistema. Mediante esta conexión, estamos conectados continuamente a Internet, y
generalmente se tiene un número IP real fijo, pero existen compañías que lo que están
enviando a través de esa conexión de alta velocidad es una toma de una red de área local que
a su vez está conectada a Internet. El número IP, en el caso que le den una número real de
Internet, ninguna empresa le garantiza que se lo vayan a conservar para siempre. Creo que
ADSL es hoy por hoy el mejor sistema de conexión a Internet.
Conexión mediante la Conexión Telefónica a redes. Para esta forma de conexión, los ISP
disponen de varias direcciones IP. Es muy normal que cada ISP tenga uno o varios paquetes
256 direcciones, pero eso dependerá del trafico real que tenga. Este ISP se va a reservar
algunas de esas direcciones para su uso particular (Una al menos para que se le pueda llamar
y comunicarse con otros servidores desde dentro de la red, otra para el servidor de páginas
Web, servidor de correo, servidor DNS si lo tiene, etc.) y el resto las va a asignar durante el
tiempo que dure la conexión a sus clientes. Esta asignación de direcciones se le denomina
asignación dinámica. Cuando nos conectamos a Internet a través de la conexión telefónica a
redes tendremos una dirección. La próxima vez tendremos otra. Por eso, en el ámbito
particular no podemos dar una dirección IP para que nos llamen, ya que es variable.
Deberemos pedir la conexión a nuestro ISP que nos pedirá el nombre de usuario y la
contraseña. Esta operación la realiza el acceso telefónico a redes de Windows. Si coinciden
nombre y contraseña con las que tiene el ISP en su banco de datos, nos asigna un número IP
de los que tenga disponible y ya estamos conectados.
Conexión a través de Red de Area Local. Este es el caso de conectarse mediante una RAL
o mediante la mayoría de las conexiones rápidas que nos brindan las compañías de
telecomunicación por cable. En este caso, la conexión a Internet se realiza en un punto de la
red, mediante un ordenador o hub denominado Gateway, Puerta de Enlace o servidor Proxy.
La puerta de enlace este tiene dos entradas de red, una conectada a Internet (A través de un
Una vez conectados, sea cual sea el medio utilizado para ello, ya estamos en la red. Ahora ya
podemos hacer Ping o Telnet a otro ordenador que también esté conectado. No se preocupe
por no saber que es hacer ping o telnet. Eso es cosa de los gurús informáticos. Algunos
alumnos, afortunadamente los menos, creían que estaban conectados a Internet solamente
cuando se abría el navegador y les mostraba la página Web de su ISP. Otros se creían
plenamente conectados cuando eran capaces de abrir su buzón de correo mediante el Outlook
Express. Pero esos efectos no son por estar conectados, sino por Abrir un Servicio.
Esto nos introduce en el concepto de servicio de Internet. La conexión TCP/IP que habíamos
establecido sólo es el soporte de comunicación para que pase por él un servicio. Uno de ellos
puede ser el de páginas Web.(http), otro el de correo, (POP3 y SMTP), otro el de FTP (ftp) ,
otro el Chat (irc).
http (hiper text transfer protocol Protocolo de transferencia de hipertexto) es el servicio para
recibir páginas Web. Las páginas Web están diseñadas de tal forma que contengan muy poca
información (para que se puedan bajar rápidamente) Existen lenguajes que pueden hacer eso
(HTML), que con muy poca información generan páginas preciosas. Pero la poca información
que tienen se tiene que cubrir con algo: con un navegador, que es como un editor de texto
pero un poco más complicado, porque es capaz de componer la página con toda su belleza a
partir de esa información tan escasa.
ftp (File Transfer Protocol Protocolo de transferencia de ficheros) Se usa para enviar o recibir
fichero completos. Es el servicio que usa cuando se baja un programa de la red.
SMTP (Single Mail Tranfer Protocol Protocolo simple de transferencia de correo) Se utiliza
para enviar mensajes de correo desde el usuario al servidor de correo y para el tráfico de estos
mensajes entre servidores de correo.
POP 3 (Post Office Protocol 3 Protocolo de la Oficina de Correo) Se utiliza para que el
servidor de correo envíe los mensajes de correo al usuario.
Irc (Internet Relay Chat Reenvío de Chat por Internet) Mediante este protocolo una máquina
a la que han accedido varios usuarios recibe mensajes escritos de uno de ellos y lo retransmite
a los demás.)
Cada uno de estos servicios tiene asignado un puerto. Esos puertos puede verlos en la lista
que tiene más atrás. Hay mas servicios, pero con esto creo que tenemos suficiente para
comenzar a hablar de lo que puede hacer Visual Basic con todos estos servicios.
Comencemos por el principio. Por el control que nos permite comunicarnos con otro ordenador
mediante TCP y UDP . El Control Winsock
Control Winsock
El control WinSock permite conectarse a un equipo remoto e intercambiar datos con los
Protocolos UDP y TCP. Ambos protocolos se pueden usar para crear la comunicación con
cualquier servicio de los expresados anteriormente. Podemos crear también aplicaciones que
residan en un servidor al que accedemos desde varios clientes. Estamos en ese caso creando
aplicaciones Cliente - Servidor
El control Winsock podríamos decir que es el control más elemental de los controles que VB
dedica a las redes IP, ya que lo único que hace por sí es conectarnos. No implementa ningún
servicio. Eso se lo tenderemos que hacer nosotros con nuestro programa. Hay controles que
nos dan directamente la conexión y el servicio para el que han sido diseñados (WebBrowser,
Internet Transfer Control) Con este sólo tenemos la conexión, pero con muchos datos y mucho
control sobre ella. Quizás por eso este es el más bonito. Una vez con la conexión establecida,
podemos establecer un servicio utilizando código puro y duro. Tiene esa opción o usar los
controles específicos que Microsoft y otras firmas le han preparado para que Vd. trabaje
menos.
Propiedad LocalIP
Devuelve la dirección IP de la máquina local en el formato xxx.xxx.xxx.xxx Es de sólo lectura y
no está disponible en tiempo de diseño.
MiVariable será una variable de tipo String. Si la dirección de su equipo es la 127.0.0.1 significa
que no está conectado.
Propiedad LocalHostName
Devuelve el nombre de la máquina local. Es de sólo lectura y no está disponible en tiempo de
diseño.
Si desea cambiarlo, teclee el nombre nuevo y haga click en Aceptar. Como es habitual en
Windows, deberá reiniciar el equipo para que tenga efecto el cambio.
Propiedad Protocol
Devuelve o establece el protocolo, TCP o UDP
El control debe estar cerrado para poder establecer esta propiedad. Si lo tiene abierto, debe
cerrarlo previamente mediante el método Close.
Cuando un equipo quiere enviar información debe conocer la dirección completa del equipo al
que va a llamar. La dirección completa es su dirección IP y el puerto al que va dirigida la
información. Si el equipo va a recibir, lo único que debemos decirle es el puerto por el que debe
estar escuchando. No necesita conocer más datos, ya que la dirección IP propia ya la conoce y
no necesita conocer ningún dato del equipo que le va enviar información.
Una aplicación es servidor cuando está escuchando. Así de fácil. (Recuerde el capítulo donde
vimos el DDE, pues el concepto cliente servidor en el tema del establecimiento de la
comunicación es el mismo). Si una aplicación escucha es que otra puede querer enviarle
información. Esa aplicación que quiere enviar información será la aplicación cliente. A poco
que nos esforcemos, con este concepto elemental de aplicación cliente – servidor, una
aplicación puede ser cliente en un momento y servidor en otro. Cuando se hable de aplicación
cliente, ya en términos de la función que realiza esa aplicación, se denomina aplicación
servidor a aquella que está dispuesta para enviar información al cliente, o realizar alguna
operación con los datos que el cliente le envía. Pero en este caso, estamos hablando de
cliente – servidor visto desde el punto de vista de las comunicaciones. No se extrañe por lo
tanto, que esa definición tan simple de que una aplicación será servidor cuando está
escuchando y cliente cuando está enviando información.
Si queremos enviar información, es decir, si queremos que nuestra aplicación sea una
aplicación cliente, debemos indicarle al Winsock el número IP o el DNS de la máquina con la
que queremos conectar. Esto lo establecemos con la propiedad RemoteHost. También le
debemos indicar el número del puerto en el que nos está esperando esa máquina. Esta
información se la introducimos con la propiedad RemotePort. Nunca le debemos forzar el
puerto propio.
Propiedad RemoteHost
Devuelve o establece el equipo remoto al que se van a enviar los datos, o el equipo del que los
recibe. El equipo remoto se le puede identificar por su número IP o por su DNS.
VariableTipoString = NombredelControlWinsock.RemoteHost
Esta propiedad puede cambiarse en tiempo de ejecución cuantas veces sea necesario, para
Otro parámetro que es necesario introducir al Winsock cuando nuestra aplicación es una
aplicación cliente (envía información) es el puerto del equipo remoto al que vamos a enviar
información. El puerto destino dependerá del servicio que estamos implementando en nuestra
aplicación. Este parámetro se introduce en la propiedad RemotePort.
Propiedad RemotePort
Devuelve o establece el número del puerto remoto con el que conectar. El valor pasado en esta
propiedad es un Long comprendido entre 1 y 65535. El numero predeterminado es el 80.
Ya tenemos establecidas las propiedades que necesita un control Winsock para enviar datos a
un equipo remoto. Veamos ahora como se establece la comunicación.
El equipo que va a recibir información solamente necesita conocer el puerto por el que debe
escuchar. Esto se lo decimos mediante la propiedad LocalPort.
Propiedad LocalPort
Devuelve o establece el puerto local que desea usar. Es de lectura y escritura, y está
disponible tanto en tiempo de diseño como en tiempo de ejecución.
Esta propiedad solamente debe utilizarse para poner Winsock a recibir. Cuando va a transmitir
datos, aunque puede asignar un puerto determinado para enviarlos es mucho mas prudente
dejar al Winsock que utilice el que quiera. De esta forma se asegura que siempre emitirá a
través de un puerto libre. (Experiencia propia: Si asigna un puerto determinado para transmitir
lo mas probable que le va a pasar es que se le cuelgue el ordenador) Para asegurarse de que
no ha asignado ningún puerto, basta con poner el valor 0 en esta propiedad.
Por lo tanto, para paquetes UDP no hace falta hacer nada para establecer la comunicación con
el equipo remoto, ni para transmitir ni para recibir.
El equipo que desea enviar datos debe solicitar la conexión. Si existe un equipo destino con
dirección IP y el puerto adecuado, este equipo le contestará. (Vea mas adelante el método
Accept) También puede ocurrir que el equipo destino o no existe, o reciba la solicitud de
conexión y no la acepte. Veremos más adelante qué ocurre en el caso de que nadie atienda la
solicitud.
Método Connect
Solicita una conexión con un equipo remoto.
Comentarios
Debe invocar el método Connect cuando intente establecer una conexión TCP.
Para recibir, primero hace falta poner el Winsock a escuchar, y una vez que reciba una
comunicación de un equipo llamante aceptarla. Para poner a escuchar al Winsock basta con
invocar el método Listen.
Método Listen
Crea un socket y lo establece a modo de escucha. (Sólo para conexiones TCP).
Sintaxis NombredelWinsock.Listen
Un Winsock solamente puede recibir una comunicación. Por lo tanto, solamente podrá atender
a un corresponsal. Esto no es lo que se busca en la mayor parte de los casos, sino que lo que
se pretende es estar a la escucha, y atender a todas las llamadas que entren, pudiendo
atender a dos o más comunicaciones simultáneamente.
Para lograr esto lo que hacemos es tener un Winsock permanentemente a la escucha para
recibir los sucesivos intentos de conexión de los corresponsales, y una vez que se recibe uno,
se crea una instancia del winsock, y es, con esta instancia, con la que le invocamos el método
Accept para aceptarla. De esta forma tendremos tantas instancias (copias) del winsock como
comunicaciones activas, más el winsock original que es el que se queda a la escucha de
nuevos intentos de conexión. Ni que decir tiene que, una vez terminada la conexión, debe
cerrarse la instancia correspondiente a esa conexión.
Evento ConnectionRequest
Se produce en el equipo local cuando el equipo remoto solicita una conexión.
Sólo para aplicaciones de servidor TCP. El evento se activa cuando llega una solicitud de
conexión. A partir de la activación de este evento, las propiedades RemoteHostIP y
RemotePort almacenan la información acerca del cliente.
Este evento pasa como parámetro un número Long con el identificador de la solicitud de
conexión. Este identificador (IdSolicitud) lo genera el servidor (el equipo que recibe) y lo
necesitaremos para aceptar la conexión mediante el método Accept.
Metodo Accept
Este método se utiliza para aceptar una conexión entrante cuando se está tratando un evento
ConnectionRequest.. Sólo se usa con el protocolo TCP.
Veamos como se crea una nueva instancia del control. Se usa el método Load. Para ello, en el
Winsock que colocamos en el proyecto le ponemos, en su propiedad Index el valor 0. Con esto
le estamos indicando que es una matriz de controles, pero solamente existe uno, cuyo índice
es el 0. Cuando utilizamos el método Load lo que hacemos es añadir un elemento más a esa
matriz de controles.
Si el control que hemos introducido en el formulario se llamaba Wsk, y le hemos puesto su
propiedad Index = 0, para añadir el segundo elemento de esa matriz, que tendrá la propiedad
Index = 1, ejecutaremos la siguiente línea de código:
Ahora ya tenemos una matriz con dos controles. (Indices 0 y 1) Dejaremos el winsock con
índice 0 para seguir recibiendo peticiones de comunicación y ejecutaremos el método Accept
sobre el segundo winsock (el de Index = 1)
Unload Wsk(1)
En principio no hay problema para que cada nueva instancia lleve un Index incrementado en 1
respecto a la conexión anterior. Basta con declarar una variable tipo Long para introducir ese
índice, y previsiblemente nunca llegaremos a superar los límites del Long. Ahora bien, es
mucho mas elegante reutilizar los números de las instancias que se van cerrando. Para esto
es necesario utilizar una variable o una matriz, donde llevemos la cuenta de las instancias
todavía abiertas del winsock original.
Cada una de las instrucciones anteriores se deberán hacer en los eventos apropiados del
winsock. Así, el crear un nuevo winsock lo haremos en el evento ConnectionRequest, y el
descargarlo lo haremos en el evento Close.
Ya tenemos la conexión establecida. Veamos ahora otras propiedades y métodos que nos
permitirán conocer la identidad del corresponsal y lo que hay que hacer para enviar y recibir
información.
Una vez establecida la conexión, mediante el método Connect por parte del cliente, y el
método Accept por parte del servidor, ya podemos conocer la identidad del corresponsal
mediante la propiedad RemoteHostIP
Evento Connect
Este evento ocurre en el Winsock que ha solicitado la conexión, cuando ésta se ha
completado. Ocurre por lo tanto cuando el winsock remoto invoca el método Accept. Puede
usar este evento parta conformar que se ha realizado la conexión.
En las aplicaciones de cliente, después de establecer la conexión con el método Connect, esta
propiedad contiene la cadena IP del equipo remoto.
Cuando utiliza el protocolo UDP, después de producirse el evento DataArrival, esta propiedad
contiene la dirección IP del equipo que ha enviado los datos UDP.
Propiedad State
Permite conocer el estado del winsock y de la conexión que está realizando. Es sólo de lectura
y no está disponible en tiempo de diseño. Devuelve un integer
Nos puede devolver uno de los siguientes valores que corresponden a las siguientes
constantes
0 sckClosed Cerrado
1 sckOpen Abierto
2 sckListening Escuchando
3 sckConnectionPending Conexión pendiente
4 sckResolvingHost Resolviendo Host
5 sckHostResolved Host resuelto
6 sckConnecting Conectando
7 sckConnected Conectado
8 sckClosing Cerrando la conexión
9 sckError Ha detectado un error
Propiedad SocketHandle
Esta propiedad se usa para utilizarla con las Apis de Windows. Devuelve el manipulador del
Winsock. Es de sólo lectura y no está disponible en tiempo de diseño.
Donde I es el índice del Winsock. Como se decía más atrás, esta propiedad solamente se
emplea usando APIS
Hemos visto varias propiedades, métodos y eventos del control Winsock. Hasta ahora hemos
visto:
Mediante estas propiedades y métodos establecemos la conexión. Nos falta ahora por ver
aquellos métodos usados durante la transmisión o recepción de datos.
Método SendData
Envía datos a un equipo remoto. Se emplea tanto en UDP como en TCP. No devuelve ningún
valor.
Donde NombredelWinsock es el nombre del winsock que debe enviar los datos, I es el índice
de ese control (en el caso de que el winsock sea una instancia de un winsock original, como se
explicó más atrás) y Datos son los datos a enviar, por ejemplo el nombre de una variable que
los contiene o la propiedad text de un textbox donde se han escrito los datos a enviar.
Si los datos a enviar son binarios, deberán enviarse como una matriz de bytes.
Cuando pasa una cadena UNICODE, se convierte a cadena ANSI antes de enviarla a la red.
Método GetData
Recupera los datos existentes en el búffer de recepción del winsock, lo almacena en una
variable y borra el contenido del búffer.
El método GetData se suele usar con el evento DataArrival, que incluye el argumento
bytesTotales. Si especifica una longMáx menor que el argumento bytesTotales, obtendrá el
mensaje de advertencia 10040, que indica que se perderán los bytes restantes.
Evento DataArrival
Se produce cuando llegan nuevos datos. Pasa como parámetro un Long con el número de
bytes recibidos. (NombredelWinsock_DataArrival (bytesTotales As Long)
Este evento no se producirá si no recupera todos los datos con una llamada GetData. Sólo se
activa cuando hay datos nuevos. Utilice la propiedad BytesReceived para comprobar la
cantidad de datos disponibles en cualquier momento.
Sintaxis NombredeWinsock.BytesReceived
Método PeekData
Este método es similar a GetData, pero no elimina los datos extraídos del búffer de recepción.
Exixte otra diferencia, PeekData solamente funciona en las conexiones TCP.
La sintaxis es similar a la de GetData.
Los parámetros de este método son los mismos que para GetData.
Evento SendProgress
Se produce mientras se están enviando datos. Pasa como parámetros los bytes enviados
desde la última vez que se activó el evento (bytesEnv) y los bytes que esperan a ser enviados
en el búffer de transmisión (bytesRest)
Evento SendComplete
Se produce cuando termina una operación de envío. No pasa argumentos.
NombredelWinsock_SendComplete()
Evento Error
Se produce siempre que ocurre un error en los procesos de segundo plano (por ejemplo, un
fallo al conectar o un fallo al enviar o recibir en segundo plano). Pasa como parámetros el
código de error, la descripción del error, el scode del error, el origen del error, y un booleano
para cancelar o aceptar el que presente un cuadro de dialogo con el aviso de error. (Los
parámetros de fichero de ayuda y contexto de ayuda, personalmente no los he visto en ninguno
de los ejemplos preparados.
Evento Close
Se produce cuando el equipo remoto cierra la conexión. Las aplicaciones deben usar el método
Close para cerrar correctamente una conexión TCP.
También puede usar el método Bind en una comunicación TCP. Se emplea cuando hay más
de un adaptador de red en el equipo (Un equipo con dos placas de red, y cada una de ellas con
un número IP). Si en ese equipo hay un Winsock, ¿Qué número IP tiene?. Podría tomar uno
de los dos indistintamente. Mediante Bind podemos asignar uno de esos números IP a un
Winsock.
Método Bind
Especifica el puerto local y la dirección IP local a usar en las conexiones TCP.
Donde:
PuertoLocal: Puerto utilizado para realizar una conexión.
IPLOcal: Dirección IP local sobre la que se va a establecer la conexión.
El programa podrá recibir simultáneamente desde varios corresponsales. Para enviar, podrá
hacerlo de uno en uno, es decir, en un determinado momento solamente podrá estar enviando
a otro corresponsal, aunque podrá enviar, en momentos sucesivos, a cuantos corresponsales
estén activos. El envío de información se podrá hacer simultáneamente a la recepción.
Para lograr esto, es necesario disponer de dos winsock en la aplicación. Uno, que estará
recibiendo en un puerto y atendiendo a todas las llamadas entrantes, (Wsk1) y otro, que se
usará solamente para enviar información a través de las llamadas salientes (Wsk2). Cada vez
que se reciba una llamada entrante, se creará una instancia de Wsk1, que será en realidad la
que acepte la llamada usando su método Connect.
Debemos elegir un puerto para la escucha. A la hora de decidir el número del puerto debemos
pensar que no se pueden usar los 2024 primeros puertos, que el puerto elegido no esté siendo
usado de forma internacional por otro servicio (que no esté registrado a ninguna aplicación a
nivel mundial) y que no esté usado en nuestros PCs para otro servicio. En nuestro ejemplo, lo
que vamos a hacer es leerlo de un fichero de configuración, y colocarlo en un TextBox llamado
TbPuertoLocal. De esta forma, al winsock lo programaremos con el puerto indicado en ese
TextBox, y a ese TextBox le llevaremos el dato con la lectura del fichero de configuración,
operación que se realiza durante la carga del formulario. De esta forma podremos poner en el
fichero de configuración el puerto deseado, sin necesidad de variar el programa.
Ya vamos teniendo una idea de la interfase gráfica necesaria para empezar. Dado el fin
didáctico de este ejemplo, se van a mostrar todos los parámetros que entran a formar parte de
la comunicación.
En esta figura se pueden ver dos partes bien diferenciadas, una en azul, (Máquina Local,
Instancias activas, Textos/Ficheros recibidos) con la parte de la aplicación correspondiente a la
recepción, La parte roja (Equipo remoto llamado) corresponde a la parte de transmisión.
En la parte roja tenemos todo lo relativo a la transmisión, y las cajas de texto pare enviar
mensajes de texto y ficheros.
Fig. 4 Este es el aspecto final del la aplicación. Vea en el disco de ejemplos el código
completo.
Para poder empezar a usar el control Internet Transfer (Inet) debe estar conectado a una red
que disponga de algún servidor Http y Ftp. No tienen porqué ser directamente a Internet.
Puede estar conectado a una Intranet donde existan. Pero en este control no podemos hacer lo
que hacíamos con el Winsock, conectarnos con nosotros mismos. Este control está para
acceder a sitios con un servidor http o ftp.
Vamos a ir introduciendo las propiedades del Inet al mismo tiempo que lo conectamos a
Internet. Y lo primero es saber si se va a conectar directamente o a través de Proxi. Supongo
que el alumno sabrá que es un Proxi, por lo que solamente vamos a explicar la propiedad que
tiene el Inet para acceder desde una Red de Area Local (RAL) con o sin Proxi y así zanjamos
esa primera dificultad que puede tener para conectarse a la Red:
Propiedad AccessType
Establece si el Inet se comunica directamente a Internet o a través de Proxi.
Puede usar un proxi distinto para acceder con protocolo Http o Ftp, y determinar porque puerto
accede a cada uno de estos servicios. El proxi suele estar programado para que en la parte
interior de la red se acceda a los servicios externos a través de un puerto distinto al real. Es
típico en la programación de los proxis acceder desde la red interior al sevicio http a través del
puerto 8080, en vez del puerto 80.
Esta forma de establecer la propiedad hará que para ftp use el servidor Proxi CorpFTP, por el
puerto 123, y que para http use el servidor CorpHTTP por el puerto 131.
Método OpenURL
Abre y devuelve el documento ubicado en la dirección URL especificada. El documento se
devuelve con el tipo Variant. Cuando termina la ejecución del método, las propiedades URL (y
las partes de la dirección URL como el protocolo) se actualizan para reflejar la URL actual.
Donde
Variable Variable tipo string donde se guardarán los datos obtenidos (Texto) o una
variable Variant donde se guardarán los datos obtenidos como matriz de bytes
url Dirección URL que se desea abrir.
TipoDatos Entero que especifica el tipo de datos. Acepta los valores 0 (Predeteminado)
para que obtenga los datos como cadena de caracteres, o 1, para que los
obtenga como matriz de bytes. Si la URL es una página Web debe poner 0 en
este valor.
<html>
<head>
<title>Ministerio de Asuntos Exteriores. España</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>
<frameset cols="1,*" frameborder="NO" border="0" framespacing="0">
<frame name="nulo" scrolling="NO" noresize >
<frame name="inicial" scrolling="auto" src="/mae/index.jsp" >
</frameset>
El método OpenURL trabaja de forma síncrona, es decir, espera a recibir el contenido del
fichero para ejecutar la línea siguiente del código. Cuando se ejecuta la línea
TextoPagina = Inet1.OpenURL(TbURL.Text, 0)
La conexión tarda un tiempo en ejecutarse, y el servidor tarda cierto tiempo en buscar el fichero
correspondiente a la petición y enviarlo. Y mientras tanto, el código está parado en esa línea, y
es solamente cuando se termina de recibir todo el contenido o cuando el Inet genera un error
de comunicación cuando se ejecuta la siguiente línea.
TbTextoPagina = TextoPagina
De no ser síncrona, y dado que la conexión tarda algo (por muy poco que fuese) al ejecutar
esta segunda línea la variable TestoPagina estaría vacía, por lo que TbTextoPagina siempre
aparecería en blanco.
El control Inet es un poco delicado. Como sabrá, la propiedad por defecto de un TextBox es la
propiedad Text. Sería de suponer que la línea:
TextoPagina = Inet1.OpenURL(TbURL, 0) =
Es igual a esta otra:
TextoPagina = Inet1.OpenURL(TbURL.Text, 0)
Pues el Inet, con la primera línea, da error. Debe tomar TbURL como el objeto tipo control.
Sale este error:
Error 35752 en tiempo de ejecución:
La dirección URL está mal generada
Es posible que desee saber si ha conectado con la URL solicitada. Eso puede saberlo
analizando la el estado de la conexión. Pero no existe ninguna propiedad que se lo indique.
Tiene que ir a buscarlo al único procedimiento que tienen el control Internet Transfer: el evento
StateChanged
Evento StateChanged
Se produce siempre que hay un cambio de estado en la conexión. Aporta el parámetro estado
que es un integer.
Este procedimiento no se comporta de igual forma cuando se conecta con una página Http que
cuando lo hace con un servidor Ftp. Nos centraremos primero en conexiones con servidores
Http, y veremos más adelante como se comporta con servidores Ftp.
Otro método muy importante para ver páginas en la Web el método GetHeader, que permite
recuperar el encabezado o paerte de él de una página. Este método puede ser muy útil cuando
tenemos que bajar una página, pero previamente queremos comprobar que ya ha sido
actualizada.
Método GetHeader
Recupera el encabezado de un archivo HTTP. Devuelve un string.
Donde:
Inet1 Nombre del control Inet
NombreEnc Opcional. Cadena que especifica el encabezado que desea recuperar. Si no
indica ninguno los recuperará todos. Los valores permitidos para nombreEnc
son (Pueden existir otros, dependiendo el servidor)
Date Devuelve la hora y la fecha de la transmisión del documento. El formato de los
datos obtenidos es Fri, 01 Nov 2002 12:10:51 GMT. Este dato puede servir
para sincronizar los relojes de muchos ordenadores, sin necesidad de recurrir a
complejos y costosos sistemas basados en GPS, ya que todos los ordenadores
pueden ponerse en hora tomándola de una página Web de referencia.
MIME-version Devuelve la versión del protocolo MIME. Normalmente los servidores no
contestan a este parámetro.
Server Devuelve el tipo del servidor. (Esta respuesta tienen mucho peligro, ya que los
hackers es lo primero que miran para realzar un ataque)
Content-length Devuelve la longitud en bytes de los datos que enviará cuando ejecutemos el
método OpenURL.
HTTP/1.1 200 OK
Server: Microsoft-IIS/5.0
Content-Location: http://www.mae.es/index.htm
Date: Fri, 01 Nov 2002 12:10:51 GMT
Content-Type: text/html
Accept-Ranges: bytes
Last-Modified: Wed, 20 Feb 2002 15:26:44 GMT
ETag: "0129d023bac11:991"
Content-Length: 420
Age: 0
Método Cancel
Cancela la solicitud actual y cierra las conexiones establecidas actualmente. No devuelve
ningún valor.
Sintaxis Inet1.Cancel
Es posible que este método genere un error, ya que en algunas ocasiones cierra la conexión
sin poder cancelar ya el proceso que está en curso. Tenga cuidado al usarlo.
Vamos a ver ahora el método más útil de este control. Con él podemos hacer todas las
funciones de envío y recogida de ficheros.
Método Execute
Ejecuta una solicitud a un servidor remoto. Sólo puede enviar solicitudes válidas para el
protocolo que esté utilizando. Lógicamente este método funcionará de forma distinta y
necesitará parámetros distintos dependiendo del protocolo que estemos usando. Para el
protocolo Http la sintaxis es la siguiente:
Operación Descripción
GET Recupera datos de la dirección URL especificada en la propiedad URL.
HEAD Envía los encabezados de la solicitud.
Envía datos al servidor. Los datos están en el argumento datos. Éste es
POST un método alternativo a GET para el que se especifican las instrucciones
adicionales en el argumento datos.
Operación de colocación. El nombre de la página que se va a reemplazar
PUT
está ubicado en el argumento datos.
Es difícil probar estos comandos. Normalmente las páginas comerciales no ofrecen los
servicios que a nosotros nos gustaría probar. Vea en los ejemplos entregados una aplicación
del comando PUT (Que no podrá ensayar en su casa excepto que tenga acceso a un servidor
con Password)
Vamos a mostrarle un ejemplo del comando GET. Pero previamente tenemos que hablar de un
método que antes habíamos citado de pasada:
Método GetChunk
Obtiene datos cuando se produce el evento StateChanged al llegar al estado de respuesta
completa (icResponseCompleted = 12) Este método se invoca después de ejecutar el
método Execute como una operación GET.
Cuando se ejecuta la operación GET los caracteres recibidos desde el servidor se almacenan
enun buffer. El método GetChunk recupera los caracteres del buffer en bloques de un tamaño
que se debe fijar en el parámetro tamaño. Si la información recibida es mayor que tamaño será
necesario hacer un bucle hasta completar la recuperación de todos los caracteres. El
parámetro tipoDatos puede ser icString (0) para recuperar una cadena de caracteres, ó
icByteArray (1) para recuperar una matriz de bytes.
Este es el código que se ha metido en el SelectCase del evento StateChanged, para el caso
que State sea 12
Dim vtData As Variant ' Variable de datos.
Dim strData As String, bDone As Boolean, F As Integer
' Obtiene el primer bloque.
vtData = Inet1.GetChunk(1024, icString)
DoEvents
Do While Not bDone
strData = strData & vtData
DoEvents
' Obtiene el bloque siguiente. Está sacando bloques de 1024 caracteres
'hasta que no queda ninguno. En ese caso, GetChunk devuelve la cadena vacía
vtData = Inet1.GetChunk(1024, icString)
If Len(vtData) = 0 Then
bDone = True
End If
Loop
TbTextoExecute.Text = strData
If TbNombreFichero <> "" Then
F = FreeFile
Open "C:\_aaa\" & TbNombreFichero For Output As #F
Print #F, strData
Close #F
End If
url es el nombre del servidor. Este parámetro puede darle algún problema si no conoce
exactamente el nombre del servidor, (Si lleva o no lleva el encabezado FTP:// , si es una DNS
o un número IP, ..) Estas direcciones son un ejemplo de ello:
“FTP://ftp.microsoft.com” “Ftp://10.3.22.119”
Instrucción es una cadena que incluye el comando (GET, PUT, ..) y el nombre o nombres de
los archivos necesarios. Ejemplos:
Operación Descripción
CD archivo1 Cambiar directorio. Cambia al directorio especificado en archivo1.
CDUP Cambiar al directorio superior. Equivale a "CD.."
CLOSE Cierra la conexión FTP actual.
DELETE archivo1 Elimina el archivo especificado en archivo1.
Directorio. Busca en el directorio especificado en archivo1. Se admiten
comodines, pero el host remoto determina la sintaxis. Si no especifica
DIR archivo1
archivo1, obtendrá una lista completa del directorio de trabajo actual.
Puede usar el método GetChunk para obtener los datos del directorio.
Recupera el archivo remoto especificado en archivo1 y crea el nuevo archivo
GET archivo1 archivo2
local especificado en archivo2.
Lista. Busca en el directorio especificado en archivo1. Se admiten comodines,
LS archivo1 pero el host remoto determina la sintaxis. Puede usar el método GetChunk
para obtener los datos de los archivos del directorio.
Crear directorio. Crea el directorio especificado en archivo1. El éxito de la
MKDIR archivo1
operación depende de los privilegios del usuario en el host remoto.
Copia el archivo local especificado en archivo1 en el archivo del host remoto
PUT archivo1 archivo2
especificado en archivo2.
Mostrar directorio de trabajo. Devuelve el nombre del directorio actual. Puede
PWD
usar el método GetChunk para obtener los datos.
QUIT Termina la sesión del usuario actual.
RECV archivo1 Recupera el archivo remoto especificado en archivo1 y crea un nuevo archivo
archivo2 local especificado en archivo2. Equivale a GET.
Cambia el nombre del archivo indicado en archivo1 por el nombre
RENAME archivo1
especificado en archivo2. El éxito de la operación depende de los privilegios
archivo2
del usuario en el host remoto.
Eliminar directorio. Elimina el directorio remoto especificado en archivo1. El
RMDIR archivo1
éxito de la operación depende de los privilegios del usuario en el host remoto.
SEND archivo1 Copia el archivo local especificado en archivo1 en el archivo del host remoto
archivo2 especificado en archivo2. Equivale a PUT.
SIZE archivo1 Devuelve el tamaño del directorio especificado en archivo1.
Siguiendo con el servidor Ftp de Microsoft podemos ensayar algunos de estos comandos:
Nos devuelve el directorio. Como no hemos realizado ninguna operación sobre ese directorio,
nos devolverá el directorio del raíz.
No contiene ficheros sino más directorios. Uno de ellos es el directorio MISC. Podemos
cambiarnos a él con la instrucción:
Y para comprobarlo vamos a pedir otra vez el directorio. Ahora ya vemos que ya contienen
ficheros, y más directorios.
beckyk/
CBCP.TXT
csformat/
DAILYKB/
DISCLAIM.TXT
FDC/
friKB/
FULLKB/
Homenet/
INDEX.TXT
Jeffreyf/
KB/
KBSPV/
Markesh/
monKB/
NBFCP.TXT
NBFCP2.TXT
NBFCP3.TXT
NBFCP4.TXT
NBFCP5.TXT
Peach/
PRODUCT.TBL
ReadMe1.txt
satKB/
Store/
STORE1/
sunKB/
test/
thuKB/
TREE.COM
tueKB/
wedKB/
Para realizar este tipo de operaciones debe asegurarse que la operación se ha terminado antes
de iniciar una nueva operación. Esto puede verlo con el valor de State del procedimiento
StateChanged. Ha de esperar a que tenga el valor icResponseCompleted ( = 12). Es en ese
momento cuando debe invocar el método GetChunk para sacar los datos del búffer. Es el
mismo código del Select Case que ha visto antes, que le repito aquí por comodidad:
Case 12
LState = "12 - Operación completada"
Una vez en el directorio adecuado, podemos “bajarnos” un fichero mediante este código:
The Microsoft Knowledge Base is currently published to this server in three formats. Two of the
formats are published for legacy reasons and are now obsolete. The KB will be posted to this
server only in HTM format as of August 1, 2002.
The Microsoft Knowledge base has been posted to this server in text format since 1993. With the growth of the world wide
web and the spread of browser technology, the HTML formats of the KB became the defining standard, and in many cases
the text rendering of the content lost important data only visible in the HTML view. We will be discontinuing text format
output to ensure our customers don't miss critical data in the articles they download from this server. One variant of text
files we have been publishing is SPV files. SPV files group articles by the product that they apply to. An article can only
belong in one group, which avoids duplicates if more than one collection is downloaded. Today, many articles apply
equally to multiple products. As such, the binary or arbitrary division of articles results in information being hidden from
customers who needed it. Effective August 1, we will no longer divide articles on this server by these arbitrary groupings
and the published SPV files will be deleted.
The KB will continue to be available for download at ftp://ftp.microsoft.com/misc/kb as HTM files.
The files which were in this location will be available until July 31, 2002 at ftp://ftp.microsoft.com/misc1.
The zipped SPV files that were being stored here are now located at
ftp://ftp.microsoft.com/misc1 until 07/31/02 at which time these files will no longer be available.
In summary:
The following folders and/or files at:
ftp://ftp.microsoft.com/bussys/...../kb
ftp://ftp.microsoft.com/deskapps/...../kb
ftp://ftp.microsoft.com/developr/...../kb
ftp://ftp.microsoft.com/MISC/
ftp://ftp.microsoft.com/peropsys/...../kb
are all located at:
ftp://ftp.microsoft.com/MISC1/
and only at:
ftp://ftp.microsoft.com/misc/kb
will there be HTM files after 7/31/02.
Thank you,
endspv@microsoft.com
Este es el motivo por el que tras el método Execute, haya que recurrir al método GetChunk,
aplicándolo cuando el parámetro State del procedimiento StateChanged haya tomado el valor
12 (Respuesta completa). No se puede saber cuando ha terminado la recepción de los datos,
por lo tanto es necesario disponer de un recurso que nos lo indique.
Es decir:
Este método no da ningún problema en cuanto a la exactitud de los datos. El fichero guardado
en el disco local es idéntico al que estaba en el servidor.
Cuando el fichero a bajar mediante OpenURL es un fichero binario (Contiene todos los
caracteres posibles) el hecho de decirle que el tipo de datos va a ser texto implica que muchos
de los caracteres se perderán (El carácter cero sin ir más lejos) En este caso debe decirle que
los datos son una matriz de Bytes. Para ello, debemos declarar como Byte la variable donde
vamos a meter cada uno de los caracteres.
Si desea guardar en un archivo los datos obtenidos con el método OpenURL, puede usar las
instrucciones Open, Put y Close, como se muestra en el código siguiente. En este ejemplo se
transfiere un archivo binario a una matriz de bytes antes de guardar los datos en disco:
With Inet1
.URL = "ftp://ftp.iies.es"
.UserName = "NombreRegistrado"
.Password = "ClavedeAcceso"
.Execute , "DIR" ' Devuelve el directorio.
Do While Inet1.StillExecuting = True
DoEvents
Loop
.Execute , "CLOSE" ' Cierra la conexión.
End With
Una vez invocado el método Execute, la conexión FTP permanecerá abierta. Puede entonces
continuar utilizando el método Execute para realizar otras operaciones de FTP, como CD y
GET. Cuando haya completado la sesión, cierre la conexión con el método Execute y con la
operación CLOSE. También puede cerrar la conexión automáticamente si modifica la
propiedad URL e invoca el método OpenURL o Execute; esta acción cerrará la conexión FTP
actual para abrir la nueva dirección URL.
Ya hemos visto un poco como funciona. No se preocupe si le empiezan fallando las primeras
aplicaciones que vaya haciendo. Con lo leído hasta aquí no se conoce todavía este control de
forma suficiente. Le llevará un buen tiempo (y muchos errores en sus aplicaciones) aumentar
sus conocimientos, pero con lo visto tiene para empezar a andar. No olvide que muchas veces
la instrucción que está empeñado en hacer no se puede porque le faltan permisos de acceso a
su servidor. Paciencia y sobre todo, apunte sus experiencias.
Propiedad AccessType
La hemos visto más atrás. Determina si se accede a Internet directamente (1), con Proxy (2) o
las preestablecidas en su PC
Propiedad Document
Devuelve o establece el archivo o documento que se usará con el método Execute. Si no
especifica esta propiedad, se empleará el documento predeterminado del servidor. Si no
especifica ningún documento, se producirá un error en las operaciones de escritura.
hInternet (Propiedad)
Devuelve el controlador de Internet de la API de Wininet.dll subyacente. Por tanto, puede usar
este controlador en llamadas directas a la API. Esta propiedad no se utiliza cuando efectúa el
acceso al control desde Visual Basic.
Propiedad UserName
Devuelve o establece el nombre de usuario que se enviará con las solicitudes a los equipos
remotos. Si deja en blanco esta propiedad, el control enviará "anonymous" como nombre de
usuario al realizar solicitudes.
Propiedad Password
Devuelve o establece la contraseña que se enviará junto con la solicitud de inicio de sesión a
los equipos remotos. Si deja esta propiedad en blanco, el control enviará la contraseña
predeterminada (Dirección de Correo electrónico).
Propiedad Protocol
Establece el protocolo que desea usar con el método Execute.
Comentarios
Cuando especifica esta propiedad, la propiedad URL se actualiza para mostrar el nuevo valor.
Además, si la parte de protocolo de la dirección URL se actualiza, también se modifica la
propiedad Protocol para reflejar el cambio. Los métodos OpenURL y Execute también
pueden modificar el valor de esta propiedad. El cambio de valor de esta propiedad no tiene
ningún efecto hasta que se invoque el siguiente método Execute u OpenURL.
Propiedad RequestTimeout
Devuelve o establece la duración, en segundos, de un intervalo de espera. Si no se responde a
una solicitud dentro del intervalo especificado y si la solicitud se efectuó con el método
OpenURL (solicitud síncrona), se produce un error. Si la solicitud se efectuó con el método
Execute, se producirá el método StateChanged junto con un código de error. El valor 0 de esta
propiedad significa espera indefinida.
Propiedad ResponseCode
Devuelve el código de error de la conexión cuando aparece el estado icError (11) en el evento
StateChanged. Si desea obtener una descripción del error, vea la propiedad ResponseInfo.
Propiedad ResponseInfo
Devuelve la descripción del último error que se ha producido.
Sintaxis Inet1.ResponseInfo
Propiedad StillExecuting
Devuelve un valor True / False que especifica si el control Internet Transfer está ocupado. El
control devolverá True cuando esté realizando una operación como obtener un archivo de
Internet y no responderá a ninguna otra solicitud mientras esté ocupado.
Propiedad URL
Devuelve o establece la dirección URL empleada por los métodos Execute y OpenURL.
Al invocar los métodos OpenURL y Execute, cambia el valor de esta propiedad por el que se
introduzca al ejecutar esos métodos. La modificación de esta propiedad no tiene ningún efecto
hasta que se llama al siguiente método OpenURL o Execute.
Nota Cuando utilice el método OpenURL, establezca la propiedad URL antes que las
propiedades Password y UserName. Si establece la propiedad URL en último lugar, las
propiedades UserName y Password estarán establecidas a "".
El Control WebBrowser
El control WebBrowser es un control DLL-ActiveX (Concretamente el Shdocvw.dll) que se
encuentra en la lista de componentes (Proyecto |Componentes) como Microsoft Internet
Controls. En la caja de herramientas está representado como un icono que representa
un globo del mundo.
Forma parte del Internet Explorer de Microsoft. Es decir, es una pieza de las usadas
por Microsoft para su navegador de Internet.
Propiedad Busy
Devuelve un valor True / False indicando que el WebBrowser está ocupado en una operación
de navegación o bajando un archivo. Puede usar el método Stop para detener la operación en
curso.
Propiedad Container
Devuelve una referencia al formulario u otro contenedor que contiene al WebBrowser. Como
devuelve una referencia al objeto, podremos ver cualquier propiedad de ese objeto, por
ejemplo, su nombre:
Label1 = brwWebBrowser.Container.Name
Propiedad Document
Devuelve una referencia al documento que está presentando el WebBrowser. Al devolver una
referencia, podemos ver cualquier propiedad de ese documento, por ejemplo, su título.
Propiedad FullScreen
Devuelven el alto y ancho de la ventana del WebBrowser medidos en la unidad de medida del
formulario o contenedor.
Devuelven la distancia desde la parte izquierda o desde la parte superior del formulario o
contenedor, al vértice superior izquierdo del WebBrowser, medidos en la unidad de medida del
formulario.
Propiedad LocationName
Devuelve una cadena con el nombre del documento que está presentando el WebBrowser. Si
lo que está presentando es una página Web del www, devuelve el título de la página. Si lo que
está presentando es un fichero del disco, preseta el nombre (Sin el path) de ese fichero
Propiedad LocationURL
Devuelve una cadena con el nombre del documento que está presentando el WebBrowser. Si
lo que está presentando es una página Web del www, devuelve el título de la página. Si lo que
está presentando es un fichero del disco, preseta el nombre con el Path completo de ese
fichero
Propiedad Offline
Devuelve o establece un valor True / False que indica si el WebBrowser está actalmente
operando en modo OffLine. En este modo, el WebBrowser está obligado a leer las páginas de
la caché del ordenador, pero si no las tiene, accede al servidor a buscarlas.
Propiedad Parent
Devuelve una referencia al formulario o contenedor del WebBrowser. Funciona igual que la
propiedad Container.
Propiedad ReadyState
Devuelve un valor que indica el estado de la operación actual del WebBrowser. Toma uno de
los siguientes valores:
Propiedad Type
Label1 = brwWebBrowser.Type
Propiedad Visible
Método ExecWB
Ejecuta un comando usando la interface IOLECommandTarget. La sintaxis es la
siguiente:
La primera cuestión que surge ahora es ¿Dónde está la lista de los comandos que se
pueden ejecutar? Desconozco donde se puede encontrar la información de todos los
comandos, pero pueden verse uno a uno en el Examinador de Objetos en las clases
OLECMDID para los los identificadores de comandos, y OLECMDEXECOPT para las
opciones. Asi, si quiere imprimir el contenido del WebBrowser basta con poner la siguiente
instrucción:
brwWebBrowser.ExecWB OLECMDID_PRINT,
OLECMDEXECOPT_PROMPTUSER
Esto es lo mismo que decir:
brwWebBrowser.ExecWB 6, 2
Y lo que hace es, imprimir (6) y presentar el cuadro de diálogo para seleccionar la impresora.
(2)
Esto de no conocer la lista de comandos aunque parece dificil (y lo es) es superable. Cuando
estamos introduciendo el método ExecWB aparece la lista con todos los comandos posibles. Y
habrá que echar imaginación y ver, dentro de la lista presentada, el comando que nos interesa.
Así sugió la línea siguiente para guardar la página:
brwWebBrowser.ExecWB OLECMDID_SAVEAS,
OLECMDEXECOPT_PROMPTUSER
Nota. Para que aparezca en el Explorador de Objetos la lista con todos los comandos
OLECMDID debe estar introducido el control WebBrowser en el proyecto.
Método GoBack
Método GoForward
Método GoHome
Abre la página especificada como página inicial en las opciones del Internet Explorer.
Método GoSearch
Método Navigate
Sintaxis:
Flags Opcional. Un valor o constante que determina varias cosas, según se explica en la lista
siguiente. Pueden sumarse dos o más valores de estos.
TargetFrameName Opcional. Indica la ventana sobre la que mostrará la nueva página. Los
valores posibles son:
Extiende el método Navigate para poder navegar por carpetas especiales tales como el
escritorio o Mi PC. La sintaxis es idéntica a la del método Navigate.
Método Refresh
Sintaxis NombredelWebBrowser.Refresh
Método Refresh2
Vuelve a cargar el documento que está presentando. La diferencia con el Método Refresh
es que este método contienen un parámetro que especifica el nivel de actualización.
Método Stop
Sintaxis NombredelWebBrowser.Stop
Evento BeforeNavigate2
Este evento ocurre cuando el control WebBrowser cambia la navegación a otra URL
diferente. Esto puede ocurrir porque el usuario ha hecho click en un link o por cualquier
otra razón. En este evento se tiene la oportunidad de cancelar la nueva navegación.
Cancel = True
DocumentComplete Event
Evento DownloadBegin
Ocurre cuando comienza una operación de navegación. Este evento ocurre inmediatamente
después del BeforeNavigate2, excepto que la navegación haya sido cancelada en aquel. A
cada evento DownloadBeguin le tiene que suceder un DownloadComplete, por lo que puede
aprovechar ambos eventos para señalar mediante un icono que se está bajando un nuevo
documento. (El Internet Explorer lo hace mediante un globo mundial girando)
Evento DownloadComplete
Evento NavigateComplete2
Ocurre cuando el WebBrowser ha alcanzado con éxito la nueva URL. A partir de este momento
es cuando comienza a bajarse el documento. Este evento es el apropiado para poner el aviso:
Sitio Web encontrado.
Evento NewWindow2
Ocurre cuando se ha creado una nueva ventana para presentar un nuevo documento.
Evento OnFullScreen
Evento OnVisible
ProgressChange Event
A modo de comentario. Hemos llegado a un capítulo que todos los alumnos desean conocer:
el capítulo donde vamos a ver como se crea un control personalizado. A lo largo de mis años
de docencia he observado que los alumnos mas aventajados tienden a realizar aplicaciones
usando controles realizados por ellos mismos, en la creencia que eso protege sus programas
frente a posibles imitaciones, ya que parece que poseen la llave que da acceso a sus secretos.
Incluso hay quien piensa que el uso de este tipo de controles da cierto prestigio como
programador. Y hay también empresas especializadas en software que comparten esas
Para crear un control, lo primero que hay que hacer es pensar, con papel y lápiz, las funciones
que debe realizar ese control, así como sus propiedades y métodos. Hay que pensar durante
este proceso, si el control se va a encapsular dentro de un OCX solo o con otros
controles, ya que un mismo OCX puede contener más de un control. Una vez
realizado este estudio previo, se procede a abrir un nuevo proyecto, eligiendo en
este caso la creación de un Control ActiveX, sobre el icono mostrado en la figura.
Aparecerá algo distinto a lo que nos encontramos cuando creamos un proyecto
EXE Standard.
Lo primero que observamos es que no aparece un formulario, sino algo, que si bien se le
parece, sabemos que no lo es. Esa especie de formulario es el objeto UserControl.
El UserControl es el objeto base del control que estamos creando. Un control ActiveX se
compone siempre de un UserControl y sobre él podremos colocar otros controles. A los
controles que colocamos sobre el UserControl se les denomina Controles Constituyentes.
Al compilar el proyecto, en vez de generar un fichero .EXE, generará un fichero .OCX Y este
OCX es idéntico a los que vemos en Proyecto | Componentes cada vez que queremos
introducir un componente que no está en la barra de herramientas. (Falta registrarlo, pero
veremos más adelante como se registra)
Se pueden introducir varios UserControl en un mismo proyecto. Esto generará igual número de
controles dentro del mismo paquete .OCX. esta práctica es bastante normal. Por ejemplo, el
MSCOMCTL.OCX (Microsoft Windows Common Controls 6.0) contiene 9 controles. Pero
tenga cuidado cuando meta controles dentro del mismo paquete. Piense que cada control va a
ocupar cierto sitio. Suponga que hace un OCX con 9 controles. Ocupará más espacio en disco
que si hubiese metido 1. Cuando meta en un proyecto uno solo de estos controles, deberá
meter en el proyecto la totalidad del espacio que ocupaban los 9 controles. El caso del control
de Microsoft citado tienen su razón de ser. El OCX contiene 9 controles, que se pueden usar
todos dentro del mismo proyecto (Barra de herramientas, barra de estado, TreeView, ListView,
Barra de Progreso, Slider, TabStrip, Image Combo e ImageList)
Registrar un control. El registro de un control es una operación que Windows debe hacer
para meter los datos de ese control (nombre y ubicación dentro del disco) dentro del registro de
Windows. Esto le facilita la búsqueda del fichero.
Lo normal es colocar los controles OCX dentro de la carpeta C:\Windows\System. Puede
ponerlo en cualquier otra carpeta, pero esa es la usual. Eso sí, una vez puesto en la carpeta
deseada y registrado no lo puede mover, ya que Windows no lo encontraría.
Hay un método muy sencillo para registrar un control cuando tiene el Visual Basic instalado en
Haga clic sobre el botón Examinar… Le aparecerá el cuadro para seleccionar un fichero.
Seleccione el fichero OCX que acaba de introducir y ese OCX ya está registrado.
Cuidado. Esta forma, sencilla y sin complicaciones, puede llevarle en más de una ocasión a
registrar controles que no desea.
Cuando no tenga VB instalado tendrá que recurrir a un programa que trae Windows: el
Regsvr32.Exe Para ello vaya a Inicio | Ejecutar y en el cuadro que le aparece introduzca el
nombre del programa seguido del nombre completo del OCX a registrar. (Fig. 19.2)
Cuando realizamos la instalación de un programa realizado en Visual Basic, ocurre con mayor
frecuencia de la deseada que uno de los controles no se registra. El registro lo hace
automáticamente la instalación, pero a veces, falla. En algunos casos el programa funciona
perfectamente, pero en otros no funciona. Y en estos casos es normal que el OCX no haya
llegado a guardarse en el disco. Solución: copiar ese OCX directamente sobre
C:\Windows\System y realizar el registro de la forma descrita.
El nuevo control que va a crear va a tener propiedades. Unas las va a tener porque son
inherentes a cualquier control (Left, Top, Tag, y aquellas que sean necesarias para la ubicación
del control y sus dimensiones) Otras, porque se han introducido durante la creación del control.
Los métodos y eventos deben introducirse todos durante la creación.
El UserControl tiene sus Propiedades, Métodos y Eventos. También las tienen los controles
constituyentes. Pero las propiedades, métodos y eventos de ninguno de ellos pasa a formar
parte de las propiedades, métodos o eventos del control que estamos creando.
Vamos a ver como se introducen las propiedades del nuevo control. Para ello vamos a estudiar
dos instrucciones de Visual Basic: Property Get y Property Let
Estas instrucciones se introducen igual que cuando se introduce una función o procedimiento
en un formulario. Haciendo clic en Herramientas | Agregar Procedimiento. Nos aparece un
cuadro donde debemos elegir Propiedad Recuerde que el elemento del menú de VB Agregar
Procedimiento solamente está activado cuando está abierta una ventana de código. La ventana
de código abierta debe ser del proyecto del control ActiveX (Del UserControl o de cualquiera de
los controles constituyentes) Se le pone el nombre que desea que tenga la propiedad. Por
ejemplo, si queremos que esa propiedad sea el color de fondo del nuevo control, pondremos
ColorDeFondo (¿Porque habríamos de poner Backcolor si el control se ha desarrollado en
España?)
Basta con hacer clic en Aceptar y ya nos ha introducido dos nuevas cosas en la ventana de
código, en el desplegable de la derecha (Fig. 19.4)
End Property
End Property
Vea que empieza como todos los procedimientos, con Public (Podría ser Private) seguido de
Property Let (o Property Get) y termina con End Property
Como puede verse, el procedimiento Property Let necesita un valor, vNewValue que ha
declarado como Variant. A ese el procedimiento para poner el valor a la propiedad necesita que
le pasemos el valor de esa propiedad. Lo que no puede saber, es el tipo de dato que debe usar
para ColorDeFondo. Por eso pone As Variant y de esta forma podemos meter cualquier valor.
Sin embargo, sabemos desde el Capítulo 1 que las variables declaradas como Variant ocupan
mucho espacio en memoria, y debemos optimizar el programa ajustando las variables. Como
sabemos que el color es un número Long, podemos sustituir el tipo Variant por un tipo Long:
Es muy útil, no necesario, comunicar al control que ha cambiado una de sus propiedades. Esto
sirve para que pueda guardar el nuevo valor en su página de propiedades. Ya lo veremos. Para
comunicarle que ha cambiado una propiedad se le añade una línea al código anterior
Este procedimiento es el que se ejecuta cuando ponemos un valor al color de fondo del nuevo
control cuando ponemos, por ejemplo
Vamos a ver el código del procedimiento que se ejecuta cuando queremos saber el valor que
tienen esa propiedad.
Sintaxis
Argumentos Argumentos que deben pasarse al llamar a ese procedimiento. El tipo de datos
de cada argumento será el mismo que en el procedimiento Property Get
Instrucciones Es el código que hay que escribir en el procedimiento para que realice lo que
desea el programador. Estas instrucciones pueden generar una condición en la que se debe
abandonar el procedimiento. En ese caso, puede salirse del procedimiento con Exit Prperty
Por Valor (ByVal) El valor de la variable fuera del procedimiento mantienen el valor, aunque
ese valor cambie dentro del procedimiento.
Por Referencia (ByRef) El valor de la variable fuera del procedimiento cambia si suse
modifica en el procedimiento.
Sintaxis
El ámbito y la forma de pasar los parámetros son idénticas a las de Property Let
Veamos que propiedades debe tener este control. Aparte de la ya citada, ColorDelFondo,
vamos a ponerle:
In_Cod_Barras. De tipo texto, para poder pasarle por programa el código de barras de un
producto.
In_Unidades. Numérica double para que permita decimales, y permitir así vender productos “al
peso”
Out_Precio_Total, numérico double con el precio total de las unidades vendidas de ese
producto
(Hemos tenido el detalle de que las variables que contienen datos que entran en el control
comienzan por In_ y las que salen del control comienzan por Out_ Esto no significa que sean
solo de escritura o de lectura)
Se introducen estas propiedades tal como se explicó más atrás. En la Fig 19.7 puede verse el
cuadro de propiedades del control, con todas estas propiedades ya metidas. Ese cuadro,
como sabemos, aparece pulsando F4 estando seleccionado el control. Ese control está sobre
un formulario de prueba que aun no sabemos como ponerlo, pero se explicará en breve. Este
cuadro es el típico de todos los controles, pero estamos acostumbrados a que, haciendo clic
con el botón derecho del ratón, aparezca un cuadro de diálogo donde podemos introducir las
propiedades. Y eso, no sabemos todavía como hacerlo.
Al elegir el asistente,
aparece un cuadro donde
nos muestra las
propiedades que hemos
metido en nuestro control.
En el caso del ejemplo,
esas propiedades son:
ColorDeFondo
In_Cod_Barras
In_Unidades
In_DeshabilitarIncr_Desc
Out_Descripcion
Out_Precio_Total
El asistente nos pide que elijamos las propiedades que queremos que aparezcan en el cuadro
de dialogo de propiedades. Se las ponemos todas. Vamos aceptando todos los pasos, hasta
que hacemos clic en el botón Finalizar. El cuadro de dialogo de propiedades ya está creado.
Vamos al formulario de prueba y hacemos clic con el botón derecho sobre el control. Aparece
por fin el cuadro de dialogo de propiedades.
Puede ahora incluir en ese cuadro cualquier control, como si se tratase de un formulario.
Queda a merced de su capacidad artística rematarlo de forma adecuada
Puede observar ahora que en la ventana de proyecto aparece ahora otro componente: la
página de propiedades.
La página de propiedades (Property Page) tiene las casi todas las propiedades, métodos y
eventos de un formulario. No vamos a extendernos en su estudio exhaustivo, ya que sería
salirse del objetivo del curso.
Solamente falta introducir el código adecuado en los procedimientos del UserControl. Por
ejemplo, para comprobar que el código de barras es correcto, creamos un procedimiento
Puede probar todas las características del nuevo control. De cualquier forma no se fíe, y realice
la comprobación final en un proyecto completamente independiente, una vez que ya haya
compilado el control.
Ya vamos avanzando en lo que podemos hacer sobre un nuevo control. Ahora vamos a
enlazarlo a una base de datos a través del control Data.
Para ver como se hace esto vamos a hacer un ejemplo, siguiéndolo paso a paso.
Propiedad AccessKeys
Devuelve o establece una cadena que contiene las teclas que funcionarán como teclas de
acceso (o teclas aceleradoras) del control. La propiedad AccessKeys es una cadena que
contiene todas las teclas de acceso del control. Por ejemplo, para establecer las letras S e Y
como teclas de acceso, la propiedad AccessKeys se establecería a "sy". Cuando un usuario
presiona una de las teclas de acceso junto con la tecla ALT, el control recibirá el enfoque
(según el valor de la propiedad ForwardFocus). Las teclas de acceso de los controles
componentes se incluyen implícitamente como teclas de acceso, aunque no aparezcan en la
propiedad AccessKeys.
ActiveControl (Propiedad)
Devuelve el control que tiene el enfoque.
Propiedad AutoRedraw
El UserControl acepta métodos gráficos. La propiedad AutoRedraw (True/False) hace lo
mismo que en el Formulario.
Propiedad BackStyle
Establece el tipo de fondo del UserControl (Opaco / Invisible)
Propiedad ClipBehavior
Sirve para determinar la zona donde se verán los métodos gráficos, cuando está transparente.
Véase en la ayuda.
Propiedad ContainedControls
Devuelve la colección de controles constituyentes y los que se hayan podido añadir en tiempo
de ejecución. Funciona de forma similar a la propiedad Controls de un formulario.
Propiedad ContainerHWnd
Devuelve el controlador de la ventana (hWnd) del contenedor de un UserControl. Se usa para
programar con APIS.
Propiedad DataMembers
Devuelve una referencia a la colección DataMembers.
Un proveedor de datos puede proporcionar múltiples conjuntos de datos a los que un receptor
puede enlazar. Cada conjunto de datos se llama "miembro de datos" y está identificado
mediante una cadena única.
La colección DataMembers contiene los nombres de todos los miembros de datos disponibles
para el receptor de datos.
EventsFrozen (Propiedad)
Devuelve un valor que indica si el contenedor está pasando por alto los eventos
desencadenados por el control. La propiedad EventsFrozen no está disponible en tiempo de
diseño del control y es de sólo lectura en tiempo de ejecución.
Cuando la propiedad EventsFrozen es True, el contenedor pasa por alto todos los eventos
desencadenados por el control. Si el control necesita producir un evento que no se puede
perder, tiene que dejarlo en la cola hasta que EventsFrozen sea False.
Extender (Propiedad)
Devuelve, para este control, el objeto Extender que almacena las propiedades del control
mantenidas por el contenedor. La propiedad Extender no está disponible en tiempo de diseño
del control y es de sólo lectura en tiempo de ejecución.
Propiedad MaskColor
Devuelve o establece el color que determina la región transparente del mapa de bits asignado
a la propiedad MaskPicture de un objeto UserControl cuya propiedad BackStyle está
establecida a 0 (Transparente).
Importante Esta característica sólo es soportada con mapas de bits de tipo imagen, como
GIF, JPEG y DIB. No es soportada con iconos, cursores o metaarchivos de Windows.
Propiedad ToolBoxBitMap
Establece la imagen con la que se va a representar el nuevo control en la caja de
herramientas. Debe ser una imagen BitMap que habremos guardado previamente en el disco.
Hasta ahora hemos visto aplicaciones que pueden utilizarse por sí solas. Pueden acceder a bases de
datos que estén en el mismo ordenador que la aplicación o en otro, unido mediante una red de área
local. La conexión a las bases de datos a las que accede se realiza, bien indicándole la dirección y
carpeta (\\MiServidor\BasesdeDatos\MiBase.Mdb) o bien “mapeando” esa dirección (crear una unidad
de disco que contienen esa dirección). De esta forma podemos acceder a una base de datos instalada en
un equipo remoto y la aplicación debe funcionar perfectamente, aunque al abrir o leer la base de datos
origine un tráfico elevado por la red de área local.
Con esta configuración podemos tener problemas de lentitud, sobre todo si la red es lenta y si hay
muchos usuarios trabajando con esa aplicación en distintos ordenadores, accediendo todos ellos a través
de la red a un servidor que contienen la base de datos. Esta lentitud se incrementaría en una gran escala
si la red a utilizar fuese Internet mediante una conexión lenta.
Pero lo que más se notaría sería la ocupación de los recursos de la red durante las consultas a esa base
de datos. Dependiendo de cómo se crease el recordset, podría darse el caso de transmitir por la red
todos los datos de la base de datos para que nuestra aplicación utilice únicamente los datos
correspondientes a un registro. Pensando en una aplicación bancaria, por ejemplo la petición de saldo
de una cuenta desde un cajero automático, los únicos datos relevantes de esa operación serán el número
de cuenta corriente, un código para indicar que lo que se desea es el saldo, y como datos de retorno, el
número de esa cuenta, el saldo actual disponible y quizás un número de comprobación (Checksum) para
comprobar que no ha existido error en la comunicación. De nada nos serviría en el cajero conocer el
estado de cantidades retenidas, operaciones pendientes, etc.
Si en ese ejemplo del cajero creásemos un objeto Database, un recordset, leyésemos todos los datos del
recordset y luego diésemos la orden de cerrarlo, estaríamos enviando información innecesaria a través
de la red, con el coste de tiempo y ocupación que ello representa.
Casi llegamos a demostrar con esta introducción que sería más conveniente hacer una aplicación que
tuviese dos partes: una, residente en el puesto de operación, (que llamaremos aplicación cliente) que
fuese capaz de enviar datos solicitando información a otra aplicación, instalada en el servidor donde
está la base de datos. Esta segunda aplicación, que llamaremos aplicación servidor, abrirá la base,
creará los recordsets necesarios, filtrará la información, realizará con ella operaciones matemáticas, y
una vez concluido todo el proceso, enviará a la aplicación cliente los datos estrictamente necesarios.
Obviamente se necesita que ambas aplicaciones sean capaces de entenderse entre sí, es decir, que
tengan un protocolo de comunicación entre ellas previamente definido. Esto es lo que se denomina una
aplicación cliente – servidor.
La aplicación cliente – servidor más popular es el navegador de Internet. Esta aplicación está formada
por una aplicación cliente (Navegador IEXplore, NetScape, etc) y una aplicación servidor (Internet
Information Server IIS, Apache, etc). El protocolo para que se comuniquen entre ellas es el protocolo
Http o el Ftp. Estos protocolos al ser públicos, permiten a cualquier empresa hacer su propia aplicación
cliente o servidor.
No es nuestro deseo llegar a tanto. El navegador de Internet es una aplicación cliente–servidor que
sobrepasa cualquier proyecto que podamos imaginar para realizarlo por medio de programación en VB.
Cualquier navegador de Internet comercial lleva asociadas una serie de funciones, muchas de ellas
transparentes para el usuario, que sería imposible crear una aplicación de este calibre, sobre todo como
ejemplo de un curso de VB. Pensemos en una aplicación más sencilla, que nos permita conocer como
funcionan las aplicaciones cliente – servidor, y sirva además para algo útil.
Esa aplicación no puede ser otra que una guía telefónica, (versión novísima del Listatel) cuya base de
datos está en un servidor conectado a Internet, al que se podrá acceder desde un número ilimitado de
puestos cliente. En la base de datos del servidor, en un conjunto de tablas, figurarán los nombres,
números de teléfono, despacho, etc. de una serie de personas, y el organigrama de esas personas dentro
de la organización. Estas tablas serán estáticas, es decir, tendrán datos que solamente se actualizarán
cuando exista un cambio en esas personas, pero no variarán durante la ejecución del programa. En otra
tabla, esta dinámica, tendremos el registro de los usuarios que están conectados en ese momento al
Una aplicación cliente puede comportarse como aplicación servidor de otra aplicación, incluso de sí
misma. Este es el caso de la aplicación cliente del SML. Cuando vamos a pedir la información de un
abonado telefónico al servidor, el cliente se comporta como tal. Sin embargo cuando otro usuario de
SML nos envía un mensaje, es nuestra aplicación la que realiza las funciones de servidor. Verá esto
con mucha más claridad cuando comencemos el ejemplo.
Toda aplicación cliente - servidor debe tener un sistema de comunicación entre la aplicación cliente y
la aplicación servidor. Parece que la comunicación que ha de tener es a través de una red IP (Red de
Area Local, Red Privada Virtual, Internet) pero puede ser cualquier otro sistema de comunicación entre
ordenadores. Sin ir más lejos, a través del puerto de comunicación serie COM-1 ó COM-2. A efectos de
los programas de la aplicación cliente o servidor solamente variará en la forma en la que reciban y
transmitan los datos. En nuestro ejemplo vamos a utilizar la comunicación a través de una red IP,
usando el control Winsock ya estudiado. Tanto la aplicación cliente como la aplicación servidor
deberán tener un Winsock. Como en nuestro caso, según explicamos más atrás, la aplicación cliente se
convierte en aplicación servidor para recibir mensajes, ésta deberá tener dos Winsocks, uno para la
función cliente, y otro para la función servidor.
Cuando se diseña una aplicación cliente – servidor hay que comenzar pensando en las funciones que va
a realizar y sobre esas funciones comenzar a escribir el protocolo de comunicación. No es fácil tener
terminado el protocolo de comunicación antes de escribir la primera línea de código del programa.
Según se van concretando cada una de las acciones que debe realizar vemos que es necesario introducir
nuevos códigos de operación en nuestro protocolo. No se preocupe que eso no es improvisar. Pero sí es
muy importante tener las cosas claras desde el principio y saber que es lo que va a hacer nuestra
aplicación para de esta forma poder saber dar respuesta correcta a cada uno de los dos programas. Y
para tener las cosas claras, el autor recomienda que el protocolo de comunicación se establezca antes de
comenzar a escribir el código en tanto se pueda. Y que se escriba en un documento en el propio
ordenador o mejor aún, en una base de datos u hoja de cálculo, que nos permita ordenar las órdenes y
avisos de nuestra aplicación para evitar de esta forma cualquier repetición involuntaria.
Es muy importante a la hora de pensar el protocolo de comunicación que este no pueda inducir a error
al programa. Si el programa tiene como función la transmisión de mensajes (Tal como es nuestro caso)
los mensajes no pueden estar limitados ni en su longitud ni en su contenido por el formato del protocolo
de comunicaciones. Se entiende mejor esto analizando el protocolo del SML (fruto únicamente de la
imaginación del autor y no sujeto a normas ni recomendaciones internacionales de ningún tipo)
La comunicación entre ambas aplicaciones se establece mediante palabras en ASCI que indican una
operación. A estas palabras las denominamos Acrónimos. Las operaciones pueden ser órdenes o avisos.
Son órdenes aquellas comunicaciones del cliente que desencadenan una operación en el servidor. Son
avisos aquellas respuestas del servidor para enviarle datos o comunicar algo al cliente. Cada orden o
aviso, puede llevar uno o varios parámetros. Los parámetros contienen la información necesaria para
que la operación que se va a realizar por esa orden o aviso pueda llevarse a cabo. En esta aplicación
SML los acrónimos todos son de 3 caracteres, utilizando los signos < > como delimitadores del
acrónimo. Si el acrónimo lleva parámetros, la separación entre los tres caracteres y el parámetro o
parámetros es la barra /
Es obvio pensar que esas tres combinaciones no pueden formar parte de ningún acrónimo. Pero como
los acrónimos los definimos nosotros, basta con hacer que ninguno contenga esas secuencias de
caracteres.
Preocúpese de estos detalles que son los realmente importantes. Verá cuando se ponga a escribir código
que siempre ha de faltar algo en el protocolo de comunicación. No se preocupe por tener que ir
rectificándolo. Eso sí, mantenga siempre actualizado el documento o base de datos con el protocolo,
explicando debidamente cada una de sus partes, los parámetros que lleva y la función que realiza. Es
documento indispensable a la hora de distribuir la aplicación.
El protocolo de comunicación debe hacerse preferentemente en ASCII puro y caracteres que tengan
representación física. Esto nos permite depurar el programa analizando la comunicación mediante un
programa externo y debidamente probado. Utilizar caracteres sin representación ASCII lo único que
puede hacerle es complicarle la vida a Ud. y a quien analice su aplicación. Cuando se envíe una cifra,
hágalo en decimal o en hexadecimal. Evite en lo posible enviar cifras sustituyendo su valor por su
carácter equivalente. La mayoría de los caracteres no los va a poder representar y tendrá serios
problemas para depurar su programa. Esto es especialmente importante con el cheksum de un mensaje,
por ejemplo. Le recomiendo que use preferentemente numeración Hexadecimal.
Un buen programador nunca oculta el protocolo de comunicación. Esto permite que otros
mejoren su aplicación. Sólo los programadores mediocres temen que se les plagie. Y por
supuesto, nadie debería aceptar una aplicación cliente – servidor si el programador no
suministra ese protocolo.
Aspectos que debe tener siempre en cuenta cuando diseñe una aplicación cliente –
servidor.
Si como es normal, utiliza una red IP para la transmisión de información entre ambas aplicaciones,
deberá poner un Winsock en la aplicación cliente, que deberá conocer la dirección IP o el DNS de la
El programa cliente debe conocer el número IP o dirección DNS del servidor. Pero el servidor no tiene
porque conocer la dirección ni DNS del cliente, ya que debe ser el cliente el que inicie siempre la
comunicación.
Al cliente nunca se le debe forzar el puerto en el que emite (Propiedad LocalPort del Winsock). El
servidor contestará siempre al cliente usando la conexión abierta por éste, es decir, usando la instancia
de su Winsock correspondiente a la conexión realizada para ese cliente)
Normalmente el servidor no podrá comunicar directamente con el cliente, ya que el cliente no está a la
escucha. Solamente en algunas aplicaciones se le dota al cliente de otro Winsock para que sea este
segundo Winsock quien esté a la escucha de una posible llamada del servidor. Esto se emplea para dar
una batida por todos los clientes (Hacer Pooling) y ver los clientes que están conectados en este
momento. Solamente lo he visto en algunas aplicaciones de seguridad. Generalmente este pooling se
sustituye por llamadas periódicas desde cada cliente para indicarle al servidor que están activos.
La comunicación entre cliente y servidor debe ser siempre TCP. El uso de UDP simplifica mucho las
comunicaciones, pero no sirve para salir a Internet, donde las tramas UDP son generalmente eliminadas
por los servidores. Vale más trabajar un poco más el código y trabajar con comunicaciones seguras
aunque sea en una red de área local que garantice la llegada de UDP.
Los paquetes broadcast deben evitarse también. Solamente deben usarse en configuraciones donde no
se conocen las direcciones de los servidores. Esto ocurre cuando la red tiene asignación dinámica de
direcciones. (Puede ocurrirle si usa una red VSAT) En cualquier caso, los paquetes broadcast deben
evitarse en lo posible. Los servidores de Internet los eliminan automáticamente.
Dado que la aplicación cliente y la aplicación servidor son completamente independientes, es muy fácil
realizar modificaciones en una de ellas, sobre todo en el servidor, sin que el cliente se entere. Basta
para ello mantener invariable el protocolo de comunicación.
Pero puede llegar el caso en el que al servidor se le hayan introducido mejoras que puede aportar al
cliente si este es capaz de reconocer un nuevo protocolo. Estamos en el caso de un servidor que debe
atender a dos versiones distintas de cliente. Si el cliente tiene la versión mejorada, usará con él un
protocolo de comunicación que permita las nuevas prestaciones. Si el cliente tiene la versión más
antigua, deberá seguir usando con él el protocolo anterior.
Esto nos lleva a que el cliente siempre debe indicar al servidor su versión. Esto no ocupa unos recursos
excesivos y puede ser muy útil cuando prevemos introducir una modificación. Esta práctica puede
incluso servir para conocer aquellos clientes a los que hay que realizar el cambio de versión y enviarles
una nueva. En el ejemplo de este curso, el cliente comunica al servidor su versión en el acto de
conectarse mediante el acrónimo <VER>.
El servidor también debería enviar su versión al cliente. No está previsto en la aplicación SML pero
confieso que no sobraría que al identificarse el cliente y devolverle el servidor la conformidad del
registro, le devolviese también la versión del servidor. ¿Se da cuenta que según se avanza en la
definición del protocolo aparecen nuevas necesidades?
Cuando utilice el puerto COM para la comunicación, bien sea directamente o a través de módem, debe
indicar claramente al usuario el estado de esa conexión, analizando la señal DSR (esta señal está activa
cuando el módem está conectado, y en caso de comunicación directa, cuando se use, el Host debe poner
a 1 esta señal para poder trabajar). Recuerde que la comunicación por el puerto COM es asíncrona
respecto a la ejecución del programa.
En el ejemplo de aplicación cliente – servidor se han ensayado también los controles avanzados de
Visual Basic (Capitulo 16). La aplicación tal como se dijo es una guía telefónica, que presenta en un
TreeView el organigrama del Organismo o empresa a la que pertenece la guía. Esto facilita la
búsqueda de una persona, haciéndolo por departamento. La guía permite también buscar por nombre,
primer apellido, segundo apellido o combinación de ambos. Una vez encontrada la persona, se cubren
todos los datos de la misma, para presentarlos en la solapa de la guía propiamente dicha, y el dato de su
alias (dato que define de forma única a cada usuario) aparece en la dirección de correo para poder
enviarle un mensaje. Para hacer una práctica con el puerto de comunicaciones COM-1 ó COM-2
utilizaremos un módem, conectado al PC a través de uno de estos puertos, para marcar el número
telefónico.
Todas las operaciones de búsqueda de datos las haremos mediante consultas a la base de datos que está
en el servidor. Las consultas se realizarán siguiendo el protocolo de comunicaciones. El organigrama se
guardará en el cliente, en una base de datos, para evitar en lo posible el envío desde el servidor. La
razón para esto es que el organigrama puede ser muy extenso, y no va a variar muy a menudo. Como
esta información habría que enviarla nada más conectarse el cliente, y como puede ser bastante amplia,
esto podría originar un tráfico intenso por la red, que es justamente lo que queríamos evitar. Solamente
se va a enviar cuando haya cambiado. ¿Cómo sabe el cliente que ha cambiado?. ¿Cómo sabe el servidor
que el cliente tiene ya la información actualizada? La idea más sencilla para conocerlo es que el cliente
envíe cada vez que se registra (al comienzo de la conexión) el nombre de la revisión del organigrama
que tienen. El nombre puede ser cualquiera. Lo que hace el servidor por defecto para poner el nombre a
la revisión es combinar una cadena de texto con el nombre de la organización seguido de la fecha
(día/mes/año) en la que se realiza la revisión. De esta forma es imposible que haya dos fichero con el
mismo nombre. Al recibir ese nombre el servidor, si es igual al de la última revisión, le devuelve un
OK. Si no es igual, le devuelve una instrucción para que el cliente solicite al servidor que le envíe la
nueva revisión. El cliente la solicita y el servidor se la envía. Y al recibirla, el cliente reemplaza el
contenido de la base de datos por los nuevos valores recibidos.
Al día de hoy (30 de Octubre de 2002) no está terminada la aplicación. Posiblemente no lo esté nunca
porque sea una aplicación en continua ampliación. Esto es lo bonito de los programas ejemplo de los
cursos de visual basic.
La comunicación entre cliente y servidor se realizará mediante el protocolo descrito más adelante, a
través de cualquier medio de comunicación. Generalmente será comunicación a través de red IP, pero
esto no debe impedir que los clientes se puedan conectar al servidor a través de otros medios de
comunicación, como puede ser un módem telefónico o un enlace vía satélite.
En cualquier caso, el contenido de las órdenes y comandos de este protocolo será siempre en ASCII,
con caracteres alfanuméricos que permitan ver perfectamente el tráfico entre ambos interlocutores. El
hecho de utilizar solamente caracteres legibles asegura igualmente la comunicación a través de
cualquier medio de telecomunicación, incluyendo dispositivos de cifrado on-line colocados entre
cliente y servidor.
La comunicación puede iniciarse tanto en el servidor como en cualquiera de los clientes. Una
comunicación puede estar compuesta de:
Ordenes
Respuestas
Confirmaciones
Avisos
Ordenes: Son aquellas partes de la comunicación que inician una operación en el equipo colateral.
Respuestas. Son siempre respuesta a una orden, y contendrán el resultado de la operación generada por
la orden, o en su defecto, la notificación de que esa orden no ha sido ejecutada o que dio resultado nulo.
Avisos: Son aquellas comunicaciones que no ejecutan ninguna acción en el colateral, pero informan de
algo. Por ejemplo, servidor fuera de servicio temporalmente, servidor restablecido. Pueden ser
individuales, en cuyo caso generarán normalmente una confirmación, o broadcast, en cuyo caso no
generarán confirmación.
La trama de una comunicación estará compuesta de unos acrónimos que indicarán la orden, respuesta,
confirmación o aviso que envían, dirección IP, nombre de la máquina o nombre del servidor que envía,
dirección IP, nombre de la máquina o nombre del servidor destino, datos, cheksum de la trama y
acrónimo de fin de trama. Cada uno de estos componentes irá metido entra los símbolos < > y su
longitud puede ser variable. Cuando se envíe información, esta se colocará detrás del acrónimo que la
define, separada por la barra /.
Ejemplos:
Si hubiese que emplear los símbolos /, &, < o > como símbolos de la información, deberá anteponerse
a cada uno de ellos el símbolo &.
Por ejemplo:
El orden de los componentes de una trama no debe ser estricto, si bien debe ir en primer lugar el
acrónimo que inicie la sesión de comunicaciones, en penúltimo lugar el cheksum, y en último lugar el
<EOT>
Una trama puede contener varias ordenes siempre y cuando estas sean congruentes.
<RHC> Remote Host Conectado. Lo devuelve el servidor o un cliente cuando otro equipo se conecta a
él.
<SAT> Servidor ATención. Inicia una sesión de comunicación desde el cliente al servidor.
<CAT> Cliente Atención. Inicia una sesión de comunicación desde una máquina (Servidor o cliente) a
una máquina cliente.
<LIC> Login Cliente. Indica que el cliente desea hacer Login en ese servidor. Pasa como
parámetros el alias y el password (en claro o cifrado) del cliente.
<LOC> LogOut de cliente. Ese cliente quiere hacer LogOut de ese servidor. Pasa como
parámetros el alias y el password (en claro o cifrado) del cliente.
<ALC> Alias de cliente. Llevará como dato el alias de ese cliente.
<ALS> Alias de servidor. Llevará como dato el alias de ese servidor.
<PWC> Password del cliente. Lleva como parámetro el password (en claro o encriptado) del cliente
<IPO> IP de la máquina origen. Llevará como dato el número IP de la máquina origen de la trama.
<IPD> IP de la máquina destino. Llevará como dato el número IP de la máquina destino.
<NMO> Nombre máquina origen. Llevará como dato el nombre de la máquina dentro de la red.
<NMD> Nombre máquina destino. Llevará como dato el nombre de la máquina dentro de la red.
<NSO> Nombre Servidor origen. Llevará como dato el nombre del servidor origen. Este nombre de
servidor será el nombre de un programa servidor, no el nombre de la máquina donde
se alberga el programa servidor.
<NSD> Nombre servidor destino. Igual que el anterior.
<QRY> Consulta a una base de datos. Llevará como datos la consulta completa en SQL
<QPR> Consulta ya preprogramada en el servidor. Lleva como parámetro el nombre de esa consulta, y
los datos que necesite esa consulta para su ejecución.
<RQY> Resultado de la consulta a la base de datos. Llevará como datos el resultado de esa consulta. Si
el resultado genera varios registros, cada uno de ellos irá entre brackets ( [ ] )
<QIP> Consulta de los datos del correo de un usuario. Envía como parámetro el Alias del usuario del
que se quieren conocer los datos. Devuelve los datos con el acrónimo <QIP>
<QIR> Consulta los datos del correo de un usuario pasándole en este caso como parámetro la
dirección de correo SML. Devuelve los datos con el acrónimo <QIP>
<QIS> Consulta los datos del correo de un usuario pasándole la dirección de correo SMTP. Devuelve
los datos con el acrónimo <QIP>
<ECO> Orden de envío del mismo dato en la respuesta a esa orden. Se envía, por ejemplo, en una
consulta a la base de datos, para que el cliente ubique la respuesta a esa consulta en un
sitio determinado.
<OCE> Se emplea para devolver el mismo dato recibido con <ECO>
<CRY> Inicia o finaliza una operación de cifrado de datos. Lleva como parámetro INI para iniciar y
FIN para terminar. En el caso de iniciar, lleva uno o dos parámetros más, el número de
clave o el nombre de un usuario con clave asociada. Puede llevar dos nombres de
usuario en caso que se cifre doblemente con la clave privada del remitente y la clave
publica del destino, en cuyo caso se envían como dos parámetros
<CRY/INI/PUB_LUIS.RODRIGUEZ/PRI_JOSE.LOPEZ>
Si el inicio no llevase parámetros de clave, el servidor entenderá que se cifra con la clave
asignada en el servidor al usuario que envía
<CRY/FIN>
En caso de una transmisión de mensajes múltiples, lleva como parámetro el número del mensaje que
termina. En caso de mensaje único no lleva ningún parámetro o puede llevar el número del mensaje.
Por ejemplo:
<CAT> <IPO/100.100.100.100><IPD/217.126.179.96><NMD/Administracion_1>
<OCE/123>
<RQY/[Antonio Fernandez_91 1234567][Antonio Fernangomez_91 7654321]>
Este mensaje devuelve el resultado de una consulta a la máquina Administracion_1 que está en la red
de área local cuyo acceso desde Internet se realiza por un módem ADSL cuyo número IP es el
217.126.179.96. Lógicamente, la máquina real que recibe esta comunicación no es la máquina
Administracion_1, sino otra máquina colocada en la misma red de área local, sobre la cual se ha hecho
NAT desde Internet para el puerto que use este sistema, y esta máquina, una vez recibido el mensaje, lo
reenvía a la máquina Administracion_1 que será quien lo trate. Si la comunicación no llevase más que
el número IP, se entiende que el destino es la máquina que tiene ese número IP.
ORDENES PREPROGRAMADAS
Las órdenes preprogramadas son aquellas que el servidor ya tienen programadas en su código. Deben ir
necesariamente tras el acrónimo <QPR> ya que en caso contrario no serán tratadas.
Este control permite la comunicación de una aplicación VB con el puerto serie. Parece en
principio que los puertos serie del PC se usan para muy pocas cosas. Prácticamente para
conectarnos a Internet a través de un módem telefónico. Existen muchísimas aplicaciones
industriales para las cuales son fundamentales los puertos COM. Las redes de área local
parece que han dejado obsoletos a los puertos COM1 y COM2. No es así. Deje volar su
imaginación. Vuelva a la página 4 del capítulo 2, y observe el panel de control de una radio
realizado en Visual Basic. La unión entre el PC y la radio, separados muchos kilómetros, se
realizó mediante el puerto COM, conectado a una línea telefónica mediante un módem. El
puerto COM es el gran olvido de los informáticos…
El control MSComm no está normalmente en la caja de herramientas, por lo que será necesario
introducirlo mediante Proyecto | Componentes.
Al tratarse de un control personalizado, presenta dos formas de ver las propiedades. Si hacemos click
con el botón derecho del ratón sobre el control y vamos a propiedades, nos presenta tres cuadros de
configuración de los típicos de los controles personalizados. Si seleccionamos el control MSComm y
pulsamos F4 , aparecerá la caja de propiedades típica de los controles VB.
PRPIEDADES
Existen propiedades que pueden establecerse en tiempo de diseño o en tiempo de ejecución, y otras que
solamente se pueden ejecutar o consultar en solamente en tiempo de ejecución. Se detallan a
continuación las primeras. Las segundas se enumerarán tras estas, aunque se nombran algunas de estas
últimas al explicar cada una de las propiedades del primer tipo.
CommPort
Indica el número del puerto serie usado. Admite los valores de 1 a 255. Cambiando esa propiedad
podemos cambiar el puerto de comunicación que vamos a usar (Un PC tiene normalmente 2 puertos
serie : El Com1 y el Com2. Puede tener sin grandes problemas Hardware hasta 4 (Com3 y Com4) Si le
damos a ese valor un número de puerto inexistente, dará error.
Settings
Indica la velocidad, paridad, número de bits y bits de stop (parada) que se van a usar en la
comunicación.
50 100 110 300 600 1200 2400 4800 9600 14400 19200 y 28800
(No es posible programar 1,5 bits de parada. Sólo lo hace cuando se programan 5 bits de
información y lo hace automáticamente).
Handshaking
Especifica el método de control sobre el flujo de información. En una comunicación serie se necesita
conocer si el puerto puede enviar información (necesita saber si el módem está preparado para recibirla)
y necesita indicarle al módem que él está preparado para recibir información. A este proceso se le
denomina Handshaking. (Handshaking = Control de Flujo)
(Como sabrá por sus conocimientos de inglés, Handshaking significa apretón de manos, ponerse de
acuerdo. Y ponerse de acuerdo entre dos terminales que van a comunicarse es establecer las
condiciones de control que uno va a tener sobre otro.)
El Control de Flujo puede hacerse de dos formas : Una mediante las señales auxiliares del puerto (RTS,
CTS, DSR, DTR), que son cables adicionales que tendrán una tensión positiva respecto a los 0V del
equipo si esa señal está activada, o una tensión negativa si no lo está. Este tipo de control del flujo de
información es el típico para comunicarse el ordenador con un módem.
Existe otra forma de controlar el flujo de información : mediante señales especiales que se envían por
los dos cables que transportan la información. Mediante estas dos señales podemos controlar que el
ordenador envíe información o deje de enviarla. De igual forma, podemos indicarle al módem que
envíe o no envíe. Estas señales especiales se denominan X-ON y X-OFF.
La propiedad Handshaking controla la forma de realizar este proceso. Puede tomar los siguientes
valores :
InBufferSize
Mediante esta propiedad establecemos el tamaño del Buffer (almacén de datos) de entrada. Este Buffer
sirve para poder recibir datos sin que tenga que intervenir la aplicación continuamente para controlar el
puerto de entrada.
OutBufferSize
Puede conocerse el número de caracteres presentes en el Buffer de salida (los que aún están por
transmitir), consultando el valor de la propiedad OutBufferCount.
RThreshold, SThreshold
Estas dos propiedades especifican el número de caracteres que deben estar presentes en los Buffers de
Recepción y Transmisión respectivamente, para que se produzca el evento OnComm relativo a
recepción y transmisión de caracteres. (Eventos EvReceive y EvSend) Si el valor de una de estas
propiedades está a 0, no se produce el evento OnComm correspondiente.
El valor que se debe dar a estas dos propiedades depende de la aplicación y del tiempo que queramos
que la aplicación está atendiendo al puerto de comunicaciones. Concretamente para la propiedad
RThreshold debemos pensar muy bien el valor que se le pone. Si ponemos un valor corto (1 es el
mínimo), cada vez que reciba un carácter se producirá el evento OnComm. (Vea la descripción de
eventos mas adelante). Al producirse este evento, ejecutará el procedimiento asociado a él, lo que hará
perder tiempo a la aplicación, impidiéndole realizar otras funciones. Si se pone un valor muy alto, el
puerto no avisará que tiene caracteres recibidos hasta que reciba un número igual al programado en esta
propiedad, por lo que no podremos procesar los datos recibidos hasta que el buffer tenga ese número de
caracteres en su interior. En número adecuado dependerá del tipo de aplicación que vayamos a realizar.
En cualquier caso, este número será inferior al número programado para la longitud del buffer,
(InBufferSize)
InputLen
Por defecto, cuando se lee el Buffer de recepción, se leen todos los caracteres, quedando el Buffer
vacío. Si se le asigna a esta propiedad un valor distinto de 0, cada vez que leamos el Buffer de
recepción leerá un número de caracteres igual a esa cantidad, permaneciendo los caracteres restantes en
el Buffer a la espera de una nueva lectura. Asignándole el valor 0 (Valor por defecto), el buffer se lee
completo.
ParityReplace
Si la comunicación se realiza con bit de paridad (Par o Impar), en recepción se comprueba byte a byte
la recepción de la paridad correcta. Si se recibe un Byte que no tiene paridad correcta, lo mas probable
es que ese Byte (carácter) se haya recibido defectuoso. Esta propiedad nos permite sustituir un carácter
que ha llegado con bit de paridad incorrecto por otro carácter. ( ? predeterminado). Se puede sustituir
por una cadena de caracteres (Error, por ejemplo).
NullDiscard
Cuando se recibe el carácter nulo (00000000) puede ser que no sirva para nada a efectos de nuestra
aplicación, o que este carácter sea un dato mas. Esta propiedad acepta los valores True / False. Si es
True se desprecia el carácter Nulo. Si es False, se toma como un carácter mas.
CTSTimeout
CDTimeout
Es el tiempo máximo de espera (en milisegundos) desde que se activa la señal DTR hasta que se recibe
la señal CD (Carrier Detect - Detección de portadora). Este tiempo solamente tendrá importancia en
ciertas aplicaciones donde se espere recibir CD continuamente. No tendrá sentido cuando la aplicación
se queda en espera a recibir una comunicación, pero sin saber cuando la tiene que recibir. Si transcurre
el tiempo programado en esta propiedad, ocurrirá el evento CDTO. Poniendo el valor 0 se deshabilita
esta propiedad y no se producirá nunca el evento CDTO.
DSRTimeout
Similar a la anterior, pero en vez de esperar la señal CD se espera la señal DSR. Esta propiedad sí tiene
sentido, ya que si, por ejemplo, estamos conectados con un módem, y nuestra aplicación se pone a la
espera de recibir alguna llamada, activa la salida DTR, y espera recibir inmediatamente la respuesta de
que el módem está dispuesto, mediante la línea DSR. Si transcurre el tiempo programado sin recibir la
señal DSR se producirá el evento DSRTO . Poniéndola a 0, se deshabilita esta propiedad y nunca
ocurrirá el evento DSRTO.
RTSEnable
Activa (Pone a 1) la señal RTS (Request To Send - Petición de envío) Esta señal debe ponerse a 1 para
indicar al módem (o al equipo que va a recibir nuestra comunicación) que deseamos enviar datos. Debe
estar activada durante toda la transmisión de datos.
Cuando se pone la propiedad Handshaking a 2 (control con RTS / CTS) ó 3 (Control con RTS / CTS y
con X-ON / X-OFF) no debemos preocuparnos de poner a 1 la señal RTS, pues lo hace
automáticamente el puerto de comunicaciones. Esta propiedad está ahí para aplicaciones donde no se
emplee ese tipo de Handshaking y necesitemos activar algo antes de transmitir. (Caso por ejemplo de
transmisión de datos por radio, donde podemos usar esta señal de salida para activar el PTT (Push To
Talk - Pulse para hablar) y poner el transmisor en marcha)
DTREnable
Activa (Pone a 1) la salida DTR (Data Terminal Ready - Terminal de Datos Listo). Esta señal se
emplea para decirle al módem que el terminal (Ordenador) está preparado para recibir datos.
Se hace la misma observación que para la propiedad anterior respecto a los valores de la propiedad
Handshaking
Interval
Indica el tiempo (en milisegundos) del intervalo entre una y otra comprobación del estado de recepción
del puerto. El valor mínimo es de 55 ms.
El análisis del puerto de comunicación no tiene nada que ver con la generación del evento OnComm.
Este evento se producirá cuando se cumplan las condiciones para ello, independientemente del tiempo
programado en esta propiedad. La comprobación del puerto cada intervalo de tiempo marcado por esta
propiedad solamente afecta a averiguar el estado de las líneas auxiliares CD, DSR y CTS, y para saber
el número de caracteres existentes en los Buffers de transmisión y recepción.
PortOpen
Abre el puerto de comunicación. Puede tener los valores True (Para abrirlo) y False (Para cerrarlo) Si
tenemos un MSComm con Nombre (Name) MSComm1, para abrirlo ejecutaremos la siguiente
sentencia :
MSComm1.PortOpen = True
MSComm1.PortOpen = False
InBufferCount.
Nos permite averiguar cuantos caracteres tenemos en el Buffer de entrada. Con el mismo MSComm
anterior, comprobaremos el número de caracteres sin leer con la sentencia :
caracteressinleer = MSComm1.InBufferCount
OutBufferCount
Nos permite conocer cuantos caracteres quedan por transmitir en el Buffer de salida. Emplearemos la
sentencia :
caracteressinenviar = MSComm1.OutBufferCount
Output
Envía caracteres al Buffer de salida. Debe existir un signo igual ( = ) entre Output y lo que se envía al
Buffer. Para enviar la frase Curso de Visual Basic ejecutaremos la sentencia :
MSComm1.Output = variable
Input
Lee el Buffer de recepción. El número de caracteres leídos dependerá del valor de la propiedad
InputLen. Cuando la propiedad InputLen tiene el valor 0, el Buffer se lee completo. Si InputLen tiene
un valor distinto de 0, se leerá un número de caracteres igual al valor de esta propiedad.
CommEvent
Devuelve el evento mas reciente que ha ocurrido para generar el evento general OnComm (Vea mas
adelante). Esta propiedad no está disponible en tiempo de diseño y es de sólo lectura en tiempo de
ejecución.
LSB Visual Basic – Guía del Estudiante Capítulo 1 Página 590
Sintaxis NombredelMSComm.CommEvent
Break
Devuelve un valor (True / False) que indica que se ha recibido la señal Break.
variable = MSComm1.Break
CDHolding
Devuelve el estado de la línea de control CD (Detección de Portadora) Si es True, esa entrada está
activada, si es False, la entrada está desactivada.
variable = MSComm1.CDHolding
CTSHolding
Devuelve el estado de la línea de control CTS (Dispuesto para enviar) Si es True, esa entrada está
activada, si es False, la entrada está desactivada.
variable = MSComm1.CTSHolding
DSRHolding
Devuelve el estado de la línea de control DSR (Data Set Ready ) Si es True, esa entrada está activada,
si es False, la entrada está desactivada.
variable = MSComm1.DSRHolding
El MSComm tiene varios eventos, pero un solo Procedimiento : el Procedimiento OnComm. Este
procedimiento se ejecuta cada vez que se produce alguno de los eventos del MSComm.
Esto quiere decir que para escribir el código apropiado en el procedimiento del MSComm será
necesario analizar qué evento se ha producido y colocar, mediante una sentencia If .. Then el código
apropiado para cada uno de los eventos que se produzcan.
Para averiguar qué evento se ha producido puede hacerse consultando el valor de la propiedad
CommEvent. (Se toma como nombre del MsComm el de MsComm1)
End If
Los eventos del Comm pueden identificarse por una constante o un número. La constante, como todas
las de Visual Basic, tiene una expresión bastante difícil. Se pone entre paréntesis el número que
identifica a ese evento. Este número debe declararse como Integer.
ComEvCD (5) Cambio en la línea CD. Para conocer el estado actual de esa línea
(Activado/Desactivado) deberemos invocar la propiedad CDHolding
ComEvDSR (4) Cambio en la línea DSR. Igual que las anteriores. Debemos invocar la
propiedad DSRHolding para averiguar su estado actual.
ComEvSend (1) Cuando quedan en el búfer de transmisión menos caracteres que los
indicados en la Propiedad SThreshold
El puerto de comunicaciones de un PC está formado por varias entradas / salidas. El soporte físico es un
conector tipo Sub-D de 9 ó 25 contactos, macho en ambas versiones. Se necesita por tanto un cable con
conector Sub-D hembra de 9 o 25 pines para acceder a él.
TxD Transmisión de datos. Es una salida del ordenador. Por ella salen los datos en serie.
RxD Recepción de datos. Es una entrada del ordenador. Por ella entran los datos en serie.
RTS Request To Send. Petición de envío. Es una salida del ordenador. El ordenador pone a
1 esta señal cuando quiere enviar datos.
CTS Clear To Send. Dispuesto para enviar. Es una entrada del ordenador. Si está a 1
significa que el ordenador puede enviar datos pues el módem (o el dispositivo que
vaya a recibirlos) está preparado para hacerlo.
DSR Data Set Ready. Dispositivo de datos preparado. Es una entrada del ordenador. Le
indica que el módem está encendido y listo para funcionar.
DTR Data Terminal Ready. Terminal de datos listo. Es una salida del ordenador. Indica que
está listo para trabajar. Suele emplearse para indicar al módem que el ordenador está
dispuesto para recibir información.
Otra señal (disponible sólo en los ordenadores que tengan conector de 25 pines, y no en todos) es la
señal RING (timbre del teléfono) Es una entrada del ordenador. Le indica que está sonando el timbre
de la línea telefónica del módem.
3 TxD 2
2 RxD 3
7 RTS 4
8 CTS 5
6 DSR 6
5 GND 7
1 CD 8
4 DTR 20
RING 22
Tierra de protección 1
(La señal RING no está disponible en el conector de 9 pines. La detección del Ring del teléfono se
realiza directamente por la línea RxD. El módem envía la palabra RING cuando suena el timbre del
teléfono. La tierra de protección tampoco se usa en este conector.