Curso Básico de Programación
en Visual Basic
Quinta Entrega: 26/Jul/97.
por Guillermo "guille" Som
Te recomiendo que leas las entregas anteriores,
aquí tienes los links:
La Primera,
la Segunda,
la Tercera
y la Cuarta.
Hoy no voy a pasar lista, porque me imagino que los que faltan estarán aprovechando el puente este de Santiago o estarán preparando las maletas para irse de vacaciones... ¡que suerte! ¡Quién pudiera estar en la costa! con las playas "abarrotadas" de gente... je, je... yo no estoy de vacaciones, pero tengo la playa a menos de 50 metros de mi casa...
...que no se te pongan los dientes largos y límpiate las babas... para tu consuelo te diré que aún no he pisado la arena de la playa... O 8-|
Vale, vale, también puedes irte al campo con los mosquitos... ¿se nota que me gusta más la playa? y que conste que la sierra que tenemos por esta zona es una maravilla... no, no soy del Patronato de Turismo...Bueno, una vez dado un "repaso" veraniego, vamos al código, que es lo que te ha traído a esta página.
Hoy no tengo nada que rectificar de la entrega anterior... salvo varios errores tipográficos... De todas formas si lees estas entregas y hay posteriores, sería conveniente que fueses a la siguiente y te leyeras el principio, ya que aparte de poner algún comentario "chorra", suelo dar un repaso a los "errores" de la entrega anterior.
Ahora viene el contenido real de la quinta entrega.
No voy a dar la solución al problema/ejercicio planteado en la entrega anterior, voy a dejar que la deduzcas. Para que tengas base suficiente, te voy a contar un poco cómo funciona el Visual Basic y por extensión todas las aplicaciones de Windows.
En la segunda entrega creamos un programa que mostraba un form en el que teníamos una caja de texto (TextBox), un botón (CommandButton) y dos etiquetas (Label).
Cuando, después de pulsar F5 o CRTL+F5, se ejecuta la aplicación, de lo único que podemos tener certeza es que se ejecutará el código que se encuentra en el procedimiento Form_Load. Este procedimiento (sub) en concreto es lo que se llama un evento, y se produce cada vez que el formulario (form) se carga (load) en la memoria. Antes de entrar en detalles del porqué podemos tener la certeza de que ese código se va a ejecutar, tengo que seguir con mi 'ponencia' de cómo funcionan las aplicaciones Windows.Se dice, (otros lo dicen y yo me lo creo), que Windows es un sistema o entorno operativo 'dominado' por los eventos. Esto ya lo dejé caer al principio de la segunda entrega. En Windows cada vez que movemos el ratón, pulsamos una tecla, tocamos una ventana o cualquiera de los controles que están en ellas, se produce un evento. Cuando se 'dispara', (los anglosajones usan 'fire' para indicar que se produce el evento), uno de estos eventos, Windows le dice al form, (de forma bastante rápida, aunque en algunas ocasiones no tanto como nos hubiera gustado), que se ha movido el ratón y que el ratón ha pasado por encima de tal ventana o de ese control y así con todas las cosas. La verdad, no es de extrañar que de vez en cuando falle el sistema, ¡¡¡es que no para de disparar!!! y algunos disparos se le puede escapar y...
...que chiste más malo, ¿verdad? Ya pensabas que el comentario ese del 'fire' era porque "el Guille se cree que está traduciendo un artículo de VB Online"...
Lo que quiero dejar claro es que a diferencia de los lenguajes que funcionan en MS-DOS, en Windows no podemos 'predecir' cual será el código que se va a ejecutar. No debes 'planificar' tu programa dando por sentado que... "después de esto el usuario 'tiene' que hacer esto otro y así yo podré hacer una comprobación para..." ¡ NO ! Aquí (en Windows) no existe la programación lineal, no des nunca por hecho que esto es lo que va a ocurrir..., porque casi con toda seguridad ¡no ocurrirá!
Veremos cómo podemos 'controlar' que algunas cosas se hagan cuando nosotros queramos, pero esto será sólo cuando el usuario de nuestra aplicación realice una 'acción' que estaba prevista; también codificaremos para que se ejecute parte del código cuando el usuario no haga lo que habíamos previsto que hiciera. Pero eso lo iremos viendo poco a poco...Todo programa de Windows tiene un punto de entrada; en el caso de Visual Basic, puede ser bien un formulario o bien un procedimiento de un módulo BAS, (de debe llamarse obligatoriamente Main); los módulos los veremos en otra ocasión.
Normalmente las aplicaciones suelen tener más de un formulario y algún que otro módulo. Pero tenga uno o ciento, siempre hay un único punto de entrada (o de inicio). Por regla general suele ser un formulario. En nuestro ejemplo sólo tenemos un form en el proyecto, por tanto no hay duda alguna de cual será el punto de entrada del programa.
Perdona si me extiendo en esto, pero tanto si tú lo sabes como si no, creo que tú ahora lo sabes... (es difícil esto de escribir una cosa para tanta gente con distintos niveles...)
Cuando Windows inicia el programa, 'debe' cargar el formulario en la memoria. Y desde el momento que se prepara para hacerlo, ya está con los 'tiritos' y mandando 'mensajes' a la ventana (todo formulario es una ventana), pero no sólo le está avisando a la nuestra que la acción ha empezado, sino que lo hace prácticamente a los cuatro vientos; si otra aplicación quiere enterarse de lo que ocurre en Windows, sólo tiene que conectarse a la 'mensajería' de éste entorno operativo y leer las 'noticias'... pero esto ya es complicarse la vida y todavía no nos la vamos a complicar tanto... o más... (pensará alguno después de respirar aliviado...)
Lo que ahora interesa es saber que el 'evento' Form_Load se produce cuando esta ventana pasa del anonimato a la vida pública, aunque no la veamos, estará en la memoria de Windows y se producirá el primer evento del que tenemos 'certeza', por tanto este es un buen sitio para poner código de inicialización.
Realmente el Form_Load no es lo primero que puede ocurrir al iniciarse un formulario, pero por ahora vamos a pensar que sí; porque sino esta entrega no la termino hasta después del verano... ¡¡¡que me gusta darle vueltas a las cosas!!!Ahora que se ha cargado el form en la memoria... ¿que ocurrirá? Pues, si el usuario se va a tomar unas cañas: nada. Sólo ocurrirá algo cuando interactuemos con el form, es decir, le demos razones a Windows para 'pegar tiritos'.
Nuestra aplicación, (te recuerdo que tenía, entre otras cosas, un textbox y un botón), esperará a que se produzca algunas de las acciones para las que está preparada.
Y la pregunta es ¿que es lo que puede ocurrir? Para saber 'todas' las cosas que pueden ocurrir en nuestra ventana, (no recuerdo si has pulsado F5 o no), finaliza el programa y muestra la ventana de código.En la parte superior de la ventana hay dos listas desplegables, la de la izquierda tiene todos los controles, (en otra ocasión veremos que no siempre es así), que tenemos en el form, incluido éste, además de uno especial que se llama General.
Pulsa en la lista de la izquierda, para que se despliegue y te mostrará esto:Estos son los cinco controles, incluyendo el form, que pueden recibir mensajes de Windows. Pulsa en cualquiera de ellos. En la lista de la derecha están todos los procedimientos (eventos) soportados por el control de la izquierda. Cada control mostrará los eventos que VB y/o Windows permite que se produzcan. Estos ocurrirán por distintos motivos, cada uno tiene su 'tarea', nos pueden avisar de que se ha pulsado el ratón, que se acaba de pulsar una tecla, que se ha modificado lo que antes había, etc.
Una llamada a la precaución.
Los eventos son 'procedimientos' y no sólo se puede llegar a ellos porque se produzca una acción del usuario o del propio Windows y si me apuras, incluso de Visual Basic... Nosotros podemos 'provocarlos' ¿cómo? simplemente haciendo una llamada al SUB que queramos o actuando en el control de manera que ocurra alguno de los eventos soportados.
Por ejemplo, en el Form_Load tenemos que se asignan cadenas vacías tanto al Label2 como al Text1:
Label2 = ""
Text1 = ""
Cuando VB asigna una cadena vacía (o cualquier otra cosa), al Label2 está borrando el contenido del 'Caption' de esta etiqueta y asignando algo nuevo, es decir lo está cambiando. En nuestro programa no ocurre nada, salvo que se borra lo que allí hubiera, pero realmente se está produciendo el evento Label2_Change, porque hemos cambiado el contenido. VB sabe que no hemos escrito código para manejar esta situación, así que... hace la vista gorda y simplemente cambia el contenido del Label2.Caption sin hacer nada más.
Pero al asignar una cadena vacía al Text1, también se borra el contenido y se produce un Change, sólo que en este caso, al no tener Caption, lo que se borra es el Text; de todas formas sea como fuere, se produce el evento Text1_Change. Nuestro querido amigo Visual Basic, sabe que hemos escrito código para este evento y sabe que tiene que hacer lo que allí se dice...
En este caso, nuestro código se ejecuta, pero realmente no hace nada de interés o por lo menos nada que se pueda apreciar de forma visual. No voy a explicar para que sirve ese código, porque ya lo hice en su día, lo que espero es que hoy lo entiendas mejor...
¿Cómo? que no sabes de qué código estoy hablando... pues del ejemplo de la segunda entrega, creo que ya lo dije al principio... a ver si prestamos más atención y dejamos de pensar en las vacaciones... ¡estos niños!El código interesante es el que se ejecuta cuando se pulsa en el botón:
Label2 = "Hola " & Text1
Aquí se asigna al Caption del Label2 lo que hay después del signo igual, en este caso actúa 'casi' como una variable. Y ya sabrás que antes de asignar un valor a una variable, se procesa todo lo que está después del signo igual, por tanto, Visual Basic tomará el contenido del Text1, es decir lo que se haya escrito y lo unirá (&) a la palabra "Hola ", una vez hecho esto, lo almacena en el Caption del Label2.
Y si en lugar de guardarlo en un control label, lo asignáramos a una variable... y si en lugar de escribir nuestro nombre, escribiésemos un número... y si la variable en la que guardamos ese número se llamara, por ejemplo, maxBucle o elMaximo...
Pues que tendríamos una parte resuelta de la tarea esa que puse como ejercicio en la entrega anterior.
Pero, este form de la segunda entrega no nos sirve. Tendremos que cargar el de la vez pasada y añadirle un par de cajas de textos y un par de etiquetas, para que indique lo que se debe introducir en cada textbox; el aspecto sería este:Pero nos encontramos con un problema: ¿cómo puedo asignar un valor a maxBucle, si las constantes no pueden cambiar de valor? Fácil, conviértela en variable. Pero debes recordar la pista que di al final: "Las variables declaradas dentro de un procedimiento son solamente visible dentro de ese procedimiento".
De este tipo de variables se dice que son locales.
Alumno: ¿Que significa esto?
Guille: Que sólo pueden usarse dentro del procedimiento en el que se han DIMensionado o declarado.
A: Vale, "mu bonito" y ¿que pasa?
G: Esto... que no pueden usarse en otro sitio...¿Recuerdas la recomendación de usar Option Explicit?
Pues gracias a Option Explicit, se solucionan el 99% de los fallos 'involuntarios' con las variables... y no exagero!!!
Es super-fácil escribir de forma incorrecta el nombre de una 'bariable' y no vengas con el cuento de que a tí nunca te ocurre, porque no me lo voy a creer... bueno, de ti si, pero no todos son tan minuciosos como tú...
(para que nadie se sienta ofendido/a, quiero que veas en esto que acabo de poner... la intención que tiene, es decir que me dirijo a más de un "ti"... ya sé que no eres tan torpe como para no haberlo 'captado', pero con esta aclaración me quedo más tranquilo.)Según cómo y dónde se declare una variable, su 'visibilidad' o área de cobertura, será diferente... también se puede usar la palabra ámbito... es que como en las emisoras de radio se habla de la cobertura... pues...
Una variable puede tener estas coberturas:
--Privada o Local a nivel de procedimiento (Sub, Function, etc.)
--Privada o Local a nivel de módulo (FRM, BAS, etc.)
--Pública o Global a nivel de aplicación (en este tipo hay una forma especial de usar las variables que veremos en otra ocasión)Explicando los dos primeros puntos.
Cuando declaramos o dimensionamos una variable 'dentro de' un procedimiento, ésta sólo es visible en ese procedimiento; fuera de él no es conocida y cualquier uso que se intente hacer de ella, producirá un error... si has sido obediente y has usado el Option Explicit. En caso de que no hayas puesto la 'obligación' de declarar todas las variables, te llevarás una sorpresa de que no tiene el valor esperado.
A: ¡JA!
G: ¿No te lo crees? Vale. Vamos a comprobarlo.
Abre un proyecto nuevo, pon un textbox y un botón..
Abre la ventana de código, borra el Option Explicit.
En el Form_Load haz esta asignación: Incredulo = "No me lo creo"
En el Command1_Click escribe esto otro: Text1 = Incredulo
Pulsa F5 y haz click en el botón...
¿Que ha pasado?
(Tengo que comprobarlo, para no meter la pata, pero se supone que el texto se borrará sin poner nada...)
Bien, eso ocurre porque la variable usada en el Form_Load no tiene nada que ver con la usada en el Command1_Click.
Con esto comprobamos o demostramos que podemos tener variables diferentes con el mismo nombre. La única condición es que no pueden estar juntas, aunque hay un truco para juntarlas sin que ellas se enteren...En este último ejemplo, nuestra intención es tener una variable que sea 'conocida' en todo el form. Cuando necesitemos variables con un ámbito a nivel de módulo, tenemos que declararla o dimensionarla en la sección de las declaraciones generales; ya sabes, en la lista izquierda de la ventana de código seleccionas General y en la de la derecha Declarations ( Declaraciones).
Muestra la ventana de código y en General/Declaraciones escribe:
Option Explict 'retornamos a las buenas costumbres
Dim Incredulo As String
Pulsa F5 para ejecutar el programa, pulsa en el botón y... ¡AHORA SI!
Tenemos una variable que puede ser 'vista' en todo el form. Ya puedes usar 'Incredulo' donde quieras, ahora siempre será la misma variable y contendrá lo último que se le haya asignado.A partir de la versión 4 del VB, entra en juego una nueva palabra, 'Private', que suele usarse en las declaraciones de las variables a nivel de módulo, de esta forma es más fácil entender la intención de la misma; por tanto la declaración anterior podemos hacerla también así:
Private Incredulo As StringHay veces, sobre todo si ya has programado antes en MS-DOS, que usamos variables como a, b, c, i, j, k...etc., (al menos yo estoy acostumbrado a llamar i, j, k a las variables que uso en los bucles FOR), cuando hago un bucle dentro de un procedimiento uso i como variable índice, (o variable 'contadora'), pero ¿que ocurre si esta 'costumbre' quiero aplicarla en varios procedimientos? Pues que dimensiono una variable i en cada uno de ellos y aquí no ha pasado nada!!!
Usar el mismo nombre de variable en distintos procedimientos
Como indica el encabezado de este nuevo párrafo, cosa que ya he comentado antes, podemos tener distintas variables con el mismo nombre en distintos procedimientos; para ello, sólo hay que dimensionarlas y el VB las almacenará en distintas posiciones de la memoria para que no se 'mezclen'.En la entrega anterior, teníamos un procedimiento llamado Contar que se usaba para eso, contar...
En este ejemplo vamos a usar un sub también llamado contar, para ver en acción todo esto que estoy diciendo.
Sitúate en la parte General/Declarations de la ventana de código y escribe o "copia/pega" lo siguiente:Private Sub Contar() Dim i As Integer For i = 1 To 2 Print "i en contar= "; i Next End SubAhora en el Form_Load, escribe esto otro:
Dim i As Integer Show For i = 1 To 3 Print "i en el Form_Load= "; i Contar NextEjecuta el programa y ...
Has visto lo que ocurre... A pesar de que ambas variables tienen el mismo nombre, son diferentes. La variable i del Form_Load no es la misma que la variable i de Contar.
Cuando usamos variables locales es como si cambiásemos el nombre y se llamaran NombreProcedimiento_Variable.
Sé que puede ser una forma 'rebuscada' de explicarlo, pero así te haces una idea.Todas las variables declaradas en un procedimiento, sólo son visibles en ese procedimiento. Si has usado QuickBasic o el compilador Basic de Microsoft (que usaba el QuickBasic Extended QBX), esto ya existía y también existía la forma de hacer que una variable declarada en un procedimiento, fuese visible fuera de él; para ello declarabas la variable como Shared (compartida); pero en VB eso NO EXISTE. La única forma de compartir una variable es declarándola en la sección General de las declaraciones.
Prueba ahora esto. Sustituye el procedimiento Contar por este otro:
Private Sub Contar(j As Integer) Dim i As Integer For i = 1 To j Print "i en contar= "; i Next End SubAquí hacemos que Contar reciba un parámetro y el valor que recibe lo usamos como el límite final del bucle For, es decir contará desde UNO hasta el valor de j.
Sustituye la llamada a Contar del Form_Load por esta:
Contar i
Le damos a Contar el parámetro i. Por tanto cada vez que se llame a este procedimiento le estamos diciendo que i es el valor máximo que tomará el bucle For que tiene dentro.
¿Cómo reaccionará? ¿Se confundirá? ...No, no voy a dejarlo para la siguiente entrega, es tan obvio que lo voy a explicar ahora mismo:
Al procedimiento Contar le da igual que se use una variable llamada i o cualquier otra, incluso un número. Lo único que necesita y espera, es recibir un valor numérico (del tipo Integer) y lo asignará a la variable j. Por tanto no ocurrirá nada extraño. Ejecuta el programa y fíjate en lo que ocurre. Sé que lo has deducido, eso está bien... vas aprendiendo... 8-)
¿Cómo? ¿Que tú aún no lo has captado? Pues dímelo... (mejor no me lo digas y repásalo de nuevo...)Otra cosa sería pretender usar una variable declarada a nivel de módulo, dentro del procedimiento, que tuviese el mismo nombre.
Si te has quedado 'con la copla', tu mismo sabrás la respuesta... ¡Efectivamente! Si dentro de un procedimiento tenemos una variable dimensionada con el mismo nombre de una declarada a nivel de módulo o a nivel global, (para usarla en cualquier sitio de la aplicación), tendrá 'preferencia' la variable local... Ésta 'tapará', ocultará o como prefieras decirlo a cualquier otra variable del mismo nombre...En la próxima entrega veremos más casos y cosas de las variables. Comprobaremos cómo usarlas a nivel Global. Pero por ahora vamos a terminar con el programa que teníamos planteado en la entrega anterior:
Aunque realmente deberías saber cómo solucionarlo...
Lo que seguramente no sabrás, es cómo hacer que estas variables tomen el valor...De acuerdo, lo explicaré. Carga el ejemplo de la cuarta entrega.
Hay varias soluciones a este problema; una sería usar variables locales, esta decisión no 'justifica' el 'pedazo' de pista que te di... pero esto es lo que hay.La línea Const minBucle = 1, maxBucle = 10. Debe quedar así:
Const minBucle = 1 Dim maxBucle As IntegerEsto hará que maxBucle deje de ser una constante y pase a ser una variable, con lo cual podremos asignarle cualquier valor.
Pero, ¿cómo le asignamos el valor?
Vamos a dar por sentado que lo que se escriba en el Text1 será el valor que debe tener maxBucle; entonces lo único que haremos es asignar a maxBucle lo que se escriba en el Text1...
Vale, pero ¿dónde?
Pues... por ejemplo, después de la declaración, así que en la siguiente línea al Dim maxBucle... escribe los siguiente:
maxBucle = Text1
Esto en teoría no daría problemas, al menos en condiciones normales, ya que el contenido de un textbox es del tipo Variant y ya vimos que un Variant puede almacenar cualquier cosa, por tanto si es un número 'intentará' convertir al tipo de la variable que recibirá el valor.
Esto no siempre funciona, sobre todo si el contenido del Text1 no es numérico. Por ahora vamos a hacerlo simple, si el usuario (en este caso tú), escribe algo no numérico lo vamos a considerar CERO... o casi...
Cambia la asignación anterior por esta otra...
¡¡¡ ALTO !!! Antes de hacerlo, pruébalo e intenta escribir una palabra en lugar de un número... ¿que ocurre?
Pues que VB no se complica la vida y te dice que 'nones'... (realmente dice Type Mismatch... Error de Tipos, es decir que lo que has escrito no es capaz de convertirlo a Integer)... así que escribe esto otro:
maxBucle = Val(Text1)
Con esto lo que hacemos es convertir el contenido del Text1 a un VALor numérico y lo asignamos en la variable...
¿Problemas? Que el valor sea mayor del que se puede guardar en un Integer, pero eso ya no es asunto de esta entrega...Ahora nos queda convertir elMaximo en variable y asignarle el valor que hay en el Text2. ¡Efectivamente! hacemos lo mismo, sólo que esta vez dentro del procedimiento Contar, por tanto la declaración Const elMaximo = 1000&, la tienes que quitar y poner estas dos líneas:
Dim elMaximo As Integer
elMaximo = Val(Text2)
Aquí el único inconveniente es que esta asignación se hace cada vez que se entra en este procedimiento... y eso, amigo mío, no es un buen estilo de programación... Sobrecargamos de forma innecesaria al procesador... ten en cuenta que la conversión a número y la asignación ¡¡¡se ejecuta cada vez que se entra en Contar!!!
Lo mejor para este caso sería declarar elMaximo como variable a nivel de módulo. Por tanto, borra el Dim elMaximo... del sub Contar y colócalo en la parte de las declaraciones generales del form.
Ahora... ¿dónde asignamos el valor para evitar la sobre-carga? Ya que tenemos la variable a nivel de módulo, ésta será 'vista' en todos los procedimientos del formulario, por tanto lo lógico sería hacerlo en el Command1_Click, ya que cuando nos interesa a nosotros saber cuanto tenemos que contar es precisamente cuando pulsamos en el botón...
Pero... ¿dónde exactamente?, después de Contando = 1Bien, ahora está la cosa mejor... haz tus pruebas y si aún no lo tienes claro... pregúntame, (te digo lo de antes: mejor no me preguntes y repásalo todo de nuevo...)
Prácticas y ejercicios
¿Quieres algo para practicar?
Este ejercicio se lo ponía a mis alumnos, cuando daba clases de BASIC, hace ya unos 10 años o más... y consistía en pedir el nombre, pedir la edad y mostrar el nombre tantas veces como años tengamos...
Claro que con el BASIC del MS-DOS era más directo y se sabia cuando se debía empezar a mostrar el nombre, para solventar esto, se mostrará el nombre 'edad' veces cuando se pulse en un botón. El aspecto del form sería algo así:No creo que sea complicado, así que vamos a complicarlo un poco más:
Mostrar el nombre 'edad' veces, dentro de un label, para ello el label deberá ocupar la parte izquierda del form.
Y una tercera versión, sería lo mismo que esta última, pero cada vez que se muestre el nombre se haga en una línea diferente.La pista: En la segunda entrega vimos de pasada el CHR. Pues decirte que si añadimos a una variable un CHR(13), lo que hacemos es añadirle un retorno de carro, es decir lo que venga después se mostrará en la siguiente línea... siempre que se 'concatene'. También existe una constante definida en VB4 o superior que es vbCrLf, esto es un retorno de carro (Cr) y un cambio de línea (Lf)
¡Que te diviertas!
Como ya es costumbre al final de cada entrega, espero tus comentarios y opiniones sobre el curso, además de aceptar críticas, que seguro que algo habrá que no entiendes o que no te gusta como está explicado.
Con esto acaba el tema por ahora... no, no se acaba el curso, no te alarmes; la próxima entrega será... pues, es que... yo creo que... si no... un día de estos, pero seguro que en el caluroso mes de Agosto.
Nos vemos.