Cómo
funcionan los eventos
|
Realizamos
programas para la gestión de empresas. Empresas medianas y
pequeñas. Programas de contabilidad, cartera de pedidos
clientes proveedores, facturación control de albaranes,
tesorería cartera de cobros y pagos y estadísticas.
Nuestro
agradecimiento a todos los que por unas causas o por otras
visitan nuestra web. Gestión de empresas PYMES. Curso de
programación de Visual Basic.
|
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 hayáis aprendido a leer
el manual y la ayuda del Visual Basic, y hasta os hayáis 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 podáis 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.
Sustitúyelo 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 terminen 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
asignáramos 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.