Curso Básico de Programación
en Visual Basic
Entrega
Veintidos: 4/Sep/98
por Guillermo "guille" Som
Si quieres linkar con las otras entregas, desde el índice lo puedes hacer
![]()
Ya habrás notado a lo largo de todas estas entregas, que de planificación, "nasti de plasti", osease nada de nada... Y es que las cosas van surgiendo y mezclándose y al final... hasta casi se entiende lo que digo... y eso que es difícil, sobre todo cuando entre una entrega y la siguiente casi han pasado dos meses... pero, como digo, suerte para los que empiezan cuando ya hay un montón de entregas, al menos se las leen del tirón... porque en más de una ocasión me han escrito, sin ánimo de ser demasiados criticones, supongo, que si el que empezara el curso en abril del año pasado, tuviese que esperar a que lo terminase... lo tenía claro... y es cierto, pero confío que los que empezasteis de los primeros, ya hayais aprendido a leer el manual y la ayuda del Visual Basic, y hasta os hayais atrevido con algunos de los muchos libros que hay sobre este lenguaje... que si bien, al principio os podrían parecer difíciles de entender, al menos ahora podais entenderlos, aunque sea un poco...
Como decía al principio, o al menos intentaba decir, el curso no lo tengo planificado y las cosas van surgiendo... la verdad es que ahora que leo los apuntes que tenía, fechados el 9 de julio, no se a que viene todo este rollo, así que mejor lo dejo y seguimos con el tema este de los eventos y los controles que podemos usar con el Visual Basic.
Ya sabes, (y si no lo sabias, te
lo cuento yo ahora), que los controles suelen estar incluidos en
un formulario, también pueden estar en controles diseñados por
nosotros y en otros tipos de "contenedores", (de basura
habrá pensado alguno, sobre todo cuando se pone manos a la obra
y no sale lo que quiere... no te desesperes, ya tendrás tiempo
de pillar buenos cabreos...), pero para simplificar, vamos a
pensar que están puestos en un formulario.
Esta aclaración viene a cuento para lo que te voy a contar
ahora.
Insisto, esto lo tengo manuscrito desde hace dos meses y la verdad es que o recuerdo en que estaba pensando cuando lo escribí, porque nada de lo que viene a continuación tiene que ver con el hecho de que un control esté en un formulario u otro contenedor, pero al menos podrás "intuir" que los controles no tienen porqué estar siempre "puestos" en un formulario.
Cuando se produce un evento en un
formulario, producido por el propio Form o por cualquier control
contenido en él, se deja de procesar el código que se estaba
ejecutando y se pasa a procesar el código contenido en el
evento.
Es importante que tengas muy presente esto que acabo de decir, ya
que es la causa de efectos no deseados.
Por supuesto, si el evento que se produce no tiene código
asociado, no pasa nada.
Aclaremos esto un poco; si te has
fijado en la ventana de código, cuando seleccionas un control de
la ventana izquierda, incluso el propio formulario, en la ventana
de la derecha te muestra los eventos disponibles, es decir los
eventos que Visual Basic podrá interceptar, normalmente no son
todos los que se generan, pero sí son los que el propio lenguaje
nos permite interceptar y por tanto en cualquiera de ellos
podemos añadir nuestro código para hacer algo cuando dicho
evento se produzca.
Pero esto no quiere decir que tengamos que escribir código en
todos ellos, sino sólo en los que nos interesen, pero el hecho
de no escribir código en un evento, no quiere decir que no se
producirá. Por ejemplo, si no escribes código en el evento LOAD
de un form, no impedirá que el form se cargue, se cargará,
independientemente de que queramos interceptar ese hecho o no.
Sigamos con lo que pasa cuando se
está ejecutando una parte del código y se produce un evento.
Por ejemplo, si mientras se procesa el código de un evento,
vuelve a producirse ese mismo evento, se vuelve a procesar el
código desde el principio y una vez terminado este segundo
evento, se continúa por dónde se interrumpió el anterior.
Esta es una de las gracias de Windows y todo el
tema de los eventos.
No te
preocupes si no te enteras que pronto veremos ejemplos.
Aunque en muchas ocasiones esto no
ocurre mientras nosotros no lo permitamos, hay veces en las que
no podemos "predecir" que es lo que ocurrirá, salvo
que comprendamos perfectamente cómo funcionan los controles y
sepamos cómo y cuando se producen algunos eventos; también
puede intervenir la suerte de darnos cuenta de que... "si
hago esto con este control, puede ocurrir este o aquel
evento"
Y esto que os digo es tan cierto como que nadie me ha regalado
aún un portátil... je, je.
Vamos a ver un pequeño ejemplo
para que salgas de dudas.
Crea un nuevo proyecto, cambia la propiedad Autoredraw
del form para que sea True, de esta forma, si lo redimensionas
podrás ver lo que se ha imprimido en el.
Escribe el siguiente código en el evento Click del form:
Private Sub Form_Click()
Dim i As Long
Dim j As Long
Print
For i = 1 To 10
Print i;
For j = 1 To 2000
DoEvents
Next
Next
Print
End Sub
Ejecuta el programa, cuando hagas
click en el formulario, verás que se imprimen los números del 1
al 10, van un poco lento, para que puedas hacer lo que te diré
ahora, mientras se estén imprimiendo los números, vuelve a
hacer click en el form, hazlo más o menos rápido, si tus
reflejos no te funcionan como quisieras, cambia el valor 2000 del
bucle j con otro mayor, así se irán imprimiendo los números de
forma más lenta.
Habrás notado que cuando aún no se ha terminado de imprimir los
primeros diez números, (si has pulsado antes de que se terminen
de imprimir, claro), se empiezan a imprimir en la siguiente
línea desde 1 y cuando dejes de hacer click, irán terminando
los bucles... supongamos que has pulsado tres veces, el resultado
podría ser este:
1 2 3 4 5 6 7 8
1 2 3 4 5 6
1 2 3 4 5 6 7 8 9 10
7 8 9 10
9 10
El primer bucle se interrumpió en
8, se inició el segundo, que se interrumpió en 6, se inició el
tercero y continuó hasta finalizar, después continuó el
segundo, (el que se quedó en 6), y termina, una vez que terminó
el segundo, se continua con el primero que se quedó en 8 y por
eso se imprimen el 9 y 10.
Como notarás, no se han perdido los valores... y el Visual
recordó por dónde se quedó... esto es debido a que todas las
variables de un procedimiento son locales a este procedimiento,
sea un evento o no, (realmente los eventos son SUBs que son
llamados por Windows), y se guardan antes de volver a entrar en
el procedimiento y una vez que el procedimiento acaba, se
desechan... la memoria que se usa para guardar temporalmente los
valores de las variables de un procedimiento se llama STACK (o
pila del programa), hay que tener en cuenta que esta
"pila" no es infinita y puede llegar a llenarse...
sobre todo cuando las cosas no se hacen bien. A este tipo de
variables locales, también se les llama variables automáticas,
por el hecho de que una vez que no se necesitan son
automáticamente borradas... cosa que no ocurre cuando las
variables se declaran a nivel de módulo o se declaran
"estáticas", más adelante veremos más sobre esto.
Lo que este pequeño ejemplo demuestra es lo que te he explicado antes, que se puede "reentrar" en un evento (y por extensión a cualquier procedimiento); si esto mismo se hiciera por código, no porque el usuario interviene con su ratón, tendríamos lo que se llama un procedimiento "recursivo", es decir que se llama a sí mismo... tendremos ocasión de ver algún ejemplo.
El evento más dado a este tipo de
"recursividad" es el evento Click. Este evento se
produce cada vez que pulsamos con el ratón sobre un control,
incluso sobre un formulario, como hemos visto en este ejemplo.
Este evento (Click), está presente en la mayoría de los
controles, prácticamente en todos.
La verdad es que poder "seguir" esto, imaginándose cómo lo hace el VB, es un poco difícil... vamos a ver otro caso, más usual y que seguramente será más fácil de entender...
Supongamos que tenemos un código
que procesa una serie de cosas y ese proceso puede llegar a ser
largo. Si ese código lo tenemos en el evento Click de un botón,
con idea de que se procese cada vez que se pulsa en el botón,
(cosa super-habitual), si no hacemos nada especial, mientras se
esté procesando el código, el form completo se quedará
"congelado", una vez que haya terminado el proceso, se
continuará "aceptando" nuevos eventos, normalmente se
quedarán pendientes de procesar y se ejecutarán a continuación
de terminar el proceso largo...
Por eso en ocasiones es conveniente usar, como en el ejemplo
anterior, la instrucción DoEvents. Con esta instrucción
permitimos que Windows notifique "en seguida" de que ha
ocurrido otro evento y nos da la posibilidad de procesarlo en ese
momento.
Eso o al menos indicar al usuario de que tenga paciencia y espere
a que se termine de hacer lo que se estaba haciendo, ya que si no
lo hacemos puede pensar que el programa se ha quedado
"colgado".
Vamos a modificar el código
anterior del Form_Click para ver esto que estoy diciendo.
Sustituyelo por este otro:
Private Sub Form_Click()
Dim i As Long
Dim j As Long
For i = 1 To 10
Print i;
For j = 1 To 2000000 'dos millones
Next
Next
Print
End Sub
Ejecuta el programa y haz click,
verás que no pasa nada, haz click de nuevo y esta vez si que
ocurre, hasta puede que el form se quede en blanco y al rato
volverá a la normalidad con las dos ristras de números
impresos... o más si no has tenido paciencia suficiente...
Una forma de solventar esa espera... es hacer que se muestre un
mensaje pidiéndonos que esperemos... pero eso no evitará que
volvamos a hacer click en el form... incluso el mensaje no se
mostrará hasta que se haya terminado de hacer lo que se estaba
haciendo... para que el mensaje se vea enseguida, se pueden hacer
dos cosas:
Una: usar un DoEvents justo después de imprimir el mensaje
Dos: usar Refresh para que se "pinte" el control, en
este caso el formulario.
...
Print "Un momento por favor..."
Refresh
For i = 1 To 10
...
En ambos casos tendríamos el mensaje mostrado en la pantalla, vale, pero si vuelves a hacer click... se volverá a procesar todo el código, etc... Aunque en esta ocasión, hasta que no finalice, no continúa el siguiente.
¿Cómo podemos evitar la
re-entrada en un evento cuando aún no ha terminado?
Usando lo que se llama un FLAG (o bandera), es decir una variable
que nos indique que ya se está procesando el código y de esta
forma poder evitar que se repita si aún no ha terminado.
Como ya te he contado antes, las variables locales son
automáticas y se desechan una vez finalizado el procedimiento,
al ser automáticas no "recuerdan" el valor que tenían
antes, salvo en las ocasiones que el VB las guarda en el Stack,
pero no son recordadas en las diferentes ocasiones en las que
entran en el procedimiento.
¿Recuerdas que te comenté lo de las variables estáticas?
Pues este tipo de variables son las que podemos usar para
prevenir estos casos. Las variables estáticas se declaran de
igual forma que las variables normales, salvo que en lugar de
usar Dim, se usa Static. Una vez declarada una variable
estática, deja de ser automática y VB no guarda una copia cada
vez que se entra en el procedimiento, sino que usa la misma cada
vez que se procese el código de ese evento. Por tanto cada vez
que se "cuela" el código en un evento (o
procedimiento), podemos saber si ya hemos estado antes, sin
terminarlo o no... para ello habría que hacer esto:
Private Sub Form_Click()
Dim i As Long
Dim j As Long
Static bFlag As Boolean
'Si no estamos en el evento
If bFlag = False Then
'conectamos la bandera
bFlag = True
Print "Un momento por favor..."
Refresh
For i = 1 To 10
Print i;
For j = 1 To 1000000
Next
'Debemos permitir que se procesen los mensajes
DoEvents
Next
Print
'Desconectamos la bandera
bFlag = False
End If
End Sub
Aquí hay dos detalles a tener en
cuenta, el primero es el uso de la variable estática, el otro es
el DoEvents, sin éste, el uso de la variable estática no
serviría para nada, ya que al no permitir a Windows que
notifique los eventos, estos se quedan guardados en espera a que
teminen los que había pendientes, y para que no se queden
guardados, usamos el DoEvents.
Prueba a pulsar varias veces en el formulario mientras se
muestran los números, verás que no se vuelven a mostrar.
Te explico un poco cómo funciona
esto:
La primera vez que se entre en el evento, el valor de bFlag
valdrá False, esto siempre es así con las variables Booleanas,
cuando se quiere averiguar el valor de una variable que no se ha
usado, ésta tiene un valor "vacio", cero en las
numéricas, cadena vacía en las cadenas y False en el caso de
las booleanas.
Como vale False, se cumple la condición, por tanto se asigna el
valor True a esa variable y se continúa procesando el código.
Cuando se llega al DoEvents, Windows procesa los mensajes que
tuviese pendiente y si tiene que enviarle uno a este formulario,
lo hará.
Suponiendo que hemos pulsado otra vez el ratón, al procesarse
los mensajes pendientes, Windows envía al form un nuevo evento
Click. Entonces se entra de nuevo en este procedimiento, pero
esta vez, el valor de bFlag no es False, por tanto no se hace
nada, ya que la condición no se cumple y se sale del evento.
Cuando se ha salido del evento, se continúa por donde estaba
antes y se sigue procesando el bucle, etc.
Una vez terminado el bucle i, se asigna de nuevo el valor False a
bFlag, para así poder permitir que se procese en otra ocasión
el código que hay.
Todo esto es posible gracias a que la variable bFlag es estática
y el valor almacenado se mantiene entre las distintas llamadas,
si no asignaramos el valor False al final, no podríamos entrar
más en este evento, ya que nunca se cumpliría la condición,
así que hay que tener cuidado con las variables estáticas, si
el uso que le queremos dar es parecido al que hemos visto.
Hay ocasiones en las que un evento
se "reproduce" muy a pesar nuestro, más que nada
porque hay veces en las que no es tan obvio.
Por ejemplo, cuando se selecciona un elemento de un ListBox, se
produce un evento Click; y si en ese evento Click, tenemos algún
código que seleccione elementos del control... podemos tener al
Visual Basic bastante atareado... entrando en un evento y desde
ese evento entrando a otro y así durante un rato... mientras
tanto, nuestro pobre VB guardando las variables automáticas en
el Stack (pila), pero llega un momento en el que la pila se
llena... y en ese momento nos suelta un mensajillo de esos que
nos indican que ya está hasta la coronilla de nosotros y de
nuestra mala forma de programar... y se para...
Igual que yo me voy a parar aquí, porque ya está bastante bien de tanto Click y tanto Stack y todas esas cosas...
Al final, la película que te he contado no era exactamente lo que estaba en el guión, pero más o menos lo que te he explicado era lo que quería explicarte...
Para terminar, te diré que en algunos controles, al pulsar Intro se produce también un evento Click, por ejemplo en los CommandButtons. Por supuesto para que se procese ese Intro, el control debe tener el foco.
Bueno, lo dicho, dejemos aquí
esta entrega que hay más cosas que hacer.
A ver si la próxima no tarda tanto como esta y seguimos
viendo... (espera que mire los apuntes, a ver que es lo que hay
preparado), ... en las notas del 12 de julio están los eventos
del ratón, en las del día 13, los del teclado... en fin, esto
promete seguir pesadillo... pero que le vamos a hacer... todo sea
para que te enteres un poco de cómo va todo esto de los
eventos...
Lo dicho en otras ocasiones: pórtate bien y no hagas estropicios y si quieres mandarme un mensajillo sobre el curso, usa el link este que te pongo.
Nos vemos.
Guillermo
![]()
Ir a la entrega anterior (la 21)