Curso Básico de Programación
en Visual Basic

 

Entrega Veinticuatro: 10/Oct/98
por Guillermo "guille" Som

Si quieres linkar con las otras entregas, desde el índice lo puedes hacer

 

Son muchas las cosas que ocurren en una aplicación mientras el usuario está trabajando, (o cualquiera sabe lo que estará haciendo). Ya vimos algunos eventos relacionados con el ratón, pero también habrá muchos otros relacionados con el teclado, es decir, cada vez que el usuario pulse una tecla se producirán varios eventos, que la tecla se mantiene pulsada, que la tecla se ha soltado... en resumen que la tecla se ha pulsado. También se puede saber cuando una serie de teclas se pulsan al mismo tiempo, realmente cuando se pulsan algunas de las "especiales": Shift, Ctrl o Alt.

Básicamente son tres los elementos que la práctica totalidad de los controles y formularios reconocen: KeyDown, KeyUp y KeyPress, vamos a verlos por separado y con algo de detalle, además de algún ejemplo aclaratorio.

 

Evento KeyPress.

Este evento se produce cada vez que una tecla se pulsa, al menos una tecla normal... ya que las teclas "especiales" no se consideran normales y no se suelen detectar en este evento, salvo que se use junto a una tecla "normal".

Veamos que información acepta este evento como parámetro:

Private Sub Text1_KeyPress(KeyAscii As Integer)

El parámetro KeyAscii nos indicará el código de la tecla pulsada, este código nos lo da como valor numérico, no como una cadena, es decir que si pulsamos la tecla A mayúscula, valdrá 65, ya que ese es el valor ASCII de ese caracter, (realmente es un valor ANSI, pero...).
Para saber el valor de las teclas, puedes pulsar F2 en el IDE del Visual Basic y buscar las constantes de KeyCode o en la ayuda busca la palabra KeyCode, de todas formas los valores Ascii se pueden saber o usar con la función ASC, por tanto se puede hacer una comparación de este tipo:

If KeyAscii = Asc("A") Then
    'se ha pulsado la A mayúscula
End If

En este evento se pueden detectar un montón de teclas, todas las alfanuméricas y otros caracteres, además de la tecla INTRO (return), aunque tendrás problemas con la tecla TAB, ya que esta tecla tiene un significado especial y no es tan fácil de detectar...
Normalmente se suele detectar la pulsación de la tecla INTRO, entre otras cosas porque suele emitir un pitido cada vez que se pulsa, al menos en las cajas de texto... para evitar ese "pitido", se puede hacer esto:

If KeyAscii = vbKeyReturn Then
    KeyAscii = 0    'Eliminamos el pitido
End If

Con esta asignación, lo que hacemos es indicarle al VB que no se ha pulsado nada... o al menos decirle que no tenga en cuenta que se ha pulsado esa tecla.

Otra de las cosas que se suele hacer cuando se pulsa INTRO es pasar al siguiente control, de la misma forma que si hubiésemos pulsado la tecla TAB, esto se suele hacer más a menudo de lo que parece, sobre todo para usuarios que están acostumbrados a usar programas de MS-DOS, para conseguirlo, además de tener "conectada" la propiedad TabStop de los controles, (si esta propiedad tiene el valor FALSE no se puede usar TAB para cambiar de control), tendremos que decirle al Windows que lo que se ha pulsado no ha sido el INTRO sino el TAB... es decir con algo como esto:

If KeyAscii = vbKeyReturn Then
    KeyAscii = 0    'Eliminamos el pitido
    'si queremos pasar al siguiente control
    'tal como lo haríamos pulsando la tecla TAB:
    SendKeys "{TAB}"
End If

Es decir, usamos la instrucción SENDKEYS para que envíe una tecla TAB...
Para ver que teclas podemos "enviar" con SendKeys, consulta la ayuda...

Por supuesto que se pueden hacer muchas otras cosas en este evento, pero eso dependerá de nuestra aplicación... y de nuestros gustos...

 

Eventos KeyDown y KeyUp.

Para tener un mayor control en las teclas pulsadas, se suelen comprobar en los eventos KeyDown y KeyUp, la principal diferencia con el evento KeyPress es que en este caso no son códigos ASCII, sino códigos de teclado...
Veamos los parámetros:

Private Sub Text1_KeyDown(KeyCode As Integer, Shift As Integer)

KeyCode es el código de la tecla, no el código ASCII, aunque en la mayoría de los casos coincide, aunque no en todos.
Shift es para saber si se ha pulsado al mismo tiempo alguna de las teclas especiales: Shift (mayúsculas), Ctrl o Alt.

El valor de las teclas KeyCode se puede ver en la ayuda o pulsando F2, como te indiqué antes, entre otras teclas, además de las normales, se pueden detectar las teclas de función (F1, F2, etc), las teclas de bloqueo de mayúsculas, bloque numérico, etc.
Los valores del parámetro Shift son los mismos que en el evento del ratón, es decir: 1 para Shift, 2 para Control, 4 para Alt y la combinación de estos valores, pásate por
la entrega anterior para ver esos valores.

Debes tener en cuenta que el evento KeyDown se produce cuando se presiona la tecla y KeyUp cuando se suelta; según que casos, puede ser interesante hacer las comprobaciones en uno u otro evento. Por ejemplo, podrías hacer algún tipo de efecto cuando se pulsa la tecla y otro diferente cuando se suelta...

 

Si quieres tener un control sobre las pulsaciones de las teclas, puedes querer comprobarlas de forma genérica antes de que sean enviadas al control en el que se produce, hay una forma de hacerlo, para que sea el formulario el que las reciba antes... Para ello deberás asignar un valor TRUE a la propiedad KeyPreview del formulario en cuestión. Ahora cada pulsación de teclas serán procesadas primero por los eventos Keyxxx del formulario y después por esos eventos del control que tenga el foco. Aunque si un botón tiene asignada a TRUE la propiedad Default, no se procesarán esas pulsaciones en el formulario.

Vamos a ver un ejemplo para detectar la pulsación de la tecla ESC y si esta se produce, mostrar un cuadro de diálogo que preguntará si queremos terminar la aplicación... no es algo común hacer esto, pero en algunas ocasiones puede ser interesante poder terminar una aplicación al pulsar ESC, dónde utilizar esta forma de "cerrar" un programa será cuestión tuya. Esto también se consigue teniendo un botón con la propiedad Cancel = TRUE, si ese botón se encarga de cerrar el programa, ya no tendrás que hacer nada más... pero vamos a suponer que no tenemos ese botón y queremos cerrarlo al pulsar ESC.

También veremos cómo pasar al siguiente control cuando pulsemos INTRO; aunque esto sólo será posible si tenemos asignada a TRUE la propiedad TabStop de cada uno de los controles que estén en ese formulario.

Para este ejemplo, vamos a crear un nuevo proyecto, automáticamente se añade Form1. Ahora insertaremos varios controles, que realmente no harán nada, pero así veremos cómo funciona todo esto. Veamos añade 2 CommandButtons, 2 TextBoxes y un par de Labels.

Te voy a explicar es cómo suelo "manejar" los controles cuando los inserto en un formulario, que tamaños suelo usar, que posición, etc. Normalmente suelo usar unos tamaños "predefinidos" para los controles:
Los Labels suelo darles una altura de 255 ó  285 si tienen borde, a los TextBoxes 315 y a los CommandButtons 375 ó 405.
El ancho dependerá del contenido que vayan a tener, en los botones suelo dejarles el ancho por defecto: 1245

La separación de los puntos del grid, los suelo tener definidos a 30x30 puntos. Es decir que si muevo los controles o les cambio el tamaño, suelo trabajar con valores de 30 puntos (¿twips?). Si quieres cambiar el valor 120 que es el que tiene el VB por defecto, haz lo siguiente:
Selecciona el menú Tools (Herramientas), te mostrará un cuadro de diálogo, selecciona la solapa General, verás que hay una serie de opciones para manipular el "grid" que se muestra durante el diseño de los formularios. Si no has cambiado nada, tendrás seleccionada las dos opciones que hay, en inglés dice lo siguiente: (es que ahora tengo instalado el VB en inglés, así que si la traducción que te doy no coincide con lo que te muestra... intenta encontrarlas)
--Show Grid (Mostrar Grid o rejilla),
--Align controls to Grid (Ajustar los controles al grid)
También hay dos cajas de texto para indicar la separación de los puntos mostrados en el grid, estos son los valores que tendrás que cambiar.

 

Array de controles.

Aunque en las aplicaciones simples no hay que preocuparse por los recursos que usemos, no está de más acostumbrarse a aprovecharlos, así nos será más fácil tomarlo como costumbre. Una forma de ahorrar esos recursos del sistema es usando arrays de controles, al menos siempre que sea posible. Por ejemplo los labels son unos controles que nos permiten usar arrays sin demasiadas complicaciones, ya que normalmente se suelen usar para poner título a las cosas y poco más. En los casos en que uso los labels para mostrar información, puede que no use un array, pero por regla general uso arrays de labels.
Para crear un array de cualquier control, se puede hacer de la siguiente forma: (esta es la que yo uso, porque entre otras cosas, me mantiene los tamaños que le asigné)
Inserta un label, se creará Label1, seleccionalo, asigna el tamaño que quieres que tenga, copialo, (pulsando el botón dereho del ratón y seleccionando Copiar), ahora pulsa en cualquier parte del formulario y selecciona pegar. Te preguntará si quieres crear un array de Label1 (o el nombre que le hayas dado), dile que Si y ya lo tienes creado.
La diferencia entre dos labels, (o cualquier otro control), diferentes y dos que estén en un array es, entre otras, estas:

  Controles diferentes Un Array de Controles
Nombre Cada una tendrá un nombre diferente: Label1, Label2, etc. Todas tienen el mismo nombre, pero se debe usar un índice para indicar a cual nos referimos.
Label1(0), Label1(1), etc.
Eventos Cada control tendrá su propio juego de eventos. Todos los controles comparten el mismo evento, pero ese evento incluirá un nuevo parámetro que será el índice dentro del array del control que lo ha producido.


La ventaja de usar arrays es que sólo tienes que incluir código en un sólo evento, ya que todos los controles en ese array comparten el mismo "nombre". Pero aún así, cada uno de los controles genera su propio evento. Para poder distinguirlos hay que usar el índice del array, así si el evento lo ha producido el que tiene el índice cero, el parámetro Index valdrá cero.
Vamos a ver cómo se mostraría esos eventos en la ventana de código:

Private Sub Label1_MouseMove(Index As Integer, Button As Integer, Shift As Integer, X As Single, Y As Single)

Los parámetros son los mismos que si no estuviesen en un array, con la diferencia de que ahora se incluye uno nuevo: Index.
Como te he comentado, éste parámetro es el que nos indicará el control que ha recibido el evento. Para poder hacer cosas diferentes según el control que sea, se puede usar una serie de If...Then o Select Case...End Select, según tus gustos.
Por ejemplo, en el evento Click de un array de Labels:

Private Sub Label1_Click(Index As Integer)
    'Para saber en que etiqueta se ha hecho click:
    Select Case Index
    Case 0
        'Se ha hecho Click en el label de índice cero
    Case 1
        'Se ha hecho Click en el label de índice uno
    End Select
End Sub

Esto mismo es aplicable a los TextBox que tengan algún tipo de relación, la ventaja, como puedes ver es que comparten los mismos eventos, en algunos casos tendrás que saber el control que se está procesando, pero en otros no, por ejemplo, si tienes un array de TextBoxes y quieres que se seleccione todo el texto al recibir el foco, esto se consigue añadiendo el siguiente código en el evento GotFocus:

Private Sub Text2_GotFocus()
    'Seleccionar el texto que haya en el Text2

    'Si usamos With, es más cómodo codificar, ya que no hay
    'que estar escribiendo el nombre del control.
    With Text2
        'Posición incial de la selección: cero, el primer caracter
        .SelStart = 0
        'Seleccionar todo el texto:
        'esto se averigua con la longitud del contenido de la
        'propiedad Text
        .SelLength = Len(.Text)
    End With
End Sub

Si tuviésemos un array de Text1, haríamos lo mismo, pero simplemente cambiando el nombre del control que ponemos después del With:

Private Sub Text1_GotFocus(Index As Integer)
    'Seleccionar el texto que haya en el Text1(Index)

    'Si usamos With, es más cómodo codificar, ya que no hay
    'que estar escribiendo el nombre del control.
    With Text1(Index)
        'Posición incial de la selección: cero, el primer caracter
        .SelStart = 0
        'Seleccionar todo el texto:
        'esto se averigua con la longitud del contenido de la
        'propiedad Text
        .SelLength = Len(.Text)
    End With
End Sub

Fíjate que el código usado es el mismo que para Text2. Si tuviésemos 20 cajas de texto, tendríamos que repetir este código veinte veces, una vez para cada control, pero al tener un array sólo habría que ponerlo una vez.
Otra forma de no tener que repetir el código un montón de veces, sería creando un procedimiento que hiciera ese trabajo. Veamos el código para ese procedimiento:
Antes de ver el código, hay que saber que el procedimiento necesita saber el control en el que se debe seleccionar, por tanto necesitará recibir como parámetro un textbox... veamos el código y espero que captes la forma de hacerlo:

Private Sub SeleccionarTexto(unTextBox As TextBox)
    'Seleccionar el texto que haya en el TextBox

    'Si usamos With, es más cómodo codificar, ya que no hay
    'que estar escribiendo el nombre del control.
    With unTextBox
        'Posición incial de la selección: cero, el primer caracter
        .SelStart = 0
        'Seleccionar todo el texto:
        'esto se averigua con la longitud del contenido de la
        'propiedad Text
        .SelLength = Len(.Text)
    End With
End Sub

Como puedes comprobar el código es el mismo que en los ejemplos anteriores, lo único que se cambia es el nombre del control que está después del WITH. El tipo de datos que se recibe como parámetro es del tipo TextBox, ya que ese ese el tipo de control que vamos a manipular, pero si se quisiera hacer más genérico y poder usarlo con cualquier control que tenga una propiedad Text, podemos cambiar el tipo de parámetro para que valga para cualquier control:

Private Sub SeleccionarTexto(unTextBox As Contol)

De esta forma, lo mismo dará usar un RichTextBox, un ComboBox o un control que disponga de las propiedades usadas.

Para usar este procedimiento, haríamos lo siguiente en el evento GotFocus de los controles en los que queremos seleccionar al recibir el foco:

'Para el Text2:
Private Sub Text2_GotFocus()
    'Llamamos al procedimiento, usando como parámetro
    'el control Text2
    SeleccionarTexto Text2
End Sub


'Para el array Text1:
Private Sub Text1_GotFocus(Index As Integer)
    'Llamamos al procedimiento usando el Text1 correspondiente,
    'en ese caso hay que indicar el índice.
    SeleccionarTexto Text1(Index)
End Sub

En el caso del Text1, que es un array, se pasa el TextBox que ha recibido el foco: por tanto hay que indicarlo con el índice.

Bueno, vamos al tema, que me estoy despistando un poco... aunque esto tenía que contártelo algún día, así que tampoco ha venido mal del todo... ¿verdad?

Para que un formulario procese la pulsación de las teclas antes que los controles, hay que asignar a la propiedad KeyPreview el valor True. Haz click en el formulario, selecciona la ventana de propiedades, (pulsa F4 si no está visible), busca la propiedad KeyPreview y selecciona True ya que por defecto el valor es False.
Añade el siguiente código al código del formulario:

Private Sub Form_KeyPress(KeyAscii As Integer)

    If KeyAscii = vbKeyEscape Then 'Se ha pulsado ESC
        'Descargamos el formulario
        Unload Me
    ElseIf KeyAscii = vbKeyReturn Then 'Se ha pulsado Intro
        'Borramos la tecla pulsada para que no "pite"
        KeyAscii = 0
        'Enviamos una pulsación TAB
        SendKeys "{TAB}"
    End If
End Sub

Nota:
Lo de la pulsación del Intro no funcionará con los botones ni con las cajas de texto que tengan un valor True en la propiedad MultiLine, ya que el Intro se usa para cambiar de línea.

El orden de tabulación, es decir que control recibirá el foco cuando se pulse la tecla TAB, estará indicado por el valor de la propiedad TabIndex de cada uno de los controles. Ese valor se cambia de forma automática cuando se modifica el valor de otro control. Al principio tiene el valor según los controles añadidos, pero si quieres cambiarlo, puedes modificar ese valor, sabiendo que el primer control que recibirá el foco será el que tenga el valor cero y después los que tengan el 1, 2, etc.

Las etiquetas no reciben el foco, pero tienen esa propiedad, entre otras cosas, es interesante que la tengan, ya que lo que hacen es pasar el foco al control que tenga el valor siguiente dentro del TabIndex. Normalmente se le suele dar a una etiqueta el TabIndex anterior al textbox que tenga "asociado", o al que le está dando el título, en caso de que esa sea su "utilidad".
Pero no podemos darle el foco a una etiqueta.
¿Entonces que utilidad tiene que tenga la propiedad TabIndex?
Que podemos usar una tecla de acceso rápido y si pulsamos esa "tecla" el foco pasará al control siguiente.
Vamos a verlo con un ejemplo.
En el formulario habíamos añadido dos labels: Label1(0) y Label1(1)
Selecciona la primera y escribe lo siguiente en la propiedad Caption: &Nombre, comprobarás que la N está subrayada, pues esa es la tecla de acceso rápido, para poder acceder a una tecla de acceso rápido tendrás que pulsar Alt+tecla, en este caso Alt+N.
Selecciona la otra etiqueta y asignale esto en el caption: &Edad. Para poder usar este acceso rápido tendrás que pulsar Alt+E.

Ahora vamos a poner en orden los valores de TabIndex:
Supongamos que el formulario tiene este aspecto:

--Selecciona el botón Aceptar, pulsa F4 para mostrar la ventana de propiedades, busca TabIndex y escribe 0, pulsa Intro.
--Selecciona el botón cancelar, en la ventana de propiedades seguirá estando seleccionada la misma propiedad, escribe de nuevo cero.
--Haz lo mismo con el resto de los controles: LblInfo, Text2, Edad, Text1, Nombre

Ahora el orden de los valores de TabIndex será: Nombre, Text1, Edad, Text2, LblInfo, Cancelar y Aceptar.

Pulsa F5 para ejecutar el programa, el foco lo recibirá primero el Text1, el cual se seleccionará. Si pulsas la tecla TAB verás que el foco va cambiando a Text2, Cancelar y Aceptar, para volver a Text, etc.
Prueba pulsando Intro cuando estés en alguno de los TextBoxes y verás que se cambia el foco al siguiente control.

Para terminar con las pruebas, pulsa Alt+N y verás que el control que recibe el foco es el Text1, después pulsa Alt+E y el que reciba el foco será el Text2.

 

Bueno, creo que ya está bien por hoy, no sea que te acostumbre a esto de las entregas largas y no es cuestión.
En la siguiente entrega veremos algunos eventos más y con ello concluiremos esta tanda, para pasar a ver algo sobre las propiedades de los controles y algunas otras cosillas... como adelanto, para tu tranquilidad, te diré que ya tengo "manuscritas" tres entregas más y algunos "esbozos" para otras cuantas... así que no te lo tomes con demasiada calma que voy a seguir dándote la lata...

Hasta la próxima que será, espero dentro de poco... porque como me retrase... no será hasta entrado el mes de Noviembre, ya que me voy unos días a Gran Canaria... así que... hazme un poco la pelota si quieres más entregas antes de que me vaya el próximo día 21... je, je...

Nos vemos.
Guillermo


 
entrega anterior ir al índice siguiente entrega

Ir al índice principal del Guille