Vous êtes sur la page 1sur 608

Curso Bsico de Programacin en Visual Basic

Apndice A

Instalar y configurar el Visual Basic 5.0 Control Creation Edition


Este es el contenido de este Apndice A del Curso Bsico de Programacin con Visual Basic Introduccin Cmo conseguir el VB5 CCE? Instalar el VB5CCE Iniciar el VB5CCE Configurar el IDE de VB5CCE Configurar las Opciones Generales Configurar la barra de herramientas

Introduccin
En vista de que algunos de los que leis este Curso Bsico de Programacin en Visual Basic, (clsico, es decir para versiones anteriores a la versin de .NET), no tenis el entorno de desarrollo de VB por aquello de que trabajis tambin con el Visual Basic incluido en el Office o porque ya no podis conseguir "legalmente" el Visual Basic, ya que no se sigue vendiendo, he decidido crear un apndice en el cual te explico cmo instalar, configurar y, sobre todo, usar el entorno de desarrollo integrado de Visual Basic 5.0 CCE (en adelante IDE, Integrated Development Environment). Si bien lo aqu tratado es sobre el IDE de VB5 CCE, en la mayora de los casos ser igualmente til y vlido para los IDEs de VB5 y VB6, en el caso de VB4 la cosa vara, pero me imagino que a estas alturas sern pocos los que tengan ese entorno de desarrollo, ni decir tiene que el VB3 o anteriores son cosas de la historia.

Si no lo sabes, te lo explico o aclaro ahora, el VB5 CCE no sirve para crear ejecutables, slo sirve para crear controles ActiveX (OCX), pero esto no es impedimento para usarlo, ya que, aunque no se puedan generar los ejecutables (EXE), si que se pueden probar todos los proyectos, bueno, casi todos, ya que no se podrn usar las aplicaciones que tengan acceso a bases de datos de DAO, sin embargo, puedes crear proyectos de acceso a datos con ADO sin el data control (por ejemplo el mostrado en la entrega 41). Nota: Si vas a usar proyectos creados con VB6, por ejemplo los zips de cdigo de algunas de las entregas de este curso, seguramente tendrs que quitar algunos de los valores incluidos en el fichero de proyecto (vbp), por regla general, puedes borrar los que hay a partir de VersionProductName. Si no quitas esos valores del vbp, el VB5CCE te dar error y no cargar el proyecto.

Cmo conseguir el VB5 CCE?


El Visual Basic 5.0 Control Creation Edition (VB5CCE en adelante) se puede conseguir de esta direccin: http://msdn.microsoft.com/vbasic/downloads/tools/cce/default.aspx

Nota del 19/Oct/2006: Segn parece el VB5CCE ya no est, es decir, est desaparecido totalmente. As que, te recomiendo que te vayas olvidando del VB "clsico" y te pases al .NET. En .NET tienes el Visual Basic 2005 Express Edition que es totalmente gratuito y operativo al 100%, adems de que es lo que debes aprender, porque el Visual Basic 6.0 (y anteriores) ya no lo soporta Microsoft e incluso ni est en venta, as que... te recomiendo que mires el curso que tengo de Visual Basic .NET que es lo que ahora debes ir aprendiendo y tambin te recomiendo que empieces a ver la seccin de WinFX porque ser lo que en un futuro prximo se empezar a usar. Este es el link del curso de Visual Basic .NET (vlido para todas las versiones de Visual Basic para la plataforma .NET) Por supuesto, lo que aprendas con este curso bsico de Visual Basic 6 y anteriores te ser de utilidad para el .NET, ya que las instrucciones siguen siendo las mismas, al menos las que estn relacionadas con el lenguaje en s, los For, If, etc.

Nota del 15/Jun/06: Como es costumbre en Microsoft, algunas cosas desaparecen (como esa pgina) y otras cambian de sitio. Por suerte, esta vez aunque la pgina del VB5CCE ha desaparecido, al menos queda el instalador y la ayuda, que gracias a ngel Rosario de Repblica Dominicana, ahora puedes bajar. Estos son los links a la fecha de esta nota: El programa de instalacin: http://download.microsoft.com/msdownload/sbn/vbcce/vb5c cein.exe Y esta es la ayuda: http://download.microsoft.com/download/5/2/5/5253a7ea1310-40d8-b762-625c2019310e/ccehelp.exe

IMPORTANTE: Ver la nota del 19 de Octubre.

Es posible que esa direccin no est disponible, bien porque la hayan cambiado de sitio (cosa habitual en el sitio de Microsoft) o bien porque lo hayan quitado definitivamente... cosa que tambin puede ocurrir. Una vez que ests en la pgina de descarga del VB5CCE, tendrs varios links, uno de ellos es el que se baja el programa instalador, pero tambin habr otro link para la ayuda y documentacin. El programa de instalacin del VB5CCE se llama: vb5ccein.exe 7.13 MB El programa de instalacin de la ayuda se llama: ccehelp.exe 2.77 MB Tambin hay varios ficheros con documentacin, pero aqu no lo vamos a tratar, aunque te recomiendo que tambin te los descargues, ya que, aunque estn en ingls, de algo te servirn... que despus te puedes quejar de que no trato todos los temas...

Instalar el VB5 CCE


Empecemos instalando el VB5CCE, para ello simplemente tendrs que ejecutar el vb5ccein.exe y seguir los pasos que se te indiquen (que sern los habituales en cualquier instalacin, as que no voy a entrar en detalles) .Una vez instalado el VB5CCE ser conveniente que instales la ayuda, ya que sta se instalar en el mismo directorio en que est el VB5CCE, por tanto te recomiendo que instales tanto el VB5 como la ayuda en el directorio que te indique, si no lo haces, al instalar la ayuda tendrs que indicar el directorio (o al menos comprobar que el que te muestra es el correcto). Al instalar el VB5CCE se crearn los accesos directos en el men de inicio, pero al instalar la ayuda no se crea ningn acceso a dicha ayuda, pero estar instalada correctamente en

el directorio VB5CCE de Archivos de Programas (Program Files en sistemas Windows en ingls, como el que yo uso). Si quieres tener un acceso directo a la ayuda, busca el fichero vb5.hlp en el directorio de instalacin.

Iniciar el VB5CCE
Una vez que tienes instalado el VB5CCE es hora de usarlo, as que, busca el acceso directo a Visual Basic 5.0 CCE (vb5cce.exe) y dejemos que empiece el espectculo... Nada ms empezar, te mostrar un cuadro de dilogo como el mostrado en la figura 1:

Figura 1, El cuadro de dilogo de inicio de VB5CCE

Este cuadro de dilogo te permite indicar el tipo de proyecto que quieres crear, si marcas la opcin Don't show this dialog in the future, este cuado no se mostrar al iniciar el VB5CCE. Los tres tipos de proyectos que permite crear son: ActiveX Control (Control ActiveX), un control para poder usarlo en tus aplicaciones e incluso en una pgina WEB. Standard EXE (EXE estndar), un programa normal y corriente. CtlGroup (Grupo de proyectos), por defecto crea un control ActiveX y un EXE estndar.

Empecemos seleccionando un ejecutable normal (Standard EXE), pero recuerda que aunque podamos seleccionarlo, no te permitir crear el ejecutable (compilar en formato EXE). El aspecto del IDE del VB5CCE ser parecido al mostrado en la figura 2.

Figura 2, El IDE del VB5 CCE despus de haberlo instalado y casi sin configurar

Ahora vamos a configurar un poco el IDE. Lo que aqu te voy a contar es como yo suelo configurarlo, pero si a ti no te gusta, eres libre de dejarlo como quieras. Nota: Si instalas el VB5CCE y ya tienes instalado el VB5 normal (en cualquier versin), la configuracin que ya tuvieras en el VB5 se mostrar en el IDE de VB5CCE. Esto quiere decir que no hay problemas de que instales el VB5CCE si ya tienes otra versin de VB. En la figura 2 tenemos varias ventanas acopladas. Arriba est la barra de herramientas con los mens y otros "comandos" (los botones debajo de los mens). A la izquierda tenemos la barra de herramientas con los controles que podemos aadir a los formularios. A la derecha, en la parte superior tenemos el explorador de proyectos. Debajo de este tenemos la ventana de propiedades. Debajo (aunque no se muestra en esta figura), suele aparecer la ventana con la posicin

del formulario, pero yo suelo cerrarla. Abajo est la ventana Inmediate, (permite ejecutar comandos directos), sta se puede mostrar seleccionndola del men View (por defecto no se muestra), en ese mismo men puedes mostrar algunas de las ocultas, por ejemplo la de Form Layout Windows que es la que te permite posiciona o ver dnde se posicionar el formulario. Por ltimo, en el centro tendremos las ventanas de diseo de formularios/controles, as como las ventanas de cdigo. stas se pueden posicionar como queramos y esa posicin ser recordada cada vez que abramos el proyecto. La informacin sobre el tamao y posicin se guarda en un fichero con la extensin vbw.

Configurar el IDE de VB5CCE Empecemos configurando las opciones generales.


Selecciona el men Tools>Options... y se mostrar un cuadro de dilogo, del cual veremos varias de las fichas en las siguientes figuras, despus de cada figura te indicar para que sirven esas opciones (o al menos algunas de ellas).

Figura 3, Opciones, ficha Editor En la ficha Editor, lo primero que debes seleccionar es Require Variable Declaration, (la he sealado para que no tengas excusas de saber dnde est esa opcin), esto har que en los nuevos proyectos creados se aada a cada mdulo (formulario, mdulo BAS, clase, etc.) la instruccin Option Explicit, ya sabrs que si quieres llevarte bien conmigo, tendrs que usar siempre esa instruccin, de esta forma te evitars muchos problemas, adems de que as siempre tendrs que declarar todas las variables que vayas a usar en tus proyectos.

La ficha Editor Format, la dejamos como est.

Figura 4, Opciones, ficha General

En la ficha General te recomiendo que asignes 60 tanto a Width como Height de Grid Units, esto te permitir posicionar mejor los controles en el formulario, ya que as estarn los puntos ms juntos (el valor por defecto es 120). Tambin puedes quitar la marca de Compile On Demand ya que esto har que siempre se compile todo el cdigo y te asegures de que lo probado es lo que tienes escrito... realmente no hace falta, pero yo siempre lo tengo as...

En la ficha Docking no hace falta hacer cambios.

Figura 5, Opciones, ficha Environment

De la ficha Environment te recomiendo que selecciones Save Changes del frame (marco) When a programa starts:, de esta forma se guardarn los cambios realizados en el cdigo... nunca se sabe si se quedar colgado el IDE o no!

Figura 6, Opciones, ficha Advanced

En la ficha Advanced no vamos a hacer cambios, pero te la muestro para que sepas que en esta ficha puedes indicar si quieres que se te avise cuando se hagan cambios en un fichero que est compartido por varios proyectos o si prefieres usar el estilo SDI en lugar del MDI, es decir que cada ventana sea independiente en lugar de estar todas "dentro" del entorno de desarrollo (IDE). En la figura 7 tienes una captura de cmo quedara el IDE de VB5CCE en modo SDI.

Figura 7, El IDE de VB5CCE en modo SDI

Configurar la barra de herramientas


Ahora vamos a aadir algunas opciones a la barra de herramientas superior. Entre las cosas que aadiremos son: Comentar/quitar comentarios de un bloque de cdigo Asignar/quitar/ir a marcadores dentro del cdigo Bloquear los controles de un formulario Crear un nuevo procedimiento. Para poder configurar la barra de herramientas, as como los mens del IDE de VB5CCE, tendremos que seleccionar la opcin View>Toolbars>Customize..., se mostrar el cuadro de dilogo mostrado en la figura 8:

Figura 8, Cuadro de dilogo Customize, ficha Commands

De la lista de la izquierda (Categories) selecciona Edit, en el cuadro de la derecha se mostrarn las opciones relacionadas con la edicin. Selecciona Comment Block (esta opcin te permite comentar el bloque de cdigo seleccionado) y arrstralo con el ratn hasta la barra de herramientas, (vers que el cursor del ratn cambia para que sepas dnde vas a soltar el elemento seleccionado), sultalo en la posicin que quieras y haz lo mismo con las opciones Uncomment Block, (quitar el comentario al bloque de texto seleccionado), Toggle Bookmark (una banderita azul), (pone/quita una marca), Next Bookmark, (ir a la siguiente marca), Previous Bookmark, (ir a la marca anterior), Clear All Bookmarks (quitar todas las marcas). Con las marcas (bookmarks) puedes marcar una parte del cdigo, (se indicar con una banderita en la barra derecha del cdigo, tal como puedes ver en la figura 9), y despus navegar entre las distintas marcas que tengas en el cdigo del proyecto. El problema es que cuando cierras el proyecto esas marcas desaparecen... cosa que en el IDE de VB .NET han mejorado o al menos permanecen.

10

Figura 9, Cmo se indican los bookmarks en la ventana de cdigo

Figura 10, Configurando la barra de herramientas En la figura 10 puedes ver esto de colocar las opciones en la barra de herramientas. Si quieres poner una lnea de separacin entre las distintas opciones (o comandos), simplemente arrastra hacia la derecha el icono (o botn) que est a la derecha de donde quieras dejar esa separacin. Por ejemplo, si quieres dejar una separacin entre el

11

comando de quitar comentarios y el de asignar marcador, tendrs que arrastrar la banderita azul un poco hacia la derecha. Por otro lado, si quieres quitar alguno de los que ya hay (o hayas aadido), simplemente arrstralos desde la barra hasta el cuadro de dilogo. Ahora aade el resto de comandos. De las categora Format selecciona Lock Controls y de Tools, selecciona Add Procedure..., una vez hecho todo esto, el look de la barra de herramientas ser el que te muestro en la figura 11.

Figura 11, El aspecto de la barra de herramientas Fjate que algunas opciones slo estarn disponibles (habilitadas) cuando se puedan usar.

Bueno, dejemos aqu esta primera parte del Apndice A, en otra ocasin veremos para que sirven algunas de las opciones de los mens y de los botones de las barras de herramientas, as como algunas otras configuraciones y, sobre todo, cmo agregar controles a un formulario, cmo aadir nuevos elementos al proyecto, cmo aadir nuevos proyectos (para poder crear multi-proyectos), etc., pero eso ser en otra ocasin. Nos vemos. Guillermo

Curso Bsico de Programacin en Visual Basic


Apndice A.2 Aadir controles a un formulario
Este es el contenido de esta segunda parte del Apndice A del Curso Bsico de Programacin con Visual Basic Introduccin Empezar con el IDE de VB Aadir controles al formulario Cambiar el tamao y posicin de los controles Aadir ms controles y usar el diseador para ajustar el tamao y la posicin

12

Introduccin
En esta segunda parte del Apndice A del Curso Bsico, vamos a ver cmo aadir controles a nuestros formularios y cmo configurar esos controles, cambiar los valores de las propiedades, etc. Esto es algo que hace aos tendra que haber hecho, lo s, pero yo es que antes era un poco ms inocente que ahora, y pensaba que los lectores de este curso se iban a tomar un poco de ms inters en aprender estas cosas usando la informacin (o documentacin) que acompaaba al Visual Basic... y creo que hace algn tiempo as era... antes la gente no se me quejaba tanto como ahora... seguramente porque los lectores eran menos numerosos... y, posiblemente, (Guille, creo que deberas decir "presuntamente", para que nadie se sienta "acusado"), porque ahora la gente tiene ms oportunidad de "conseguir" gratuitamente el Visual Basic, (es que hay "viejos VisualBasikeros" que son muy caritativos, no te creas otra cosa), y es posible que no se acompae "ningn" tipo de documentacin... Espero que nadie se de por aludido con el comentario anterior, y si es as, echando mano del refranero: el que se rasca, es porque algo le pica... (que malo soy!) La cuestin es que, sea como sea, aqu estn estos apndices, los cuales intentarn "ayudarte" en la medida de lo posible a que aprendas "mejor" este querido, (al menos por mi), lenguaje de programacin. En este caso seguiremos usando el Visual Basic 5.0 Control Creation Edition (VB5CCE para los amigos), ya que, al ser un entorno (IDE) gratuito, todos tendris la oportunidad de poder "jugar" con l. En la primera parte de este Apndice A te expliqu cmo conseguirlo, cmo instalarlo y como configurar algunas de las opciones y barras de herramientas, as que... si an no te lo has ledo, deberas hacerlo antes de seguir. Te recuerdo o aclaro que todo lo que voy a explicar aqu, estar basado en el Visual Basic 5.0 Control Creation Edition, pero tambin ser vlido para el Visual Basic 5.0 e incluso para el Visual Basic 6.0, pero en algunos casos no ser "tan evidente" para la versin 4.0, aunque en estas fechas, esa versin ya es ms difcil de encontrar, as que... si tienes el VB4, intenta amoldarte a las explicaciones. Y si no tienes ninguna de esas versiones, ya que trabajas con el Visual Basic de Office, pues... bjate el VB5CCE y prueba con lo que aqu voy a explicar o bien, intenta amoldarte a como el Office te presente el diseador de formularios... La cuestin es que si lo aqu dicho no se adapta al 100% a lo que tu tienes... que te lo curres! Gracias.

Empezar con el IDE de VB: Configurar un formulario.


En cuanto iniciamos el Visual Basic, se nos presenta un cuadro de dilogo para que seleccionemos el tipo de proyecto que queremos crear, (para ms detalles, ver Iniciar el VB5CCE), para este ejemplo, elegiremos un proyecto normal (Standard EXE). Nota: Si no se muestra el cuadro de dilogo de nuevo proyecto, puede ser que hayas marcado la casilla "Don't show this dialog in the future", lo cual viene a decir que no muestre ese cuadro de dilogo nunca ms; si ese es tu caso, tendrs que crear un nuevo proyecto seleccionando New Project (Nuevo Proyecto) del men File (Fichero) o bien pulsando la combinacin de teclas Ctrl+N.

13

Al crear un nuevo proyecto de tipo EXE, tendremos un formulario llamado Form1. Por ahora vamos a dejar ese nombre para que sea ms fcil de comprender todo lo que voy a explicar. Vamos a configurar el formulario para que sepas "adaptarlo" a tu gusto. Para poder configurar el formulario, tendremos que tenerlo a la vista. Un formulario se compone de dos partes: Lo que mostrar y el cdigo. Para poder aadir controles al formulario o para cambiar la apariencia del mismo, tendremos que tenerlo a la vista, es decir, tendremos que mostrar el "diseador de formularios". Cuando queramos usar el diseador de formularios, tendremos que seleccionar el formulario de la ventana de proyectos y pulsar la combinacin de teclas Maysculas+F7 (Shift+F7) o bien pulsar en el icono con forma de formulario que hay en la ventana de proyectos, (ver figura 1).

Figura 1 Para mostrar la ventana del cdigo, podemos pulsar F7 o bien en el icono que est a la izquierda del que muestra el formulario, (ver figura 1) Una vez que tenemos el formulario en modo de diseo, podemos pulsar F4 para mostrar la ventana de propiedades. En esta ventana (ver figura 2) se mostrarn las propiedades que podemos cambiar mientras estamos "diseando" el formulario, es decir, no se mostrarn todas las propiedades, ya que algunas de ellas puede que no sean "configurables" en tiempo de diseo, es decir, que no afecten al aspecto visual. Tambin hay que tener en cuenta de que, (como comprobaremos en un momento), en la ventana de propiedades se mostrarn las propiedades del "objeto" que est seleccionado. En este caso, se mostrar sin problemas las propiedades del formulario por la sencilla razn de que no tenemos ningn otro "objeto" dentro del formulario (ni en la aplicacin).

14

Figura 2, Ventana de propiedades Como podemos comprobar, la ventana de propiedades est dividida en tres partes: -La parte superior nos indica el "objeto" que tenemos seleccionado, en este caso es el formulario. Podemos usar esa lista desplegable para seleccionar el objeto que queremos configurar. Ah se nos indica el nombre del objeto, el cual estar en negrita, y el tipo de objeto se mostrar en letra normal. -La parte del centro nos muestra las propiedades, en esa ventana tenemos dos fichas (o solapas), para mostrar la informacin de forma alfabtica (Alphabetic) o bien por categoras (Categorized). En la columna de la izquierda est el nombre de la propiedad y en la de la derecha est el valor. -En la parte inferior tenemos la descripcin de la propiedad que tenemos actualmente seleccionada. En el caso de la figura 2, la propiedad seleccionada es Caption y abajo nos dice (en ingls) que esa propiedad sirve para mostrar el texto en la barra de ttulo. Si quisiramos que en la barra de ttulos del formulario se mostrara otra cosa distinta a lo que actualmente se muestra, simplemente tendramos que escribir en la casilla que est a la derecha de la propiedad el texto que queremos mostrar. Las propiedades ms importantes (o las ms usadas) del formulario las iremos viendo con algo de detalle en este o en otros "apndices". Lo importante es que sepas cmo poder cambiar los valores y, si te atreves, que vayas probando por tu cuenta y riesgo... y como resulta que ya sabes cmo cambiar (y dnde) cambiar esos valores, ahora no tienes excusa. Para muestra un botn: La propiedad AutoRedraw nos puede servir cuando queremos mostrar las pruebas hechas en las primeras entregas del curso, en las que tenamos que incluir "Show" al principio del evento Form_Load para que se pudiera ver lo que se "escribe" en el formulario. Si queremos darle un valor verdadero (True) a esa propiedad, simplemente seleccionaremos ese valor de la casilla que est a la derecha de la propiedad y asunto arreglado.

15

Tambin decirte que en las versiones ms recientes de Visual Basic hay ms propiedades que en las versiones anteriores, por ejemplo, en la versin 5 tenemos algunas propiedades que no estaban disponibles en la versin 4. Tal es el caso de la propiedad que le indica a Windows dnde debe situar el formulario cuando se muestre. Si buscamos la propiedad StartUpPosition, tenemos unos valores a elegir, por defecto tendr un valor de 3 - Windows Default, lo cual quiere decir que ser el Windows el que posicione el formulario (o la ventana), pero si elegimos el valor 2 CenterScreen, el formulario se posicionar en el centro de la pantalla. Si seleccionamos el valor 0 - Manual, el formulario se posicionar en la posicin que manualmente le indiquemos en las propiedades Left y Top. Por ltimo, si elegimos 1 - CeterOwner se centrar en el formulario que es "el propietario", esto lo veremos cuando se trate el manejo de ms de un formulario. Pero dejemos las propiedades del formulario, (en otra ocasin intentaremos hacer un repaso de algunas de ellas y el efecto que conseguiremos al aplicar distintos valores), y sigamos viendo otras cosas.

Aadir controles al formulario.


Esta es otra de las cosas que con ms asiduidad haremos, ya que no es habitual que un formulario est vaco, sino que tenga controles. Los controles ms usados son las etiquetas (Label), las cajas de texto (TextBox) y los botones (CommandButton), seguidos de las listas (ListBox), listas desplegables (ComboBox) (que algunos traducen tambin por cajas combinadas), los CheckBox y OptionBox (o RadioButton) y por ltimo los controles que suelen hacer de contenedores de otros controles: los marcos (Frame) y los cuadros de imgenes (PictureBox). Empecemos aadiendo una etiqueta, una caja de textos y un botn. Para aadir cualquier control al formulario, usaremos la barra de herramientas que est en la parte izquierda del IDE de Visual Basic (ver figura 3).

16

Figura 3 Si posicionas el ratn encima de los iconos, te mostrar un ToolTip (una cajita emergente) con el nombre del control. La etiqueta es la A que hay en la segunda fila de controles, la caja de textos es el que est a la derecha, tambin en la segunda fila y el botn es el que est a la derecha en la tercera fila. Para aadir un control, podemos hacer doble pulsacin (doble-click) sobre el elemento de la barra de herramientas y se aadir al formulario, posicionndose en el centro del mismo y teniendo el tamao predeterminado. Tambin podemos pulsar una vez, y a continuacin "dibujarlo" en el formulario, dndole el tamao que queramos y posicionndolo tambin en el sitio que queramos. Por ahora vamos a elegir el primer mtodo y despus cambiaremos el tamao y posicin. Haz doble-click en la etiqueta, esto aadir una etiqueta al formulario, el nombre de ese objeto se llamar Label1, para cambiar la posicin de la etiqueta Label1, podemos seleccionarla en el formulario y moverla con el ratn. Vamos a situarla en la parte superior izquierda, para que deje el centro del formulario libre, ya que el resto de los controles se posicionarn en el mismo sitio y si dejamos la etiqueta ah y aadimos nuevos controles, los nuevos controles ocultarn la etiqueta. A continuacin aadiremos una caja de textos, por tanto haz doble-click en el icono correspondiente y una vez que est incluido en el formulario, lo posicionaremos en la parte superior, a la izquierda de la etiqueta. El nombre que tendr esa caja de textos ser Text1. Por ltimo aadiremos un botn, as que pulsa en el icono correspondiente y djalo donde lo ha posicionado el diseador de formularios, el nombre que tendr ser Command1. El aspecto del formulario con estos controles ser el mostrado en la figura 4.

17

Figura 4 Fjate en los nombres de los controles, que son los mismos que los "textos" mostrados. El diseador de formularios de Visual Basic siempre va nombrando los controles usando el mismo "algoritmo", (dicho as parece hasta algo importante, je, je), osea: usando un nombre "reducido" del tipo de control seguido de un nmero, el cual se va incrementando cada vez que aadimos un nuevo control del mismo tipo. Si aadimos una nueva etiqueta (control Label), qu nombre tendra? (la respuesta dentro de un ratillo).

Cambiar el tamao y posicin de los controles.


Por regla general, no se suelen dejar los controles con el tamao que el diseador le ha dado al insertarlos en el formulario, por tanto, ser algo habitual el que tengamos que cambiar el tamao de los controles. Ese cambio del tamao de los controles podemos hacerlo de dos formas distintas: 1. Usando el diseador de formularios y el ratn, para ello simplemente tendremos que seleccionar el control que queramos redimensionar, mantener el ratn pulsado en el borde que nos interese usar para cambiar el tamao y ajustarlo a nuestro gusto. Por ejemplo, para cambiar la altura del botn, pulsaremos en ese control, y desde el borde inferior iremos cambiando el tamao, por ejemplo, para que tenga una altura de 375, ver la figura 5. Si queremos cambiar, en una sola accin, tanto el ancho como el alto, podemos hacerlo desde una de las esquinas del control, ver la figura 6. Mientras se est cambiando el tamao del control, se mostrar un "ToolTip" con el nuevo tamao.

18

2. La segunda forma de cambiar el tamao es usando la ventana de propiedades y usar las propiedades Height y Width, las cuales cambiarn respectivamente la altura y anchura del control.

Figura 5

Figura 6

Vamos a cambiar el tamao de los controles para que tengan los que aqu te indico: La etiqueta (Label1), tendr un tamao de 1245 x 255 (ancho x alto) La caja de textos (Text1), tendr un tamao de 2895 x 315 El botn (Command1), tendr el tamao 1245 x 375 Para cambiar la posicin de los controles, como puedes imaginarte, tambin podemos hacerlo de dos formas, arrastrando el control hasta la nueva posicin o bien asignando valores a las propiedades Left y Top, las cuales representarn los valores izquierdo y superior respectivamente. Posiciona la etiqueta en la posicin 180, 150 (izquierda, arriba), la caja de textos en la posicin 1560, 120 y el botn djalo en el mismo sitio que estaba: 1740, 1350. Nota: Como te coment en la primera parte del Apndice A, podemos cambiar el espaciado de puntos del grid (o rejilla) del diseador de formularios, en aquella ocasin te dije que usaras 60 x 60, pero yo suelo usar 30 x 30, por tanto para que los controles se "puedan mover" en tiempo de diseo a las posiciones que te indico, es posible que tengas que cambiar esos valores, (ver la figura 4 de la primera parte del Apndice A). Todos estos cambios son en tiempo de diseo, es decir, mientras estamos "diseando" el formulario, ya que esos ajustes de tamao tambin podemos hacerlo en tiempo de ejecucin, es decir, cuando pulsemos F5 para ejecutar la aplicacin. El sitio habitual para hacer esos cambios en tiempo de ejecucin es en el evento Load del formulario o bien en el evento Resize del mismo. Aunque, la verdad sea dicha, no es comn cambiar el tamao de los controles, salvo en contadas ocasiones, por ejemplo, cuando queremos adaptarlos al nuevo tamao del formulario, etc., pero ese tema no lo vamos a ver, al menos ahora, ya que, aunque sea de paso, se toc en la entrega veintinueve. Las propiedades Left, Top, Height y Width tambin las podemos usar en tiempo de ejecucin para asignar un nuevo valor en cualquiera de ellas, Tambin quiero "recordarte" que existe un mtodo en casi todos los controles (incluso en los formularios) que sirve para cambiar la posicin y/o el tamao de los controles de una vez (es decir con una sola instruccin); ese mtodo es: Move y se usa de la siguiente forma: <control>.Move Izquierda, Arriba, Ancho, Alto El nico valor requerido es el primero (posicin izquierda), pero si se especifica cualquiera de los otros tres, los anteriores tambin se deben especificar; aunque los siguientes no tenemos la obligacin de indicarlos, adems de que seguirn conservando los valores que ya tuvieran. Por ejemplo, si slo queremos cambiar la posicin del control, podemos indicar los dos primeros parmetros de ese procedimiento. Pero si adems de la posicin queremos cambiar el alto, estamos obligados a indicar tambin el alto... ya que no

19

podemos dejar ese "hueco". Como consejo, decirte que es ms "rpido" usar el mtodo Move que asignar individualmente las propiedades. Otra cosa, la posicin siempre se considera relativa a la posicin superior izquierda del formulario, la cual estar indicada por 0,0. El formulario a su vez estar posicionado relativamente a la pantalla (Screen), considerndose igualmente que la posicin superior izquierda es la posicin 0,0.

Aadir ms controles y usar el diseador para ajustar el tamao y la posicin.


Una vez que tenemos estos controles, pueden ocurrir dos cosas, que lo dejemos como est, porque no necesitemos ms controles, o bien que queramos aadir ms controles, y puede que hasta ocurra que lo que necesitemos sea aadir ms etiquetas y cajas de textos, los cuales queremos que tengan el mismo tamao que los que ya tenemos. Esto es algo habitual y muy comn, por ejemplo si queremos hacer un formulario que pida datos al usuario. Para ver cmo solucionar de una forma fcil y rpida esta situacin, vamos a aadir dos nuevas etiquetas y dos cajas de textos. Espera! Tranqui... no te precipites, que te voy a explicar un par de truquillos... Esto podemos hacerlo como te he explicado hace unos prrafos, es decir, haciendo dobleclick sobre el icono en la barra de herramientas, pero tambin podemos hacerlo de otra forma. Ahora veremos estos dos sistemas, para que practiques un poco y de paso te explico cmo usar algunas de las "facilidades" que nos da el diseador de formularios. Empecemos usando la forma "clsica", es decir hacer doble-click sobre el icono de la barra de herramientas. Aade una etiqueta y una caja de textos, estos dos controles estarn ahora posicionados en el centro del formulario, si no has cambiado el botn del sitio que estaba, la caja de textos habr ocultado tanto la etiqueta como el botn. No te preocupes, ya que esto es algo que puede ocurrir y siempre es bueno saber cmo "salir del paso". As que, seleccionaremos la caja de textos, para seleccionarla, simplemente pulsa UNA vez en ella, ahora haremos que tenga el mismo tamao que la otra caja. Mantn pulsada la tecla Control (Ctrl) y haz un click en la otra caja de textos (la que tenemos en la parte de arriba), de esta forma tendremos las dos cajas seleccionadas... (Guille, dile que ya puede soltar la tecla Control). Ahora para cambiar el tamao y hacer que las dos tengan el mismo tamao, podemos hacerlo de dos formas, (como viene siendo habitual), la primera, sera mostrando la ventana de propiedades y asignado a las propiedades Height y Width un valor, el cual se asignar a los controles que estn seleccionados, (esta es la forma que yo lo he estado haciendo durante un montn de tiempo, incluso trabajando con el VB5 y 6, la fuerza de la costumbre), pero hay otra forma ms "prctica" y es mediante el men Format (Formato), seleccionando el sub-men Make Same Size (hacer del mismo tamao), el cual a su vez nos mostrar tres opciones: Width (ancho), Height (alto), Both (ambos), por tanto seleccionaremos la ltima: Both, de forma que se hagan iguales tanto en altura como en anchura, ver la figura 7.

20

Figura 7, El men Format De ese mismo men Format, podemos seleccionar Align (alinear) para que estn alineados a la izquierda, por tanto, selecciona la opcin Lefts de Format>Align, (se supone que los dos controles siguen estando seleccionados). Otra cosa que podemos hacer es pegar el segundo control al primero, de forma que estn "juntitos", pero no revueltos, para ello, selecciona Format>Vertical Spacing>Remove y los dos controles estarn juntos, uno debajo de otro. Por ahora vamos a dejar los controles "pegados". Nota: Decirte que cuando seleccionamos controles para usar las opciones del men Format, el ltimo que se haya seleccionado ser el que "tenga el mando" y el resto se adaptar a ese ltimo control seleccionado. Una vez que hemos cambiado el segundo textbox de sitio, en el centro del formulario tendremos el botn y "debajo" estar la etiqueta, como resulta que la etiqueta es ms alta que el botn, debajo del botn sobresaldr la etiqueta, as que, podemos seleccionarla, pulsar la tecla Control y pulsar sobre la otra etiqueta y haremos lo mismo que con la caja de textos, Format>Make Sime Size>Both y a continuacin Format>Align>Lefts y por ltimo: Format>Vertical Spacing>Remove. Truco: Tambin puedes seleccionar esa etiqueta usando la ventana de propiedades, de la lista desplegable, selecciona Label2 y ahora pulsa la tecla Control, etc., es decir, si no puedes seleccionarla en el formulario, porque est oculta, por ejemplo, siempre nos queda el recurso de seleccionarla desde la ventana de propiedades. Ahora tendremos las dos etiquetas y las dos cajas de textos del mismo tamao y con la misma posicin izquierda, aunque demasiado pegados con sus compaeros, as que ahora, manualmente, vamos a cambiar la posicin. Baja un poquito la segunda caja de textos y posiciona la etiqueta 30 puntos ms bajo que la caja de textos, (que es una distancia "conveniente"), es decir si el valor de la propiedad Top de la segunda caja de textos (Text2) tiene un valor de 510, la segunda etiqueta (Label2) debera tener 540.

21

Ahora vamos a aadir una nueva etiqueta y una caja de textos, pero usando otro "truco". En este caso, tambin queremos que tanto la etiqueta como la caja de textos tengan el mismo tamao que las dos ltimas que acabamos de aadir, por tanto, selecciona la etiqueta y la caja de textos que est a su derecha; ya sabes cmo hacerlo: selecciona la etiqueta y manteniendo pulsada la tecla Control, pulsa en la caja de texto, aunque tambin puedes hacerlo de otra forma: primero pulsa en cualquier parte del formulario para quitar cualquier seleccin que hubiera, a continuacin, con el ratn, selecciona la etiqueta y la caja de textos, (de la misma forma que seleccionaras varios objetos en cualquier aplicacin, incluso en el explorador de Windows, ver la figura 8)

Figura 8 Una vez que tenemos los dos controles seleccionados, copiaremos los dos controles, bien mediante el men Edit>Copy, bien mediante el men contextual (pulsando con el botn secundario del ratn) y seleccionando Copy (Copiar). Ahora vamos a pegar lo que hemos copiado, ya sabrs cmo... (ver la figura 9)

Figura 9 Al pulsar en pegar, se nos mostrar un cuadro de dilogo preguntndonos si queremos crear un array de controles, (ver la figura 10), pulsa en el botn NO, ya que no queremos crear ningn array de controles. Primero te preguntar por si quieres crear el array de Label2 y despus de Text2 (aunque puede que el orden sea al revs, pero eso no es

22

importante). El tema de los arrays de controles, lo vimos en la segunda parte de la entrega 11.

Figura 10 Esto har que se "peguen" dos nuevos controles, los cuales tendrn el mismo tamao que los copiados, incluso estarn posicionados "relativamente" a la misma distancia, pero se pegarn en la parte superior del formulario, como estarn los dos seleccionados, podemos arrastrarlos hasta que estn en la posicin que queramos, en este caso, los posicionaremos debajo de los anteriores. Si necesitas ajustar las posiciones, ya sabes que puedes recurrir al men Format y posicionarlos correctamente. Un detalle a tener en cuenta, es que los controles tendrn como nombre Label3 y Text3, pero mostrar el mismo "texto" que los dos que se usaron para la copia. Para cambiar el texto mostrado, tendrs que asignar un valor a la propiedad Caption de la etiqueta y para cambiar el texto mostrado en la caja de textos, tendrs que cambiar la propiedad Text.

Nota: Como puede ser que te haya ocurrido, sobre todo si no tienes mucha prctica con el ratn o bien porque tu ratn sea muy sensible, es posible que al "intentar" hacer un click en un control, realmente hayas hecho un doble-click, si es eso lo que te ha ocurrido, vers que se muestra la ventana de cdigo con el evento "predeterminado" de ese control, no te preocupes, simplemente cierra la ventana de cdigo y vuelve a intentarlo.

Ahora ya sabes dnde est el men Format, as que... juega un poquito con las distintas opciones y practica, por ejemplo centrando los controles en el formulario, etc.

Ahora que ests entretenido "jugueteando" con las opciones del men Format, vamos a dejar aqu esta segunda parte del Apndice A, entre otras cosas, porque te he explicado lo que quera explicarte: cmo agregar controles a un formulario, poder cambiarle el tamao y posicin, as como explicarte cmo cambiar otras propiedades... adems de que ya se ha hecho un poquito larga... (je, je... es una excusa como otra cualquiera). En otra ocasin veremos ms cosillas que podemos hacer con el IDE de Visual Basic, as como otras cosas relacionadas con los controles y formularios. Espero que estos apndices vayan rellenando las lagunas que se hayan quedado en las entregas "normales". No te voy a adelantar ms cosas que contendrn estos apndices, pero si te dir que no

23

se quedarn en cosas relacionadas con el IDE (entorno de desarrollo), pero... como siempre, tendrs que esperar para poder saber de qu tratarn... mientras tanto, sigue practicando y leyendo cosas sobre el Visual Basic. Nos vemos. Guillermo

Curso Bsico de Programacin en Visual Basic

Tipos de proyectos de VB6, cmo compilarlos, y ms...


Apndice B
Este es el contenido de la primera parte del Apndice B del Curso Bsico de Programacin con Visual Basic Una batallita del Guille Introduccin Crear un nuevo proyecto de Visual Basic Las propiedades del proyecto o o o o Propiedades generales del proyecto Propiedades Generar Propiedades Compilar Propiedades Componente Qu es un EXE ActiveX?

Compilar el proyecto

24

Una batallita del Guille


Como bien sabrs, este Curso Bsico de Programacin con Visual Basic empez siendo "genrico" para todas las versiones que haba en aquel "lejano" 18 de Abril de 1997 (s, hace muchos aos ya), pero con el tiempo los temas tratados fueron centrndose ms en la versin de 32 bits de Visual Basic 4.0 (con esa versin se podan crear aplicaciones tanto de 16 como de 32 bits, con las versiones anteriores solo podamos crear de 16 bits), despus llegaron las versiones 5.0 y 6.0 que es la ltima de esta familia de Visual Basic, ya que la versiones posteriores (7.0, 8.0 y la futura 9.0) solamente sern para las versiones de .NET Framework. En las dos ltimas versiones de Visual Basic (5.0 y 6.0) se introdujeron nuevas caractersticas que hacan (y siguen haciendo) muy potentes a este lenguaje, que a pesar de tener ms de 15 aos de edad sigue siendo el favorito de muchos programadores (o desarrolladores, segn gustos). En este curso bsico, he intentado ensearte todo lo que puedes hacer con este lenguaje, aunque, como es natural, no lo he explicado todo, y no se si algn da conseguir explicarlo todo, pero como ves, despus de tres aos, an quiero seguir mantenindolo vivo, ya que a pesar de lo que muchos predecan, la gente no acaba por pasarse a la versin de .NET de este lenguaje, la razn principal es porque an hay muchos sitios (no sitios de Internet, sino sitios de trabajo) en el que siguen "exigiendo" a sus empleados que "sepan" programar con VB6. As que... creo que habr VB6 durante mucho tiempo ms.

Introduccin
Y en este apndice, (por no llamarlo entrega, que tambin poda llamarlo as, pero... he preferido que sea un apndice, por aquello de que en realidad es algo "aparte" del curso en s), quiero explicarte los tipos de proyectos que podemos crear con Visual Basic 6.0 (cuando digo 6.0 tambin es vlido para el 5.0, incluso para el 4.0, aunque en este ltimo ya no se utiliza, adems de que no permite crear todos los tipos de proyectos que veremos en este apndice). Tambin te explicar cmo compilar esos diferentes tipos de proyectos y cmo configurarlos, ya que an hay gente que se "queja" de que no saben a ciencia cierta para que sirven las diferentes opciones que tenemos en la configuracin de los proyectos de Visual Basic. Antes de seguir, te recomiendo que le eches un vistazo a los dos apndices anteriores, en el primero (el apndice A.1) te explico cmo crear nuevos proyectos usando Visual Basic 5.0 Crontrol Creation Edition (la versin gratuita de VB5); en el segundo (apndice A.2) te explico cmo aadir controles a los formularios y esas cosillas. Te recomiendo que te los leas, porque lo mismo piensas que en este te explicar lo mismo... pero no, lo que aqu veremos es cmo configurar y compilar los distintos tipos de proyectos que podemos crear con Visual Basic 6.0.

Nota: Todas las capturas y explicaciones de este apndice sern de la versin empresarial de Visual Basic 6.0 en espaol, pero lo explicado ser igualmente vlido para el resto de versiones y ediciones, ya que solo

25

trataremos de los 4 tipos principales de proyectos que podemos crear con Visual Basic.

Crear un nuevo proyecto de Visual Basic


Lo primero que haremos es crear un nuevo proyecto, para as poder ver cmo los tenemos que configurar. Por tanto, debemos iniciar el VB y por regla general nos mostrar la pantalla mostrada en la figura 1:

Figura 1. Los tipos de proyectos que podemos crear con VB6

Nota: Si la pantalla mostrada en la figura 1 no se muestra al iniciar el Visual Basic, tendrs que seleccionar Nuevo proyecto del mens de Archivo.

26

Empezaremos creando uno del tipo EXE estndar, que ser el ms comn, es decir, el que nos permite crear un .EXE de toda la vida. Siempre que se crea el nuevo proyecto de tipo EXE tendremos un formulario en la ventana del Explorador de proyectos, que no te voy a explicar para que sirve, ya que est bien explicado en los dos apndices anteriores. Ahora lo que veremos es cmo "configurar" las propiedades de este proyecto.

Las propiedades del proyecto


Para configurar las propiedades del proyecto, debemos seleccionar Propiedades <nombre proyecto>... del men Proyecto, tal como vemos en la figura 2.

Figura 2. Configurar las propiedades del proyecto Al seleccionar esa opcin tendremos a la vista un cuadro de dilogo como el de la figura 3.

27

Figura 3. La ficha General de las propiedades del proyecto

Propiedades generales del proyecto


La primera ficha (o solapa o tab) que se muestra es: General. Desde esta ficha podemos configurar varias cosas. Vemosla cada una de ellas. En la parte superior izquierda tenemos un cuadro de dilogo con el Tipo de proyecto que hemos seleccionado, por defecto tendr el valor EXE estndar (o el que hayamos elegido al crear el nuevo proyecto), de esa lista podemos elegir cualquier otro que nos interese, que sern los cuatro que te coment antes: EXE estndar, EXE ActiveX, DLL ActiveX y Control ActiveX. Como no queremos cambiar el tipo de proyecto, lo dejamos como est, aunque si decides cambiar el tipo de proyecto a generar, desde aqu lo puedes cambiar.

Junto a esa lista tenemos una lista desplegable con el Objeto inicial, es decir, si tenemos varios formularios, podemos indicar cual ser el que se usar como formulario inicial de la aplicacin. Tambin podemos seleccionar Sub Main, si es que queremos que la aplicacin empiece por el mtodo Main. Ese mtodo o procedimiento debe estar declarado en un mdulo de tipo BAS, por ahora no hablaremos de este mtodo pero lo har en otra ocasin, por ejemplo para crear una aplicacin de VB6 que no tenga formularios.

28

Debajo de estas dos opciones, tenemos el Nombre del proyecto, este nombre ser el mismo que el mostrado en el Explorador de proyectos, por tanto podemos cambiarlo desde estas opciones o bien desde el mencionado explorador. Como puedes comprobar, el nombre que muestra es el predeterminado: Proyecto1.

Si queremos que nuestra aplicacin de VB6 tenga su propia ayuda, podemos indicar el nombre del fichero en la casilla Nombre del archivo de Ayuda, y si no sabemos el "path" en el que se encuentra, podemos usar el botn con los res puntos suspensivos que hay a continuacin. Tambin podemos indicar el "ID" del contenido de la ayuda, con idea de que sea ese "id" el que se use cuando el usuario pulse en F1. Los tipos de ficheros que soporta el Visual Basic 6.0 son ayudas con extensin .HLP y las que tienen extensin .CHM.

Nota: La forma de crear ficheros de ayuda para VB y cmo usarlas estn explicadas en mi sitio, (y desde el ndice principal puedes acceder a ellas), aunque es posible que algn da de estos las resuma y las incluya en el curso bsico, ya veremos...

La casilla con la Descripcin del proyecto en realidad solo se utiliza (o es til) para los proyectos de tipo ActiveX, pero no es mala costumbre rellenarla siempre.

Los checkbox que hay abajo a la izquierda sirven para diferentes cosas, aunque dependiendo del tipo de proyecto estarn habilitadas o no, en este caso, est habilitada y seleccionada la de Actualizar controles ActiveX, esto vale por si tenemos aadido algn control ActiveX (.ocx) en nuestro proyecto y si se detecta que hay una nueva versin, se actualizar el proyecto para usar esa versin. Por regla general lo podemos dejar marcado.

Las opciones del grupo Modelo de subprocesos solo estar disponible para proyectos ActiveX.

Propiedades Generar
La segunda ficha de las propiedades del proyecto (ver figura 4) es la de Generar el proyecto.

29

Figura 4. Propiedades Generar del proyecto En esta ficha tenemos los datos del Nmero de versin, que sern las que se muestren cuando pulsemos en las propiedades del ejecutable que creemos desde VB6. Si marcamos la casilla que hay debajo: Incremento automtico, cada vez que creemos un nuevo ejecutable, se incrementar automticamente el valor de Revisin. Yo normalmente lo tengo desmarcado, ya que aado mis nmeros de revisiones, etc., de forma manual, pero si lo dejas marcado, nunca se te olvidar cambiar ese valor, que siempre es conveniente para saber que esta versin es diferente a las anteriores.

En el grupo Aplicacin tenemos el Ttulo del proyecto y el formulario que se usar para el icono de la aplicacin. En la lista desplegable que hay junto a Icono se mostrarn los formularios que tengamos y el icono que tenga el seleccionado ser el que se use para nuestra aplicacin. Por defecto estar el formulario usado como inicio de la aplicacin.

En el grupo de Informacin de la versin tenemos varias opciones en Tipo, segn las vayamos seleccionando, podemos escribir el valor en la casilla que hay bajo Valor. Los valores de tipo son los siguientes: Tipo Comentarios Derechos de autor Marcas comerciales legales Descripcin Descripcin larga de la aplicacin El copyright Las marcas comerciales de los productos usados con esta aplicacin

Descripcin del archivo Descripcin corta de la aplicacin

30

Nombre de la compaa Nombre del producto

El nombre de la compaa El nombre del producto

Como te comentaba, estos son los valores que se muestran en Windows cuando mostramos las propiedades del ejecutable. La versin, descripcin corta y el copyright son los datos que se muestran en la parte superior de la solapa Versin. El resto de datos se muestran al seleccionar las opciones que hay en la parte inferior de la solapa Versin, tal como puedes ver en la figura 5.

Figura 5. Las propiedades del ejecutable en Windiws XP

En Argumentos de la lnea de comandos podemos indicar lo que queramos, de forma que ese valor sea el que se pueda interceptar mediante la funcin Command$ para simular lo que pasara si el usuario de nuestra aplicacin la indiciara indicando algo a continuacin del nombre del ejecutable, ya sea desde una ventana de "DOS" o bien porque le ha agregado opciones despus del nombre del ejecutable en un acceso directo. Por ejemplo, si la aplicacin se inicia as: proyecto1.exe pepito.doc, el valor de la lnea de comandos ser "pepito.doc" y ese valor ser el que se indique en Command$.

31

Si se escriben ms cosas, por ejemplo: proyecto1.exe Erase una vez que se era, todo lo que haya despus del nombre del ejecutable ser el contenido de la lnea de comandos.

La casilla Argumentos de compilacin condicional sirve para crear "constantes de compilacin", las constantes de compilacin son las que podemos usar con: #If CONSTANTE = VALOR Then. Esas constantes se tendrn en cuenta mientras compilamos el ejecutable, es decir, no son valores que podamos cambiar una vez que el .exe est creado. La forma de indicarlo en esa casilla sera de esta forma: NOMBRE = VALOR : NOMBRE2 = VALOR, es decir, las usamos como si hiciramos una asignacin y si queremos definir ms de una, las separamos con dos puntos. Esto es equivalente a definir esas constantes con la instruccin #Const NOMBRE = VALOR en todos los ficheros de nuestro proyecto.

Por ltimo, la casilla Quitar informacin de controles ActiveX no usados, nos viene muy bien si hemos aadido "componentes" (controles .ocx) a la barra de herramientas de controles, pero que no hemos aadido en ninguno de los formularios, por tanto, cuando compilemos no se harn referencia a esos controles no usados.

Propiedades Compilar
En esa ficha de las configuraciones del proyecto tenemos las opciones para eso, para compilar nuestra aplicacin. Las opciones disponibles son las mostradas en la figura 6, y te las detallo un poco a continuacin.

32

Figura 6. Las opciones de compilacin del proyecto actual

La primera opcin es Compilar a P-Code, seleccionndola haremos que el compilador de VB cree un ejecutable en el que se utiliza "pseudo cdigo". Esta compilacin lo que hace es crear un ejecutable ms pequeo, ya que prcticamente todo lo que contiene son llamadas a las funciones y cdigo ejecutable del "runtime" de Visual Basic. En la mayora de los casos, esta ser la opcin ms conveniente, sobre todo si lo que nos interesa es que nuestro ejecutable no ocupe mucho.

El grupo de Compilar a cdigo nativo nos da varias opciones que veremos ahora, la diferencia entre la compilacin a cdigo nativo y el P-Code es que en la compilacin a cdigo nativo se utiliza un compilador "de verdad", es decir, en lugar de usar funciones que llaman a las que estn en el runtime, se sustituyen por cdigo mquina, si bien el runtime de VB sigue utilizndose, ya que no todo el cdigo usado se incluye en el ejecutable. Al compilar en modo nativo, el fichero ejecutable suele ser mucho ms grande que el de P-Code, tambin suele tardar ms en compilar, dependiendo de la cantidad de cdigo que tengamos en el proyecto. Y a pesar de lo que puedas pensar no siempre el resultado de compilar a cdigo nativo suele resultar en un ejecutable ms rpido, independientemente de como hayas seleccionados las opciones extras de compilar a cdigo nativo. Cuando seleccionamos la opcin de compilar a cdigo nativo, tenemos otras sub opciones que nos sirven para "afinar" dicha compilacin, las tres primeras nos permiten optimizar para un cdigo ms rpido, lo cual suele ir acompaado de un ejecutable an ms "pesado". La segunda opcin es optimizar para cdigo reducido, como te puedes imagina, en este caso se "opta" por reducir el tamao del ejecutable aunque ello vaya en detrimento de la velocidad, por ltimo, sin optimizacin, pues eso, no se optimiza ni para cdigo rpido ni para reducido... ni fu, ni fa!

33

Optimizar para Pentium Pro(tm), pues eso, optimiza el cdigo para los Pentium Pro, en otros Pentium tambin funcionar, pero puede que el rendimiento sea menor... o eso dice la documentacin, en fin... Lo de Generar informacin para depuracin simblica, es por si queremos generar un fichero .pdb para poder usar desde Visual Studio (o Visual C++) para depurar el cdigo una vez compilado. Al pulsar en el botn Optimizaciones avanzadas, nos mostrar el cuadro de dilogo que podemos ver en la figura 7, yo prcticamente nunca he usado estas opciones, y como puedes comprobar en esa figura, tampoco se recomienda su uso si realmente no sabemos lo que hacemos, as que... mi recomendacin es que no "trastees" aqu, y si lo vas a hacer, lete lo que dice la ayuda... ya que yo no te lo voy a explicar... je, je, es que si yo no lo uso, pues... el que quiera usarlo que se busque la vida ;-))) No, en serio, no te recomiendo que uses estas opciones salvo que antes hayas comprobado "el efecto" que puede producir.

Figura 7. Las opciones de optimizaciones avanzadas

La opcin final de la pantalla de configuracin de compilacin (figura 6), la que indica Direccin base de la DLL, y que solo estar activada en los proyectos de tipo DLL ActiveX y control ActiveX (un control ActiveX, en realidad es una DLL con la extensin .ocx). Esta opcin incluye un valor que por defecto es: &H11000000, ese valor ser la direccin de memoria en la que se cargar esa DLL, la recomendacin es que siempre usemos una diferente para nuestras DLLs, si al cargar la DLL en memoria, esa direccin est ocupada se utilizar otra, si no est ocupada, se usar la que indiquemos. Yo por regla general le doy el valor que empieza por &H57nnnnnn, por tanto, te rogara que usaras otra distinta, as si en tu equipo hay una DLL que yo he hecho y una tuya, cada una usar su "segmento" de memoria.

34

Propiedades Componente
La ficha Componente solo estar disponible cuando creemos proyectos de tipo ActiveX, en la figura 8 vemos esa ficha si tenemos seleccionado un proyecto de tipo EXE ActiveX.

Figura 8. Opciones de Componente de nuestro proyecto actual En el grupo Modo de inicio podemos seleccionar Independiente o Componente ActiveX. Estas opciones solo est disponibles cuando el proyecto es de tipo EXE ActiveX. Para comprender bien debes saber cmo funcionan los EXE ActiveX, y no creo que ese este el lugar idneo para explicarte como "furula" este tipo de proyecto, pero te explicar un poco, para que no andes demasiado perdido (o creas que quiero escurrir el bulto, je, je).

Qu es un EXE ActiveX?
Un EXE ActiveX es un componente ActiveX (o componente COM), en realidad todo lo que creemos con VB6 son componentes ActiveX/COM, (ver la entrega 47 para ms detalles), pero es un componente diferente, ya que nos permite usarlo de dos formas diferentes: como un ejecutable normal que es capaz de proporcionar "objetos automatizados" y tambin se puede usar como un control ActiveX, en el sentido de que simplemente proporciona esos objetos de automatizacin pero no se "ve" que se est ejecutando (o en funcionamiento). Los ejemplos "clsicos" de componentes EXE ActiveX son las aplicaciones como Word o Excel, que tanto pueden hacer de componente ActiveX como de una aplicacin normal y corriente. Pues bien, si seleccionamos la opcin Independiente, le estamos indicando al

35

compilador de VB que nuestra idea ser usar este ejecutable como un EXE normal que es capaz de proporcionar objetos de automatizacin, es decir, se ejecutar de forma visible pero si la "referenciamos" desde otro proyecto podremos usar las clases que expone pblicamente. Por otra parte, si seleccionamos Componente ActiveX, la aplicacin se iniciar de forma "oculta", y solo proporcionar esas clases pblicas que hayamos indicado en dicho ejecutable.

De la opcin Servidor remoto no te voy a hablar, ya que nunca la he usado, as que...

En el cuadro Compatibilidad de la versin tenemos tres opciones y se utilizan para los componentes ActiveX (sean del tipo que sean), estas opciones indicarn cmo tratar el compilador el cdigo generado. Normalmente cuando creamos un componente ActiveX, ste debe estar registrado en el registro del sistema operativo, si no lo est, no podremos usarlo. Pues bien, el propio Visual Basic se encarga de hacer ese registro, (pero solo en nuestro equipo), y dependiendo de como tengamos seleccionada estas opciones actuar de una forma u otra. Si seleccionamos Sin compatibilidad, querr decir que cada vez que compilemos el componente crear una nueva entrada en el registro, bueno, que crear un nuevo valor que identifica a este componente. Esta opcin solo debemos seleccionarla si la queremos empezar "en limpio" un componente, y no ser opcin que habitualmente seleccionaremos. Si seleccionamos Compatibilidad de proyecto le estamos indicando que siempre que se compile actualice el resto de proyectos en el que estemos usando dicho componente, de forma que podamos seguir usndolos sin problemas y sin tener que andar cambiando las referencias de los proyectos que usan el componente. Todo esto es para cuando creamos un componente ActiveX y aadimos proyectos normales que lo utilicen, para ms detalles sobre todo esto, lete la entrega 47. Por ltimo tenemos la opcin de Compatibilidad binaria, en la que adems hay una caja de textos, en la que estar el componente que hemos creado. La compatibilidad binaria significa que siempre se mantendr la misma clave del registro de Windows, esto quiere decir que si modificamos el cdigo "interno" de las clases que tenemos en ese componente, esos cambios se intentarn manejar como compatibles con las versiones anteriores, de forma que si ya tenemos distribuido el componente, esos cambios no afecten a las aplicaciones que lo utilicen. Esto en realidad es ms "peliagudo" de lo que en este pequeo comentario pueda explicarte, y como seguramente ser motivo de una entrega propia, no me voy a enrollar demasiado, solo decirte que el propio Visual Basic nos avisar si "rompemos" esa compatibilidad binaria, dndonos opciones para solucionar el "problema". En la caja de textos indicaremos el componente previamente compilado con el que queremos mantener esa compatibilidad binaria. Y si quieres saber ms, lete esto.

Y la ltima opcin de este dilogo es la ficha Depurar, de esta ficha, pues tampoco te voy a comentar nada, ni siquiera te voy a poner una captura, porque... si no voy a comentarte

36

nada, que ms da lo que contenga? Pues eso.

Compilar el proyecto
Para terminar, veamos cmo compilar el proyecto o proyectos que tengamos. Como ya sabes, en el entorno de Visual Basic podemos tener ms de un proyecto abierto, normalmente tendremos varios cuando estemos creando una DLL ActiveX o un Control ActiveX y queramos probarlo en un proyecto normal, tal como te coment en la entrega 47. En cualquier caso, para compilar ese o esos proyectos lo haremos mediante la opcin Generar <nombre proyecto>... si es un proyecto independiente, bien el nico que tengamos abierto o el que tengamos seleccionado si es que tenemos ms de uno. En caso de que tengamos varios proyectos, podemos seleccionar la opcin Generar grupo de proyectos... que como vemos en la figura 9, estar deshabilitado si no tenemos varios proyectos cargados.

Figura 9. Las opciones para generar el proyecto est en el men de Archivo Dependiendo del tipo de proyecto que tengamos cargado en el IDE de Visual Basic, la extensin ser .exe para los proyectos EXE estndar y EXE ActiveX, .dll para el tipo de proyecto DLL ActiveX o bien .ocx para el tipo de proyecto Control ActiveX. Al pulsar en esa opcin nos mostrar un cuando de dilogo como el de la figura 10, en el que indicaremos el nombre que queremos usar y dnde guardarlo. Bueno, ese cuadro de

37

dilogo solo se mostrar si ya hemos compilado (o generado) nuestra aplicacin como mnimo una vez, ya que la primera vez la generar directamente usando como nombre el mismo nombre del proyecto y como path el del propio proyecto.

Figura 10. Las opciones de generar proyecto

Si pulsamos en el botn Opciones... nos mostrar el cuadro de dilogo de propiedades del proyecto pero solo con las solapas (o fichas) Generar y Compilar.

Pues esto es todo por hoy... que para mi ha sido ayer y hoy, ya que como soy tan "pamplinas", pues tardo una eternidad en escribir cuatro cosillas para que tu lo tengas ms fcil... ;-)))) Pero no me quejo... o casi... je, je. Hasta la prxima!

Nos vemos. Guillermo

Curso Bsico de Programacin en Visual Basic


38

A estas alturas no es fcil hacer un curso de introduccin a la programacin con Visual Basic. Si fuese a raz de la primera o segunda versin, incluso con la tercera, sera ms fcil. Pero ahora est ms crudo, por aquello de que soporta 16 y 32 bits y te enfocas en una u otra direccin o... Pero como el ttulo indica, esto ser una cosa para introducir al que quiere empezar a programar, incluso para el que viene de otro lenguaje, aunque seguramente, sobre todo al principio, se encontrar con conceptos demasiados bsicos. Como este curso no se publica sobre algo que ya est terminado, ir hacindolo sobre la marcha, espero tus indicaciones sobre si voy demasiado rpido o si realmente me estoy pasando en cuanto a 'simpleza'. De cualquier forma, me gustara or tu comentario y tus impresiones, eso me animar a seguir y sobre todo a terminar. Bueno, manos a la obra. A ver como me sale el tema, (al final no he encontrado los apuntes que ya tena hechos, de cuando daba clases a nios y a no tan nios, de esto hace ya 10 aillos de nada) Lo advierto, a los que saben algo y a los que lo saben todo (o casi todo), lo que viene a continuacin es super bsico. Y pensando tambin en los que por una razn u otra no tienen unos conocimientos que a otros les parecer absurdo, s que an hay gente que no tienen ni 'repajolera' idea de lo que es una expresin, una variable y ni que decir tiene sobre las matrices, los nmeros binarios o la notacin hexadecimal... Que es una variable? En cualquier programa siempre necesitaremos hacer clculos, usar informacin, procesarla y mostrarla. En la mayora de los casos, necesitaremos un lugar temporal en el cual guardar parte de esa informacin, incluso toda. Todos los lenguajes, y el Basic no iba a ser menos, nos permiten guardar datos en la memoria, para que cuando los necesitemos, podamos tomarlos, modificarlos y volverlos a guardar para usarlos ms tarde. Si no seguimos unas normas o usamos unas reglas, de poco nos iba a servir el guardar esa informacin, si despus no podemos almacenarla en ningn sitio, pero ese ser tema de otro captulo... ahora centrmosno en la memoria. La memoria? Espero que este concepto lo tengas claro, pero si no es as, ah va un poco de 'rollo': La memoria es el lugar donde el ordenador almacena de forma temporal los programas y parte de la informacin que necesita o utiliza. As pues, los lenguajes de programacin usan tambin esa memoria para guardar informacin propia del lenguaje y del programa que queramos realizar. El programa una vez que se est ejecutando puede necesitar tambin guardar informacin, aunque esta informacin slo estar disponible mientras se est ejecutando. Esas posiciones o lugares de la memoria donde los programas pueden almacenar informacin son las variables. El que se llamen de esta forma es porque podemos hacer que el contenido de ese lugar de almacenamiento vare, es como si tuvisemos una serie de cajas y en ellas pudisemos guardar cosas, con la salvedad de que en cada caja slo puede haber una cosa a la vez; aunque tambin veremos cmo hacer que el contenido pueda variar y que vare dependiendo de lo que contena antes... Veamos un ejemplo: Imagnate que quieres guardar tu nombre en una variable, para ello tendramos que 'guardar' el nombre en la memoria, es decir asignar a una variable un valor. En este caso nuestro nombre. Para ello el lenguaje de programacin pone a nuestra disposicin unos lugares donde almacenar nuestro nombre, pero nos impone una serie de reglas de conducta. Si queremos guardar en una de nuestras cajas una hoja, por lo menos tendremos una caja con el tamao adecuado y que tenga una 'forma', para que el papel no vuele al menor soplo de viento.

39

Esta es la regla bsica para poder usar esos lugares de almacenamientos (variables): Llamar a esas posiciones de memoria con un nombre. Simple, verdad? En principio, es todo el requisito necesario: que le demos un nombre al sitio en el que queremos guardar la informacin. Por tanto, si queremos guardar un nombre, (el nuestro, por ejemplo), en la memoria, podramos llamarlo nombre. Y ahora tendremos que seguir otra norma de conducta (o funcionamiento), que en este caso el lenguaje Basic nos dicta: Para guardar en una variable (posicin de memoria) algo, debes hacerlo de la siguiente manera: ---Pon el nombre con el que quieres llamar a esa parte de la memoria, ---a continuacin pones el signo igual (=) y ---despus lo que quieras guardar. Por tanto para guardar Guillermo en la variables nombre, tendramos que hacer (o casi): Nombre = Guillermo Pero esto poda llevar a confusin, ya que el Basic no nos dice nada sobre cmo debemos llamar (o si lo prefieres, cmo hay que escribir) el nombre de una variable, por tanto Guillermo tambin podra ser una variable, (es que el Basic, a pesar de que llevo tantos aos bregando con l, no sabe que ese es mi nombre!!!). As pues, cuando queramos guardar en una variable una palabra, una frase, nombre o cualquier tipo de informacin alfabtica, tendremos que indicarlo poniendo dicha informacin dentro de comillas dobles, el ejemplo quedara as: Nombre = "Guillermo" Ahora no hay confusin posible, hemos seguido lo que el Basic nos ha dicho: variable, signo igual, valor a almacenar. Si queremos guardar un nmero en una variable, la cosa es ms simple: Numero = 7 Te ests enterando? Pero, que ocurre si quisiramos repetir 7 veces a Guillermo? Podras hacer esto, multiplicar a Guillermo por 7 (como no hay bastante con uno...) Paliza = "Guillermo" * 7 Pero el Basic te dira que eso no est bien, no porque Guillermo (yo) no sea un paliza, sino porque te dira que no coinciden los tipos (Type Mismatch) Que son los tipos? Los distintos tipos de datos. (que esfuerzo mental...) Los datos pueden ser, bsicamente, de dos tipos: Numricos: slo nmeros y Alfanumricos: cualquier cosa, letras y/o nmeros, pero es tratada como si fuesen palabras, frases, etc. Para el Basic 7 y "7" son dos tipos de datos diferentes. El primero es el nmero 7 y en el segundo caso, es el literal (o palabra) "7" As que cuando veas algo entrecomillado, piensa que no es un nmero, sino una palabra (ms vulgarmente llamada cadena de caracteres o string en ingls) Hemos visto que no podemos multiplicar una palabra (cadena) por un nmero, pero si podemos multiplicar una variable por un nmero (siempre que la variable sea numrica, por supuesto) Segn esto, el Basic debera permitir hacer esto: Guillermo = 5 Paliza = Guillermo * 7 El Basic tomara el 5 y lo almacenara en una variable numrica llamada Guillermo. Despus se encuentra con: Paliza = Guillermo * 7 y aqu lo que hace es evaluar la expresin que est despus del signo igual, lo calcula y el resultado lo guarda en la variable que est a la izquierda del signo de asignacin (=)

40

Expresin? Expresin es cualquier cosa que el Basic tenga que 'desglosar' para poder entenderla, incluso a veces ni eso... Por ejemplo cuando el Basic se encuentra con 5 * 2 tiene que 'evaluar' lo que significa, para poder hacer el clculo, de esta forma sabr que tenemos una operacin en la cual queremos multiplicar dos nmeros, una vez que ha evaluado nuestra intencin de multiplicar esos dos nmeros, efectuar el clculo y almacenar el resultado en... si no le decimos dnde, lo har en una memoria que tiene para esas cosas, pero si no le indicamos que debe hacer con ese resultado, nos dar un error... Si le decimos simplemente: 5 * 2 El Basic no sabr que hacer con el resultado de esta 'expresin' (que por cierto es 10) y nos dir: o te espabilas o lo tienes crudo conmigo. As que lo ms juicioso sera decirle: vale, vale, gurdalo en una variable, as que: Resultado = 5 * 2 guardara un 10 en la variable Resultado. Tambin podramos decirle que nos mostrara el resultado, en lugar de guardarlo en una variable, y aqu llega nuestra primera instruccin: Print. Con ella le decimos al Basic que lo imprima (o sea que los muestre, ms adelante veremos dnde), segn lo dicho, haciendo esto: Print 5 * 2, el Basic dira que muy bien y mostrara un 10 Pero, volvamos al Paliza del Guillermo, es decir al ejemplo de Paliza = Guillermo * 7 Si quisiramos mostrar el valor de Paliza, tendramos que hacer algo como esto: Print Paliza, y nos mostrara 35, ya que el valor de Paliza sera 35, porque el contenido de Guillermo es 5 y 5 * 7 es 35 (y sin calculadora!!!) Veamos si es cierto que Guillermo vale 5. Haciendo Print Guillermo, mostrar un 5. Antes de seguir 'imaginando' las cosas, vamos a verla en funcionamiento. Es decir vamos a probar que todo esto es cierto. Carga el Visual Basic (si es que an no lo has hecho). Te crear un Form nuevo, que estar vaco. Cirralo y muestra la ventana de cdigo. Mostrar la parte de las declaraciones Generales del Formulario. Si tiene escrito Option Explicit, (estar en la parte superior), brralo, ms adelante te explicar para que sirve. Ahora sitate en Form (seleccinalo de la lista desplegable que est a la izquierda), te mostrar: Private Sub Form_Load() End Sub Sitate en medio, es decir, en una lnea en blanco despus del Private... y escribe el ejemplo, quedara as: Private Sub Form_Load() Show Guillermo = 5 Paliza = Guillermo * 7 Print Paliza End Sub

41

Pulsa F5, para ejecutar el programa, y vers que se escribe el 35. Bien, ya tienes una forma de mostrar datos. Ahora veamos otros ejemplos, antes debes parar el programa, para ello cierra el Form, pulsando en el botn que tiene una X, o bien pulsa en el botn detener de la barra de herramientas del VB. Sitate de nuevo en el cdigo del Form_Load, escribe despus de la lnea del Print, lo siguiente: Print Guillermo Pulsa de nuevo F5 y vers que ahora adems del 35, hay un 5 debajo. El valor de la variable Guillermo. Pero, que ocurrira si cambisemos el valor de Guillermo? Aade estas lneas a continuacin de la anteriores, para que quede de esta forma: Private Sub Form_Load() Show Guillermo = 5 Paliza = Guillermo * 7 Print Paliza Print Guillermo Guillermo = 10 Print Guillermo Print Paliza End Sub Despus de pulsar F5, te mostrar los siguientes valores (cada nmero en una lnea), 35, 5, 10, 35 Esperabas que el ltimo fuese 70? Fjate que cuando asignamos a Paliza el contenido de Guillermo, ste era 5, por tanto el Basic evalu la expresin 5 * 7 y almacen el resultado (el 35). Una vez almacenado el resultado, el Basic se olvid de dnde haba sacado ese 5. Si queremos que se 'actualice' el valor de Paliza, tendremos que indicrselo de nuevo al Basic, para que vuelva a evaluar la expresin y hacer la correspondiente asignacin. Para ello, pon en medio de los dos ltimos prints la siguiente asignacin: Paliza = Guillermo * 7 Esta vez, al ejecutar el programa, mostrar un 70, que ser el nuevo contenido de Paliza. Ya para terminar, borra todo lo anterior y escribe: (por supuesto debes detener el programa...) Private Sub Form_Load() Show Nombre = "Guillermo" Print Nombre End Sub

42

Pulsa F5 y vers que se muestra el contenido de la variable Nombre, es decir Guillermo. Prueba ahora con esto otro (es un clsico): Print "Hola Mundo" Y para rematar, y de camino ver otra posibilidad del Print, escribe en lugar del Print Nombre: Print "Hola " ; Nombre El punto y coma, se usa para indicarle al Basic que se deben mostrar las cosas una a continuacin de la otra. Ahora te habr mostrado: Hola Guillermo, fjate que despus de hola y antes de cerrar las comillas hay un espacio. Bien, creo que con esto es suficiente por hoy... o por ahora. Si tienes algn comentario, hazlo pulsando en este link. As sabr que opinin te merece este primer captulo del cursillo/tutorial o como prefieras llamarlo. Nos vemos, (espero que pronto).

Nota: En todos los FORM_LOAD debers poner SHOW al principio para que se muestre lo que se imprime.
Ya lo he cambiado para que no te quejes, pero los que empezaron el curso antes del 4 de Sep'98, lo tuvieron ms crudo...

Curso Bsico de Programacin en Visual Basic


Seguimos con este cursillo bsico. Antes de nada una pequea aclaracin, para que los ejemplos que se dieron en la entrega anterior, debers poner un Show en el form Load antes de los Prints, para que se muestre algo... sino no se ver nada. Una vez hecha esta pequea puntualizacin, vamos a la tarea. El tema de hoy ser sobre el manejo de entrada de datos y una forma un poco ms prctica de mostrar los datos, empezaremos a vernos ya con lo que es programar con Windows, es decir el manejo de eventos, o al menos el movernos dentro de los eventos y saber "controlarlos" para las cosas que queramos hacer. Con el "basic clsico", no haba problemas. Las cosas se producan de forma lineal, es decir empezaban por el principio del programa y se iban ejecutando a medida que se avanzaba por l. Salvo en las ocasiones en las que en un momento dado "saltbamos" a otro sitio, pero era porque nosotros as lo habamos programado. De una forma u otra tenamos control total sobre lo que poda ocurrir. Pero en Windows (o los entornos dominados por los eventos), la cosa no es tan sencilla. Debemos controlar todas (o casi) las posibilidades que se pueden producir... Antes vamos a dejar un par de cosillas claras. Segn como tengas configurado el entorno de desarrollo, habrs tenido problemas con los ejemplos de la entrega anterior. La razn

43

es que el Visual Basic nos permite controlar un poco mejor el tema de las variables que queremos usar. Ese control lo da la instruccin: Option Explicit. Si ponemos esto en la parte destinada a las declaraciones de cualquier mdulo, nos obligar a declarar las variables que vamos a usar en dicho mdulo. En el ltimo ejemplo de la entrega anterior, tenamos una variable llamada Nombre, en la cual almacenamos un nombre, por tanto podramos haberle avisado a Visual Basic que reservara espacio para una variable, y para hacerlo usamos la instruccin DIM, con sta le indicamos que nos guarde un "cachillo" de la memoria para nuestro uso: Dim Nombre Tambin nos ofrece una variante con respecto al "basic clsico" y es, precisamente, el tipo de datos variant. Con este tipo podemos asignar a una variable cualquier tipo de dato. Desde un nmero hasta una cadena de caracteres, pasando por cualquier tipo de objeto que Visual Basic pueda manejar (ms o menos). Los que hayan tenido experiencias anteriores, con Basic u otro lenguaje, sabrn que cada variable debe ser del tipo de datos que queramos asignarle. En VB por supuesto esto tambin es posible y recomendable. Ya vimos que los tipos de datos podan ser numricos o de caracteres. Pero dentro de los numricos, tenemos cuatro tipos bsicos: enteros, enteros largos, simples y dobles. Cada uno de ellos tienen unas capacidades determinadas, adems de ocupar ms memoria o menos, (ahora lo veremos), lo ms importante es que los nmeros enteros y entero largo slo admiten nmeros enteros (de ah sus nombres), es decir que no admiten decimales. Sin embargo los otros dos si admiten decimales. Estas capacidades puedes encontrarlas en el manual del basic o en la ayuda, lo que a mi me interesa que sepas es cmo poder indicarle al Visual Basic que reserve ese espacio de memoria para un tipo determinado. Ya te he dicho que el espacio que ocupa en memoria es diferente para cada uno de estos tipos, veamos en la siguiente tabla cmo declararlos y cuanto ocupa: Tipo Entero Entero Largo Simple Doble Espacio ocupado Tipo de declaracin 2 bytes 4 bytes 4 bytes 8 bytes Integer Long Single Double Ejemplo Dim Numero As Integer Dim Numero As Long Dim Numero As Single Dim Numero As Double

En el caso de las variables que van a guardar nombres (cadenas de caracteres), se deben declarar como String y el espacio que ocupa ser 4 bytes ms un byte por cada caracter que tenga, en el caso de VB de 32 bits realmente ocupar 2 bytes por cada caracter que tenga. La longitud mxima de una variable del tipo String ser de aproximadamente 32.000 caracteres y la forma de declararla ser: Dim Cadena As String Una vez declarada podemos asignarle la cantidad de caracteres que queramos (sin pasarnos) y cuantas veces queramos. Hay que tener en cuenta que en cualquier variable slo se queda el ltimo valor asignado. Ya lo vimos en la entrega anterior, pero vamos a refrescarlo un poco: Dim Nmero As Integer Nmero = 5 Print Nmero

44

Nmero = 12 Print Nmero En este ejemplo, el ltimo valor almacenado en Nmero es el 12. El 5 que tena en un principio se perdi. Pero, que tengo que hacer para sumarle una cantidad al valor almacenado en una variable? Es decir, cmo incrementar el valor de una variable numrica? La respuesta la tienes en cmo se manejan las expresiones y las asignaciones a las variables. Como ya vimos anteriormente, al asignar a una variable una expresin, primero se calcula la expresin y una vez obtenido el resultado, se asigna a la variable. Recuerdas lo que ocurra con la variable Paliza? Vamos a verlo de nuevo, pero usando otros nombres menos "cachondos" Dim N As Integer Dim M As Integer N = 10 M = N * 3 Print M El resultado de este programa sera 30, que es lo que resulta de multiplicar 10 por 3. Cuando se asigna a la variable M el valor de N (que es 10) multiplicado por 3, el VB toma el contenido de N y lo multiplica por 3. Una vez que sabe la solucin, asigna ese valor, en este caso 30) a la variable que hay a la izquierda del signo igual. Sabiendo esto, podramos simplificar la cosa y hacer los siguiente: N = N * 3 Print N Tambin obtendramos 30. Ya que cuando el Basic calcula la expresin de la derecha del signo igual, N vale 10, una vez obtenido el resultado del clculo lo asigna a la variable de la izquierda del signo de asignacin, sin importarle lo ms mnimo de que variables es y como en este caso hemos usado la misma, pues se queda el ltimo valor, perdindose el que originalmente estaba "guardado". Esto es til cuando necesitamos "contar" de forma secuencial, para as incrementar el valor de una variable. Ya veremos alguna "utilidad" para estos casos. Ahora vamos a ver cmo podemos manejar las cadenas de caracteres, es decir las variables de tipo String. Con estas variables ocurre lo mismo que con las numricas, pero la nica operacin que podemos realizar es la suma. Realmente una suma en una cadena de caracteres es "pegar" dos cadenas en una sola. Por ejemplo si hacemos esto: N = 3 + 2. El valor obtenido es 5 y eso es lo que se guarda en N. Sin embargo con los strings hacer esto: Cadena = "A" + "B", se guardar "AB", es decir se unirn las dos cadenas en una sola. Para este tipo de operacin se recomienda usar mejor el signo &. Que entre otras cosas le indica al Visual Basic que lo que pretendemos hacer es unir dos cadenas, no sumarlas. Aunque "tericamente" no se pueden sumar cadenas, slo con-catenarlas, veremos cmo

45

podemos llegar a producir "problemillas" de entendimiento entre el VB y nuestras "mentes poderosas". Como te he comentado al principio el tipo de datos Variant acepta de todo, nmeros, nombres, etc. Si no le indicamos de forma correcta al VB cual es nuestra intencin, podemos confundirle y hacer que el resultado de algo que nosotros dbamos "por hecho", al final se convierte en un pequeo caos para nuestras pobres mentes. Vamos a verlo con un par de ejemplos, en estos casos: (al no indicarle de que tipo son las variables, el Basic entiende que nuestra intencin es usar el tipo Variant) Dim Num1 Dim Num2 Num1 = 5 Num2 = 3 Num1 = Num1 + Num2 Print Num1 Que imprimir? Prubalo y saldrs de duda. Bueno, imprimir un 8. Ahora veamos este otro ejemplo: Dim Num1 Dim Num2 Num1 = "5" Num2 = "3" Num1 = Num1 + Num2 Print Num1 Fjate que lo que vara es slo las comillas. El resultado en este caso es 53, es decir ha unido las dos cadenas. Ahora quita las comillas del 5, para dejarlo as: Dim Num1 Dim Num2 Num1 = 5 Num2 = "3" Num1 = Num1 + Num2 Print Num1 Alehop! Que ha pasado? Pues que ha impreso un 8, es decir ha "pasado" de que el tres sea una cadena de caracteres y lo ha tomado por un nmero... En esta ocasin, slo vamos a cambiar la lnea de la asignacin para dejarla de esta forma:

46

Num1 = Num1 & Num2 El resultado ser 53. Porque le hemos indicado que una las dos cadenas, por tanto al encontrase con esta "operacin" ha considerado al nmero 5 como una cadena, en lugar de un nmero. Cambia ahora la asignacin del Num2, para que sea: Num2 = 3 Vuelve a mostrar 53, el signo & tiene unos poderes enormes... y a pesar de ser dos nmeros la nica operacin que puede realizar es la concatenacin de cadenas, por tanto el tipo Variant se convierte por arte de magia en cadena. Pero fjate si es "fuerte" el poder de conviccin que tiene este operador, que aunque cambiemos el tipo de las variables, sigue "convenciendo" al basic que tipo de operacin debe hacer. Esto no debera ocurrir as, pero ocurre. Dim Num1 As Integer Dim Num2 As Integer Num1 = 5 Num2 = 3 Num1 = Num1 & Num2 Print Num1 Sigue mostrando 53, aunque en este caso debera producir un error, ya que un Integer no es una cadena. As que "cuidadn" con las operaciones que realizamos. Ya que si aades esta lnea: Print Num1 * 2 Vers que realmente Num1 tiene guardado un nmero y el resultado ser: 106 A dnde nos lleva todo esto? A que debemos usar los signos (operadores) de forma adecuada. Y si nuestra intencin es sumar nmeros, empleemos el signo +, en caso de que queramos unir cadenas de caracteres, usaremos el & Para rematar esta segunda entrega, vamos a usar un textbox para que se puedan introducir datos y empecemos a manejar los eventos, mejor dicho empecemos a "habituarnos" a los eventos. Aade al form dos Label, un TextBox y un botn de comandos. El aspecto ser algo parecido al de la siguiente figura:

47

Aade el siguiente cdigo y despus ejecuta el programa, ya sabes F5. Escribe algo en el cuadro de texto y pulsa en el botn. Private Sub Form_Load() Label2 = "" Text1 = "" End Sub

Private Sub Command1_Click() Label2 = "Hola " & Text1 End Sub Cuando pulsas F5, se produce el evento Form_Load, por tanto se asigna al Label2 y al Text1 una cadena vaca, con lo cual borramos el contenido anterior, que es el que se muestra en la Figura. Hasta que no pulsemos el botn mostrar, no ocurrir nada y el programa estar esperando a que ocurra algo. Una vez pulsado el botn, se produce el evento Click del Command1 y se hace lo que se indica en su interior, que es tomar lo que hay en la caja de texto y unirla a la palabra Hola, para asignarla al Label2. Ahora, imagnate que quieres mostrar el nombre en maysculas. Lo nico que tendras que hacer es lo siguiente: Private Sub Command1_Click() Label2 = "Hola " & UCase(Text1) End Sub Lo que se ha hecho es decirle al VB que convierta en maysculas lo que ya est en Text1. Esa es la "utilidad" del UCase. Pero y si quisiramos que conforme se va escribiendo se vayan convirtiendo los caracteres a maysculas? Aqu entraran ms instrucciones/funciones del Visual Basic, as cmo otro de los eventos que pone a nuestra disposicin, en este caso el evento que se produce cada vez que se modifica el contenido del textbox: Change, escribe lo siguiente: Private Sub Text1_Change() Text1 = UCase(Text1) End Sub Prubalo y vers lo que ocurre. Queda "guay" verdad? Pero no es lo que nosotros pretendamos. Vamos a intentar remediarlo y de camino vemos nuevas instrucciones/propiedades, en este caso del TextBox.

48

Private Sub Text1_Change() Text1 = UCase(Text1) Text1.SelStart = Len(Text1) End Sub La lnea que se ha aadido (realmente la habrs tecleado t), lo que le indica al Visual Basic es que haga lo siguiente: Calcula la longitud del contenido del Text1, (Len cuenta los caracteres de una cadena y lo devuelve como nmero), SelStart es una propiedad del TextBox que entre otras cosas, le indica la posicin en la que se insertar el siguiente caracter que se escriba o bien nos puede indicar la posicin actual del cursor. Por tanto obliga a poner el cursor, (el palico ese que parpadea y que nos indica que podemos escribir), al final de la ltima letra que contiene el Text1. Ahora ya sabes que cada vez que "cambie" el Text1, se produce un evento Change. Pero hay otra forma de hacer esto mismo y es controlando cada tecla que se pulsa. Esto lo podemos "controlar" en el evento KeyPress, el cual se produce cada vez que se pulsa una tecla. Borra el procedimiento anterior y escribe este otro: Private Sub Text1_KeyPress(KeyAscii As Integer) Dim s As String s = UCase(Chr(KeyAscii)) KeyAscii = Asc(s) End Sub Ahora han entrado dos nuevas funciones en accin: Chr, la cual convierte un nmero en una cadena... realmente convierte un cdigo ASCII en la letra que representa (busca en la ayuda ASCII y lete lo que dice en las opciones que te muestra). Por otra parte Asc hace lo contrario, es decir convierte una letra en el cdigo ASCII. Y lo que nosotros hacemos es: convertir el cdigo de la tecla pulsada, representado por la variable KeyAscii, en una cadena, la pasamos a maysculas y despus la volvemos a asignar a la variable, para "engaar" al Visual Basic y as hacerle pensar que realmente hemos tecleado una letra en maysculas. Bueno, aqu voy a dejar la cosa, pues creo que con esto es te puedes ir "entreteniendo". Nos vemos, espero en la prxima entrega. Y ya sabes que espero que me mandes tus comentarios, por lo menos para saber que hay alguien interesado en seguir este curso "super-bsico".

Curso Bsico de Programacin en Visual Basic


49

Bien, despus de un mes y pico, seguimos con la tercera entrega del curso "super-bsico" de programacin con Visual Basic. Si quieres ver las entregas anteriores, pulsa en los siguientes links: este para la Primera y este otro para la Segunda. Esta entrega la voy empezar con recomendaciones e instrucciones del buen hacer en Visual Basic, espero que sigas algunas, preferiblemente todas, estas normas. Ya has visto cmo maneja el Visual Basic las variables, si a esta "libertad" (aunque ms bien es libertinaje), le aadimos que no nos obliga a nada, es decir el VB nos est diciendo: "puedes usar las variables para lo que quieras, cmo quieras (o casi) y cuando quieras" Y esto en principio podra parecer una buena cosa, pero realmente es un mal hbito, que muchos de los que vens del BASIC, ya tenis formado y creo que ahora sera un buen momento para empezar a cambiar. Lo primero que debes hacer es ir al men Herramientas (Tools) y en Opciones (Options) marca la casilla que indica "Requerir declaracin de variables" (Require Variable Declaration), esto aadir a cada nuevo mdulo (FRM, BAS o CLS) la siguiente instruccin: Option Explicit, de esta forma tendrs la obligacin de declarar cada una de las variables que uses en el programa. Y tu preguntars: Para que obligar a que se declaren las variables? La respuesta es bien sencilla: para que las declares... (algunas veces me asombro de la lgica tan aplastante de mis comentarios) Bromas aparte, es recomendable que declares las variables que vayas a usar y te dira ms: no slo es bueno declarar las variables, sino que mejor an es declararlas del tipo adecuado. Ya vimos que hay diferentes tipos de variables, no slo de tipos genricos como podran ser para almacenar caracteres y nmeros, sino que dentro de las numricas hay varios tipos, y cada uno de ellos tiene una razn de ser. En mis tiempos del BASIC normalito, es decir del MS-DOS, no exista esta obligacin de declarar "forzosamente" las variables y cuando estabas escribiendo un programa (proyecto que lo llaman ahora), grande, acababas "inevitablemente" usando ms variables de la cuenta porque ya no recordabas si la variable "i" o "j" estaba siendo usada a nivel global o no... (yo es que con el despiste que gasto, me vea creando las variables "ii", "j2", etc., para no "meter la pata") y esto no era lo peor, al fin y al cabo lo nico que ocurra era que estaba "desperdiciando" memoria, por no tener un control de las variables que estaba usando; lo malo era que se podan escribir errneamente los nombres de las variables de forma que al final, el programa no funcionaba bien porque al escribir un nombre de variable, habamos cambiado el nombre... era frustrante y algunas veces te volvas loco buscando el fallo... La ventaja de usar el Option Explicit, es que si escribes mal una variable, el VB te avisa... bueno, algunas veces te avisa, sobre todo cuando se encuentra con la variable "mal escrita". Aqu viene la segunda recomendacin del da: cuando ejecutes un programa, hazlo con Control+F5, de esta forma se hace una compilacin completa y "ms o menos" exhaustiva del cdigo, avisndote cuando hay algo que no "cuadra", con el VB3 no haba problemas, ya que siempre se haca la compilacin completa, pero desde el VB4 se puede pulsar F5 y hasta que no llega al procedimiento actual, no comprueba si todo lo que hay en l est correcto. As que para "curarte en salud" procura hacer la compilacin completa La tercera recomendacin no es obligatoria, siempre que sigas la que voy a dar despus, esta es una norma que tambin he usado desde mis tiempos de MS-DOS (aunque reconozco que ltimamente no la pongo en prctica, ya que hago lo que despus

50

comentar en la cuarta recomendacin). En todos los mdulos, antes slo eran BAS, pona al principio la siguiente lnea: DEFINT A-Z de esta forma le indicaba al BASIC que mi intencin era usar todas las variables del tipo Integer (entero), (realmente despus usaba del tipo que me daba la gana, pero mi primera intencin era no complicarme la vida con la mayora de las variables), cuando quera usar una variable diferente de Integer, le indicaba "explcitamente" de que tipo era y as me obliga a usar la mayora de ellas de este tipo que a la larga es o era el ms usado, ya que para hacer bucles (ya te explicar en un ratillo que es eso de los bucles y cmo hacerlos en VB) y otros clculos "normales", era ms que suficiente y en la mayora de los casos: ms rpido. En Basic, y por supuesto todava en Visual Basic, aunque cada vez va a menos, se puede indicar el tipo de una variable de varias formas, al declararlas con Dim, vimos que se haca de la siguiente forma: Dim unNumero As Integer Dim unNumeroLargo As Long Dim otroNumero As Single Dim masNumeros As Double Dim unNombre As String Dim multiUso As Variant Cada una de estas variables es de un tipo distinto, las cuatro primeras numricas, la quinta para almacenar cadenas de caracteres y la ltima del tipo por defecto del VB: Variant que como su nombre indica (aunque en ingls), es Variante y puede almacenar prcticamente cualquier cosa, objetos incluidos, (ya veremos los objetos en otra ocasin). Lo del tipo por defecto, es siempre que no se haya especificado un tipo determinado para todas las variables, por ejemplo usando el DEFINT A-Z, el tipo por defecto ya no es Variant, sino Integer. Al grano, "quesnoche", a lo que iba era que adems de declarar las variables de esta forma, tambin se puede hacer de de esta otra: Dim unNumero% Dim unNumeroLargo& Dim otroNumero! Dim masNumeros# Dim unNombre$ En el caso de Variant no existe un caracter especial para indicar que es de ese tipo, as que cuando quieras usar una variable Variant, tendrs que declararla como en el primer ejemplo. An queda otro carcter para otro tipo de datos numrico, el tipo Currency que se puede declarar con @. Este tipo ocupa 8 bytes y permite guardar nmeros de tipo moneda, es decir nmeros no enteros, pero con un nmero determinado y fijo de decimales, ahora no recuerdo, pero en la ayuda o en los manuales podrs ver la "retaila" de nmeros que cada tipo admite.

51

Para terminar con las recomendaciones de hoy, voy a indicarte algo que debes tener en cuenta cuando declaras variables y que an los ms expertos caen en la trampa. Adems de declarar las variables con Dim, poniendo cada declaracin en una lnea, cosa que por otro lado queda bastante claro y es como suelo hacerlo, aunque ltimamente estoy volviendo a coger malos hbitos... ser la edad? Tambin se pueden declarar ms de una variable con un mismo DIM, vamos a verlo con un ejemplo: Dim Numero As Integer, NumeroLargo As Long, otroNum As Single, Nombre As String, Numerazo As Double por supuesto tambin valdra de esta otra forma: Dim Numero%, NumeroLargo&, otroNum!, Nombre$, Numerazo# Y si me apuras, tambin de esta otra: Dim Numero%, NumeroLargo As Long, otroNum As Single, Nombre$, Numerazo# Pero sea como fuere, en todos los ejemplos se ha especificado el tipo que queremos asignar. Por supuesto tambin podremos declarar variables de esta forma: Dim unaVariable, otraVariable, terceraVariable Pero, surge esta pregunta de que tipo son estas tres variables? (al menos se espera que te surja...) La respuesta es bien sencilla, si se ha entendido toda la "retahila" que te he soltado anteriormente: Sern del tipo Variant o del especificado con el DEFINT A-Z (es decir Integer) Voy a suponer que la tercera recomendacin no la ests poniendo en prctica, por tanto seran del tipo Variant. Pero fjate que podras caer en el error, sobre todo si has programado algo en C, de pensar que esta lnea: Dim Numero, otroNumeroInt, elTercero As Integer o esta otra: Dim Numero As Integer, otroNumeroInt, elTercero estn declarando tres nmeros Integer y no es as, lo que se est declarando sera, en el primer caso: Numero y otroNumeroInt como Variant y elTercero como entero. en el segundo caso slo Numero sera del tipo entero y las otras dos variables del tipo Variant. Sera "ideal" que fuese como aparenta, pero el VB no hace estas "virgueras", ni incluso en la versin 5. Por tanto, cuando declares variables, fjate bien de que tipo son las que ests declarando, para no llevarte sorpresas, sobre todo con los redondeos y errores de desbordamiento... Un desbordamiento se produce cuando asignamos a un nmero un valor mayor del que est capacitado para almacenar, as si un entero slo acepta valores de +/- 32767 (realmente acepta hasta -32768), al asignarle un valor de 40000, nos dir que "turur" y dar error. En cuanto a que tipo de variable usar en cada caso, tendrs que tener en cuenta que quieres hacer. Normalmente en los bucles se suelen usar variables enteras, bien Integer, si sabemos que no nos vamos a pasar de 32767, bien Long Integer que puede almacenar un valor de dos mil y pico millones... (quien los tuviera, aunque fuese en calderilla!) Vamos a ver un ejemplo (al fin algo de cdigo se escucha entre el pblico...), con este cdigo podrs comprobar la velocidad de los bucles con los distintos tipos de variables y

52

as poder comprobar cual es la ms adecuada. Crea un nuevo proyecto y asigna unos cuantos Labels (6 en total) y un botn. Cuando ejecutes este programilla, puedes ir tranquilamente a tomar caf, porque se tomar su tiempo... En teora nos mostrar el tiempo que emplea en hacer unos bucles con tipos diferentes de datos. Para que sea fiable, debers especificar unos valores altos, ya que con nmeros pequeos no es demasiado fiable, e incluso con nmeros altos tampoco... la cosa era poner algo de cdigo para "rematar" el captulo de hoy... En la prxima entrega explicar las instrucciones que se han usado y en algunos casos, explicar hasta el por qu de usarlas. O sea esto es lo que se dice un programa intil que adems de consumir recursos del sistema y hacernos perder el tiempo, no vale para nada... (es que despus de probarlo, me he dado cuenta de que o todos los formatos son prcticamente igual de rpidos o yo he estado "engaado" durante todo este tiempo...) Option Explicit Private Sub Command1_Click() Dim nInt As Integer Dim nLng As Long Dim nSng As Single Dim nDob As Double Dim nCur As Currency Dim nVar As Variant Dim timer1#, timer2 As Double Const minBucle = 1, maxBucle = 10 Command1.Caption = "Calculando..." timer1 = Timer For nInt = minBucle To maxBucle Contar CInt(nInt), Label1 Next timer2 = CDbl(Timer - timer1) Label1 = "Duracin con Integer: " & timer2 DoEvents timer1 = Timer For nLng = minBucle To maxBucle Contar CInt(nLng), Label2 Next timer2 = CDbl(Timer - timer1)

53

Label2 = "Duracin con Long: " & timer2 DoEvents timer1 = Timer For nSng = minBucle To maxBucle Contar CInt(nSng), Label3 Next timer2 = CDbl(Timer - timer1) Label3 = "Duracin con Single: " & timer2 DoEvents timer1 = Timer For nDob = minBucle To maxBucle Contar CInt(nDob), Label4 Next timer2 = CDbl(Timer - timer1) Label4 = "Duracin con Double: " & timer2 DoEvents timer1 = Timer For nCur = minBucle To maxBucle Contar CInt(nCur), Label5 Next timer2 = CDbl(Timer - timer1) Label5 = "Duracin con Currency: " & timer2 DoEvents timer1 = Timer For nVar = minBucle To maxBucle Contar CInt(nVar), Label6 Next timer2 = CDbl(Timer - timer1) Label6 = "Duracin con Variant: " & timer2 DoEvents Command1.Caption = "Calcular" End Sub Private Sub Contar(valor As Integer, etiqueta As Control)

54

Dim i As Integer Dim unDoble As Double Const elMaximo = 1000& For i = 1 To elMaximo unDoble = unDoble + 1 etiqueta.Caption = valor * elMaximo + unDoble DoEvents Next End Sub Te espero en la prxima entrega. Y como te digo y seguir dicindote: espero que me mandes tus comentarios, para saber si slo lo estis leyendo t y alguien ms... es que si no me dices nada, me aburro y acabar por dejarlo...

Curso Bsico de Programacin en Visual Basic


Buenaaas, aqu estamos de nuevo (tu, yo y mi otro yo), empiezo con correcciones, ya sabes, fallos que tiene uno, y otras aclaraciones. El primero es un fallo garrafal, que gracias a Antonio Banderas, al que le tengo que agradecer tambin otras cosas, que espero que vaya en beneficio de los que estis leyendo todas estas cosas sobre el VB, (si, aparte de nosotros tres hay otros que siguen este curso). Bueno, a lo que iba, que siempre me despisto... en la segunda entrega, en el ltimo ejemplo de Num1, Num2, la asignacin: Num1 = Num1 + Num2, debera ser: Num1 = Num1 & Num2 (s que te habrs dado cuenta del detalle, sobre todo por la explicacin posterior), este "desliz" ya est corregido, as que si lo has ledo despus del 30 de Junio (del 97), ya est como tena que estar. La siguiente aclaracin, es un despiste, no demasiado grande, ya que doy por hecho de que algo habrs ledo sobre VB, bien los manuales, bien la Ayuda o algn libro sobre este lenguaje. Pues la cosa consiste que en la tercera entrega se crea un procedimiento: Contar, para crear procedimientos o funciones, tienes que usar el men Insert y seleccionar Procedure (esto en VB4), en VB3 si no recuerdo mal, estaba en el men "View" y en VB5 est en Tools (Herramientas) Bien, hechas estas aclaraciones, voy a explicar, tengo que hacerlo, ya que me compromet y... lo prometido es deuda o eso dicen, a saber... Antes del cdigo hice este comentario: O sea esto es lo que se dice un programa intil que adems de consumir recursos del sistema y hacernos perder el tiempo, no vale para nada... (es que despus de probarlo, me he dado cuenta de que o todos los formatos son prcticamente igual de rpidos o yo he estado "engaado" durante todo este tiempo...)

55

Y la cosa es que antes de hacer este ejemplo, yo crea que algunos tipos de datos eran ms rpidos que otros, (y a pesar de que el ejemplo demuestra, o casi, lo contrario, sigo creyndolo...). La cosa es que en 32 bits un Long debera ser ms rpido que el resto. Y los enteros ms rpidos que los de coma flotante... voy a probarlo en un 386 sin copro, a ver... ahora vuelvo... Ya puestos a probar, he probado, aqu se demuestra lo "morro" (o cabezn) que soy, y en esta tabla (pulsando en el link), tienes los diferentes valores en distintos equipos y con distintas versiones de VB. Vers que sorpresilla te llevas... Lo has visto? Dejar este tema, que ya es mucho lo que le he dedicado, vamos a ver el programa y as cambiamos de tercio... Para introducir cdigo en cualquiera de los eventos de los controles o del formulario, lo nico que tienes que hacer es seleccionar el control y el evento que queremos codificar de las listas desplegables, en el mdulo de cdigo, pulsando en Cdigo en la ventana en la que se muestra los mdulos y formularios que forma un proyecto. En la lista de la izquierda seleccionamos el control y en el de la derecha nos mostrar todos los eventos soportados por VB para ese control. Si sabemos el nombre del control y el del evento, podemos teclearlo directamente o bien si copiamos cdigo de otro sitio, simplemente con pegarlo, se va a su sitio. En el caso de querer aadir al cdigo, una funcin o procedimiento se puede hacer de varias formas, lo acabo de decir, pero lo repito un poco ms claro: 1. Directa: Escribir el cdigo directamente, con lo cual se crear un nuevo "apartado" en la lista de las funciones/ procedimientos. En caso de que no sea un evento soportado por los controles de nuestro formulario, se mostrar en la lista de la izquierda, estando seleccionada en la derecha "General" 2. Copiar/Pegar: Pues eso, si copias una funcin/procedimiento y lo pegas en la ventana de cdigo... 3. Por men de VB: Segn las distintas versiones de VB, ser un men u otro, debers especificar el nombre del procedimiento o la funcin, marcando la casilla correspondiente. En VB4/VB5 vers que aparte de los Procedimientos (Sub) y las Funciones (Function) hay tambin Propiedades (Property), estas las veremos en otra ocasin. Tambin vers que puedes declararlas Pblicas o Privadas. Esto no es posible en VB3, al menos en los procedimientos y funciones de los formularios. En otra ocasin veremos todas estas cosas y con ejemplos, que es lo que "mola". Bueno, toda esta "retahla" vena a cuento de cmo introducir cdigo en los eventos de los controles o del formulario y cmo crear nuestras propias instrucciones (esto es lo que ms me gust del QuickBasic 4.0, poder crear mis propias instrucciones (subs) y funciones). Ya es hora de coger el listado de la entrega anterior y "destriparlo". Vamos a ver cada cosa por separado, que aunque parezca que es mucho cdigo, realmente est "repetido", o casi...

56

Option Explicit Esto nos obliga a declarar todas las variables que usemos en el mdulo, ponerlo es una recomendacin, incluso te la impondra como norma. Para que salga de forma automtica en cada nuevo mdulo, selecciona del men Tools/Advanced la opcin Declare variables required (o algo parecido, que viene a significar Requiere la declaracin de variables) Siguiendo nuestro recorrido por el cdigo, no encontramos con: Private Sub Command1_Click() Lo de Private significa que slo se puede acceder a este procedimiento desde dentro del mdulo en el que est declarado. Es decir no se puede llamar desde otro form o mdulo BAS. Sub indica que es un procedimiento, no una funcin, ni una propiedad. Los Subs actan como instrucciones propias del lenguaje. Las funciones tambin, pero devuelven un valor, mientras que los subs no devuelven nada, lo que cogen se los quedan ellos, aunque en su momento veremos que tambin nos pueden dar algo a cambio. Command1_Click, este es el nombre que habr que usar para acceder a l desde cualquier punto de ste mdulo. Los parntesis sin nada dentro, indica que este procedimiento no recibe parmetros; los parmetros lo veremos dentro de un "ratillo" Toda esta lnea es la descripcin del procedimiento y cuando se le llame, bien desde nuestro propio cdigo, bien porque se pulse sobre el botn, se ejecutar todo lo que est dentro de l. El final del procedimiento est marcado por End Sub. Las lneas con DIM indican que estamos declarando las variables y lo que se especifica despus del AS es el tipo de variable, los cinco primeros son de cada uno de los tipos numricos soportados (otro da veremos otro tipo cuasi-numrico), el sexto es Variant, el multi-uso, el que vale para todo. Veamos ahora que es lo que se hace con esta lnea: Dim timer1#, timer2 As Double Aqu he declarado dos variables del tipo Double. Al separarlas con comas no hay que repetir la palabra DIM, pero s el tipo de cada variable. Ya vimos en la entrega anterior que algunos tipos de variables se podan indicar mediante unos caracteres especiales, (estos tipos son los heredados de versiones anteriores al Visual Basic 2, en esa versin, se introdujo el tipo Variant), en este caso # es lo mismo que Double, por tanto se podra haber hecho tambin de cualquiera de estas tres formas: Dim timer1#, timer2# Dim timer1 As Double, timer2# Dim timer1 As Double, timer2 As Double Ahora fjate que esta otra no hara la misma tarea: Dim timer1, timer2 As Double Esto funcionara con lenguajes como el C, (realmente el tipo se pone delante), pero en Basic no declara las dos variables como Double. La segunda variable si es Double, pero la primera es del tipo por defecto, en nuestro caso Variant. As que mucho ojito con las declaraciones de las variables. En algn sitio, no voy a decir dnde, porque lo mismo fue un "lapsus" del autor, pero deca que de esta forma declaraban tres variables de tipo Integer: Dim i, j, k As Integer Sigamos nuestra andadura, ahora veamos esta declaracin/asignacin: Const minBucle = 1, maxBucle = 10

57

Aqu lo que se declaran son dos constantes, stas a diferencia de las variables, no pueden cambiar de valor, de ah su nombre, por tanto permanecern siempre con el mismo valor. Cuando se declara una constante, no es necesario especificar el tipo, VB se encarga de adivinarlo y usar el tipo adecuado, realmente lo que hace es sustituir estas "palabras" por el valor que hay despus del signo igual. En caso de hacer esto: cNombre = "Una palabra". Visual Basic sabe que es una cadena de caracteres y cada vez que se encuentre con cNombre lo sustituir por "Una palabra". Ahora viene la explicacin del "por qu" usar constantes. Adems de "esclarecer" los listados, los hace ms fciles de comprender, tambin nos permite modificar un valor en un slo sitio, con lo que ganamos en "confianza", al asegurarnos de que no omitiremos alguno de los sitios dnde tengamos o queramos cambiar el valor antiguo por uno nuevo. En nuestro ejemplo, minBucle y maxBucle se usan en seis partes diferentes del procedimiento, si quisieras probar con otros valores, tendras que cambiar en seis sitios esos valores, pero al declararlos como constantes, slo cambiando el valor asignado, tenemos todo el trabajo hecho. Esto adems de ser ms fiable y legible, nos puede ahorrar algn que otro quebradero de cabeza y si adems le aadimos que no ocupan espacio extra, salvo en la tabla de smbolos, una vez compilado el programa slo se "compilarn" las constantes usadas. Sin embargo con las variables no ocurre esto, ya que aunque no se usen, ocupan memoria. Un inciso, esto de explicar tan detalladamente los listados, no va a ser norma, ya que al final todos nos aburriramos, slo lo har cuando lo crea conveniente o bien si lo solicitas, en este caso, no tendr ms remedio que cumplir tus deseos... Command1.Caption = "Calculando..." Cambiamos el texto mostrado en el botn, para avisarnos de que est haciendo algo... timer1 = Timer Asignamos el valor de la funcin Timer a la primera de las dos variables que usaremos para calcular el tiempo empleado por cada uno de los bucles. Esta funcin devuelve el nmero de segundos transcurridos desde la media noche. For nInt = minBucle To maxBucle Esto es un bucle, que se repetir desde minBucle (1) hasta maxBucle (10) y la variable nInt es la que llevar la cuenta o la que se usar para saber el valor actual del bucle. Deberamos ver primero la declaracin del procedimiento Contar, para entender lo que hace la lnea que viene despus del For. Private Sub Contar(valor As Integer, etiqueta As Control) Declaramos un procedimiento privado llamado Contar (acta como una instruccin ms del VB, ya que no representa a ningn control ni evento), entre los parntesis estn declarados los dos parmetros que espera recibir en la llamada, el primero es un nmero entero y el segundo (separado por una coma), un Control, que puede ser cualquier control de VB. El resto del procedimiento ahora no es demasiado significativo Ahora veamos esta lnea: Contar CInt(nInt), Label1 Contar es el nombre del procedimiento y a continuacin se deben indicar los parmetros que espera recibir. En este caso no sera necesario CINT ya que lo que hace esta funcin es convertir el nmero que se pone dentro de los parntesis en un nmero entero y como resulta que nInt es un nmero entero... pues no hay nada que convertir! El segundo parmetro es el control Label1, ya sabes que tenemos 6 etiquetas en nuestro programa desde Label1 a Label6

58

Cuando llegue estos datos al procedimiento Contar, valor tomar lo que valga nInt y etiqueta se aduear de Label1. Next Contina repitiendo el bucle hasta que se alcance el valor mximo, realmente el Next lo que hace es lo siguiente: nInt = nInt + 1 Es nInt menor o igual que maxBucle? Si la respuesta es SI, sigue con lo que haya despus de la lnea FOR, en caso negativo contina con la lnea siguiente al Next (realmente en la siguiente instruccin despus del Next, ya veremos esto en otra ocasin) timer2 = CDbl(Timer - timer1) Asignamos a la segunda variable que usamos para el clculo del tiempo la diferencia entre el nuevo valor de los segundos transcurridos desde la media noche (Timer) y el valor que tena timer1, es decir cuantos segundos... antes de empezar el bucle. El CDBL es una fucin que devuelve un valor Doble. Es decir hace la resta y ese valor resultante lo convierte en doble. Label1 = "Duracin con Integer: " & timer2 Esta asignacin realmente es Label1.Caption, si se omite la propiedad, Visual Basic usa la que tiene por defecto, que segn los manuales, suele ser la propiedad que se usa con ms frecuencia. En este caso el Caption, es decir lo que se muestra en la etiqueta. DoEvents Esta es una instruccin "controvertida" y a la que muchos programadores no les hace demasiada gracia usar, no porque no tenga su utilidad, sino porque hay que saber realmente lo que hace y tener cuidado cuando la usamos, ya que algunas veces puede crear errores o confusin... realmente no es tan drstico, pero casi... DoEvents, detiene la ejecucin del programa y devuelve el control a Windows, para que ejecute los mensajes que tiene pendientes en la cola de mensajes... ? no te preocupes si no te enteras, es as y punto. por qu la uso? Pues para dar tiempo a que se vea el contenido del Label; prueba a quitarla y vers lo que ocurre, o debera ocurrir... que ya a estas alturas no me sorprendera nada que se mostrara... El resto del programa es idntico a este bucle, pero usando distintas variables y las dems etiquetas. El caso es que Contar espera una variable de nmero entero y un control, en el caso del control es obvio que se estn poniendo las distintas etiquetas y en el caso del nmero se convierte a entero, porque eso es lo que espera nuestra instruccin y si no lo hacemos as, se quejar. Ya slo queda ver una lnea del procedimiento Contar: etiqueta.Caption = valor * elMaximo + unDoble unDoble contar desde 1 hasta elMaximo, en cada vuelta del bucle, se asignar al caption de la etiqueta pasada al procedimiento y el DoEVents que viene a continuacin se encargar de que se muestre ese contenido. Bueno, tambin se asigna valor * elMaximo, es decir que cuando valor valga 1, se estar mostrando 1000 + unDoble, realmente para hacer un contador se tendra que haber usado lo siguiente: etiqueta.Caption = (valor -1) * elMaximo + unDoble, para que mostrara desde el 1, en lugar de empezar desde el 1001. Una vez que Contar termina, por el End Sub, vuelve el control al bucle que lo llam y se ejecuta la siguiente instruccin. Por tanto Contar se llamar tantas veces como dure el bucle en el que se encuentra.

59

Creo que queda todo ms o menos claro y aunque este cdigo no es muy til por s, ha servido para ver algunas cosillas del VB. Para terminar vamos a ver una serie de cambios y a ver si adivinis que es lo que hace... as os servir de ejercicio, cosa que algunos me habis pedido, pero que an no es el momento de hacerlos. En las declaraciones generales aade esta declaracin: Dim Contando As Integer En Contar, aade lo siguiente despus del DoEvents: If Contando = 0 Then Exit For Al principio del Command1_Click, aade estas lneas: If Contando Then Command1.Caption = "Calcular" Contando = 0 DoEvents Exit Sub End If Contando = 1 En cada uno de los bucles, pon esto despus de llamar a Contar... If Contando = 0 Then Exit Sub Y antes del End Sub aade esto otro: Command1.Caption = "Calcular" Contando = 0 Bueno, ah dejo esto y como ejercicio podras aadir dos controles TextBox para especificar los valores de maxBucle y elMaximo, de forma que segn los valores introducidos, que por defecto deben ser 10 y 1000, se usen los que especifiques en cada TextBox y se tengan en cuenta cuando pulsas (haces click) en el botn. Como pista te dir que las variables usadas y/o declaradas dentro de un procedimiento son slo visibles dentro de ese procedimiento. No te quejars del "pedazo" de pista que te he dado... A disfrutar y hasta la prxima entrega... cuando? Eso ni se sabe... Y ya sabes que espero tus comentarios sobre el cursillo este de marras... Nos vemos.

Curso Bsico de Programacin en Visual Basic


60

Hoy no voy a pasar lista, porque me imagino que los que faltan estarn aprovechando el puente este de Santiago o estarn preparando las maletas para irse de vacaciones... que suerte! Quin 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 lmpiate las babas... para tu consuelo te dir que an no he pisado la arena de la playa... O 8-| Vale, vale, tambin puedes irte al campo con los mosquitos... se nota que me gusta ms 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 cdigo, que es lo que te ha trado a esta pgina. Hoy no tengo nada que rectificar de la entrega anterior... salvo varios errores tipogrficos... De todas formas si lees estas entregas y hay posteriores, sera conveniente que fueses a la siguiente y te leyeras el principio, ya que aparte de poner algn 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 solucin 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 cmo funciona el Visual Basic y por extensin todas las aplicaciones de Windows. En la segunda entrega creamos un programa que mostraba un form en el que tenamos una caja de texto (TextBox), un botn (CommandButton) y dos etiquetas (Label). Cuando, despus de pulsar F5 o CRTL+F5, se ejecuta la aplicacin, de lo nico que podemos tener certeza es que se ejecutar el cdigo 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 cdigo se va a ejecutar, tengo que seguir con mi 'ponencia' de cmo 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 ratn, pulsamos una tecla, tocamos una ventana o cualquiera de los controles que estn 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 rpida, aunque en algunas ocasiones no tanto como nos hubiera gustado), que se ha movido el ratn y que el ratn ha pasado por encima de tal ventana o de ese control y as con todas las cosas. La verdad, no es de extraar que de vez en cuando falle el sistema, es que no para de disparar!!! y algunos disparos se le puede escapar y... ...que chiste ms malo, verdad? Ya pensabas que el comentario ese del 'fire' era porque "el Guille se cree que est traduciendo un artculo 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 cdigo que se va a ejecutar. No debes 'planificar' tu programa dando por sentado que... "despus de esto el usuario 'tiene' que hacer esto otro y as yo podr hacer una comprobacin para..." NO ! Aqu (en Windows) no existe la programacin lineal, no des nunca por hecho que esto es lo que va

61

a ocurrir..., porque casi con toda seguridad no ocurrir! Veremos cmo podemos 'controlar' que algunas cosas se hagan cuando nosotros queramos, pero esto ser slo cuando el usuario de nuestra aplicacin realice una 'accin' que estaba prevista; tambin codificaremos para que se ejecute parte del cdigo cuando el usuario no haga lo que habamos 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 mdulo BAS, (de debe llamarse obligatoriamente Main); los mdulos los veremos en otra ocasin. Normalmente las aplicaciones suelen tener ms de un formulario y algn que otro mdulo. 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 slo 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 difcil 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 slo le est avisando a la nuestra que la accin ha empezado, sino que lo hace prcticamente a los cuatro vientos; si otra aplicacin quiere enterarse de lo que ocurre en Windows, slo tiene que conectarse a la 'mensajera' de ste entorno operativo y leer las 'noticias'... pero esto ya es complicarse la vida y todava no nos la vamos a complicar tanto... o ms... (pensar alguno despus 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 pblica, 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 cdigo de inicializacin. 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 despus 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 caas: nada. Slo ocurrir algo cuando interactuemos con el form, es decir, le demos razones a Windows para 'pegar tiritos'. Nuestra aplicacin, (te recuerdo que tena, entre otras cosas, un textbox y un botn), 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 cdigo. En la parte superior de la ventana hay dos listas desplegables, la de la izquierda tiene todos los controles, (en otra ocasin veremos que no siempre es as), que tenemos en el form, incluido ste, adems de uno especial que se llama General. Pulsa en la lista de la izquierda, para que se despliegue y te mostrar esto:

62

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 estn 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 ocurrirn por distintos motivos, cada uno tiene su 'tarea', nos pueden avisar de que se ha pulsado el ratn, que se acaba de pulsar una tecla, que se ha modificado lo que antes haba, etc. Una llamada a la precaucin. Los eventos son 'procedimientos' y no slo se puede llegar a ellos porque se produzca una accin del usuario o del propio Windows y si me apuras, incluso de Visual Basic... Nosotros podemos 'provocarlos' cmo? 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 vacas tanto al Label2 como al Text1: Label2 = "" Text1 = "" Cuando VB asigna una cadena vaca (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 cdigo para manejar esta situacin, as que... hace la vista gorda y simplemente cambia el contenido del Label2.Caption sin hacer nada ms. Pero al asignar una cadena vaca al Text1, tambin se borra el contenido y se produce un Change, slo 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 cdigo para este evento y sabe que tiene que hacer lo que all se dice... En este caso, nuestro cdigo se ejecuta, pero realmente no hace nada de inters o por lo menos nada que se pueda apreciar de forma visual. No voy a explicar para que sirve ese cdigo, porque ya lo hice en su da, lo que espero es que hoy lo entiendas mejor... Cmo? que no sabes de qu cdigo estoy hablando... pues del ejemplo de la segunda entrega, creo que ya lo dije al principio... a ver si prestamos ms atencin y dejamos de pensar en las vacaciones... estos nios! El cdigo interesante es el que se ejecuta cuando se pulsa en el botn: Label2 = "Hola " & Text1 Aqu se asigna al Caption del Label2 lo que hay despus del signo igual, en este caso acta 'casi' como una variable. Y ya sabrs que antes de asignar un valor a una variable, se procesa todo lo que est despus 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 asignramos a una variable... y si en lugar de escribir nuestro nombre, escribisemos un nmero... y si la variable en la que guardamos ese nmero se llamara, por ejemplo, maxBucle o elMaximo... Pues que tendramos 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 aadirle un par de cajas de textos y un par de etiquetas, para que indique lo que se debe introducir en cada textbox; el aspecto sera este:

63

Pero nos encontramos con un problema: cmo puedo asignar un valor a maxBucle, si las constantes no pueden cambiar de valor? Fcil, convirtela 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 slo 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 recomendacin 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-fcil 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 intencin que tiene, es decir que me dirijo a ms de un "ti"... ya s que no eres tan torpe como para no haberlo 'captado', pero con esta aclaracin me quedo ms tranquilo.) Segn cmo y dnde se declare una variable, su 'visibilidad' o rea de cobertura, ser diferente... tambin 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 mdulo (FRM, BAS, etc.) --Pblica o Global a nivel de aplicacin (en este tipo hay una forma especial de usar las variables que veremos en otra ocasin) Explicando los dos primeros puntos. Cuando declaramos o dimensionamos una variable 'dentro de' un procedimiento, sta slo 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 'obligacin' de declarar todas las variables, te llevars 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 botn.. Abre la ventana de cdigo, borra el Option Explicit.

64

En el Form_Load haz esta asignacin: Incredulo = "No me lo creo" En el Command1_Click escribe esto otro: Text1 = Incredulo Pulsa F5 y haz click en el botn... 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 condicin es que no pueden estar juntas, aunque hay un truco para juntarlas sin que ellas se enteren... En este ltimo ejemplo, nuestra intencin es tener una variable que sea 'conocida' en todo el form. Cuando necesitemos variables con un mbito a nivel de mdulo, tenemos que declararla o dimensionarla en la seccin de las declaraciones generales; ya sabes, en la lista izquierda de la ventana de cdigo seleccionas General y en la de la derecha Declarations ( Declaraciones). Muestra la ventana de cdigo 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 botn 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 versin 4 del VB, entra en juego una nueva palabra, 'Private', que suele usarse en las declaraciones de las variables a nivel de mdulo, de esta forma es ms fcil entender la intencin de la misma; por tanto la declaracin anterior podemos hacerla tambin as: Private Incredulo As String Hay 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 prrafo, cosa que ya he comentado antes, podemos tener distintas variables con el mismo nombre en distintos procedimientos; para ello, slo hay que dimensionarlas y el VB las almacenar en distintas posiciones de la memoria para que no se 'mezclen'. En la entrega anterior, tenamos un procedimiento llamado Contar que se usaba para eso, contar... En este ejemplo vamos a usar un sub tambin llamado contar, para ver en accin todo esto que estoy diciendo. Sitate en la parte General/Declarations de la ventana de cdigo y escribe o "copia/pega" lo siguiente: Private Sub Contar() Dim i As Integer

65

For i = 1 To 2 Print "i en contar= "; i Next End Sub Ahora 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 Next Ejecuta 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 cambisemos 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, slo son visibles en ese procedimiento. Si has usado QuickBasic o el compilador Basic de Microsoft (que usaba el QuickBasic Extended QBX), esto ya exista y tambin exista 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 declarndola en la seccin 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 Sub Aqu hacemos que Contar reciba un parmetro y el valor que recibe lo usamos como el lmite 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 parmetro i. Por tanto cada vez que se llame a este procedimiento le estamos diciendo que i es el valor mximo que tomar el bucle For que tiene dentro. Cmo reaccionar? Se confundir? ...

66

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 nmero. Lo nico que necesita y espera, es recibir un valor numrico (del tipo Integer) y lo asignar a la variable j. Por tanto no ocurrir nada extrao. Ejecuta el programa y fjate en lo que ocurre. S que lo has deducido, eso est bien... vas aprendiendo... 8-) Cmo? Que t an no lo has captado? Pues dmelo... (mejor no me lo digas y repsalo de nuevo...) Otra cosa sera pretender usar una variable declarada a nivel de mdulo, dentro del procedimiento, que tuviese el mismo nombre. Si te has quedado 'con la copla', tu mismo sabrs la respuesta... Efectivamente! Si dentro de un procedimiento tenemos una variable dimensionada con el mismo nombre de una declarada a nivel de mdulo o a nivel global, (para usarla en cualquier sitio de la aplicacin), tendr 'preferencia' la variable local... sta 'tapar', ocultar o como prefieras decirlo a cualquier otra variable del mismo nombre... En la prxima entrega veremos ms casos y cosas de las variables. Comprobaremos cmo usarlas a nivel Global. Pero por ahora vamos a terminar con el programa que tenamos planteado en la entrega anterior: Aunque realmente deberas saber cmo solucionarlo... Lo que seguramente no sabrs, es cmo 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 sera usar variables locales, esta decisin no 'justifica' el 'pedazo' de pista que te di... pero esto es lo que hay. La lnea Const minBucle = 1, maxBucle = 10. Debe quedar as: Const minBucle = 1 Dim maxBucle As Integer Esto har que maxBucle deje de ser una constante y pase a ser una variable, con lo cual podremos asignarle cualquier valor. Pero, cmo 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 dnde? Pues... por ejemplo, despus de la declaracin, as que en la siguiente lnea al Dim maxBucle... escribe los siguiente: maxBucle = Text1 Esto en teora no dara 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 nmero 'intentar' convertir al tipo de la variable que recibir el valor. Esto no siempre funciona, sobre todo si el contenido del Text1 no es numrico. Por ahora vamos a hacerlo simple, si el usuario (en este caso t), escribe algo no numrico lo vamos a considerar CERO... o casi... Cambia la asignacin anterior por esta otra... ALTO !!! Antes de hacerlo, prubalo e intenta escribir una palabra en lugar de un nmero... que ocurre?

67

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 numrico 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, slo que esta vez dentro del procedimiento Contar, por tanto la declaracin Const elMaximo = 1000&, la tienes que quitar y poner estas dos lneas: Dim elMaximo As Integer elMaximo = Val(Text2) Aqu el nico inconveniente es que esta asignacin se hace cada vez que se entra en este procedimiento... y eso, amigo mo, no es un buen estilo de programacin... Sobrecargamos de forma innecesaria al procesador... ten en cuenta que la conversin a nmero y la asignacin se ejecuta cada vez que se entra en Contar!!! Lo mejor para este caso sera declarar elMaximo como variable a nivel de mdulo. Por tanto, borra el Dim elMaximo... del sub Contar y colcalo en la parte de las declaraciones generales del form. Ahora... dnde asignamos el valor para evitar la sobre-carga? Ya que tenemos la variable a nivel de mdulo, sta ser 'vista' en todos los procedimientos del formulario, por tanto lo lgico sera hacerlo en el Command1_Click, ya que cuando nos interesa a nosotros saber cuanto tenemos que contar es precisamente cuando pulsamos en el botn... Pero... dnde exactamente?, despus de Contando = 1 Bien, ahora est la cosa mejor... haz tus pruebas y si an no lo tienes claro... pregntame, (te digo lo de antes: mejor no me preguntes y repsalo todo de nuevo... ) Prcticas y ejercicios Quieres algo para practicar? Este ejercicio se lo pona a mis alumnos, cuando daba clases de BASIC, hace ya unos 10 aos o ms... y consista en pedir el nombre, pedir la edad y mostrar el nombre tantas veces como aos tengamos... Claro que con el BASIC del MS-DOS era ms directo y se sabia cuando se deba empezar a mostrar el nombre, para solventar esto, se mostrar el nombre 'edad' veces cuando se pulse en un botn. El aspecto del form sera algo as:

No creo que sea complicado, as que vamos a complicarlo un poco ms: Mostrar el nombre 'edad' veces, dentro de un label, para ello el label deber ocupar la parte izquierda del form.

68

Y una tercera versin, sera lo mismo que esta ltima, pero cada vez que se muestre el nombre se haga en una lnea diferente. La pista: En la segunda entrega vimos de pasada el CHR. Pues decirte que si aadimos a una variable un CHR(13), lo que hacemos es aadirle un retorno de carro, es decir lo que venga despus se mostrar en la siguiente lnea... siempre que se 'concatene'. Tambin existe una constante definida en VB4 o superior que es vbCrLf, esto es un retorno de carro (Cr) y un cambio de lnea (Lf) Que te diviertas!

Como ya es costumbre al final de cada entrega, espero tus comentarios y opiniones sobre el curso, adems de aceptar crticas, 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 prxima entrega ser... pues, es que... yo creo que... si no... un da de estos, pero seguro que en el caluroso mes de Agosto. Nos vemos.

Curso Bsico de Visual Basic Solucin de Ejercicios

He preferido poner la solucin a los ejercicios en una pgina separada, as creo que ser mejor. Solucin al primero, preguntar el nombre, preguntar la edad y mostrar el nombre "edad" veces: Tenemos dos TextBoxes: Text1 y Text2, un botn: Command1, el cdigo sera:
Private Sub Command1_Click() Dim i As Integer Dim j As Integer Dim Nombre As String j = Val(Text2) Nombre = Text1 For i = 1 To j Print Nombre

69

Next End Sub

El segundo es un poco ms complicado, pero no tanto, espero. Slo hay que asignar el nombre al Label, suponiendo que fuese Label3, sera algo como esto: Private Sub Command1_Click() Dim i As Integer Dim j As Integer Dim Nombre As String j = Val(Text2) Nombre = Text1 For i = 1 To j Label3 = Label3 & Nombre Next End Sub Por ltimo, para que cada nombre se muestre en una lnea diferente, hay que aadirle a continuacin un retorno de carro y cambio de lnea, en VB4 hay una constante definida para ello: vbCrLf, en el VB3 habra que declararla de esta forma: Dim vbCrLf As String vbCrLf = Chr$(13) & Chr$(10) Este sera el cdigo: Private Sub Command1_Click() Dim i As Integer Dim j As Integer Dim Nombre As String j = Val(Text2) Nombre = Text1 For i = 1 To j Label3 = Label3 & Nombre & vbCrLf Next End Sub Espero que te hayas apuntado un 10, pero si no has logrado hacerlos bien, no te preocupes, poco a poco irs quedndote con la copla.

70

Curso Bsico de Programacin en Visual Basic


Ya sabes que mi segundo nombre es: DESPISTE, si no lo sabas, ahora lo sabes... Y es que con tanta explicacin de la Quinta Entrega, al final expliqu cosas, pero no todo. Al final de la Cuarta Entrega se hicieron una serie de cambios al programa ese de los bucles. La explicacin de la utilidad de todo ese cdigo es, en trminos generales, para cancelar en cualquier momento el programa. Cmo? Hemos declarado Contando a nivel de mdulo, por tanto ser visible en todo el form. Cuando se declara una variable, el VB le asigna siempre un valor predeterminado, en caso de las cadenas de caracteres (string) ese valor es "" (cadena vaca); a los nmeros se le asigna un valor CERO. Por tanto, al iniciar el programa, la variable Contando valdr CERO, as que la primera vez que se pulse en el botn Command1 y se compruebe esto: If Contando Then... Ah, si... no he explicado para que sirve esto del If...Then... Las instrucciones If/Then se usan para comprobar si la 'expresin' que se usa despus del IF se cumple o no... En caso de que se cumpla (sea cierta), se ejecutar todo lo que haya despus de THEN... algo as: IF <EXPRESIN> THEN <INSTRUCCIONES> Tambin puede usarse de esta otra forma: IF <EXPRESIN> THEN <INSTRUCCIONES> [<MS INSTRUCCIONES>] END IF En la primera forma de uso, las instrucciones se ponen a continuacin de THEN como en el caso de:

If Contando = 0 Then Exit For

En la otra forma de usarlo, las instrucciones se ponen en las siguientes lneas y podemos escribir tantas lneas como queramos. Todas se intentarn procesar... Esto para el caso de que al evaluar la expresin se cumpla como verdadero. En las ocasiones en las que no se cumple la expresin se har lo siguiente, segn la forma de usar el IF/THEN: --En el primer mtodo, se procesan las instrucciones que hay en la siguiente lnea y se contina a partir de ah el programa. --En el segundo caso, se busca END IF y se contina por la siguiente lnea...

71

Ms adelante, en otra entrega, veremos otras formas de usar IF...THEN... Ahora voy a explicar un poco esto de la evaluacin de las expresiones. El IF espera encontrar un valor CERO (FALSO) o un valor distinto de cero (por extensin VERDADERO) En If Contando = 0 Then Exit For, la expresin es Contando = 0, aqu no se est usando la asignacin, sino que se est evaluando si el contenido de la variable Contando es igual a cero; en caso de que sea cierto, es decir que Contando valga cero, (algunas veces me maravillo de mi lgica aplastante...), se devuelve un valor Verdadero (TRUE). Por otra parte si Contando NO vale cero, se devolver un valor Falso (FALSE, CERO). En otros lenguajes se usa un smbolo distinto para el igual, segn sea una asignacin o una comparacin. En Pascal (Delphi) la asignacin es := y la comparacin es = En C, C++, la asignacin es = y la comparacin == (esto del == puede 'sonarte' si has hecho algo con JavaScript) En el caso de If Contando Then, al no haber una expresin que evaluar, lo que se comprueba es si el contenido de la variable es cero o no es cero, en caso de que sea NO CERO, se cumple la condicin y se procesa todo lo que viene a continuacin; pero si la variable vale CERO, el IF lo interpretar como FALSO y pasa a ejecutar lo que haya a continuacin de End If. Por tanto, (volviendo por fin al listado), la primera vez que se pulse en el botn Command1 y se compruebe esto:

If Contando Then
No se cumplir la condicin, ya que el contenido de Contando es cero, y se pasar a lo que hay despus del End If, es decir a: Contando = 1 y se contina con el programa como antes de aadir todas estas cosas... Pero, si pulsas de nuevo en el botn, se vuelve a procesar lo que hay dentro de Command1_Click, (esto es posible porque al usar DoEvents, permitimos que Windows siga recibiendo y procesando 'tiritos'), pero cuando entramos por segunda vez, Contando vale 1 (uno), (ya que al ser una variable a nivel de mdulo conserva el ltimo valor que hayamos asignado), y esta vez al evaluar: If Contando Then si se cumple, as que se procesan las lneas que vienen a continuacin... entre ellas est Contando = 0 y DoEvents que vuelve a permitir a Windows que las otras cosas que antes de pulsar en el botn continen ejecutndose, (esto se hace de forma asncrona, es decir, Windows se da por enterado y da los avisos (mensajes) cuando quiera), pero contina con la siguiente instruccin sin esperar a que esos mensajes se terminen de procesar, entonces se encuentra con el Exit Sub que lo manda fuera del evento... (Si no te ha quedado demasiado claro, no te preocupes veremos ms de esto a lo largo del curso...) En caso de que hayamos pulsado el botn cuando an no haba terminado todo lo que este Sub estaba haciendo, se contina igual que si se hubiese hecho PAUSA y despus PLAY. Con la salvedad de que si VB se encuentra con alguno de los If Contando = 0 Then Exit Sub, dejar de procesar y se saldr del procedimiento... Esta no es la mejor forma de cancelar una tarea ya iniciada, pero algo es algo... Tambin es posible que al pulsar por segunda vez en el botn, se estuviese dentro del Sub

72

Contar, en este caso, tambin se evaluara la expresin y se saldra del procedimiento... as que tambin dejara de procesarse todo. Cuando pulsemos por tercera vez... iniciaremos el proceso de nuevo...

Bueno, ahora si que puedo dar por terminada la Quinta Entrega. Nos vemos.

Curso Bsico de Programacin en Visual Basic


Sexta Entrega: 11/Ago/97. por Guillermo "guille" Som Si quieres linkar con las otras entregas, desde el ndice lo puedes hacer

Solucin de los ejercicios de la Quinta Entrega Pulsa este link para ver la solucin de los ejercicios de la quinta entrega (aunque el nombre la pgina sea: basico06_sol) Como hemos visto en el apndice de la entrega anterior, la instruccin IF... THEN... nos permite tomar decisiones segn el valor de una variable o el resultado de una expresin. En esta entrega veremos como sacarle rendimiento a esta instruccin. Pero antes de entrar en detalles, veamos cmo podemos decirle al Basic que haga las cosas. En realidad vamos a ver la forma en que se le puede decir que las haga... Forma de especificar las instrucciones en Visual Basic Las instrucciones en Basic no tienen porqu estar cada una en una lnea. Se pueden escribir varias instrucciones en la misma lnea, pero separando cada una de ellas con el signo : (dos puntos). Cuando VB encuentra los dos puntos, deja de 'interpretar' la instruccin y pasa a la accin, una vez traducido a su lenguaje interno, toma lo que hay despus del signo : y sigue su camino en busca de ms instrucciones o el final de la lnea. Vemoslo de forma prctica: Nombre = "Pepe" : Print Nombre Esta lnea tiene dos instrucciones: una asignacin y una instruccin Print. Podemos poner cuantas instrucciones queramos, separadas con los dos puntos. Pero, (siempre hay un pero), si una de las instrucciones es el IF/THEN la cosa puede cambiar...

73

Ya vimos que IF comprueba la expresin que viene a continuacin, si es cierta, ENTONCES procesa lo que haya despus de THEN. En caso de ser en la misma lnea, interpretar todas las instrucciones que estn a continuacin; en caso de ser un bloque IF... THEN... END IF, ejecutar todo lo que est dentro de ese bloque. Ahora bien, si la expresin es falsa pasa a la siguiente lnea, tanto si es o no un bloque. En el caso del bloque la siguiente lnea a interpretar ser la que est despus de END IF. En los tiempos del BASIC interpretado de MS-DOS, era habitual encontrar las lneas con varias instrucciones separadas por dos puntos. En mi caso, cuando empec a usar el QuickBasic 2.0 y al poder usar bloques IF... THEN... END IF, fui dejando a un lado el "mogolln" de instrucciones en la misma lnea... Ahora, salvo en contadas excepciones, escribo cada instruccin en una lnea. Y te recomiendo que hagas lo mismo, tu cdigo ganar en claridad y si alguna vez vuelves a verlo, te ser ms fcil de entender. Despus de este pequeo respiro, veamos cmo estara formada una lnea de VB para usar con un IF... THEN... [instrucciones:] IF <expresin> THEN <instrucciones si es cierto [:ms instrucciones...]> A continuacin de THEN podemos incluir cuantas instrucciones queramos, separadas por dos puntos. Estas slo se ejecutarn cuando la expresin sea cierta. Si el resultado de la expresin es falso, se obvia 'todo' lo que hay despus de THEN y se pasa a la siguiente lnea. Espero que lo hayas asimilado y que no te indigestes con lo siguiente... Pero, (...), existe otra instruccin que PUEDE acompaar al IF... THEN... y es para los casos en los cuales el resultado de la expresin sea FALSO. Si, ya s que dije que cuando es falso se pasa a la siguiente lnea, pero eso es cuando no se usa la clusula ELSE. Con sta, la definicin de la instruccin "tomadora de decisiones" quedara as: IF <expresin> THEN <si se cumple> ELSE <si no se cumple> Tanto en <si se cumple> como en <si no se cumple> pondremos tantas instrucciones como queramos, (separadas por dos puntos). Pero no te recomiendo que lo hagas, es preferible, al menos para darle "claridad" a nuestro cdigo, usar el bloque: IF <expresin> THEN <si se cumple> ELSE <si no se cumple> END IF S que esto puede ocupar ms lneas de cdigo, pero nuestro "coco" lo agradecer, ya que es ms fcil de comprender, sino veamos un ejemplo:

74

IF numero > limite THEN Print "tu nmero es grande" ELSE Print "OK, McKey!" END IF Ahora vemoslo en una sla lnea: IF numero > limite THEN Print "tu nmero es grande" ELSE Print "OK, McKey!" En este ejemplo, an queda claro, pero lo podramos complicar con ms instrucciones para ambos casos, es decir, para cuando la expresin es cierta y tambin cuando es falsa. En los tiempos del BASIC que venan incorporados con los ordenadores, cada lnea del programa haba que numerarla, ya que todo lo que se escriba sin nmero de lnea, se ejecutaba inmediatamente; al igual que ocurre con lo que se escribe en la ventana Inmediate del Visual Basic. Los nmeros de lneas se usaban, adems de porque era obligatorio, para cambiar el orden de ejecucin, sobre todo despus de una comparacin. De esta forma, an sin tener bloques IF/THEN/ELSE/END IF, se podan simular. Cmo se lograba? Usando una instruccin que muchos creen que es "indecente, antisocial, etc."

La estrella del Basic: (redoble de tambores) "GOTO"


A partir de hoy, ms de un purista de la programacin no me dirigir la palabra... pero no me importa... Se ha "denostado" (injuriado) con exageracin el uso de esta instruccin. Realmente es una instruccin de "bifurcacin", es decir, sirve para "IR A" otro sitio de nuestro programa. Su uso ha sido el ms criticado de los NO PARTIDARIOS del Basic y siempre han basado sus crticas en el exagerado uso del GOTO en todos los programas Basic. Pero aclaremos que C tambin tiene esta instruccin y que cualquier programa en cdigo mquina (ensamblador) est "plagado" de JMP que para el caso es lo mismo que el sufrido GOTO, realmente una instruccin GOTO nmero_linea se convierte en JMP nmero_linea. No voy a recomendar el uso del GOTO, para nada, ya que hoy da es innecesario su uso. Antes no tenamos ms remedio, porque el BASIC no dispona de instrucciones para poder estructurar el cdigo. Pero sera una tontera querer hacer creer que no existe esta instruccin. Sabiendo que est y cmo podemos evitarla, es preferible a decir que no existe y si por casualidad la descubres... a que la uses. Por tanto, insisto en mi recomendacin, (de esta forma los PURISTAS volvern a dirigirme la palabra), NO USES EL GOTO, ni an cuando creas que no tienes ms remedio... aunque, aqu entre nosotros, algunas veces es ms cmodo usarlo... pero que no se entere nadie... Este es un programa de los de antes, sirve para mostrar en pantalla los nmeros del 1 al 10 y sin usar el FOR/NEXT 10 A = 1 20 Print A

75

30 A = A + 1 40 IF A <= 10 THEN GOTO 20 'Con el Commodore este programa se sola escribir as: 10 A=1 20 PRINTA:A=A+1:IFA<=10GOTO20 'Sin ESPACIOS NI NADA... TODO APELMAZADO... que ms daba usar el GOTO? Imagine there's no heaven... (es que est sonando J. Lennon... un momento...) En este ejemplo, es obvio que podramos sustituirlo con: 10 For A = 1 To 10 20 30 Next El NEXT hace lo mismo que la asignacin y la comparacin. Pero hay otras maneras, para ello existe una serie de instrucciones que funcionan de manera similar, veamos otros ejemplos con ms instrucciones para hacer bucles, seguir usando los nmeros de lnea por aquello de la "nostalgia", pero salvo en el primer ejemplo, en los dems no es necesario. 10 A = 1 20 While A <= 10 30 40 Print A A = A + 1 Print A

50 Wend El WHILE/WEND ya casi ni se usa, porque tienen un sustituto ms verstil, ahora lo veremos, pero el uso sera: WHILE <expresin> <instrucciones si se cumple> WEND Es decir, MIENTRAS la expresin sea cierta, repite todo lo que haya hasta el WEND. Por supuesto podemos ponerlo todo en una sola lnea: 10 A = 1 : While A <= 10 : Print A : A = A + 1 : Wend Pero esto tampoco es recomendable, queda algo "difuso"... El WEND funciona como IF A <=10 THEN GOTO xxx, con la ventaja que evitamos el GOTO y lo que hace es comprobar si la expresin que hay tras el WHILE es cierta o no, en caso de que sea verdadero el resultado, (ya sabes, distinto de CERO), se ejecutar todo lo que

76

hay entre WHILE y WEND. Estas instrucciones ya existan en el GWBASIC (el basic de los PCs) Hay que tener cuidado con esto de que la expresiones evalan el cero como FALSO y cualquier otro valor como VERDADERO, por ejemplo: A = 1 While A Print A A = A + 1 Wend Wend o While 1 Print A A = A + 1

En ambos casos el bucle sera "infinito", realmente se detendra en un momento dado, ya que llegara a "desbordarse" el valor mximo y en ese momento el programa acabara por producirse un error... pero prueba esto y vers: While 1 Print "Hola Mundo" Wend Esto nunca finalizar, salvo que pulses CRTL+BEAK (o INTERrumpir), para detener el programa.

Ms instrucciones para hacer bucles Con el QuickBasic 3.0, (yo no llegu a tenerlo, pero creo que fue ah dnde se introdujo), entr en funcionamiento una nueva forma de hacer bucles: DO... LOOP El ltimo ejemplo podramos escribirlo as: Do Print "Hola Mundo" Loop Pero la "gracia" de este tipo de bucle es que podemos usar dos nuevas clusulas para evaluar cuanto durar el bucle. La primera es WHILE y funciona igual que en WHILE/WEND A = 1 Do While A <= 10 Print A A = A + 1

77

Loop La ventaja es que WHILE se puede poner tanto despus de DO como a continuacin de LOOP. Si lo usamos como DO WHILE <expresin>... la forma de actuar es igual que WHILE/WEND, es decir, se evala la expresin y slo en caso de que sea cierta, se ejecuta lo que est dentro del bucle, es decir entre DO y LOOP. Pero si evaluamos la expresin en LOOP, se ejecutar todo lo que hay tras el DO, como mnimo una vez y se seguir repitiendo si se cumple la condicin. De esta forma, como he dicho, se ejecutar el contenido del bucle, como mnimo una vez. Veamos un ejemplo: A = 1 Do Print A A = A + 1 Loop While A <= 10 El problema es que si A, en lugar de valer 1, tiene un valor superior a 10, tambin se ejecutar, al menos, una vez. A = 11 : Do : Print A: A = A + 1: Loop While A <= 10 Que mal queda en una sola lnea, verdad? Pero con DO/LOOP tambin puede usarse UNTIL, en este caso, el bucle se repetir HASTA que se cumpla la expresin A = 1 Do Until A > 10 Print A A = A + 1 Loop Fjate que la expresin ha cambiado de <= (menor o igual) a > (mayor), ya que ahora se evala de esta forma: Hasta que A sea mayor que diez, REPITE todo hasta LOOP. Por supuesto tambin podemos usarlo despus del LOOP: A = 1 Do Print A A = A + 1 Loop Until A > 10

78

Aqu hago la misma aclaracin que antes, si el valor inicial de A es ms de 10 se ejecutar como mnimo una vez. Realmente para contar de forma secuencial y prcticamente para casi todo tipo de bucle, no es necesario hacer los bucles con DO/LOOP, ya que FOR/MEXT lo hace bastante bien. Sigamos con estos bucles, pero en lugar de contar de menor a mayor, vamos a contar " pa trs", es decir de mayor a menor... quin sabe, lo mismo necesitas hacer un programa que cuente al revs... A = 10 Do While A >= 1 Print A A = A - 1 Loop Cuando se muestre el 1, A=A-1 se convertir en A = 0 y la comparacin A >= 1 no se cumplir, por tanto dejar de repetirse el bucle, pero esto tambin se puede hacer con FOR/NEXT: For A = 10 To 1 Print A Next El nico inconveniente es que NO SE REPITE NI UNA VEZ... Por qu? Porque si no se le indica lo contrario, FOR/NEXT siempre cuenta de forma ascendente y cuando ve que A debe ir de 10 hasta 1 y que eso no es ascendente... pasa de ejecutar el bucle. Esto es una cosa a tener en cuenta, FOR siempre evala los valores del bucle que tiene que hacer y si no est entre los valores que debe, no se ejecuta ni una sola vez. En este caso debe empezar por DIEZ y llegar hasta UNO, as que se da cuenta de que ya ha terminado... incluso sin haber empezado... que listo es el FOR! Para que el FOR cuente hacia atrs, necesitamos un nuevo peldao (esto en ingls quedara "clavado"), en la escala evolutiva del FOR/NEXT (ah queda eso!!!) Ya sin coas, se necesita la palabra STEP para indicarle que no queremos ir de uno en uno de forma ascendente, en nuestro ejemplo lo usaramos as: For A = 10 To 1 Step -1 Print A Next De esta forma contar desde 10 hasta 1, restando uno en cada repeticin. Pero, que hacer si queremos usar otros valores? Simplemente ponerlo despus de STEP, por ejemplo: For A = 10 To 1 Step -1 For A = 1 To 10 Step 3, etc, etc.

79

Insisto, todo esto est muy bien, pero en la prctica usaremos otras cosas adems de contar de forma lineal, con incrementos o sin ellos... habr veces que queramos salir de un bucle. Ya lo hemos visto, por ejemplo Exit Sub sala del procedimiento, recuerdas el Exit For? Para salir de los bucles podremos Exit y a continuacin For, Do, etc. Pero NO podremos salir de un bucle WHILE/WEND. Ya veremos ejemplos para estos casos y otros que surgirn ms adelante. Bien, creo que ya hemos dado demasiadas vueltas con tanto bucle, para terminar: los ejercicios esos que tanto os gustan. 1.) Haz un programa que al pulsar en un botn (CommandButton) haga un bucle entre dos valores que habrs introducido por medio de dos cajas de textos (una para el inicio y otra para el final) 2.) Otro que tenga una tercera caja de textos y que el valor introducido en ella sea el incremento. 3.) Como tercer ejercicio, una vez terminado el bucle, que muestre en un Label las veces que se ha repetido. Por ejemplo, si hacemos un bucle del uno al diez de uno en uno, se repetir diez veces; pero si lo hacemos de dos en dos, se repetir cinco veces... Como pista, decirte que no tendrs que hacer ninguna comparacin para obtener el resultado, la solucin es tan SIMPLE que seguramente la descartars "porque no puede ser tan fcil" Por supuesto, me gustara que los bucles los hicieras tanto con FOR/NEXT y DO/LOOP. Ya puestos, podras hacerlo con el WHILE/WEND e incluso con el GOTO... Feliz programacin! Y seguimos con la costumbre esta de pedirte tus comentarios sobre el curso. Sobre todo necesito saber si realmente est claro y entendible... ya que si no no valdr para mucho el montn de horas que le dedico a cada una de las entregas... si, aunque no te lo creas, le dedico ms de 5 horas a cada entrega... Si no pasa nada raro, seguramente en este mismo mes habr una nueva entrega. Nos vemos. Guillermo

Curso Bsico de Programacin en Visual Basic


Hola fans del curso bsico, es que poco a poco se estn apuntando ms gente a este cursillo y eso me pone en un compromiso... me vais a obligar a terminarlo!!! Bromas aparte, espero que te siga interesando y que se lo digas a tus vecinas, compaeros de clase, gente de tu curro y dems personal que te encuentres por la calle,

80

el bus, el metro, cuando llames a un 906 y sobre todo cuando te tomes unas copas con los amigos... a ver si al final entre todos me compris una casita de verdad... Vamos a ver las soluciones de los ejercicios de la sexta entrega, para ver que nivel llevas, porque me imagino que te has enterado de todo lo que pona, ya que ltimamente no recibo preguntas ni dudas ni quejas porque algo estuviera mal... salvo algn que otro despistadillo que ha empezado por la primera entrega, hace los ejercicios y me "mailea" dicindome que no le muestra nada... y eso que lo he puesto al final de la primera entrega y en letra GORDA pa que se vea bien... Esto pasa por bajarse las pginas, guardarlas en el disco duro y despus leerla al montn de das... Bueno, despus del rapapolvo... todo para que se te olvide que esta entrega iba a estar el mes pasado... SOLUCIONES A LOS EJERCICIOS DE LA SEXTA ENTREGA: 1.) Private Sub Command1_Click() Dim A As Integer Dim B As Integer Dim i As Integer A = Val(Text1) B = Val(Text2) For i = A To B Print i Next Show End Sub ' El bucle tambin se puede hacer de esta forma: i = A Do While i <= B Print i i = i + 1 Loop ' Y de esta tambin i = A While i <= B Print i i = i + 1 Wend 2.)

81

Private Sub Command1_Click() Dim A As Integer Dim B As Integer Dim C As Integer Dim i As Integer A = Val(Text1) B = Val(Text2) C = Val(Text3) For i = A To B Step C Print i Next Show End Sub ' Otra forma de solucionarlo i = A Do While i <= B Print i i = i + C Wend 3.) ' Aadir estas lneas: Dim D As Integer '... '...For D = D +1 '... Next Label1 = "Nmero de repeticiones: " & D Y eso es todo, no pongo otras posibles formas de obtener los resultados, porque tu mismo habrs comprobado si estn bien o no.

Ahora empezamos con la entrega real, es decir la sptima. Ya disponemos de instrucciones suficientes para empezar a "profundizar" en las cosas ms difciles... o casi. Ya sabes que hay que usar Option Explicit en todos los mdulos de cdigo... Esto no es obligatorio, pero si no quieres que perdamos la amistad, usalo. Gracias. Tambin cmo usar las variables y los diferentes tipos, puedes hacer bucles, tomar

82

decisiones, realmente es ms correcto decir: hacer que VB tome decisiones, ya sabes cmo pedir datos al usuario y tambin cmo mostrarlos. Sabes crear tus propias instrucciones... Jo! Cuanto sabes! Me tienes "anonadado" Pero an no sabes una cosa: cmo crear Funciones Qu son las funciones? Para simplificar, te dir que una funcin es un procedimiento (como el Sub), que puede devolver un valor. Normalmente se usa para que lo devuelva, aunque veremos que no siempre es necesario; de todas formas, cuando necesites un procedimiento que no necesite devolver un valor, usa el Sub. Cmo declarar/codificar una funcin? mbito Function Nombre ([parmetros]) As Tipo Dnde mbito puede ser Public o Private, dependiendo de la "cobertura" o visibilidad. Ya sabes, Private slo es visible en el propio mdulo en el que se encuentra definido y Public es en todo el proyecto, incluso fuera de l... Los parmetros son los valores que esta funcin puede necesitar para "cumplir" su misin. stos son opcionales, es decir puede tenerlos o no, incluso si tiene, puede ser uno o varios, para declarar varios parmetros hay que separarlos por comas... Los corchetes, que se suelen usar en los manuales, la ayuda, etc, sirven para indicar que son opcionales, pero no se te ocurra ponerlos en ninguna funcin!, ya que no forman parte del lenguaje Basic... El tipo es para saber que tipo de dato devolver la funcin. El valor devuelto por una funcin lo podemos usar para asignarlo a una variable: a = MiFuncin() o incluirlo en una expresin: If MiFuncin() + 15 > LoQueSea Then La ventaja real frente a los Subs es la posibilidad de devolver un valor, imaginate que quieres crearte tu propio procedimiento para averiguar si un determinado archivo existe... si lo hace como funcin podras devolver un valor cero para indicar que no existe el archivo y un valor distinto de cero indicara que el archivo en cuestin existe. Por tanto, podramos usarlo de esta forma: If Existe(NombreArchivo) Then ... Ya que estamos puestos, veamos cmo hacer esta funcin de forma simple y as te explico una cosa muy importante de toda funcin: poder devolver el valor! Public Function Existe(sArchivo As String) As Integer Existe = Len(Dir$(sArchivo)) End Function Para devolver un valor, ste se asigna a una variable que tiene el msmo nombre que la funcin. Ya vimos que LEN devuelve el nmero de caracteres de la cadena que ponemos entre los parntesis; si, LEN tambin es una funcin, pero incluida en el propio Visual Basic. Dir$ es otra funcin del VB que devuelve el nombre de un archivo, (slo el nombre), o una cadena vaca, en caso de que no haya ninguno en la direccin pasada por el parmetro que se ha usado. Para saber ms de esta funcin, as como de otras, puedes buscar en la ayuda... Hemos visto que en las expresiones usamos unos operadores para hacer las comparaciones, aqu tienes los seis posibles: = igual, > mayor que, < menor que, >= mayor o igual, <= menor o igual y <> distinto. Recuerda que el signo igual funciona de forma diferente, segn se use en un expresin o

83

en una asignacin. Pero adems de estos signos, podemos usar en nuestras expresiones unos operadores lgicos, estos son: AND, OR y NOT Podramos desear hacer una comparacin y comprobar si varias cosas se cumplen, por ejemplo: If A>10 And Len(Nombre)<>0 Then ... Para que esta expresin se cumpla, deben ser ciertas las dos condiciones, es decir que A sea mayor que 10 "y" que la longitud de Nombre sea distinta de cero. Podemos usar tantas condiciones como queramos, sin pasarnos demasiado para que la cosa funciones mejor. Aqu las dos condiciones deben cumplirse, pero en este otro ejemplo: If A>10 Or Len(Nombre)<>0 Then ... cumplindose cualquiera de las dos, se acepatara como vlido. Cuando el If se procesa, se toma todo lo que hay entre IF y THEN y se considera como una sla expresin. Si quieres puedes asignar a una variable el resultado de una expresin, el valor devuelto siempre ser 0 (cero) en caso de que no se cumpla todo lo expuesto y -1 cuando sea cierta. Para manejar estos valores de Cierto (-1) y Falso (0), Visual Basic tiene un tipo especial llamado Boolean, los valores que puede aceptar una variable de este tipo son: True (verdadero) y False (falso). Veamos un ejemplo: Dim b As Boolean, i As Integer Dim a As Integer, Nombre As String Show a = 15 Nombre = "Guille" b = (a > 10 And Len(Nombre) <> 0) i = (a > 10 Or Len(Nombre) <> 0) Print "Valor de B "; b Print "Valor de i "; i Te has fijado en el detalle? B vale True (verdadero), sin embargo i vale -1. Pero para el caso los dos valores significan lo mismo: si estas expresiones se hubiesen usado en una comparacin, las dos hubiesen devuelto un valor verdadero. El tercer operador lgico (Not) sirve para negar algo, es decir invertir el valor contenido en una variable, o casi... If Not A>10 Then ... Parece lgico el resultado, verdad?, si no se cumple que A sea mayor que diez, ser cierto; comprobmoslo: Dim A As Integer 'recuedas que tienes que poner Show?

84

A = 5 If Not A > 10 Then Print A; "no es mayor que 10" End If BINGO! Funciona! "Mu" bonito, pero... que es lo que ocurre? Se toma A > 10 y se procesa, como A no es mayor que 10, se devuelve un valor falso (0) y despus se hace Not 0 que da como resultado -1 (verdadero), por tanto se cumple la condicin. Ya vimos que el valor devuelto por una variable se puede usar en una comparacin, si es cero se toma como falso y si es distinto de cero, como verdadero. Prueba ahora esto: A = 0 If Not A Then Print A; "es cero" Else Print A; "es distinto de cero" End If Tambin funciona, ya que Not 0 es -1, por tanto el If lo da por cierto, si cambiamos el valor incial de A por un valor distinto de cero: A = 5 If Not A Then Print A; "es cero" Else Print A; "es distinto de cero" End If Que ha pasado aqu? Simple, que no es lo que esperbamos... Cuando hicimos Not 0 era evidente, ya que se convierte en -1, pero Not 5 no se convierte en cero, sino en: -6 y ya sabes que el IF considera como verdadero todo lo que no sea cero. No quiero entrar en detalles de porqu ocurre esto, slo decirte que la responsable de todo es la notacin binaria... los ceros y unos que dicen que es el lenguaje nativo de los cacharros estos... talvez ms adelante tratemos un poco de la notacin binaria, pero no por ahora... recuerdo que en mis tiempos del GwBasic la usaba bastante, incluso tena rutinas para "representar" en ceros y unos un nmero... Vale, para que te entretengas probando... (este link es para el programa completo Dec2Bin.zip 1.75 KB)
Private Function Dec2Bin(sNumDec As String) As String

85

' Recibe una cadena que ser un nmero decimal ' Devuelve ese nmero representado por ceros y unos ' Dim i As Integer Dim lngNum As Long Dim sTmp As String lngNum = Val(sNumDec) sTmp = "" For i = MaxBits - 1 To 0 Step -1 If lngNum And 2 ^ i Then sTmp = sTmp & "1" Else sTmp = sTmp & "0" End If Next Dec2Bin = sTmp End Function ' Long, por si las moscas ' Cadena temporal

Ahora puedes comprobar porqu NOT 5 da como resultado -6, usa esta rutina para probarlo, si escribes 5, te mostrar: 00000101 y si escribes -6 lo que muestra es: 11111010, fijate que ha cambiado todos los ceros por unos y viceversa. Eso es lo que hace el NOT, invertir los valores binarios y como un valor binario slo puede ser 0 1, no se complica demasiado la vida. Prueba a escribir el valor 0 y el valor -1 y conviertelo a notacin binaria, fijate lo que el Visual Basic normalmente ve. Prueba con el tema de la notacin binaria, as sabrs realmente cmo funciona todo esto de las comparaciones (por dentro). Lo que nunca falla es completar la expresin, por ejemplo si haces esto: If Not A<>0 Then Print A; "es CERO" Else Print A; "NO es CERO" End If Esto siempre funcionar de la forma esperada. Pero sera ms fcil, o al menos ms inteligible, hacerlo as: If A = 0 Then Print A; "es CERO"

86

Else Print A; "NO es CERO" End If Es que algunas veces se puede uno complicar la vida ms de lo necesario... Cuando quieras comprobar un valor devuelto por cualquier expresin, puedes hacerlo asignndolo a una variable o bien mostrando el valor: Print Not A Cuando se usa AND pra evaluar varias partes de una expresin hay que tener presente que siempre se procesan todas las condiciones y finalmente se decide si es cierto o no el valor devuelto. Esto que parece lgico, algunas veces puede llevar a confusin e incluso producir efectos no deseados en el programa. Prueba con esta nueva versin de la funcin Existe. En un form debes poner una etiqueta Label1. Private Function Existe(Archivo As String) As Integer Existe = Len(Dir$(Archivo)) If Existe Then Label1 = Archivo & " Si existe" Else Label1 = Archivo & " No existe" End If ' Esto es ms corto, pero talvez menos evidente: ' Label1 = Archivo & IIf(Existe, " Si", " No") & " existe" DoEvents End Function Private Sub Form_Load() Dim A As Integer, Nombre As String Show Label1 = "" Nombre = "C:\Autoexec.BIN" A = 5 If A > 10 And Existe(Nombre) Then Print A; "mayor de 10 y " & Nombre & " existe" Else Print A; "no es mayor de 10 o " & Nombre & " no existe" End If

87

End Sub En el ejemplo comprobars que a pesar de que la segunda parte de la comparacin no se cumpla, a no ser que tengas en tu disco C un archivo que se llame as, el caption del Label se ha cambiado. Es decir que se ha procesado la segunda parte de la expresin a pesar de que la primera A>10 es FALSA. Imagnate que en lugar de ser una funcin rpida, hubiese sido otra cosa que tardara un poquito ms de la cuenta... Para casos como estos, (la verdad es que no son demasiado habituales), deberas hacerlo as: Private Sub Form_Load() Dim A As Integer, Nombre As String Show Label1 = "" Nombre = "C:\Autoexec.BIN" A = 5 If A > 10 Then If Existe(Nombre) Then Print A; "mayor de 10 y " & Nombre & " existe" Else Print A; "es mayor de 10 pero " & Nombre & " no existe" End If Else Print A; "no es mayor de 10 o " & Nombre & " no existe" End If End Sub Talvez sea ms largo y haya que usar ms cdigo, pero en ocasiones es ms "resultn". Usando este nuevo "estilo", slo se comprobar si existe el archivo cuando A sea mayor que diez. Lo que debes sacar en claro de todo esto es que despus de un THEN puedes "anidar" ms expresiones IF...THEN...ELSE. Incluso se puede usar en una sola lnea, slo que el resultado "visual" del cdigo no es tan "presentable"... If A > 10 And Existe(Nombre) Then Print A; "mayor de 10 y " & Nombre & " existe" Else Print A; "no es mayor de 10 o " & Nombre & " no existe" Aunque podramos usar el caracter _ que se puede usar en VB para separar lneas largas, pero es como si estuviese toda en la misma lnea, as que la lnea anterior, se quedara as:

88

If A > 10 And Existe(Nombre) Then _ Print A; "mayor de 10 y " & Nombre & " existe" _ Else _ Print A; "no es mayor de 10 o " & Nombre & " no existe" Fjate que a pesar de aparentar que es un BLOQUE IF, no tiene el END IF del final, esto es porque yo lo he "estructurado" de esa forma, no porque sea lo mismo. El uso de _ es slo esttico y para VB todo se trata de una misma lnea, por tanto tendr un lmite de caracteres posibles a usar, el lmite que VB le ponga, que creo que es 1024... pero no me hagas demasiado caso... Antes he mencionado la palabra "anidacin", sta se usa para indicar que una serie de instrucciones estn dentro de otras. En este caso hemos anidado dos IF... THEN, pero lo ms habitual es hacerlo con los bucles (FOR, DO, etc.), vemoslo: Dim i%, j%, c% For i = 1 To 10 For j = 1 To 10 c = c + 1 Next Next Print c Lo que debes saber, o al menos tener en cuenta, es que cuando anidamos varios bucles, lo externos empiezan antes (elemental querido Watson), pero los internos finalizan primero (...) y hasta que no lo hagan, no podrn continuar los de fuera. En el ejemplo, por cada repeticin del bucle i, se completa un bucle j. Por eso el valor de c es 100 (10*10) Esto, en ocasiones, puede ralentizar el programa, y dar la impresin de que el programa se ha quedado "colgado", prueba a poner otro bucle dentro del j y cambia los valores mximo de los dos bucles internos a 1000, te recomiendo que la variable c sea LONG y que te sientes... No hace falta que hagas la prueba, es una chorrada... Lo que interesa es que dentro de un proceso cualquiera y por supuesto tambin en los bucles, podramos necesitar que el Visual Basic nos mostrara alguna indicacin de que est "ocupado", por ejemplo cambiando la forma del cursor del ratn, como hacen otros programas, incluso el propio VB cuando est "atareado". Para ello tendremos que cambiar la propiedad MousePointer para que muestre el reloj de arena: MousePointer = vbHourGlass ' vbHourglass es igual a 11, por si tienes usas el VB3 '... lo que sea MousePointer = vbDefault ' 0 si usas VB3 Pero algunas veces el cursor no se cambia... para asegurarnos que cambie, usa el DoEvents despus de asignar el valor para el reloj de arena. De esta forma permitimos que Windows procese sus mensajes (recuerdas?) y as tiene ocasin de cambiar el puntero del ratn.

89

Bueno, hasta aqu llega esta entrega. No hay ejercicios, slo te pedira que revisaras la ayuda y te leyeras lo que all pone referente a las instrucciones que vamos viendo... aunque me imagino que tendrs otras cosas que hacer... El caso es que no hay ejercicios y ya est. Siguiendo con la costumbre todas las entregas anteriores, te pido tus comentarios sobre esta entrega y el curso en general, ms que nada para saber si voy bien encaminado y no me pierdo, lo que pretendo es que se entienda y que aprendas... Ya me contars. Nos vemos en la prxima entrega, que espero que no sea tan tardona como esta...

Curso Bsico de Programacin en Visual Basic


Ya estamos de nuevo por aqu, esta vez he procurado ser un poco ms rpido para que no pierdas el hilo... (realmente he sido demasiado rpido, pero no te acostumbres) El problema de escribir las entregas, no es porque no sepa que poner... a este nivel an se bastantes cosas... ejem! Lo que ocurre es que normalmente suelo hacerlo en papel... quien lo dira verdad? Y es porque no tengo ordenador en mi "piso" y all es dnde aprovecho, por aquello de la tranquilidad, para concentrarme en las cuatro chorradillas que voy a decir... despus toca pasarlo a "limpio" y como no soy demasiado diestro en esto de escribir a mquina, hice rabona cuando tena clases de mecanografa, pues algunas veces tardo ms de la cuenta. Pero como ahora estoy en unas vacaciones "virtuales", he aprovechado y le he mangado el porttil a mi jefe, con la excusa de que se lo voy a limpiar de "basura" y todo ese rollo. Vale, no me enrollo ms con mis cosillas y vamos a pasar a un tema que, por su extensin, seguramente lo voy a dar en dos entregas. Empezamos con la octava. Ahora hay que ponerse "formales"... Ya has visto todo el tema de las variables y sabes para que sirven, (al menos as debera ser, ya que nadie me ha preguntado sobre el tema), pero hay veces que necesitamos ms. Imagnate que quieres saber cuantas veces por hora te rascas la cabeza intentando comprender lo que hay en mis pginas... Podramos tener unas cuantas variables, una por cada hora del da, con nombres como: Hora1, Hora2, etc. y cuando te arrasques a las 22 horas, haras esto: Hora22 = Hora22 + 1 Con lo que aumentas en uno el contenido de la variable Hora22... y tu dirs... que problema hay? Ninguno, pero es que se me ha ocurrido contarte esto como podra haberte contado otra cosa. Pero imagnate que quieres sumar las veces que te has rascado la cabeza en todo el da, podras hacer: VecesDia = Hora1 + Hora2 + ...+ Hora24 Tampoco hay problema, sumamos todas las variables (24 en este caso) y guardamos el

90

total en la variable VecesDia. Pero la cosa se va complicando, verdad? No lo crees? Pues toma un poco ms de complicacin: Cmo haras para saber la hora en que ms veces te has rascado la cabeza? No te doy la solucin... Es demasiado largo y seguramente hasta complicado como para ponerlo como ejemplo para que salgas de tu incredulidad... Ahora bien, si quieres hacerlo, hazlo, pero despus no me preguntes si est bien o no... sera una prdida de tiempo, ya que, como vas a ver dentro de muy pocas lneas, hay una forma de hacerlo bastante ms simple. Toda esta retahla, es para explicarte la forma con que el Basic, y otros lenguajes, nos facilita la vida en tareas de este tipo... realmente no creo que haya nadie que tenga una utilidad de este tipo, ya que no es til esto de saber cuantas veces nos rascamos la cabeza por no comprender algo... ni an cuando ese algo sea lo que yo escribo... Un poco de historia (la ma) La primera vez que me top con los ARRAYS (de eso va esta entrega), fue con un programa de dados, en mi querido VIC-20. Lo copi de un libro ingls que tena 50 juegos para el Vic-20, saba que funcionaba, pero no saba cmo... Durante un montn de tiempo, (a lo mejor fue una o dos semanas, es que antes los das duraban ms que ahora), us mtodos parecidos, sin saber porqu funcionaba... slo saba que funcionaba y simplemente lo aceptaba. Despus cay en mis manos un libro, esta vez en espaol, y me puse a leer la parte que explicaba esto de las "matrices", antes no eran arrays... la verdad es que lo le como 20 veces o ms... y pareca que nunca iba a lograr asimilarlo, es que soy bastante duro de mollera y antes, que era ms joven, calculo que unas 14 veces ms joven que ahora, tena la cabeza ms dura. En aquella ocasin si que me hubiese venido bien el programilla este de rascarme la cabeza... Esto es lo que deca, el susodicho libro: "El lenguaje BASIC permite definir otro tipo de variables numricas: A(1), A(2)...A(N) se llaman variables con ndice y estn formadas por un nombre de variable, [...], seguido de un nmero natural entre parntesis. [...] el conjunto ordenado de estas variables se llama lista. [...]" Ahora que lo he vuelto a leer, casi lo entiendo; pero para un pobre cateto ignorante como yo, aquello sonaba a chino. Y si tu lo entiendes, me alegro por ti... Como te he comentado, el prrafo ese est sacado de un libro que fue de los pocos que tuve, al menos en castellano, de los ingleses slo me interesaban los listados, que era lo nico que prcticamente traan. La cosa es, que a pesar de eso, hasta aprend un poco, a duras penas, sobre todo porque en aquellos tiempos no tena a quin preguntarle, ni quin me explicara algunas de las muchas dudas que tena... y si no tena ms dudas era porque tampoco haba profundizado demasiado. Pero lo que he sacado en claro es que para aprender a programar hay que practicar, practicar y seguir practicando... es como todo, cuanto ms practicas... o terminas por aburrirte o aprendes... Vamos al tema, pero sin teoras, las teoras se las dejo a los eruditos... ellos saben cmo explicar "bien" las cosas... Y que conste que no tengo nada en contra de las teoras, lo que ocurre, al menos a mi, es que prefiero entender las cosas de forma prctica, que soy "mu" torpe yo y si no lo veo funcionar, no me entero... En el ejemplo ese de las horas, nos vendra muy bien que pudisemos usar otra variable para la hora de nuestro "picor" y hacer algo como: HoradelPicor = HoradelPicor + 1, dnde "delPicor" sera la hora en que nos rascamos la cabeza, as si "delPicor" es 22, incrementar Hora22. S, lo reconozco, una vez intent

91

hacerlo, crea que se poda hacer as: delPicor = 22 HoradelPicor = HoradelPicor + 1 Pero el basic no haca lo que yo quera, ni siquiera me daba error, si en aquellos tiempos hubiese existido el Option Explicit, me habra percatado de muchas cosas antes de tiempo... Por suerte para todos, existen los ARRAYS (o variables con ndice) y realmente la forma de hacerlo es casi como yo crea, lo nico que cambiaba era la forma... total, por un par de parntesis... Hora(delPicor) = Hora(delPicor) +1 Con esto le decimos al Basic: coge lo que hay guardado en la variable que est en la posicin delPicor del array Hora... Vale, captado. Me estoy lanzando y an no te he presentado a los arrays. Un Array es una serie de variables que tienen el mismo nombre y para acceder a cualquiera de esas variables, usamos un nmero (ndice) para indicar cual de esas variables es la que nos interesa... Te has enterado de que estoy hablando de variables o tengo que decirlo ms veces? Vale, admito que tampoco he sido demasiado claro, es que realmente no es tan fcil de asimilar, pero en cuanto lo veas con algunos ejemplos, seguro que lo "asimilas". El basic, que es "mu" listo, cuando ve esto: Hora(delPicor), dice: "Cuanto vale lo que est dentro del parntesis?" (en caso de que sea una expresin en lugar de un nmero o una variable, la evaluara primero y usara el resultado), una vez que sabe cuanto vale lo que est dentro del parntesis... "ahora cojamos, del array, el contenido de la variable que est en esa posicin" (para el basic un array no es ms que una serie de variables que tienen el mismo nombre y lo nico que vara es "la direccin" en la que est guardado el valor. O sea que maneja los arrays de la misma forma que a las variables simples, pero ampliando nuestros horizontes "variabilsticos". Ahora fjate cmo podemos sumar las veces que nos hemos rascado la cabeza a lo largo del da: For i= 1 to 24 vecesDia = vecesDia + Hora(i) Next Cada vez que i cambia de valor, (en este caso tomando valores desde 1 hasta 24), el Basic usa una variable diferente del array Hora, cuando i vale 1 est usando Hora(1) y cuando i vale 22, usa Hora(22). Lo de saber a que hora te has rascado ms veces la cabeza te lo dejo como ejercicio, slo te dir que si haces esto: masVeces = Horas(HoraMasVeces) sabrs cuantas veces te has rascado a la hora HoraMasVeces... Los elementos de un array se comportan como variables normales, pudiendo usarlas en expresiones y en cualquier otro sitio en el que podamos usar una variable, realmente uno de los pocos sitios donde no puede usarse es como ndice de un bucle FOR, pero por lo dems, en cualquier parte. Imagnate que en la posicin 22 del array Hora, es decir en Hora(22) tenemos guardado un valor 15, al hacer esto: Print Hora(22) * 10 mostrara 150, porque el VB ha sustituido Hora(22) por su valor y lo ha multiplicado por 10, es como si internamente hubiese hecho: Print 15*10.

92

Y tu dirs: esto mismo es lo que hace con las dems variables... Efectivamente, ya que es una variable, especial, pero una variable al fin y al cabo. Sigamos imaginando... suponte que quieres guardar en una variable a que horas te pones a leer, cada da, las pginas del Guille y que sabes que sern tres veces diarias, lo hay masoquistas... Podras hacer algo como esto: Vez(1) = 20: Vez(2) = 22: Vez(3) = 23 '...(de nueve a diez vas a cenar) con lo cual tendras un array con tres ndices. El valor de cada una de las variables del array "Vez", sera la hora en que "guilleas" ... y si quieres incrementar los "rascones" de la hora que est en la posicin H, (que puede ser 1, 2 3): Ahora = Vez(H) ' Si H vale 1, Ahora sera igual a 20 Hora(Ahora) = Hora(Ahora) + 1 Pero esto podras hacerlo ahorrndotela variable Ahora, sera as: Hora(Vez(H)) = Hora(Vez(H)) + 1 Complicado? Pues si... que quieres que te diga... pero dejmoslo estar... Veamos cmo podemos usar en nuestros programas este tipo especial de variables. Antes yo las llamaba: "variables dimensionadas", entre otras cosas porque era nicamente cuando necesitaba usar DIM, al menos si iba a usar ms de 10 "posiciones", aunque tambin puede ser que lo leyera en algn sitio, no importa... Para decirle al Basic que vas a usar tres variables en el array Vez, hay que hacerlo de esta forma: Dim Vez(3) Ahora lo que necesitamos es un array para guardar los picores de las 24 horas del da: Dim Hora(24) Bueno, ya sabes casi todo lo que tienes que saber de los arrays... ahora cmprate un buen libro "terico" y estdiatelo... o lete lo que dice el manual del Visual Basic... que tambin puede valer. An sigues por ah...? Bueno, ya que insistes, te explicar algunas cosillas ms... Los arrays son variables, algo especiales, pero variables al fin y al cabo. Por tanto, podemos tener arrays numricas, de carateres y en definitiva de cualquier tipo que el Basic permita, lo nico que tenemos que hacer es indicrselo al reservar memoria: Dim Vez(3) As Integer Dim Amigos(1000) As String Dim Salario(1000) As Currency Cuando declaramos un array, el Basic reserva memoria para cada una de las variables que vamos a usar, o casi, ya que en realidad reserva una posicin ms, no por nada en especial, sino porque empieza a contar desde cero; por ejemplo en el array Vez, el ndice ms bajo que podramos usar es el 0 y el ms alto el 3. Esto ha sido as desde siempre... aunque en un intento de "cambiar" las cosas, un listillo dijo: "El Basic debera empezar a contar desde uno" y se sacaron una nueva instruccin de la manga, desde mi punto de vista lo podran haber hecho mejor, pero como mis "preferencias" no las tuvieron en cuenta... (tampoco tuve la oportunidad, la verdad sea dicha...), el caso es que dijeron: OPTION BASE 1 para que el ndice menor de un array sea UNO y OPTION BASE 0 para empezar por CERO, esta ser la predeterminada. Por tanto si usamos este cdigo: Option Base 1

93

Dim Vez(3) As Integer Crea un array con tres variables (del 1 al 3) Ms adelante, otro listillo, (este fue un poco ms inteligente), dijo: "Y si el usuario pudiera decidir el valor menor y el mayor del ndice de una array"... "pues que bien", contest otro... Y as fue. Imagnate que tu slo te rascas la cabeza de 10 a 23, puedes dimensionar as el array Hora: Dim Hora(10 To 23) As Integer De esta forma slo "reservas" la memoria que necesitas... Ya ves que todo son facilidades, aunque hay una cosa "muy" importante que hay que tener en cuenta: Si pretendes acceder a una posicin del array que no est reservada, el Visual Basic te avisa de que hay un error y detiene (termina) el programa. Esto es lo nico grave, pero si tu aplicacin tiene informacin importante pendiente de guardar, o se encontraba en medio de un proceso largo de clculo... realmente si que ser grave... en otra ocasin veremos cmo detectar los errores y poder "manejarlos" para que no nos dejen en la "estacada"... Por tanto si declaras un array que reserva tres posiciones, siempre consecutivas, no podremos acceder a ninguna posicin anterior o posterior a las que tenemos declarada. Pero... y si necesito ms espacio? Lo nico que tienes que hacer es re-dimensionar el Array: ReDim Vez(5) As Integer Problemas? Si, que ya has perdido lo que antes haba almacenado... Cuando yo empec con el Basic (otra batallita?, habr que seguirle la corriente...), no exista el ReDim. Pero si exista una forma de conseguir esto mismo. El truco consista en "borrar" el array creado y volver a dimensionarlo... tambin recuerdo que me dio muchos quebraderos de cabeza adaptar mi cdigo (escrito en el intrprete GwBasic... la GW ser Gates, William?) a un compilador... eso de usar varias veces el DIM no lo digera bien... pero eso es otra historia, que seguramente no contar... Para borrar un array de la memoria, hay que usar ERASE seguido por el nombre del array, por ejemplo: Erase Hora Con esto conseguimos lo mismo que con ReDim Vez(5) As Integer: Erase Vez Redim Vez(5) As Integer Pero, y si no quisiramos perder los valores anteriores... Pues copia los datos en otro Array temporal, borras el primero, lo vuelves a dimensionar con el nuevo nmero de elementos y a continuacin copias los datos del array temporal en el array que acabas de redimensionar, despus borras el array temporal, ya que no lo necesitars... No, no es necesario tantas cosas, pero esto es lo que haba que hacer con el VB antes de la versin 3 y con el QuickBasic antes de la versin 4, ahora slo hars esto: ReDim Preserve Vez(5) Adems cuando se ReDimensiona un array no hace falta volver a especificar el tipo de dato, ya que tomar el mismo que se us inicialmente al declararlo. La ventaja del ReDim, con o sin Preserve, (podra haber hecho un chiste malo con esto del Preserve, pero me abstengo...), es que puedes ampliar o reducir el nmero de variables de un array... supn que despus de dimensionar Vez a cinco, lo piensas mejor y decides que con dos veces es suficiente, pues nada, haces esto: Redim Preserve Vez(2) y ya est. Lo importante es que slo reserves la memoria que vas a necesitar.

94

En la siguiente entrega veremos ms cosas de los arrays, as como algunas otras instrucciones, pero no te voy a adelantar nada, no sea que despus no est lo que tengo pensado y te mosquees. Ahora vamos al apartado de los ejercicios, que adems del que te dije casi al principio, te voy a poner otro ms: 1.) Tienes un array con un nmero cualquiera de elementos, averigua cual de las variables de ese array es la que tiene el valor mayor. 2.) La que tiene el valor menor y que no sea cero. Como ves no te quiero que te esfuerces demasiado. Ya sabes, si hay algo que no hayas terminado de comprender, no dudes en comentrmelo, de esta forma no seguir enrollndome con cosas que no hayan quedado "super claras". Esto ser en la prxima entrega, que espero sea dentro de muy poquito, (aprovechando las vacaciones virtuales estas que tengo), as que si vas a comentarme algo, hazlo pronto. Nos vemos.

Curso Bsico de Programacin en Visual Basic


Hola, ya ves que no puedo dedicarle todo el tiempo que yo quisiera a esto del curso bsico. Pero lo importante es que ste siga "pa lante" aunque haya un poco de lapso de tiempo entre cada una de las entregas... No voy a prometerte que voy a ser ms aplicado y no tardar tanto entre cada entrega porque a lo mejor no cumplo y despus me echas la bronca... De todas formas intentar hacer un pequeo esfuerzo... y con la ayuda y compresin de todos vosotros espero conseguirlo... cmo? pues muy fcil, me gustara que las consultas que recibo no tuviera que contestarlas todas... es que son muchas, sobre todo se me acumulan los jueves y viernes, ya que esos das los dedico por completo a la maquetacin de un peridico semanal de informacin local que editamos en nuestra empresa... y no slo la maquetacin sino que me tengo que encargar de llevar y recoger la filmacin y hay que hacerlo fuera de Nerja, por lo que, a la "paliza" de la autoedicin, hay que aadirle la paliza del viaje de ida/espera (algunas veces larga)/vuelta. As que si ests interesado en echarme un cable, dmelo y te pondr en la lista de colegas "respondones" del Guille... Gracias por adelantado. Despus de la presentacin y el "comecoco", vamos con las soluciones de los ejercicios. Solucin a los ejercicios de la Octava entrega El primero:

95

'Poner este cdigo en el Form_Load Dim Hora(24) As Integer Dim i As Integer, Mayor As Integer 'Llenar el array con nmeros... '(en esta entrega veremos cmo hacerlo de forma aleatoria) '... 'Comprobar cual es el mayor For i = 1 To 24 If Hora(i) > Mayor Then Mayor = Hora(i) End If Next Print "El nmero mayor es:"; Mayor Un poco de explicacin, ya que no creo que sea suficiente con ensear la solucin. El problema que te has podido encontrar es, seguramente, la forma de asignar valores al array, aunque siempre queda el recurso de poder "llenarlo manualmente"; pero eso lo veremos en esta misma entrega. La cuestin es comparar el contenido de cada una de las horas con la variable que guardar el nmero mayor. Al principio esta variable, como ya deberas saber, tiene el valor CERO, as que cuando se haga la comparacin If Hora(i)>Mayor Then, si la variable que est en la posicin "i" del array "Hora" tiene un valor mayor que cero, se cumplir la condicin y se pasar a asignar ese valor a la variable que contendr el nmero mayor de los 24 que tenemos en el array. El bucle contina y cada vez que se cumpla la condicin de que el contenido de Hora(i) es mayor que el que tenemos en la variable Mayor, se asignar este y as hasta que se termine de dar vueltas... Si no te has enterado... preprate para la solucin del segundo ejercicio. El segundo: 'Los mismos comentarios iniciales que el primero Dim Hora(24) As Integer Dim i As Integer, Menor As Integer ' For i = 1 To 24 If Hora(i) Then If Menor = 0 Then Menor = Hora(i) Else If Hora(i) < Menor Then Menor = Hora(i) 'Si el contenido de Hora(i) es menor 'lo asignamos como menor 'Slo si no vale cero 'Si an no tiene un valor 'se lo asignamos

96

End If End If End If Next Print "El nmero menor es "; Menor Este est ms o menos explicado en los comentarios, pero voy a dejrtelo un poco ms claro: La cuestin consiste en comprobar primero si el contenido del elemento "i" del array "Hora" tiene un valor distinto de cero, (si vale cero no lo tendremos en cuenta), lo siguiente que se hace es comprobar si el contenido de "Menor" vale cero, si es as, quiere decir que an no le hemos asignado ningn valor, por tanto le asignamos lo que tenga Hora(i). En posteriores comprobaciones lo que se hace es averiguar si el valor guardado en el elemento del array es menor que el que tenemos en nuestra variable "Menor" y si es as, quiere decir que tenemos un nmero ms pequeo, por tanto lo asignamos para que siempre "Menor" tenga el nmero menor (valga la redundancia). Pero y si quisiramos tener en cuenta tambin el CERO... Pues que tendramos que hacerlo de otra forma, ya que esta es slo para el caso expuesto... te dejo que lo pienses, pero no es demasiado difcil, incluso ms simple que esta solucin, lo que ocurre es que entran en juego pequeos detalles que seguramente veremos en esta entrega... Ahora vamos a empezar la Novena Entrega. No voy a empezar, o mejor dicho, no voy a continuar con los arrays, pero no te preocupes que slo ser un pequeo alto en el camino, lo que veremos primero es algo que nos va a facilitar hacer pruebas con los arrays... se trata... (redoble de tambores) de:

Nmeros Aleatorios
En algunos casos vamos a necesitar generar nmeros aleatorios, (nmeros sacados al azar, al menos en teora...), y si no necesitas usar nmeros aleatorios, vamos a usarlos en algunos de los ejercicios, as que voy a explicar cmo va esto: La funcin que se usa para generar nmeros aleatorios es: RND Esta funcin devuelve un nmero que ser mayor o igual a CERO y menor que UNO. Creo que se representa as: 0<RND<1, pero si no es as, da igual o algn "experto" me lo dir. Lo que interesa saber es que nunca llegar a valer uno y que puede ser igual a cero. Si hacemos esto: x = Rnd * 6 El valor X nunca llegar a 6, en la mayora de los casos se suelen quitar los decimales usando INT, en este caso, haciendo x = Int(Rnd * 6), x podr valer 0, 1, 2, 3, 4 5 y si hacemos esto otro: x = Int(Rnd * 6) + 1. Los valores sern de 1 a 6. Si queremos valores del 65 al 90 la expresin sera esta: x = Int(Rnd * 26) + 65. Ya que Int(Rnd * 26) producir un nmero que estar entre el 0 y el 25 (ambos inclusives), al sumarle 65... pues eso, estar en el rango deseado. Este ejemplo nos sirve para cuando necesitemos obtener un cdigo ASCII de una letra de la A a la Z, (en maysculas), ya que los cdigos ASCII de las letras son: A=65, B=66... Z=90 en caso de que sean maysculas, para obtener los valores en minsculas slo hay que aadirle 32 y ya los tendremos porque a=97, b=98... z=122. En estos rangos se excluye la ee, tanto maysculas como minsculas y las vocales acentuadas... es que los seores que crearon esta norma (American Standard Code for Interchange Information, o algo parecido y se suele pronunciar ASKI), eran de los USA y all no usan esas letras... recuerdo mis tiempos de comics, cuando Alex Nio se llamaba Alex Nino, al menos en los crditos de los comics USA...

97

El problema de los nmeros aleatorios, es que no son tan aleatorios, es decir cada vez que se inicia el programa produce el mismo nmero, (al menos en los basics anteriores, he ledo que ahora el VB4 no genera la misma secuencia cada vez que se inicia el programa, eso lo veremos despus), vamos a ver un ejemplo para que lo compruebes, esto lo he comprobado con el VB2 (que es el que tengo en el porttil que me he llevado a casa para escribir las entregas, hasta que mi jefe me lo pida... que ser en pocos das, puede ser), se supone que en los dems funcionar igual, pero como te digo para el VB4 hay un "truco" que veremos despus... cuando lo compruebe, je. Escribe esto en el Form_Load del nuevo proyecto que habrs tenido que crear para probar... si no lo has hecho, ya tardas... Private Sub Form_Load Show 'Aqu debe estar esto, sino no se ver nada... recuerdas? Print Rnd * 25 End Sub A m me ha mostrado 17.63869, ejectalo varias veces y vers que siempre muestra el mismo nmero... Existe una forma de solucionar esta falta de "aleatoriedad" y es cambiando la "semilla" que se usa como base para la imPLANTAcin de nmeros aleatorios, para ello se usa Randomize seguido de un nmero, pero si el nmero es el mismo... no conseguimos nada... Prueba poniendo Randomize 5 despus del Show y antes del Print, prueba a ejecutarlo varias veces, a mi me ha mostrado 8.143144 todas las veces que lo he ejecutado. El problema es que al usar un nmero "fijo" como semilla para la generacin de nuevos nmeros, el nmero producido siempre es el mismo, esto est bien para hacer pruebas fijas o cuando queremos "drnosla" de mago con los colegas que saben menos que nosotros, ya que podemos "predecir" los nmeros que mostrar en una secuencia seguida... con este "truco" dejaba "alucinado" a los chavales a los que le daba clases... slo tena que memorizar una serie de nmeros y se quedaban "alucinados" cuando les deca el que iba a salir... claro que despus tena que poner "pies en polvorosa" cuando les explicaba "la trampa". Bueno, al tema, que no es plan de contar batallitas... El VB nos proporciona una funcin que devuelve el nmero de segundos transcurridos desde la media noche (TIMER) y usando esta funcin como "semilla" lograremos producir nmeros que "casi" ser aleatorios... al menos sern ms difciles de "pronosticar", cambia el Randomize 5 por Randomize Timer y vers que ya no se produce el mismo nmero cuando ejecutes varias veces el programa...salvo que hagas "trampas" cambiando la hora del equipo... En VB4 y superior, se puede hacer esto mismo poniendo Randomize -1, de esta forma la "semilla" es diferente cada vez que se ejecuta, pero prefiero usar el Timer ya que es ms "compatible". Que tal un jueguecito para practicar? Hay que hacer un programa que genere un nmero entre uno y cien y hay que intentar adivinarlo... Si el nmero que damos es mayor o menor, que el VB nos avise y cuando acertemos que nos lo comunique y termine el programa... Para que VB se comunique, te voy a decir cmo hacerlo... Para preguntarte el nmero y guardarlo en la variable N, haz esto: N = Val(InputBox("Escribe un nmero entre 1 y 100")). El InputBox muestra una pantalla preguntando y devuelve lo que escribamos o una cadena vaca si pulsamos en cancelar, el Val convierte esa cadena en nmero. Para avisar que el nmero N, (el que nosotros le decimos al VB), es menor o mayor, cambiar xxxx por lo que corresponda: MsgBox "El nmero " & CStr(N) & " es xxxx"

98

De esta forma se mostrar un cuadro de dilogo indicando si vamos bien encaminados o no... Ya mejoraremos o ampliaremos este "ejercicio" para hacer ms cosas, incluso que el ordenador averige el nmero... que sin ningn tipo de "suerte" lo adivinar en 5 6 veces, lo mismo que tu debers hacer si sigues algunas normas o trucos... slo decirte lo de "divide y vencers" (no s porqu me ha dado ahora por esa cita...) En la prxima entrega veremos ms cosas sobre los arrays... Si te atreves podras hacer los siguientes cambios al "problemilla" planteado anteriormente: 1. Comprobar que el nmero introducido en el InputBox est entre 1 y 100, en caso de que no sea as, volver a preguntar. 2. Si se escribe CERO mostrar el nmero que el VB haba "pensado" y terminar. 3. Cuando lo acertemos que nos indique en cuantos intentos lo hemos conseguido. 4. Un programa que sea al revs, es decir: que nosotros pensemos un nmero del 1 al 100 y el VB intente adivinarlo, para ello deber mostrarnos un nmero y nosotros indicarle si lo ha acertado. 5. Otro igual, pero indicndole si nuestro nmero es Menor, Mayor o es correcto... habr que darle las mismas oportunidades... (este es el que tiene el "truco" del divide y vencers... En los casos 4 y 5 que muestre tambin el nmero de intentos que le ha llevado solucionarlo... La pista para que el ordenador sepa si es menor o mayor es usar el MsgBox, pero como funcin: If MsgBox("Mi nmero es: " & CStr(x) & Chr$(13) & "He acertado?", 4) = 6 Then MsgBox "Lo he acertado en " & CStr(v) & " veces." Exit Do '... De esta forma mostrar un cuadro de dilogo con dos opciones "SI" y "NO", el nmero 4 es el encargado de eso. El valor devuelto ser 6 si se pulsa en SI y 7 si se pulsa en NO. Esto en VB4 se podra hacer as: If MsgBox("Mi nmero es: " & CStr(x) & vbCrLf & "He acertado?", vbYesNo) = vbYes Then MsgBox "Lo he acertado en " & CStr(v) & " veces." Exit Do '... Con lo cual, aunque sea en ingls, es ms intuitivo. Esto de los MsgBox lo veremos en una entrega "especial"

99

Las soluciones estn en este link... no quiero que ests esperando las soluciones hasta la prxima entrega... para que veas que algunas veces soy un poco "ms considerado" Bueno, creo que esta entrega no debera tener demasiadas dudas... en los manuales o la ayuda viene explicado esto de los nmeros aleatorios, pero si quieres dejarme algn comentario, hazlo e intentar aclararte cualquier duda... pero no lo hagas sobre las soluciones, ya que las obtienes en el link del prrafo anterior. Que lo "randomices" bien... Nos vemos.

Curso Bsico de Programacin en Visual Basic


Pues aqu estn las soluciones, creo que esta forma de darlas ser la mejor, ya que no tendrs que esperar a que est lista la siguiente entrega para saber si has conseguido resolver los problemillas/ejercicios con xito... El juego bsico: Dim n As Integer Dim x As Integer Randomize Timer x = Int(Rnd * 100) + 1 Do n = Val(InputBox$("Escribe un nmero del 1 al 100")) If n = x Then Exit Do If n < x Then MsgBox "El nmero " & CStr(n) & " es menor." Else MsgBox "El nmero " & CStr(n) & " es mayor." End If Loop MsgBox "Lo has acertado." 1. Comprobar que el nmero introducido en el InputBox est entre 1 y 100, en caso de que no sea as, volver a preguntar. Una de las soluciones, sin usar el GOTO:

100

Dim n As Integer Dim x As Integer Randomize Timer x = Int(Rnd * 100) + 1 Do Do n = Val(InputBox$("Escribe un nmero del 1 al 100")) Loop While n < 1 Or n > 100 If n = x Then Exit Do If n < x Then MsgBox "El nmero " & CStr(n) & " es menor." Else MsgBox "El nmero " & CStr(n) & " es mayor." End If Loop MsgBox "Lo has acertado." 2. Si se escribe CERO mostrar el nmero que el VB haba "pensado" y terminar. Dim n As Integer Dim x As Integer Randomize Timer x = Int(Rnd * 100) + 1 Do Do n = Val(InputBox$("Escribe un nmero del 1 al 100")) If n = 0 Then MsgBox "Mi nmero era el " & CStr(x) Unload Me End End If Loop While n < 1 Or n > 100 If n = x Then Exit Do If n < x Then MsgBox "El nmero " & CStr(n) & " es menor." Else

101

MsgBox "El nmero " & CStr(n) & " es mayor." End If Loop MsgBox "Lo has acertado." 3. Cuando lo acertemos que nos indique en cuantos intentos lo hemos conseguido. Dim n As Integer Dim x As Integer Dim v As Integer Randomize Timer x = Int(Rnd * 100) + 1 Do Do n = Val(InputBox$("Escribe un nmero del 1 al 100")) If n = 0 Then MsgBox "Mi nmero era el " & CStr(x) 'Si est en el Form_Load 'End 'Si est en un procedimiento Exit Sub End If Loop While n < 1 Or n > 100 v = v + 1 If n = x Then Exit Do If n < x Then MsgBox "El nmero " & CStr(n) & " es menor." Else MsgBox "El nmero " & CStr(n) & " es mayor." End If Loop MsgBox "Lo has acertado en " & CStr(v) & " veces." 4. Ahora al revs, es decir: que nosotros pensemos un nmero del 1 al 100 y el VB intente adivinarlo, para ello deber mostrarnos un nmero y nosotros indicarle si lo ha acertado.

102

Dim x As Integer Dim v As Integer Randomize Timer Do x = Int(Rnd * 100) + 1 v = v + 1 If MsgBox("Mi nmero es: " & CStr(x) & Chr$(13) & "He acertado?", 4) = 6 Then MsgBox "Lo he acertado en " & CStr(v) & " veces." Exit Do End If Loop 5. Otro igual, pero indicndole si nuestro nmero es Menor, Mayor o es correcto... habr que darle las mismas oportunidades... (este es el que tiene el "truco" del divide y vencers... Dim x As Integer Dim v As Integer Dim a As Integer Dim z As Integer a = 1 z = 100 'En este caso no necesitamos nmeros aleatorios Do x = (z - a) / 2 + a v = v + 1 If MsgBox("Mi nmero es: " & CStr(x) & Chr$(13) & "He acertado?", 4) = 6 Then MsgBox "Lo he acertado en " & CStr(v) & " veces." Exit Do Else If MsgBox("Entonces... " & CStr(x) & " es mayor?", 4) = 6 Then 'El nmero del ordenador es mayor 'debe estar entre x-1 y a z = x - 1 Else

103

'El nmero del ordenador es menor 'debe estar entre x+1 y z a = x + 1 End If End If Loop

Ahora a esperar a la dcima entrega que ser dentro de poquito... un mes, un ao... quin sabe?

Curso Bsico de Programacin


Hola, ya estoy de nuevo por estos lares... para seguir dndole caa al curso bsico. La entrega anterior fue un pequeo alto en el camino antes de continuar nuestra andadura por los escabrosos caminos de los arrays... UF! Quin ese ese que escribe? Guille!!!

Arrays Multidimensionales
En algunas ocasiones hasta los arrays se quedan cortos... al menos los arrays simples o unidimensionales, (una sola dimensin), si en el ejemplo de la octava entrega, el de los rascamientos, quisieras saber las veces que te has rascado cada da del mes... tendramos otra vez el problema, podramos usar un array para cada da del mes: Dia1(24), Dia2(24)... pero de nuevo tendramos complicaciones para algunos clculos... Por suerte para nosotros, existe otra forma de usar los arrays. En nuestro caso nos servira el que los arrays tuviesen dos dimensiones, al estilo de una tabla con filas para cada da del mes y columnas para cada una de las horas del da en cuestin, para hacer esto, dimensionaremos un array de esta forma: Dim Dias(31, 24) As Integer Para guardar o recuperar un valor lo haremos de la misma forma que con un array simple, pero especificando dos valores separados por una coma: Dias(DiaMes, HoraDia) = 1 y por supuesto, podemos usarlo con bucles FOR: For Dia = 1 To 31 For Hora = 1 To 24 RascadasMes = RascadasMes + Dias(Dia, Hora) Next Next

104

Despus que estos dos bucles terminen, la variable RascadasMes tendr el total de veces que nos hemos rascado cada uno de los das del mes que estamos procesando. Si queremos almacenar las rascadas de cada da de cada mes de un ao, No Problem! Dim Meses(12, 31, 24) As Integer De esta forma solucionaramos en problema, ya que al aadir una tercera dimensin, podemos usar esta para cada uno de los meses, por ejemplo, el total de veces que nos hayamos rascado a las 22 horas del da 30 del mes 9 (septiembre), estara en: Meses(9, 30, 22) Reconozco que este ejemplo de las rascada no es til, pero por lo menos hemos visto cmo usar los arrays. Recuerda que los arrays pueden ser de cualquier tipo: Integer, String, Double, etc.

Cuntos elementos tiene un array?


En algunas ocasiones podemos necesitar saber el nmero de elementos contenidos en un array, para estos casos existen dos funciones, una para saber el ndice menor y otra para saber el mayor. Por ejemplo si tenemos este array: Horas(8 To 22) El menor sera 8 y el mayor 22, para averiguarlo: Menor = LBound(Horas) Mayor = UBound(Horas) Esta forma es para los arrays unidimensionales, para averiguar estos valores en arrays con ms de una dimensin, tendremos que especificar la dimensin de la que queremos averiguar ese valor menor o mayor, por ejemplo, si tenemos Dias(1 To 31, 0 To 23) MenorMes = LBound(Dias,1) 'Devolvera 1 MayorMes = Ubound(Dias, 1) 'Devolvera 31 MenorHora = LBound(Dias, 2) 'Devolvera 0 MayorHora = UBound(Dias, 2) 'Devolvera 23

Redimensionando arrays multidimensionales


Veamos ahora cmo funciona el Redim y Redim Preserve con los arrays con varias dimensiones: igual S, da lo mismo que el array tenga una o muchas dimensiones. Lo nico que debemos saber es que no podemos cambiar el nmero de dimensiones, aunque s el nmero de elementos de cada una de las dimensiones. Un ejemplo: Tenemos inicialmente esta declaracin: Dim Meses(1 To 6, 1 To 31, 0 To 11) As Integer y necesitamos ampliar la ltima dimensin de 11 a 23: Redim Meses(1 To 6, 1 To 31, 0 To 23) o Redim Preserve Meses(1 To 6, 1 To 31, 0 To 23) si queremos conservar los valores almacenados. Lo que no podemos hacer es esto: Redim Meses(1 To 31, 0 To 23) porque pasamos de tener tres dimensiones a pretender tener slo dos y eso, no est permitido. Ni al revs tampoco, es decir si tenemos un array con dos dimensiones y queremos que tenga tres. Si queremos hacer esto ltimo, tendremos que eliminar el primer array y volver a dimensionarlo con las dimensiones que queramos tener: Dim Dias(31, 24)

105

Erase Dias Dim Dias(12, 31, 24) El problema es que perdemos los datos... cosa que, en caso de necesidad, podramos solucionar copiando los datos a otra variable y volviendo a asignarla al nuevo array dimensionado... Pero muchos de estos problemas se solucionan con las colecciones y el uso del tipo Variant, as como con los objetos o clases definidas por nosotros... pero eso ser ms adelante... todava hay muchas otras cosas "esenciales" que aprender y conceptos que siempre debes tener en cuenta... que poco a poco estoy intentando recalcar para que tu "coco" vaya asimilndolos... espero conseguirlo. Nota: Hay que tener en cuenta que la nica dimensin que podemos redimensionar en un array multidimensional es la ltima.

Cuantas dimensiones puede tener un array?


Si la mente no me falla, el nmero de dimensiones es 256. Quin necesita tantas? Los valores menor y mayor de los ndices estn comprendidos dentro del rango de un valor Integer del VB, es decir entre -32768 y 32767 o sea 65536 valores o ndices distintos. Esto lo comento como "curiosidad" pero deberas comprobarlo en los manuales.

Unas cadenas, por favor


Ya he comentado en la octava entrega que los arrays tambin permiten asignar cadenas de caracteres, realmente se pueden tener arrays de cualquier tipo de variables. Pero no mezcladas. Si un array se dimensiona del tipo Integer slo podremos almacenar valores numricos enteros. Incluso cuando lo Redimensionemos deber tener el mismo tipo con el que en un principio lo habamos dimensionado. Este inconveniente se solucionar en una prxima entrega y con las colecciones. Con lo que sabemos hasta ahora es con lo que vamos a trabajar. Y vamos a practicar un poco con los arrays de caracteres, para ello vamos a crear un array de cadenas con caracteres aleatorios. No tiene ninguna utilidad, pero servir para uno de los ejercicios. Dimensionaremos un array de 100 elementos, a cada uno de esos elementos asignarle entre 10 y 50 caracteres comprendidos entre la A y la Z, recuerda que los cdigos ASCII de la A es el 65 y la Z el 90. Ahora os pondr una forma "fcil" de clasificar ese array, la parte de la asignacin es la que t tendrs que hacer. ' Const MaxCadenas = 100 Dim cadena(1 To MaxCadenas) As String Dim c As Integer Dim sTmp As String

106

Dim i As Integer Dim j As Integer Randomize Timer list1.Clear 'Asignar los valores '...Escribe aqu tu cdigo... 'Clasificar For i = 1 To MaxCadenas For j = 1 To i - 1 'para ordenar de forma descendente: 'If cadena(i) > cadena(j) Then If cadena(i) < cadena(j) Then 'intercambiar los valores sTmp = cadena(i) cadena(i) = cadena(j) cadena(j) = sTmp End If Next Next list1.Clear For i = 1 To MaxCadenas list1.AddItem cadena(i) Next Creo que el procedimiento es lo suficientemente "simple" como para que lo entiendas... verdad? Lo que debes "observar" en este mtodo es que cada uno de los elementos del bucle i se compara con todos los anteriores, de forma que si alguno anterior es "mayor" se intercambien las posiciones... Supn que en la posicin cadena(1) tienes almacenado "HOLA" y en la posicin 2 est la palabra "AMIGO" La condicin se cumplir cuando la variable i valga 2 y j valga 1, quedando por tanto en el orden correcto. Lo que debes saber de las cadenas de caracteres es que cuando se hace una comparacin el Visual Basic comprueba los valores ASCII de las letras que componen la palabra, en este caso la letra A est antes que la H, as que A es menor que H. Tambin debers saber que los nmeros estn antes que las letras, por tanto si una cadena de caracteres empieza por una cifra del 0 al 9, se ordenar antes que la "A" y que la "a" estar despus que la Z Si quieres saber los valores ASCII de los caracteres "ms o menos" stndard, haz este bucle:

107

'Cdigos ASCII For i = 32 To 122 Debug.Print i; Chr$(i) Next

Ahora los ansiados ejercicios, (realmente ha sido cortita esta entrega verdad?) Para los ejercicios, usando este trozo para guardar nmeros aleatorios en un array unidimensional, espero que no tengas problemas para guardarlos en un array multidimensional. T = Int(Rnd * 31) + 20 'Nmero de rascadas, T valdr de 20 a 50 For i = 1 To T H = Int(Rnd * 23) + 1 'H valdr de 1 a 23 Horas(H) = Horas(H) + 1 Next Los ejercicios usando este ejemplo: 1. Saber que hora tiene el valor mayor y a que hora empezaste a rascarte (es decir la primera hora del array que contiene un valor) 2. Que hora fue la ltima en que te arrascaste (no necesita explicacin...) 3. Modificar el ejemplo anterior para que el nmero de veces que te rascas valga (aleatoriamente) de 100 a 1000 y saber tambin cual de estas horas tiene el valor menor (en caso de que haya varios, slo tienes que averiguar uno de ellos) Para que no te compliques mucho la vida, decirte que con un par de lneas, puedes averiguar el mayor o el menor... no sea que quieras hacer un mogolln de comparaciones. A disfrutarlo! Esta entrega no da ms de s, no es que haya querido hacerla deprisa y corriendo, es que realmente lo "bsico" est aqu explicado, si quieres profundizar ms, ya sabes dnde buscar informacin... en los libracos esos que venan con tu VB. Si an as, piensas que no te has enterado... repsatela unas diecisis veces ms... Nos vemos. 10/Ene/98: Las soluciones de esta entrega.

108

Curso Bsico de Programacin en Visual Basic


Pues se me fue la olla ms de la cuenta... y a pesar de que algunos me lo han recordado... en fin... aqu estn las soluciones de los ejercicios de la dcima entrega. Por si no te acuerdas, estos eran los ejercicios: (es que yo ya no me acordaba... y como he perdido la chuleta con las soluciones, tendr que resolverlos... hum!) Si quieres pasar directamente a las soluciones y saltarte este "cachito", pulsa aqu.

[repeticin del final de la entrega 10] Ahora los ansiados ejercicios, (realmente ha sido cortita esta entrega verdad?) Para los ejercicios, usando este trozo para guardar nmeros aleatorios en un array unidimensional, espero que no tengas problemas para guardarlos en un array multidimensional. T = Int(Rnd * 31) + 20 'Nmero de rascadas, T valdr de 20 a 50 For i = 1 To T H = Int(Rnd * 23) + 1 'H valdr de 1 a 23 Horas(H) = Horas(H) + 1 Next Los ejercicios usando este ejemplo: 1. Saber que hora tiene el valor mayor y a que hora empezaste a rascarte (es decir la primera hora del array que contiene un valor) 2. Que hora fue la ltima en que te arrascaste (no necesita explicacin...) 3. Modificar el ejemplo anterior para que el nmero de veces que te rascas valga (aleatoriamente) de 100 a 1000 y saber tambin cual de estas horas tiene el valor menor (en caso de que haya varios, slo tienes que averiguar uno de ellos) Para que no te compliques mucho la vida, decirte que con un par de lneas, puedes averiguar el mayor o el menor... no sea que quieras hacer un mogolln de comparaciones.

Las soluciones:
109

Primero: He hecho un pequeo cambio al ejemplo que se usara, para que tambin se incluya la HORA CERO, esta sera una de las formas de conseguirlo:

'Ejercicio 1 Dim T%, i%, H%, Horas%(0 To 23) Randomize T = Int(Rnd * 31) + 20 'Nmero de rascadas, T valdr de 20 a 50 For i = 1 To T H = Int(Rnd * 24) 'H valdr de 0 a 23 Horas(H) = Horas(H) + 1 Next

Dim ValorMayor%, HoraValorMayor%, HoraInicioRascada% 'Este valor ser para saber que no se ha asignado Const NoAsignado = -1 ValorMayor = 0 HoraValorMayor = NoAsignado HoraInicioRascada = NoAsignado 'Las horas van de 0 a 23 For H = 0 To 23 'Si esta hora tiene algn valor, hacer las comprobaciones If Horas(H) Then ' '===Para saber la primera hora de la rascada=== ' 'Si la hora de inicio no se ha asignado If HoraInicioRascada = NoAsignado Then 'Asignar esta hora HoraInicioRascada = H End If ' '===Para saber la hora que tiene el valor mayor===

110

' 'Si el valor actual es mayor... If Horas(H) > ValorMayor Then ValorMayor = Horas(H) HoraValorMayor = H End If End If Next 'Para mostrar los valores: 'HoraValorMayor ser la hora que tiene el valor mayor 'El valor mayor se puede conseguir as: ' ValorMayor ' Horas(HoraValorMayor) Esta forma dar error si no hay ninguna hora con el valor mayor ' 'HoraInicioRascada ser la hora en la que empezaste a rascarte... ' Lo nico que hay que notar es lo siguiente: Ya que las horas empezarn por CERO, debemos asignar un valor "inexistente" a los valores de las variables que contendrn las horas de inicio y de mayor valor... Por qu? Porque el Basic asigna automticamente el valor cero a las variables numricas y como estas variables pueden tener un valor de CERO (0) a Veintitrs (23), el valor por defecto puede ser un valor vlido... as que asignndole -1 (menos uno), nos aseguramos que no tendr un valor que pueda confundirnos... lo captas? si no es as... ya te enterars cuando te ocurra... ;-)

El segundo: Usando la parte de asignacin de las horas, es lo mismo que para saber la primera hora, pero usando otra variable. 'Ejercicio 2 Dim UltimaHora As Integer 'Este valor ser para saber que no se ha asignado Const NoAsignado = -1 UltimaHora = NoAsignado 'Las horas van de 0 a 23

111

For H = 0 To 23 'Si esta hora tiene algn valor, hacer las comprobaciones If Horas(H) Then ' '===Para saber la ultima hora de la rascada=== ' 'Si la hora actual es mayor que la asignada If H > UltimaHora Then 'Asignar esta hora UltimaHora = H End If End If Next Esta solucin no tiene mayor inconveniente, una vez comprendida la primera solucin.

El tercero: Para que el nmero aleatorio que se asigna est entre 100 y 1000, cambia la asignacin a T de esta forma: T = Int(Rnd * 901) + 100 Recuerda que Int(Rnd * 901) produce un valor que va desde 0 a 900 ambos inclusive, as que sumndole 100 tendremos un valor de 100 a 1000. Para averiguar la hora con el menor valor, usaremos dos variables, una para guardar el valor menor y otra para que "recuerde" a que hora ocurri eso... Aunque habrs comprobado que eso de averiguar el valor menor no es tan "lgico" como parece... La explicacin es que al asignar el valor CERO a una variable cuando se inicializa, este valor puede ser menor que cualquiera que se haya asignado, este problema no existe si se asignan valores negativos, pero como ese no es el caso... Para solucionarlo, se pueden hacer varias cosas, aqu explico las dos que creo que cubren todas las posibilidades: 1. Si HoraValorMenor an no se ha asignado, la primera hora que se compruebe, ser la hora con el menor valor. 2. Cuando es la "primera" hora que se comprueba, ese debe ser el valor menor hasta el momento. Esto mismo se puede hacer para el valor mayor, si sabemos que pueden asignarse

112

valores negativos... ya que si as fuera, al tener un valor cero, ste sera mayor que cualquier nmero negativo...

'Ejercicio 3 Dim T%, i%, H%, Horas%(0 To 23) Randomize T = Int(Rnd * 901) + 100 'Nmero de rascadas, T valdr de 100 a 1000 For i = 1 To T H = Int(Rnd * 24) 'H valdr de 0 a 23 Horas(H) = Horas(H) + 1 Next Dim ValorMenor%, HoraValorMenor% 'Este valor ser para saber que no se ha asignado Const NoAsignado = -1 ValorMenor = 0 HoraValorMenor = NoAsignado 'Las horas van de 0 a 23 For H = 0 To 23 'Si esta hora tiene algn valor, hacer las comprobaciones If Horas(H) Then ' '===Para saber la hora que tiene el valor menor=== ' 'NOTA: 'Para que FUNCIONE, hay que hacer una de estas dos cosas: ' '1- Si no se ha asignado el valor menor... If HoraValorMenor = NoAsignado Then ValorMenor = Horas(H) HoraValorMenor = H End If '2- Si es el primer valor, ser el menor...

113

If H = 0 Then ValorMenor = Horas(H) HoraValorMenor = H End If ' 'hasta que se demuestre lo contrario... If Horas(H) < ValorMenor Then ValorMenor = Horas(H) HoraValorMenor = H End If End If Next Para Terminar: Bien, ya tenemos todas las soluciones, ahora pongmoslo todo junto y veamos para que sirve eso de NoAsignado... 'Todos los ejercicios Dim T%, i%, H%, Horas%(0 To 23) Randomize T = Int(Rnd * 901) + 100 'Nmero de rascadas, T valdr de 100 a 1000 For i = 1 To T H = Int(Rnd * 24) 'H valdr de 0 a 23 Horas(H) = Horas(H) + 1 Next Dim ValorMayor%, HoraValorMayor%, HoraInicioRascada% Dim UltimaHora% Dim ValorMenor%, HoraValorMenor% 'Este valor ser para saber que no se ha asignado Const NoAsignado = -1 ValorMayor = 0 HoraValorMayor = NoAsignado HoraInicioRascada = NoAsignado UltimaHora = NoAsignado ValorMenor = 0

114

HoraValorMenor = NoAsignado 'Las horas van de 0 a 23 For H = 0 To 23 'Si esta hora tiene algn valor, hacer las comprobaciones If Horas(H) Then ' '===Para saber la primera hora de la rascada=== ' 'Si la hora de inicio no se ha asignado If HoraInicioRascada = NoAsignado Then 'Asignar esta hora HoraInicioRascada = H End If ' '===Para saber la ultima hora de la rascada=== ' 'Si la hora no se ha asignado If H > UltimaHora Then 'Asignar esta hora UltimaHora = H End If ' '===Para saber la hora que tiene el valor mayor=== ' 'Si el valor actual es mayor... If Horas(H) > ValorMayor Then ValorMayor = Horas(H) HoraValorMayor = H End If ' '===Para saber la hora que tiene el valor menor=== ' 'NOTA: 'Para que FUNCIONE, hay que hacer una de estas dos cosas: ' '1- Si no se ha asignado el valor menor...

115

If HoraValorMenor = NoAsignado Then ValorMenor = Horas(H) HoraValorMenor = H End If '2- Si es el primer valor, ser el menor... If H = 0 Then ValorMenor = Horas(H) HoraValorMenor = H End If ' 'hasta que se demuestre lo contrario... If Horas(H) < ValorMenor Then ValorMenor = Horas(H) HoraValorMenor = H End If End If Next 'Mostrar los datos: Dim sTmp As String sTmp = "Estos son los valores:" & vbCrLf If HoraInicioRascada <> NoAsignado Then sTmp = sTmp & "Hora de inicio de rascada: " & CStr(HoraInicioRascada) & vbCrLf End If If UltimaHora <> NoAsignado Then sTmp = sTmp & "ltima hora de rascada: " & CStr(UltimaHora) & vbCrLf End If If HoraValorMenor <> NoAsignado Then sTmp = sTmp & "La hora en la que empezaste a rascarte fue: " & CStr(HoraValorMenor) & vbCrLf End If If HoraValorMayor <> NoAsignado Then sTmp = sTmp & "La hora en la que terminaste de rascarte fue: " & CStr(HoraValorMayor) & vbCrLf

116

End If MsgBox sTmp

Curso Bsico de Programacin en Visual Basic


Cuanto tiempo, verdad? Pues ya ves que no desisto, te costar librarte de mis cursos, j, j. Antes de empezar, quiero agradecer a: Pedro Jos Gonzlez Casares piter@arrakis.es por haberse entretenido en corregir los errores tipogrficos esos que uno suelta de vez en cuando... gracias. A ver si me acostumbro a escribirlos en el Word y as pasarle el corrector, es que me da algo de pereza y como me entiendo bien con el FrontPage Express este que incluye el IE4, pues para que cambiar. Quizs te preguntars porqu no uso el FrontaPage 98 (la beta que es la gratuita) y as puedo pasarle el corrector... pues porque el diccionario que incorpora est en "gringo" y as cualquiera corrige... Lo primero es lo primero, as que, en este link tienes las soluciones de los ejercicios de la dcima entrega. Y ya vamos al tema de hoy:

Nuestras propias variables


Ya has visto prcticamente la totalidad de tipos de variables que Visual Basic soporta, tambin has visto cmo agrupar variables para crear arrays. Ahora voy a explicar cmo puedes crear tus propias variables. Realmente no es crear una variable, sino un tipo de variable especial en el que se pueden agrupar variables de distintos tipos; es lo que en otros lenguajes se llaman estructuras. En Basic son tipos definidos por el usuario (TDU o UDT User Defined Type, que diran los English-speaken esos) En un UDT podemos incluir variables que estn relacionadas, realmente puedes incluir casi todo lo que se te ocurra, pero no tendra mucho sentido incluir cosas que no tengan relacin, vamos, digo yo. Imagnate que quieres hacer una utilidad que dibuje puntos en la pantalla y necesitas tener la posicin de, digamos 1000 puntos, una solucin sera declarar dos arrays, una para la posicin de la fila y otra para la columna de cada punto: Dim PuntoY(1000) As Integer, PuntoX(1000) As Integer Cuando necesitemos dibujar el punto N en X,Y, haramos algo como esto:

117

PuntoY(N) = y: PuntoX(N) = x Y si lo que pretendemos es averiguar la posicin del punto A, lo sabramos as: y = PuntoY(A): x = PuntoX(A) Simplemente estaramos usando un array para la fila (PuntoY) y otro para la columna (PuntoX), para simplificar todo esto, podemos crear un tipo en el cual tendramos almacenado la posicin de cada punto, para ello, hay que hacer una declaracin de la siguiente forma: Type tPunto X As Integer Y As Integer End Type cada vez que necesitemos una variable de este Nuevo tipo, tendremos que declararla como cualquier otra variable: Dim unPunto As tPunto Mu bonito, pero cmo asignamos los valores? De una forma muy especial, para acceder a cada uno de los datos que puede almacenar nuestra variable tendremos que especificar el nombre de la variable, un punto y a continuacin la variable interna que nos interese... Vemoslo: unPunto.X = 100 unPunto.Y = 20 Para saber el valor guardado en la X de nuestra variable lo sabramos as: columna = unPunto.X Siempre usando el punto despus de la variable interna, esto puedes encontrrtelo en algunos libros o manuales, usando la expresin: "para acceder a un miembro de una estructura de datos definida por el usuario..." pero el significado, al final, es el mismo... Bien, ahora si queremos crear un array para guardar los mil puntos esos a los que me refera al principio: Dim Puntos(1000) As tPunto Para almacenar la posicin del punto N:

118

Puntos(N).X = x: Puntos(N).Y = y Y seguro que ahora sabrs como obtener la posicin del punto A. Pero tambin podemos almacenar el punto actual en una variable normal de este tipo y asignar ese valor a un elemento del array: Dim PuntoActual As tPunto PuntoActual.X = un_valor: PuntoActual.Y = otro_valor Puntos(N) = PuntoActual

Distintos tipos de variables en un tipo definido Los tipos definidos, no slo sirven para "mezclar" variables del mismo tipo, sino que puedes tener variables de varios tipos, incluso variables de tipos definidos... S, un verdadero lo... Espero que despus de leerte esta entrega y con los ejemplos que veremos en las prximas, (cuando le toque el turno al manejo de ficheros), se te aclararn las dudas.

Otro ejemplo clsico Este es uno de los ms usados para este tipo especial de variables y la verdad es que es tambin el ms usado, enseguida sabrs porqu. Situacin: Datos: El Tipo: Tener los datos de todos los colegas y otros que no lo son tanto. Nombre y apellidos, direccin, telfono fijo, telfono mvil, direccin e-mail, URL y cualquier otra cosa que se te ocurra. Para un caso como este, (simplificando un poco), podramos usar este tipo definido: Type tColega Nombre Apellidos Direccion Poblacion Edad As String As String As String As String As Integer As Long 'Esto por si

VecesQueLeHeMandadoUnMailYNoContesta soy yo 8-) End Type

Fjate en el ltimo "campo" del tipo, es una chorrada (aunque ms de uno no pensar as), pero es para que veas que se pueden usar nombres de variables "super-largos",

119

hasta 40 caracteres! Es decir, que si te gust Mary Poppins, podras tener una variable que se llamara: MeGustaSupercalifragilisticoespialidoso. Ya en serio, no es conveniente el uso de nombres tan largos, no hagas caso de la propaganda esa que te dicen que es mejor usar nombres descriptivos, ya que es una lata tener que escribirlos!!! En caso de que te de el "punto" de escribir nombres largos, puedes hacerlo, incluso puedes usar ms de 40 letras, slo que las primeras 40 tienen significado... es decir que si al supercali... ese le aades ms letras, slo reconocer las 40 primeras. Por ejemplo: SupercalifragilisticoespialidosoChitiChitiBangBangVolando SupercalifragilisticoespialidosoChitiChitiBangBangParado Para el VB ser la variables (un momento que cuente las letras): SupercalifragilisticoespialidosoChitiChi De todas formas sigo pensando que es algo tedioso eso de escribir nombres tan largos... Otro caso de las variables, creo que este an no lo hemos visto, es este: Cuando vayamos a usar este tipo de variables para guardar los datos en ficheros (dentro de un par de entregas ya los estars usando), es conveniente "definir" la longitud mxima de las cadenas de caracteres, por ejemplo: Reservar 20 caracteres para el nombre 50 para la direccin, en este caso la declaracin de las variables se haran as: Dim Nombre As String * 20

Dim Direccion As String * 50 Y si estn en un tipo definido: Type tFijos Nombre End Type La ventaja de hacerlo as: al tener una longitud fija, podemos acceder a cualquier registro haciendo unos pequeos clculos... aunque de esto se encarga de forma automtica el propio Basic y como he dicho antes: lo veremos ms despus. As String * 20 Direccion As String * 50

Vamos con algunos ejemplos. Ya tenemos definido el tipo tColega, si queremos usarlo slo hay que DIMensionar una variable para que sea de ese tipo: Dim unColega As tColega Y para guardar el nombre de ese colega:

120

unColega.Nombre = "Pepito" Con los dems campos se hara igual. Ahora, se nos presenta la situacin de que tenemos, por poner un ejemplo, 50 colegas; as que vamos a reservar espacio para todos ellos: Dim misColegas(1 To 50) As tColega Para almacenar el nombre del colega nmero uno: misColegas(1).Nombre = "Maria de las Mercedes" Para mostrarlos, simplemente hacemos un bucle que recorra este array y asunto concluido: For i = 1 To 50 Print misColegas(i).Nombre, misColegas(i).Apellidos, ...etc. Next Que quieres imprimir de forma aleatoria uno de los 50 nombres, digamos para gastarle una inocentada, pues haces esto: Print misColegas(Int(Rnd * 50) + 1).Nombre Ya sabes, si no lo sabias, ahora lo sabrs, que el ndice de un array, el numrico ese que se pone dentro de los parntesis, puede ser cualquier expresin numrica, que de como resultado un valor que est dentro de los lmites de la cantidad de variables que tiene ese array... S, es que si ese valor no est "dentro" de los elementos que tienes dimensionados, te "regaar" el VB dicindote: Index Out of Range (o sea: T'as pasao, colega)

Un alto en el camino. Toma papel y lpiz, porque esto es una nueva instruccin. Ya has visto en el ejemplo de imprimir los 50 nombres, que cada vez que accedes a uno de los campos (o variables internas) del tipo definido, tienes que usar el nombre de la variable el punto y despus el campo. Pues a partir del VB4, este asunto se ha simplificado y no slo para los tipos definidos, ya vers, en un futuro no muy lejano, calculo que antes del ao 2010, que se puede usar en todas las situaciones en las que "algo" tenga otros "algos" dentro de l y haya que acceder por medio del punto... Si no lo captas, no te preocupes, ya te enterars bien...

121

La palabra mgica es: WITH No te voy a hacer una presentacin formal de esta instruccin, ya tienes el manual del VB o la ayuda y all seguro que estar bien "definida", vamos a ver cmo usarla en el ejemplo este que nos traemos entre manos: For i = 1 To 50 With misColegas(i) Print .Nombre, .Apellidos, .Direccion, ...etc. End With Next Ves que fcil! Hasta he puesto otro de los campos... De esta forma no tienes que repetir el nombre de la variable, el Visual ya sabe que te ests refiriendo a misColegas(i), porque esa es la variable que has usado despus de With. Esto mismo se puede usar con cualquier objeto del VB, los tipos definidos no son objetos, pero se parecen, en unas cuantas de miles de entregas ms, te enterars del porqu... Por ejemplo para asignar varias de las propiedades de un TextBox llamado Text1: With Text1 .SelStart = 0 .SelLength = Len(.Text) End With Este mismo ejemplo sin With, como lo tendran que hacer con el VB3, sera esto: Text1.SelStart = 0 Text1.SelLength = Len(Text1.Text) Como comprobars, est ms claro si se usa el With Adems, se pueden anidar varios Withs... unos dentro de otros, pero siempre el PUNTO har referencia al ltimo que se ha puesto, esta situacin ni la voy a "ejemplificar" ya que cuando le toque el turno, le tocar...

Bueno, vamos a dejarlo por ahora; en la siguiente parte de esta entrega, usaremos todo esto en un pequeo programa en el cual se introducirn los datos y todas esas cosas... Pero eso ser maana (hoy realmente, pero ms tarde) En seguida nos vemos.

122

Curso Bsico de Programacin en Visual Basic


Y estas son las soluciones a los dos ejercicios propuestos.

1.- Poder modificar uno de los colegas. Este es el cdigo a insertar en el Command3_Click:

Cdigo para modificar un colega El truco ser primero mostrar slo un colega y a continuacin, modificar los campos que queramos, el cdigo del Command2_Click quedara as, para que al escribir un nmero en el Text7, sea este colega el que se muestre, en caso de que el nmero introducido sea menor que 1 o mayor que el nmero actual de colegas, se mostrarn todos.

123

Los cambios al Command2_Click

Curso Bsico de Programacin en Visual Basic


Lo prometido es deuda y a pesar de las numerosas y continuas desconexiones (cortes en la lnea, ruido, etc, etc), aqu est el "final" de la undcima entrega... Te creas que era el final del curso...? Pues an te queda mucho que aguantar... Vamos a continuar la entrega con un ejemplo. Crea un nuevo proyecto, aade 6 labels y 6 textboxes, un par de commandbuttons... Cmo? Que no sabes cmo hacerlo... hum!

124

Cuando inicias el VB, se crea un nuevo proyecto con un form por defecto, as que ese paso lo puedes conseguir simplemente cargando el Visual Basic. Una vez que tienes esto, te mostrar una pantalla como esta:

Pantalla de inicio del VB4 A la izquierda est la barra con los controles que se pueden usar, pulsa (doble click) en el que tiene la A, esto situar una etiqueta llamada Label1, en el centro del form... sitala en la esquina superior izquierda, para ello plsala con el botn izquierdo del ratn y arrstrala hasta arriba y a la izquierda... Pulsa otras cinco veces... y ve colocndolas debajo de la anterior, es decir una debajo de otra... Ahora debes pulsar el que est al lado de la etiqueta: (textbox) y haz la misma operacin, pero los sita junto a cada una de las etiquetas anteriores. Una vez terminado todo el proceso, debers tener seis etiquetas y seis cajas de texto. Por ltimo pulsa en el botn que est debajo de la caja de texto y los colocas en la parte inferior derecha, pulsa de nuevo en el mismo objeto y lo pones justo al lado del anterior, al final debes tener algo como esto:

125

El form de prueba, con todos los controles Hay una forma ms rpida de hacerlo... y que adems te permite, si quieres, crear arrays de controles. Sera pulsando en el Label una vez, a continuacin en el TextBox. Los seleccionas y le das a Edicin/Copiar, tambin con el botn derecho del ratn. Cmo los seleccionas para poder copiarlo? Pulsa el Label, pulsa la tecla Control, dejando pulsada la tecla control, pulsa en el TextBox, vers que se quedan los dos "resaltados", ahora suelta la tecla Control, en el men de Edicin, selecciona Copiar (o Copy si tienes la edicin inglesa). Ya estn copiados en la memoria del VB, ahora en el men Edicin selecciona Pegar (Paste en guiri), te mostrar un mensaje de que si quieres crear un array del Label1, pulsa Si o No, dependiendo de que quieras crear ese array o no, en el Label haz lo que quieras, pero cuando te pregunte si quieres crearlo del TextBox, dile que no... Sita los nuevos controles debajo de los anteriores y repite la operacin, pero en esta ocasin slo tienes que volver a pegar... ya que an siguen copiados en memoria. Ahora ocurrirn dos cosas, dependiendo de si le dijiste que SI o NO a la creacin de arrays del Label, en caso de haberle dicho SI, slo preguntar si quieres crear un array de TextBox1; por otro lado, si le contestaste No, te preguntar de nuevo si quieres crear el array de Label1 y despus te interrogar sobre el TextBox1... Reptelo hasta que tengas 6 controles de cada en el form y despus haces lo de los botones... Esto lo practicas unas 500.000 veces y acabas por cogerle el "tranquillo"... 8-) Un detalle: el texto que te mostrar ser Label1 y Text1, pero los nombres de los controles sern diferentes, sino me crees, ve pulsando cada uno de ellos y busca la propiedad Name de la ventana esa que hay a la derecha, la que pone Properties - Form1. Lo que interesa es que tengas los controles mostrados en la figura anterior y que los TextBoxes tengan los nombres Text1, Text2... hasta Text6, los botones deben llamarse Command1 y Command2.

126

Ahora abre el panel de cdigo, para hacer esto... cosa que a estas alturas ya deberas saber, es pulsando en el botn "View Code", ese que est en la ventana del proyecto, s, esa... la de la esquina superior derecha. Escribe esto:

El cdigo de las declaraciones

Con esto acabamos de declarar el tipo definido, una constante con el nmero mximo de colegas que por ahora queremos tener, un array para almacenar los datos de esos colegas y una variable que ir llevando la cuenta de los colegas que tenemos actualmente. Volvamos al form, pulsa en el botn Command1 y en la ventana de propiedades busca la que pone Caption, selecciona el texto Command1 y escribe esto: "Nuevo Colega", ahora pulsa en el Commad2 y cambia el caption por "Mostrar". En cada uno de los Labels, empezando por el de arriba, escribe el nombre de los campos de que se componen nuestro tipo definido, en el ltimo, puedes poner algo ms corto... por ejemplo: Veces. El siguiente cdigo lo pones en el Form_Load, para que se ejecute cuando inicies el proyecto.

127

Cdigo que se ejecutar al iniciarse el proyecto Esto otro lo escribes en el Command1_Click:

Cdigo que se ejecutar al pulsar en el Command1 Y por ltimo, escribe esto en el Command2_Click:

128

Cdigo que se ejecutar al pulsar en Command2 Con esto, ya puedes escribir los datos correspondientes y despus pulsas en Nuevo Colega, los datos escritos se asignarn a cada uno de los campos del tipo definido, como estamos usando un array, hay que especificar el nmero en el que queremos insertar esos datos, la variable Colega se va incrementando y en caso de que pulsemos en nuevo colega y ya tengamos el nmero mximo, nos mostrar un mensaje indicndonos que ya no hay espacio para ms. El botn de mostrar los datos, lo que hace es que va mostrando cada uno de los colegas que tenemos... te los tienes que ver todos... as que... prepara el cuerpo para los ejercicios de esta entrega, ah van: 1. Poder modificar uno de los colegas. Pista: Aade un nuevo TextBox, un nuevo CommandButton, en el caption del botn escribes: "Modificar" y el nmero que introduzcas en el nuevo TextBox ser el colega a modificar. 2. Mostrar los colegas a partir de un nmero determinado , por ejemplo, si en ese TextBox escribes 5, mostrar desde el 5 hasta el ltimo introducido, puedes usar el mismo botn Mostrar, de forma que si pones 0 1, te muestre todos. Las soluciones estn en este link, (es que como no lo haga ahora, se me olvida...), pero no hagas trampas e intntalo primero... ya sabes que slo t sabrs si ests jugando limpio.

129

Y hasta aqu ha llegado esta undcima entrega, espero que no te impacientes hasta la siguiente, pero para que te vayas haciendo el cuerpo, vamos a tratar el manejo de ficheros, es decir guardar y recuperar datos del disco... As que atento y espero, como es costumbre, tu comentario sobre esta entrega o sobre el curso en general... pero no me hagas trampas y aproveches el link para hacerme consultas... que el consultorio del Dr. Guille est cerrado temporalmente... ;-) Nos vemos. Guillermo

Curso Bsico de Programacin en Visual Basic


Cuanto tiempo, verdad? Pues ya ves que no desisto, te costar librarte de mis cursos, j, j. Antes de empezar, quiero agradecer a: Pedro Jos Gonzlez Casares piter@arrakis.es por haberse entretenido en corregir los errores tipogrficos esos que uno suelta de vez en cuando... gracias. A ver si me acostumbro a escribirlos en el Word y as pasarle el corrector, es que me da algo de pereza y como me entiendo bien con el FrontPage Express este que incluye el IE4, pues para que cambiar. Quizs te preguntars porqu no uso el FrontaPage 98 (la beta que es la gratuita) y as puedo pasarle el corrector... pues porque el diccionario que incorpora est en "gringo" y as cualquiera corrige... Lo primero es lo primero, as que, en este link tienes las soluciones de los ejercicios de la dcima entrega. Y ya vamos al tema de hoy:

Nuestras propias variables


Ya has visto prcticamente la totalidad de tipos de variables que Visual Basic soporta, tambin has visto cmo agrupar variables para crear arrays. Ahora voy a explicar cmo puedes crear tus propias variables. Realmente no es crear una variable, sino un tipo de variable especial en el que se pueden agrupar variables de distintos tipos; es lo que en otros lenguajes se llaman estructuras. En Basic son tipos definidos por el usuario (TDU o UDT User Defined Type, que diran los English-speaken esos) En un UDT podemos incluir variables que estn relacionadas, realmente puedes incluir casi todo lo que se te ocurra, pero no tendra mucho sentido incluir cosas que no tengan relacin, vamos, digo yo. Imagnate que quieres hacer una utilidad que dibuje puntos en la pantalla y necesitas tener la posicin de, digamos 1000 puntos, una solucin sera declarar dos arrays, una para la posicin de la fila y otra para la columna de cada punto:

130

Dim PuntoY(1000) As Integer, PuntoX(1000) As Integer Cuando necesitemos dibujar el punto N en X,Y, haramos algo como esto: PuntoY(N) = y: PuntoX(N) = x Y si lo que pretendemos es averiguar la posicin del punto A, lo sabramos as: y = PuntoY(A): x = PuntoX(A) Simplemente estaramos usando un array para la fila (PuntoY) y otro para la columna (PuntoX), para simplificar todo esto, podemos crear un tipo en el cual tendramos almacenado la posicin de cada punto, para ello, hay que hacer una declaracin de la siguiente forma: Type tPunto X As Integer Y As Integer End Type cada vez que necesitemos una variable de este Nuevo tipo, tendremos que declararla como cualquier otra variable: Dim unPunto As tPunto Mu bonito, pero cmo asignamos los valores? De una forma muy especial, para acceder a cada uno de los datos que puede almacenar nuestra variable tendremos que especificar el nombre de la variable, un punto y a continuacin la variable interna que nos interese... Vemoslo: unPunto.X = 100 unPunto.Y = 20 Para saber el valor guardado en la X de nuestra variable lo sabramos as: columna = unPunto.X Siempre usando el punto despus de la variable interna, esto puedes encontrrtelo en algunos libros o manuales, usando la expresin: "para acceder a un miembro de una estructura de datos definida por el usuario..." pero el significado, al final, es el mismo... Bien, ahora si queremos crear un array para guardar los mil puntos esos a los que me refera al principio:

131

Dim Puntos(1000) As tPunto Para almacenar la posicin del punto N: Puntos(N).X = x: Puntos(N).Y = y Y seguro que ahora sabrs como obtener la posicin del punto A. Pero tambin podemos almacenar el punto actual en una variable normal de este tipo y asignar ese valor a un elemento del array: Dim PuntoActual As tPunto PuntoActual.X = un_valor: PuntoActual.Y = otro_valor Puntos(N) = PuntoActual

Distintos tipos de variables en un tipo definido Los tipos definidos, no slo sirven para "mezclar" variables del mismo tipo, sino que puedes tener variables de varios tipos, incluso variables de tipos definidos... S, un verdadero lo... Espero que despus de leerte esta entrega y con los ejemplos que veremos en las prximas, (cuando le toque el turno al manejo de ficheros), se te aclararn las dudas.

Otro ejemplo clsico Este es uno de los ms usados para este tipo especial de variables y la verdad es que es tambin el ms usado, enseguida sabrs porqu. Situacin: Datos: El Tipo: Tener los datos de todos los colegas y otros que no lo son tanto. Nombre y apellidos, direccin, telfono fijo, telfono mvil, direccin e-mail, URL y cualquier otra cosa que se te ocurra. Para un caso como este, (simplificando un poco), podramos usar este tipo definido: Type tColega Nombre Apellidos Direccion Poblacion Edad As String As String As String As String As Integer As Long 'Esto por si

VecesQueLeHeMandadoUnMailYNoContesta

132

soy yo 8-) End Type

Fjate en el ltimo "campo" del tipo, es una chorrada (aunque ms de uno no pensar as), pero es para que veas que se pueden usar nombres de variables "super-largos", hasta 40 caracteres! Es decir, que si te gust Mary Poppins, podras tener una variable que se llamara: MeGustaSupercalifragilisticoespialidoso. Ya en serio, no es conveniente el uso de nombres tan largos, no hagas caso de la propaganda esa que te dicen que es mejor usar nombres descriptivos, ya que es una lata tener que escribirlos!!! En caso de que te de el "punto" de escribir nombres largos, puedes hacerlo, incluso puedes usar ms de 40 letras, slo que las primeras 40 tienen significado... es decir que si al supercali... ese le aades ms letras, slo reconocer las 40 primeras. Por ejemplo: SupercalifragilisticoespialidosoChitiChitiBangBangVolando SupercalifragilisticoespialidosoChitiChitiBangBangParado Para el VB ser la variables (un momento que cuente las letras): SupercalifragilisticoespialidosoChitiChi De todas formas sigo pensando que es algo tedioso eso de escribir nombres tan largos... Otro caso de las variables, creo que este an no lo hemos visto, es este: Cuando vayamos a usar este tipo de variables para guardar los datos en ficheros (dentro de un par de entregas ya los estars usando), es conveniente "definir" la longitud mxima de las cadenas de caracteres, por ejemplo: Reservar 20 caracteres para el nombre 50 para la direccin, en este caso la declaracin de las variables se haran as: Dim Nombre As String * 20

Dim Direccion As String * 50 Y si estn en un tipo definido: Type tFijos Nombre End Type La ventaja de hacerlo as: al tener una longitud fija, podemos acceder a cualquier registro haciendo unos pequeos clculos... aunque de esto se encarga de forma automtica el propio Basic y como he dicho antes: lo veremos ms despus. As String * 20 Direccion As String * 50

Vamos con algunos ejemplos.

133

Ya tenemos definido el tipo tColega, si queremos usarlo slo hay que DIMensionar una variable para que sea de ese tipo: Dim unColega As tColega Y para guardar el nombre de ese colega: unColega.Nombre = "Pepito" Con los dems campos se hara igual. Ahora, se nos presenta la situacin de que tenemos, por poner un ejemplo, 50 colegas; as que vamos a reservar espacio para todos ellos: Dim misColegas(1 To 50) As tColega Para almacenar el nombre del colega nmero uno: misColegas(1).Nombre = "Maria de las Mercedes" Para mostrarlos, simplemente hacemos un bucle que recorra este array y asunto concluido: For i = 1 To 50 Print misColegas(i).Nombre, misColegas(i).Apellidos, ...etc. Next Que quieres imprimir de forma aleatoria uno de los 50 nombres, digamos para gastarle una inocentada, pues haces esto: Print misColegas(Int(Rnd * 50) + 1).Nombre Ya sabes, si no lo sabias, ahora lo sabrs, que el ndice de un array, el numrico ese que se pone dentro de los parntesis, puede ser cualquier expresin numrica, que de como resultado un valor que est dentro de los lmites de la cantidad de variables que tiene ese array... S, es que si ese valor no est "dentro" de los elementos que tienes dimensionados, te "regaar" el VB dicindote: Index Out of Range (o sea: T'as pasao, colega)

Un alto en el camino. Toma papel y lpiz, porque esto es una nueva instruccin.

134

Ya has visto en el ejemplo de imprimir los 50 nombres, que cada vez que accedes a uno de los campos (o variables internas) del tipo definido, tienes que usar el nombre de la variable el punto y despus el campo. Pues a partir del VB4, este asunto se ha simplificado y no slo para los tipos definidos, ya vers, en un futuro no muy lejano, calculo que antes del ao 2010, que se puede usar en todas las situaciones en las que "algo" tenga otros "algos" dentro de l y haya que acceder por medio del punto... Si no lo captas, no te preocupes, ya te enterars bien... La palabra mgica es: WITH No te voy a hacer una presentacin formal de esta instruccin, ya tienes el manual del VB o la ayuda y all seguro que estar bien "definida", vamos a ver cmo usarla en el ejemplo este que nos traemos entre manos: For i = 1 To 50 With misColegas(i) Print .Nombre, .Apellidos, .Direccion, ...etc. End With Next Ves que fcil! Hasta he puesto otro de los campos... De esta forma no tienes que repetir el nombre de la variable, el Visual ya sabe que te ests refiriendo a misColegas(i), porque esa es la variable que has usado despus de With. Esto mismo se puede usar con cualquier objeto del VB, los tipos definidos no son objetos, pero se parecen, en unas cuantas de miles de entregas ms, te enterars del porqu... Por ejemplo para asignar varias de las propiedades de un TextBox llamado Text1: With Text1 .SelStart = 0 .SelLength = Len(.Text) End With Este mismo ejemplo sin With, como lo tendran que hacer con el VB3, sera esto: Text1.SelStart = 0 Text1.SelLength = Len(Text1.Text) Como comprobars, est ms claro si se usa el With Adems, se pueden anidar varios Withs... unos dentro de otros, pero siempre el PUNTO har referencia al ltimo que se ha puesto, esta situacin ni la voy a "ejemplificar" ya que cuando le toque el turno, le tocar...

135

Bueno, vamos a dejarlo por ahora; en la siguiente parte de esta entrega, usaremos todo esto en un pequeo programa en el cual se introducirn los datos y todas esas cosas... Pero eso ser maana (hoy realmente, pero ms tarde) En seguida nos vemos.

Curso Bsico de Programacin en Visual Basic


Antes de empezar, recordarte que ya estn las soluciones de la dcima entrega. Te recomiendo que las veas y "las estudies", ya que tiene "truco"... y sera conveniente que te enteraras bien del porqu... chale un vistazo y vers porqu te lo digo. Ya vamos al tema de esta entrega... la nmero doce, (duodcima), es que es un li esto de los nmeros ordinales, as que usar nmeros normalitos, que todos los entendemos mejor... Con esta entrega, empiezo la serie de manejo de datos almacenados en disco. As que presta atencin a esta y las siguientes entregas, para que esto del manejo de la informacin almacenada en disco te sea fcil de usar. Adems de las bases de datos, que tambin veremos en este curso bsico... o en las secuelas que pueda tener... existe una forma de almacenar los datos que nuestra aplicacin pueda manejar. Porque de qu servira hacer, por ejemplo, un programa tipo editor de textos si no pudisemos almacenar lo que se ha escrito... No, an no te voy a explicar cmo hacer un editor de textos, antes hay que ver algunas cosillas ms, pero todo llega en esta vida, as que no desesperes... El Basic maneja tres tipos de ficheros: secuenciales, aleatorios y binarios. Cada uno tiene una serie de caractersticas en la forma de acceder a los datos. El ms bsico y tambin el ms empleado, es el secuencial; con este tipo de fichero, los datos se almacenan y recuperan de forma secuencial, es decir: un dato despus de otro... El inconveniente que tiene esta forma de almacenar los datos, es que para recuperar lo que est en la dcima posicin, tenemos que leer los nueve anteriores... Si tenemos que guardar, por ejemplo, el contenido de un array de strings, lo normal es que lo hagamos secuencialmente, es decir primero el contenido de la primera variable del array, despus la segunda, tercera, etc., hasta llegar a la ltima. Para recuperar estos datos, actuaramos de igual forma, pero asignando al array los datos ledos del disco. Recuerda que para asignar una posicin N, antes tendremos que leer las N-1 posiciones anteriores. La ventaja de esta forma de almacenar los datos, es que la longitud de las cadenas, por ejemplo pueden ser variables... esto, ahora te parecer una cosa normal, pero lo entenders cuando veamos los otros tipos de accesos. Tambin permite que puedas almacenar distintos tipos de datos... no te preocupes, ya sabes que en estas entregas todo est "demostrado"... ms o menos, ya que la teora est bien para los tericos, pero para los torpes como yo... lo mejor es la prctica... no es que quiera llamarte torpe... pero, as me siento menos solo... j, j.

136

El inconveniente es, como ya he repetido, que para acceder a un dato en concreto, se deben "leer" todos los anteriores. Esta inconveniencia del acceso secuencial se arregla usando el acceso aleatorio. Con este tipo de acceso, puedes leer del fichero el dato almacenado en cualquier posicin, sin tener que leer primero todos los anteriores... Pero, no todo es perfecto... tambin tiene un "pequeo" inconveniente... que los datos guardados en un fichero aleatorio deben ocupar el mismo espacio, o sea que sean de la misma longitud... Si guardas cadenas de caracteres, todas ocuparn el mismo espacio en el disco, aunque unas tengan ms caracteres "vlidos" que otras... Tambin se pueden mezclar nmeros y cadenas de caracteres... pero eso tiene tambin sus "inconvenientes" o mejor dicho su "truco" para poder usarlo sin armar un KAOS... Ya que estamos con los distintos tipos de acceso, te dir as por encima de que va el tipo Binario, ste es un poco especial y permite leer la informacin almacenada de la forma que queramos, todo depender de la longitud de la variable que usemos para acceder al fichero... todo estas cosas quedarn explicadas y aclaradas en esta o en prximas entregas... Bien, ya sabes que tipos de ficheros puedes manejar con el Visual Basic, ahora vamos a ver como hacerlo, por supuesto, con las instrucciones correspondientes para poder hacerlo, ya que de eso se trata... o es que esperabas poder acceder a la informacin de los ficheros sin usar instrucciones del VB? Cada vez que quieras abrir un fichero, tienes que usar un nmero de "canal" por el que VB nos suministrar la informacin, este canal. El canal se indica por medio de un nmero de 1 a 255. Gracias a este nmero, el Basic se comunica con el sistema operativo para acceder a los datos. El Basic nos facilita la tarea de conseguir ese nmero, con idea de que no usemos una lnea que est en uso... La instruccin, en realidad es una funcin, para conseguir un nmero de canal libre, es: Freefile. Esta funcin devuelve un nmero entero, el cual se almacenar en una variable y as podremos usarlo para el manejo de los datos almacenados. NumFic = Freefile Una vez que conozcamos un canal por el que poder acceder, tendremos que abrirlo: Open "Prueba.txt" For Output As NumFic Con esta lnea, abrimos el fichero Prueba.txt de forma secuencial para poder escribir en l. Una vez que tenemos una "va" de comunicacin, podremos escribir informacin usando una versin un poco maquillada de la instruccin Print... El maquillaje es el nmero de canal con el que podemos acceder al fichero: Print #NumFic, "Lo que sea" #NumFic es el nmero de fichero (o canal) por el que accedemos al fichero abierto y despus de ese nmero, usamos una coma y a continuacin lo que queremos guardar en el fichero. Cuando hayamos acabado de guardar cosas, tendremos que cerrar el fichero que hemos abierto, para poder liberar ese canal abierto y as poder usarlo en otra ocasin, esto se consigue con el comando Close: Close NumFic Es importante esto de cerrar el fichero abierto, ya que en ese momento es cuando el Basic guarda la informacin que an tiene "temporalmente" almacenada en una memoria intermedia que usa para que el acceso a datos sea, al menos en teora, ms rpido. A esta memoria intermedia se le llama "buffer". El VB la usa para ir guardando la informacin que vamos a grabar fsicamente en el disco, antes de grabarla, la guarda ah y cuando est llena, la escribe en el disco y la libera, esto se consigue con el close, para asegurarnos que todo lo que tenga que estar guardado, realmente lo est. El valor de este

137

bfer para los ficheros secuenciales y aleatorios puede ser de 32767 bytes como mximo, antes con el Basic del DOS el valor por defecto era de 128 bytes y el mximo de 255 caracteres, pero esto hace tiempo que cambi y ahora incluso, (al menos en el acceso de 32 bits), aunque en la ayuda no lo indique as, puede ser mayor que todo eso... Ya tendremos ocasin de comprobarlo. Todo esto est muy bien, pero si quieres especificar esa longitud... cmo y/o dnde se especifica? Ahora sabrs cmo y dnde. Para ello vamos a ver cmo se usa al completo la orden OPEN y sus posibilidades de uso. Open RutaAcceso [For Modo] [Access acceso] [tipo de bloqueo] As [#]nmerofichero [Len=longitudregistro] Lo que est entre corchetes son parmetros opcionales. Fjate en el detalle que FOR Modo est entre corchetes, esto significa que si no se especifica el modo, el Visual Basic entiende que quieres acceder de forma aleatoria. La explicacin de cada uno de estos parmetros los tienes en la ayuda, as que si no quieres esperar a que los explique todos, vete a la ayuda y le echas un vistazo. Yo empezar a explicarte lo que ahora necesitas saber y poco a poco iremos viendo las distintas posibilidades... Pero si no quieres esperar... ya sabes... echa mano del F1 y accede a la explicacin de la ayuda o del manual... Vamos a ver lo que nos interesa de esta instruccin: El path completo, o a medias, de dnde queremos que se almacene el fichero o el lugar en el que est almacenado. Por ejemplo: C:\Datos\Un directorio\Prueba.txt .\Algo\Prueba.txt, siempre que en el directorio actual haya un directorio que se llame "Algo" o simplemente Prueba.txt (esto le indicar que estar en el directorio actual) Output, para ficheros de salida, es decir para guardar los datos. Si el fichero existe, lo borrar (sobrescribir) y si no existe, lo crear. Modo Input, para leer los datos de un fichero ya existente. Append, como el Output, pero aadiendo la informacin al final del fichero, si este ya existe. Random, para acceso aleatorio. Binary, para acceso binario.

RutaAcceso

As NmeroFichero

Aqu se indica el nmero de fichero (canal) por el que accederemos a la informacin. El signo de nmero (#) es opcional. Y NmeroFichero, puede ser una variable o una constante.

138

Las otras opciones ya las veremos, ahora nos centraremos en las cosas que son ms fciles, siempre hay tiempo para complicarse la vida, as que nos la complicaremos ms adelante, cuando ya tengamos un poco de idea de todo este folln... RutaAcceso, a estas alturas deberas saber de que va todo esto del PATH, pero si no lo sabes, te lo explico por encima: un path es una ruta de acceso a un fichero... comor? Pues eso, si quieres guardar la informacin en el disco, tendrs que saber en que parte del disco la quieres guardar, incluso en que disco quieres almacenarla. Y lo ms importante, cmo vas a llamar el sitio en el que se guardar. Esto es un poco como las variables, si quieres tener distintas cosas en la memoria del Basic, usas distintos nombres de variables, pues lo mismo con los ficheros, usando distintos nombres de ficheros puedes tener informacin diferente almacenada en el disco. Para empezar, debes saber que tienes que usar un nombre en el que almacenar la informacin que quieres "conservar", para despus poder acceder a ella en el momento que la necesites. La ventaja de esto con respecto a los nombres de las variables es que puedes usar distintas partes del disco para guardar esa informacin, aunque el nombre "real" del fichero sea el mismo... A ver, si quieres guardar los rascones esos que te dabas en las entregas anteriores, puedes decirle al Basic que quieres usar un fichero que se llame: rascones. Pero suponte que quieres tener todos los rascones de todos los meses del ao almacenados en distintos ficheros, uno para cada mes. Podras hacer algo como esto: darle a cada fichero un nombre diferente o bien usar la "extensin" del fichero para cada uno de los meses... Por ejemplo: rascones.ene para enero, rascones.dic para los de diciembre... etc. Y si quieres que esos datos se guarden en el disco A, pues slo tienes que decirle que el fichero se llama: A:\rascones.ene Si tienes la intencin de guardar cada grupo de ficheros en carpetas (directorios) diferentes, tambin puedes indicarselo en la ruta esta de acceso: C:\Datos\A1998\rascones.ene Por supuesto para poder hacer esto ltimo debes tener un disco C (quin no lo tiene?), un directorio A1998 que est dentro de otro llamado Datos que est a su vez en el directorio raz del mencionado disco C. En caso que no se especifique la ruta completa, el Visual Basic usar el directorio actual para acceder al fichero. Debes saber que el visual crear el fichero indicado, pero si no existen los directorios o no puede tener acceso a ellos, dar error y no abrir el fichero. Que error? El nmero 76: Path not found (No se ha encontrado la ruta de acceso) Hay ms errores, muchos, pero estos ya te los irs encontrando y en su momento veremos cmo poder detectarlos. Veremos tambin cmo crear las rutas esas de acceso, en caso de que no existan, para as asegurarnos que existen antes de guardar la informacin en el disco... pero todo a su debido tiempo... Ahora lo que vamos a ver es unos ejemplos de cmo guardar informacin y despus poder "leerla", ya que esto es lo ms bsico y lo que en principio debemos saber.

Cmo guardar la informacin? Ya te he dicho antes de que con Print se puede guardar la informacin en el disco, veamos cmo: Print #NumFic, Nombre Print #NumFic, 125

139

Tambin podemos guardar esta misma informacin as: Print #NumFic, Nombre, 125 Es decir que si quieremos guardar varias cosas con una misma instruccin, lo haremos usando una coma como separador. De esta forma cada cosa que est separada se guardar en el disco en "lneas" distintas.

Cmo leer la informacin? Para poder leer la informacin, adems de abrir el archivo para lectura modo INPUT, hay que usar una de estas instrucciones: Input #NumFic, Variable Tambin con: Line Input #NumFic, variable. La diferencia entre el Input y el Line Input la veremos dentro de un ratillo. Antes tendremos que ver cmo acceder a ese nombre y a ese nmero que antes hemos guardado... Input #NuFic, unNombre, unNumero Un detalle que debes tener en cuenta es que si el Nombre que guardamos tiene alguna coma, puede que no accedas a los datos como pretendas... Vamos a verlo con un ejemplo. Crea un nuevo proyecto en el VB y aade dos botones (CommandButton), escribe este cdigo y pruebas: Private Sub Command1_Click() Dim Nombre$, Num% Dim NumFic% Nombre = "Prez, Pepito" Num = 22 NumFic = FreeFile Open "C:\Prueba.txt" For Output As NumFic Print #NumFic, Nombre, Num Close NumFic End Sub Private Sub Command2_Click() Dim Cadena$, Numero% Dim nF% nF = FreeFile Open "C:\Prueba.txt" For Input As nF Input #nF, Cadena, Numero

140

Close nF MsgBox "Cadena= " & Cadena & vbCrLf & _ "Nmero= " & Numero End Sub Ahora pulsa en F5 y dale primero al botn Command1, despus le das al Command2 y vers que no te muestra lo esperado. En lugar de mostrar: Cadena= Prez, Pepito Nmero= 22 Te ha mostrado: Cadena= Prez Nmero= 0 Que ha ocurrido? En primer lugar, decirte que esto mismo con el Basic del MS-DOS hubiese dado un error, pero debido a como maneja el VB las variables, se ha tragado lo que ha encontrado... Que ha encontrado? Pues que tiene que asignar a Cadena la "palabra" Prez y a la variable Numero el "nmero" Pepito... que al no ser un nmero, le ha dado el valor cero... Esto es debido a que cuando INPUT lee los datos espera una coma o el final de lnea para "distinguir" entre los diferentes datos a asignar a las variables indicadas. Vale, dirs, pongmoslo en distintas lneas y as los leer correctamente: Input #nF, Cadena Input #nF, Numero Pero con esto no lo solucionars, prubalo y vers que tengo razn. De gente desconfiada est el mundo lleno! ...no te he dicho que dara el mismo resultado... HUM! Bien, cmo solucionarlo? Lo has adivinado? Pues eso mismo, usando el Line Input... Pero con Line Input no se pueden especificar ms de una variable en la misma instruccin... as que ponlas en dos lneas. Line Input #nF, Cadena Line Input #nF, Numero OPS! Que ha ocurrido? Si has pulsado simplemente F5, te habr dado un error al pulsar en el segundo botn... Y si has pulsado Control+F5, te habr indicado, con el mismo error, que los tipos no coinciden, (si usas la versin inglesa: Type Mismatch) Esto es debido a que Line Input slo puede leer cadenas de caracteres, mejor dicho slo se pueden usar variables de tipo string (cadena), ya que esta instruccin lee todo lo que hay en la lnea actual del fichero abierto, hasta el final de la lnea.

141

Cmo solucionarlo? Usando una variable intermedia o simplemente usando el INPUT normal para leer el nmero. Veamos cmo sera de las dos formas: Line Input #nF, Cadena Input #nF, Numero Dim sTmp$ Line Input #nF, Cadena Line Input #nF, sTmp Numero = Val(sTmp) Ahora si que tendremos el resultado correcto: Cadena= Prez, Pepito Nmero= 22 EXACTO! Tampoco nos ha mostrado esto... Por qu? Muy sencillo, realmente no es sencillo, sino que despus de que me haya ocurrido como dos millones de veces, resulta hasta lgico... 8-( Si miras el contenido del fichero C:\Prueba.txt, te dars cuenta de que el contenido de este fichero es: Prez, Pepito 22 Entre Pepito y el 22 hay un tabulador, Chr$(9). Esto es debido a que Print x, y muestra los valores en distintas "posiciones" de tabulacin, lo mismo ocurre cuando se guarda en el disco... Para solucionar todo esto y hacer que la cosa funcione bien, te aconsejo que cada dato lo guardes con distintas instrucciones Print, de esta forma cada dato se guarda en distintas lneas del fichero. As que cambia el cdigo del Command1, para que en lugar de un slo Print, haya dos: Print #NumFic, Nombre Print #NumFic, Num Ahora todo debe funcionar bien. Vale, prubalo si no te fas... Tena yo razn? Pues claro, ...ya que lo he comprobado antes... ;-) Si no sabemos el tipo de datos que tenemos almacenado, lo mejor es usar la instruccin Line Input y as nos curamos en salud, pero si sabemos que, por ejemplo, todos los datos son numricos y se han almacenado sin usar comas... Mejor un ejemplo: En este caso vamos a guardar en un array una serie de nmeros aleatorios, los vamos a guardar en un fichero y despus los leeremos para asignarlos en otro array y los mostraremos en un label. Para hacerlo, crea un nuevo proyecto, el anterior lo puedes borrar ya que es de una inutilidad total. Aade un Label que ocupe prcticamente todo el Form, salvo la parte de abajo, en la que pondrs dos botones. Pega el cdigo este que te pongo, pulsa F5 y primero pulsa en el Command1, para despus pulsar en el Command2

142

Private Sub Command1_Click() Dim Numeros(1 To 10) As Integer Dim i% Dim nFic% Randomize 'Asignamos los valores For i = 1 To 10 Numeros(i) = Int(Rnd * 100) + 1 Next 'Abrimos el fichero nFic = FreeFile Open "C:\Prueba.txt" For Output As nFic For i = 1 To 10 Print #nFic, Numeros(i) Next Close nFic Label1 = "Nmeros guardados en el disco" End Sub

Private Sub Command2_Click() Dim MasNumeros(1 To 10) As Integer Dim i% Dim nFic% 'Abrimos el fichero para leer los datos nFic = FreeFile Open "C:\Prueba.txt" For Input As nFic For i = 1 To 10 Input #nFic, MasNumeros(i) Next Close nFic 'Asignamos estos nmeros al label: Label1 = "" For i = 1 To 10

143

Label1 = Label1 & MasNumeros(i) & vbCrLf Next End Sub Este es un ejemplo sencillo de cmo asignar datos a un array, guardarlos en el disco y despus leerlos. Y hasta aqu hemos llegado... Como ejercicio, haz un programa que al pulsar en un botn, te pida diez nombres, los guarde en un fichero y despus pulsando en otro botn los muestre en un label. No es necesario que uses un array para guardar los datos, pero podras hacer dos versiones, con y sin un array. Como pista te recordar que la funcin InputBox puede servirte para esto de preguntar, ya que el valor que devuelve es la cadena de caracteres introducida en la caja de dilogo que muestra. Esto del InputBox ya lo vimos en la novena entrega, o en las soluciones, pero te explico brevemente cmo funciona: variable$=InputBox("Escribe un nombre") Facil, verdad? Pues esa es toda la pista que te voy a dar. Ahora se "legal" y no veas las soluciones de esta entrega hasta que lo hayas hecho t. En la pgina de las soluciones tienes un "extra", as que aunque sepas cmo hacerlo... te pasas a verla... vale? Espero que te haya resultado instructiva esta entrega, a pesar de haberte dejado con la miel en la boca, pero as son las cosas y no es plan de darlo todo de golpe. Para cuando la siguiente entrega? Ah!, misterios de la vida... eso ni se sabe. As que permanece a la escucha y ya vers cuando... no quiero prometer que ser pronto, que despus me regaas... as, que... a esperar! Si hay algo que no entiendas o simplemente quieres hacer algn comentario sobre esta entrega o cualquier otro tipo de peloteo o lo que te de la gana decirme sobre el curso bsico, usa este link... Pero no lo aproveches para las consultas... que te conozco rosco! Nos vemos pronto. Guillermo

Curso Bsico de Programacin en Visual Basic


144

Como ejercicio, haz un programa que al pulsar en un botn, te pida diez nombres, los guarde en un fichero y despus pulsando en otro botn los muestre en un label. No es necesario que uses un array para guardar los datos, pero podras hacer dos versiones, con y sin un array. Solucin 1: Sin usar Array: Private Sub Command1_Click() Dim i% Dim nFic% Dim Nombre$ 'Solucin 1, sin array nFic = FreeFile 'Abrimos el fichero para almacenar los datos Open "C:\Prueba.txt" For Output As nFic For i = 1 To 10 Nombre = InputBox("Escribe el nombre nmero " & CStr(i)) Print #nFic, Nombre Next Close nFic End Sub Private Sub Command2_Click() Dim i% Dim nFic% Dim Nombre$ 'Solucin 1, sin array Label1 = "" nFic = FreeFile 'Abrimos el fichero para leer los datos Open "C:\Prueba.txt" For Input As nFic For i = 1 To 10 Input #nFic, Nombre Label1 = Label1 & Nombre & vbCrLf Next Close nFic End Sub

145

Solucin 2: Usando un Array: Private Sub Command1_Click() Dim i% Dim nFic% Dim losNombres(1 To 10) As String 'Solucin 1, sin array 'Primero preguntamos los nombres For i = 1 To 10 losNombres(i) = InputBox("Escribe el nombre nmero " & CStr(i)) Next 'Ahora los guardamos nFic = FreeFile Open "C:\Prueba.txt" For Output As nFic For i = 1 To 10 Print #nFic, losNombres(i) Next Close nFic End Sub

Private Sub Command2_Click() Dim i% Dim nFic% Dim variosNombres(1 To 10) As String 'Solucin 2, con array nFic = FreeFile Open "C:\Prueba.txt" For Input As nFic 'Leer los diez nombres For i = 1 To 10 Line Input #nFic, variosNombres(i) Next Close nFic

146

'mostrar los nombres Label1 = "" For i = 1 To 10 Label1 = Label1 & variosNombres(i) & vbCrLf Next End Sub Fjate que con esta segunda forma, si usas un array a nivel de mdulo o global, puedes mostrar los datos cuando quieras, leyndolos una vez y despus mostrndolos en cualquier ocasin, hasta que asignes nuevos datos. Lo mismo ocurre al pedir esos nombres y despus guardndolos en el disco cuando quieras. En el siguiente listado, puedes ver un ejemplo (simple) de esto que te digo. Fjate que a la hora de leer los nombres, primero se comprueba si existe el fichero, esto ya lo vimos en la entrega del IF...THEN Option Explicit Dim losNombres(1 To 10) As String

Private Sub cmdGuardar_Click() Dim i% Dim nFic% 'Ahora los guardamos nFic = FreeFile Open "C:\Prueba.txt" For Output As nFic For i = 1 To 10 Print #nFic, losNombres(i) Next Close nFic Label1 = "Datos guardados correctamente" End Sub

Private Sub cmdLeer_Click() Dim i%

147

Dim nFic% 'Nos aseguramos que exista el fichero: If Len(Dir$("C:\Prueba.txt")) Then nFic = FreeFile Open "C:\Prueba.txt" For Input As nFic 'Leer los diez nombres For i = 1 To 10 Line Input #nFic, losNombres(i) Next Close nFic Label1 = "Datos ledos correctamente" Else Label1 = "No existe el fichero de nombres" End If End Sub

Private Sub cmdMostrar_Click() Dim i% 'mostrar los nombres Label1 = "" For i = 1 To 10 Label1 = Label1 & losNombres(i) & vbCrLf Next End Sub

Private Sub cmdPreguntar_Click() Dim i% Label1 = "" 'Borramos el contenido anterior For i = 1 To 10 losNombres(i) = ""

148

Next 'Tambin se puede hacer as: (esto es ms rpido) 'ReDim losNombres(1 To 10) 'Preguntamos los nombres For i = 1 To 10 losNombres(i) = InputBox("Escribe el nombre nmero " & CStr(i)) Next End Sub

Private Sub Form_Load() Label1 = "" End Sub

Curso Bsico de Programacin en Visual Basic


Desde luego que los das pasan como horas... no s si ser la edad, pero... Jo! Cuando he visto la fecha de la entrega anterior... Hace un mes !!! No voy a prometerte nada, pero har un pequeo esfuerzo para acelerar los plazos de entrega, sino, puede que te aburras o decidas cambiar de profe... La solucin sera: disponer de un porttil... pero... en fin... Esto de la esponsorizacin no da para tanto... lo mismo a ti te sobra uno... ejem! Bueno, despus de la "necesaria" introduccin, vamos a lo que te ha trado por aqu; ya que me imagino que no es precisamente el saber que "necesito" un porttil... esto... si es un Pentium 200, con un mnimo de 32 megas de RAM, pantalla SVGA color, disco duro de un par de Gigas y modem... pues mejor... je, je, nunca se sabe si te puedes encontrar con un alma caritativa... y, como dice el refrn: "El que no llora... no mama..." ;-) Guille! Djate de chorradas y vamos al tema! (esta es la voz de mi conciencia: mi otro yo) En la entrega anterior ya vimos cmo guardar y recuperar informacin de un fichero; vamos a seguir con el tipo secuencial, pero esta vez vamos a leer todo el contenido de una sola vez, para ello vamos a usar dos nuevas funciones: LOF e INPUT. No te confundas con la funcin INPUT, ahora vers que no se usa de igual forma de como usamos anteriormente la "instruccin", recuerda que esta nueva, es una funcin y las funciones siempre devuelven un valor. La que antes usamos era una instruccin y las instrucciones hacen algo, pero no devuelven valores... ya vers que esto mismo se puede

149

aplicar a las funciones; ya que estamos en ello, y a ttulo de curiosidad, si no quieres usar el valor devuelto por una funcin, cosa que se hace muchas veces con las llamadas al API de Windows, debers hacerlo anteponindole a la funcin la instruccin CALL. Esto lo veremos cuando empecemos con el API, que, aunque te parezca que es un tema avanzado, lo empezaremos a ver muy pronto. Ahora veamos cmo usar esas dos funciones: variable = LOF(#canal) Esta funcin devuelve, en bytes, el tamao del fichero abierto con el canal indicado dentro del parntesis. LOF es la abreviatura de: Length Of File (longitud del fichero). Por tanto si queremos saber la longitud de un fichero, lo abrimos, asignamos a una variable el valor devuelto por LOF y despus hacemos lo que tengamos que hacer... Que slo quieres averiguar la longitud, pues lo cierras y ya est, por ejemplo: Dim nFic As Integer Dim sFic As String Dim tamFic As Long sFic = "C:\Autoexec.bat" nFic = Freefile Open sFic For Input As nFic tamFic = LOF(nFic) Close nFic MsgBox "El tamao de " & sFic & vbCrLf & "es de " & tamFic & " bytes" La verdad es que si lo que pretendes es saber la longitud de un fichero, puedes usar la funcin FileLen, sta se usa poniendo el nombre del fichero entre los parntesis y tambin devuelve el tamao en bytes: (Tanto una funcin como la otra devuelven un valor LONG) Dim sFic As String sFic = InputBox("Nombre del fichero:", "Mostrar tamao", "C:\Autoexec.bat") If Len(sFic) Then MsgBox "El tamao de " & sFic & vbCrLf & "es de " & FileLen(sFic) & " bytes" End If Este trozo de cdigo te preguntar, (usando InputBox), el nombre de un fichero, por defecto te mostrar C:\Autoexec.bat y si se ha escrito algo, mostrar el tamao en bytes. Fjate que el valor devuelto por una funcin no slo se puede asignar a una variable, sino

150

que tambin se puede usar directamente. Si, ya s que lo he dicho en otras ocasiones, pero lo repito para que no te quede duda. Ahora veamos cmo "funciona" la funcin INPUT: Esta funcin devuelve una cadena con el contenido de un fichero que est abierto... realmente devuelve el nmero de caracteres que le indiquemos y esos caracteres los tomar del fichero abierto con el "canal" indicado, veamos cmo usarla: cadena = Input$(numCar, #canal) El signo dlar ($), lo uso para saber que estoy trabajando con una funcin que devuelve un valor de cadena... La mayora de este tipo de funciones del Visual Basic, devuelven indistintamente un valor variant o string, para obligarle a que devuelva una cadena, debemos ponerle al final el signo $, ni que decir tiene que esto slo es vlido para las funciones que devuelvan cadenas de caracteres, no para las que devuelvan otro tipo de datos... Pero no te preocupes de este tema, ya que el VB se encarga de hacer lo que tenga que hacer para que obtengas lo que tienes que obtener... Que lo!!! Ahora vamos a ver cmo podemos leer todo el contenido de un fichero y asignarlo en una variable de cadena: Dim nFic As Integer Dim sFic As String Dim tamFic As Long Dim sContenido As String sFic = InputBox("Nombre del fichero:", "Mostrar fichero", "C:\Autoexec.bat") If Len(Dir$(sFic)) Then nFic = FreeFile Open sFic For Input As nFic tamFic = LOF(nFic) sContenido = Input$(tamFic, nFic) Close nFic MsgBox sContenido End If Fjate que uso un MsgBox para mostrarlo, en caso de que el contenido del fichero sea "demasiado" grande, el botn Aceptar no se ver... tienes dos opciones: pulsar Intro o ESC, de esta forma quitars el mensaje de la pantalla, a pesar de que no tenga "visible" el botn Aceptar. Un par de detalles, la funcin Input$() devuelve una cadena de caracteres, el tamao mximo de caracteres que admite estar delimitado por el sistema operativo y sobre todo por la versin del VB, en 32 bits este tamao "casi" no tiene lmite, pero en 16 bits, ser de 64 KB como mximo. El otro detalle es que la comprobacin que se hace para saber si existe el fichero, no es a

151

prueba de "manazas". Me explico: si el nombre que se indica en sFic no existe y/o el path indicado tampoco, no pasa nada, todo funcionar bien; pero si le indicas la unidad A, o cualquier otra, cuando no hay disco insertado, la cosa deja de funcionar y te mostrar un error. Pero todo esto tiene solucin, ahora mismo te dir cual es, pero antes voy a desglosarte el funcionamiento de Len(Dir$(...)) Fjate que aqu se usan dos funciones: Len(Cadena) Dir$(sFichero) Esta funcin devuelve el nmero de caracteres de la cadena indicada Esta otra, lo que devuelve es el nombre del primer fichero que coincida con la "especificacin" indicada en sFichero, en caso de que no haya coincidencias, devolver una cadena vaca.

Por tanto, si no existe el fichero, Dir$ devuelve una cadena vaca y la longitud de una cadena vaca es CERO, as que en la comparacin, If Len(Dir$(sFic)) Then Visual Basic sustituir Len(Dir$(sFic)) por el valor devuelto, recuerda que para IF un cero significa FALSO y cualquier otro valor ser VERDADERO, insisto en este punto, ya explicado anteriormente, para que se te quede claro. Lo que quizs no sepas es que en Dir$(sFic), sFic puede contener signos comodines (? y/o *), para indicar cualquier tipo de fichero. Esto se suele usar tambin en las rutinas de bsqueda que se hacen en las bases de datos, as que mejor que te vayas enterando cmo usarlo y "buscar" por ah informacin, ya que aqu te voy a explicar un poco el significado, para que lo puedas usar al indicar los ficheros. Como "aadido", decirte que en el sistema de bsqueda del Windows 95, tambin puedes buscar ficheros o contenidos, usando estos comodines. Ejemplos: (las pruebas se pueden hacer desde una ventana del MS-DOS, usando el comando DIR) La interrogacin (?) se usa para indicar que no nos importa el carcter que haya en esa posicin. El asterisco (*) sirve para que nos devuelva todos los que tengan los caracteres anteriores a este signo, pero que los restantes no los tenga en cuenta.

Dir Auto*.bat Dir Auto* o Dir Auto*.* Dir A?t*.*

Dir Dato??98.txt

Mostrar todos los ficheros que empiecen por Auto y que la extensin sea .bat Todos los ficheros que empiecen por Auto, sin importar ni la extensin ni nada, ya que al usar .* se indica que cualquier cosa es vlida. Es recomendable usar la segunda forma. Mostrar todos los ficheros que la primera letra sea A y la tercera T, como se usan *, esto indica que nos da igual lo que haya despus de la T, por tanto si tuvisemos ficheros llamados: Arte.txt, Autoexec.bat, Automovil.doc, Arial.ttf, nos devolvera los tres primeros, porque coinciden en lo indicado. Nos devolver todos los ficheros que tengan en las 4 primeras letras la palabra dato y en las posiciones 7 y 8 el nmero 98, la extensin ser txt. Adems en las posiciones 5 y 6 podr tener cualquier cosa, pero deber tener 8 caracteres. As que Dato0198.txt, dato1298.txt sera nombre encontrados, pero no lo sera: data0198.txt ni dato0298.doc, en el primer caso, porque

152

empieza con la palabra data y en el segundo porque la extensin no es txt Una vez visto, por encima, esto de los signos comodines, vamos a ver cmo hacer nuestra forma de comprobar si existe un ficheros, algo ms fiable y segura. Para ello, vamos a crear una funcin que se llame: Existe y esta funcin devolver FALSO (cero) si el fichero no existe y en caso de que exista, devolver VERDADERO (-1) Esta funcin podremos usarla de cualquiera de estas dos formas: If Existe(unFichero) Then 'Si existe, hacer lo que corresponda End If If Not Existe(unFichero) Then 'El fichero en cuestin no existe End If Para poder "detectar", o interceptar, los posibles errores que se produzcan al intentar comprobar si existe el fichero, usaremos esto: On [Local] Error Resume Next Con estas instrucciones, se le indica al Visual Basic que en caso de que se produzca un error, "pase" de ese error y contine con la siguiente instruccin. Esto est bien, pero... cmo sabremos que se ha producido el error? ya que, si continua... pues... eso, no se detiene... Respuesta: Usando la funcin ERR. Esta funcin devuelve el nmero del error que se haya producido, o cero si no se produjo error. A partir del VB4, esta funcin se puede sustituir por la propiedad Number del objeto Err, por tanto Err.Number tambin nos dir que nmero de error se ha producido, ten en cuenta que Number es la propiedad por defecto de este objeto. Pero como quiero que este curso bsico sea lo ms genrico posible y no se incline slo por el VB4 o superior... pues intentar usar funciones que sean compatibles... aunque tampoco prometo nada, as que te recomiendo que dejes de lado las versiones de 16 bits o al menos la versin 3 del VB, porque esta "consideracin" que estoy teniendo puede que cambie... de hecho seguramente pondr cosas que slo estarn disponibles a partir de la versin 4... y cuando la cosa vaya avanzando ms, incluso slo cosas de la versin 5... aunque para esas fechas ya estar en el mercado el VB7, por lo menos... El caso es que yo estoy acostumbrado a usar ERR, as que... eso es lo que hay... je, je. Y ya sin ms rodeos, veamos cmo quedara la funcin Existe, si te fijas es casi igual que la que puse hace ya algunas entregas, pero usando la deteccin de errores: Private Function Existe(ByVal unFichero As String) As Boolean On Local Error Resume Next

153

Existe = Len(Dir$(unFichero)) If Err Then Existe = False End If Err = 0 On Local Error GoTo 0 End Function Te explico cada una de las lneas: Private Function Existe(ByVal unFichero As String) As Boolean Esta es la declaracin de la funcin, el Private es por si lo quieres usar en un form, o en cualquier otro mdulo, pero que slo sea visible para el cdigo de ese mdulo. Si quieres que est visible en todo el proyecto, cosa recomendable para este tipo de funciones, cambia el Private por Public y escribe el cdigo en un mdulo BAS. As podrs usarlo en cualquier form o mdulo que tengas en tu proyecto. Veamos porqu he declarado el parmetro de esta forma: Byval unFichero As String Este es el parmetro que le pasamos a la funcin, es decir: el fichero o especificacin que queremos comprobar si existe. Lo de especificacin es porque puedes usar esta funcin para saber si existen archivos que empiecen por la A, asignando al parmetro los comodines que creas necesarios: If Existe("C:\A*.*") Then comprobar si en el directorio raiz de la unidad C hay archivos que empiecen por la letra A. El ByVal le indica al Visual que use una copia del parmetro, con lo cual evitaremos que el cdigo de nuestra funcin lo modifique; ya que al usar una copia, no tenemos acceso al original, esto tambin acelera el manejo de nuestra funcin. El As Boolean del final, es el tipo de dato que devolver nuestra funcin, tambin se podra usar Integer, de esta forma, si ests usando el VB3, podrs usar la funcin, sin mayor problema. On Local Error Resume Next Esta es la instruccin que pone en marcha la deteccin de errores. Local es para indicarle al VB que slo est operativa dentro de esta funcin, sirve para que, en caso de que externamente haya otro mecanismo de deteccin de errores, el que est operativo sea el que acabamos de declarar... cuando abandonemos la funcin seguir funcionando la otra rutina que hubiera. Existe = Len(Dir$(unFichero)) Asigna a Existe la cantidad de caracteres devueltos por la funcin DIR. Al ser del tipo Boolean, el VB automticamente asigna Verdadero o Falso. En el caso de que la funcin se haya declarado como Integer, un cero indica que es falso y cualquier otro valor que es verdadero, por tanto la cosa funcionar igualmente independientemente del tipo devuelto por esta funcin. Seguro? Veamos un ejemplo:

154

If Not Existe("algo.txt") Then MsgBox "El fichero indicado no existe" End If Prueba esto cambiando el tipo de dato devuelto por la funcin Existe, cuando lo pongas como Integer, siempre te dir que no existe el fichero... La explicacin de porqu ocurre esto, ya lo vimos anteriormente, y es porque NOT Un_Nmero, devuelve un nmero, no un cero... Y recuerda que, cuando Existe es del tipo Integer, devuelve el nmero de caracteres... Si quieres que slo devuelva 0 -1 para que sea igual que False/True, podras hacer esto otro: Existe = Len(Dir$(unFichero)) <> 0 De esta forma se evalua la expresin y en caso de que sea cierto que el resultado es distinto de cero, se asignar un -1, en cualquier otro caso se asignar un cero y ahora si que actuar igual que si el tipo devuelto fuese boolean. If Err Then Ahora comprobamos si se produjo algn error al intentar buscar el fichero en cuestin. En el supuesto de que se produzca un error, se pasar a la lnea despus del IF, asignando FALSE al valor que devolver la funcin. Como sabrs o te habrs imaginado, el valor que debe devolver una funcin se asigna al nombre de la funcin, en otros lenguajes se usa RETURN valor, por ejemplo en C o en el JavaScript... Por tanto, Existe = False, slo se asignar cuando se produzca un error. Err = 0 Esta es una de esas recomendaciones "obligatorias" que yo hara siempre que uses el Resume Next, en este caso no es necesario porque el cdigo de la funcin termina ah, pero si hubiese ms cdigo, ese valor de error seguira asignado a Err y cualquier otra comparacin posterior al objeto Err, podra "alterar" el buen funcionamiento de nuestro programa. (Recuerda que en VB4 o posterior, realmente se asigna a la propiedad Number del objeto Err) On Local Error Goto 0 Esta ltima lnea "libera" la deteccin de errores de esta funcin, en nuestro caso, tampoco es necesaria, ya que al finalizar un procedimiento, cualquier rutina de deteccin de errores se elimina. Esto, al igual que lo dicho anteriormente, sirve si queremos dejar de detectar errores en las siguientes lneas de nuestra rutina. Como ya no hay ms lneas de cdigo, la verdad es que no es necesaria, pero suelo usarla siempre, costumbres que tiene uno de saber cuando dejo de interceptar los errores. Bien, ya tenemos nuestra funcin a prueba de usuarios inexpertos o con malas intenciones... tambin para los olvidadizos... Por qu me miras? Es que nunca has intentado acceder a la unidad A cuando an no has insertado un disquete? Pues yo s... je, je...

155

Vamos a probarla. Crea un nuevo proyecto, agrega un mdulo bas. Asegrate que tiene Option Explicit al principio, ya sabes que esto es para que cualquier error tipogrfico al escribir una variable, no obligar al VB a crearla, sino que nos avisar de que esa variable no existe, es decir, sirve para obligarnos a declarar todas las variables que vayamos a usar. Ahora copia y pega o escrbela nuevamente, la declaracin de la funcin Existe, asegrate que sea pblica y no privada. Cierra el mdulo y asegrate que el Form1 est visible y en modo de diseo, es decir que se vea el Form. Aade un Label, un TextBox y un CommandButton. Modifica el caption del label para que contenga este texto: Fichero a comprobar: En el caption del botn escribe: Comprobar. Sita los controles para que tengan un aspecto "agradable" y escribe lo siguiente en el Command1_Click: Private Sub Command1_Click() If Existe(Text1.Text) Then MsgBox "Si existe el fichero: " & Text1.Text Else MsgBox "NO EXISTE el fichero: " & Text1.Text End If End Sub Prueba a escribir en el TextBox signos comodines para comprobar que todo funciona, por ejemplo: *.bas Igualmente escribe algn nombre que sepas que no existe... y para salir de dudas, intenta acceder a un fichero de la unidad A, pero sin poner un disco... Todo correcto, verdad? Pues me alegro, de que as sea... No, no te preocupes que no hay "gato" encerrado... bueno, s, los tres que tengo en mi casa, pero esos no tienen nada que ver con el programa... No te voy a poner ningn ejercicio en esta entrega, ya lo har en la siguiente, as que paciencia y no desesperes, que ya la tengo escrita en papel, por tanto puede que maana mismo est en lnea... es que si contino, se me va a pasar la hora y quiero que sea hoy da diez el da que publique esta entrega... chorradillas que se le ocurren a uno...

Como es habitual, y las buenas costumbres no hay que perderlas, si quieres hacer algn comentario sobre esta entrega o sobre el curso bsico en general... o tienes ese porttil que me hara tan feliz... y me lo quieres regalar, claro, usa este link Te repito por ensima vez que no aproveches el link para las consultas... que ya nos vamos conociendo... a pesar de que no des la cara... Nos vemos. Guillermo

156

Curso Bsico de Programacin en Visual Basic


No ha sido al da siguiente, pero tampoco ha pasado un mes, as que espero que no te quejes demasiado. Voy a continuar con el tema del DIR$, para ello vamos a hacer un programilla que nos muestre todos los ficheros que coincidan con la especificacin indicada... recuerdas lo de los comodines? Pues en eso nos vamos a enfocar ahora, es decir, servir para que te muestre todos los ficheros BAS de un directorio o lo que quieras... Para ello vamos a usar un control que hasta ahora slo lo hemos visto, pero sin entrar en detalles sobre l: el ListBox. Tambin vamos a usar el DIR$ de otra forma... una de las varias disponibles... Crea un nuevo proyecto, aade un ListBox, un Label, un TextBox y un CommandButton, el aspecto sera ms o menos as:

Haz dobleclick en el Command1 y escribe: Private Sub Command1_Click() Dim sTmp As String On Local Error Resume Next

157

sTmp = Dir$(Text1.Text) If Err = 0 Then Do While Len(sTmp) List1.AddItem sTmp sTmp = Dir$ Loop End If Err = 0 End Sub Ejecuta el programa y escribe *.* en el TextBox, pulsa en el botn y te mostrar en el listbox todos los archivos del directorio actual. Vamos a ver que es lo que hace el cdigo: sTmp = Dir$(Text1.Text) Con esto, guardamos en sTmp el primer fichero que coincida con lo que hemos escrito en el Text1. If Err = 0 Then Si no se produce un error... Do While Len(sTmp) ...se entra en el bucle, pero slo si el contenido de sTmp no es una cadena vaca. Recuerda que DIR$ devuelve una cadena vaca si no se ha encontrado un fichero que coincida con lo indicado... List1.AddItem sTmp Esto aade al ListBox un nuevo elemento con el contenido de sTmp sTmp = Dir$ Fjate que DIR$ se usa sin indicarle nada ms, salo de esta forma si quieres que siga comprobando si hay ms ficheros que coincidan con la especificacin indicada la vez anterior que se le pas un parmetro. Si el contenido del TextBox tena algn signo comodn, Dir$ devolver el siguiente fichero que coincida, en caso de que no queden ms ficheros "coincidentes", devolver una cadena vaca. Loop Repite el bucle si se cumple la condicin que pusimos despus de DO WHILE, es decir: continuar si la longitud, nmero de caracteres, de sTmp NO ES CERO. Prueba, sin cerrar el programa, con varias cosas, por ejemplo: *.vbp, *.bas, etc. Que pasa? Si ests indicando varias cosas que buscar, y las encuentra, te dars cuenta que el listbox se va llenando... es decir, adems de lo nuevo, que estar al final, sigue lo anterior... Cmo solucionarlo? Borrando el contenido del listbox. 'Repetir mientras haya ficheros 'Lo aadimos a la lista 'Asignar el siguiente fichero

158

Para borrar el contenido del listbox, usa esto: List1.Clear Dnde debo ponerlo? En nuestro ejemplo, yo lo pondra justo despus del On Local Error..., o antes, da igual, ya que lo que se pretende es que se borre al hacer CLICK en el botn. Por supuesto no lo pongas dentro del DO...LOOP, ya que no servira para lo que queremos... puesto que se borrara continuamente... S, ya s que no eres tan torpe como para hacerlo, pero... Un detalle que puede que sea simple, pero que en nuestro ejemplo nos ahorrara pulsaciones. Si al pulsar INTRO se simulara el CLICK del botn, nos ahorrara el tener que "desplazarnos" a ese botn para que muestre lo que ha encontrado y si queremos seguir mostrando ms cosas, el tener que desplazarnos nuevamente al TextBox. Para conseguir esto, todo lo que tenemos que hacer, es indicarle al VB que el botn sea un botn "por defecto". Muestra el form, y pulsa una vez en el botn, te mostrar la ventana de propiedades de este control, busca la propiedad DEFAULT y mrcala como TRUE, vers que el botn ahora est remarcado con un borde negro. Ejecuta de nuevo el programa, escribe cualquier cosa en el TEXT1 y pulsa INTRO, ahora te mostrar los ficheros hallados, pero el cursor permanece en el TextBox, listo para poder escribir una nueva especificacin... Ahora vamos a los ejercicios: 1.- Modifica el ejemplo para que en lugar de guardar los ficheros hallados en un listbox, lo haga en un array. 2.- Una vez hecho esto, aade al listbox todos los ficheros hallados... mejor dicho, para que no hagas trampas, aade al listbox el contenido del array, es decir todos y cada uno de los ficheros previamente asignados. 3.- Aade otro botn al form y al pulsar en l, que guarde en un fichero, (por ejemplo: hallados.txt), todos los ficheros hallados, es decir los que estn en el array. Creo que con esto, ya tienes para entretenerte un rato. Como pista, ya sabes el tipo de pistas que doy..., te dir que la asignacin al array, puedes hacerla de dos formas: UNA: Usando un nmero mximo de ficheros a asignar. DOS: Usando un nmero variable, es decir que se aadan slo la cantidad de ficheros hallados. Y si haces los ejercicios de las dos formas posibles, mejor an. Si ves que te atrancas y no sabes por dnde hincarle el diente, aunque sea postizo, chale un vistazo a la entrega ocho y a la entrega doce, en ellas encontrars la solucin a estos ejercicios... bueno, la solucin, lo que se dice la solucin: no, pero si unas verdaderas auto-pistas para poder solucionarlos. Y como soy un poco "diablillo", no te voy a poner un link a las soluciones... al menos hasta que ponga la siguiente entrega, as que... a rabiar unos das! Es que algunas veces... Soy malo! Pues nada, a esperar... que de seguro ser poco, pero mientras tanto...

159

Y recuerda que espero tu comentario, no me preguntes si lo ests haciendo bien o no, que de eso se encargar la pgina con las soluciones, slo pregntame algo que no hayas entendido..., de esta entrega, claro, no de otra cosa que no est en el curso... que casi de seguro estar en otra de las secciones que hay en mis pginas...

Nos vemos. Guillermo

Curso Bsico de Programacin en Visual Basic


Estos eran los ejercicios: 1.- Modifica el ejemplo para que en lugar de guardar los ficheros hallados en un listbox, lo haga en un array. 2.- Una vez hecho esto, aade al listbox todos los ficheros hallados... mejor dicho, para que no hagas trampas, aade al listbox el contenido del array, es decir todos y cada uno de los ficheros previamente asignados. 3.- Aade otro botn al form y al pulsar en l, que guarde en un fichero, (por ejemplo: hallados.txt), todos los ficheros hallados, es decir los que estn en el array. Vamos a ver la solucin al primero de los ejercicios de las dos formas que propuse, es decir usando un nmero fijo de ficheros y uno variable. En el caso del nmero fijo, he asignado un valor de 50, como ese valor est en una constante, slo tendrs que cambiar el valor de la constante para cambiar el nmero de ficheros, veamos el listado:
Private Sub Command1_Click() Dim sTmp As String Const MaxFicheros = 50 Dim sFicheros(1 To MaxFicheros) As String Dim nFic As Integer On Local Error Resume Next sTmp = Dir$(Text1.Text) nFic = 0 If Err = 0 Then Do While Len(sTmp) 'Repetir mientras haya ficheros 'slo asignarlo si tenemos espacio reservado

160

If nFic < MaxFicheros Then nFic = nFic + 1 'Lo aadimos al array sFicheros(nFic) = sTmp End If sTmp = Dir$ Loop End If Err = 0 End Sub 'Asignar el siguiente fichero

Usando un nmero variable de ficheros. En esta ocasin usaremos el Redim Preserve para hacer hueco en el array que guardar los nombres de los archivos.
Private Sub Command1_Click() Dim sTmp As String Dim sFicheros() As String Dim nFic As Integer On Local Error Resume Next sTmp = Dir$(Text1.Text) nFic = 0 If Err = 0 Then Do While Len(sTmp) nFic = nFic + 1 'Adecuar el tamao del array a los ficheros leidos ReDim Preserve sFicheros(nFic) 'Lo aadimos al array sFicheros(nFic) = sTmp sTmp = Dir$ Loop End If Err = 0 End Sub 'Asignar el siguiente fichero 'Repetir mientras haya ficheros

En el segundo ejercicio, hay que guardar el contenido del array, en este caso, el array debe estar declarado a nivel de mdulo, ya que un array declarado dentro de un procedimiento es local a ese procedimiento y por tanto no estar disponible fuera de el. Si no lo haces as cada uno de los arrays que uses (y dimensiones) en cada SUB ser slo

161

visible en ese procedimiento... Por tanto el Dim sFicheros() As String debes ponerlo en la parte de las declaraciones del form. Este cdigo debers agregarlo despus de asignar todos los ficheros al array, justo despus del Loop, para que est dentro del IF que comprueba que no se haya producido error..
Dim i As Integer 'Guardar el contenido del array Open "prueba.txt" For Output As 1 For i = 1 To nFic Print #1, sFicheros(i) Next Close 1

Fijate que no he usado el Freefile para "buscar" un canal libre. En lugar de eso he usado el nmero 1. Te lo digo por dos razones, la primera es para que no lo confundas con la letra L minscula y la segunda es para que sepas que se pueden usar constantes, aunque no te lo recomiendo, pero como en este caso, se con toda seguridad de que mi aplicacin no tiene abierto ningn otro fichero, puedo permitirme el lujo de hacerlo as, de forma directa. El tercer ejercicio, no debera tener mayor problema, todo lo que hay que hacer es un bucle que asigne al listbox cada uno de los ficheros del array:
'Borrar el contenido del listbox List1.Clear 'Agregarle cada uno de los ficheros del array For i = 1 To nFic List1.AddItem sFicheros(i) Next

Este cdigo aadelo justo despus de guardar los datos en el disco, aunque tambin puedes ponerlo despus, siempre que est despus del Loop, cualquier sitio es bueno. Fijate que a pesar de que selecciones distintos tipos de ficheros, sin cerrar el programa, por supuesto, estos no se incrementan en la lista, no slo por el List1.Clear, sino porque al hacer Redim Preserve el nmero de elementos del array se adapta al valor de nFic y este valor empieza siempre por cero, as que siempre se tendr en el array el nmero correcto de ficheros. La asignacin que hago para ponerlo a cero, no es necesaria, ya que cuando se dimensiona una varible numrica, sta variable contiene inicialmente un valor cero. Pero imaginate que no haces esa asignacin o que quieres asegurarte que el contenido del array se "libere" antes de empezar a asignarle datos... para ello tendras que usar: ERASE sFicheros, con esta instruccin borramos el contenido del array. En el caso de que el

162

nmero de elementos del array fuese fijo, a lo que se llama un array esttico, simplemente se borrara el contenido del array, pero seguira existiendo el array con las 50 "dimensiones" creadas. Si, por el contrario, el array es dinmico, es decir que podemos cambiar el tamao del mismo, lo que hacemos es "eliminarlo" de la memoria, por tanto necesitaremos dimensionarlo (o REdimensionarlo) para poder usarlo nuevamente. Bueno, ahora a esperar a la siguiente entrega... hasta entonces... un saludo.

Curso Bsico de Programacin en Visual Basic


Parece ser que pocos hicisteis caso de mi comentario, en la entrega trece, sobre el porttil, lo mismo es que sois supersticiosos, pero la cuestin es que sigo sin l... as que no te quejes de que tarden tanto las entregas sobre el curso bsico... La verdad es que me hubiese gustado encontrarme con un mensajillo de un alma caritativa que me hubiese dicho que pona a mi disposicin el susodicho porttil, pero me aguantar sin l... por lo menos espero los comentarios sobre lo que te parece el curso, aunque ms espero comentarios sobre las cosas que no entiendas, ya que la intencin es que sean claras y que consigas aprender a programar con este lenguaje, que a pesar de que muchos dicen que es fcil, cosa que no dudo, tiene muchas cosillas como para complicarnos un poco la vida y para eso estoy yo aqu, par intentar que no sean tan difciles... al menos lo intento, a ver si lo consigo. Una vez hecho el obligado comentario inicial, vamos a continuar nuestra andadura con los ficheros secuenciales. En esta entrega vamos a preparar una pequea utilidad que nos permitir editar un fichero de texto y guardar los cambios en el disco; no le pidas peras al olmo que no te las dar, as que no creas que vamos a "programar" un verdadero editor... eso puede que sea ms adelante... supongo... Realmente el acceso secuencial va a pintar poco en esto que vamos a hacer, pero nos servir para ir "tomndole" cario a unos controles que usaremos en el 99.9% de nuestras aplicaciones. Para esta tarea, crea un nuevo proyecto y aade un label, dos textbox y dos commandbutton. Distribyelos de esta forma:

163

El Textbox grande (Text2) tiene la propiedad Multiline a True, de esta forma se ir mostrando todo el contenido conforme lo vayamos rellenando. Los nombres de los otros controles sern Label1, Text1, cmdAbrir para el botn de Abrir y cmdGuardar para el de guardar. El problema de los textbox es que no soportan ms de 64KB, aunque en teora si, pero en la prctica lo que soportan son unos 32000 caracteres, que en la versin de 32 bits suponen esos 64KB, por si no lo sabes en Windows de 32 bits cada carcter est representado por dos bytes. Para solventar ese "pequeo" inconveniente, vamos a dar por hecho de que slo admite 32000 caracteres, de esta forma tambin nos servir el programa en VB de 16 bits. A este proyecto hay que agregarle un mdulo BAS que tenga la funcin Existe que vimos en la entrega trece, si no quieres aadir un nuevo mdulo, simplemente copia y pega esa funcin en este formulario. Si la funcin la usas desde un mdulo BAS, debe ser pblica, si la pegas en este formulario, puede ser privada. En el formulario tambin puede ser pblica, pero lo que nos interesa es que pueda accederse desde el form, as que no es necesario declararla de esa forma. Ahora aade el siguiente cdigo para el botn abrir: Private Sub cmdAbrir_Click() 'Abrir Dim i As Long, tamFic As Long

164

If Existe(Text1.Text) Then Text2.Text = "" i = FreeFile Open Text1.Text For Input As i tamFic = LOF(i) Text2.Text = Input$(tamFic, i) Close i End If End Sub Esta rutina no est hecha a prueba de fallos, pero no te preocupes que ya lo hars... como ejercicio? efectivamente! Este es el cdigo del botn guardar: Private Sub cmdGuardar_Click() 'Guardar Dim i As Long i = FreeFile Open Text1.Text For Output As i Print #i, Text2.Text Close i End Sub Fjate que cdigo ms corto... as da gusto hacer programas! Aunque tampoco est preparado para cualquier impedimento... Vamos a aadirle una serie de mejoras, entre ellas el que avise, al guardar, si es que vamos a sobrescribir un fichero existente. Otra mejora sera comprobar si se ha modificado el contenido del Text2 y avisar antes de abrir un nuevo fichero. La tercera mejora que se me ocurre es comprobar que no se abra un fichero con ms caracteres de los "permitidos"... aunque este lo dejar para que lo hagas t. La razn de poner estas mejoras por separado, es decir, contrtelo antes de hacerlo, es para darte la oportunidad de que lo hagas por tu cuenta... Ahora no recuerdo si tienes la base suficiente para hacerlo, pero... podra ser... comprubalo por tu cuenta. Antes de darte la solucin a dos de estas tres mejoras, voy a contarte un "rollito" para que as no se vean las soluciones... no te preocupes que no te voy a contar una de mis batallitas, slo voy a "ampliar" tus conocimientos... (que bien te ha quedado eso Guille)

165

Cuando usamos un textbox multiline, es decir en el que podemos escribir varias lneas de texto, el Visual nos da la posibilidad de poder usarlo de varias formas, si no le decimos nada, conforme vayamos escribiendo, se ir mostrando el texto en la siguiente lnea, a esto se le llama wordrap o algo as, que viene a significar que no se corten las palabras al cambiar de lnea... para hacer esto mismo en el BASIC normalito del MS-DOS, haba que crear una rutina para comprobar cuando haba que mandar una palabra a la siguiente lnea... dejemos los viejos tiempos y continuemos con los nuevos... Pero si lo que quieres es que cada lnea escrita se quede en la misma lnea hasta que pulses intro, debers indicrselo al Visual Basic, dicindole que slo aada al textbox un scroll horizontal, prubalo y decide... Cmo aadirle los scrollbars... no pienses que tienes que usar los controles que el Visual tiene para eso, slo debes modificar la propiedad ScrollBars del control Text2. Tienes varias opciones: 0- None 1- Horizontal 2- Vertical 3- Both Ningn scrollbar Slo el scroll horizontal, para cambiar de lnea debes pulsar Intro Slo el scroll vertical, esto es como sin scrolls, pero nos permite navegar hacia abajo. Ambos scrolls, pues eso, una mezcla de los dos.

Si los compruebas, vers al asignar a esta propiedad el valor 0 y 2, el resultado es el mismo, al menos en lo que se refiere a la hora de escribir en l, ya que el resultado visual es diferente; a lo que me refiero es que el texto se ajustar automticamente haya o no un intro para separar cada lnea, la diferencia, es que con el scroll vertical, podemos navegar fcilmente hacia la parte no visible del texto escrito. Cuando especificamos el Scroll horizontal, tanto con el valor 1, como con el 3, ya te dars cuenta de que cada lnea est separada, siempre que hayas pulsado Intro para cambiar de lnea. Tambin puedes usar Shift+Intro o Control+Intro para efectuar un cambio de lnea. Para comprobarlo, haz el textbox ms pequeo, al menos en anchura, por ejemplo ajstalo al tamao del Text1 y escribe varias lneas, pulsando en algunas Intro y otras escribiendo bastante texto... Luego decide que tipo de scroll prefieres. Una cosa que debes saber es que esta propiedad es de slo lectura, al menos en tiempo de ejecucin, o sea que no puedes cambiar que scrolls deben mostrarse una vez que el programa est en funcionamiento. Me imagino que te habrs fijado que en el Notepad (Bloc de Notas) que incluye el Windows, existe una opcin para ajustar las lneas automticamente, es decir para usar los dos scrolls o slo el vertical. Cmo se consigue este efecto si no se puede cambiar la propiedad ScrollBars? Pues... usando dos TextBoxes, uno de ellos con la propiedad ScrollBars a 2 (Vertical) y el otro asignando el valor 3 (Both) Para probarlo, debers crear un array del Text2, para ello, cpialo y vuelve a pegarlo, te preguntar si quieres tener una matriz de este control, contstale que s. Al Text2(0), el que tiene el valor Index igual a CERO, asgnale la propiedad ScrollBars a 2 y al otro, el valor 3. Sitalos en la misma posicin del form, para que uno est encima del otro, no te preocupes de cual est encima, eso lo controlaremos nosotros. Aade un nuevo botn, escribe en el Caption: Ajustar lneas y dale el nombre cmdAjustar. Declara una variable a nivel de mdulo para saber cual es el TextBox que est activo: Dim QueText2 As Integer En el Form_Load escribe esto: Text2(0).Zorder para que se ponga encima del otro TextBox. En el evento Click del nuevo botn aade este cdigo:

166

Private Sub cmdAjustar_Click() Dim QueText2Ant As Integer 'Valor del TextBox actual QueText2Ant = QueText2 'Nuevo valor QueText2 = QueText2 + 1 'si nos pasamos... volvemos al principio If QueText2 > 1 Then QueText2 = 0 'asignar al nuevo textbox el contenido Text2(QueText2).Text = Text2(QueText2Ant).Text 'Lo hacemos visible Text2(QueText2).ZOrder 'borramos el contenido anterior Text2(QueText2Ant).Text = "" End Sub Ahora tendrs que cambiar el cdigo usado para leer y escribir, simplemente cambia el Text2.Text por Text2(QueText2).Text y asunto arreglado, ya que el text que estar visible es el que indica esa variable. Ejecuta el programa, escribe algo en el textBox, preferiblemente alguna lnea larga y pulsa Intro para crear otras, pulsa en el botn de ajustar lneas y vers el efecto. Si quieres tener esta posibilidad en este programa, debers recordar de cambiar cualquier uso de Text2 por Text2(QueText2), ya que si no lo haces, el Visual Basic se encargar de recordrtelo... Bueno, creo que el rollito este ha sido ms largo de lo que tena previsto, pero espero que haya valido la pena. Vamos a ver las soluciones a las mejoras... las soluciones las voy a dar suponiendo que no tienes esta posibilidad de usar los dos TextBoxes para el ajuste de lnea, es que sino, me va a romper todo el esquema que tena previsto y no es plan... Para saber cuando se va a sobrescribir el fichero, lo que hay que hacer es comprobar primero si ese fichero ya existe y despus de comprobarlo, avisar con un MsgBox si se quiere sobre-escribir, en caso negativo simplemente no se guarda el contenido del TextBox en el fichero. Vamos a ver el cdigo necesario, este deber estar en el botn de Guardar... (elemental mi querido Watson) Private Sub cmdGuardar_Click() 'Guardar

167

Dim i As Long Dim SobreEscribir As Boolean 'Se asigna el valor Verdadero, por si no existe SobreEscribir = True 'Si ya existe, preguntar If Existe(Text1.Text) Then If MsgBox("ATENCIN, el fichero ya existe." & vbCrLf & _ "Quieres sobreescribirlo?", vbQuestion + vbYesNo) = vbNo Then 'Hemos contestado que no, as que... SobreEscribir = False End If End If 'Si no existe o se quiere sobreescribir... If SobreEscribir Then i = FreeFile Open Text1.Text For Output As i Print #i, Text2.Text Close i End If End Sub Fjate en el MsgBox, al usar & _ es para que el VB pueda mostrar en lneas distintas lo que se debera escribir en una misma. Despus usamos vbQuestion para que muestre la interrogacin y vbYesNo es para que nos muestre los botones SI / NO. Si pulsamos en Si, no se cumple la condicin y si pulsamos en NO, si que se cumple, por tanto asignamos un valor falso a la variable SobreEscribir para que en la siguiente comparacin no se cumpla y no se guarde el contenido del Text2. Al principio le he dado el valor VERDADERO a la variable que decide si se debe sobrescribir o no, esto lo he hecho porque si no existe el fichero, no nos preguntar y si no le damos de "arrancada" el valor Verdadero, nunca lo tendr, ya que la nica asignacin que hacemos es la darle un valor FALSO, en caso de que no queramos guardar el contenido. Como habrs podido comprobar, para poder guardarlo con otro nombre, debers escribir ese nombre en el Text1. Para la segunda mejora, necesitaremos una variable a nivel de mdulo, as que aade esta declaracin en la seccin de las declaraciones generales del formulario: Dim Modificado As Boolean Como recordars de la segunda entrega, en los TextBoxes hay un evento, CHANGE, que se "dispara" cada vez que cambia el contenido de la propiedad Text de estos controles.

168

Usaremos este evento para saber cuando se ha cambiado el contenido del Texto escrito. Aade este cdigo al formulario: Private Sub Text2_Change() Modificado = True End Sub De esta forma, cada vez que se cambie el contenido de este control, se cambiar tambin el valor de esa variable y as podremos saber si se ha cambiado o no. Esto lo comprobaremos en el procedimiento Abrir, de forma que si se ha modificado, nos pregunte si queremos guardarlo antes de abrir uno nuevo. El cdigo, ms o menos sera algo as: If Modificado Then If MsgBox("El texto se ha modificado..." & vbCrLf & _ "Quieres guardarlo?", vbQuestion + vbYesNo) = vbYes Then 'Guardarlo '... End If End If Realmente no sera tan simple. Ahora lo veremos al completo. Con esto de comprobar si est modificado se nos presentan dos problemas: El primero es que al no conservar el nombre del fichero abierto anteriormente, o con el que acabamos de guardar lo que hayamos escrito antes de intentar abrir otro, no sabremos con que nombre guardarlo, ya que al usar el contendido del Text1, ste puede cambiar y seguramente no conseguiramos nuestro objetivo. Por suerte, la solucin a este inconveniente es tan simple como la de usar una variable, a nivel de mdulo, para guardar el nombre del fichero abierto o guardado por ltima vez. Nota: Fjate que uso variables a nivel de mdulo para algunas cosas, de esta forma estas variables, como ya deberas saber, estarn disponibles en cualquier parte del mdulo actual, en este caso: el formulario. Aade esta declaracin en la parte general de las declaraciones del formulario: Dim sFichero As String Ahora modifica el cdigo para que podamos usar esta variable: Private Sub cmdAbrir_Click() 'Abrir Dim i As Long, tamFic As Long

169

Dim sTmp As String If Modificado Then If MsgBox("El texto se ha modificado..." & vbCrLf & _ "Quieres guardarlo?", vbQuestion + vbYesNo) = vbYes Then 'Conservar el nombre actual sTmp = Text1.Text 'y asignar el anterior Text1.Text = sFichero 'guardarlo... cmdGuardar_Click 'Volvemos a dejar el Text1 como estaba Text1.Text = sTmp End If End If 'Asignamos el nombre del fichero sFichero = Text1.Text If Existe(sFichero) Then Text2.Text = "" i = FreeFile Open sFichero For Input As i tamFic = LOF(i) Text2.Text = Input$(tamFic, i) Close i End If End Sub Fjate en las asignaciones que hay que hacer antes de guardar el contenido del Text2, esto es necesario, ya que en ese evento se usa el contenido del Text1, para saber en que fichero se debe guardar. Bien, ya tenemos una parte resuelta, la otra es que una vez que la variable Modificado ha tomado el valor TRUE no lo suelta. Este valor debera de dejar de valer verdadero cuando lo guardemos, as que aade al final del procedimiento que guarda el fichero, esta asignacin: Modificado = False Realmente debers ponerla despus del Close i y dentro de la comparacin que decide si se debe guardar o no el contenido del Text2. Tambin tendremos que asignar en este procedimiento el valor de la variable sFichero, para que al asignarse en el evento Abrir su valor al TextBox, ste tome el que tuviera esa

170

variable cuando se guard un fichero. Esto debers hacerlo una vez que se guarde el fichero, es decir, si no hemos cancelado la grabacin. sFichero = Text1.Text Aunque pueda parecer que realmente no tiene sentido el uso de esta variable, ya que tanto en Guardar como en Abrir se le asigna el contenido del Text1, lo tiene en el caso de querer abrir uno nuevo, antes de haber guardado los cambios, si no se tuviera esta variable, no conservaramos el nombre del fichero, antes de haber modificado el contenido del Text1 para asignar un nuevo nombre... Aunque parte de este "come-coco" se solucionara con el uso de un cuadro de dilogo que preguntara el nombre del fichero a abrir o a guardar, de esta forma no sera necesario dejar siempre un TextBox para que se pueda escribir el nombre. An as, necesitaramos una variable para conservar el nombre del fichero... Bien, vamos a probar esto... a ver si funciona bien... Como podrs comprobar, no est todo lo "refinado" que quisiramos... Despus de abrir un fichero y sin haber modificado el contenido del Text2, intenta abrir otro, te dir que el texto se ha modificado... Por qu ocurre esto? Cuando asignamos al Text2 el contenido del fichero, estamos "cambindolo", por tanto se produce el evento Change y se asigna el valor de la variable Modificado, as que tambin tendremos que asignar un valor FALSE a esta variable despus de abrir el fichero y asignarlo al Text2, es decir al final del procedimiento Abrir. De esta forma slo se activar la "alarma" cuando realmente se modifique el texto. Este valor asignarlo justo despus de cerrar el fichero o despus de haber asignado al Text2 el contenido del fichero. Para finalizar, un par de cosillas para mejorar. Cuando un form se abre, cosa que ocurre automticamente al iniciarse este programa, se produce el evento Load, este ahora no nos interesa, el que nos interesa es el que se produce cuando el form se cierra, cosa que tambin ocurre al cerrar la aplicacin, es decir el evento Unload. En este evento tambin se puede poner una comprobacin para que, en caso de no haber guardado el contenido del Text2, tengamos la oportunidad de poder guardarlo antes de cerrar definitivamente el form. As que, aade este cdigo en el evento Form_Unload: Private Sub Form_Unload(Cancel As Integer) If Modificado Then If MsgBox("El texto se ha modificado..." & vbCrLf & _ vbYes Then "Quieres guardarlo?", vbQuestion + vbYesNo) = 'Asignar el anterior

171

Text1.Text = sFichero 'guardarlo... cmdGuardar_Click End If End If Set Form1 = Nothing End Sub Aunque en el caso de que no hayamos usado ningn nombre, el contenido de sFichero, no sea adecuado, as que tambin podramos tener un valor por defecto en esta variable, que sera el que se mostrase al iniciarse el programa, por tanto vamos a aadir un valor por defecto a esta variable y al Text1, esto lo haremos en el evento Form_Load: Private Sub Form_Load() sFichero = "Prueba15.txt" Text1.Text = sFichero End Sub Fjate que al final del Form_Unload he puesto Set Form1 = Nothing, esto se suele usar cada vez que descarguemos un formulario, para asegurarnos que se libere la memoria que pudiera estar ocupando... de esto ya veremos ms cuando nos topemos con las clases y la creacin de objetos... piensa que cada control y formulario es un objeto y cuando un objeto no se usa, debemos indicarle al Visual que se deshaga de l... Pero esto lo veremos en otra ocasin.

Ahora los ejercicios: 1.- Como ya he comentado, los TextBoxes no soportan todos los caracteres que quisiramos, para redondear, digamos que soportan 32000. Esto es casi cierto en VB de 16 bits, realmente admiten 32767 (32 KB). En 32 bits en teora soportan 64 KB, pero como el entorno de 32 bits usa caracteres de 2 bytes, estos 64 kas se quedan en 32. Para 16 bits, existe una funcin del API de Windows que permite asignar 64 KB a un TextBox, en 32 bits no tiene ningn efecto ya que, como he repetido ms de una vez... admite 64 KB. Para no liarte ms de lo que ya puedas estar, vamos a dar por sentado que slo se pueden asignar a un TextBox 32000 caracteres, que en 32 bits no es lo mismo que 32000 bytes... (je, que me gusta liar al personal) Lo que tienes que hacer es que a la hora de abrir un fichero, compruebes que no tenga ms de 32000 caracteres y en caso de que el fichero los tenga, no abrirlo. 2.- En este segundo ejercicio, lo que vas a hacer es leer slo 32000 caracteres del fichero que tenga ms de esos... as al menos podrs editar esa parte de los ficheros grandes,

172

cosa que no es recomendable, sobre todo si lo guardas, ya que podras "cargarte" un fichero que puede ser necesario... El que avisa... Aunque, para curarte en salud, podras impedir que se guardase un fichero del cual no se hayan ledo todos los caracteres que tuviese... por tanto, un tercer ejercicio: 3.- Si el fichero abierto tiene ms de 32000 caracteres, leer slo esta cantidad y usar un "flag" para impedir que se guarde... Bueno, espero que esta entrega te haya sido provechosa y que seas capaz de completar los ejercicios, la solucin de los cuales te pongo en este link, adems en la pgina de las soluciones tendrs el cdigo para usarlo con los dos Textboxes para permitir el ajuste de lnea. Y como suele ser costumbre al terminar una entrega, ya sabes que espero tu comentario, sobre todo para preguntarme cosas que no hayas entendido de esta entrega, para que si lo considero oportuno, aclararlo en una prxima. Por supuesto que tambin puedes usar ese link para darme "nimos" a que contine con el curso... o si te sobra ese porttil y me lo quieres regalar..., pero, por favor, no lo uses para hacerme consultas... despus no te quejes si no te las contesto, que algunos aprovechis el rollo ese del peloteo para soltar alguna consultilla... que ya nos vamos conociendo...

Nos vemos.

Curso Bsico de Programacin en Visual Basic


1.- Comprobar que no tenga ms de 32000 caracteres y en caso de que el fichero los tenga, no abrirlo. 'Esta es la parte que se encarga de abrirlo, el cdigo anterior lo he omitido

'Asignamos el nombre del fichero sFichero = Text1.Text If Existe(sFichero) Then Text2.Text = "" i = FreeFile Open sFichero For Input As i tamFic = LOF(i)

173

If tamFic <= 32000 Then Text2.Text = Input$(tamFic, i) End If Close i 'Una vez abierto, no est modificado Modificado = False End If En este caso, lo nico que hay que hacer es comprobar si el tamao es menor o igual al mximo indicado, si es as, abrir el fichero, por tanto, slo tendremos que poner la parte que asigna al Text2 el contenido del fichero, dentro de la comparacin.

2.- En caso de que sea mayor de 32000 caracteres, leer slo los 32000 primeros. La solucin es casi como la anterior, bueno, casi, es decir tendremos que hacer una comparacin y en caso del que sea cierta, asignar a la variable que indica el nmero de caracteres a leer el valor mximo que queremos. 'Asignamos el nombre del fichero sFichero = Text1.Text If Existe(sFichero) Then Text2.Text = "" i = FreeFile Open sFichero For Input As i tamFic = LOF(i) ' If tamFic > 32000 Then tamFic = 32000 End If Text2.Text = Input$(tamFic, i) Close i 'Una vez abierto, no est modificado Modificado = False End If Por tanto comprobamos si el contenido de tamFic es mayor de 32000, en caso de ser cierto, se cumple la condicin, asignamos este valor a esa variable, de esta forma slo se leern esos caracteres.

174

3.- Si el tamao es mayor de 32000 no poder guardarlo (y como extra: no poder editarlo) Para conseguir esto, debemos aprovechar el cdigo usado en la segunda solucin, de forma que si es mayor de 32000, podamos asignar un flag, para saber que no se debe guardar. Tambin podramos evitar que se modificase el contenido del TextBox, esto se consigue asignando la propiedad Locked a True. De esta forma no se podr escribir en el TextBox. Por supuesto que en caso contrario se debera permitir la escritura, ahora veremos cmo hacer estas cosas. Veamos las cosas por partes: 'Asignamos el nombre del fichero sFichero = Text1.Text If Existe(sFichero) Then Text2.Text = "" i = FreeFile Open sFichero For Input As i tamFic = LOF(i) 'Nos aseguramos que se pueda editar Text2.Locked = False Text2.ForeColor = vbWindowText If tamFic > 32000 Then tamFic = 32000 'Si el tamao no es el leido, 'no permitir escribir Text2.Locked = True 'si adems cambiamos el color... mejor Text2.ForeColor = vbGrayText End If Text2.Text = Input$(tamFic, i) Close i 'Una vez abierto, no est modificado Modificado = False En esta primera solucin, an no evitamos que se pueda guardar, pero al menos cambiamos el color del texto y evitamos que se pueda modificar el contenido, para ello he cambiado el valor de la propiedad ForeColor, usando dos constantes predefinidas, la primera es para asignar el color normal del texto y la segunda es para asignar el color Gris cuando no podamos escribir en el TextBox, la ventaja de usar estas constantes, al menos con el VB5, es que si cambias los colores, usando el panel de control de Windows, estos se usan de forma automtica al asignarlos con estas constantes.

175

Ahora pasemos a ver cmo evitar que se guarde, cuando se abra parcialmente un fichero. Para ello necesitaremos otra variable a nivel de mdulo: Dim PoderGuardar As Boolean Con esta indicaremos que podemos o no guardar el contenido del TextBox. Se asignar al abrir el fichero y se comprobar al guardarlo. Veamos al completo los procedimientos de Abrir y Guardar: Private Sub cmdAbrir_Click() 'Abrir Dim i As Long, tamFic As Long Dim sTmp As String If Modificado Then If MsgBox("El texto se ha modificado..." & vbCrLf & _ vbYes Then "Quieres guardarlo?", vbQuestion + vbYesNo) = 'Conservar el nombre actual sTmp = Text1.Text 'y asignar el anterior Text1.Text = sFichero 'guardarlo... cmdGuardar_Click 'Volvemos a dejar el Text1 como estaba Text1.Text = sTmp End If End If 'Asignamos el nombre del fichero sFichero = Text1.Text If Existe(sFichero) Then Text2.Text = "" i = FreeFile Open sFichero For Input As i tamFic = LOF(i) 'Nos aseguramos que se pueda editar Text2.Locked = False Text2.ForeColor = vbWindowText 'y que se pueda guardar PoderGuardar = True If tamFic > 32000 Then tamFic = 32000

176

'Si el tamao no es el leido, 'no permitir escribir Text2.Locked = True 'si adems cambiamos el color... mejor Text2.ForeColor = vbGrayText 'No permitir que se guarde PoderGuardar = False End If Text2.Text = Input$(tamFic, i) Close i 'Una vez abierto, no est modificado Modificado = False End If End Sub

Private Sub cmdGuardar_Click() 'Guardar Dim i As Long Dim SobreEscribir As Boolean 'Slo se ejecuta el cdigo si se puede guardar If PoderGuardar Then 'Se asigna el valor Verdadero, por si no existe SobreEscribir = True 'Si ya existe, preguntar If Existe(Text1.Text) Then If MsgBox("ATENCIN, el fichero ya existe." & vbCrLf & _ vbYesNo) = vbNo Then "Quieres sobrescribirlo?", vbQuestion +

'Hemos contestado que no, as que... SobreEscribir = False End If End If 'Si no existe o se quiere sobrescribir... If SobreEscribir Then i = FreeFile

177

Open Text1.Text For Output As i Print #i, Text2.Text Close i 'Ya hemos guardado las modificaciones Modificado = False sFichero = Text1.Text End If End If End Sub

Pues creo que esto es todo... Una cosa que me gustara que hicieras, al menos as aprenderas ms, es que intentaras entender las soluciones, no te limites a copiarlas, sin saber el porqu de su uso... por supuesto que no slo hay una solucin y si la que tu has encontrado, sirve para lo mismo, pues mejor an. Aqu tienes el cdigo para usar dos controles Text2, chale un vistazo a las comparaciones realizadas para poder saber si se ha modificado y esas cosas, ya que ahora vas a trabajar con dos controles, como si slo fuese uno... Que lo disfrutes y prueba a experimentar otras posibilidades...

Curso Bsico de Programacin en Visual Basic


Habrs notado que ya no aparecen los links al principio de esta entrega, ello se debe a que he aadido una nueva pgina, desde la cual puedes linkar con todas las entregas, adems de poder ver una lista de las "palabras" tratadas en todas las entregas aparecidas, as como en que entrega(s) apareci esa palabra, no es nada "preciso", pero intentar ir "depurndolas". La intencin es que puedas saber de un vistazo en que entrega se ha tratado y... espero hacerlo pronto, en cual de ellas es en la que se ha explicado, porque una cosa es que se use la palabra y otra diferente que se explique... Como digo, es slo un primer intento, que espero ir mejorando, para que te sea fcil encontrar la palabra o tema que te interesa. Decirte que esa lista la he "sacado" con una utilidad, que seguramente pondr pronto en mis pginas y que trata de mostrar cada una de las "palabras" que aparecen en los ficheros que se procesen... adems de esa utilidad, he tenido que ir leyndome cada una de las palabras y tomando nota, por lo tanto alguna se me ha podido escapar...

178

Bueno, ya vale... vamos al tema de la presente entrega:

Los ficheros aleatorios (Random)


Pues eso, ahora le toca el turno a los ficheros aleatorios. Ya te coment que este tipo de fichero se suele usar cuando todos los datos que incluye tienen la misma longitud. Normalmente a cada uno de los datos guardados se les llama registro. Todos los registros tienen la misma longitud, o al menos deberan tenerla... no, no es una contradiccin, pero si quieres que funcione bien, deben ser iguales, porque el sistema que usa el VB para acceder a cada uno de los registros es una simple operacin: Posicin_Actual = (Nmero_Registro -1) * Tamao_del_Registro + 1 Pero no te asustes... t no tienes que hacer ningn clculo... de eso se encarga el Visual Basic. Para acceder a los datos de los ficheros abiertos como Random, vienen como anillo al dedo, (si es que el anillo te encaja bien), los tipos definidos por el usuario, aunque cualquier cadena servira para esta tarea. Antes de empezar a manejar ficheros de acceso aleatorio, es importante planear lo que vamos a almacenar en ellos; siempre se debe planear, pero en esta ocasin es un poco ms importante, ms que nada porque, como ya he dicho, tenemos la obligacin de saber la longitud de los datos, (al menos de cada uno de los registros), a almacenar. Ya que esa longitud, (la de cada registro), debemos especificarla a la hora de abrir el fichero. Un pequeo detalle antes de continuar, cuando abrimos un fichero secuencial para lectura (Input), el Visual Basic o el sistema operativo, slo nos permite leer datos de ese fichero, es decir: no podemos leer y escribir al mismo tiempo. Igualmente ocurre cuando lo abrimos para escritura (Output), slo que en esta ocasin slo podemos escribir y no leer. Sin embargo, con los ficheros abiertos como aleatorios (Random) o binarios (Binary), una vez que el fichero est abierto, podemos leer o escribir indistintamente. Alguna ventaja teniamos que tener... no iban a ser todo "obligaciones". Sigamos con lo nuestro... Veamos cmo, por fin, usar estos ficheros: Para verlo... nada mejor que con un ejemplo: Primero definimos un tipo de datos, en este caso vamos a guardar el nombre, edad y la cuenta de email de unos colegas, por tanto emplearemos un tipo definido con estos tres campos, veamos: Private Type t_colegas Nombre Edad email End Type Declaramos una variable de este "nuevo" tipo de datos, que ser la que usaremos para acceder al fichero: As String * 30 As Integer As String * 50

179

Dim unColega As t_colegas Y abrimos el fichero: Open "miscolegas.dat" For Random As nFic Len = Len(unColega) El Len(unColega), le est indicando al VB que la longitud de cada registro es la que tenga ese tipo de datos. No siempre hay que hacerlo as, podramos asignar a una variable esa longitud y usarla para abrir el fichero: lenColega = Len(unColega) Open "miscolegas.dat" For Random As nFic Len = lenColega Lo que quiero dejar claro es que al Visual Basic no le importa con qu variable accedemos al fichero, sino que longitud tendr esa variable, o si lo prefieres, cual ser la longitud de cada registro. Por tanto, si sabemos que nuestro tipo de datos, (o lo que es lo mismo: cada registro), tiene 82 bytes de longitud, podramos usar un string con esa longitud para acceder al fichero en cuestin y... Toc, toc! Guille!!! S? No crees que te ests precipitando? Yo? Por qu? Porque ests hablando de acceder as o asao a los datos y an no has explicado cmo leer o escribir esos datos... Pues... s... es cierto... je... Si no fuera por el otro Guille... en fin... Esto..., cuando quieras leer el contenido de un registro en particular del fichero abierto, usa esta instruccin: Get #canal, num_registro, variable_de_datos Para escribir usa esta otra: Put #canal, num_registro, variable_de_datos Es decir GET para leer y PUT para escribir, en esto los ingleses lo tienen ms fcil, ya que al menos a ellos esas dos palabras tienen algo de sentido... Despus de cualquiera de estas instrucciones se indica el nmero de fichero ( #canal), seguido por el nmero de registro al que queremos acceder, (num_registro), y por ltimo la variable, (variable_de_datos), que usamos para leer, (GET), los datos del disco o almacenarlos en l (PUT) Un detalle que tienes que tener siempre presente al definir un tipo de datos para acceder a los ficheros aleatorios: asegurate de que todas las cadenas contenidas en ese tipo, sean de longitud fija; lo mismo si dentro del tipo usas arrays, estos deben ser de ndices constantes, es decir que estn definidos al crear el tipo. Todas estas "precauciones" se consiguen con datos "estticos", es decir que permanecen

180

sin cambios, al menos en lo que a tamao se refiere. Los "dinmicos" son los que pueden cambiar "dinmicamente" de tamao. Aclaremos estos puntos antes de continuar. Una variable declarada de esta forma: Dim cadenaEstatica As String * 50 Siempre tendr 50 caracteres, incluso si hacemos esta asignacin: cadenaEstatica = "" Con esto no eliminamos las 50 celdas de memoria, sino que la rellenamos de espacios... Prueba con este cdigo: Crea un nuevo proyecto y aade esto en el Form_Load: Private Sub Form_Load() Dim cadenaEstatica As String * 50 Show cadenaEstatica = "Hola" Print cadenaEstatica & "Pepe" cadenaEstatica = "" Print cadenaEstatica & "Pepe" Print Print "Longitud de la cadena:"; Len(cadenaEstatica) Print "Asc(cadenaEstatica) ="; Asc(cadenaEstatica) End Sub El resultado sera este:

Es decir que una cadena esttica (o de longitud fija), siempre tendr el nmero de caracteres que le indicamos al declararla. Sin embargo una cadena dinmica slo tendr los caracteres que le asignemos. Prueba este cdigo: Private Sub Form_Load()

181

Dim cadenaDinamica As String Show cadenaDinamica = "Hola" Print cadenaDinamica & "Pepe" cadenaDinamica = "" Print cadenaDinamica & "Pepe" Print Print "Longitud de la cadena:"; Len(cadenaDinamica) Print "Asc(cadenaDinamica) ="; Asc(cadenaDinamica) End Sub Habrs obtenido algo parecido a esto:

O sea "Illegal Function Call", este error lo da al intentar conseguir el cdigo ASCII de la cadena... pero como la cadena est vaca... pues no hay nada que obtener, por tanto: Error al canto!!! Aparte del error, te puedes fijar que "HolaPepe" sale junto y que la longitud de la variable es cero. Cuando asignamos una cadena vaca a un string dinmico, lo dejamos "seco", es decir sin nada en el interior... Lo mismo ocurre con los arrays, ya sean de caracteres o numricos. Los estticos siempre conservan el nmero de elementos, incluso cuando se eliminan, (al menos eso es lo que parece), con Erase. Aunque esto ya lo vimos, no est de ms recordarlo: Al "eliminar" con Erase un array dinmico, lo eliminamos de la memoria, pero en los estticos, simplemente ponemos el contenido a cero, (o a cadenas vacas si son de

182

cadenas dinmicas). Prubalo: Dim arrayEstaticoInteger(1 To 10) As Integer Dim arrayEstaticoString(1 To 5) As String Dim arrayDinamicoInteger() As Integer Dim arrayDinamicoString() As String Cuando se declaran los arrays con el nmero de elementos que va a contener, siempre con constantes, estos arrays son estticos, porque siempre contendrn ese nmero de elementos. Sin embargo los dinmicos, se declaran sin decirles cuantos elementos van a contener y despus se usa Redim o Redim Preserve para cambiar el nmero de elementos. Veamos un ejemplo de un tipo definido con un array esttico: Private Type t_colegas2 Nombre Edad email(1 To 3) End Type Aqu hemos definido un "campo" email con un array de tres elementos. Recuerda que aunque los tipos definidos permitan tanto cadenas de longitud variable como arrays dinmicos, si ese tipo se va a usar para acceder a datos de un fichero aleatorio, su longitud siempre tiene que ser fija y coincidir con la longitud que se indic a la hora de abrir ese fichero... si no tienes esta "precaucin"... no acceders con "fiabilidad" a los datos contenidos en ese fichero... despus no digas que no te advert... De todas formas, vamos a comprobarlo... Escribe este cdigo en un form: 'Este cdigo en las declaraciones del form Option Explicit Private Type t_colegas Nombre Edad email End Type Dim unColega As t_colegas As String * 30 As Integer As String * 50 As String * 30 As Integer As String * 50

183

Private Type t_colegaDinamico Nombre Edad email End Type Dim unColegaDinamico As t_colegaDinamico Private Sub Form_Load() Dim nFic As Integer nFic = FreeFile Open "miscolegas.dat" For Random As nFic Len = Len(unColega) 'Asignamos los datos With unColega .Nombre = "Pepe" .Edad = 22 .email = "pepe22@mundo.net" End With Put #nFic, 1, unColega 'ahora lo hacemos con el colega dinmico With unColegaDinamico .Nombre = unColega.Nombre .Edad = unColega.Edad .email = unColega.email End With 'Aqu dar error: Put #nFic, 2, unColegaDinamico Close End Sub Ejecuta el programa y te encontrars con un "bonito" error nmero: 59 Bad Record Length o La longitud de registro es incorrecta, si usas la versin castellana del Visual Basic. Lo que nos quiere decir este error es que no puedes grabar datos de una longitud diferente a la especificada a la hora de abrir el fichero. Pero... por qu? si, aparentemente, la longitud es la misma... Pues eso, que slo es en apariencia... si en la ventana "Inmediato" escribes esto, saldrs As String As Integer As String

184

de dudas: Print len(uncolega); len(uncolegadinamico) El resultado ser este: 82 10 En el primer caso, el del coleguilla normal, la longitud es la esperada: 82 Pero en nuestro amigo dinmico, la longitud es 10, independientemente de que "en teora" tenga almacenados esos 82 caracteres del "esttico". Lo que ocurre es que al ser dinmico, el contenido de las variables de caracteres se almacenan en otro sitio y lo nico que hay en esas variables es un "puntero" a la direccin de memoria en la que estn los datos almacenados... al menos ahora sabemos que una variable de cadena de caracteres tiene una longitud de 4 bytes... creo que eso ya lo vimos en la entrega nmero dos o tres... dnde hablamos de lo que ocupa cada variable... los enteros (Integer) ocupan 2 bytes, independientemente de cmo est declarada la variable... lo mismo ocurre con los otros datos numricos. Como te deca, si sabemos que la longitud del registro es de 82 caracteres, podemos definir una cadena de esa longitud y usarla para acceder a los datos. Probemos esto otro: Dim unaCadena As String * 82 '... unaCadena = "Prueba de una cadena" Put #nFic, 2, unaCadena '... Ahora si que funcionar. Pero si esa variable est definida como String normal, no funcionar... Resumiendo: para acceder a los registros de un fichero abierto como Random, las variables usadas deben tener definida la longitud igual que el tamao especificado a la hora de abrirlo. Un poco de complicacin: Vamos a ver un ejemplo, "ilustrativo" ms que prctico. Ya he comentado que con una cadena de longitud fija podemos acceder a un registro. Ahora comprobars lo "cmodo" que es usar los tipos definidos. Pero este ejemplo lo pongo para que sepas cmo se almacenan los datos numricos en una cadena que se va a usar para guardar datos en un fichero de acceso aleatorio. Hemos "comprobado" que un Integer se almacena en dos bytes... por tanto lo que se guarda en el disco es precisamente eso: dos bytes. Cuando se usa un tipo definido, no te tienes que preocupar de cmo se almacena, simplemente asignas el valor a la variable numrica y del resto se ocupa el VB. Viendo el ejemplo anterior, es fcil almacenar un entero, si usamos un tipo definido, pero cmo lo haremos si la variable en la que se almacenar los datos es una cadena de longitud fija? Para ello debemos "desglosar" los datos en distintas partes: Para el Nombre usaremos los primeros 30 bytes de la cadena, para la edad los dos

185

siguientes, es decir: bytes 31 y 32, para el email desde la posicin 33 hasta la 82, o sea desde la 33 con 50 bytes de longitud. Si escribes esto: Dim unaCadena As String * 82 Dim elNombre As String * 30 Dim laEdad As String * 2 Dim elEmail As String * 50 Dim nFic As Integer Dim unaCadena As String * 82 Show nFic = FreeFile Open "miscolegas.dat" For Random As nFic Len = Len(unColega)

elNombre = "Pepe de 23 aos" laEdad = 23 elEmail = "Pepe23@mundo.net" unaCadena = elNombre & laEdad & elEmail Put #nFic, 2, unaCadena Close nFic No conseguirs el resultado esperado... en lugar de 23 aos, tendr: 13106 !!! Dudo mucho que llegue a conseguir esa edad... ni an siendo un gnomo... Si queremos guardar un nmero debemos convertirlo antes... el problema es que "NO HAY FUNCIONES DE CONVERSIN" Antes si que las haba... cuando digo antes, me refiero al Basic del MS-DOS. Para cada tipo de dato numrico existan un par de funciones, una para convertir el nmero en una cadena y el otro para convertir la cadena en un nmero... Y no me estoy refiriendo a las funciones que convierten los nmeros en letras y las cadenas en nmeros... aunque pueda parecerte una contradiccin... no es lo mismo convertir un nmero en una cadena normal que en una cadena para almacenar en el disco como si de un nmero se tratase... A saber, STR o CSTR convierten un nmero en una cadena, es decir que si el nmero es 23, lo convierten en "23" en el caso de CSTR y en " 23" si se usa STR, fjate en el espacio que hay antes del 2 en el segundo caso. Pero cuando se debe convertir un nmero en una cadena de 2 bytes... la cosa cambia... Y no te confundas... no pienses que si usas CSTR lo tienes solucionado... porque en el

186

ejemplo este de "23" est claro... pero y si el nmero es 1998? Tendramos una cadena de 4 caracteres... Como te deca, en los tiempos del MS-DOS, existan las funciones MKI$ y CVI para convertir los nmeros enteros; MKL$ y CVL para los enteros largos, MKS$ y CVS para los "singles" y MKD$ y CVD para los Double. No busques estas funciones... porque no estn... Aunque podemos fabricrnoslas... pero, como no es plan de "romperse" el coco con una chorradilla de estas... slo vamos a usar el ejemplo de los nmeros enteros. Cmo se usaran? En el ejemplo anterior haramos esto: elNombre = "Pepe de 23 aos" laEdad = Mki(23) elEmail = "Pepe23@mundo.net" unaCadena = elNombre & laEdad & elEmail Put #nFic, 2, unaCadena La funcin se hara as: Private Function Mki(ByVal unInteger As Integer) As String Dim sLoByte As String Dim sHiByte As String Dim iChar As Integer iChar = unInteger \ 256 sHiByte = Chr$(iChar) iChar = unInteger Mod 256 sLoByte = Chr$(iChar) 'Devolvemos el valor Mki = sLoByte & sHiByte End Function Que complicacin verdad? Pues como estas... muchas ms... pero eso antes, en el MS-DOS... desde que hay "mogolln" de espacio de almacenamiento... he perdido de vista muchas de estas cosas de "manejo" de nmeros a "bajo nivel"... Pero antes haba que ahorrar cuantos bytes se pudieran... La cuestin es que se divide el nmero en dos partes, la alta que se consigue dividiendo el entero entre 256 y la baja, que es el resto que queda... despus de haberlo dividido por 256... que de eso es de lo que se encarga el MOD... Despus se convierten esos valores en dos bytes usando el CHR$... se unen... y sin necesidad de agitarlo... conseguimos el "batido" que queremos... por tanto, lo asignamos

187

al valor que devolver la funcin... (recuerda que una funcin devuelve un valor cuando se asigna ese valor a su nombre...) Bien... un lo... verdad? Pues ahora ms los... Cmo se asigna esto al revs? Es decir: Cmo se convierte una cadena de 2 bytes en un entero? Con los tipos definidos no hay que hacer nada... ya sabes, que hace la conversin automticamente. Pero si usamos una cadena de longitud fija... ya es otro cantar... Vamos a ver la declaracin de la funcin "equivalente" al CVI del viejo Basic del MS-DOS: Private Function Cvi(ByVal unaCadena As String) As Integer Dim sLoByte As String Dim sHiByte As String sLoByte = Left$(unaCadena, 1) sHiByte = Right$(unaCadena, 1) 'Devolvemos el valor Cvi = Asc(sLoByte) + Asc(sHiByte) * 256 End Function No voy a entrar en detalles, as que paso a un ejemplo "completo" para ver estos resultados. Si vas a comprobar cmo "trabajan" alguna de estas funciones usando "puntos de interrupcin", es decir que quieres que el VB se detenga en una lnea determinada, (para ello debers posicionarte en la lnea, que no debe ser un comentario, y pulsar F9, al llegar a esa lnea el Visual se detiene), debers cambiar la propiedad Autoredraw del form y ponerla a TRUE, de esta forma el contenido del Form (el que se imprime dentro del Form_Load), se mantiene... 'Esto escribelo en el Form_Load Dim nFic As Integer Dim unaCadena As String * 82 Dim laEdad23 As Integer Show laEdad23 = 23 nFic = FreeFile Open "miscolegas.dat" For Random As nFic Len = Len(unColega)

188

'Asignamos los datos With unColega .Nombre = "Pepe" .Edad = 22 .email = "pepe22@mundo.net" End With Put #nFic, 1, unColega

Dim elNombre As String * 30 Dim laEdad As String * 2 Dim elEmail As String * 50 elNombre = "Pepe de 23 aos" laEdad = Mki(laEdad23) elEmail = "Pepe23@mundo.net" unaCadena = elNombre & laEdad & elEmail Put #nFic, 2, unaCadena Get #nFic, 1, unColega With unColega Print Print "Colega 1:" Print .Nombre Print .Edad Print .email End With 'Si se lee as, no hay problemas Get #nFic, 2, unColega With unColega Print Print "Colega 2:" Print .Nombre Print .Edad Print .email End With

189

'De esta manera est la cosa ms complicada... Get #nFic, 2, unaCadena Print Print "Colega 2:" Print Mid$(unaCadena, 1, 30) Print Cvi(Mid$(unaCadena, 31, 2)) Print Mid$(unaCadena, 33, 50) Observa que aunque el segundo dato se haya guardado usando una cadena de longitud fija, (unaCadena), se puede leer tanto con un tipo definido, si es de la misma longitud, claro, como con la cadena de longitud fija. El problema es que tenemos que "desglosar" el contenido de esa cadena... Recuerda que te coment que cada uno de los "campos" del registro ocupa un espacio determinado... o sea que tienen una longitud fija... por eso hay que usar el Mid$, para tomar cada uno de los "trozos" de esa cadena. El Mid$ funciona de la siguiente forma: Mid$ (cadena, posicin_de_inicio, numero_de_caracteres) Lo que significa que devolver el nmero de caracteres indicados por numero_de_caracteres empezando por la posicin: posicin_de_inicio. En caso de que no se especifique el nmero de caracteres, devolver toda la cadena desde la posicin indicada hasta el final de la misma. Y ya que estamos con estas funciones... voy a explicarte las dos usadas en la funcin CVI: Left$ y Right$ El Left$ toma de una cadena los caracteres que se le indiquen, empezando por la izquierda. Right$ hace lo mismo, pero empieza a contar desde la derecha. Unos ejemplos: Left$("Hola Mundo", 4) devolver "Hola", es decir los 4 primeros caracteres. Right$("Hola Mundo", 4) devolver "undo", porque son los cuatro caracteres que hay a la derecha... El Left$ se puede sustituir por Mid$ de la siguiente forma: Mid$("Hola Mundo", 1, 4) es decir: empezando por el primer caracter, dame cuatro... Sin embargo esto otro: Mid$("Hola Mundo", 4) nos dar: "a Mundo", o lo que es lo mismo: desde la posicin cuarta, todo lo que haya en la cadena. Y si quieres imprimir cada uno de los caracteres de una cadena, prueba esto: Dim i As Integer Dim unaCadena As String

190

unaCadena = "Hola Mundo" For i = 1 To Len(unaCadena) Print Mid$(unaCadena, i, 1) Next Lo que se hace aqu es un bucle para cada uno de los caracteres de la cadena, esto se sabe con LEN, que devuelve la longitud de una cadena y con el Mid$, vamos tomando todos los caracteres de uno en uno. Ya que le decimos que nos muestre el caracter que est en la posicin i... slo muestra uno, porque esa es la cantidad que le indicamos que nos devuelva... UF! Creo que algunas veces se me va la olla... y se me olvidan cosas que... creyendo que he explicado... las uso... En fin... la cuestin es que ahora sabes unas instrucciones nuevas... Y ya que estamos... Veamos cmo se puede usar tambin MID$, lo que ocurre es que esta "funcin" tambin es una "instruccin", al igual que ocurra con INPUT, segn se use delante o detrs del signo igual, se convierte en funcin o en instruccin. Ya sabes que una funcin devuelve un valor y se puede usar en una expresin, pero una instruccin "hace" algo y no devuelve ningn valor ni se puede usar en una expresin. En el ejemplo de guardar los registros, se usaron tres variables para almacenar los datos en el fichero: Dim elNombre As String * 30 Dim laEdad As String * 2 Dim elEmail As String * 50 elNombre = "Pepe de 23 aos" laEdad = Mki(laEdad23) elEmail = "Pepe23@mundo.net" unaCadena = elNombre & laEdad & elEmail Put #nFic, 2, unaCadena Pues esto mismo se puede hacer de esta otra forma: Mid$(unaCadena, 1, 30) = "Prueba de una cadena" Mid$(unaCadena, 31, 2) = Mki(laEdad23) Mid$(unaCadena, 33, 50) = "pepe23@mundo.net" Put #nFic, 2, unaCadena

191

Es decir, al igual que la funcin con el mismo nombre, la instruccin MID$ sustituye en "unaCadena" los caracteres indicados por el inicio y la longitud por los que estn despus del signo igual... Creo que me he pasado... bueno, esto al final viene bien... ya que as tenemos otra entrega "medio" preparada... ya que de la que tena prevista de 7 pginas "manuscritas" slo he usado 2 y poco ms... Para completar esta "extraa" entrega... extraa por "no prevista"... vamos a "rematarla" con unos ejercicios, que en este caso sern de manejo de cadenas con las funciones que acabamos de ver... Los ejercicios: 1- Haz que se muestren los caracteres de una cadena al revs. Por ejemplo, si la cadena es "Hola Mundo", se deber imprimir: "odnuM aloH" Que es til si queremos hacer carteles para que se vean al derecho al mirar por el retrovisor del coche... (por decir algo) 2- Usando esta misma cadena, es decir "Hola Mundo", divdela en las dos palabras. Se supone que deber servirte para cualquier frase, as que no hagas nada "fijo". Lo que tienes que hacer es devolver en una cadena todos los caracteres hasta el primer espacio y el resto en otra cadena. Para esto, si quieres, puedes usar el Left$ y el Mid$... al menos para la primera palabra. Y ya est... no te voy a complicar ms la vida... que ya vendrn complicaciones posteriores. Pulsando en este link tienes las soluciones.

Para terminar, slo pedirte disculpas por haberme/haberte "liado"... pero... eso es lo que hay... 8-)

Y como no es plan de perder la costumbre de pedirte que hagas un comentario de la entrega que acaba de terminar... aqu tienes el link para que me hagas saber que te ha parecido... A estas alturas no tendra que recordarte que ese link slo es para hacer comentarios de esta entrega o del curso en general... as que no te voy a decir que no aproveches el link para hacerme consultas... Nos vemos. Guillermo

Curso Bsico de Programacin en Visual Basic


192

1- Haz que se muestren los caracteres de una cadena al revs. Dim unaCadena As String Dim i As Integer unaCadena = "Hola Mundo" Cls For i = Len(unaCadena) To 1 Step -1 Print Mid$(unaCadena, i, 1); Next Print Lo que se hace es un bucle "al revs", es decir: empezando por el ltimo caracter... avanzando hasta el primero. De eso se encarga el STEP -1

2- Usando esta misma cadena, es decir "Hola Mundo", divdela en las dos palabras. ' Dim unaCadena As String Dim i As Integer Dim palabra1 As String Dim palabra2 As String unaCadena = "Hola Mundo" Cls For i = 1 To Len(unaCadena) If Mid$(unaCadena, i, 1) = " " Then palabra1 = Mid$(unaCadena, 1, i) 'Tambin as: 'palabra1 = Left$(unaCadena, i)

193

palabra2 = Mid$(unaCadena, i + 1) 'Realmente deberamos abandonar el bucle 'Por la tremenda: 'Exit For 'Terminando el bucle: 'i = Len(unaCadena) End If Next Print palabra1 Print palabra2 En este segundo ejemplo, si no abandonamos el bucle, bien usando EXIT FOR, (algunos me odiarn por esto), bien asignando a la variable i el valor mximo que puede tener: Len(unaCadena); en el caso de que la variable tenga ms de un espacio, se asignar a palabra2 la ltima palabra y a palabra1 todo lo anterior. Si se abandona el bucle, en ese mismo caso: palabra1 tendr la primera palabra y palabra2 todo el resto. Para comprobarlo, cambia la asignacin por esta otra: unaCadena = "Hola Mundo desastroso" Y comprueba lo que te digo... Ms adelante veremos otras formas de "obtener" cada una de las palabras de una cadena...

Ya slo queda esperar a la prxima entrega... en la que "remataremos" lo del acceso aleatorio ese... Nos vemos. Guillermo

Curso Bsico de Programacin en Visual Basic


A estas alturas ya estars acostumbrado a mis "desvaros", eso demuestra que soy de lo menos ordenado de lo que se pueda ser, pero... ese es un problemilla que llevo conmigo desde chiquitillo, as que... La cuestin es que parece ser que no voy a terminar con esto del acceso a los ficheros... y eso que tengo ganas de terminar con ello para poder pasar a otra cosa, pero... se me resiste el tema...

194

Realmente no es que se me resista, es que por el camino van surgiendo nuevas cosillas que es bueno que sepas y como este curso no est enfocado para que lo aprueben en ningn plan nacional de estudios... pues eso... Lo que si que me interesa saber es que no te pierdes con tantos desvaros... es decir, que sigues la lnea... aunque eres libre de poder desviarte y salirte por la tangente para mirar los manuales y la ayuda del Visual Basic, que aunque no te lo creas, tambin sirve para aprender... ahora, lo que pasa es que hay que saber comprenderla... y de eso creo que es de lo que de una forma u otra me estoy encargando yo... si no lo consigo, malo y si lo consigo... mejor para todos. Siguiendo con esos desvaros, vamos a empezar esta entrega con el manejo de las cadenas, que, aunque ya vimos un poco de ella en la entrega anterior, no estar de ms saber algo ms. Ten en cuenta que la mayora de las veces vamos a manipular cadenas... y no es plan de que nos pillen "atados" sin saber que hacer... (chiste malo, lo reconozco...) Seguramente no me voy a enrollar demasiado en este tema, digo "seguramente" porque no s en que acabar esta entrega... as que paciencia y disfruta y aprende con lo que se diga en ella. Ahora vamos al tema. Ya hemos visto, cmo tomar caracteres de una cadena desde el principio (LEFT), desde el final (RIGHT) y desde cualquier punto (MID); tambin podemos saber cuantos caracteres tiene una cadena (LEN) e incluso convertir un nmero en cadena (CSTR) o una cadena en nmero (VAL). Tienes que recordar que el VB tiene como tipo de variable por defecto el Variant, por tanto estas funciones realmente devuelven un tipo Variant, dentro de este tipo hay una serie de subtipos, que realmente son los tipos que vimos al principio de este curso, es decir Integer, Long, String, etc. A que viene este nuevo lo Guille? A que es conveniente que lo sepas, nada ms, pero, al menos por ahora no te preocupes por ello, el VB se encarga de hacer sus conversiones, aunque nosotros podemos obligarle a que lo haga, para ello deberemos usar esas funciones aadindole al final el signo dlar ($), ya sabes que ese signo es el que se emplea para indicar que una variable es de cadena. Por tanto LEFT$, MID$ y RIGHT$ devolvern siempre una cadena, mientras que LEFT, MID y RIGHT lo que devuelven es un Variant de subtipo String... un lo... pero ya te digo que no debes preocuparte. Nota del 24/Ago/2003: Repasando esta entrega quiero hacer un inciso, que aunque en el prrafo anterior ha quedado "medio-claro", quiero que se clarifique totalmente, ya que en ese prrafo no te dije que el rendimiento ser mejor si lo que pretendes es usar cadenas de caracteres. Me estoy refiriendo a que uses las funciones acabadas con el signo dlar ($), de esa forma le ests "confirmando" al Visual Basic que quieres usar cadenas de caracteres y no un dato del tipo Variant con "sub-tipo" String. Un Variant siempre es ms "pesado" que un tipo String, (lo de pesado quiere decir que necesitar de ms "tratamiento" por parte del VB). Hay un montn de funciones para esto del manejo y conversin de las cadenas, en esta entrega veremos algunas de ellas, las que usaremos ms a menudo. Si eres de espritu inquieto, (por decir algo), puedes echar mano de la ayuda del Visual Basic y buscar las distintas funciones e instrucciones que manejan o manipulan las cadenas... la verdad que te puedes entretener bastante con ellas... aunque no te fes de todo lo que veas y te diga la ayuda si no lo compruebas por ti mismo... que al menos uno de los ejemplos est

195

equivocado, pero me imagino que se deber a la traduccin... en fin... por no fiarte, no te fes ni de lo que yo te diga, siempre comprueba las cosas, as saldrs de dudas... y si no sales de dudas, pues... peor "pa t" Creo que voy a complicarte un poco ms la vida, pero no te espantes, no va a ser en vano. Adems de los tipos de datos que hemos vistos hasta ahora, hay otro que sirve para esto de las cadenas, o casi, ya que se podra usar para manejar cada uno de los bytes de una cadena, fijate que he dicho "bytes" y no caracteres, ya que si trabajas en un entorno de 32 bits, cada caracter de una cadena se compone de dos bytes, si ests usando el VB de 16 bits slo hay un byte por cada uno de los caracteres. El tipo en cuestin es BYTE y en ese tipo slo se puede almacenar un valor que va desde 0 a 255, es decir un byte, la lgica algunas veces es aplastante... As que, si le echas un vistazo a la ayuda, te encontrars con algunas funciones que tienen al final una letra B, eso significa que devuelve valores tipo Byte, as por ejemplo LENB, devolver el nmero de bytes que tiene una cadena. Prueba esto, vers que si lo usas con el VB de 16 bits te devuelve una cosa distinta a lo que hara en 32 bits: Print Len("Hola Mundo") Print LenB("Hola Mundo") Lo que se mostrar, (recuerda de ponerle un Show, si escribes este cdigo en el evento Load del form), ser 10 y 20 si usas el VB de 32 bits, (recuerda que el VB5 slo es de 32 bits), pero en 16 bits (VB3 VB4-16), mostrar en ambos casos 10. Vamos a practicar algunas cosillas con esto de los Bytes (o con datos de tipo Byte). Ya sabes que puedes crear una array de cualquier tipo de datos, incluso de tipos definidos, as que tambin podemos crear arrays del tipo Byte, pero este tipo de arrays tienen una particularidad. Una cadena (variable de tipo string), realmente es un array del tipo Byte, esto nos permite manejar las cadenas de caracteres como si fuesen arrays de bytes o mejor an usar los arrays del tipo byte, como si fuesen cadenas. Vamos a comprobarlo, pon estas lneas de cdigo en el evento Load de un form: Show 'para que se muestre Dim aByte() As Byte aByte = "Hola Mundo" Print aByte Como comprobars se muestra Hola Mundo... igual que si lo hubieses asignado a una cadena de caracteres. En el VB4-16 te habr dado un error, para evitar ese error y que se muestre el contenido,

196

puedes asignarlo a una cadena e imprimir esa cadena, tambin puedes asignarlo a un Label y se mostrar el contenido de ese array como si fuese una cadena. Esto mismo, por supuesto tambin es aplicable al Visual Basic de 32 bits. Aclaracin para los que trabajis con 16 bits: Para no complicar mucho las cosas, si ests usando 16 bits, comprueba lo que aqu se diga y saca tus propias conclusiones. A pesar de que an hay sistemas funcionando de 16 bits y gente que por tanto programa para esos sistemas, creo que el enfoque adecuado de este curso debera concentrarse en los sistemas de 32 bits, de todas formas, la mayora de las cosas "realmente" vlidas servirn tanto para 16 como para 32 bits. Pero voy an ms lejos, dentro de poco, no s realmente en cuantas entregas, pero el enfoque se ir concentrando en el Visual Basic 5 y posteriores, (cuando los haya), ya que en el VB5 se han introducido cosas realmente interesantes para la programacin enfocada a objetos, (que pronto empezaremos a tocar), que aunque la mayora de esas cosas sean vlidas para el VB4, en algn momento dejar de serlo y slo sern aplicables al VB5 y posteriores... Quiero dejar esto claro, para que despus no haya sorpresas... de todas formas, recalco que esto es un curso bsico y como tal, servir para prcticamente cualquier versin de VB, aunque mi "despiste" seguramente me llevar a tocar cosas slo aplicables en el VB5... aunque eso ser seguramente en la segunda parte de este curso... que la habr, de eso puedes tener casi un 100% de certeza. Pero si pruebas esto: Print Len(aByte) El Visual Basic te dir que "nanai de la china", osease que no puedes saber cuantos caracteres tiene un array de bytes... Al menos usando Len... ni an usando LenB, as que si quieres lo compruebas, pero te dar el mismo error. Ahora bien, para saber los elementos que contiene un array, vimos que existen dos funciones: LBOUND y UBOUND, la primera para saber el valor del primer elemento y la ltima para saber el ltimo elemento del array. Por tanto podemos usar estas dos funciones para averiguar la longitud de esa cadena: Print UBound(aByte) Esto nos dar el nmero mximo de bytes de este array... s, muestra 19 (en 32 bits), en el entorno de 16 bits nos informar de que el elemento mayor de esta matriz es de 9; ya sabes que para 32 bits cada caracter son dos bytes, por tanto 10 caracteres son 20 bytes. El porqu de que muestre uno menos es porque empieza a contar por CERO, es decir que el elemento inferior de este array ser cero, lo podemos comprobar con esto otro: Print LBound(aByte) Tanto en VB de 16 cmo de 32 bits mostrar cero.

197

Si te preguntas que pasara si se aadiera a las declaraciones generales del Form la instruccin: Option Base 1, puedes comprobarlo, pero no cambiar los resultados mostrados, los arrays de Byte siempre empiezan por cero, independiente del valor que indiquemos en el Option Base. Esto es una afirmacin a medias, me explico, si a un array de bytes le asignas el contenido de una cadena, el elemento inferior siempre ser CERO, pero puedes dimensionar una matriz de este tipo de la misma forma que lo haras con cualquier otra de cualquier otro tipo de datos. Lo que ocurre es que deberas tener la precaucin de que el nmero de elementos fuese par si lo que pretendes es que contenga "algo parecido" a una cadena. Vamos a seguir probando un poco ms, escribe este cdigo: Dim aByte() As Byte aByte = "Hola" Dim i& Print aByte For i = LBound(aByte) To UBound(aByte) Print aByte(i); Next Print En el VB de 16 bits, te mostrar esto: 72 111 108 97, sin embargo en 32 bits el resultado sera este otro: 72 0 111 0 108 0 97 0 (si, ya se que he dicho que no iba a decir nada de los 16 bits, pero...) Con esto, se demuestra, ms o menos, eso de que cada caracter de 32 bits ocupa dos bytes, el primero es el normal y el segundo es el extendido... Para "rematar" esto de los arrays de bytes, una ltima prueba, si usas el VB4-16 debers modificarlo un poco, ya que slo tienes un byte por cada caracter y realmente es menos "instructivo" que si ests usando 32 bits. 'Se supone que tienes estas declaraciones de las variables i y a: 'Dim a$, i& 'Los valores deben ser pares ReDim aByte(-10 To 19) Dim j% j = 65 For i = LBound(aByte) To UBound(aByte) Step 2 aByte(i) = j aByte(i + 1) = 0 j = j + 1

198

Next Print aByte Print LBound(aByte), UBound(aByte) a = aByte Print a Aqu hemos usado un array de bytes con ndices que van de -10 a 19, recuerda que de -10 a -1 van 10 y de 0 a 19 van 20, por tanto sern 30 los bytes que tiene este array, es decir 15 caracteres, que van desde A (65) hasta O (letra o mayscula, 79), por eso se imprimen estos quince caracteres: ABCDEFGHIJKLMNO. Para terminar con estas pruebas, escribe esto a continuacin de lo anterior: Print LeftB(a, 6) Print RightB(a, 6) Print MidB(a, 5, 6) Esto ser lo que habr mostrado (en 32 bits): ABC, MNO y CDE Fjate que el MidB empieza por un nmero impar, si lo hicieras por uno par, el VB te mostrara una serie de interrogaciones... porque no sabe que es lo que quieres hacer con eso... ms o menos... Si te preguntas que es lo que ocurrira si en lugar de MidB(a, 5, 6) escribieras esto otro: MidB(a, 0, 6), te encontraras con un error del Visual Basic, ya que el argumento desde, debe ser un valor 1 hasta la longitud de la cadena. Bien, ya sabes algo ms, el resto que veamos tratar de las funciones de cadenas normales y corrientes, cualquier otra prueba con bytes la haces "por libre", cosa que te recomiendo para que te vayas acostumbrando a ellas, pero no te compliques demasiado, ya que no son tan frecuentes como debieran y siempre tendrs a mano la ayuda para poder "recordar" que estn ah.

Ms funciones de manejo de cadenas, por favor.


Convertir nmeros a cadenas: Cuando quieras convertir un nmero en una cadena, adems de usar la "conversin automtica" que hace el propio VB, puedes usar cualquiera de estas dos funciones: CStr y Str$ La diferencia "visible" entre estas dos funciones es, que la primera te devuelve slo el nmero, sin espacios, mientras que la segunda, en caso de que sea un nmero positivo, devolver el nmero pero con un espacio delante: Dim a As String, i As Integer

199

i = 10 a = CStr(i) Print "i=" & a a = Str$(i) Print "i=" & a Como vers, en el primer caso se mostrar: i= 10, en el segundo: i=10, es decir que Str$ le aade el espacio que tiene los nmeros positivos. Si el valor de i fuese -10 mostrara lo mismo en ambos casos. Pero esa no es la nica diferencia, si estamos trabajando con variables (o datos) que contengan decimales, CStr utilizar el decimal que est seleccionado en la configuracin regional y Str$ siempre usar el punto como separador decimal. ' Cuidado con los decimales (24/Ago/03) Dim d As Double d = 123.456 a = CStr(d) Print "d= " & a a = Str$(d) Print "d= " & a

Cambiar a maysculas / minsculas: Para hacer esto, usaremos las funciones: LCase y UCase, la primera convierte la cadena a minsculas y la segunda a maysculas: a = "Un niito clido" Print LCase(a) Print UCase(a) Aunque sea una chorradilla de frase, es para que compruebes que los caracteres acentuados y la ee tambin se convierten. En los nmeros, esto de la conversin no tiene sentido, por tanto no les afectar esto de la conversin y permanecern iguales.

Quitar los espacios del principio y del final: Las funciones usadas para hacer esto, son tres: LTrim$, RTrim$ y Trim$ La primera quita los espacios del principio, la segunda los espacios en blanco del final y la tercera los quita tanto del principio como del final. Esta ltima funcin es lo mismo que si hiciramos esto: Ltrim$(RTrim$(unaCadena)), que

200

es lo que haca yo antes de que el Visual Basic incluyese esta funcin... Vamos a ver un ejemplo: a = " Un ejemplo "

Print "<" & LTrim$(a) & ">" Print "<" & RTrim$(a) & ">" Print "<" & Trim$(a) & ">" El resultado ser el siguiente: <Un ejemplo >, < Un ejemplo>, <Un ejemplo> Es decir: se comprueba que actan como se esperaba...

Averiguar el cdigo de los caracteres: Ya sabes que cada "letra" que puede tener una cadena de caracteres est representada por un cdigo, as la A es el cdigo 65, la Z es el 90, la a es el 92 y la z es el 132. El cdigo del espacio es 32. Para averiguar los cdigos de los caracteres y para asignar a una cadena cualquier cdigo, convertido en el correspondiente caracter, se usan las funciones Chr$ y Asc. La primera devuelve el caracter correspondiente al nmero que se le pasa como parmetro. La segunda hace la operacin inversa, nos dice cual es el cdigo del caracter que se le ha indicado. Veamos un ejemplo: Print Asc("Z") Print Chr$(90) El primero nos dir que 90 es el cdigo de la Z maysculas y en el segundo caso, nos confirma que el caracter representado por 90 ser la Z. Si en ASC se le pasa una cadena con ms de una caracter, slo nos devuelve el valor del primero: Print Asc("Hola") Imprimir 72, es decir el valor de la H mayscula.

Llenar una cadena con una cantidad de caracteres: Para esto, podemos usar un bucle y en cada iteracin del mismo incrementar el contenido de una cadena o bien podemos usar dos de las funciones que el Visual Basic pone a nuestra disposicin: Space$ y String$ La primera devuelve la cantidad de espacios que se le indique, si queremos 10 espacios haremos esto: a = Space$(10) A la segunda se le pasan dos parmetros, el primero indicando la cantidad de caracteres que queremos y el segundo el cdigo de ese caracter: a = String$(10, 65) nos dar diez

201

letras A maysculas. Tambin podemos pasarle en el segundo parmetro una cadena, por tanto el ejemplo anterior se podra escribir as: a = String$(10, "A") Si la cadena que le pasamos tiene ms de un caracter, nos devuelve slo el primer caracter de esa cadena: a = String$(10, "Pepe") Es decir slo 10 letras P.

Comparar el contenido de dos cadenas: Ya sabes que las cadenas se pueden comparar igual que se hacen con los nmeros: usando el signo igual. If "Hola" = "hola" Then Es un ejemplo "chorra", pero se puede hacer. Esta comparacin debera devolver FALSE, ya que la H inicial en una de las cadenas est en maysculas y en la otra en minscula. Si no le hemos indicado nada al VB, nos dir que son diferentes, pero podemos indicarle al Visual que al comparar las cadenas, ignore las diferencias entre las maysculas y minsculas. Para ello tendremos que agregar en la parte general del mdulo, donde est el Option Explicit, la siguiente instruccin: Option Compare Text De esta forma, las comparaciones realizadas siempre sern independiente de que sean maysculas o minsculas, incluso si estn mezcladas las maysculas con las minsculas. Por defecto el Visual Basic hace lo que se llama una comparacin binaria, es decir que comprueba el cdigo de cada uno de los caracteres que componen las cadenas; tambin se le puede indicar esto para que todas la comparaciones realizadas sean siempre en modo "binario", aunque no hace falta indicarlo, ya que es el valor por defecto, pero si quieres hacerlo, para saber que ests haciendo las comparaciones de esa forma, aade esta lnea: Option Compare Binary Las comparaciones tambin se pueden efectuar para saber cual es mayor o menor, el VB se guiar por el cdigo de cada caracter comparado, debers tener en cuenta que los nmeros estn antes que las letras, por tanto "1234" sera menor que "ABCD" y "VWXYZ" ser mayor que "DEFGH" Pero adems de efectuar las comparaciones de esta forma, existe una funcin que sirve precisamente para eso: StrComp Esta funcin permite que las comparaciones se hagan de distinta forma: Usando el valor por defecto, es decir el indicado en Option Compare, usando siempre comparacin binaria, (distingue entre maysculas y minsculas) y usando siempre comparacin "textual", (no hace distincin entre maysculas y minsculas) Los parmetros que usa esta funcin son los siguientes: cadena1, cadena2 y opcionalmente el tipo de comparacin a realizar. Si este ltimo parmetro no se especifica, se efectuar la comparacin segn se indique en Option Compare. En otro caso, los valores pueden ser: 0 (comparacin binaria) 1 (comparacin textual). Existe otro valor que es usado para las bases de datos de Access, pero que nunca lo he llegado a probar... slo lo he visto cuando he leido la ayuda. Los valores que devolver esta funcin sern: 0 si las dos cadenas son iguales,

202

-1 si la primera cadena es menor que la segunda 1 si la primera cadena es mayor que la segunda. Debes tener en cuenta que "HOLA" es menor que "hola", si la comparacin es binaria, lo mismo con "Hola", ya que la H mayscula tiene un cdigo menor que la h minscula. Veamos un ejemplo: Dim b As String a = "Hola" b = "hola" Print "Usando el Option Compare: "; StrComp(a, b) Print "Usando comparacin textual: "; StrComp(a, b, vbTextCompare) Print "Usando comparacin binaria: "; StrComp(a, b, vbBinaryCompare) Los valores de las constantes vbTextCompare y vbBinaryCompare ya estn definidos por el VB y son 1 y 0 respectivamente.

Asignar valores e incrementar el contenido de una cadena: Muchas de las cosillas que estamos viendo en esta entrega, ya se han usado en otras entregas, pero as tendrs una referencia ms a mano... adems de servirte de recordatorio, por si lo has olvidado. La concatenacin consiste en aadir a una cadena el contenido de otras cadenas o caracteres. Como sabrs si quieres incrementar el valor de una variable numrica, haces esto: Num = Num + 1 El basic interpretar primero la expresin, o lo que haya despus del signo igual y el resultado lo asignar a la variable que est a la izquierda del signo igual. Lo mismo hace con las cadenas de caracteres, lo que ocurre es que slo permite la suma. Aunque desde hace tiempo que Microsoft no recomienda esta forma de concatenar las cadenas, ya que el Visual Basic puede "malintepretarlo" y recomienda que se use el signo & (ampersand, creo que se llama). Usando ese signo, el VB sabe exactamente lo que debe hacer. Por tanto si haces esto: a = "Hola" b = "mundo" a = a & " " & b Print a Te mostrar Hola mundo, es decir ha guardado en "a" lo que ya haba adems de un espacio y el contenido de "b"

203

Este tipo de concatenacin tambin puedes hacerlo en los controles Label y Text o en cualquiera que disponga de alguna propiedad en la que puedas guardar una cadena. Por ejemplo el Caption de un label (que es la propiedad por defecto y por tanto se puede omitir). Si tienes un Label2 en el form, podras hacer esto: Label2 = "Hola" Label2 = Label2 & " " & b

Y se mostrar en ese label lo mismo que antes se imprimi. Esto mismo se puede hacer indicando que se asigna a la propiedad Caption de esa etiqueta: Label2.Caption = "Hola" Label2.Caption = Label2.Caption & " El resultado sera el mismo. Nota: Siempre que quieras "sumar" cadenas, deberas usar el signo & en lugar de +, ya que el primero es evidente que "suma" cadenas (concatena), mientras que el segundo, aunque tambin "suma", debera usarse slo para "sumar" nmeros. " & b

Averiguar la posicin de una cadena dentro de otra: Para esto usaremos la funcin Instr, esta funcin espera dos parmetros como mnimo, aunque permite cuatro: Instr( [posInicio,] Cadena1, Cadena2[, tipo_comparacin]) Las dos cadenas, son las que usar para devolver la posicin de la segunda cadena dentro de la primera. Si hacemos esto: Print Instr("Hola mundo", "mundo") nos mostrar un SEIS, ya que "mundo" est en la posicin sexta, al menos ah es dnde empieza. Si se especifica posInicio, empezar a comprobar a partir de esa posicin inclusive, por tanto: Print Instr("Hola mundo", "o") imprimir 2 Print Instr( 6, "Hola mundo", "o") imprimir 10, ya que busca una letra "o" a partir de la posicin 6. El ltimo parmetro: tipo_comparacin es para realizar comparaciones binarias o de texto, como ya hemos visto anteriormente, si este parmetro tiene la misma funcin que en StrComp. Es decir si no se especifica, la comparacin se hace segn el Option Compare y si se indica, se hace segn lo indicado. Si especificas este parmetro, debers indicar tambin la posicin de inicio, sino el VB te dir que los tipos de datos no coinciden. Un ejemplo: Print InStr("Hola mundo", "o") Print InStr(6, "Hola mundo", "o")

204

Print InStr("Hola mundo", "O") Print InStr(1, "Hola mundo", "O", vbBinaryCompare) Print InStr(1, "Hola mundo", "O", vbTextCompare) Dependiendo de que tengamos Option Compare Text el tercer caso imprimir un cero (si no tenemos la comparacin textual) o un 2 si tenemos esa opcin de comparacin En los dems casos, siempre devolver el mismo resultado. Pero no te confundas, devuelven lo mismo, porque sabemos que debe devolverlo, ya que se especifica como segunda cadena una constante que representa a una "o" minscula y en la primera cadena tenemos dos de esa letra, otro gallo cantara si tanto la primera como la segunda cadena fuesen variables y no sabemos lo que va a contener. Cuando sea imprescindible saber la posicin de una cadena dentro de otra, teniendo en cuenta el que haya que distinguir o no entre las maysculas y minsculas, es recomendable que uses el tipo de comparacin que quieres realizar. Si no lo haces, al menos recuerda o comprueba que tipo de comparacin se realizarn por defecto en ese mdulo. Bien, creo que ya tienes a tu disposicin casi todas las funciones de manejo de cadenas que puedas necesitar. Ahora toca que practiques un poco con ellas para que le vayas cogiendo el "tranquillo" y te las aprendas. Una cosa que quiero aclarar es que no pretendo hacer con este curso un "sustituto" de la ayuda o los manuales del Visual Basic, por eso te recomiendo siempre que las instrucciones con las que te encuentres en algunos de los ejemplos, las busques en la ayuda o los manuales, lo mismo que para saber "manejarte" en el entorno de Visual Basic. S que esto ya lo he dicho antes, o al menos debera haberlo dicho, pero lo hago de nuevo para que no esperes un "diccionario" de todas las instrucciones que tiene el Visual Basic. Lo que pretendo siempre es que te enteres de cmo se usan esas instrucciones e incluso cuando usar una en lugar de otra... en fin... si ves que alguna que otra vez se me va la "olla", me lo dices e intentar solucionar ese despiste. Ejercicios? Te podra poner muchos... pero slo voy a ponerte uno y con ese tendrs "pa rato" Escribe una funcin que devuelva la posicin de una cadena dentro de otra, pero comprobando esa posicin por el final. Es decir que Instr("Hola Mundo","o") devolvera 2, pero con nuestra funcin devolvera 10. Podramos llamarla RInstr y se usara as: RInstr("Hola mundo","o") Haz una segunda versin en la cual se le pase como primer parmetro la posicin por la que se empezar a comprobar, de esta forma: RInstr2(6,"Hola mundo","o") nos dar un valor 2 En las soluciones veremos cmo podemos indicar parmetros opcionales en nuestros procedimientos y crearemos una nica funcin que se use igual que Instr, es decir pasando como primer parmetro opcional la posicin de inicio de la bsqueda.

Y siguiendo la "sana" costumbre, te pido tu opinin sobre esta entrega.

205

Nos vemos. Guillermo P.S.

Curso Bsico de Programacin en Visual Basic


Empezaremos viendo la parte fcil del ejercicio, es decir la que nos muestra la posicin cuando la funcin espera slo dos parmetros, el primero ser la cadena en la que hay que encontrar lo que haya en la segunda cadena. Private Function RInstr1(ByVal s1 As String, ByVal s2 As String) As Long Dim i As Long Dim sTmp As String RInstr1 = 0 For i = Len(s1) To 1 Step -1 sTmp = Mid$(s1, i, Len(s2)) If sTmp = s2 Then RInstr1 = i Exit For End If Next End Function Este tiene poco que explicar. Se hace un bucle desde la ltima posicin de la primera cadena Len(s1) hasta el principio, ya que lo que necesitamos es encontrar la posicin de s2 empezando a "mirar" desde el final. A continuacin guardamos en sTmp un trozo de cadena que va desde la posicin actual y que contiene tantos caracteres como tenga la segunda cadena, si te fijas, el bucle se podra hacer tambin de esta forma: For i = Len(s1) - Len(s2) + 1 To 1 Step -1 Ya que no nos sirve comparar el ltimo caracter con la cadena s2 cuando esta ltima tiene ms de un caracter, por ejemplo. Esto es inapreciable en cadenas cortas, pero en otras ms largas, acortar el tiempo del bucle. Para el segundo caso, hay que saber que los procedimientos (sub y function) permiten parmetros opcionales, estos se indican con la palabra Optional delante de los parmetros que vayan a ser opcionales. La nica pega es que cuando un parmetro es opcional los que le siguen tambin tienen

206

que ser opcionales. Entonces cmo indicar que el parmetro opcional sea el primero? Pues... ms o menos fcil... aunque con truco. Cuando un parmetro es opcional, se puede usar la funcin IsMissing para comprobar si se ha especificado o no ese parmetro. Esto es cierto siempre que el parmetro opcional sea de tipo Variant sin un valor por defecto. En el VB4 todos los parmetros opcinales deben ser Variant y sin valores por defecto, pero en VB5 (y posteriores) los parmetros opcionales pueden ser del tipo que queramos, adems de poder tener un valor por defecto, es decir, si no se especifica, se le da un valor "predeterminado". Ahora vamos a usar slo los parmetros sin valores predeterminados, pero ms adelante seguramente lo usaremos. Sabiendo que con IsMissing podemos averiguar si se ha especificado o no el parmetro en opcional, haremos lo siguiente: Si se especifican los tres parmetros, el primero ser la posicin por la que se empezar a comprobar. Si no se especifica el ltimo parmetro, engaaremos al Visual Basic, dicindole que los dos parmetros introducidos son la primera y la segunda cadena... Ya ves que slo es echarle un poco de "cabeza" al asunto... Vamos a ver la declaracin de esta funcin: Private Function RInstr(ByVal v1 As Variant, ByVal v2 As Variant, Optional ByVal v3 As Variant) As Long Dim i As Long Dim sTmp As String Dim s1 As String Dim s2 As String Dim posIni As Long If IsMissing(v3) Then 'Si no se especifican los tres parmetros s1 = CStr(v1) s2 = CStr(v2) posIni = Len(s1) Else posIni = CLng(v1) s1 = CStr(v2) s2 = CStr(v3) End If 'Valor inicial de la bsqueda, si no se encuentra, es cero RInstr = 0 'Siempre se empieza a buscar por el final 'la posicin por la que empezar 'la primera cadena (segundo parmetro) 'la segunda cadena (tercer parmetro) 'La primera cadena 'la segunda cadena 'el ltimo caracter de la cadena

207

For i = posIni - Len(s2) + 1 To 1 Step -1 'Tomar el nmero de caracteres que tenga la segunda cadena sTmp = Mid$(s1, i, Len(s2)) 'Si son iguales... If sTmp = s2 Then 'esa es la posicin RInstr = i Exit For End If Next End Function Ahora quedara hacer una funcin que comtemple las mismas opciones que tiene el InStr normal, es decir que se pueda especificar un cuarto parmetro que nos indique si la comparacin se hace de una forma u otra. Que te parece esto como un ejercicio nuevo? Saba que contestara de forma afirmativa... je, je... Pues intntalo... y si quieres ver la solucin, selecciona el contenido de la caja negra esa que hay al final y vers...

A ver si ya terminamos en la prxima entrega el acceso aleatorio a los ficheros.

Nota: He quitado lo de la seleccin porque no siempre funciona... al menos ahora no se ve el texto al seleccionarlo...

' Private Function RInstr(ByVal v1 As Variant, ByVal v2 As Variant, _ Optional ByVal v3 As Variant, _ Optional ByVal v4 As Variant) As Long Dim i As Long Dim sTmp As String Dim s1 As String

208

Dim s2 As String Dim posIni As Long If IsMissing(v3) Then 'Si no se especifican los tres parmetros s1 = CStr(v1) s2 = CStr(v2) posIni = Len(s1) Else posIni = CLng(v1) s1 = CStr(v2) s2 = CStr(v3) End If 'Valor inicial de la bsqueda, si no se encuentra, es cero RInstr = 0 'Siempre se empieza a buscar por el final For i = posIni - Len(s2) + 1 To 1 Step -1 'Tomar el nmero de caracteres que tenga la segunda cadena sTmp = Mid$(s1, i, Len(s2)) 'Si se especifica el tipo de comparacin If Not IsMissing(v4) Then 'Se usa StrComp con ese parmetro If StrComp(sTmp, s2, v4) = 0 Then 'esa es la posicin RInstr = i Exit For End If Else 'Aqu tambin se podra usar 'If StrComp(sTmp, s2) = 0 Then 'Si son iguales... If sTmp = s2 Then 'esa es la posicin RInstr = i Exit For End If 'la posicin por la que empezar 'la primera cadena (segundo parmetro) 'la segunda cadena (tercer parmetro) 'La primera cadena 'la segunda cadena 'el ltimo caracter de la cadena

209

End If Next End Function

A diferencia del Visual Basic, no dar error si se especifica el cuarto parmetro y no se especifican los dems, por la sencilla razn de que si slo se especifican 3, el cuarto nunca llega a tener un valor... (elemental mi querido Guille). De lo que tendrs que tener "precaucin", al menos si quieres que funcione todo bien, es de indicar los valores que se esperan... ya que sino es as, no conseguirs tu propsito... o al menos no el deseado.

Curso Bsico de Programacin en Visual Basic


A estas alturas ya estars acostumbrado a mis "desvaros", eso demuestra que soy de lo menos ordenado de lo que se pueda ser, pero... ese es un problemilla que llevo conmigo desde chiquitillo, as que... La cuestin es que parece ser que no voy a terminar con esto del acceso a los ficheros... y eso que tengo ganas de terminar con ello para poder pasar a otra cosa, pero... se me resiste el tema... Realmente no es que se me resista, es que por el camino van surgiendo nuevas cosillas que es bueno que sepas y como este curso no est enfocado para que lo aprueben en ningn plan nacional de estudios... pues eso... Lo que si que me interesa saber es que no te pierdes con tantos desvaros... es decir, que sigues la lnea... aunque eres libre de poder desviarte y salirte por la tangente para mirar los manuales y la ayuda del Visual Basic, que aunque no te lo creas, tambin sirve para aprender... ahora, lo que pasa es que hay que saber comprenderla... y de eso creo que es de lo que de una forma u otra me estoy encargando yo... si no lo consigo, malo y si lo consigo... mejor para todos. Siguiendo con esos desvaros, vamos a empezar esta entrega con el manejo de las cadenas, que, aunque ya vimos un poco de ella en la entrega anterior, no estar de ms saber algo ms. Ten en cuenta que la mayora de las veces vamos a manipular cadenas... y no es plan de que nos pillen "atados" sin saber que hacer... (chiste malo, lo reconozco...) Seguramente no me voy a enrollar demasiado en este tema, digo "seguramente" porque no s en que acabar esta entrega... as que paciencia y disfruta y aprende con lo que se diga en ella. Ahora vamos al tema.

210

Ya hemos visto, cmo tomar caracteres de una cadena desde el principio (LEFT), desde el final (RIGHT) y desde cualquier punto (MID); tambin podemos saber cuantos caracteres tiene una cadena (LEN) e incluso convertir un nmero en cadena (CSTR) o una cadena en nmero (VAL). Tienes que recordar que el VB tiene como tipo de variable por defecto el Variant, por tanto estas funciones realmente devuelven un tipo Variant, dentro de este tipo hay una serie de subtipos, que realmente son los tipos que vimos al principio de este curso, es decir Integer, Long, String, etc. A que viene este nuevo lo Guille? A que es conveniente que lo sepas, nada ms, pero, al menos por ahora no te preocupes por ello, el VB se encarga de hacer sus conversiones, aunque nosotros podemos obligarle a que lo haga, para ello deberemos usar esas funciones aadindole al final el signo dlar ($), ya sabes que ese signo es el que se emplea para indicar que una variable es de cadena. Por tanto LEFT$, MID$ y RIGHT$ devolvern siempre una cadena, mientras que LEFT, MID y RIGHT lo que devuelven es un Variant de subtipo String... un lo... pero ya te digo que no debes preocuparte. Nota del 24/Ago/2003: Repasando esta entrega quiero hacer un inciso, que aunque en el prrafo anterior ha quedado "medio-claro", quiero que se clarifique totalmente, ya que en ese prrafo no te dije que el rendimiento ser mejor si lo que pretendes es usar cadenas de caracteres. Me estoy refiriendo a que uses las funciones acabadas con el signo dlar ($), de esa forma le ests "confirmando" al Visual Basic que quieres usar cadenas de caracteres y no un dato del tipo Variant con "sub-tipo" String. Un Variant siempre es ms "pesado" que un tipo String, (lo de pesado quiere decir que necesitar de ms "tratamiento" por parte del VB). Hay un montn de funciones para esto del manejo y conversin de las cadenas, en esta entrega veremos algunas de ellas, las que usaremos ms a menudo. Si eres de espritu inquieto, (por decir algo), puedes echar mano de la ayuda del Visual Basic y buscar las distintas funciones e instrucciones que manejan o manipulan las cadenas... la verdad que te puedes entretener bastante con ellas... aunque no te fes de todo lo que veas y te diga la ayuda si no lo compruebas por ti mismo... que al menos uno de los ejemplos est equivocado, pero me imagino que se deber a la traduccin... en fin... por no fiarte, no te fes ni de lo que yo te diga, siempre comprueba las cosas, as saldrs de dudas... y si no sales de dudas, pues... peor "pa t" Creo que voy a complicarte un poco ms la vida, pero no te espantes, no va a ser en vano. Adems de los tipos de datos que hemos vistos hasta ahora, hay otro que sirve para esto de las cadenas, o casi, ya que se podra usar para manejar cada uno de los bytes de una cadena, fijate que he dicho "bytes" y no caracteres, ya que si trabajas en un entorno de 32 bits, cada caracter de una cadena se compone de dos bytes, si ests usando el VB de 16 bits slo hay un byte por cada uno de los caracteres. El tipo en cuestin es BYTE y en ese tipo slo se puede almacenar un valor que va desde 0 a 255, es decir un byte, la lgica algunas veces es aplastante... As que, si le echas un vistazo a la ayuda, te encontrars con algunas funciones que tienen al final una letra B, eso significa que devuelve valores tipo Byte, as por ejemplo LENB, devolver el nmero de bytes que tiene una cadena. Prueba esto, vers que si lo usas con el VB de 16 bits te devuelve una cosa distinta a lo que hara en 32 bits:

211

Print Len("Hola Mundo") Print LenB("Hola Mundo") Lo que se mostrar, (recuerda de ponerle un Show, si escribes este cdigo en el evento Load del form), ser 10 y 20 si usas el VB de 32 bits, (recuerda que el VB5 slo es de 32 bits), pero en 16 bits (VB3 VB4-16), mostrar en ambos casos 10. Vamos a practicar algunas cosillas con esto de los Bytes (o con datos de tipo Byte). Ya sabes que puedes crear una array de cualquier tipo de datos, incluso de tipos definidos, as que tambin podemos crear arrays del tipo Byte, pero este tipo de arrays tienen una particularidad. Una cadena (variable de tipo string), realmente es un array del tipo Byte, esto nos permite manejar las cadenas de caracteres como si fuesen arrays de bytes o mejor an usar los arrays del tipo byte, como si fuesen cadenas. Vamos a comprobarlo, pon estas lneas de cdigo en el evento Load de un form: Show 'para que se muestre Dim aByte() As Byte aByte = "Hola Mundo" Print aByte Como comprobars se muestra Hola Mundo... igual que si lo hubieses asignado a una cadena de caracteres. En el VB4-16 te habr dado un error, para evitar ese error y que se muestre el contenido, puedes asignarlo a una cadena e imprimir esa cadena, tambin puedes asignarlo a un Label y se mostrar el contenido de ese array como si fuese una cadena. Esto mismo, por supuesto tambin es aplicable al Visual Basic de 32 bits. Aclaracin para los que trabajis con 16 bits: Para no complicar mucho las cosas, si ests usando 16 bits, comprueba lo que aqu se diga y saca tus propias conclusiones. A pesar de que an hay sistemas funcionando de 16 bits y gente que por tanto programa para esos sistemas, creo que el enfoque adecuado de este curso debera concentrarse en los sistemas de 32 bits, de todas formas, la mayora de las cosas "realmente" vlidas servirn tanto para 16 como para 32 bits. Pero voy an ms lejos, dentro de poco, no s realmente en cuantas entregas, pero el enfoque se ir concentrando en el Visual Basic 5 y posteriores, (cuando los haya), ya que en el VB5 se han introducido cosas realmente interesantes para la programacin enfocada a objetos, (que pronto empezaremos a tocar), que aunque la mayora de esas cosas sean vlidas para el VB4, en algn momento dejar de serlo y slo sern aplicables al VB5 y posteriores... Quiero dejar esto claro, para que despus no haya sorpresas... de todas formas, recalco que esto es un curso bsico y como tal, servir para prcticamente cualquier versin de VB, aunque mi "despiste" seguramente me llevar a tocar cosas slo aplicables en el

212

VB5... aunque eso ser seguramente en la segunda parte de este curso... que la habr, de eso puedes tener casi un 100% de certeza. Pero si pruebas esto: Print Len(aByte) El Visual Basic te dir que "nanai de la china", osease que no puedes saber cuantos caracteres tiene un array de bytes... Al menos usando Len... ni an usando LenB, as que si quieres lo compruebas, pero te dar el mismo error. Ahora bien, para saber los elementos que contiene un array, vimos que existen dos funciones: LBOUND y UBOUND, la primera para saber el valor del primer elemento y la ltima para saber el ltimo elemento del array. Por tanto podemos usar estas dos funciones para averiguar la longitud de esa cadena: Print UBound(aByte) Esto nos dar el nmero mximo de bytes de este array... s, muestra 19 (en 32 bits), en el entorno de 16 bits nos informar de que el elemento mayor de esta matriz es de 9; ya sabes que para 32 bits cada caracter son dos bytes, por tanto 10 caracteres son 20 bytes. El porqu de que muestre uno menos es porque empieza a contar por CERO, es decir que el elemento inferior de este array ser cero, lo podemos comprobar con esto otro: Print LBound(aByte) Tanto en VB de 16 cmo de 32 bits mostrar cero. Si te preguntas que pasara si se aadiera a las declaraciones generales del Form la instruccin: Option Base 1, puedes comprobarlo, pero no cambiar los resultados mostrados, los arrays de Byte siempre empiezan por cero, independiente del valor que indiquemos en el Option Base. Esto es una afirmacin a medias, me explico, si a un array de bytes le asignas el contenido de una cadena, el elemento inferior siempre ser CERO, pero puedes dimensionar una matriz de este tipo de la misma forma que lo haras con cualquier otra de cualquier otro tipo de datos. Lo que ocurre es que deberas tener la precaucin de que el nmero de elementos fuese par si lo que pretendes es que contenga "algo parecido" a una cadena. Vamos a seguir probando un poco ms, escribe este cdigo: Dim aByte() As Byte aByte = "Hola" Dim i& Print aByte

213

For i = LBound(aByte) To UBound(aByte) Print aByte(i); Next Print En el VB de 16 bits, te mostrar esto: 72 111 108 97, sin embargo en 32 bits el resultado sera este otro: 72 0 111 0 108 0 97 0 (si, ya se que he dicho que no iba a decir nada de los 16 bits, pero...) Con esto, se demuestra, ms o menos, eso de que cada caracter de 32 bits ocupa dos bytes, el primero es el normal y el segundo es el extendido... Para "rematar" esto de los arrays de bytes, una ltima prueba, si usas el VB4-16 debers modificarlo un poco, ya que slo tienes un byte por cada caracter y realmente es menos "instructivo" que si ests usando 32 bits. 'Se supone que tienes estas declaraciones de las variables i y a: 'Dim a$, i& 'Los valores deben ser pares ReDim aByte(-10 To 19) Dim j% j = 65 For i = LBound(aByte) To UBound(aByte) Step 2 aByte(i) = j aByte(i + 1) = 0 j = j + 1 Next Print aByte Print LBound(aByte), UBound(aByte) a = aByte Print a Aqu hemos usado un array de bytes con ndices que van de -10 a 19, recuerda que de -10 a -1 van 10 y de 0 a 19 van 20, por tanto sern 30 los bytes que tiene este array, es decir 15 caracteres, que van desde A (65) hasta O (letra o mayscula, 79), por eso se imprimen estos quince caracteres: ABCDEFGHIJKLMNO. Para terminar con estas pruebas, escribe esto a continuacin de lo anterior: Print LeftB(a, 6) Print RightB(a, 6)

214

Print MidB(a, 5, 6) Esto ser lo que habr mostrado (en 32 bits): ABC, MNO y CDE Fjate que el MidB empieza por un nmero impar, si lo hicieras por uno par, el VB te mostrara una serie de interrogaciones... porque no sabe que es lo que quieres hacer con eso... ms o menos... Si te preguntas que es lo que ocurrira si en lugar de MidB(a, 5, 6) escribieras esto otro: MidB(a, 0, 6), te encontraras con un error del Visual Basic, ya que el argumento desde, debe ser un valor 1 hasta la longitud de la cadena. Bien, ya sabes algo ms, el resto que veamos tratar de las funciones de cadenas normales y corrientes, cualquier otra prueba con bytes la haces "por libre", cosa que te recomiendo para que te vayas acostumbrando a ellas, pero no te compliques demasiado, ya que no son tan frecuentes como debieran y siempre tendrs a mano la ayuda para poder "recordar" que estn ah.

Ms funciones de manejo de cadenas, por favor.


Convertir nmeros a cadenas: Cuando quieras convertir un nmero en una cadena, adems de usar la "conversin automtica" que hace el propio VB, puedes usar cualquiera de estas dos funciones: CStr y Str$ La diferencia "visible" entre estas dos funciones es, que la primera te devuelve slo el nmero, sin espacios, mientras que la segunda, en caso de que sea un nmero positivo, devolver el nmero pero con un espacio delante: Dim a As String, i As Integer i = 10 a = CStr(i) Print "i=" & a a = Str$(i) Print "i=" & a Como vers, en el primer caso se mostrar: i= 10, en el segundo: i=10, es decir que Str$ le aade el espacio que tiene los nmeros positivos. Si el valor de i fuese -10 mostrara lo mismo en ambos casos. Pero esa no es la nica diferencia, si estamos trabajando con variables (o datos) que contengan decimales, CStr utilizar el decimal que est seleccionado en la configuracin regional y Str$ siempre usar el punto como separador decimal. ' Cuidado con los decimales (24/Ago/03) Dim d As Double

215

d = 123.456 a = CStr(d) Print "d= " & a a = Str$(d) Print "d= " & a

Cambiar a maysculas / minsculas: Para hacer esto, usaremos las funciones: LCase y UCase, la primera convierte la cadena a minsculas y la segunda a maysculas: a = "Un niito clido" Print LCase(a) Print UCase(a) Aunque sea una chorradilla de frase, es para que compruebes que los caracteres acentuados y la ee tambin se convierten. En los nmeros, esto de la conversin no tiene sentido, por tanto no les afectar esto de la conversin y permanecern iguales.

Quitar los espacios del principio y del final: Las funciones usadas para hacer esto, son tres: LTrim$, RTrim$ y Trim$ La primera quita los espacios del principio, la segunda los espacios en blanco del final y la tercera los quita tanto del principio como del final. Esta ltima funcin es lo mismo que si hiciramos esto: Ltrim$(RTrim$(unaCadena)), que es lo que haca yo antes de que el Visual Basic incluyese esta funcin... Vamos a ver un ejemplo: a = " Un ejemplo "

Print "<" & LTrim$(a) & ">" Print "<" & RTrim$(a) & ">" Print "<" & Trim$(a) & ">" El resultado ser el siguiente: <Un ejemplo >, < Un ejemplo>, <Un ejemplo> Es decir: se comprueba que actan como se esperaba...

Averiguar el cdigo de los caracteres: Ya sabes que cada "letra" que puede tener una cadena de caracteres est representada por un cdigo, as la A es el cdigo 65, la Z es el 90, la a es el 92 y la z es el 132. El cdigo del espacio es 32. Para averiguar los cdigos de los caracteres y para asignar a una cadena cualquier cdigo,

216

convertido en el correspondiente caracter, se usan las funciones Chr$ y Asc. La primera devuelve el caracter correspondiente al nmero que se le pasa como parmetro. La segunda hace la operacin inversa, nos dice cual es el cdigo del caracter que se le ha indicado. Veamos un ejemplo: Print Asc("Z") Print Chr$(90) El primero nos dir que 90 es el cdigo de la Z maysculas y en el segundo caso, nos confirma que el caracter representado por 90 ser la Z. Si en ASC se le pasa una cadena con ms de una caracter, slo nos devuelve el valor del primero: Print Asc("Hola") Imprimir 72, es decir el valor de la H mayscula.

Llenar una cadena con una cantidad de caracteres: Para esto, podemos usar un bucle y en cada iteracin del mismo incrementar el contenido de una cadena o bien podemos usar dos de las funciones que el Visual Basic pone a nuestra disposicin: Space$ y String$ La primera devuelve la cantidad de espacios que se le indique, si queremos 10 espacios haremos esto: a = Space$(10) A la segunda se le pasan dos parmetros, el primero indicando la cantidad de caracteres que queremos y el segundo el cdigo de ese caracter: a = String$(10, 65) nos dar diez letras A maysculas. Tambin podemos pasarle en el segundo parmetro una cadena, por tanto el ejemplo anterior se podra escribir as: a = String$(10, "A") Si la cadena que le pasamos tiene ms de un caracter, nos devuelve slo el primer caracter de esa cadena: a = String$(10, "Pepe") Es decir slo 10 letras P.

Comparar el contenido de dos cadenas: Ya sabes que las cadenas se pueden comparar igual que se hacen con los nmeros: usando el signo igual. If "Hola" = "hola" Then Es un ejemplo "chorra", pero se puede hacer. Esta comparacin debera devolver FALSE, ya que la H inicial en una de las cadenas est en maysculas y en la otra en minscula. Si no le hemos indicado nada al VB, nos dir que son diferentes, pero podemos indicarle al Visual que al comparar las cadenas, ignore las diferencias entre las maysculas y minsculas. Para ello tendremos que agregar en la parte general del mdulo, donde est el Option Explicit, la siguiente instruccin:

217

Option Compare Text De esta forma, las comparaciones realizadas siempre sern independiente de que sean maysculas o minsculas, incluso si estn mezcladas las maysculas con las minsculas. Por defecto el Visual Basic hace lo que se llama una comparacin binaria, es decir que comprueba el cdigo de cada uno de los caracteres que componen las cadenas; tambin se le puede indicar esto para que todas la comparaciones realizadas sean siempre en modo "binario", aunque no hace falta indicarlo, ya que es el valor por defecto, pero si quieres hacerlo, para saber que ests haciendo las comparaciones de esa forma, aade esta lnea: Option Compare Binary Las comparaciones tambin se pueden efectuar para saber cual es mayor o menor, el VB se guiar por el cdigo de cada caracter comparado, debers tener en cuenta que los nmeros estn antes que las letras, por tanto "1234" sera menor que "ABCD" y "VWXYZ" ser mayor que "DEFGH" Pero adems de efectuar las comparaciones de esta forma, existe una funcin que sirve precisamente para eso: StrComp Esta funcin permite que las comparaciones se hagan de distinta forma: Usando el valor por defecto, es decir el indicado en Option Compare, usando siempre comparacin binaria, (distingue entre maysculas y minsculas) y usando siempre comparacin "textual", (no hace distincin entre maysculas y minsculas) Los parmetros que usa esta funcin son los siguientes: cadena1, cadena2 y opcionalmente el tipo de comparacin a realizar. Si este ltimo parmetro no se especifica, se efectuar la comparacin segn se indique en Option Compare. En otro caso, los valores pueden ser: 0 (comparacin binaria) 1 (comparacin textual). Existe otro valor que es usado para las bases de datos de Access, pero que nunca lo he llegado a probar... slo lo he visto cuando he leido la ayuda. Los valores que devolver esta funcin sern: 0 si las dos cadenas son iguales, -1 si la primera cadena es menor que la segunda 1 si la primera cadena es mayor que la segunda. Debes tener en cuenta que "HOLA" es menor que "hola", si la comparacin es binaria, lo mismo con "Hola", ya que la H mayscula tiene un cdigo menor que la h minscula. Veamos un ejemplo: Dim b As String a = "Hola" b = "hola" Print "Usando el Option Compare: "; StrComp(a, b) Print "Usando comparacin textual: "; StrComp(a, b, vbTextCompare) Print "Usando comparacin binaria: "; StrComp(a, b, vbBinaryCompare)

218

Los valores de las constantes vbTextCompare y vbBinaryCompare ya estn definidos por el VB y son 1 y 0 respectivamente.

Asignar valores e incrementar el contenido de una cadena: Muchas de las cosillas que estamos viendo en esta entrega, ya se han usado en otras entregas, pero as tendrs una referencia ms a mano... adems de servirte de recordatorio, por si lo has olvidado. La concatenacin consiste en aadir a una cadena el contenido de otras cadenas o caracteres. Como sabrs si quieres incrementar el valor de una variable numrica, haces esto: Num = Num + 1 El basic interpretar primero la expresin, o lo que haya despus del signo igual y el resultado lo asignar a la variable que est a la izquierda del signo igual. Lo mismo hace con las cadenas de caracteres, lo que ocurre es que slo permite la suma. Aunque desde hace tiempo que Microsoft no recomienda esta forma de concatenar las cadenas, ya que el Visual Basic puede "malintepretarlo" y recomienda que se use el signo & (ampersand, creo que se llama). Usando ese signo, el VB sabe exactamente lo que debe hacer. Por tanto si haces esto: a = "Hola" b = "mundo" a = a & " " & b Print a Te mostrar Hola mundo, es decir ha guardado en "a" lo que ya haba adems de un espacio y el contenido de "b" Este tipo de concatenacin tambin puedes hacerlo en los controles Label y Text o en cualquiera que disponga de alguna propiedad en la que puedas guardar una cadena. Por ejemplo el Caption de un label (que es la propiedad por defecto y por tanto se puede omitir). Si tienes un Label2 en el form, podras hacer esto: Label2 = "Hola" Label2 = Label2 & " " & b

Y se mostrar en ese label lo mismo que antes se imprimi. Esto mismo se puede hacer indicando que se asigna a la propiedad Caption de esa etiqueta: Label2.Caption = "Hola" Label2.Caption = Label2.Caption & " El resultado sera el mismo. " & b

219

Nota: Siempre que quieras "sumar" cadenas, deberas usar el signo & en lugar de +, ya que el primero es evidente que "suma" cadenas (concatena), mientras que el segundo, aunque tambin "suma", debera usarse slo para "sumar" nmeros.

Averiguar la posicin de una cadena dentro de otra: Para esto usaremos la funcin Instr, esta funcin espera dos parmetros como mnimo, aunque permite cuatro: Instr( [posInicio,] Cadena1, Cadena2[, tipo_comparacin]) Las dos cadenas, son las que usar para devolver la posicin de la segunda cadena dentro de la primera. Si hacemos esto: Print Instr("Hola mundo", "mundo") nos mostrar un SEIS, ya que "mundo" est en la posicin sexta, al menos ah es dnde empieza. Si se especifica posInicio, empezar a comprobar a partir de esa posicin inclusive, por tanto: Print Instr("Hola mundo", "o") imprimir 2 Print Instr( 6, "Hola mundo", "o") imprimir 10, ya que busca una letra "o" a partir de la posicin 6. El ltimo parmetro: tipo_comparacin es para realizar comparaciones binarias o de texto, como ya hemos visto anteriormente, si este parmetro tiene la misma funcin que en StrComp. Es decir si no se especifica, la comparacin se hace segn el Option Compare y si se indica, se hace segn lo indicado. Si especificas este parmetro, debers indicar tambin la posicin de inicio, sino el VB te dir que los tipos de datos no coinciden. Un ejemplo: Print InStr("Hola mundo", "o") Print InStr(6, "Hola mundo", "o") Print InStr("Hola mundo", "O") Print InStr(1, "Hola mundo", "O", vbBinaryCompare) Print InStr(1, "Hola mundo", "O", vbTextCompare) Dependiendo de que tengamos Option Compare Text el tercer caso imprimir un cero (si no tenemos la comparacin textual) o un 2 si tenemos esa opcin de comparacin En los dems casos, siempre devolver el mismo resultado. Pero no te confundas, devuelven lo mismo, porque sabemos que debe devolverlo, ya que se especifica como segunda cadena una constante que representa a una "o" minscula y en la primera cadena tenemos dos de esa letra, otro gallo cantara si tanto la primera como la segunda cadena fuesen variables y no sabemos lo que va a contener. Cuando sea imprescindible saber la posicin de una cadena dentro de otra, teniendo en cuenta el que haya que distinguir o no entre las maysculas y minsculas, es recomendable que uses el tipo de comparacin que quieres realizar. Si no lo haces, al menos recuerda o comprueba que tipo de comparacin se realizarn por defecto en ese mdulo. Bien, creo que ya tienes a tu disposicin casi todas las funciones de manejo de cadenas que puedas necesitar.

220

Ahora toca que practiques un poco con ellas para que le vayas cogiendo el "tranquillo" y te las aprendas. Una cosa que quiero aclarar es que no pretendo hacer con este curso un "sustituto" de la ayuda o los manuales del Visual Basic, por eso te recomiendo siempre que las instrucciones con las que te encuentres en algunos de los ejemplos, las busques en la ayuda o los manuales, lo mismo que para saber "manejarte" en el entorno de Visual Basic. S que esto ya lo he dicho antes, o al menos debera haberlo dicho, pero lo hago de nuevo para que no esperes un "diccionario" de todas las instrucciones que tiene el Visual Basic. Lo que pretendo siempre es que te enteres de cmo se usan esas instrucciones e incluso cuando usar una en lugar de otra... en fin... si ves que alguna que otra vez se me va la "olla", me lo dices e intentar solucionar ese despiste. Ejercicios? Te podra poner muchos... pero slo voy a ponerte uno y con ese tendrs "pa rato" Escribe una funcin que devuelva la posicin de una cadena dentro de otra, pero comprobando esa posicin por el final. Es decir que Instr("Hola Mundo","o") devolvera 2, pero con nuestra funcin devolvera 10. Podramos llamarla RInstr y se usara as: RInstr("Hola mundo","o") Haz una segunda versin en la cual se le pase como primer parmetro la posicin por la que se empezar a comprobar, de esta forma: RInstr2(6,"Hola mundo","o") nos dar un valor 2 En las soluciones veremos cmo podemos indicar parmetros opcionales en nuestros procedimientos y crearemos una nica funcin que se use igual que Instr, es decir pasando como primer parmetro opcional la posicin de inicio de la bsqueda.

Y siguiendo la "sana" costumbre, te pido tu opinin sobre esta entrega. Nos vemos. Guillermo P.S. Aqu tienes las soluciones a esta entrega

Curso Bsico de Programacin en Visual Basic


Empezaremos viendo la parte fcil del ejercicio, es decir la que nos muestra la posicin cuando la funcin espera slo dos parmetros, el primero ser la cadena en la que hay que encontrar lo que haya en la segunda cadena. Private Function RInstr1(ByVal s1 As String, ByVal s2 As String) As Long Dim i As Long Dim sTmp As String

221

RInstr1 = 0 For i = Len(s1) To 1 Step -1 sTmp = Mid$(s1, i, Len(s2)) If sTmp = s2 Then RInstr1 = i Exit For End If Next End Function Este tiene poco que explicar. Se hace un bucle desde la ltima posicin de la primera cadena Len(s1) hasta el principio, ya que lo que necesitamos es encontrar la posicin de s2 empezando a "mirar" desde el final. A continuacin guardamos en sTmp un trozo de cadena que va desde la posicin actual y que contiene tantos caracteres como tenga la segunda cadena, si te fijas, el bucle se podra hacer tambin de esta forma: For i = Len(s1) - Len(s2) + 1 To 1 Step -1 Ya que no nos sirve comparar el ltimo caracter con la cadena s2 cuando esta ltima tiene ms de un caracter, por ejemplo. Esto es inapreciable en cadenas cortas, pero en otras ms largas, acortar el tiempo del bucle. Para el segundo caso, hay que saber que los procedimientos (sub y function) permiten parmetros opcionales, estos se indican con la palabra Optional delante de los parmetros que vayan a ser opcionales. La nica pega es que cuando un parmetro es opcional los que le siguen tambin tienen que ser opcionales. Entonces cmo indicar que el parmetro opcional sea el primero? Pues... ms o menos fcil... aunque con truco. Cuando un parmetro es opcional, se puede usar la funcin IsMissing para comprobar si se ha especificado o no ese parmetro. Esto es cierto siempre que el parmetro opcional sea de tipo Variant sin un valor por defecto. En el VB4 todos los parmetros opcinales deben ser Variant y sin valores por defecto, pero en VB5 (y posteriores) los parmetros opcionales pueden ser del tipo que queramos, adems de poder tener un valor por defecto, es decir, si no se especifica, se le da un valor "predeterminado". Ahora vamos a usar slo los parmetros sin valores predeterminados, pero ms adelante seguramente lo usaremos. Sabiendo que con IsMissing podemos averiguar si se ha especificado o no el parmetro en opcional, haremos lo siguiente: Si se especifican los tres parmetros, el primero ser la posicin por la que se empezar a comprobar. Si no se especifica el ltimo parmetro, engaaremos al Visual Basic, dicindole que los dos parmetros introducidos son la primera y la segunda cadena... Ya ves que slo es echarle un poco de "cabeza" al asunto... Vamos a ver la declaracin de esta funcin:

222

Private Function RInstr(ByVal v1 As Variant, ByVal v2 As Variant, Optional ByVal v3 As Variant) As Long Dim i As Long Dim sTmp As String Dim s1 As String Dim s2 As String Dim posIni As Long If IsMissing(v3) Then 'Si no se especifican los tres parmetros s1 = CStr(v1) s2 = CStr(v2) posIni = Len(s1) Else posIni = CLng(v1) s1 = CStr(v2) s2 = CStr(v3) End If 'Valor inicial de la bsqueda, si no se encuentra, es cero RInstr = 0 'Siempre se empieza a buscar por el final For i = posIni - Len(s2) + 1 To 1 Step -1 'Tomar el nmero de caracteres que tenga la segunda cadena sTmp = Mid$(s1, i, Len(s2)) 'Si son iguales... If sTmp = s2 Then 'esa es la posicin RInstr = i Exit For End If Next End Function Ahora quedara hacer una funcin que comtemple las mismas opciones que tiene el InStr normal, es decir que se pueda especificar un cuarto parmetro que nos indique si la comparacin se hace de una forma u otra. Que te parece esto como un ejercicio nuevo? Saba que contestara de forma afirmativa... je, je... 'la posicin por la que empezar 'la primera cadena (segundo parmetro) 'la segunda cadena (tercer parmetro) 'La primera cadena 'la segunda cadena 'el ltimo caracter de la cadena

223

Pues intntalo... y si quieres ver la solucin, selecciona el contenido de la caja negra esa que hay al final y vers...

A ver si ya terminamos en la prxima entrega el acceso aleatorio a los ficheros. Nos vemos. Guillermo

Para ver la solucin, selecciona todo el contenido del siguiente cuadro y vers... Nota: He quitado lo de la seleccin porque no siempre funciona... al menos ahora no se ve el texto al seleccionarlo...

' Private Function RInstr(ByVal v1 As Variant, ByVal v2 As Variant, _ Optional ByVal v3 As Variant, _ Optional ByVal v4 As Variant) As Long Dim i As Long Dim sTmp As String Dim s1 As String Dim s2 As String Dim posIni As Long If IsMissing(v3) Then 'Si no se especifican los tres parmetros s1 = CStr(v1) s2 = CStr(v2) posIni = Len(s1) Else posIni = CLng(v1) s1 = CStr(v2) s2 = CStr(v3) End If 'la posicin por la que empezar 'la primera cadena (segundo parmetro) 'la segunda cadena (tercer parmetro) 'La primera cadena 'la segunda cadena 'el ltimo caracter de la cadena

224

'Valor inicial de la bsqueda, si no se encuentra, es cero RInstr = 0 'Siempre se empieza a buscar por el final For i = posIni - Len(s2) + 1 To 1 Step -1 'Tomar el nmero de caracteres que tenga la segunda cadena sTmp = Mid$(s1, i, Len(s2)) 'Si se especifica el tipo de comparacin If Not IsMissing(v4) Then 'Se usa StrComp con ese parmetro If StrComp(sTmp, s2, v4) = 0 Then 'esa es la posicin RInstr = i Exit For End If Else 'Aqu tambin se podra usar 'If StrComp(sTmp, s2) = 0 Then 'Si son iguales... If sTmp = s2 Then 'esa es la posicin RInstr = i Exit For End If End If Next End Function

A diferencia del Visual Basic, no dar error si se especifica el cuarto parmetro y no se especifican los dems, por la sencilla razn de que si slo se especifican 3, el cuarto nunca llega a tener un valor... (elemental mi querido Guille). De lo que tendrs que tener "precaucin", al menos si quieres que funcione todo bien, es de indicar los valores que se esperan... ya que sino es as, no conseguirs tu propsito... o al menos no el deseado.

Curso Bsico de Programacin en Visual Basic


225

Ya es hora de seguir con los ficheros de acceso aleatorio, despus del alto en el camino para ver cmo se manejan las cadenas de caracteres en Visual Basic, an no hemos visto todas las funciones, pero si las ms comunes. No te preocupes... no voy a continuar en esta entrega con esas funciones... ya le llegarn el turno... Por fin! Habrs exclamado... y con razn... pero son cosas que debes saber... y como de lo que se trata es de saber ms y/o mejor, pues... nunca estn de ms... No recuerdo exactamente en que punto me qued en las explicaciones sobre el acceso aleatorio de la entrega diecisis, as que... si ves que me repito, pues... salud! y a seguir adelante... Una cosa que hay que tener superclarsimo en esto del acceso aleatorio, es que debemos usar tipos definidos para acceder a los datos del fichero... para que complicarnos con funciones conversoras de datos, si el Visual lo hace de forma automtica por nosotros? Por tanto, te aconsejo que "siempre" uses variables definidas, ya que no hay un motivo vlido para no usarlas. En los siguientes ejemplos, vamos a usar el tipo definido que usamos en la entrega diecisis, el del colega... En este primer caso, vamos a guardar en un registro determinado el contenido de tres cajas de textos, cada una de ellas para cada uno de los campos del tipo definido: Private Sub cmdGuardar_Click() Dim unColega As t_colega Dim nFic As Long Dim numColega As Long nFic = FreeFile Open "colegas.dat" For Random As nFic Len = Len(unColega) 'Sacar un valor aleatorio numColega = Rnd * 25 + 1 Label1(3) = "nmero de colega: " & CStr(numColega) With unColega .Nombre = Text1 .Edad = Val(Text2) .email = Text3 End With 'Guardar los datos en el disco Put #nFic, numColega, unColega

226

Close nFic End Sub Aqu vemos los pasos que normalmente se realizan con cualquier tipo de fichero... --Se asigna a una variable un nmero de fichero libre (ya sabes: el canal por el cual VB se comunicar con el disco) --Se abre el fichero en cuestin, recuerda que las extensiones que uses son a tu antojo, no hay necesidad de usar un tipo de extensin especfica, salvo que hagas un programa que "entienda" los datos contenidos en ese tipo de extensin y puedas abrir los ficheros con slo hacer doble click... pero esto es un tema para ms adelante... --Se asigna a la variable el dato a guardar y --Se guarda... Este ejemplo no sera prctico, ya que puede que salga el mismo nmero ms de una vez y se perderan los datos anteriores. Porque debes saber que, cuando se guarda informacin en un registro determinado, ste funciona de la misma forma que las variables... o casi, es decir: cuando se guarda un nuevo valor, el que hubiera antes "desaparece". Una cosa que debes saber, aunque me imagino que lo habrs comprobado al ejecutar el programa, y si no ha sido as, no te preocupes... si te sirve de consuelo, tarde unos mesesillos en "detectar" esto que te voy a explicar ahora... Puedes acceder a cualquier registro de un fichero aleatorio, incluso si antes no has guardado nada. De la misma forma, puedes guardar informacin en el registro 7 aunque antes no hayas guardado en ninguno de los 6 anteriores. El problema? Que si lees informacin de un registro en el que no has guardado informacin anteriormente... puedes encontrarte con "basura", y de hecho la encontrars... Por qu? Porque accedes a una parte del disco que, posiblemente tena guardada alguna otra informacin... aprovecho esto, para decirte que, cuando borras un fichero del disco, este fichero no se borra, al menos no se borra la informacin que contena. Cmo solucionar este problemilla? Hay varios mtodos, el que yo normalmente usaba, (ahora casi no trabajo con ficheros de acceso aleatorio), era guardar informacin "vaca" en unos cuantos registros y cuando esos estaban ocupados, guardaba otro puado y as. Algunas veces, si saba que el fichero iba a tener un nmero limitado de registros, los grababa todos con datos vacos, es decir cadenas con slo espacios y nmeros con valor cero. En otras ocasiones tena un campo del registro al que le asignaba un valor, si al leer el registro, tena ese valor "predeterminado", quera decir que ya contena informacin vlida, si no era as, pona los campos con valores "vacos" y as evitaba la basura. Si quieres comprobarlo... as de paso me sirve para que veas un ejemplo de cmo acceder a los datos del disco y mostrarlo en unas cajas de texto. Private Sub cmdLeer_Click() Dim unColega As t_colega Dim nFic As Long

227

Dim numColega As Long nFic = FreeFile Open "colegas.dat" For Random As nFic Len = Len(unColega) 'Sacar un valor aleatorio numColega = Rnd * 25 + 1 Label1(3) = "nmero de colega: " & CStr(numColega) 'leer ese registro Get #nFic, numColega, unColega With unColega Text1 = .Nombre Text2 = .Edad Text3 = .email End With Close nFic End Sub Si has probado el ejemplo de guardar, para poder conseguir datos con contenido "basura", debers probar algunas veces ms que las que hayas probado antes... por qu? porque estamos usando nmeros aleatorios y al no existir un "Randomize", la secuencia de nmeros aleatorios siempre es la misma cada vez que ejecutas el programa... lo recuerdas? Bien, aparte del asuntillo este de la "basura", no tiene ningn misterio esto del acceso aleatorio... pero an no hemos terminado, no queda mucho para que te toque "trabajar", pero todava tienes un respiro... si a que te explique ms cosas se puede llamar respiro... En el tercer ejemplo que vamos a ver, se van a mostrar todos los colegas que tenemos guardados en el fichero. Se supone que los datos los hemos guardado de forma ordenada, no como en los ejemplos anteriores, ya que no tiene ningn motivo guardar a los colegas aleatoriamente... no sea que cojan complejo de bola de bingo... Ya te he comentado que la longitud de todos los registros de un fichero aleatorio tienen que ser iguales... y ahora lo podrs comprobar... que algunas veces no hay que fiarse de todo lo que yo diga... El nmero de registros es el resultado de dividir la longitud del fichero por la longitud de cada registro... numRegistros = Lof(nFic) \ Len(unColega) Fjate que uso \ para dividir. Las divisiones realizadas con este signo dan como resultado nmeros enteros, mientras que el habitual / realiza una divisin de coma flotante... es decir que puede tener decimales. Como un registro no puede estar formado por "nosecuantos caracteres y pico", en este

228

caso es recomendable el uso de la divisin de nmeros enteros, entre otras cosas porque tambin es ms rpida... Por tanto este cdigo nos informara del nmero de registros y hara un bucle entre todos los registros que tiene el fichero. ' nFic = FreeFile Open "colegas.dat" For Random As nFic Len = Len(unColega) numRegistros = Lof(nFic) \ Len(unColega) For i = 1 To numRegistros Get #nFic, i, unColega 'Mostrar el contenido del registro '... Next Close nFic Podemos sustituir el Get #nFic, i, unColega por esto otro: Get #nFic, , unColega. Cuando no se indica el nmero de registro, tanto en Get como en Put, Visual Basic usa el registro actual. Cada vez que se accede a un registro determinado, el Basic lo "marca" como registro actual, de esta forma sabe cual debe ser el siguiente registro al que debe acceder si no se le indica ninguno. Si hacemos esto: Get #nFic, 15, unColega, al hacer esto otro: Put #nFic, , unColega, se estar guardando en el nmero 16. Es decir, el VB mantiene un "puntero" al siguiente registro. El bucle anterior podra haber quedado as: For i = 1 To numRegistros Get #nFic, , unColega 'Mostrar el contenido del registro '... Next De todas formas, siempre suelo especificar el nmero de registro al que quiero acceder, as me parece que estoy ms seguro de dnde se leer o escribir el registro. Uno de los problemas que tiene este tipo de ficheros es que estamos "atados" a la longitud del registro. Que ocurre si nos d el "punto" de quitar, aadir o cambiar la longitud de alguno de los campos? Pues que lo tenemos ms bien chungo... estaremos metidos en un pequeo lio... Una vez que hemos definido el tamao del registro, y tenemos datos en el fichero, cualquier cambio que hagamos, dar como resultado algo no esperado... Pero, todo tiene arreglo... aunque en este caso, nos lo tendremos que "currar" nosotros. Tendremos que fabricarnos alguna rutina que se encargue de esta conversin.

229

Y ese podra ser el ejercicio de esta entrega: Realizar una pequea utilidad que convierta un fichero de acceso aleatorio con registros de una longitud conocida, en otro con registros de otra longitud o con campos de longitud diferente al original. Y digo "longitud conocida", porque si no sabemos la longitud de cada registro, incluso de cada campo de ese registro, poco podremos hacer... Un detalle importante es que para acceder a los ficheros abiertos como "random", slo podemos hacerlo con variables de longitud fija, ya sean tipos definidos o cadenas. Se podra pensar que haciendo esto: Dim unaCadena As String 'Asignamos 83 espacios a esta cadena unaCadena = Space$(83) nFic = FreeFile Open "colegas.dat" For Random As nFic Len = Len(unaCadena) '... Get #nFic, 1, unaCadena Pues no! Ya que la variable unaCadena no tiene longitud fija y el Visual Basic necesita que lo sea. Otra cosa es que hagamos esto otro: 'Esta cadena siempre tendr este nmero de caracteres Dim unaCadena As String * 83 nFic = FreeFile Open "colegas.dat" For Random As nFic Len = Len(unaCadena) '... Get #nFic, 1, unaCadena Ahora si que funcionara bien la cosa, ya que el VB tiene la informacin que necesita... La verdad es que hecho de menos una instruccin que tena el Basic del MS-DOS y era que podas definir una serie de variables para acceder a los registros, incluso podas declarar una array... y cada uno de los elementos del array con la longitud de cada uno de los campos... Pero eso ya no est, as que... hay que usar los tipos definidos que al fin y al cabo es la mejor opcin para este tipo de ficheros. Para el ejercicio, se supone que tenemos un fichero con registros declarados de esta forma: Private Type t_Colega

230

Nombre As String * 30 Edad email End Type Y lo queremos convertir en registros que tengan esta otra: Private Type t_Colega Nombre As String * 30 Edad email URL End Type Por supuesto, queremos que los registros que haya sigan conservando los datos que tuviesen... sino, que clase de rutina conversora sera? Acurdate de lo que digo en muchas ocasiones, no pienses en complicarte la vida, siempre procura ir a lo simple, ya que en la mayora de las ocasiones ese es el camino correcto... Cuando veamos la siguiente entrega, o al menos cuando veamos los ficheros de acceso binario, (mi intencin es que sea en la prxima entrega, pero ya sabes...), crearemos otra utilidad de conversin ms flexible que esta, es decir, una rutina que convierta un fichero a un formato que pueda ser definido por el usuario en tiempo de ejecucin. Aunque no sea lo habitual, y normalmente una entrega termina cuando te pongo los ejercicios, vamos a ver un ejemplo completo para introducir y mostrar datos en un fichero de acceso aleatorio. Cmo? Que quieres hacerlo t? Pues vale, me parece estupendo... Desde luego, es que tengo unos alumnos que no me merezco... 8-) No te quejes... y no digas que t no has dicho nada... yo he odo voces que me decan: queremos hacerlo nosotros! Y eso es lo que hay... Para esta aplicacin vamos a usar tres cajas de texto en los que introduciremos los valores a guardar en cada campo, con sus correspondientes labels para indicarnos los nombres de esos campos. Existir otro label/textbox para indicarnos el nmero del registro actual, es decir en el que se almacenarn los datos o del que se leern esos datos. Un par de botones para Guardar y Leer... Te resulta familiar? Pues as es... algo parecido a lo que ya vimos en la entrega nmero once cuando se usaron arrays de tipos definidos... El aspecto del "programilla" sera algo as: As Integer As String * 50 As String * 128 As Integer As String * 50

231

No he aadido nada para mostrar todos los registros... pero ya tendremos tiempo de hacerlo. Bueno, pues hasta aqu hemos llegado... Las soluciones de la entrega 18 estn en este link.

Y a pesar de parecer un poco pesado, realmente me gustara recibir tu opinin sobre esta entrega. Nos vemos. Guillermo

Curso Bsico de Programacin en Visual Basic


Vamos a ver las soluciones a los ejercicios de la entrega dieciocho: El primero era crear una utilidad para convertir un fichero de un tipo a otro. Una solucin sera esta: Private Type t_Colega Nombre As String * 30 Edad As Integer email As String * 50 End Type Private Type t_Colega2 Nombre As String * 30 Edad As Integer

232

email As String * 50 URL As String * 128 End Type

Private Sub cmdConvertir_Click() Dim unColega As t_Colega, unColega2 As t_Colega2 Dim nFic As Long, nFic2 As Long Dim numColegas As Long Dim i As Long 'abrir el fichero original nFic = FreeFile Open "colegas.dat" For Random As nFic Len = Len(unColega) 'abrir el fichero de destino nFic2 = FreeFile Open "colegas2.dat" For Random As nFic2 Len = Len(unColega2) numColegas = LOF(nFic) \ Len(unColega) For i = 1 To numColegas 'leer el registro Get #nFic, i, unColega 'Asignar los nombres del nuevo tipo With unColega unColega2.Nombre = .Nombre unColega2.Edad = .Edad unColega2.email = .email unColega2.URL = "" End With 'Guardar el nuevo registro Put #nFic2, i, unColega2 Next Close nFic2 Close nFic

233

'Si quieres eliminar el fichero anterior y cambiarle el nombre 'hazlo despus de cerrar los ficheros End Sub Este es el listado completo del segundo ejercicio: '-----------------------------------------------------------------'Ejercicio de la entrega 18 ' 'Guillermo 'guille' Som, 1998 '-----------------------------------------------------------------Option Explicit 'Tipo para usar en el fichero Private Type t_Colega Nombre As String * 30 Edad As Integer email As String * 50 End Type 'Esta variable se usar para acceder a los datos Dim m_unColega As t_Colega 'Nmero de registros del fichero Dim m_numColegas As Long 'Nmero del colega actual, usado cuando se edita, etc. Dim m_elColega As Long 'Esta variable guardar el fichero a usar Dim m_sFicColegas As String 'Esta se usar como FLAG para saber si hemos cambiado 'el registro actual Dim m_Modificado As Boolean (26/Abr/98)

Private Sub cmdGuardar_Click() Dim nFic As Long 'Slo si el nmero del colega es el indicado en Text4

234

'de esta forma slo se guardar cuando se pulse en 'Nuevo o en Leer If m_elColega = Val(Text4) Then nFic = FreeFile Open m_sFicColegas For Random As nFic Len = Len(m_unColega) With m_unColega .Nombre = Text1 .Edad = Val(Text2) .email = Text3 End With 'Guardar los datos en el disco Put #nFic, m_elColega, m_unColega Close nFic 'Ajustar el nmero de colegas m_numColegas = CuantosColegas() m_Modificado = False 'Posicionar el cursor en el nmero de registro Text4.SetFocus End If End Sub

Private Sub cmdLeer_Click() Dim nFic As Long 'No se comprueba si se ha modificado el registro actual 'esto habra que tenerlo en cuenta... lo he dejado preparado 'con la variable m_Modificado 'Te dejo que hagas las comparaciones pertinentes... '... 'Slo leer si no se est aadiendo uno nuevo If m_elColega <= m_numColegas Then m_elColega = Val(Text4)

235

'Pero que no se lea un valor "no vlido" If m_elColega > 0 And m_elColega <= m_numColegas Then nFic = FreeFile Open m_sFicColegas For Random As nFic Len = Len(m_unColega) 'leer ese registro Get #nFic, m_elColega, m_unColega 'quitarle los espacios "extras", ya que al ser 'de longitud fija, los espacios en blanco tambin 'se mostrarn en la caja de texto 'Para comprobarlo, quita el Trim$ y vers lo que 'ocurre cuando el nombre tiene menos caracteres... With m_unColega Text1 = Trim$(.Nombre) Text2 = .Edad Text3 = Trim$(.email) End With Close nFic m_Modificado = False Text1.SetFocus Else 'si el nmero no es vlido... Text4.SetFocus m_elColega = 0 End If End If End Sub

Private Sub cmdNuevo_Click() 'Comprobar si se ha modificado? '... 'Aadir un nuevo colega,

236

'slo si no se est introduciendo uno nuevo If m_elColega <> m_numColegas + 1 Then m_elColega = m_numColegas + 1 Text4 = m_elColega 'Limpiar el contenido de las cajas de texto Text1 = "" Text2 = "" Text3 = "" 'Limpiar tambin la variable el registro actual, 'aunque realmente no es necesario... With m_unColega .Nombre = "" .Edad = 0 .email = "" End With m_Modificado = False 'Posicionar el cursor en el campo del nombre Text1.SetFocus End If End Sub

Private Sub Form_Load() 'asignamos el path del fichero de colegas: m_sFicColegas = App.Path & "\Colegas.dat" 'Esta asignacin fallar si el path es el directorio raiz 'por tanto se debera comprobar de esta forma: If Right$(App.Path, 1) = "\" Then m_sFicColegas = App.Path & "Colegas.dat" Else m_sFicColegas = App.Path & "\Colegas.dat" End If 'Tambin de esta otra forma... algo menos "clara" m_sFicColegas = App.Path & _ IIf(Right$(App.Path, 1) = "\", "", "\") & _

237

"Colegas.dat" 'Inicialmente leer el nmero de registros 'lo pongo en una funcin para usarlo cuando se necesite, 'sin tener que repetir el proceso, aunque corto, pero... m_numColegas = CuantosColegas() 'Borrar el contenido de los TextBox Text1 = "" Text2 = "" Text3 = "" Text4 = "" 'Para empezar no se ha modificado m_Modificado = False End Sub

Private Function CuantosColegas() As Long 'Esta funcin se encarga de informarnos del nmero de registros 'que tiene el fichero 'Usarlo slo cuando queremos saber esta informacin y 'no necesitamos mantener el fichero abierto 'si no existe el fichero, se producir un error On Local Error Resume Next CuantosColegas = FileLen(m_sFicColegas) \ Len(m_unColega) If Err Then CuantosColegas = 0 End If Label1(4) = "Nmero de colegas:" & CuantosColegas Err = 0 End Function

Private Sub Form_Unload(Cancel As Integer)

238

'Por si se qued o estaba el fichero abierto... Close Set Form1 = Nothing End Sub

Private Sub Text1_Change() 'Si en lugar de usar tres TextBox distintos se usara un array 'sera ms cmodo, ya que slo se pondr esta asignacin 'en un slo evento Change. ' m_Modificado = True End Sub Private Sub Text2_Change() m_Modificado = True End Sub Private Sub Text3_Change() m_Modificado = True End Sub

A ver si la prxima entrega no se hace de rogar demasiado, que ya estamos casi a punto de acabar con esto del acceso a los ficheros... Nos vemos. Guillermo

Curso Bsico de Programacin en Visual Basic


S que va a sonar a excusa barata y esas cosas, pero si te dijera que el "manuscrito" lo tengo terminado desde el 26 de mayo... Ahora ya no es por aquello del porttil, an no lo tengo, parece ser que no hay "almas" tan bondadosas por esos mundos de Internet... pero bueno... Estaba pensando yo que lo mismo con un programilla de esos a los que les hablas y escriben de forma automtica... la verdad es que he estado probando con el SDK que tiene Microsoft, pero ese no me termina de valer, salvo que las entregas las escriba en

239

yankinglish... pero no es plan... en fin, tendr que seguir buscando las teclas y despus de pulsar una tecla mirar para el papel... Bueno, menos rollo, a ver si soy capaz de terminarlo pronto, para ir a ponerme como un salmonete, que hoy es da de playa, adems de que es fiesta local y esas cosas pa que los catetillos podamos ir a darnos un remojn a la playa, con una buena torta de San Juan... Me acuerdo yo que antes... vale, vale!, no hace falta que grites..., lo dejo, pero que sepas que te pierdes lo que iba a decir...

Acceso binario a ficheros.


Para terminar con los tipos de acceso a ficheros, vamos a ver la forma ms potente y a la vez la ms complicada... o casi. Con el acceso binario podemos acceder a cualquier punto del fichero y, lo ms importante, leer o guardar la cantidad de caracteres que queramos. Antes de entrar en detalles, veamos cmo indicarle al VB que vamos a usar este tipo de acceso. Como siempre, esto se har al abrir el fichero: Open Nombre_Fichero For Binary As Numero_Fichero Cuando abrimos un fichero en modo binario, al igual que suceda en el modo aleatorio (random), se tiene acceso tanto de lectura como de escritura. Tambin se usan las instrucciones GET y PUT para leer o escribir la informacin, pero a diferencia del acceso aleatorio, no estamos obligados a usar una variable de longitud fija, (adems de longitud previamente especificada a la hora de abrir el fichero), si esto fuese as, no habra diferencia con el acceso aleatorio... as que esta entrega casi ni existira... Cmo leemos datos de un fichero de acceso binario? Ya te he comentado que se usa GET para leer datos, la cuestin est en cmo indicarle al Visual Basic la cantidad de caracteres a leer... Pues hagamos la pregunta: Cmo le indicamos al VB la cantidad de caracteres a leer? Vamos a verlo con un ejemplo: A$ = Space$(10) Get nFic, , A$ Esto leer 10 caracteres. Osea, se leern tantos caracteres como "capacidad" tenga la variable usada. Si slo quisiramos leer slo un caracter, esta variable tendra una longitud de un caracter... (algunas veces alucino con mi lgica tan contundente, en fin...) La ventaja es obvia: no es necesario estar atados a un nmero fijo de caracteres, simplemente asignndole una cantidad de caracteres a la variable usada, es suficiente. El problema puede surgir a la hora de determinar la posicin desde la que leeremos esos caracteres. Ves, nada es perfecto, y si no controlamos el tema, pues, tendremos algn que otro quebradero de cabeza. La posicin tendremos que indicarla nosotros mismos, aunque tambin podemos dejar que sea el propio Visual el que se encargue de este tema, todo depender de lo que queramos hacer.

240

Ya vimos en el acceso aleatorio que el clculo de la posicin de cada registro podamos dejarlo de forma automtica, es decir que sea el propio VB el que "decida" la posicin. Realmente el VB no decide nada, ya que es una caracterstica de GET y PUT, si no se le indica la posicin, usa la "predeterminada" y esa posicin se ajusta automticamente cada vez que se lee o escribe informacin, el clculo se hace tomando la ltima posicin y aadindole la longitud del dato. En el caso de los ficheros aleatorios esa posicin es "ficticia" (o relativa), ya que el VB convierte la posicin real dentro del fichero en nmero de registros... Pero ahora no estamos con el acceso aleatorio, sino con el binario y con este tipo de acceso, trabajamos con posiciones "reales", es decir que si hacemos esto: Get nFic, 3, A$ Leeremos caracteres desde la posicin tres del fichero, el nmero de caracteres ledos estar indicado por la longitud de la variable A$ Como ya coment antes, la ventaja es que no estamos obligados a leer un nmero determinado de caracteres y el inconveniente es que hay que saber lo que estamos haciendo y se puede convertir en un inconveniente si no lo usamos de la forma adecuada. Pero, vamos a demostrar esto que acabo de decir. Crea un nuevo proyecto, asignale a la propiedad AutoRedraw del form el valor TRUE, de esta forma no habr problemas a la hora de imprimir, (y ver lo impreso), en el formulario. Esto del Autoredraw es til cuando nuestro form quede oculto por otra ventana, nunca perder lo que hayamos imprimido en l. Aade un commandbutton y escribe el siguiente cdigo: Private Sub Command1_Click() Dim nFic As Integer Dim sFic As String Dim sCadena As String 'sCadena tiene 20 caracteres sCadena = "Prueba de una cadena" sFic = "binarios_19.dat" nFic = FreeFile Open sFic For Binary As nFic Put nFic, , sCadena Close nFic 'leer los datos guardados nFic = FreeFile Open sFic For Binary As nFic Get nFic, , sCadena Print sCadena Close nFic End Sub

241

Ejecuta la aplicacin (pulsando F5), pulsa en el Command1, y vers que todo funciona bien. Ahora aade lo siguiente antes del Get nFic, , sCadena: sCadena = Space$(5) Y ejecuta de nuevo el programa. Como vers slo se han leido los cinco primeros caracteres de lo que se guard anteriormente. Es decir slo mostrar Prueb, porque la cadena usada para leer tiene esa cantidad de caracteres. Este es un detalle que debers recordar, as que apntatelo. La ventaja es que podemos guardar y leer distintos tipos de datos mezclados. Por ejemplo, si sabemos que tenemos un tipo definido y despus una cadena de caracteres, podemos mezclarlo. Pero es importante que a la hora de leer los datos, leamos la cantidad "justa" de caracteres, y en el orden correcto. Vamos a ver esto que acabo de decir. Borra el cdigo anterior o crea un nuevo proyecto y aade un command y el siguiente cdigo: 'Esto en la parte General del form Option Explicit Private Type t_colega Nombre As String * 30 Edad As Integer End Type

Private Sub Command1_Click() Dim nFic As Integer Dim sFic As String Dim sCadena As String Dim unColega As t_colega unColega.Nombre = "Guille" unColega.Edad = 40 'sCadena tiene 20 caracteres sCadena = "Prueba de una cadena" sFic = "binarios_19.dat" nFic = FreeFile Open sFic For Binary As nFic Put nFic, , unColega Put nFic, , sCadena Close nFic

242

'leer los datos guardados nFic = FreeFile Open sFic For Binary As nFic Get nFic, , unColega Get nFic, , sCadena 'mostramos los datos leidos Print unColega.Nombre, unColega.Edad Print sCadena Close nFic End Sub Si inviertes el orden de las variables a la hora de leer, pues causas un pequeo desastre, ya que no lees lo que esperas leer. Osea que no uses esto del acceso binario "al voleo", sino pensndolo bien. Entonces, cuando es conveniente usar el acceso binario? Siempre que queramos acceder a un fichero del que estimemos que puede que no sea del tipo ASCII, es decir un fichero que pueda contener cualquier clase de caracteres. Normalmente los ficheros ASCII, (o los usados habitualmente para acceso secuencial), terminan cuando se encuentra un cdigo EOF (caracter ASCII nmero 26) o cuando ya no hay ms caracteres en el fichero; sin embargo con el acceso binario slo se "acaban" cuando no quedan ms caracteres que leer del fichero. Por supuesto que si un fichero se ha guardado usando un tipo de acceso, puede abrirse usando otro tipo de acceso, aunque estos casos no son recomendables, salvo que sepamos lo que hacemos... Veamos un nuevo ejemplo. Ya sabes, borra el cdigo usado anteriormente o crea un nuevo proyecto. Private Sub Command1_Click() Dim nFic As Integer Dim sFic As String Dim sCadena As String sFic = "binarios_19.dat" nFic = FreeFile Open sFic For Binary As nFic Put nFic, , "Prueba de una cadena" Put nFic, , vbCrLf 'Se guarda una segunda cadena Put nFic, , "Segunda cadena" Put nFic, , vbCrLf Close nFic

243

'leer como secuencial nFic = FreeFile Open sFic For Input As nFic Do While Not EOF(nFic) Line Input #nFic, sCadena Print sCadena Loop Close nFic End Sub Como habrs comprobado, se ha ledo todo lo que haba en el fichero, incluso cosas que haba de pruebas anteriores. Ahora engaemos al VB y hagamos que piense que un fichero se ha acabado antes de que se acabe de forma "real". Sustituye el cdigo del Command1 por este otro: Private Sub Command1_Click() Dim nFic As Integer Dim sFic As String Dim sCadena As String Dim sEOF As String * 1 sEOF = Chr$(26) sFic = "binarios_19.dat" nFic = FreeFile Open sFic For Binary As nFic Put nFic, , "Prueba de una cadena" Put nFic, , vbCrLf 'Aadimos un cdigo de fin de fichero Put nFic, , sEOF 'Se guarda una segunda cadena Put nFic, , "Segunda cadena" Put nFic, , vbCrLf Close nFic 'leer como secuencial nFic = FreeFile Open sFic For Input As nFic

244

Do While Not EOF(nFic) Line Input #nFic, sCadena Print sCadena Loop Close nFic End Sub Ahora slo se ha mostrado lo guardado antes del cdigo almacenado en la variable sEOF. Pero el fichero contina teniendo lo que antes tena. Lo que ocurre es que cuando se abre un fichero secuencial y el VB se encuentra con el cdigo 26, piensa que se debe haber terminado el fichero en cuestin. Ahora vamos a leerlo como binario... Por supuesto, sabiendo lo que se ha guardado y cmo se ha guardado. Private Sub Command1_Click() Dim nFic As Integer Dim sFic As String Dim sCadena As String Dim sEOF As String * 1 Dim sCRLF As String * 2 sEOF = Chr$(26) sCRLF = vbCrLf 'sCadena tiene 20 caracteres sCadena = "Prueba de una cadena" sFic = "binarios_19.dat" nFic = FreeFile Open sFic For Binary As nFic Put nFic, , sCadena Put nFic, , sCRLF Put nFic, , sEOF 'Se guarda una cadena de 15 caracteres Put nFic, , "Segunda cadena" Put nFic, , sCRLF Close nFic 'leer los datos guardados nFic = FreeFile Open sFic For Binary As nFic

245

'Se leen slo 5 caracteres de los 20 guardados sCadena = Space$(5) Get nFic, , sCadena Print sCadena sCadena = Space$(15) 'Leemos los caracteres que quedaron pendientes, 'ya que sCadena slo ley 5 de los 20 caraceteres que tena Get nFic, , sCadena 'tambin leemos los caracteres "extras" que se guardaron Get nFic, , sCRLF Get nFic, , sEOF Print sCadena Get nFic, , sCadena Print sCadena Close nFic End Sub Bien, ahora ya hemos conseguido leer todo, pero fjate en el detalle de que hemos tendo que leer los cdigos "extras" que se guardaron, es decir el retorno de carro y el de fin de fichero. Si no lo hubieramos hecho... pruebalo y lo compruebas... Habrs observado una lnea de ms y un caracter extrao antes de "Segunda cade"... creo que no hace falta que te explique el porqu... verdad? Normalmente con el acceso binario podemos leer todos los caracteres que haya en un fichero. Se suele usar cuando no sabemos la estructura de ese fichero y tenemos alguna forma de "interpretar" lo que leemos, aunque esto ltimo no se aprende en ningn curso y no hay regla fija. En la mayora de las ocasiones que uso el acceso binario, es cuando quiero leer informacin de un fichero para buscar algo en concreto. Ese fichero puede ser un ejecutable, una DLL o cualquier otro tipo de fichero. Si de antemano se que es un fichero secuencial, seguramente no lo leera como binario, ya que el tiempo de acceso y lectura de un fichero secuencial es menor que uno abierto como binario. Osea que se lee antes uno abierto con For Input que con For Binary. Esta diferencia en el tiempo de acceso es apreciable sobre todo cuando se manejan muchos ficheros... Hablando de leer todo el contenido de un fichero, ya sabes que existe un funcin llamada Input$, a la que se le indica el nmero del fichero abierto y la cantidad de caracteres que queremos leer y nos lo devuelve para que podamos asignarlo a una variable de cadena. El fichero que hemos creado con el ltimo ejemplo no es realmente un fichero ASCII, ya que contiene caracteres binarios, es decir, caracteres que no estn en el rago ASCII del 32 al 255 (en algunos casos hasta el cdigo 127). Veamos la reaccin del VB ante un caso "no deseado", es decir leer lo que no debemos leer: 'Se supone que has probado los ejemplos anteriores y que existe el fichero indicado

246

Private Sub Command1_Click() Dim nFic As Integer Dim sFic As String Dim sCadena As String sFic = "binarios_19.dat" nFic = FreeFile ' Open sFic For Input As nFic sCadena = Input$(LOF(nFic), nFic) Close nFic Print sCadena End Sub Aqu lo que se pretenda era leer el fichero de una vez. Pero como se ha abierto el fichero como secuencial, el VB ha detectado que se la ledo un cdigo de fin de fichero, precisamente antes de que se acabe el fichero y nos avisa con un magnfico mensaje de error. Esto se soluciona, bien abriendo el fichero secuencial, pero usando EOF(nFic) para comprobar si hemos alcanzado el final del fichero o bien abriendo el fichero en modo binario y usando la funcin Input$. Private Sub Command1_Click() Dim nFic As Integer Dim sFic As String Dim sCadena As String sFic = "binarios_19.dat" nFic = FreeFile ' Open sFic For Binary As nFic sCadena = Input$(LOF(nFic), nFic) Close nFic Print sCadena End Sub Ahora puedes observar que se lee TODO lo que hay en el fichero, ya que al abrirlo en modo binario, no se tiene en cuenta ningn caracter especial y se lee hasta dnde se le indique.

247

Creo que es suficiente por hoy. Hay ms cosas, en cuanto al acceso a los ficheros, pero no vamos a alargar esta entrega, que puede ser que te empaches con tantas cosas y no es bueno.

Los ejercicios Recuerdas uno de los ejercicios de la entrega 18? Consista en cambiar el formato de un fichero de acceso aleatorio en otro con los campos en otro formato (longitud de los campos). Pues bien, con el acceso aleatorio slo era posible si conocamos de antemano el tamao del registro nuevo (y, por supuesto, la longitud de cada campo). Ahora con lo que sabes del acceso binario y unas cuantas miles de lneas de cdigo, podrs hacerlo para convertir cualquier fichero a un nuevo formato y as poder usarlo de forma genrica. Es decir, tenemos un fichero con una serie de campos y queremos cambiar la estructura de ese fichero y, por supuesto, traspasar la informacin existente al nuevo formato. El usuario de esta utilidad, tendr que saber la estructura del fichero de origen y tambin saber la nueva estructura a la que quiere convertir el susodicho fichero de datos. Para no complicar demasiado la cosa, vamos a usar slo 10 campos, pero se podran permitir ms... El aspecto del form en tiempo de diseo sera el siguiente: No te preocupes por los "campos" que faltan en el destino, enseguida te explico cmo hacer que aparezcan, al menos a la hora de ejecutar el programa. Esto te servir para otras veces en las que necesites crear controles en tiempo de ejecucin y posicionarlos adecuadamente, o casi... Veamos el aspecto del form y despus veremos el cdigo usado para "crear" esos controles:

248

El cdigo: Private Sub Form_Load() Dim i As Integer 'Crear los controles de destino '(empezamos por UNO porque el control CERO ya est creado) For i = 1 To 9 'Cargarlos en memoria Load lblDest(i) Load txtDestTam(i) 'Asignarles la posicin y hacerlos visible With txtDestTam(i) .Visible = True .Top = txtDestTam(i - 1).Top + .Height + 45 lblDest(i).Top = .Top - 15 lblDest(i).Visible = True lblDest(i) = "Campo " & i + 1 & ":" End With Next

249

'Borrar el contenido de los TextBoxes For i = 0 To 9 txtTam(i).Text = "" txtDestTam(i).Text = "" Next End Sub La cuestin est en que al pulsar en el botn de convertir el fichero, antes se hayan asignado los valores correspondientes. La informacin que hay que pasarle a la aplicacin de cada campo, es la siguiente: tamao de cada campo. Lo que hay que saber es el tamao de los nmeros al guardarlo en disco, para ello echale un vistazo a la ayuda del VB y te dir lo que ocupa cada tipo, aunque creo que en alguna de las primeras entragas ya lo vimos. Para muestra, un botn: Los Integers ocupan dos bytes, los Longs cuatro bytes, etc. La cuestin es que se asignen los valores de forma correcta, sino, la cosa puede no funcionar. Y con esto y un bizcocho... hasta otra entrega, que no creo que sea maana a las ocho... Este es el link a la solucin de esta entrega, recuerda no hacer trampas e intentarlo primero. (De todas formas, el link no te llevar a ningn sitio, ya que an no la he puesto... je, je...) Nota 25/Jun/98: Ya est operativo el link de la solucin al ejercicio.

Y continuando con la sana costumbre de recibir tus preciados, y la mayora de las veces aduladores, comentarios, cosa que agradezco y que dicho sea de paso, es la nica razn por la que sigo con el curso bsico... pues eso, aqui te dejo el link para que me escribas lo que te ha parecido esta entrega, pero, recuerda: no para hacer consultas, gracias. Nos vemos. Guillermo

Curso Bsico de Programacin en Visual Basic


Ahora s que est la solucin de la entrega 19, la verdad es que si no lo has conseguido, no debes preocuparte demasiado, no era tan "simple" como podra parecer, ya que se necesita de un poco de "tablas" y manejo en esto de la programacin, as que si ests dispuesto a ser sincero, por favor envame un mensaje diciendo si lo conseguiste o no,

250

esto me ayudar a saber si tengo que poner cosas ms sencillas o dedicarme a ensear otras cosillas, no s..., por ejemplo porqu cuando todo est oscuro no se ve nada... je. Para volver a la entrega 19, pulsa en este link. Este es el listado completo de la solucin que YO he encontrado al ejercicio, por supuesto no tiene porqu ser igual a la tuya, si quieres puedes mandarme una copia del resultado que has encontrado... no te garantizo nada, pero lo mismo hasta te comento sobre l... Venga, nimo! que lo difcil an no ha empezado... ;-) Esta es una foto del programa en ejecucin y el listado del mismo:

'-----------------------------------------------------------------'Ejercicio para la entrega 19 '(solucin) ' 'Guillermo 'guille' Som, 1998 '-----------------------------------------------------------------Option Explicit (24/Jun/98)

Private Sub Form_Load() Dim i As Integer

251

'Para probar uso el fichero de colegas.dat 'el tamao de cada campo era: 30, 2, 50 'Private Type t_Colega ' ' ' ' txtOrigen = "colegas.dat" 'Crear los controles de destino '(empezamos por UNO porque el control CERO ya est creado) For i = 1 To 9 'Cargarlos en memoria Load lblDest(i) Load txtDestTam(i) 'Asignarles la posicin y hacerlos visible With txtDestTam(i) .Visible = True .Top = txtDestTam(i - 1).Top + .Height + 45 lblDest(i).Top = .Top - 15 lblDest(i).Visible = True lblDest(i) = "Campo " & i + 1 & ":" 'Ajustar el TabIndex, '(se supone que ya estaban por orden) lblDest(i).TabIndex = txtDestTam(i - 1).TabIndex + 1 .TabIndex = lblDest(i).TabIndex + 1 End With Next 'Borrar el contenido de los TextBoxes For i = 0 To 9 txtTam(i).Text = "" txtDestTam(i).Text = "" Next End Sub Nombre As String * 30 Edad As Integer email As String * 50

'End Type

Private Sub cmdConvertir_Click()

252

'Variables para los nombres y nmeros de ficheros Dim nFic As Long, nFic2 As Long Dim sFic As String, sFic2 As String 'Estos arrays controlarn los tamaos de cada campo Dim aOrigen() As Long Dim aDestino() As Long 'Nmero de campos en cada fichero Dim nOrigen As Integer Dim nDestino As Integer 'Tamaos de los registros Dim tOrigen As Integer Dim tDestino As Integer 'Las cadenas que contendrn los datos Dim sOrigen As String Dim sDestino As String 'Nmero de registros del fichero de origen Dim numReg As Integer Dim tamFic As Long 'Para usos generales Dim i As Long, j As Long Dim posReg As Long Dim sTmp As String 'Antes de hacer nada, comprobamos que exista el fichero 'de origen sFic = Trim$(txtOrigen) If Len(Dir$(sFic)) = 0 Then MsgBox "ATENCIN! No existe el fichero de origen." txtOrigen.SetFocus Exit Sub End If 'Asignamos el nombre del fichero de destino sFic2 = Trim$(txtDestino) 'Se asignarn los tamaos de cada registro, se dejar 'de comprobar cuando el contenido del textbox sea cero. 'Si se usara un TextBox con el nmero de campos, la cosa 'sera ms fcil de controlar, pero...

253

' 'Empezamos por el origen For i = 0 To 9 If Val(txtTam(i)) = 0 Then 'ya no hay nada ms que comprobar Exit For Else nOrigen = nOrigen + 1 ReDim Preserve aOrigen(nOrigen) 'asignamos el tamao del campo nOrigen aOrigen(nOrigen) = Val(txtTam(i)) 'ajustamos el tamao total del registro tOrigen = tOrigen + aOrigen(nOrigen) End If Next 'Ahora comprobamos el destino For i = 0 To 9 If Val(txtDestTam(i)) = 0 Then 'ya no hay nada ms que comprobar Exit For Else nDestino = nDestino + 1 ReDim Preserve aDestino(nDestino) 'asignamos el tamao del campo nDestino aDestino(nDestino) = Val(txtDestTam(i)) 'ajustamos el tamao total del registro tDestino = tDestino + aDestino(nDestino) End If Next ' 'Ya tenemos la informacin suficiente, ' 'Por si da error al acceder a los ficheros On Local Error GoTo ErrorConvertir 'Abrimos los ficheros en modo binario nFic = FreeFile Open sFic For Binary As nFic

254

'Averiguar el nmero de registros de este fichero tamFic = LOF(nFic) numReg = tamFic \ tOrigen 'Comprobar que el tamao especificado concuerda con el fichero 'Si el nmero de registros multiplicado por el tamao de cada 'registro es diferente al tamao del fichero... If numReg * tOrigen <> tamFic Then MsgBox "Los tamaos especificados en los campos de origen" & vbCrLf & _ "no concuerdan con el tamao del fichero.", vbCritical, "Convertir ficheros" Close txtTam(0).SetFocus Exit Sub End If 'Abrimos el fichero de destino nFic2 = FreeFile Open sFic2 For Binary As nFic2 ' 'Preparamos la cadena que contendr los datos de origen 'esta no cambiar de tamao sOrigen = Space$(tOrigen) 'Hacemos un bucle para todos los registros de origen For j = 1 To numReg Get nFic, , sOrigen 'La cadena de destino se formar con el tamao de 'los campos de origen ms el tamao de los nuevos campos, 'si el nmero de campos de destino es diferente, 'simplemente se rellenar la cadena con espacios sDestino = "" ' 'Esta variable contendr la posicin dentro del registro 'del campo que se est procesando posReg = 1 For i = 1 To nOrigen 'Tomamos el contenido del campo actual sTmp = Mid$(sOrigen, posReg, aOrigen(i)) 'Asignamos este campo y lo rellenamos de espacios sTmp = Left$(sTmp & Space$(aDestino(i)), aDestino(i))

255

sDestino = sDestino & sTmp 'ajustamos el tamao de la posicin dentro del registro 'de origen posReg = posReg + aOrigen(i) Next 'Ahora hay que rellenar la cadena de destino con espacios 'suficientes hasta completar el nmero de caracteres 'que se han especificado. ' 'El TRUCO est en aadirle a la cadena de destino la 'cantidad de caracteres totales y slo quedarnos 'con esa cantidad, de esta forma nos aseguramos que 'tendremos la cantidad que necesitamos tener... ' sDestino = Left$(sDestino & Space$(tDestino), tDestino) 'Lo guardamos Put nFic2, , sDestino Next 'Se acab de convertir, cerramos los ficheros Close 'Guardamos la informacin de los formatos usados: ' 'Uso un formato standard INI para que se pueda leer de forma 'fcil, incluso usando el ejemplo de la entrega 20 ' nFic = FreeFile Open "Convertir.ini" For Output As nFic 'Datos de origen: Print #nFic, "[Datos de Origen]" Print #nFic, "Fichero=" & sFic Print #nFic, "Nmero de campos=" & nOrigen For i = 1 To nOrigen Print #nFic, "Tamao Campo" & CStr(i) & "=" & aOrigen(i) Next Print #nFic, "" 'Datos de destino: Print #nFic, "[Datos de Destino]" Print #nFic, "Fichero=" & sFic2

256

Print #nFic, "Nmero de campos=" & nDestino For i = 1 To nDestino Print #nFic, "Tamao Campo" & CStr(i) & "=" & aDestino(i) Next Close 'Avisamos de que todo acab bien MsgBox "Se ha convertido el fichero de forma satisfactoria," & vbCrLf & _ "La informacin de los datos convertidos est en: Convertir.ini", _ vbInformation, "Convertir ficheros." SalirConvertir: Close Exit Sub ErrorConvertir: MsgBox "Se ha producido el siguiente error:" & vbCrLf & _ Err.Number & " " & Err.Description, vbCritical, "Convertir ficheros" Resume SalirConvertir End Sub El contenido del fichero "Convertir.ini" de la prueba que he hecho, sera el siguiente: [Datos de Origen] Fichero=colegas.dat Nmero de campos=3 Tamao Campo1=30 Tamao Campo2=2 Tamao Campo3=50 [Datos de Destino] Fichero=colegas2.dat Nmero de campos=4 Tamao Campo1=40 Tamao Campo2=2 Tamao Campo3=50 Tamao Campo4=128

257

Nos vemos. Guillermo Si quieres los listados del programilla, para verlo ms cmodamente, los puedes bajar pulsando en este link.

Curso Bsico de Programacin en Visual Basic


Para compensar un poco el que hayan pasado casi dos meses entre la entrega 18 y la 19, te doy esta del tirn... no es por nada, es que ya la tena lista y realmente tena que ir junto con la diecinueve, lo que pasa es que no quise que la entrega anterior fuese demasiado larga y as te lo tomas con ms ganas... espero.

Ya hemos visto las distintas formas de acceder a los ficheros, ahora vamos a ver una instruccin que puede sernos til cuando decidamos "movernos" dentro del fichero. Me explico: cuando se trat el acceso secuencial, coment que la informacin haba que leerla de forma secuencial, es decir un dato despus de otro, bueno, pues esto es cierto slo a medias. No empieces a pegar saltos de alegra, porque tampoco es para tanto. El tema est en que si lees la informacin de forma "seguida", entonces si que es as, pero, si te entra hipo, puedes acceder a cualquier parte del fichero. Cmo? Pues con la siguiente instruccin que te voy a presentar ahora... A ver, instruccin ven, que te voy a presentar... venga, no te de vergenza, estamos en confianza... (es que dice que no le gusta su nombre), aqu est... oras, ores, damas y caballeros, les presento a: SEEK Esta instruccin (que tambin es una funcin) se usa, en modo instruccin, para posicionarnos en cualquier parte del fichero abierto, tanto para leer como para escribir. Si se usa como funcin, nos devuelve la posicin actual, es decir en la que nos encontramos, del fichero. Veamos cmo usarla: Seek #numFic, posicin y tambin variable = Seek(#numFic) El valor devuelto es de tipo Long. Dependiendo del modo en el que est abierto el fichero habr que "interpretar" el valor de distinta forma: Para los ficheros de tipo secuencial y binario, nos da la posicin en bytes (o caracteres). Para los ficheros abiertos como aleatorios (random), nos da la posicin en nmero de registros. Cuando lo usamos en modo instruccin, lo que hace es posicionar el puntero dentro del fichero, de modo que lo siguiente que se lea o se escriba se har en la posicin indicada.

258

Ni que decir tiene que el valor de la posicin debe ser un valor legal. Es decir, no podemos posicionarnos en la posicin CERO ni en una posicin NEGATIVA, ya que no es "legal". El uso de esta funcin/instruccin es til cuando necesitemos avanzar o retroceder dentro del fichero. Y como el movimiento se demuestra andando, vamos a ver un ejemplo. Vamos a crear una pequea utilidad que leer datos de un fichero, buscando claves especiales. Imaginate que quieres acceder a un fichero al estilo de los ficheros INI, en ese tipo de ficheros existen una serie de secciones que estn "enmarcadas" entre corchetes y a continuacin vienen una serie de datos (claves) con una especie de asignaciones que representan los valores de esas claves. Veamos un ejemplo de un fichero de este tipo: [elGuille] nombre=guille email=mensaje@elguille.info La seccin se llama "elGuille" y los dos campos son "nombre" y "email" Realmente para acceder a este tipo de ficheros no necesitaramos usar Seek, ya que podemos acceder secuencialmente, pero vamos a ver cmo podemos "posicionarnos" en una seccin en concreto, despus de haber ledo el contenido y haber tomado "buena nota" de las posiciones. La utilidad de ejemplo, nos va a mostrar todas las secciones disponibles y despus podremos acceder rpidamente a una posicin en concreto. Veamos el aspecto del formulario y el cdigo correspondiente:

'Esta fecha no est mal, es que ya lo tena "manuscrito" desde entonces... 'Ejemplos del curso bsico, ejemplo de Seek ' (26/May/98)

259

Option Explicit Private Type tSecciones Nombre As String Posicion As Long End Type Private aSecciones() As tSecciones Private nSecciones As Integer Private sFic As String Private nFic As Integer

Private Sub Form_Load() 'deshabilitar el botn de leer contenidos cmdLeerContenido.Enabled = False Text1 = "" List1.Clear 'Creamos el fichero de ejemplo sFic = "basico_20.ini" nFic = FreeFile Open sFic For Output As nFic Print #nFic, "[elProfe]" Print #nFic, "Nombre=Guillermo" Print #nFic, "email=mensaje@elguille.info" Print #nFic, "" Print #nFic, "[Alumnos]" Print #nFic, "Cantidad=2" Print #nFic, "Nombre_01=Pepito" Print #nFic, "email_01=pepito@servidor.com" Print #nFic, "Nombre_02=Juanita" Print #nFic, "email_02=juani@servidora.net" Print #nFic, "" Print #nFic, "[Fecha]" Print #nFic, "Fichero creado el da=26/May/1998" Print #nFic, "Fichero actualizado el da=" & Format$(Now, "dd/mmm/yyyy") Close

260

End Sub

Private Sub cmdLeerContenido_Click() 'Leemos el contenido de la seccin seleccionada en el list Dim sCadena As String Dim nItem As Long nItem = List1.ListIndex If nItem >= 0 Then 'borramos el contenido del Text1 Text1 = "" nFic = FreeFile Open sFic For Input As nFic 'posicionamos el fichero en el sitio que nos interesa Seek nFic, aSecciones(nItem + 1).Posicion 'ahora leemos el contenido del fichero hasta encontrar [ 'o hasta que se acabe el fichero Do While Not EOF(nFic) Line Input #nFic, sCadena If Left$(sCadena, 1) = "[" Then 'nada ms que leer Exit Do Else Text1 = Text1 & sCadena & vbCrLf End If Loop Close nFic End If End Sub

Private Sub cmdLeerSecciones_Click() Dim sCadena As String Dim Posicion As Long 'Borramos el contenido del ListBox

261

List1.Clear 'Leemos las secciones disponibles nFic = FreeFile Open sFic For Input As nFic Do While Not EOF(nFic) Line Input #nFic, sCadena 'guardamos la posicin actual, justo despus de leer, 'de esta forma nos indicar la posicin a partir de la 'cual leeremos el contenido... Posicion = Seek(nFic) 'Si es una seccin If Left$(sCadena, 1) = "[" Then 'incrementamos el nmero de secciones nSecciones = nSecciones + 1 'redimensionamos el array ReDim Preserve aSecciones(nSecciones) 'asignamos los valores al array With aSecciones(nSecciones) .Nombre = sCadena .Posicion = Posicion End With 'aadimos esta seccin a la lista List1.AddItem sCadena End If Loop Close nFic If nSecciones Then 'MsgBox "Se han hallado " & nSecciones & " secciones" 'seleccionamos el primer item del listbox List1.ListIndex = 0 'habilitamos el botn cmdLeerContenido.Enabled = True Else MsgBox "No hay secciones en el fichero: " & sFic End If End Sub

262

Private Sub List1_DblClick() 'Tambin podemos ver el contenido de una seccin 'haciendo doble-click cmdLeerContenido_Click End Sub Te explico un poco cmo funciona todo esto. Ya que de lo que se trata no es slo de ver cdigo sino de explicarlo, verdad? En primer lugar declaramos las variables que se van a usar. Una de estas variables es un tipo definido que contendr el nombre de la seccin y la posicin dentro del fichero. Creamos un array dinmico (es decir redimensionable) de este nuevo tipo de datos, en l guardaremos cada una de las secciones halladas en el fichero procesado. Tambin dimensionamos una variable que contendr el nmero de secciones halladas, aunque slo se usa mientras se lee la informacin del fichero, por tanto no es necesario que est declarada en la parte general de las declaraciones del formulario. Recuerda que las variables declaradas en la parte general de un formulario, estn disponibles en todo el formulario. En el Form_Load, que es el punto de entrada de nuestra utilidad, adems de asignar el nombre del fichero y guardar un contenido de ejemplo, borramos el contenido del Text1 y el List1, adems deshabilitamos el botn de leer el contenido de una seccin, para que no se use hasta que no haya datos. Al pulsar en el botn que lee las secciones, primero borramos lo que hubiese antes en el array de secciones y asignamos cero a la variable que contiene el nmero de secciones. Despus de abrir el fichero, en modo secuencial, vamos leyendo lnea por lnea, en cuanto nos encontramos con una lnea que empieza por corchete [, quiere decir que hemos encontrado una seccin, por tanto, incrementamos la variable que lleva la cuenta de las secciones halladas, redimensionamos el array usando Preserve para no perder la informacin antes almacenada, y asignamos la informacin del nombre y la posicin que hemos obtenido con Seek. Fjate que la lectura de la posicin se hace despus de haber ledo la seccin del fichero, esto es as, porque lo que necesitamos saber es la posicin que viene a continuacin de la seccin, ya que despus de la seccin es cuando vienen las claves. (En realidad, se asigna siempre despus de leer una lnea, pero a nosotros slo nos interesa su valor cuando hemos encontrado una seccin). Seguimos leyendo hasta encontrar el final del fichero. Por qu se ha usado el acceso secuencial? Por la sencilla razn de que este tipo de fichero suele ser de tipo ASCII, es decir que no contiene caracteres "raros" y que normalmente se editan en programas del tipo NotePad. De hecho el Windows tiene asociado al bloc de notas (Notepad) para abrir los ficheros que contengan la extensin INI. Adems de que al no saberse la longitud de los datos que contiene, pero que si sabemos que cada lnea termina con un retorno de carro (o cambio de lnea), es ms cmodo usar el Line Input # para leer toda la lnea; el modo binario no nos sera de utilidad, salvo que leysemos el fichero caracter por caracter, cosa que ralentizara el proceso.

263

Para acceder a una seccin en concreto, cosa que ocurre al pulsar en el botn cmdLeerContenido, simplemente abrimos el fichero, posicionamos el "puntero" en el sitio adecuado y leemos lo que haya en el fichero hasta que encontremos otra seccin, (que empezar por un corchete), o hasta que lleguemos al final del fichero. Tambin he puesto cdigo para que al hacer doble-click en un elemento del List1, se lea el contenido de la seccin correspondiente, para ello lo nico que se hace es llamar al evento Click del botn cmdLeerContenido.

Y hasta aqu ha llegado esta entrega, (que realmente formaba parte de la entrega 19), ahora vamos a ver un par de ejercicios para que te vayas soltando en esto de la programacin con el Visual Basic. El primer ejercicio realmente no usa Seek pero si algo parecido a la utilidad esta de leer el contenido de los ficheros del tipo INI, y consiste en crear un array con el contenido de todas las secciones y todas las claves y valores de cada seccin. De forma que el fichero slo se lea una vez y cuando se quiera mostrar el contenido de una seccin se use el contenido del array. La verdad es que no es nada fcil, pero tampoco pienses que es tan complicado como para no poder resolverlo, al menos deberas intentarlo y no coger el camino fcil de ver la solucin, entre otras cosas, porque lo que se pretende con estos ejercicios es que cojas "soltura" en la programacin y si adems de soltarte te quedas con las "buenas" costumbres, pues mejor. A que buenas costumbres me refiero? A usar Option Explicit en todos los mdulos, para de esta forma declarar las variables antes de usarlas y a "indentar" el cdigo para que te sea ms fcil seguirlo... Despus del sermn vamos a ver la pista que te doy: La pista es que puedes usar estos tipos definidos para crear el array de claves y su contenido, y el array para almacenar cada seccin y las claves de cada una de ellas. ' Private Type tContenidos Clave As String Contenido As String End Type Private Type tSecciones Nombre As String NumClaves As Integer Contenidos() As tContenidos End Type Private aSecciones() As tSecciones Como sabes cada clave tiene este formato: Clave=Contenido. Esto te lo digo para que la clave vaya por un lado y el contenido por otro, aunque sea algo ms complicado que almacenar simplemente la clave y el contenido, a la larga te ayudar a manipular mejor las cadenas de caracteres y tambin le darn mayor utilidad al cdigo que se cree con este ejercicio.

264

Como segundo ejercicio, haz lo mismo, pero en lugar de almacenar en el array cada clave y su contenido, usa Seek para "recordar" la posicin de cada una de las claves de cada seccin para despus poder acceder a esa parte del fichero para leer lo que nos interesa. Este segundo ejercicio es un poco ms complicadillo, ya que necesitar usar de forma correcta Seek, tanto en modo funcin como en modo instruccin. Este es el tipo de datos que tendrs que usar: ' Private Type tSecciones Nombre As String NumClaves As Integer Contenidos() As Long End Type Private aSecciones() As tSecciones Fjate que aqu slo guardamos en Contenidos la posicin de cada clave dentro del fichero. Suerte y no desesperes si no lo consigues, no me gustara perder a todos mis alumnos de golpe... creo que no lo soportara. Las soluciones de los dos ejercicios estn en este link.

Y ya slo queda que hagas tu comentario sobre esta entrega. Y si hay algo que necesites que te aclare, relacionado con lo que se ha visto en esta entrega, no te cortes y pregntame, pero slo relacionado con lo que estamos viendo, vale? Nos vemos. Guillermo

Curso Bsico de Programacin en Visual Basic


Estas son las soluciones de los ejercicios de la entrega veinte, si quieres ver el contenido de la susodicha entrega, pulsa en este link y te llevar a la entrega 20. El primero: '

265

'Ejemplos del curso bsico, ejemplo de Seek ' 'Solucin a los ejercicios de la entrega 20 ' Option Explicit Private Type tContenidos Clave As String Contenido As String End Type Private Type tSecciones Nombre As String NumClaves As Integer Contenidos() As tContenidos End Type Private aSecciones() As tSecciones Private nSecciones As Integer

(26/May/98)

Private sFic As String Private Sub cmdLeerContenido_Click() 'Leemos el contenido de la seccin seleccionada en el list Dim sCadena As String Dim nItem As Long Dim i As Integer nItem = List1.ListIndex If nItem >= 0 Then 'borramos el contenido del Text1 Text1 = "" With aSecciones(nItem + 1) For i = 1 To .NumClaves sCadena = .Contenidos(i).Clave & "=" & .Contenidos(i).Contenido Text1 = Text1 & sCadena & vbCrLf Next End With End If

266

End Sub Private Sub cmdLeerSecciones_Click() Dim nFic As Integer Dim sCadena As String Dim Posicion As Long Dim nClaves As Integer Dim i As Integer 'Borramos el contenido del ListBox List1.Clear 'Leemos las secciones disponibles nFic = FreeFile Open sFic For Input As nFic Do While Not EOF(nFic) Line Input #nFic, sCadena 'Si es una seccin If Left$(sCadena, 1) = "[" Then nClaves = 0 'incrementamos el nmero de secciones nSecciones = nSecciones + 1 'redimensionamos el array ReDim Preserve aSecciones(nSecciones) 'asignamos los valores al array aSecciones(nSecciones).Nombre = sCadena 'aadimos esta seccin a la lista List1.AddItem sCadena 'ahora leemos el contenido del fichero hasta encontrar [ Do While Not EOF(nFic) Line Input #nFic, sCadena If Left$(sCadena, 1) = "[" Then 'nada ms que leer 'restablecemos la posicin anterior Seek nFic, Posicion Exit Do Else Posicion = Seek(nFic) 'Posicin del signo igual

267

i = InStr(sCadena, "=") If i Then nClaves = nClaves + 1 ReDim Preserve aSecciones(nSecciones).Contenidos(nClaves) With aSecciones(nSecciones) .NumClaves = nClaves 'La clave estar antes del signo igual .Contenidos(nClaves).Clave = Trim$(Left$ (sCadena, i - 1)) 'el contenido de la clave despus del signo .Contenidos(nClaves).Contenido = Mid$ (sCadena, i + 1) End With End If End If Loop End If Loop Close nFic If nSecciones Then 'seleccionamos el primer item del listbox List1.ListIndex = 0 'habilitamos el botn cmdLeerContenido.Enabled = True Else MsgBox "No hay secciones en el fichero: " & sFic End If End Sub Private Sub Form_Load() Dim nFic As Integer 'deshabilitar el botn de leer contenidos cmdLeerContenido.Enabled = False Text1 = "" List1.Clear

268

'Creamos el fichero de ejemplo sFic = "basico_20.ini" nFic = FreeFile Open sFic For Output As nFic Print #nFic, "[elProfe]" Print #nFic, "Nombre=Guillermo" Print #nFic, "email=mensaje@elguille.info" Print #nFic, "" Print #nFic, "[Alumnos]" Print #nFic, "Cantidad=2" Print #nFic, "Nombre_01=Pepito" Print #nFic, "email_01=pepito@servidor.com" Print #nFic, "Nombre_02=Juanita" Print #nFic, "email_02=juani@servidora.net" Print #nFic, "" Print #nFic, "[Fecha]" Print #nFic, "Fichero creado el da=26/May/1998" Close cmdLeerSecciones_Click End Sub Private Sub List1_DblClick() cmdLeerContenido_Click End Sub

El segundo:
' 'Ejemplos del curso bsico, ejemplo de Seek ' 'Solucin a los ejercicios de la entrega 20 ' Option Explicit Private Type tSecciones Nombre As String (26/May/98)

269

NumClaves As Integer Contenidos() As Long End Type Private aSecciones() As tSecciones Private nSecciones As Integer Private sFic As String

Private Sub cmdLeerContenido_Click() 'Leemos el contenido de la seccin seleccionada en el list Dim nFic As Integer Dim sCadena As String Dim nItem As Long Dim i As Integer nItem = List1.ListIndex If nItem >= 0 Then nFic = FreeFile Open sFic For Input As nFic 'borramos el contenido del Text1 Text1 = "" With aSecciones(nItem + 1) For i = 1 To .NumClaves 'Nos posicionamos en el sitio que nos interesa Seek nFic, aSecciones(nItem + 1).Contenidos(i) Line Input #nFic, sCadena Text1 = Text1 & sCadena & vbCrLf Next End With Close nFic End If End Sub Private Sub cmdLeerSecciones_Click() Dim nFic As Integer Dim sCadena As String Dim Posicion As Long

270

Dim nClaves As Integer Dim i As Integer 'Borramos el contenido del ListBox List1.Clear 'Leemos las secciones disponibles nFic = FreeFile Open sFic For Input As nFic Do While Not EOF(nFic) Line Input #nFic, sCadena 'Si es una seccin If Left$(sCadena, 1) = "[" Then nClaves = 0 'incrementamos el nmero de secciones nSecciones = nSecciones + 1 'redimensionamos el array ReDim Preserve aSecciones(nSecciones) 'asignamos los valores al array aSecciones(nSecciones).Nombre = sCadena 'aadimos esta seccin a la lista List1.AddItem sCadena 'ahora leemos el contenido del fichero hasta encontrar [ Do While Not EOF(nFic) 'En las claves nos interesa saber la posicin 'antes de empezar a leerlas Posicion = Seek(nFic) Line Input #nFic, sCadena If Left$(sCadena, 1) = "[" Then 'nada ms que leer 'restablecemos la posicin anterior Seek nFic, Posicion Exit Do Else i = InStr(sCadena, "=") 'Si es una clave tendr el signo igual If i Then nClaves = nClaves + 1 ReDim Preserve aSecciones(nSecciones).Contenidos(nClaves)

271

With aSecciones(nSecciones) .NumClaves = nClaves .Contenidos(nClaves) = Posicion End With End If End If Loop End If Loop Close nFic If nSecciones Then 'seleccionamos el primer item del listbox List1.ListIndex = 0 'habilitamos el botn cmdLeerContenido.Enabled = True Else MsgBox "No hay secciones en el fichero: " & sFic End If End Sub Private Sub Form_Load() Dim nFic As Integer 'deshabilitar el botn de leer contenidos cmdLeerContenido.Enabled = False Text1 = "" List1.Clear 'Creamos el fichero de ejemplo sFic = "basico_20.ini" nFic = FreeFile Open sFic For Output As nFic Print #nFic, "[elProfe]" Print #nFic, "Nombre=Guillermo" Print #nFic, "email=mensaje@elguille.info" Print #nFic, "" Print #nFic, "[Alumnos]"

272

Print #nFic, "Cantidad=2" Print #nFic, "Nombre_01=Pepito" Print #nFic, "email_01=pepito@servidor.com" Print #nFic, "Nombre_02=Juanita" Print #nFic, "email_02=juani@servidora.net" Print #nFic, "" Print #nFic, "[Fecha]" Print #nFic, "Fichero creado el da=26/May/1998" Close cmdLeerSecciones_Click End Sub Private Sub List1_DblClick() cmdLeerContenido_Click End Sub Espero que con los comentarios y si fuera necesario un repasillo a la entrega veinte, no tendrn demasiada complicacin para entender el listado.

Nos vemos. Guillermo

Curso Bsico de Programacin en Visual Basic


Con lo que hemos visto hasta ahora tendrs, o al menos deberas tener, una buena base sobre el lenguaje BASIC (o Visual Basic), pero saber un montn de instrucciones no es suficiente. Por suerte, no slo hemos visto un "diccionario" del VB. Si has empezado casi de cero, seguramente no te habr costado adaptarte a la programacin en Windows, pero si por el contrario arrastras "conocimientos" de cualquier otro lenguaje que funcionaba bajo MS-DOS, puede que esa adaptacin te cueste ms, sobre todo si el lenguaje con el que trabajabas era el BASIC. Un problema de los que descendemos, por aquello de la herencia, del MS-DOS, es que "pretendemos" aprovechar lo que ya tenamos hecho y queremos adaptar al Visual Basic nuestros programas: UN ERROR! S, como lo oyes, intentar adaptar todo un programa DOS a Windows es una tarea muy

273

dura y a la larga poco efectiva, te lo digo por propia experiencia; otra cosa es adaptar ciertas rutinas, esto es ms fcil, sobre todo si no interacta con el usuario. El decir todo esto es para que desistas en "ventanizar" una aplicacin BASIC-DOS, seguramente, salvo que la tuvieras bastante bien estructurada con procedimientos y funciones, te costar ms adaptarla que hacerla de nuevo. No voy a explicar cmo adaptar un listado MS-DOS al Windows, sera una prdida de tiempo, sobre todo si a ti no te interesa, lo que si vamos a "volver" a ver es cmo controlar una aplicacin de Windows, (sino volver a ver, al menos profundizar en el tema). Porque hay ocasiones en las que nosotros debemos tomar el control, por ejemplo cuando el usuario tiene que rellenar un campo y no debe pasar a otro hasta que lo haya hecho de forma correcta. En una ocasin anterior ya vimos que los programas realizados en Visual Basic, y por extensin todos los programas que tengan que trabajar en Windows, se basan en los eventos. Si entendemos bien para que sirven los eventos, seguramente nos ser ms fcil "controlar" el funcionamiento del programa. As que, en esta y en las prximas entregas, vamos a darle un repaso a los eventos ms usuales. Tambin veremos con detalle, unos cuantos controles, los ms habituales, de forma que su uso y aplicacin sean "casi naturales" y al final acabes usndolos como si los conocieras de toda la vida.

Un poco de definicin. Te recuerdo que un evento es una especie de aviso que manda el control de que algo ha ocurrido o est ocurriendo. Esto de los eventos es la "esencia" de la programacin Windows. No estamos atados a una programacin secuencial (o lineal), como se haca en MS-DOS. Con ese tipo de programacin, nosotros decidamos lo que iba a ocurrir a continuacin, y si no decidamos, al menos podamos prever lo que poda suceder. Pero la programacin en Windows es otra historia; el Windows nos "avisa" de que algo est ocurriendo, bueno, realmente Windows no nos avisa de nada, (a ver si piensas que te va a mandar un mensaje por mail), son los controles mediante sus eventos los que hacen sonar la campana para que sepamos que el usuario est haciendo algo, como mover el ratn, presionar una tecla, etc.

Para muestra... ...un botn y un label y un textbox y un... Empecemos por el Label que es ms simple. Las etiquetas (Label) se usan para mostrar informacin, normalmente este tipo de control no necesita intervencin por parte del usuario. Normalmente se suele usar de estas dos formas: Como etiqueta informativa que acompaa a otro control y que nos indica la informacin que ese control nos da o nos pide, segn sea el caso.

274

Por ejemplo, si queremos que el usuario introduzca un nombre, usaremos un textbox para que escriba en l, pero tambin pondremos una etiqueta indicndole que es lo que se espera que escriba. Como panel o lnea informativa, indicndole algn tipo de informacin al usuario. Es habitual que en la parte inferior de un form se aada una etiqueta que "informe" de lo que se debe hacer o lo que el programa est haciendo. Tambin se usan las etiquetas para informar de las opciones que ha seleccionado o del proceso que la aplicacin ha realizado o est a punto de realizar. Resumiendo, cada vez que tengas que informar al usuario, usa etiquetas para ello. Siempre que esa "informacin" no requiera de su intervencin. Las cajas de texto (TextBox) son los controles, por excelencia, para la introduccin de informacin por parte del usuario. Cada vez que el usuario deba escribir lo que nuestra aplicacin necesite, se usar un textbox. Los botones (CommandButton) son los que indicarn, normalmente, al programa que el usuario ha finalizado de introducir la informacin que se necesita y que debe procesarla o si el usuario cambia de opinin y no quiere hacer lo que se peda. Es, por tanto, habitual que se usen dos botones para conseguir esto, uno para "aceptar" y otro para "cancelar". Tambin es habitual que se use para pasar a otra pantalla de informacin. Los ListBox y ComboBox se suelen usar para mostrarle al usuario una "lista" de posibilidades de las que debe escoger una o varias, (para esto ltimo es ms habitual el listbox). Aunque el ComboBox se puede usar tambin para la introduccin de informacin, no es lo habitual, en la mayora de los casos es preferible usar el TextBox, aunque tambin veremos los casos en los que nos viene mejor usar el Combo. Los CheckBox se usan cuando el usuario necesite indicar si se usa o no una opcin determinada, esta opcin debe ser tan "autosuficiente" y slo se necesitar saber si la quiere usar o no. Por ejemplo, si necesitamos que el usuario indique si una vez procesado los datos queremos que se imprima o no. El uso de los OptionButtons es para las ocasiones en las que necesitemos indicar al usuario que escoja entre unas cuantas y elija slo una. Hay ocasiones en las que en lugar de un checkbox, se usan dos optionbuttons; para el mismo caso de imprimir o no, para que indique el sexo, etc. Por supuesto que hay ms controles, pero al menos estos son los que se usarn ms habitualmente, aunque para que la "relacin" sea ms completa, veremos tambin otros dos controles que se suelen usar para "contener" y agrupar a los dems controles: Los PictureBox y Frames, stos se usan para poner controles dentro, sobre todo el Frame. La primera recomendacin para hacer esto, adems de tenerlos "fsicamente" separados

275

del resto, tambin es ms fcil moverlos a otro sitio cuando estamos diseando la aplicacin o el "interface" de cara al usuario. En otras ocasiones necesitaremos estos contenedores para agrupar los OptionButtons, cuando veamos en profundidad los controles, lo entenders. Como ltimamente no dispongo de mucho tiempo y adems me gusta "chincharos" un poco, dejo aqu esta entrega, as que, permanece pendiente y mientras tanto repsate el manual y la ayuda, de esta forma te ser ms fcil adaptarte.

En esta ocasin no te voy a pedir ningn comentario sobre la entrega, ya que tampoco es mucho lo que ha dado de s, pero no quiero que pasen "meses" entre entrega y entrega, as que... aunque poco, algo es algo. De todas formas, en la prxima entrega veremos ya algunos de los eventos ms habituales. Este tipo de entregas son un poco "aburridas", ya que hay que empezar a manejar un poco los conceptos antes de pasar a los ejemplos, por tanto te pido un poco de consideracin para conmigo y aguantes hasta que venga lo realmente interesante: los programillas de ejemplo y esas cosas. Mientras tanto, prtate bien y no hagas estropicios. Nos vemos. Guillermo

Curso Bsico de Programacin en Visual Basic


Ya habrs notado a lo largo de todas estas entregas, que de planificacin, "nasti de plasti", osease nada de nada... Y es que las cosas van surgiendo y mezclndose y al final... hasta casi se entiende lo que digo... y eso que es difcil, 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 montn de entregas, al menos se las leen del tirn... porque en ms de una ocasin me han escrito, sin nimo de ser demasiados criticones, supongo, que si el que empezara el curso en abril del ao pasado, tuviese que esperar a que lo terminase... lo tena claro... y es cierto, pero confo que los que empezasteis de los primeros, ya hayis aprendido a leer el manual y la ayuda del Visual Basic, y hasta os hayis atrevido con algunos de los muchos libros que hay sobre este lenguaje... que si bien, al principio os podran parecer difciles de entender, al menos ahora podis entenderlos, aunque sea un poco... Como deca 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 tena, 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, tambin pueden estar en controles diseados por nosotros y

276

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 tendrs tiempo de pillar buenos cabreos...), pero para simplificar, vamos a pensar que estn puestos en un formulario. Esta aclaracin 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 continuacin tiene que ver con el hecho de que un control est en un formulario u otro contenedor, pero al menos podrs "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 cdigo que se estaba ejecutando y se pasa a procesar el cdigo 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 cdigo asociado, no pasa nada. Aclaremos esto un poco; si te has fijado en la ventana de cdigo, 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 aadir nuestro cdigo para hacer algo cuando dicho evento se produzca. Pero esto no quiere decir que tengamos que escribir cdigo en todos ellos, sino slo en los que nos interesen, pero el hecho de no escribir cdigo en un evento, no quiere decir que no se producir. Por ejemplo, si no escribes cdigo 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 cdigo y se produce un evento. Por ejemplo, si mientras se procesa el cdigo de un evento, vuelve a producirse ese mismo evento, se vuelve a procesar el cdigo desde el principio y una vez terminado este segundo evento, se contina por dnde 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 cmo funcionan los controles y sepamos cmo y cuando se producen algunos eventos; tambin 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 an un porttil... je, je. Vamos a ver un pequeo 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 podrs ver lo que se ha imprimido en el. Escribe el siguiente cdigo en el evento Click del form: Private Sub Form_Click() Dim i As Long

277

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, vers que se imprimen los nmeros del 1 al 10, van un poco lento, para que puedas hacer lo que te dir ahora, mientras se estn imprimiendo los nmeros, vuelve a hacer click en el form, hazlo ms o menos rpido, si tus reflejos no te funcionan como quisieras, cambia el valor 2000 del bucle j con otro mayor, as se irn imprimiendo los nmeros de forma ms lenta. Habrs notado que cuando an no se ha terminado de imprimir los primeros diez nmeros, (si has pulsado antes de que se terminen de imprimir, claro), se empiezan a imprimir en la siguiente lnea desde 1 y cuando dejes de hacer click, irn terminando los bucles... supongamos que has pulsado tres veces, el resultado podra ser este: 1 1 1 7 9 23 23 23 89 10 45678 456 4 5 6 7 8 9 10 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, despus 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 notars, no se han perdido los valores... y el Visual record por dnde 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, tambin se les llama variables automticas, por el hecho de que una vez que no se necesitan son automticamente borradas... cosa que no ocurre cuando las variables se declaran a nivel de mdulo o se declaran "estticas", ms adelante veremos ms sobre esto. Lo que este pequeo ejemplo demuestra es lo que te he explicado antes, que se puede "reentrar" en un evento (y por extensin a cualquier procedimiento); si esto mismo se hiciera por cdigo, no porque el usuario interviene con su ratn, tendramos lo que se llama un procedimiento "recursivo", es decir que se llama a s mismo... tendremos ocasin de ver algn ejemplo.

278

El evento ms dado a este tipo de "recursividad" es el evento Click. Este evento se produce cada vez que pulsamos con el ratn sobre un control, incluso sobre un formulario, como hemos visto en este ejemplo. Este evento (Click), est presente en la mayora de los controles, prcticamente en todos. La verdad es que poder "seguir" esto, imaginndose cmo lo hace el VB, es un poco difcil... vamos a ver otro caso, ms usual y que seguramente ser ms fcil de entender... Supongamos que tenemos un cdigo que procesa una serie de cosas y ese proceso puede llegar a ser largo. Si ese cdigo lo tenemos en el evento Click de un botn, con idea de que se procese cada vez que se pulsa en el botn, (cosa super-habitual), si no hacemos nada especial, mientras se est procesando el cdigo, el form completo se quedar "congelado", una vez que haya terminado el proceso, se continuar "aceptando" nuevos eventos, normalmente se quedarn pendientes de procesar y se ejecutarn a continuacin de terminar el proceso largo... Por eso en ocasiones es conveniente usar, como en el ejemplo anterior, la instruccin DoEvents. Con esta instruccin 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 cdigo anterior del Form_Click para ver esto que estoy diciendo. Sustityelo 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, vers 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 nmeros impresos... o ms si no has tenido paciencia suficiente... Una forma de solventar esa espera... es hacer que se muestre un mensaje pidindonos 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 despus de imprimir el mensaje Dos: usar Refresh para que se "pinte" el control, en este caso el formulario.

279

... Print "Un momento por favor..." Refresh For i = 1 To 10 ... En ambos casos tendramos el mensaje mostrado en la pantalla, vale, pero si vuelves a hacer click... se volver a procesar todo el cdigo, etc... Aunque en esta ocasin, hasta que no finalice, no contina el siguiente. Cmo podemos evitar la re-entrada en un evento cuando an 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 cdigo y de esta forma poder evitar que se repita si an no ha terminado. Como ya te he contado antes, las variables locales son automticas y se desechan una vez finalizado el procedimiento, al ser automticas no "recuerdan" el valor que tenan 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 estticas? Pues este tipo de variables son las que podemos usar para prevenir estos casos. Las variables estticas se declaran de igual forma que las variables normales, salvo que en lugar de usar Dim, se usa Static. Una vez declarada una variable esttica, deja de ser automtica 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 cdigo de ese evento. Por tanto cada vez que se "cuela" el cdigo en un evento (o procedimiento), podemos saber si ya hemos estado antes, sin terminarlo o no... para ello habra 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

280

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 esttica, el otro es el DoEvents, sin ste, el uso de la variable esttica no servira para nada, ya que al no permitir a Windows que notifique los eventos, estos se quedan guardados en espera a que terminen los que haba pendientes, y para que no se queden guardados, usamos el DoEvents. Prueba a pulsar varias veces en el formulario mientras se muestran los nmeros, vers que no se vuelven a mostrar. Te explico un poco cmo 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 numricas, cadena vaca en las cadenas y False en el caso de las booleanas. Como vale False, se cumple la condicin, por tanto se asigna el valor True a esa variable y se contina procesando el cdigo. 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 ratn, al procesarse los mensajes pendientes, Windows enva 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 condicin no se cumple y se sale del evento. Cuando se ha salido del evento, se contina 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 ocasin el cdigo que hay. Todo esto es posible gracias a que la variable bFlag es esttica y el valor almacenado se mantiene entre las distintas llamadas, si no asignramos el valor False al final, no podramos entrar ms en este evento, ya que nunca se cumplira la condicin, as que hay que tener cuidado con las variables estticas, 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, ms 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 algn cdigo 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 automticas 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...

281

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 pelcula que te he contado no era exactamente lo que estaba en el guin, pero ms o menos lo que te he explicado era lo que quera explicarte... Para terminar, te dir que en algunos controles, al pulsar Intro se produce tambin 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 ms cosas que hacer. A ver si la prxima 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 estn los eventos del ratn, en las del da 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 cmo va todo esto de los eventos... Lo dicho en otras ocasiones: prtate bien y no hagas estropicios y si quieres mandarme un mensajillo sobre el curso, usa el link este que te pongo. Nos vemos. Guillermo

Curso Bsico de Programacin en Visual Basic


Tengo muchas excusas que darte, para justificar este retraso, pero no voy a drtelas, ya que no har que esta entrega se publicara antes... La verdad es que esto de tanta "teora" me aburre un poco y no me motiva, pero se que es importante que sepas todo esto de los eventos y dems cosillas, as que voy a intentar seguir, ya que en cuestin de un par de entregas estaremos con otras cosas... no se que cosas, pero seguro que con otros temas... lo importante es que sigas aprendiendo... pero no slo de lo que publico en el curso bsico, sino que si le echas una visual a los manuales y a la ayuda... algo tendrs aprendido, espero que con todas las cosas que llevo puestas hasta la fecha te haya servido para poder entender lo que en esos manuales y ayuda se dice... y si no es as, no te preocupes, que algn da lo podrs entender... Bien, vamos a ver algunos de los eventos que se producen con el ratn. Como ya te coment en la entrega anterior, se puede saber los eventos que un control puede interceptar, al menos los que el Visual Basic nos permite, son los que se muestran en la ventana de cdigo, en la parte izquierda se selecciona el control o formulario y en la derecha estn los eventos que podemos "codificar". No todos los controles procesan los mismo eventos, pero en el caso de los eventos del ratn, casi la mayora los interceptan... aunque no todos los que quisiramos, pero al menos algunos...

282

En estas imgenes podemos ver las listas de "objetos" disponibles en el formulario, en los que podemos interceptar eventos y algunos de los eventos disponibles en el formulario.

En la lista desplegable de la izquierda estn los controles que estn contenidos en el formulario, as como la seccin "General" usada para las declaraciones de variables y procedimientos.

En la lista de la derecha, se muestran los diferentes eventos disponibles para el control seleccionado en la izquierda, en esta imagen vemos algunos de los eventos "interceptados" por el VB referente a los formularios normales.

Por ejemplo, podemos saber cuando se est moviendo el ratn por el control, (te recuerdo que los formularios tambin tienen estos eventos), si se ha presionado un botn y hasta que botn se ha presionado... Vamos a verlos con un poco ms de detalle:

Evento MouseDown. Este evento se produce cuando pulsamos un botn del ratn. Con los parmetros que tiene este evento podemos saber que botn se ha pulsado, la posicin dentro del control y si se est pulsando la tecla Shift (Maysculas), Control o Alt. Vamos a ver los parmetros y algunos de los valores disponibles.

283

Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single) Button Indica el botn que se est pulsando, puede tener los siguientes valores: 1, se est pulsando el botn izquierdo, 2, se est pulsando el derecho, 4, se est pulsando el botn central. Slo se controla un botn a la vez. Indica si se est pulsando algunas de las teclas de "cambio", Shift, Ctrl o Alt, el valor devuelto corresponde con: 1, se est pulsando la tecla Shift (maysculas) 2, se est pulsando Control 4, se est pulsando Alt Se permiten combinaciones de estos valores, es decir que si se pulsan varias de esas teclas, los valores sern: 3, se estn pulsando Shift y Ctrl, 5, se estn pulsando Shift y Alt, 6, se estn pulsando Ctrl y Alt, 7, se estn pulsando las tres teclas. Indica las coordenadas de la posicin del puntero del ratn dentro del control. Es importante saber que estos valores son slo dentro del control en cuestin, no referente al formulario o la pantalla. El evento MouseDown se suele usar, entre otras cosas, para mostrar mens emergentes (PopUpMenus), normalmente controlando que se haya pulsado el botn derecho, (cuando Button vale 2)

Shift

X, Y

Evento MuseUp. Este evento se produce cuando se suelta el botn, los parmetros son los mismos que para MouseDown, pero en este caso lo que se detecta es cuando se "suelta" el botn pulsado.

Evento Click. Este evento se produce cuando se hace "click" en un control... es una combinacin del MouseDown seguido de un evento MouseUp... debes tener en cuenta que si ests gestionando los eventos MouseDown, MouseUp y Click te encontrars con que se producen los tres eventos en ese orden, es decir, primero se presiona, despus se suelta y por ltimo se produce el Click.

Evento DblClick. Este como podrs imaginarte, se produce cuando se hace una doble pulsacin, doble click. Si ests controlando estos cuatro eventos, adems de hacer tus propias comprobaciones, deberas saber que normalmente se producen en este orden:

284

Primero el MouseDown, seguido de un MouseUp, a continuacin un Click seguido de un DblClick y por ltimo un MouseUp. Todo esto lo puedes comprobar con el siguiente cdigo: ' 'Pruebas para la entrega 23 del curso bsico ' Option Explicit Private Sub Form_Click() Debug.Print "Se ha pulsado sobre el formulario (Click)" End Sub Private Sub Form_DblClick() Debug.Print "Doble pulsacin en el formulario (DblClick)" End Sub Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single) Debug.Print "Se ha pulsado el botn del ratn (MouseDown)" End Sub Private Sub Form_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single) Debug.Print "Se ha soltado el botn del ratn (MouseUp)" End Sub (04/Oct/98)

Evento MouseMove. Este evento se produce cada vez que movemos el ratn por un control o por el formulario. Los parmetros de este procedimiento son los mismos que en los otros referentes al ratn: Private Sub xxx_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single) Con este evento podemos saber cuando estamos dentro del control, sabiendo esto, podemos cambiar su apariencia, etc. El problema es que no sabemos cuando no estamos en el control, aunque si interceptamos la entrada en otras partes del formulario, podremos saber que no estamos en el control en cuestin, ya que si estamos en otro control... quiere decir que no estamos en este... Elemental querido Watson!

285

Si quieres ver un ejemplo para hacer "garabatos" en la pantalla... algunos lo llaman dibujar, en el cdigo del evento MouseDown, selecciona la palabra MouseDown y pulsa F1, en el ejemplo que se muestra est el programa para hacer esos garabaticos...

Y esto es todo por hoy... poco, pero mejor poco que nada... la prxima entrega ser sobre los eventos del teclado... que podra haber puesto en esta, pero... Y despus veremos algunos otros ms... lo que no se es si ser en la prxima o en la siguiente... ya veremos. Y para seguir con la norma... si quieres dejar un mensaje referente al curso bsico, puedes hacerlo, para ello usa el siguiente link para que te resulte ms fcil y as controle que lo que me quieres decir es referente al curso y no para hacer una consulta, ya que algunos aprovechan la ocasin para preguntar... Este es el link para que me mandes tu comentario. Nos vemos. Guillermo

Curso Bsico de Programacin en Visual Basic


Son muchas las cosas que ocurren en una aplicacin mientras el usuario est trabajando, (o cualquiera sabe lo que estar haciendo). Ya vimos algunos eventos relacionados con el ratn, pero tambin habr muchos otros relacionados con el teclado, es decir, cada vez que el usuario pulse una tecla se producirn varios eventos, que la tecla se mantiene pulsada, que la tecla se ha soltado... en resumen que la tecla se ha pulsado. Tambin 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. Bsicamente son tres los elementos que la prctica totalidad de los controles y formularios reconocen: KeyDown, KeyUp y KeyPress, vamos a verlos por separado y con algo de detalle, adems de algn 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 informacin acepta este evento como parmetro: Private Sub Text1_KeyPress(KeyAscii As Integer)

286

El parmetro KeyAscii nos indicar el cdigo de la tecla pulsada, este cdigo nos lo da como valor numrico, no como una cadena, es decir que si pulsamos la tecla A mayscula, 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 funcin ASC, por tanto se puede hacer una comparacin de este tipo: If KeyAscii = Asc("A") Then 'se ha pulsado la A mayscula End If En este evento se pueden detectar un montn de teclas, todas las alfanumricas y otros caracteres, adems de la tecla INTRO (return), aunque tendrs problemas con la tecla TAB, ya que esta tecla tiene un significado especial y no es tan fcil de detectar... Normalmente se suele detectar la pulsacin 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 End If Con esta asignacin, 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 hubisemos pulsado la tecla TAB, esto se suele hacer ms a menudo de lo que parece, sobre todo para usuarios que estn acostumbrados a usar programas de MS-DOS, para conseguirlo, adems 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 haramos pulsando la tecla TAB: SendKeys "{TAB}" End If Es decir, usamos la instruccin SENDKEYS para que enve 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 aplicacin... y de nuestros gustos... 'Eliminamos el pitido

287

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 cdigos ASCII, sino cdigos de teclado... Veamos los parmetros: Private Sub Text1_KeyDown(KeyCode As Integer, Shift As Integer) KeyCode es el cdigo de la tecla, no el cdigo ASCII, aunque en la mayora 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 (maysculas), 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, adems de las normales, se pueden detectar las teclas de funcin (F1, F2, etc), las teclas de bloqueo de maysculas, bloque numrico, etc. Los valores del parmetro Shift son los mismos que en el evento del ratn, es decir: 1 para Shift, 2 para Control, 4 para Alt y la combinacin de estos valores, psate 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; segn que casos, puede ser interesante hacer las comprobaciones en uno u otro evento. Por ejemplo, podras hacer algn 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 genrica 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 debers asignar un valor TRUE a la propiedad KeyPreview del formulario en cuestin. Ahora cada pulsacin de teclas sern procesadas primero por los eventos Keyxxx del formulario y despus por esos eventos del control que tenga el foco. Aunque si un botn tiene asignada a TRUE la propiedad Default, no se procesarn esas pulsaciones en el formulario. Vamos a ver un ejemplo para detectar la pulsacin de la tecla ESC y si esta se produce, mostrar un cuadro de dilogo que preguntar si queremos terminar la aplicacin... no es algo comn hacer esto, pero en algunas ocasiones puede ser interesante poder terminar una aplicacin al pulsar ESC, dnde utilizar esta forma de "cerrar" un programa ser cuestin tuya. Esto tambin se consigue teniendo un botn con la propiedad Cancel = TRUE, si ese botn se encarga de cerrar el programa, ya no tendrs que hacer nada ms... pero vamos a suponer que no tenemos ese botn y queremos cerrarlo al pulsar ESC. Tambin veremos cmo pasar al siguiente control cuando pulsemos INTRO; aunque esto slo ser posible si tenemos asignada a TRUE la propiedad TabStop de cada uno de los controles que estn en ese formulario.

288

Para este ejemplo, vamos a crear un nuevo proyecto, automticamente se aade Form1. Ahora insertaremos varios controles, que realmente no harn nada, pero as veremos cmo funciona todo esto. Veamos aade 2 CommandButtons, 2 TextBoxes y un par de Labels. Te voy a explicar es cmo suelo "manejar" los controles cuando los inserto en un formulario, que tamaos suelo usar, que posicin, etc. Normalmente suelo usar unos tamaos "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 separacin de los puntos del grid, los suelo tener definidos a 30x30 puntos. Es decir que si muevo los controles o les cambio el tamao, 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 dilogo, selecciona la solapa General, vers que hay una serie de opciones para manipular el "grid" que se muestra durante el diseo de los formularios. Si no has cambiado nada, tendrs seleccionada las dos opciones que hay, en ingls dice lo siguiente: (es que ahora tengo instalado el VB en ingls, as que si la traduccin 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) Tambin hay dos cajas de texto para indicar la separacin de los puntos mostrados en el grid, estos son los valores que tendrs que cambiar.

Array de controles.
Aunque en las aplicaciones simples no hay que preocuparse por los recursos que usemos, no est de ms acostumbrarse a aprovecharlos, as nos ser ms fcil 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 ttulo a las cosas y poco ms. En los casos en que uso los labels para mostrar informacin, 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 tamaos que le asign) Inserta un label, se crear Label1, seleccionalo, asigna el tamao que quieres que tenga, copialo, (pulsando el botn dereho del ratn 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 estn en un array es, entre otras, estas: Controles diferentes Nombre Cada una tendr un nombre diferente: Label1, Label2, etc. Un Array de Controles Todas tienen el mismo nombre, pero se debe usar un ndice para indicar a cual nos referimos.

289

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 parmetro que ser el ndice dentro del array del control que lo ha producido.

La ventaja de usar arrays es que slo tienes que incluir cdigo en un slo evento, ya que todos los controles en ese array comparten el mismo "nombre". Pero an 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 parmetro Index valdr cero. Vamos a ver cmo se mostrara esos eventos en la ventana de cdigo: Private Sub Label1_MouseMove(Index As Integer, Button As Integer, Shift As Integer, X As Single, Y As Single) Los parmetros 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 parmetro es el que nos indicar el control que ha recibido el evento. Para poder hacer cosas diferentes segn el control que sea, se puede usar una serie de If...Then o Select Case...End Select, segn 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 algn tipo de relacin, la ventaja, como puedes ver es que comparten los mismos eventos, en algunos casos tendrs 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 aadiendo el siguiente cdigo en el evento GotFocus: Private Sub Text2_GotFocus() 'Seleccionar el texto que haya en el Text2 'Si usamos With, es ms cmodo codificar, ya que no hay

290

'que estar escribiendo el nombre del control. With Text2 'Posicin incial de la seleccin: 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 tuvisemos un array de Text1, haramos lo mismo, pero simplemente cambiando el nombre del control que ponemos despus del With: Private Sub Text1_GotFocus(Index As Integer) 'Seleccionar el texto que haya en el Text1(Index) 'Si usamos With, es ms cmodo codificar, ya que no hay 'que estar escribiendo el nombre del control. With Text1(Index) 'Posicin incial de la seleccin: 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 Fjate que el cdigo usado es el mismo que para Text2. Si tuvisemos 20 cajas de texto, tendramos que repetir este cdigo veinte veces, una vez para cada control, pero al tener un array slo habra que ponerlo una vez. Otra forma de no tener que repetir el cdigo un montn de veces, sera creando un procedimiento que hiciera ese trabajo. Veamos el cdigo para ese procedimiento: Antes de ver el cdigo, hay que saber que el procedimiento necesita saber el control en el que se debe seleccionar, por tanto necesitar recibir como parmetro un textbox... veamos el cdigo y espero que captes la forma de hacerlo: Private Sub SeleccionarTexto(unTextBox As TextBox) 'Seleccionar el texto que haya en el TextBox

291

'Si usamos With, es ms cmodo codificar, ya que no hay 'que estar escribiendo el nombre del control. With unTextBox 'Posicin incial de la seleccin: 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 cdigo es el mismo que en los ejemplos anteriores, lo nico que se cambia es el nombre del control que est despus del WITH. El tipo de datos que se recibe como parmetro es del tipo TextBox, ya que ese ese el tipo de control que vamos a manipular, pero si se quisiera hacer ms genrico y poder usarlo con cualquier control que tenga una propiedad Text, podemos cambiar el tipo de parmetro 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, haramos 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 parmetro '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

292

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 tena que contrtelo algn da, as que tampoco ha venido mal del todo... verdad? Para que un formulario procese la pulsacin 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. Aade el siguiente cdigo al cdigo 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 pulsacin TAB SendKeys "{TAB}" End If End Sub Nota: Lo de la pulsacin 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 lnea. El orden de tabulacin, 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 automtica cuando se modifica el valor de otro control. Al principio tiene el valor segn los controles aadidos, 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 despus 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 ttulo, 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 rpido y si pulsamos esa "tecla" el foco pasar al control siguiente. Vamos a verlo con un ejemplo. En el formulario habamos aadido dos labels: Label1(0) y Label1(1) Selecciona la primera y escribe lo siguiente en la propiedad Caption: &Nombre,

293

comprobars que la N est subrayada, pues esa es la tecla de acceso rpido, para poder acceder a una tecla de acceso rpido tendrs 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 rpido tendrs que pulsar Alt+E. Ahora vamos a poner en orden los valores de TabIndex: Supongamos que el formulario tiene este aspecto:

--Selecciona el botn Aceptar, pulsa F4 para mostrar la ventana de propiedades, busca TabIndex y escribe 0, pulsa Intro. --Selecciona el botn 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 vers que el foco va cambiando a Text2, Cancelar y Aceptar, para volver a Text, etc. Prueba pulsando Intro cuando ests en alguno de los TextBoxes y vers que se cambia el foco al siguiente control. Para terminar con las pruebas, pulsa Alt+N y vers que el control que recibe el foco es el Text1, despus 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 cuestin. En la siguiente entrega veremos algunos eventos ms 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 ms y algunos "esbozos" para otras cuantas... as que no te lo tomes con demasiada calma que voy a seguir dndote la lata... Hasta la prxima que ser, espero dentro de poco... porque como me retrase... no ser hasta entrado el mes de Noviembre, ya que me voy unos das a Gran Canaria... as que...

294

hazme un poco la pelota si quieres ms entregas antes de que me vaya el prximo da 21... je, je... Nos vemos. Guillermo

Curso Bsico de Programacin en Visual Basic

Si esto de los nmeros de las entregas fuese como los aos de un matrimonio, con esta cumpliramos las bodas de plata, pero esto no es ningn matrimonio ni nada que se le parezca... o casi, ya que es casi como un compromiso entre t, el lector, y yo, el profe, as que vamos a ver si seguimos llevndonos bien para no tener que divorciarnos ni nada por el estilo. La mejor forma de llevarnos bien es que cada uno haga lo que tiene que hacer, t estudiar y practicar y yo dedicarme a seguir cumpliendo entregas... que por otro lado son menos pesadas que cumplir aos... Hoy vamos a continuar con los eventos, esta ser la ltima de la serie, que ya est bien. En la prxima entrega haremos un pequeo repaso a cmo se deben declarar las variables y algunos otros consejos que espero que sigas a pies juntillas, o al menos los tenga casi siempre presentes... ya vers a que me refiero, pero ahora vamos a ver esos cuantos eventos que en algunos casos es conveniente saber utilizar.

Otros eventos.
Ya habrs comprobado que eventos hay para casi cualquier cosa, muchos de ellos no se suelen usar de forma habitual, y siquiera los habituales. La razn es porque no siempre es necesario interceptar todos los avisos que nos envan los controles. Segn el tipo de control y en ciertas de estancias se usan unos u otros pero an no he visto ninguna aplicacin que necesitara manipular todo, qu digo todo, ni siquiera la mitad de ellos. Creo que sera una locura. Hay que saber que algunos eventos son procesados por los controles o formularios cuando ese control o formulario est activo o recibe el foco, mientras esto no ocurra, el susodicho control estar en standby.

Evento GotFocus. Cuando un control se convierte en activo y recibe el foco, produce el evento GotFocus, es decir que tiene el foco de atencin del usuario; de igual forma, cuando deja de ser el control activo, hace sonar la alarma mediante el evento LostFocus.

295

El control en el que suele usarse estos eventos, de forma ms habitual, es el TextBox. Normalmente en el evento de GotFocus se suele seleccionar todo el texto que contenga, para hacerlo, como ya vimos con "profusin" en la entrega anterior, se suele usar un cdigo como este: Private Sub Text1_GotFocus() With Text1 .SelStart = 0 .SelLength = Len(.Text) End With End Sub Aunque ya deberas saberlo, te vuelvo a repetir lo que hacen esas lneas de cdigo: .SelStart = 0 .SelLength = Len(.Text) Le indica al control que la posicin de inicio del texto seleccionado empiece por la posicin cero, es decir la primera letra. SelLength es la longitud del texto seleccionado y el valor que se le asigna es la longitud total del texto que haya en el control.

Por tanto lo que se hace es seleccionar todo el contenido del TextBox, las propiedades SelStart y SelLength tambin se podran usar para saber cul es el texto que hay seleccionado y poderlo usar en los casos de bsqueda y sustitucin de palabras. Pero hay otra forma ms rpida de saberlo, mediante la propiedad SelText del TextBox, con esta propiedad nos indica que texto es el que est seleccionado en ese momento. Para almacenar en una variable el texto que actualmente est seleccionado, haremos esto: 'Guardar en sBuscar el texto seleccionado en Text1 sBuscar = Text1.SelText Evento LostFocus. En el evento LostFocus se suele comprobar si el contenido es el que se espera que haya. Realmente es un pequeo problema el tener que validar los datos en este evento, ya que cuando se producen, inmediatamente hay otro control que recibe el foco y si resulta que los datos contenidos en ese control no son los que esperbamos se intente volver a darle el foco a este control, con lo cual se producira un evento LostFocus en el control que recibi el foco cuando el primero lo perdi y si en ese segundo control tambin hay una comprobacin de datos y stos son errneos, tendremos tal cacao que el pobre Visual Basic se terminar quejndose. Aunque el que se habr quejado habr sido t, ya que me imagino que no te has enterado de lo que acabo de decir, pero no te voy a dar un ejemplo para que lo entiendas, en vez de eso, te voy a poner un ejercicio y as de camino practicas un poco. Crea un proyecto nuevo, en el Form inserta dos TextBox y CommandButton, el botn se usar para salir del programa, en cada uno de los TextBox se comprobar que el contenido no est vacio y no se permitir salir del TextBox en cuestin si el contenido no

296

es el que esperamos que sea, por ejemplo, podras hacer que el primero slo admita nmeros y el segundo slo letras. Aunque esto se podra hacer de otra forma, prefiero que lo hagas en el LostFocus, para que practiques. Pero como te digo, si lo prefieres, puedes validar cualquier otra cosa, por ejemplo que tenga algo escrito o cualquier otra cosa que se te ocurra... yo no suelo validar casi nada en esos eventos ya que normalmente suelen dar ms quebraderos de cabeza que ventajas. Pero de lo que se trata es que seas t el que decida que es lo mejor o peor para tus programas, esto es extensible a cualquier otra cosa que yo u otra persona pueda decirte: evalalo primero y despus decide si es lo que realmente te hace falta. Para que realmente funcionen esas comprobaciones, asignales una cadena vacia nada ms cargarse el formulario y aade tambin cdigo al botn de salir para que descargue el formulario, as podrs comprobar que hasta que no se cumplan las condiciones especificadas, no dejar de "pitarte" para que escribas lo que debes escribir. Por supuesto, estas comprobaciones se harn en los eventos LostFocus de cada control y si el contenido no cumple las condiciones que esperamos, habr que volver a darle el foco, usando el mtodo SetFocus, adems de un Beep para que sepamos que los datos contenidos no son vlidos. El cdigo a usar sera algo como esto: Private Sub Text1_LostFocus() '... 'Si el contenido del control no es vlido entonces Beep Text1.SetFocus End Sub Prubalo y vers cmo ahora entenders lo que digo, y si no lo entiendes al menos habrs visto cmo el Visual Basic se queja. Bueno, vale, te pongo el cdigo que no permitir que los TextBoxes estn vacios y si est al cambiar de control, se quedar en ese, pero como te he dicho, esto volver loco al VB. '-----------------------------------------------------------------'Pruebas para la entrega veinticinco del curso bsico Option Explicit Private Sub Form_Load() 'Borramos el contenido de los TextBox Text1 = "" Text2 = "" (18/Oct/98) '-----------------------------------------------------------------'Volvemos a darle el foco

297

'Hacemos que el Text1 sea el primero en recibir el foco Text1.TabIndex = 0 End Sub Private Sub cmdSalir_Click() Unload Me End Sub Private Sub Text1_LostFocus() 'No permitir que el TextBox est vacio If Len(Trim$(Text1)) = 0 Then Beep Text1.SetFocus End If End Sub Private Sub Text2_LostFocus() 'No permitir que el TextBox est vacio If Len(Trim$(Text2)) = 0 Then Beep Text2.SetFocus End If End Sub Ejecuta el programa, el foco lo tendr el primer cuadro de texto, sin escribir nada, pulsa la tecla TAB para cambiar al otro control, ver cmo el VB se vuelve turuta... Tendrs que pulsar Control+Break (Cttl+Inter) para pararlo... En la versin 6 del VB se incluye un nuevo evento: Validate que ser el que se use para "validar" el contenido de un TextBox, en lugar de hacerlo en el evento LostFocus. Sobre este evento ya veremos algo ms adelante (en unas cuantas celebraciones ms), aunque dentro de poco lo podrs ver en la seccin sobre el VB6 que tengo en mis pginas, las cuales seguramente se sumarn en su momento a este curso... cuando les toque el turno.

Evento Change. Otro evento relacionado con los controles que permiten introducir informacin es: Change. Este evento se disparar siempre que el contenido del control cambie, por tanto en un TextBox se producir si lo que hay en la propiedad Text cambia. Esto ya lo vimos en las primeras entregas, adems de cmo no usar este evento, para que no diese un error de desbordamiento de la pila.

298

Arrastrar y Soltar controles y otros "objetos".


Pero no creas que esto terminar aqu ya que te voy a explicar algo sobre cmo arrastrar y soltar, ya que sta es una de las peculiaridades del entorno Windows y es una de las cosillas que deberas de tener siempre en tus aplicaciones. Aunque el ejemplo y la explicacin que te voy a dar es para el Visual Basic 5, si quieres ver cmo se hace con la versin anterior te recomiendo que te leas los trucos de mis pginas. Tambin te voy a dar un ejemplo de cmo hacer que se puedan mover los controles de tamao y o posicin dentro del formulario, en tiempo de ejecucin, claro. No es perfecto, pero medio funciona, el problema que tiene es que hay que codificar en muchos eventos, pero para ello he creado unos procedimientos para hacer menos tedioso el tema... La "versin" que tena antes de esta "utilidad", no permita que se soltara un control encima de otro, slo permita que se soltasen en el formulario, osea, si en la nueva posicin haba otro control, no se dejaba en ese sitio... Pero como te digo, ahora va mucho mejor... despus de el evento OLEDragDrop veremos el cdigo y un poco de explicacin.

Evento OLEDragDrop. Veamos primero cmo hacer que nuestra aplicacin permita que se suelte un fichero y cuando se haga se muestre en un TextBox, por tanto no pruebes con ficheros que no sean de texto. Para poder hacer esto, hay que asignar las siguientes propiedades del Form y del TextBox que mostrar el texto: OLEDropMode = 1 ' Manual El TextBox debera ser Multiline para que se muestre bien el fichero soltado. Para asignar a un TextBox el primer fichero de los que se sueltan en un formulario o cualquier control que tenga la propiedad OLEDropMode igual a uno, vamos a usar un procedimiento genrico, se supone que ser para ficheros de texto, incluso documentos RTF si el control en cuestin es un RecichTextBox. Private Sub AsignarFichero(unTextBox As Control) 'Asignar al TextBox indicado el contenido del fichero soltado Dim sFic As String Dim nFic As Long Dim sText As String 'Asignar slo el primer fichero sFic = Data.Files(1) 'Abrirlo y leer el contenido nFic = FreeFile Open sFic For Input As nFic

299

sText = Input$(LOF(nFic), nFic) Close nFic 'Asignarlo al control indicado unTextBox = sText End Sub Para usar este procedimiento, lo llamaremos desde el evento OLEDragDrop del formulario y del control en cuestin: Private Sub Form_OLEDragDrop(Data As DataObject, Effect As Long, _ Single) End Sub Private Sub Text3_OLEDragDrop(Data As DataObject, Effect As Long, _ Single) End Sub Ahora ya puedes "soltar" un fichero de texto en el formulario o el TextBox y se mostrar el contenido en ese TextBox. Button As Integer, Shift As Integer, X As Single, Y As Button As Integer, Shift As Integer, X As Single, Y As

AsignarFichero Text3

AsignarFichero Text3

Evento DragDrop y propiedad Drag Ejemplo de cmo redimensionar y mover controles en tiempo de ejecucin. Ahora vamos a ver cmo mover controles por el formulario y soltarlos en la posicin deseada. Tambin vamos a ver cmo, mediante el uso de una llamada al API de Windows, podemos hacer que un control sea "redimensionable", la nica pega para hacer que sea redimensionable, o si lo prefieres, la nica condicin, es que el control sea una "ventana", no te confundas, los controles en Visual Basic son controles, pero el Windows trata a algunos de ellos como si fuesen ventanas, por ejemplo el control TextBox es para Windows una ventana, sin embargo el control Label no lo es. Normalmente se puede saber si un control es "ventanero" si tiene la propiedad hWnd, ya que los que no son realmente una ventana, no tienen esa propiedad. Veamos el cdigo y las declaraciones para hacer que todo esto pueda funcionar. Primero te explico que en cada control hay que hacer lo siguiente en los eventos MouseDown y DragDrop, salvo en el Form, como veremos despus, ya que en el Form slo se detectar la accin de soltar el control:

300

Private Sub NombreDelControl_DragDrop(Source As Control, X As Single, Y As Single) EndDragDrop NombreDelControl, Source, X, Y End Sub Private Sub NombreDelControl_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single) IniciarDrag NombreDelControl, Button, X, Y End Sub En los controles que no queramos permitir que se suelte, hay que "cancelar" la operacin de Drag & Drop, por ejemplo, si tenemos un botn y no queremos que se suelte encima, habra que hacer algo como esto: Private Sub cmdSalir_DragDrop(Source As Control, X As Single, Y As Single) 'Cancelar la accin de soltado CancelarDrag Source End Sub El procedimiento de calcular la nueva posicin se encarga de hacer las comprobaciones pertinente para posicionar adecuadamente el control soltado; si el control que se mueve, est contenido a su vez en otro control, no se permite que se mueva fuera de su contenedor. Esta forma de actuar puedes cambiarla si quieres, aunque deberas tener en cuenta que el contenedor debe ser el control de destino o el formulario en caso de que se suelte sobre el form, tambin podras hacer que al soltarse un control en otro, el que recibe el control fuese el contenedor... todo es cuestin de que hagas pruebas y lo adecues a tus gustos. Una cosa que debes tener en cuenta si quieres mover los TextBox, es que no te permitir seleccionar el texto usando el ratn, ya que al pulsarse con el ratn se llama al procedimiento que "avisa" que se est haciendo el Drag&Drop y al dejarlo "invisible", pierde el foco, aunque esto se soluciona en el procedimiento que calcula la nueva posicin, podras querer que no se pueda mover, ese es el caso del Text4 del ejemplo que pongo a continuacin. Ahora veamos el cdigo de este ejemplo, el cdigo lo puedes conseguir pulsando en este link: codigo25.zip (6.62 KB)

'---------------------------------------------'Prueba para redimensionar Pictures ' y mover controles ' (23/Sep/96)

301

'Revisado/mejorado: el 18/Oct/98 ' 'Guillermo 'guille' Som, 1996-98 '---------------------------------------------Option Explicit Dim frmName As String Dim DY As Single Dim DX As Single Dim NumColumnas As Integer Dim NumFilas As Integer Dim bIniciando As Boolean 'Declaraciones del API para 32 bits Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _ (ByVal hWnd As Long, ByVal nIndex As Long) As Long Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _ (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long Private Declare Function SetWindowPos Lib "user32" _ (ByVal hWnd As Long, ByVal hWndInsertAfter As Long, _ ByVal X As Long, ByVal Y As Long, ByVal cX As Long, ByVal cY As Long, _ ByVal wFlags As Long) As Long Const GWL_STYLE = (-16) Const WS_THICKFRAME = &H40000 ' Const SWP_DRAWFRAME = &H20 Const SWP_NOMOVE = &H2 Const SWP_NOSIZE = &H1 Const SWP_NOZORDER = &H4 Private Sub CmdSalir_Click() Unload Me End Sub

302

Private Sub cmdSalir_DragDrop(Source As Control, X As Single, Y As Single) 'Cancelar la accin de soltado CancelarDrag Source End Sub Private Sub Form_DragDrop(Source As Control, X As Single, Y As Single) 'Cuando se suelta en el form, no especificar el destino EndDragDrop Source, X, Y End Sub Private Sub Form_Load() bIniciando = True 'Asignamos el nombre del formulario, ya que lo necesitaremos 'para saber si se mueve encima del form o de otro control frmName = Me.Name 'Hacer estos controles redimensionables, 'usando el API CambiarEstilo PicColum(0) CambiarEstilo Text2 CambiarEstilo Picture1 CambiarEstilo Frame1 CambiarEstilo Picture3 CambiarEstilo Picture4 CambiarEstilo Text4 Text3 = "Left= " & Text3.Left & ", Top= " & Text3.Top 'Para crear filas/columnas en un Control NumFilas = 2 Label1(0) = "Cabecera de la colunma" Load Text1(1) With Text1(1) Set .Container = PicColum(0) .Visible = True

303

.Top = Text1(0).Top + Text1(0).Height End With Load Label2(1) With Label2(1) .Visible = True .Top = Label2(0).Top + Label2(0).Height .Caption = "Fila 2" End With NumColumnas = 1 bIniciando = False End Sub Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single) Text2.Text = "X= " & Str$(X) & ", Y= " & Str$(Y) End Sub Private Sub Frame1_DragDrop(Source As Control, X As Single, Y As Single) EndDragDrop Source, X, Y, Frame1 End Sub Private Sub Frame1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single) IniciarDrag Frame1, Button, X, Y End Sub Private Sub Label1_DragDrop(Index As Integer, Source As Control, X As Single, Y As Single) EndDragDrop Source, X, Y, Label1(Index) End Sub Private Sub Label2_DragDrop(Index As Integer, Source As Control, X As Single, Y As Single) EndDragDrop Source, X, Y, Label2(Index) End Sub Private Sub PicColum_DragDrop(Index As Integer, Source As Control, X As Single, Y As Single)

304

EndDragDrop Source, X, Y, PicColum(Index) End Sub Private Sub PicColum_Resize(Index As Integer) Dim k As Integer Dim i As Integer If bIniciando Then Exit Sub 'ajustar el ancho del Label y los texts Label1(Index).Width = PicColum(Index).Width For i = 0 To NumFilas - 1 k = i * NumColumnas + Index Text1(k).Width = PicColum(Index).Width Next PicColum(0).Left = Label2(0).Width For i = 0 To NumColumnas - 1 If i > 0 Then PicColum(i).Left = PicColum(i - 1).Left + PicColum(i 1).Width End If PicColum(i).Top = 0 Next End Sub Private Sub Picture1_DragDrop(Source As Control, X As Single, Y As Single) EndDragDrop Source, X, Y, Picture1 End Sub Private Sub Picture1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single) IniciarDrag Picture1, Button, X, Y End Sub Private Sub Picture2_DragDrop(Source As Control, X As Single, Y As Single) EndDragDrop Source, X, Y, Picture2 End Sub

305

Private Sub Picture2_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single) IniciarDrag Picture2, Button, X, Y End Sub Private Sub Picture3_DragDrop(Source As Control, X As Single, Y As Single) EndDragDrop Source, X, Y, Picture3 End Sub Private Sub Picture3_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single) IniciarDrag Picture3, Button, X, Y End Sub Private Sub Picture4_DragDrop(Source As Control, X As Single, Y As Single) EndDragDrop Source, X, Y, Picture4 End Sub Private Sub Picture4_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single) IniciarDrag Picture4, Button, X, Y End Sub Private Sub Text1_DragDrop(Index As Integer, Source As Control, X As Single, Y As Single) EndDragDrop Source, X, Y, Text1(Index) End Sub Private Sub Text2_DragDrop(Source As Control, X As Single, Y As Single) EndDragDrop Source, X, Y, Text2 End Sub Private Sub Text2_DragOver(Source As Control, X As Single, Y As Single, State As Integer) 'Si no se quiere que pase por encima otro control 'CancelarDrag Source End Sub

306

Private Sub Text2_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single) IniciarDrag Text2, Button, X, Y End Sub Private Sub Text3_DragDrop(Source As Control, X As Single, Y As Single) EndDragDrop Source, X, Y, Text3 End Sub Private Sub Text3_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single) IniciarDrag Text3, Button, X, Y End Sub Private Sub Text4_DragDrop(Source As Control, X As Single, Y As Single) EndDragDrop Source, X, Y, Text4 End Sub Private Sub Text4_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single) 'Si se quiere poder seleccionar con el ratn 'quitar la siguiente llamada, aunque entonces no se permitir 'mover el control 'IniciarDrag Text4, Button, X, Y End Sub Private Sub CambiarEstilo(queControl As Control) Dim Style As Long On Local Error Resume Next Style = GetWindowLong(queControl.hwnd, GWL_STYLE) If Err Then Err = 0 MsgBox "El control " & queControl.Name & " no permite que se redimensione", vbInformation Exit Sub End If

307

Style = Style Or WS_THICKFRAME Style = SetWindowLong(queControl.hwnd, GWL_STYLE, Style) Style = SetWindowPos(queControl.hwnd, _ Me.hwnd, 0, 0, 0, 0, SWP_NOZORDER Or _ SWP_NOSIZE Or SWP_NOMOVE Or SWP_DRAWFRAME) End Sub Private Sub CancelarDrag(Source As Control) Source.Visible = True Source.Drag vbCancel End Sub Private Sub EndDragDrop(Source As Control, X As Single, Y As Single, Optional Dest As Object) Dim posX As Long Dim posY As Long If Dest Is Nothing Then 'Si no se especifica el control de origen, 'por ejemplo cuando se suelta en el formulario posX = -30 posY = -30 'Si el nombre del contenedor del Source no es el nombre 'del destino, no moverlo. 'Esto ocurrir cuando se mueve un control contenido en otro 'y el destino no es el control que lo contiene If Source.Container.Name <> frmName Then CancelarDrag Source Exit Sub End If Else 'Si el control destino es el mismo que el contenedor del Source 'Esto ocurrir cuando el control que se mueve se suelta 'en el control en que est contenido If Dest.Name = Source.Container.Name Then posX = -60 posY = -60

308

Else 'Si el nombre del contenedor del Source no es el nombre 'del destino, no moverlo. otro 'Esto ocurrir cuando se mueve un control contenido en 'y el destino no es el control que lo contiene If Source.Container.Name <> frmName Then CancelarDrag Source Exit Sub End If With Dest 'El nombre del formulario se asignar 'a la variable frmName If .Container.Name = frmName Then 'Esto ocurrir cuando se suelte un control 'en otro que est contenido en el formulario, 'no contenido en otro control posX = .Left posY = .Top Else 'Esto ocurrir cuando se suelte en un control 'que est contenido en otro control posX = .Container.Left + .Left + 60 posY = .Container.Top + .Top + 60 End If End With End If End If 'Posicionar el control soltado With Source .Visible = True .Move posX + X - DX, posY + Y - DY .Drag vbEndDrag .ZOrder End With 'Si el Source es un textbox, darle el foco If TypeOf Source Is TextBox Then

309

Source.SetFocus End If 'Si se van a usar RichTextBox, hacer la comparacin correspondiente End Sub Private Sub IniciarDrag(Source As Control, Button As Integer, X As Single, Y As Single) If Button = vbLeftButton Then DX = X DY = Y Source.Drag vbBeginDrag 'Cambiar a no visible, ya que si no, 'el form no detectara que se ha soltado, 'si el puntero del ratn no sale del control. Source.Visible = False Source.Drag End If End Sub Hasta aqu ha llegado esta "entrega de plata", si quieres hacer un comentario, gurdatelo para otra ocasin... je, je. Parte de esta entrega la tena escrita desde el 15 de julio de 1998, en total un folio manuscrito por las dos caras y pasado al Word usando el ViaVoice de IBM que aunque no me entiende, (seguramente porque yo no "dicto" bien), algo es algo y en cuanto le ensee a entenderme seguramente las entregas llegarn con mayor rapidez. Aunque es algo "lentillo" y la verdad es que el cdigo no hay forma de hacrselo entender, pero seguir probando a ver si logro que algn da sepa de que le estoy hablando... al menos me rio un poco de lo que escribe cuando no me entiende... Hasta la prxima que ser... imagnatelo... Nos vemos. Guillermo Nerja, 18 de Octubre de 1998

Curso Bsico de Programacin en Visual Basic


310

La entrega de hoy, para quitarnos un poco la resaca de la "celebracin" de la anterior, se basar en consejos y observaciones sobre las declaraciones de variables y otras cosillas que debers tener muy en cuenta para no meter la pata hasta el cuello, ya que en ocasiones "nos equivocamos" por no permitir que el Visual Basic tenga en cuenta algunas cosillas de las que "intentamos" hacer, con buena intencin, pero que en ocasiones no son todo lo correctas que debieran. No soy el nico que te dar o que ha dado estos consejos, ya que todos los programadores que llevamos algn tiempo con el VB solemos hacerlo, entre otras cosas porque nos da algo de "rabia" que los programadores de otros lenguajes, (lase Pascal/Delphi y C/C++), se rian de que el Visual Basic es muy propenso a errores tipogrficos... realmente no lo dicen de esta forma, pero el caso es que se quejan de que este lenguaje no "obligue" a declarar las variables, lo malo que tienen "esos" programadores es que realmente no conocen el Visual Basic, al menos no lo habrn visto a fondo o no lo han tocado desde la versin 2... pero para eso estamos aqu los que si lo hemos "estudiado" y lo seguimos haciendo, para que los menos "expertos" aprendan lo ms rpido posible y sobre todo lo mejor posible... Como te comentaba muchos de los errores tipogrficos se solucionan usando una instruccin, que ya te he comentado en otras ocasiones a lo largo de este curso, interminable, de Visual Basic y no es otra que: Option Explicit. Cuando insertamos esta instruccin al principio de un mdulo, ya sea del cdigo de un formulario, mdulos BAS o de cualquier otro, el VB nos obliga a declarar las variables que usemos. Algunos pueden ver eso de no tener que declarar las variables como una ventaja del VB, pero a la larga tener que declarar las variables antes de usarlas, es algo que se agradece... cosa que comprobars en cuanto leas un poco ms de esta entrega. De todas formas, no tienes porqu estar incluyendo esta instruccin en cada mdulo, el Visual Basic puede hacerlo por t de forma automtica cada vez que aadas un nuevo mdulo o formulario en tu aplicacin. Este automatismo no es un valor por defecto en las versiones anteriores al VB6, (en esta versin ya lo es y se agradece, al menos para que los que empecis lo tengis por defecto), pero puedes hacer que lo sea, para ello tendrs que marcar la opcin que para ello tiene el IDE del VB en el men Herramientas/Opciones en la solapa Editor estar "Require variable declaration" (al menos as se llama en la versin inglesa del VB, que es la que uso ltimamente) Que ventajas tiene esto? Que si te equivocas al escribir el nombre de una variable, el VB te avisar de este eerror y as ser ms fcil solucionar problemas, te puedo asegurar que "muchos problemas". Vamos a comprobarlo, crea un nuevo proyecto, te aadir un nuevo formulario (Form1), muestra la ventana de cdigo y si tiene insertado Option Explicit, brralo. Muestra el form y aade un CommandButton, aade el siguiente cdigo al evento Click de este botn: Private Sub Command1_Click() ' Dim SumaActual As Long Dim i As Long

311

SumaActual = 0 For i = 1 To 5000 SumaActual = SumActual + 1 Next MsgBox "El contenido de SumaActual es " & SumaActual End Sub Pulsa F5, haz click en el Command1 y vers que no hace lo que se espera... Ya que si analizas el cdigo, el valor mostrado debera ser 5000, pero, no es as... Vamos a dejar que el VB descubra el error por nosotros; para ello vamos a ejecutar de nuevo el programa, pero esta vez aade Option Explicit al principio del mdulo, es decir en la parte de las declaraciones generales del formulario, vuelve a ejecutar de nuevo el programa, nada ocurre... pulsa en el botn Command1 y vers que si que haba un error tipogrfico. Seguramente dirs que tampoco es para tanto... total, si se escriben bien los nombres de las variables... pues s, pero cuando tengas proyectos con varios formularios y algunos mdulos de otro tipo... entonces ya me contars si no es mejor contar con "detector de errores tipogrficos". Seguramente te habrs fijado que al ejecutar el programa, (usando F5 o pulsando en el botn de la barra de herramientas), el Visual Basic no te ha avisado del fallo, slo lo ha hecho cuando has hecho click en el botn, esto est bien, pero no ayuda mucho cuando el proyecto es grande y tiene varios formularios y varios/muchos procedimientos, ya que hasta que no se ejecute el cdigo que hay en ellos no nos avisar del "fallo". Pero, por suerte, esto se puede evitar... y adems de dos formas distintas. La primera es ejecutando la aplicacin con Ctrl+F5, es decir pulsando la tecla control y al mismo tiempo F5, de esta forma se analiza todo el cdigo antes de iniciar el programa y si el Visual Basic encuentra algn error, te avisar. La otra forma es indicarle al VB que "siempre" analice el cdigo antes de ejecutarlo, de esta forma es como si siempre pulsramos Ctrl+F5 aunque slo se pulse F5. Para que esto sea as, muestra el cuadro de dilogo de Herramientas y en la solapa General busca la casilla "Compile on demand" (o como sea en la versin en espaol), si est marcada esta opcin, qutale la marca, esto en proyectos grandes hace que tarde un poquito ms al ejecutarse en el IDE, pero es porque se analiza todo el cdigo antes de empezar la ejecucin del programa, yo ya me he acostumbrado a que siempre se compile y lo tengo de esta form, ya que no hay nada que ms me moleste que se detenga el programa al entrar en un procedimiento por culpa de un error tipogrfico, la verdad es que si esta opcin hubiese estado en el Basic del MS-DOS nos hubiera evitado algn que otro quebradero de cabeza, por desgracia lleg tarde, con el Visual Basic para DOS, pero ms vale tarde que nunca... aunque ahora casi ni se utiliza... Para terminar con esto de las ventajas de usar Option Explicit, vamos a ver cmo saber si una variable est en uso o no. Bueno, no es que nos de esa informacin, pero algo parecido, la verdad es que echo de menos una caracterstica del QuickBasic del MS-DOS y era que al pulsar F1 en una variable, nos indicaba en que mdulos se estaba usando dicha variable, pero... aunque no existe esa forma de ver cmo est declarada una variable en el Visual Basic, podemos

312

usar otra forma, que es pulsando Shift+F2; te mostrar la declaracin de esa variable... si no te muestra la declaracin, es que no est declarada. A lo que iba, si tenemos declarada una variable, se respeta el "estado" de maysculas/minsculas de ese nombre de variable, por tanto si en el caso del ejemplo anterior, hubisemos escrito (todo en minsculas) sumaactual, el VB automticamente hubiese convertido dicho nombre a SumaActual o como lo hubisemos escrito al DIMensionarla; esta es una de las formas de saber si ya tenemos la variable declarada... aunque sea en otro procedimiento... el nico "fallo" es que el VB recuerda la ltima forma en que se dimension una variable... es decir, si en un procedimiento tenemos esta declaracin: Dim j As Integer y en otra parte de nuestro proyecto esta otra: Dim J As Integer, el VB convertir todas las variables "j" al estado de la ltima declaracin que hayamos usado... Pero al menos sabremos que la tenemos declarada y de esto vamos a ver ms cosas...

Declarar las variables del tipo adecuado Ya que estamos en esto de las declaraciones de las variables, voy a darte otro consejo, que si no estan evidente como el anterior, en algunas ocasiones puede hacer que tu aplicacin trabaje de forma ms eficiente... aunque no te voy a hacer ninguna demostracin, ya que la demostracin la tuviste en la entrega cuatro. La recomendacin es que declares las variables con el tipo adecuado, es decir, que especifiques el tipo de la variable cada vez que la declares, si no lo haces, el Visual Basic le asignar el tipo por defecto, que normalmente suele ser Variant y aunque ese tipo sirva para todo, no es el recomendable para muchas ocasiones, al menos en lo que a bucles se refiere, por poner un ejemplo. Tambin puedes especificar el tipo por defecto, esto yo lo haca siempre en mis programas de MS-DOS, aunque ahora en Visual Basic para Windows no lo suelo hacer, ya que siempre declaro las variable del tipo que quiero que tengan. Pero para que lo puedas hacer, aunque me imagino que ya sabrs, te recuerdo cmo hacerlo. Aade justo despus del Option Explicit la instruccin DEFxxx a-z, sustituye las xxx por el tipp de tu preferencia, por ejemplo: DefInt A-Z asignar el tipo Integer a todas las variables declaradas sin un tipo determinado; por otro lado: DefLng A-Z har que el tipo predeterminado sea el Long. Se pueden usar varios de estos DEFxxx usando distintos rangos de letras, ya que el Defxxx funciona de esta forma: Deftipo rango letras [, rango2 [, rango3, etc]] dde tipo es Int para Integer, Lng para Long, Sng para Single, etc. (busca esta palabra en la ayuda para ms informacin) Los rangos son letras que le indicarn al VB que las variables que empiecen por esas letras sean del tipo indicado, por ejemplo: DefInt a-c, i, j DefLng L-N, R Declarar las variables, siempre que no se especifique el tipo, que empiecen por a, b, c, i y j como variables Integer y las que empiecen por L, m, n y r como Long.

313

Por ejemplo con estas declaraciones: Dim Ahora, Nombre As String, Longitud, Resultado As Currency Ahora ser Integer, Longitud ser Long y las otras dos, del tipo indicado: Nombre es String y Resultado del tipo Currency. Pero ya te digo que prefiero declarar las variables del tipo adecuado, es decir especificando el tipo que ser: Dim Ahora As Integer, Nombre As String, Longitud As Long, Resultado As Currency Yo as lo tengo ms claro, pero puedes hacerlo como prefieras. Lo que si debes hacer es usar el tipo que realmente necesites, por ejemplo si vas a hacer bucles que no supere el valor 32767, puedes usar una variable de tipo Integer, si quieres guardar en sDatos una cadena, declarala como tipo String, etc. Ya que si siempre als declaras como el valor por defecto y se te olvida aadir el DEFxxx correspondiente, tendrs que las variables ser del tipo Variant, que entre otras cosas es ms lenta y ocupa ms memoria. Adems de que en un futuro te ser ms fcil saber el tipo de datos que esas variables contendrn... cosa que agradecers cuando veas de nuevo el cdigo unos meses o aos despus... Y para que te sea ms fcil recordar que debe almacenar cada variable: Usa nombres descriptivos para las variables Pues eso, es ms fcil saber que la variable "Nombre" guarda un nombre que si slo usas "n " Aunque en esto de dar nombre de las variables, coincido con otros programadores, de usar ciertas variables "no descriptivas" para ciertas cosas, no es por nada, sino porque como todos, he aprendido viendo cdigo de otros programadores y siempre se te pega algo del "estilo" que se va generalizando... por ejemplo, en el uso de las variables i, j, k para los bucles. Otro aspecto es el de usar ciertos "prefijos" para indicar el tipo y la visibilidad de las variables... en esto no todos estamos de acuerdo, pero casi... Seguramente habrs "oido" hablar de la notacin "hungara"... no voy a entrar en detalles de que va esto, pero si decirte ms o menos como se aplica, al menos en algunos casos, a la hora de declarar las variables. Para simplificar, y mucho, te dir que el prefijo es la letra o letras (normalmente tres), que se usan delante del nombre de la variable, por ejemplo, las variables del tipo Integer se declararan as: Dim intContador As Integer, lngContador As Long, strNombre As String , etc... De esta forma, es fcil saber que strNombre es del tipo String, aunque no tengamos "cerca" la declaracin de la variable. Yo suelo abreviar el tema y suelo declararlas de esta otra forma: (como otros programadores, que no vayas a creer que es invencin mia) Dim iContador As Integer, sNombre As String Con el tipo Long no siempre uso la "l" (ele minscula), entre otras cosas porque se confunde con el nmero "1" y casi siempre suelo declarar las variables Long con "lng". Para los otros tipos no suelo hacerlo de ninguna forma en particular, aunque el tipo "boolean" las declaro con la letra "b". En el caso de que las variables sean pblicas no suelo aadirle ningn tipo de identificador, ya que en el caso de los formularios se convierten en propiedades, no entro en ms detalles en esto de las propiedades, ya que ser el tema de la prxima entrega.

314

Pero esto de los prefijos no slo se aplican a las variables, tambin se hace a los controles y formularios. Por ejemplo: Prefijo cmd txt lbl chk cbo lst pic img Control CommandButon TextBoxes Labels CheckBoxes CombosBoxes ListBoxes PictureBox Image Ejemplo cmdSalir txtNombre lblStatus chkImpresora cboCiudad lstNombres picStatus imgBoton

Para los tipos definidos las declaraciones del tipo suelo empezarlos con "t_" y las variables declaradas a partir de un tipo con "t", las enumeraciones (ya las veremos en su momento), las empiezo por "e" y las constantes con "c"... En el caso de las variables a nivel de mdulo, las que son visibles en todo el mdulo actual, ahora veremos ms sobre esto, suelo anteponerle el prefijo "m_". Aunque todo hay que decirlo, no tengo un estilo fijo en esto de nombrar a las variables y controles, aunque en la mayora de los casos, es ste el que suelo aplicar... y cada vez con ms "consistencia", ya que a la larga te ayuda el usar algn tipo de recordatorio del tipo/nivel de visibilidad de los controles y variables. Para ir acabando con las recomendaciones del da, voy a darte la penltima: Usa comentarios en tus listados Y es que los comentarios, entre otras cosas, no ocupan espacio en el programa una vez compilado y a la larga, siempre es a la larga, ya que cuando el cdigo est reciente, nos acordamos de todo... pero cuando se lee despus de pasado algunos meses... puede que te ocurra lo que a mi... que al estar revisando listados antiguos, tuve que entender lo que hacia el cdigo y despus comentarlo para que la prxima vez que tuviese que modificarlo, me resultara ms fcil. Por tanto, si no quieres verte en este aprieto, usa y abusa de los comentarios, aunque sea un poco "pesado", te repito que se agradece, sobre todo si el cdigo cae en manos de otro programador, por ejemplo en un grupo de trabajo y tiene que entender lo que "pretendas" hacer...

La visibilidad o "cobertura" (o si lo prefieres: mbito) de las variables.

315

Y para acabar esta entrega "sermonstica" y casi de repaso, vamos a ver el tema de la visibilidad de las variables o el "alcance" que estas tienen dentro del mdulo e incluso del proyecto completo. De que va esto de la visibilidad? Esto va de que cuando se declaran las variables, estas no estn visibles en toda la aplicacin... AH! que lo que no entiendes es lo de "visible"... Te lo explico con un ejemplo: Supn que tienes un procedimiento que cuenta el nmero de veces que una letra est en una cadena, por ejemplo en "Visual Basic" la letra "a" est dos veces, talvez no tenga mucha utilidad este ejemplo, pero... ' Public Function CuantasVeces(ByVal sCadena As String, _ ByVal sLetra As String) As Long 'Esta funcin devolver el nmero de veces que est 'la letra sLetra en la cadena sCadena Dim i As Long Dim nVeces As Long 'Variable para el bucle 'variable temporal para el nmero de veces

'bucle para recorrer la cadena completa For i = 1 To Len(sCadena) 'Si el caracter comprobado es la letra buscada '(se supone que sLetra es slo un caracter) If Mid$(sCadena, i, 1) = sLetra Then 'incrementar el nmero de veces nVeces = nVeces + 1 End If Next 'Devolver el total de veces CuantasVeces = nVeces End Function En este cdigo hay dos variables declaradas: "i" y "nVeces", pues bien, estas variables slo son visibles dentro de esta funcin, es decir, que no tendrn nada que ver con otras variables que tengan el mismo nombre, y estn declaradas en otros sitios. Talvez este ejemplo "solitario" no est demasiado claro. Pero es un fallo que muchos "cometemos", (o al menos, algunos hemos cometido, aunque ya lo tengamos superado... o casi.) He visto ms de un listado en el que se declara una variable en un procedimiento y

316

despus se "intenta" usar en otro sitio distinto en el cual "no se ve" esa variable... Por supuesto, estos problemas de "visibilidad" se evitan usando Option Explicit... realmente no se evitan... sino que nos "obliga" (el VB) a que lo evitemos... ya que no nos dejar usar el programa si no estn las variables "debidamente" declaradas... o al menos declaradas aunque no sea de la forma adecuada... (ver ms arriba lo que comento sobre usar el tipo adecuado). Vamos a verlo con un ejemplillo... para que te des cuenta de lo que podas haberte evitado si usaras el Option Explicit... y si an no te ha dado tiempo a comprobarlo, porque eres de los que siguen "las reglas", ahora te dars cuenta de que poda haber sido peor tu aprendizaje... Para este ejemplo, tendrs que crear un nuevo proyecto, se aade por defecto un formulario (Form1), aade un mdulo BAS (Module1) y escribe lo siguiente en el mdulo BAS: 'En el mdulo BAS Option Explicit Public sNombre As String Public nVeces As Long Public Sub MostrarNombre() 'Muestra el nombre y el valor de nVeces MsgBox "El nombre es: " & sNombre & vbCrLf & _ "y se ha pulsado: " & nVeces & " veces" End Sub Escribe esto en el formulario (Form1) que tendr un TextBox (Text1) y botn (Command1): 'En el Form1 Option Explicit 'Esta variable es "privada" y visible slo en este formulario Private nVeces As Long Private Sub Command1_Click() 'Declaramos una variable "privada", 'y por tanto slo es "visible" dentro de este procedimiento Dim sNombre As String 'Asignamos a la variable el contenido del Text1

317

sNombre = Text1 'incrementamos las veces que se ha pulsado en el botn nVeces = nVeces + 1 'mostramos la informacin MostrarNombre End Sub Ejecuta el programa y vers que cuando pulses en el Command1, no te muestra el nombre y las veces siempre sern cero... aunque pulses varias veces. Por qu ocurre esto si las variables sNombre y nVeces estn declaradas como pblicas en el mdulo BAS? Esto es as, porque a pesar de haber declarado esas dos variables como pblicas en el mdulo BAS, (es decir visible en todo el proyecto), no se ha asignado nada a ellas... Seguro? Entonces, que pintan las asignaciones: sNombre = Text1 y nVeces = nVeces + 1 que hay en el procedimiento Command1_Click? Te lo explico: El caso de sNombre es ms fcil de comprender, a saber, cuando el Visual Basic entra en el procedimiento Command1_Click, declara una variable de tipo cadena llamada sNombre, esa variable slo es visible dentro de este procedimiento, es decir que cuando se use, el VB usar la memoria que ha reservado al encontrarse esa declaracin, por tanto el contenido del TextBox se guardar en ese espacio de memoria, osea: la variable sNombre del procedimiento Command1_Click. En el caso de nVeces es parecido al de sNombre, pero en este caso la "visibilidad" de esta variable es ms amplia que la de sNombre, ya que al estar declarada a nivel de mdulo, es visible en todo el formulario, pero al ser privada slo ser visible, (o accesible, o disponible, como prefieras), dentro del cdigo que est en el propio formulario y no fuera de l. Por tanto, al llamar al procedimiento MostrarNombre, (que est declarado como pblico en el mdulo BAS y por tanto disponible en todo el proyecto), el VB se encuentra con que queremos usar dos variables... comprueba si est debidamente declaradas y usa los valores que contienen... es decir, ninguno; por tanto muestra los valores por defecto... una cadena vacia y un valor cero. Esto en ingls se llama "scope", mbito o alcance; si lo entiendes mejor es la "cobertura" que tiene esa variable, como la de los telfonos mviles (celulares) o emisoras de radio... si no tiene cobertura en una zona, no se escuchar en esa zona... Vamos a ver las distintas formas de declaracin de una variable, para ver el "alcance" que tienen: -Declaradas en un procedimiento (Sub, Function o Property) Slo son visibles en ese procedimiento -Declaradas Privadas (o con Dim) en un formulario o mdulo (BAS, CLS u otro tipo) Slo son visibles en ese formulario o mdulo, incluidos todos los procedimientos de ese mdulo, salvo la restriccin anterior. -Declaradas Pblicas (o Globales) en un mdulo BAS

318

Son visibles en todo el proyecto, salvo cuando se encuentren con variables declaradas en los casos anteriores. -Declaradas Pblicas en formularios o mdulos de clase Sern visibles en cualquier sitio, siempre que se preceda con el nombre del formulario o clase... (ya veremos esto en otra ocasin) O slo dentro de ese mdulo, como si fuesen privadas al propio mdulo. Por tanto, las variables que tengan el mismo nombre, se usarn segn la ltima declaracin que tenga esa variable y si est al alcance del procedimiento en el que se est usando... que lio, verdad? Esta cercana en la declaracin de la variable se puede explicar tambin as: Primero se usarn las que estn declaradas en el propio procedimiento Despus las que estn en el mismo mdulo (o formulario o clase o... cuando digo mdulo, me refiero al "sitio" en el que escribes el cdigo) Por ltimo, las declaradas como globales o pblicas en un mdulo BAS

Hay que puntualizar que las variables pblicas declaradas en un formulario, mdulo de clase o control de usuario, se convierten en propiedades de esos "objetos", y como sabrs para usar una propiedad de un "objeto", debes especificar el nombre del objeto seguido de un punto y despus el nombre de la propiedad; aunque cuando se usan en el mismo mdulo o formulario, no es necesario usar el nombre del "objeto que las contiene", por tanto, si no se especifica el nombre del mdulo, slo son visibles dentro del mismo mdulo en el que se han declarado. Para que el ejemplo anterior funcionara como debe, tendrs que quitar las declaraciones que hay de las variables sNombre y nVeces en el formulario y slo dejar las del mdulo BAS. Prubalo y vers que ahora si que se muestra lo que se debe mostrar.

Usar una variable indicando el mdulo en el que se han declarado Ya que he tocado el tema de especificar el nombre del "objeto" delante de las variables, tambin se pueden usar de esa forma y as evitaramos el tema este de la visibilidad y alcance de las variables, aunque es algo ms "liante"... Esta forma de usarlo fuera del mdulo en el que se ha declarado, slo es vlido si las variables son "pblicas". Para verlo, un ejemplo: Crea un nuevo proyecto, se crear un formulario (Form1), aade un mdulo BAS (se crear Module1, no le cambies el nombre al mdulo) Escribe esto en el mdulo BAS y el formulario, segn se especifica: 'En el mdulo BAS Option Explicit Private pVariable As Long

319

'Esta variable est declarada en el Form1 y en este mdulo Public unaVariable As Long Public gVariable As Long

'El Form1 escribe: Option Explicit Private unaVariable As Long Public otraVariable As Long Private Sub Form_Load() Show 'para saber si est declarada, 'selecciona la variable y pulsa Shift+F2, 'te mostrar dnde est declarada 'Esta variable es la declarada en este formulario unaVariable = 10 'Estas variables estn declaradas como pblicas en el mdulo BAS otraVariable = 20 gVariable = 30 'Esta otra est declarada como pblica en el mdulo BAS, 'pero como tiene el mismo nombre que la declarada en el formulario, 'para poder usarla, hay que especificar el nombre del mdulo Module1.unaVariable = 40 'Esta dar error ya que es privada en el mdulo BAS '(Variable no definida) 'pVariable = 50 'si se usa de esta forma: 'Module1.pVariable = 50 'se encontrar la declaracin al pulsar Shift+F2, 'pero dar error de que no se encuentra el mtodo

320

'Mostrar el contenido de las variables: Print "unaVariable="; unaVariable Print "otraVariable="; otraVariable Print "gVariable ="; gVariable Print "Module1.unaVariable ="; Module1.unaVariable 'Esta no se puede mostrar porque es Privada y por tanto 'slo visible dentro del mdulo BAS 'Print "Module1.pVariable ="; Module1.pVariable End Sub Creo que los comentarios son aclaradores, as que ejectalo y vers lo que muestra, prueba a quitar Module1 cuando se use y asigne a "unaVariable" y vers que el primer valor se pierde (como debe ser...).

Bueno, aunque he tardado en publicarla... al fin hemos continuado con las entregas del cursillo bsico... La verdad es que el manuscrito era del 30 de Septiembre, empec a escribirlo en el OutLook Express cuando estaba en Canarias el 29 de Octubre y por fin lo he acabado el 6 de Diciembre... pero ms vale tarde que nunca... verdad? Confo en que haya servido de algo tanta espera... as que si quieres hacer algn comentario... hazlo pulsando en este link... Nos vemos. Guillermo

Curso Bsico de Programacin en Visual Basic

Ha pasado un largo tiempo, verdad? aunque en otras ocasiones las esperas han sido an ms largas, as que no te quejes demasiado... De todas formas, aunque ya lo habrs notado, las ltimas entregas que estoy publicando ya las tena escrita desde haca un montn de tiempo, esta en particular desde el 7 de Octubre del 98, pero la pereza unida a la falta de tiempo... hacen que se retrasen en publicar, pero ya sabes que esto de "mecanografiar" no es lo mio... pero ms vale tarde que nunca. Y ahora vamos a ver de que va todo esto y a ver si vale la pena, esperar tanto...

321

Propiedades, mtodos... y lo que se me ocurra


Una vez vistos los eventos, al menos los ms usuales, vamos a tratar ahora con las propiedades y mtodos de los formularios y controles. Que es una propiedad? Una propiedad, simplificando para que se entienda, es una informacin o dato "propio" del objeto; por ejemplo, un "objeto" label tiene una propiedad Caption que le indica lo que tiene que mostrar. Las propiedades son como variables que le indican al control o formulario y por extensin a todos los objetos, qu mostrar, cmo mostrarse o hacer cualquier otra cosa... sino hacer, al menos tener informacin para poder hacer algo... Veamos ms ejemplos, as conocers algunas propiedades "comunes" a la mayora de controles: Width Height Top Indica el ancho del control. Cambiando el valor de esta propiedad, cambiamos el ancho del control... como es de suponer... El alto del control, si queremos cambiarlo... La posicin superior del control. Normalmente hace referencia al control que lo contiene. Un form normal, har referencia a la posicin en la pantalla. La posicin izquierda del control. Mismo comentario que para Top Si se muestra o no Si est disponible o no. El nombre del control u objeto. Para usos "personalizados", es como una especie de variable que tiene cada control, en el que podemos guardar lo que queramos y no afectar al aspecto y/o funcionamiento del control. Esta no est presente en todos los controles, pero es tpico para los botones command y las etiquetas. Sirve para contener el texto a mostrar. Esta tampoco es comn a todos los controles, pero tambin sirve para mostrar texto, aunque normalmente los controles que la tienen suelen permitir que ese texto se modifique, el caso tpico son los TextBox.

Left Visible Enabled Name Tag

Caption

Text

Y como estas, muchas ms, para saber las propiedades que tiene un control, busca en la ayuda y te dir las que dispone... je,je... a ver si os acostumbro a pulsar F1 y me ahorro seguir con las entregas... No es que yo no quiera decrtelo, pero me parece absurdo documentar algo que ya est documentado y aunque no te lo creas bastante bien... aunque habr algunas propiedades que necesitarn de alguna explicacin y ejemplos... y esas, casi seguro, que las veremos.

322

Ahora vamos con los mtodos. Los mtodos no son otra cosa que procedimientos (SUB o FUNCTION). Un mtodo siempre hace algo, a diferencia de las propiedades que, aunque tambin pueden hacer algo, su papel suele ser parecido a una variable; por ejemplo, si queremos ocultar un formulario, llamamos al mtodo Hide, que queremos mover el formulario, pues usamos Move con los parmetros correspondientes y asunto arreglado. Es decir, son procedimientos normales y corrientes, pero su comportamiento est relacionado con el objeto, control o formulario, al que pertenece. Muchas veces a las funciones se las considera propiedades en lugar de mtodos, a esta "opinin" no debes darle ms importancia de la que tiene y centrarte en lo que realmente te interesa: aprender a usarlas e incluso a crearlas. Para saber ms sobre los mtodos... te digo lo mismo que con las propiedades, pulsa F1 y leete la ayuda.

Algunas propiedades son slo de lectura, es decir que no puedes modificarla en tiempo de ejecucin, aunque normalmente si las que puedes modificar en tiempo de diseo, es decir cuando ests trabajando en el IDE del Visual Basic (bien! es la primera vez que sale el nombre en lo que llevo escrito... no te preocupes, este desvaro debe ser a causa del "catarro" que tengo) Por ejemplo, la propiedad Sorted que le indica a un ListBox si debe mostrar los elementos clasificados o no, es de slo lectura cuando ejecutamos el programa, pero mientras "diseamos" el proyecto, nos permite cambiar ese valor. Como el ttulo de esta entrega deca: Propiedades, mtodos y lo que se me ocurra, se me ocurre comentarte que con el VB5 y superior, puedes crearte tus propios controles, para que? para personalizar su funcionalidad a tu gusto o a tus necesidades... pero si no tienes el VB5, an puedes seguir leyendo, ya que no voy a explicar nada, al menos por ahora, que no puedas hacer con el VB4 (lo siento por los que usen VB3, aunque tampoco vendra mal seguir leyendo). Cuando apareci el VB4, adems de permitir crear aplicaciones de 32 bits, tanto el Windows 95 como el Windows NT son sistemas operativos que "soportan" aplicaciones de 32 bits, aunque tambin funcionan las de 16 bits; sin embargo el Windows 3.xx slo soporta aplicaciones de 16 bits, aunque con un "apao" permite ejecutar algunas de 32 bits... aunque no las de VB... a que vena todo esto? Como intentaba decirte antes de "desvariar", con el VB4 lleg la primera intentona de Microsoft para hacer del Visual Basic un lenguaje orientado a objetos, en este punto te podra contar cmo llamaban la gente de Microsoft a esta versin del VB, pero como lo que se, lo he ledo en otros libros... no est bien que te lo cuente... como soy... verdad? (mejor te hubieras callao Guille) Bueno, al grano. Con el Visual Basic 4 lleg un nuevo tipo de mdulo: el mdulo de clase, el cual tiene como extensin .cls Adems de esta nueva extensin, los proyectos de VB tambin estrenaron "look" y pasaron de ser .MAK a .VBP (Visual Basic Project), no es que sea una ventaja, pero al menos se les dio "personalidad" a los proyectos de VB, ya que la extensin MAK, adems de ser usada por las versiones anteriores de VB, tambin las usaban (y usan) los compiladores de Microsoft, tanto para BASIC como para C/C++ y lo ms frustrante del compilador del BASIC de MS-DOS, es que exista una utilidad llamada MAKE, (para crear los ejecutables), que tambin las usaba... y algunas veces era un folln... Dejemos el cuento, que algunas veces me paso con mis "pelculas"...

323

Para ir calentando motores, ya que no creers que te voy a explicar cmo usar este nuevo tipo de mdulos... bueno, no te lo voy a explicar por ahora, pero si dentro de poquitas entregas... Vamos a ver cmo usarlo de forma "muy" genrica, para que al menos sepas que puedes crear tus propias "propiedades" y mtodos... gracias a:

Los mdulos de clase


Aunque sin necesidad de "conocer" las clases, puedes crear tus propiedades y mtodos en los formularios... aunque eso creo que lo tengo anotado para una entrega posterior... (es que ahora intento adelantar trabajo y voy preparando algo de material...) Venga, va!... vamos a insertar un mdulo de clase: Si tienes un proyecto abierto, en el men "project" (proyecto), selecciona aadir mdulo de clase. Esto crear una clase llamada Class1. Cmbiale el nombre y haz que se llame cNombre. En el cdigo escribe esto: Public Nombre As String Public AoNacimiento As Integer Con esto acabamos de crear dos propiedades, ahora vamos a ver cmo usarlas. Antes vamos a crear un mtodo que calcular la Edad: Public Function Edad() As Integer Edad = Year(Now) - AoNacimiento End Function Cuando queramos saber la edad que tenemos (o que tiene la clase), simplemente le restamos al ao actual nuestro ao de nacimiento... La funcin Now devuelve un valor que es la fecha actual (da y hora), por su lado Year toma slo el ao de la fecha indicada... PST! Aqu puedes hacer trampas, por ejemplo si quieres quitarte algunos aillos: Edad = Year(Now) - AoNacimiento -10 'Cambia el 10, por lo que quieras "rejuvenecer" Ahora en serio, vamos a ver cmo usar esta clase. A diferencia de los Form y mdulos BAS, las variables (propiedades) y procedimientos (mtodos) de las clases no se pueden usar si no le hemos indicado a Visual Basic que sepa de su existencia y no me refiero a tener que DIMensionar una variable, cosa que hay que hacer siempre, sino a otra forma de "existencia"... Para poder usar un mdulo de clase, tendremos que declarar una variable de una forma parecida a como lo hacemos con el resto de tipos de datos que ya hemos visto:

324

Dim unNombre As New cNombre Con esta declaracin le decimos al VB que cree una nueva (New) variable que nos permita usar esta clase llamada cNombre. Nota del 24/Ago/2003: Aqu he usado esa forma de declarar e "instanciar" (crear en memoria) una clase, pero esta no es una forma recomendable y nunca, nunca, deberas usarla. En lugar de declarar y crear una clase de esa forma, usa este otro cdigo: Dim unNombre As cNombre Set unNombre = New cNombre En la entrega correspondiente veremos con ms profundidad todo esto de las clases y la creacin de los objetos en la memoria, la asignacin de esos objetos mediante Set, etc... pero eso ser en otro momento. Una vez que tenemos creado el "objeto", lo usamos de la misma forma que hacamos con los tipos definidos: unNombre.Nombre ="Guillermo" unNombre.AoNacimiento = 1957 'Para mostrar la edad de este cuarentn, haremos: MsgBox unNombre.Nombre & " tiene " & unNombre.Edad & " aos..." Esto imprimir: (suponiendo que estamos en 1999) Guillermo tiene 42 aos... (aunque debera decir "tendr", pero bueno...) Ahora analiza el cdigo y deduce cmo funciona... ya que lo vamos a dejar aqu... as parecer una teleserie y estars pendiente de la pantalla... je, je... esto es la "revancha" por rerte de mi edad. Nos vemos Guillermo P.S. No te preocupes por la "cortedad" de esta entrega... pronto habrs ms... P.S. 2 Si hubiese querido, me podra haber enrollado mostrndote un montn de propiedades

Curso Bsico de Programacin en Visual Basic


325

Cada vez que miro las fechas en que estn escritas las entregas y en la que las publico, me da no se qu... pero es que no puedo hacer ms... Aunque ahora, gracias a Encarnica, esto puede que cunda ms... Quin es Encarnica... Encarni?, (que sino se puede mosquear...) Es la "secretaria" de la empresa... bueno, lo de secretaria es por llamarla de alguna forma, ya que tambin se encarga de otros menesteres administrativos... y ahora, adems, en sus ratos libres, se atreve a pasarme a mquina mis "manuscritos"... trabajo que le dar por un lado, entender mi letra y por otro entender el Visual Basic, lo mismo hasta aprende... je, je. Gracias Encarni!... No os doy un e-mail para que les deis tambin las gracias, porque no tiene, pero si quieres agradecrselo, usa este link. Nota: No es necesario que escribas nada, con el asunto es suficiente, pero si escribes... que sepas que no debes incluir consultas ni nada de eso que acostumbris a hacer cada vez que os encontris con un link para enviar un mensaje... este link es exclusivo para agradecer a Encarni el que me alivie el trabajo de teclear y que las entregas aparezcan con mayor frecuencia...

Formularios, mdulos y algo ms...


Ya habrs visto que cada vez que se crea un nuevo proyecto, por regla general, se aade un formulario (Form1.frm) en este formulario podemos aadir algunos controles para interactuar con el usuario es decir, que el usuario puede escribir en las cajas de texto, pulsar botones, etc. Tambin, hemos visto, aunque sea por encima, cmo controlar y poder interceptar algunos eventos Pues, ya est si sabes todo eso, para que seguir?. En nuestros proyectos, podemos tener ms de un formulario, por ejemplo, uno podra ser el principal, el que hace la mayor parte del trabajo, otro podra mostrarnos mensajes de aviso o el resultado de cualquier ora accin La cuestin es que podemos usar y mostrar cuando sea conveniente otros formularios. Vamos a ver un ejemplo, sencillito para no complicarnos demasiado la vida, en el que veremos tres formularios este ejemplo jugar a adivinar un nmero y nos informar de las veces que hemos jugado, etc. Tambin se podr cambiar de jugador El formulario principal lo usaremos como punto de inicio, y nos mostrar el nombre del jugador actual, las veces que ha jugado y los puntos conseguidos, adems tendr 3 botones, uno para cambiar de jugador, otro para jugar una partidita, y un tercero para terminar el programa. Usaremos otro formulario que permitir escribir el nombre del jugador, y otro ms, que usaremos para jugar, aunque todo esto no sera necesario, pero as es ms fcil y manipulamos ms formularios. Necesitamos una serie de variables que sean globales a todo el proyecto, por ejemplo, el nombre del jugador. Esto podemos hacerlo de dos formas: Aadiendo un mdulo bas, y declarando una variable pblica, para que sea visible en todo

326

el proyecto. Otra forma sera aadiendo una variable pblica al formulario principal Tambin sera visible en todo el proyecto, aunque para acceder a ella tendramos que indicar el nombre del formulario Vamos a usar el segundo mtodo, ya que al ser el formulario que se usar como punta de entrada, siempre estar disponible. Veamos el cdigo del formulario principal al que llamaremos frmPrincipal, para hacer esto, haz que se muestre el formulario, pulsa F4 y en las propiedades, selecciona name (nombre) y sustituye form1 por frmPrincipal. A partir de ahora, cada vez que quieras referirte al formulario principal, tendrs que hacerlo con ese nombre: frmPrincipal. Muestra la ventana de cdigo de este formulario y aade esto (recuerdas para que sirve Option Explicit?) Option Explicit Public Nombre As String Ahora nuestro formulario tiene una nueva propiedad, a la que podremos acceder desde el resto del proyecto usndolo de esta forma: frmPrincipal.Nombre Este formulario principal hace pocas cosas, no s por qu todos los que tienen un papel principal suelen hacer pocas cosas Bueno, al lio... La verdad es que lo nico que hace es llamar a otros formularios y acabar t dirs lo que quieras, pero esto de mandar, hacer poco y adems ser el principal en fin cosas de la vida Vamos a aadir un nuevo formulario, que se llamar frmNombre. Para hacerlo, selecciona el men Proyecto, Aadir formulario, selecciona uno normal y ya est disponible para que le cambies el nombre y codifiques en l. Recuerda: pulsa F4 y selecciona la propiedad Name, borra Form2 y escribe frmNombre. Como te coment, este formulario jefe tiene tres botones, a los que llamar: cmdNombre, cmdJugar, cmdTerminar; cada uno de ellos hace lo que los nombres, ms o menos, indican. Veamos el cdigo: Private Sub cmdNombre_Click() ' Mostranos el form que pide el nombre ' Esto har que se muestre y no se pueda hacer otra cosa ' hasta que se cierre u oculte frmNombre.Show vbModal End Sub

327

Ya ves que hace poco, simplemente muestra otro formulario que se encargar de pedir el nombre del jugador. Aqu hay unas cosillas a tener en cuenta, incluso deberas tomar nota, as que toma nota y qudate con la copla Con Show mostramos el formulario en cuestin, fjate que se usa NombreFormulario.Show, lo cual llama a un mtodo propio de ese formulario, que lo que hace es mostrarlo Otra cosa a tener en cuanta es que si es formulario no est cargado en memoria, al usar este mtodo se carga de forma automtica, esto lo veremos en ms ocasiones, as que... tranquilzate, toma aire, que todo llega El mtodo Show acepta algunos parmetros, que yo conozca son dos, ambos son opcionales y por el momento slo veremos el primero de ellos. ste primer parmetro del mtodo Show, lo que hace es mostrar el formulario de forma modal o no. Mostrar un formulario modal lo que hace es mostrar ese formulario y no hacer otra casa en la aplicacin hasta que deje de mostrarse; es como si toda la aplicacin, (la nuestra), se congele y slo preste atencin a lo que ocurre en ese formulario, esto es importante sobre todo cuando es necesario esperar a que el usuario introduzca los datos necesarios

Debo reconocer que cuando empec con el Visual Basic, esto de no saber que exista esta forma de mostrar los formularios, me dio algn que otro comecoco Por suerte, aprend que la tecla F1 serva para algo ms que estar al lado de F2... Cuando no necesites esperar a que el usuario introduzca datos o no sea importante que se siga usando las aplicaciones a pesar de que se muestre ese formulario; puedes mostrarlo de forma no modal, ese es el valor por defecto del primer parmetro, es decir que si se usa: NombreFormulario.Show, el formulario no se muestra modal, as que ojito A lo que vamos, usando frmNombre.Show vbModal, se muestra el formulario y se espera a que se cierre u oculte. Veamos lo que hace este formulario, la verdad es que tampoco hace mucho, salvo darle informacin al formulario principal. Ahora veremos como. El formulario frmNombre tiene este aspecto:

328

Un textbox para el nombre del usuario, un botn aceptar y otro cancelar Los nombres de estos controles son: txtNombre, cmdAceptar y cmdCancelar. Podramos haber usado la funcin InputBox para hacer esto, pero no es lo mismo El botn cancelar simplemente descarga el formulario de la memoria con: Unload Me. El botn aceptar tambin descarga el formulario, pero antes de eso, asigna el nombre introducido en la propiedad Nombre del formulario principal, fjate en el cdigo para ver como se hace: frmPrincipal.Nombre = txtNombre Como ya coment antes, las propiedades de un formulario se pueden acceder siempre usando el nombre del formulario en cuestin, si no se usara cmo sabramos qu formulario es? La nica excepcin es cuando esas propiedades se usan dentro del mismo formulario ahora lo veremos. Pero esto no est bien o casi si dejsemos en el textbox del formulario que se usa para pedir el nombre, el valor que hay por defecto, es decir el que se muestra cuando se aade el control al formulario, en este caso Text1, nos daramos cuenta que sera ms lgico que se mostrase el nombre del usuario actual, si hubiese alguno, por tanto se debera mostrar el nombre y as permitir usar ese nombre o escribir uno nuevo. Esto lo podemos hacer de dos formas: Una: cmo la propiedad Nombre del form principal es pblica, podemos asignarla al textbox cuando se cargue el formulario que pide el nombre, es decir en el evento Load de frmNombre: Private Sub Form_Load() txtNombre = frmPrincipal.Nombre End Sub La otra forma, sera usar la posibilidad de poder referirnos a los controles de un formulario desde otro formulario; se har de la misma forma que para acceder a una propiedad Veamos el cdigo, en este caso, la asignacin se hace en el procedimiento que se encarga de mostrar el formulario frmNombre, es decir en el evento cmdNombre_Click Private Sub cmdNombre_Click() frmNombre.txtNombre = Nombre frmNombre.Show vbModal End Sub Nota: Cuando se muestra un form modal, es importante no usar el mtodo Show dentro del cdigo del evento Form_Load del formulario mostrado, en el caso de que se haga, el VB nos dar un error indicndonos que no se puede mostrar un formulario que se est mostrando en forma modal.

329

Espero que lo tengas claro, ya que te voy a complicar un poco ms la vida, aunque antes veamos unas consideraciones: Habrs odo de reutilizacin de cdigo y esas otras cosillas relacionadas con la Programacin Orientada a Objetos, (OOP), aunque no toda las buenas formas de hacer las cosas tienen que estar relacionadas con la OOP aunque mejor no entrar en polmica. Si usamos el formulario tal como lo he codificado, no podrs usarlo de forma genrica, (reutilizarlo), ya que dentro de ese cdigo se hace referencia a otro formulario es decir, siempre que usemos frmNombre, necesitaremos tener otro formulario llamado frmPrincipal que al menos tenga una propiedad llamada Nombre Aunque no tiene porqu ser una propiedad como hemos visto, tambin podra ser un control pero olvdate de esto para que no te les demasiado Bien, cmo lo solucionamos? Si has ledo algo sobre la programacin orientada a objetos, puede que te hayas encontrado con palabras como encapsulacin de Qu significa esto? Sin entrar en demasiados detalles didcticos, encapsular sera tener la autonoma suficiente para no depender del exterior y an as, poder funcionar o lo que es lo mismo, al formulario frmNombre no le importa si el formulario que quiere usarlo tiene o no una propiedad llamada Nombre, tampoco le molestar que ese formulario se llame frmPrincipal o de otra forma. El que tiene que saber cosas de frmNombre, ser el formulario que quiere usarlo Qu tiene que saber? En este ejemplo slo existe una propiedad llamada txtNombre que se encargar de devolver el nombre introducido y tambin debera indicar que se ha cancelado aunque esto te lo dejo, (por ahora), a ti ya veremos la respuesta ms adelante. Veamos el cdigo tal como estara despus de todo esto que te he explicado. Empecemos por el final, veamos el cdigo del formulario encapsulador, (frmNombre): Option Explicit Private Sub cmdAceptar_Click() Hide End Sub

Private Sub cmdCancelar_Click() Hide End Sub Ahora te explico de que va esto Veamos antes el cdigo del form principal (frmPrincipal): Option Explicit Public Nombre As String

330

Private Sub cmdNombrer_Click() frmNombre.txtNombre = Nombre frmNombre.Show vbModal Nombre = frmNombre.txtNombre Unload frmNombre End Sub Dirs que nos hemos complicado demasiado antes era ms corto no todo van a ser ventajas, aunque si por aadir dos lneas de cdigos ganamos un formulario que es independiente tu decides Aunque, personalmente, este ltimo cdigo lo escribira as: Private Sub cmdNombrer_Click() With frmNombre .txtNombre = Nombre .Show vbModal Nombre = .txtNombre End With Unload frmNombre End Sub Ahora tenemos ms lneas de cdigo, pero ganamos en claridad adems si te acostumbras a usar With, (segn dicen), el cdigo es ms rpido, al menos cuando se hacen referencias a objetos con distintos niveles, este no es el caso, pero ya te topars con casos de mltiples referencias todo llegar Antes de seguir encontrando pegas, veamos porqu ha cambiado Unload Me por Hide: Unload Descarga un formulario de la memoria, despus de Unload se debe indicar el nombre del formulario. En este caso se usa Me para indicar que se descarga el formulario en el que est esa instruccin. Simplemente oculta el formulario, pero no lo descarga.

Hide

La diferencia est en que si descargamos el formulario, lo borramos de la memoria, por tanto destruimos todo el contenido y lo que hubiese almacenado en los controles y esto no es lo que nos interesa, ya que necesitamos conservar el contenido del txtNombre para que el cdigo que use frmNombre pueda saber el nombre que se introdujo. Un lo verdad? Pues si te las con cuatro lneas de cdigo, no sabes lo que te espera... je, je.

331

Espero que te vayas quedando con la esencia y que vayas asimilando cosas no pretendas saber programar lo que pretendo es que al final, (si es que esto puede terminar algn da), sepas programar y por extensin saber desenvolverte con el Visual Basic. Ahora te dejo con el ejercicio de mejorar lo presente, por ejemplo que el formulario principal sepa que se ha pulsado en Cancelar no te comas mucho el coco y no te compliques demasiado, intenta buscar soluciones fciles, que las hay aunque tambin puedes complicarte la existencia y hacerlo de otra forma diferente usando otras propiedades, por ejemplo ops! ...creo que me he pasado con la ayuda En la prxima entrega seguiremos que ya es tarde y maana hay que currar Pincha este link para ver una de las soluciones Nos vemos. Guillermo Nerja, 6/Oct/98, 03:10 P.S. UF! Hace ya bastante tiempo, verdad?

Curso Bsico de Programacin en Visual Basic


Si has notado que la entrega anterior est "inacabada" ests en lo cierto... la verdad es que gracias a uno de los "tpicos" despistes que me caracterizan, se me fue un poco la "olla" y cre haberla acabado, pero... no fue as, por tanto, espero que no te enfades mucho conmigo y tengas paciencia, que pronto estar terminada... por ahora sigue con lo que hay que al ser un tema diferente no te causar ningn trastorno cerebral... y si te lo causa... bienvenido al club! Esta entrega tambin ha sido "mecanografiada" por Encarnica... que ya tiene pasadas a limpio hasta la entrega 32... aunque slo la parte explicativa, ya que el cdigo me lo deja a mi... que ella an no sabe programar... as que si hay retrasos en la publicacin, es slo culpa mia... Tambin quiero daros las gracias, en nombre de Encarni por los que la habis felicitado por "ofrecerse" a pasar a limpio mis notas "ininteligibles". Gracias otra vez Encarni!

Mens, submens, popupmens y ajuste de tamaos.


En la entrega de hoy vamos a ver cmo aadir mens a nuestros programas. Para verlo "ejemplarizado" vamos a crear un minieditor (por fin!) pero no te hagas ilusiones... ser tan simple que casi slo vamos a utilizar un cuadro de texto y sus posibilidades sern

332

bsicamente las que nos de el textbox... bueno, realmente tendr algunas ms... ya veremos. Para crear el programa, abre un proyecto nuevo, el el form que se crea por defecto, aade un TextBox, asgnale estos valores a las propiedades indicadas: Propiedad Multiline Scrollbars Left Top Name Valor True 3-Both 0 0 txtEditor

Multiline para que permita ms de una lnea de texto; los dos Scrollbars para que podamos escribir lneas de cualquier longitud y que slo cambien al pulsar Intro; Left y Top, para que se posicione en la esquina superior izquierda del formulario.

Crear una barra de estado (StatusBar) Vamos a aadirle al formulario un Picture para que nos sirva de Statusbar, para ello aade un picture al proyecto, seleccinalo y asgnale la propiedad Align a 2 - Align Bottom para que se pegue a la parte inferior del formulario. Asgnale un valor 315 a la propiedad Height, en la propiedad BorderStyle, asgnale un 0 (sin borde). Selecciona del toolbar una etiqueta haz doble click y se insertar en el formulario. Selecciona la etiqueta y crtala (men edicin / cortar). Selecciona el picture y pega la etiqueta (men edicin / pegar). Asgnale estas valores a las propiedades indicadas: Height = 285, Top = 15, Left = 30, BorderStyle = 1 (con borde). Ahora vamos a darles nombre a los controles, al TextBox llmalo txtEditor, el Picture ser picStatus, el Label se llamar lblStatus.

Posicionar los controles automticamente en el formulario Antes de empezar a crear los mens, vamos a indicarle al Visual Basic que "posicione" correctamente los controles cuando el formulario cambie de tamao. Para ello, abre la pantalla del cdigo, selecciona Form de la lista de la izquierda y Resize en la lista de la derecha. Cada vez que un formulario cambia de tamao se ejecuta el evento Form_Resize, por tanto este es el sitio en el que tendremos que codificar para adaptar los controles al tamao adecuado. Lo que vamos a hacer es ajustar el tamao de la etiqueta al tamao del Picture y el TexBox para que ocupe todo el tamao restante. Pero slo haremos los clculos cuando el form no se minimice, ya que si est minimizado, no se ve nada, as que para que vamos a ajustar el tamao de algo que no se ve; para indicarle al VB que slo ejecute el cdigo cuando no vaya a minimizar la aplicacin usaremos la propiedad WindowState, si esta es diferente de vbMinimized querr decir que no se ha minimizado, as pues, aade este cdigo:

333

' Slo cuando no est minimizado el formulario If WindowState <> vbMinimized Then Y ahora empezaremos a ajustar los tamaos: El label ser igual de ancho que el PicStatus menos 60, para que tenga un poco de "respiro" por los lados: ' lblStatus.Width = picStatus.ScaleWidth - 60 esta es simple, ahora haremos lo mismo lo mismo con el txtEditor: ' txtEditor.Width = ScaleWidth el alto ser el alto del form menos el alto del PicStatus: ' txtEditor.Height = ScaleHeight - picStatus.Height y ya est. Se supone que en tiempo de diseo asignaste 0 a las propiedades Left y Top del txtEditor, aunque si quieres, puedes hacerlo en este mismo evento: ' txtEditor.Move 0, 0 eso es lo mismo que haber asignado 0 a las propiedades Left y Top es ms rpido cambiar el tamao de un control con Move que asignando cada una de las propiedades por separado, ya que se usa un slo mtodo en lugar de 4 asignaciones a propiedades, as pues, podramos haber cambiado el tamao del txtEditor de esta otra forma, aunque seguramente sera menos "instructivo": ' txtEditor.Move 0, 0, ScaleWidth, ScaleHeight - picStatus.Height Vamos a probarlo : Pulsa F5 y cambia el tamao de la ventana, vers como se "adaptan" los controles aunque el label no parece enterarse verdad?

334

Para que el label se ajuste al tamao del picture, hay que ajustar ese tamao en el evento Resize del picStatus: ' Private Sub picStatus_Resize() ' Slo cuando no est minimizado el formulario If WindowState <> vbMinimized Then ' Aqu se ajustar el tamao del label ' cuando cambie el del picStatus lblStatus.Width = picStatus.ScaleWidth - 60 End If End Sub Algo sobre los tamaos de los controles: Diferencia entre Height/Width y ScaleHeight/ScaleWidth Como habrs notado, para ajustar el ancho y alto, se est usando ScaleWidth y ScaleHeight, aunque para calcular el alto del textbox tambin se usa picStatus.Height, en cuanto te explique que significan estas propiedades, seguro que lo entiendes. ScaleWidth y ScaleHeight son propiedades que nos informan del ancho y alto "interno" del formulario o control, es decir lo que miden sin contar el borde. Width y Height, por otro lado, nos dicen que el ancho y alto "externo" del form o control. Cuando se calcula el alto del txtEditor necesitamos saber el alto interno del formulario (ScaleHeight) al que hay que restarle el alto total de picStatus (picStatus.Height). Si hubisemos usado Height en el lugar de ScaleHeight, los clculos no nos hubiesen salido correctos, ya en el valor devuelto por esa propiedad nos indicara el alto total del form. Ahora mismo, tal como est el programa habra poco diferencia, aunque an as no se ajustara perfectamente, "desajuste" que quedara demasiado evidente en cuanto aadamos mens. (mens? No era de eso de lo que iba a tratar esta entrega). Otro detalle es que para referirnos al alto y ancho "interno" del formulario, lo hemos usado sin indicar nada ms, esto siempre es as cuando hagamos referencia a una propiedad de un objeto, (en este caso un formulario), y el cdigo se ejecuta "dentro" de ese formulario. Sin embargo cuando hacemos referencia a las propiedades de otros controles, (incluso de otro formulario), tendremos que anteponer el nombre de ese control delante de la propiedad o mtodo, para que el VB sepa a que control nos estamos refiriendo. Borra la lnea que cambiaba el tamao de la etiqueta en el evento Form_Resize y vuelve a pulsar F5, cambia el tamao del formulario, esta vez si que se adapta bien el lblStatus dentro del picture que la contiene. Ya puedes detener el programa, pulsando en la "x" del form. Haz que se muestre el fomulario ya que es necesario para poder aadir mens a nuestro "mini-editor".

335

Aadir mens a un formulario


Como habrs observado en todas las aplicaciones de Windows, los mens se muestran en la parte superior de las aplicaciones, esto no es ningn descubrimiento, excepcional pero la cuestin es que si se muestran en la parte superior tendremos que hacer un nuevo clculo al cambiar el tamao del formulario? La respuesta es: no. Al aadir mens a nuestro formulario, el tamao de ste se ajusta automticamente y no tendremos que tener en cuenta el espacio que ocupa para "ajustar" los controles que tengamos en l. Es decir, que el cdigo del Form_Resire sigue siendo vlido con o sin mens. Una vez aclarado este punto, antes de empezar a aadir mens, otro poco de teora pero no te asustes, no es demasiado la teora, slo para aclarar "conceptos". Cuando creamos mens tenemos varios "niveles", normalmente son dos: el men principal que siempre est visible en la parte superior y los elementos que se muestran cuando hacemos click (o pulsamos) en ese men principal. Cada vez que pulsamos en un elemento de la "lista" de mens principal se muestran los que "cuelgan" de l. Habrs observado que muchos de los elementos de los mens, tanto principales como secundarios, tienen una letra subrayada, eso quiere decir que pulsando Alt ms esa letra, se despliega o selecciona esa opcin del men, es como si pulsramos con el ratn. Cuando empiece con la explicacin vers cmo podemos crear nuestras propias letras de acceso, incluso cmo aadir "accesos rpidos" a algunas de las opciones de los mens, todo esto lo veremos ahora mismo.

Cmo aadir mens a nuestro formulario (ahora si)


Para poder "disear" los mens, tienes que tener visible el formulario en el que mostraremos los mens, as que si el formulario no est mostrado, haz que se muestre, (haciendo dobleclick en la ventana del explorador de proyectos) Para entrar en modo de diseo de mens, puedes hacerlo de dos formas: seleccionando del men Tools (Herramientas) la opcin Menu Editor o pulsando el icono de herramientas. Te mostrar un cuadro de dilogo como el que sigue: de la barra

336

Las partes ms importantes son: Caption/Descripcin que es el texto que se mostrar, Name/Nombre del men que ser donde escribamos el cdigo a ejecutar cuando se seleccione ese men. Vamos a empezar por aadir un men "Fichero", (o archivo si as lo prefieres), en este men tendremos las opciones de Abrir, Guardar, Guardar como y Salir, despus aadiremos otras, segn convenga. Tambin tendremos otro men principal llamado Edicin con las clsicas opciones de ese tipo de men: Deshacer, Cortar, Copiar, Pegar, etc. Pero empecemos por el de fichero: Escribe en el "Caption", &Ficheros, el signo & le indicar al Visual Basic que muestre subrayada la letra que sigue a ese signo, de esa forma se podr acceder pulsando Alt y la letra subrayada, es decir Alt+F En el nombre del men escribe: mnuFic y pulsa Intro o en el botn Siguiente. Se "limpiarn" las casillas de texto y estar listo para escribir las opciones de este men: Escribe en Caption: &Abrir... y en nombre mnuFicAbrir los tres puntos suspensivos es una norma recomendable, que indica que se mostrar un cuadro de dilogo, acostmbrate a seguirla, de esta forma tus aplicaciones tendrn un aspecto "standard windows" (realmente no es un estndar de windows, sino una norma anterior anterior, pero) Despus de la opcin Abrir vamos a aadir Guardar, por tanto en el caption del men escribimos &Guardar y en el nombre de esa opcin: mnuFicGuardar, en este caso no

337

aadimos los tres puntos seguidos ya que lo habitual en las opciones guardar, es guardar sin preguntar, salvo que an no se le haya dado nombre al fichero. Como habrs observado cada vez que aades una opcin se va mostrando en la lista inferior. Antes de seguir vamos a ver cmo quedan nuestros mens. Pulsa el botn "aceptar" del cuadro de dilogo del "diseador de mens". Sorpresa! Como puedes observar, tenemos tres opciones "principales": Ficheros, Abrir y Guardar, pero esta no era la intencin, ya que Abrir y Guardar slo se deberan mostrar al seleccionar el men Ficheros Qu ha pasado? Muy fcil, al menos cuando se sabe cmo trabaja esto de los mens, que al no indicarle lo contrario todos los mens se muestran en la barra principal! cmo podemos crear los submens (o mens que se muestran al seleccionar un men)? Ahora lo veremos, antes de hacerlo, en tiempo de diseo, es decir, sin pulsar F5, pulsa en el men Fichero. Se mostrar la ventana de cdigo, el combo de en la parte izquierda se mostrar mnuFic y en el de la derecha vers que es el evento click, por tanto estaremos en el procedimiento mnuFic_Click. Todo lo que escribas en este men se ejecutar cuando selecciones esta opcin. No escribas nada, cierra la ventana de cdigo para volver a mostrar el formulario. Vamos a hacer que los mens se muestren como deben: al seleccionar Ficheros que se despliegue el men con las opciones Abrir, Guardar, etc. Entra en el diseo de mens, (esto tendrs que hacerlo siempre que quieras aadir nuevas opciones de mens o modificar las ya existentes.) Si te fijas en la lista inferior, vers que las tres opciones que tenemos estn alineadas a la izquierda. Selecciona Abrir en la lista inferior, comprobars que se "rellenan" las casillas con la descripcin y el nombre del men, eso nos indica que est seleccionada esa opcin y que cualquier cambio que hagamos, se har en esa opcin. S que todo esto es evidente, pero por si no lo habas captado ahora pulsa en la flecha que seala a la derecha, esto har que la opcin seleccionada se desplace a la derecha, esto se muestra por tres puntos delante de Abrir, no los confundas con los tres puntos que nosotros le aadimos al final. Haz lo mismo con "Guardar" Cierra el cuadro de dilogo y veras que ahora slo se muestra el men Ficheros. Bien! Ya tenemos lo que queramos! Selecciona ese men y vers que se muestran las dos opciones que hemos aadido; en esta ocasin no se muestra la ventana de cdigo, pero ya veremos que e evento "sigue operativo". Pulsa en la opcin "Abrir..." y en esta ocasin se mostrar la ventana de cdigo con el procedimiento: "mnuFicAbrir_Click", para comprobar que funciona vamos a aadir un mensaje que se mostrar cuando seleccionemos esta opcin: ' Private Sub mnuFicAbrir_Click()

338

' Abrir MsgBox "Esta es la opcin Abrir..." End Sub Ahora para comprobarlo, pulsa F5 y selecciona el men Ficheros, se mostrarn las dos opciones que tenemos en este men: Abrir y guardar. Selecciona Abrir y vers que se muestra el mensaje, lo cual quiere decir que todo est bien. Cierra la aplicacin y vuelve a mostrar el formulario para aadir ms opciones a los mens; as que haz que se muestre el diseador de mens. Selecciona la ltima de las opciones de la lista y pulsa en el botn Siguiente para que podamos aadir ms opciones. Escribe G&uardar como... en la descripcin y mnuFicGuardarComo en el nombre. Si esta nueva opcin se muestra en la lista totalmente a la izquierda, pulsa en la flecha de identacin a la derecha para que est al mismo nivel que las otras dos, es decir que tenga tres puntos delante del Caption que le hemos dado. Pulsa de nuevo en el botn Siguiente, ahora tendrs que escribir un guin, (signo menos), en la descripcin del men, en el nombre del mismo escribe: mnuFicSep1, no sirve de nada, ya que las lneas "divisorias" no se pueden seleccionar, pero deben tener un nombre. Lo que debes "recordar" es que si se indica un "-" en la descripcin del men, estamos indicndole al VB que lo que queremos es que muestre una lnea de divisin. Pulsa en siguiente y escribe: &Salir y mnuFicSalir (ya no es necesario que te diga dnde debes escribirlo, verdad?) Ya tenemos las opciones del men Ficheros, ahora vamos con el men de edicin. Aade este men a continuacin de Salir, escribe &Edicin en el Caption y mnuEdit en el nombre. Cuando lo hayas escrito ver que est debajo de Salir y con los tres puntos delante, si lo dejamos as, no se mostrar en la barra principal de mens, sino que ser una opcin ms del men Ficheros, y eso no es lo que queremos, por tanto, pulsa en la flecha que seala a la izquierda para que se pegue totalmente a la izquierda, y desaparezcan los puntos suspensivos que hay delante del Caption. Porque como ya vimos antes, las opciones que se muestran en la lista y que estn sin los puntos suspensivos son las que se mostrarn en la barra de mens. Antes de aadir opciones al men de edicin, vamos a modificar las opciones que tenemos, insertaremos una nueva al principio, que servir para crear un nuevo fichero. El caption, como puedes imaginar, ser Nuevo y el nombre del men ser mnuFicNuevo. Vamos a aadirla: asegrate que estemos en modo de diseo de mens. La nueva opcin la vamos a insertar al principio, es decir justo antes de Abrir. Por tanto, selecciona Abrir de la lista inferior, pulsa el botn insertar y podrs escribir la descripcin: &Nuevo y el nombre del men: nmuFicNuevo. Cada vez que quieras insertar una nueva opcin puedes hacerlo de esta forma o bien aadiendo la opcin al final y despus "situarla" en el lugar correspondiente usando las flechas arriba y abajo. Cuando insertas una opcin, usando el botn insertar, la opcin insertada tiene la misma "indentacin" que la que estaba seleccionada antes de pulsar en insertar. Ya sabes que puedes modificar dicha identacin, o desplazamiento, usando las flechas de izquierda y derecha. Si lo que quieres es borrar un elemento de men, simplemente la seleccionas y pulsa en "eliminar", aunque esto slo elimina la opcin del men, no el cdigo que tuviese

339

asociado, lo mismo ocurre cuando eliminamos o cambiamos el nombre de un control: si ya tena cdigo en algunos eventos, este cdigo sigue estando, pero no en el sitio que debiera no voy a seguir con esto, ya que lo veremos en otra ocasin, pero al menos cuando le llegue el turno te "sonara" Como ya vimos en La entrega 26 se suele seguir unas "normas" a la hora de nombrar a los controles y variables, y si no se "suele" seguir, al menos se recomienda. En este caso los mens se preceden con "mnu" seguido del nombre de men principal y por ltimo el nombre de la opcin. Aunque, como todos los consejos, eres libre de seguir estas normas o de crear las tuyas propias. En mi caso, "intento" seguir stas que te estoy indicando, aunque algunas veces me las salto, normalmente con la opcin "Salir" que simplemente la llamo: mnuSalir, aunque es mejor llamarlo mnuFicSalir para que sepamos que est "incluida" en el men fic-heros. Pero en esto de los nombre de los mens, aparte de que los puedes "nombrar" como quieras, existe otra forma de hacerlo. Ya vimos que se pueden crear "arrays" de controles y que la ventaja era, sobre todo si estaban relacionados, que no necesitamos escribir el mismo cdigo para cada uno de los eventos que queramos interceptar; simplemente usando el ndice podramos distinguir un control de otro; pero siempre, y esa es la ventaja, en un mismo procedimiento de evento. Pues esto mismo se puede hacer con los mens: podemos crear un array. La nica diferencia es que se crea de forma un poco ms manual y se hace en la "ventana de diseo de mens".

Array de mens
Para ello, se usa el mismo nombre de men, pero usando un "ndice" diferente para cada opcin. El nico requisito "obligatorio" es que los elementos de un array de mens han de estar correlativos. Habitualmente se incluyen en ese array todos los elementos de un men principal bueno, los que se muestran al seleccionar esa opcin. Y esto es lo que vamos a hacer nosotros: incluir en un array todos las opciones del men edicin, que sern las siguientes: Deshacer, separacin, cortar, copiar, pegar, separacin, seleccionar todo; posteriormente aadiremos ms opciones aunque seguramente ser en otra entrega. El nombre del array ser: mnuEditor, el primer elemento, de ndice cero, ser: Deshacer. Vamos a aadirlo pasito a pasito, para que no tropieces y te "descalabres". Supongo que ya estars en el diseador de mens y que la opcin seleccionada es la ltima: Edicin. Pulsa en el botn Siguiente y escribe en el Caption: Des&hacer, en el nombre del men: mnuEditor, (sin acento o tilde... como prefieras llamarlo), en Index escribe 0; pulsa el botn con la flecha a la derecha, para indicar que esta opcin pertenece al men Edicin. Pulsa en siguiente, si no se indenta, ya sabes cmo debes hacerlo; escribe un "-" en el Caption, mnuEditor en el nombre, pero en ndice escribe 1, ya que al llamarse de la misma forma el men, el Visual Basic esperar encontrarse con un ndice que lo diferencie. Haz lo mismo con el resto e las opciones y recuerda usar ndices correlativos, y por supuesto diferentes...

340

Cuando hayas introducido todas las opciones, cierra el diseador de mens si te da algn tipo de error, puede ser porque no hayas usado el mismo nombre de men para todas las opciones del men edicin, porque no estn los ndices correlativos o porque no estn todos indentados en el mismo nivel, es decir con tres puntos suspensivos a la izquierda (esto es lo que se muestra en la lista y no tienes que escribirlos). Este sera el aspecto:

Recuerda que los elementos de un array de mens deber ser correlativos y estar en el mismo nivel de indentacin ("osease" pertenecer al mismo men). Si todo est bien, al mostrar el formulario en tiempo de diseo, no en ejecucin, y pulsar en el men edicin, vers que se muestra las opciones que hemos escrito. Pulsa en cualquier de ellas y vers que siempre se muestra el mismo evento en la pantalla de cdigo: mnuEditor_Click (Index As Integer) El parmetro index ser el que nos indique cual de las opciones ha sido la que se ha coleccionado. Para poder hacer cosas diferentes segn la opcin seleccionada haremos algo como esto: ' Private Sub mnuEditor_Click(Index As Integer) Select Case Index Case 0 ' Deshacer Case 2

341

' Cortar ' Etc... ' End Select End Sub Pero para que resulte ms fcilmente entendible y modificable, en lugar de nmeros, vamos a usar constantes, de esta forma, si aadimos o eliminamos alguna de las opciones del men, slo tendremos que cambiar el valor de la constante y el resto del cdigo no habr que modificarlo. As pues, en la ventana de cdigo, selecciona la parte general de las declaraciones y aade esto: ' ' Constantes para el men de Edicin. ' Los valores se corresponden con el ndice de mnuEditor Const cEdDeshacer = 0 Const cEdCortar = 2 Const cEdCopiar = 3 Const cEdPegar = 4 Const cEdSeleccionarTodo = 6 Ahora las distintas opciones de "select" en el evento mnuEditor_Click quedarn as: ' Private Sub mnuEditor_Click(Index As Integer) Select Case Index Case cEdDeshacer ' Case cEdCortar ' Case cEdCopiar ' Case cEdPegar ' Case cEdSeleccionarTodo ' End Select End Sub

342

Con lo cual hemos ganado en "legibilidad" en el cdigo y, aunque an no lo "sepas", en facilidad a la hora de modificar el cdigo. El cdigo a usar ser el contenido de la siguiente entrega, ya que esta se acaba aqu. Adems del cdigo a usar, veremos cmo aadirle, a las opciones del men, teclas de acceso rpido, por ejemplo: Control + X para cortar, etc. Pero eso ser en nuestro siguientes episodio. Permanezca atento a la pantalla continuar. Nos vemos Guillermo

Curso Bsico de Programacin en Visual Basic


Te extraa ver otra entrega tan pronto? Aprovecha y no te quejes... que ya llegarn nuevas "lagunas"; aunque puede que no tantas... depende... o depender del tiempo, ganas, inspiracin y hasta motivacin... que hasta para escribir entregas sobre el Visual Basic hay que estar motivado e inspirado... que no es tan sencillo esto de ponerse a escribir cuatro chorradillas sobre un lenguaje... y sino, que se lo digan al Guille... En serio, esto de explicar, en la medida de lo posible, las cosas de forma que hasta se entienda, es algunas veces complicado... aunque, dicho sea de paso, algunas veces no lo consigo... pero... eso es lo que hay, y como por ahora no recibo muchas quejas, pues... me imagino que la cosa queda ms o menos clara... aunque se que no todo lo claro que a algunos le gustara... Quiero aclarar un par de cosillas, para que no te entre la desesperacin si no das pie con bola... Desde hace algunas entregas, la versin de VB que hay que usar es como mnimo la 4, (preferiblemente de 32 bits), aunque cada vez voy dirigindome ms a la versin 5, giro que sobre todo se notar cuando empecemos con el tema de los mdulos de clases y esas cosillas orientadas a objetos; as que si tienes un VB anterior a la versin 5, ve plantendote el conseguir una nueva versin... o te perders algunas cosillas interesantes. El que avisa... Como viene siendo habitual desde hace un par de entregas, esta tambin est "tecleada" por la "gena" de Encarni, que ya ha terminado con los manuscritos o garabatos que le di... as que, si tardo en publicarlas, la culpa ser slo ma... Hablando de publicar... de vez en cuando recibo una peticin de "autorizacin" para usar estas entregas en diferentes sitios, tanto de Internet como para "revistas" de colegios y esas cosillas, gracias a los que lo hacis y lo nico que pido es que se "respete" el

343

contenido, es decir TODO el contenido, incluido los "desvaros" y chorradillas, como estas, que escribo... si me entero de que no lo hacen... me voy a enfadar. Ya est bien de tantas chorradas, as que vamos a seguir con los mens que el tiempo apremia.

Antes de ver el cdigo "operativo" de las opciones que aadimos en la entrega anterior, vamos a seguir aprendiendo cosas de los mens, para tener los conceptos ms claros, que al fin y al cabo es lo que interesa.

Mens que contienen mens

Aunque en nuestro pequeo editor no lo vamos a usar, veremos cmo se crean opciones de mens que a su vez muestran otros mens. Para ello vamos a crear otra opcin en el men ficheros para poder imprimir, pero que nos mostrar un par de opciones: configurar la impresora adems de la opcin imprimir. El aspecto de este men sera el siguiente:

Para poder conseguir un men dentro de otro men no hay que hacer nada especial simplemente usaremos lo que hasta ahora hemos visto: indentar opciones. Ya viste que cuando queramos mostrar las opciones de uno de los mens principales, simplemente "desplazbamos" las siguientes opciones hacia la derecha pues esto mismo es lo que hay que hacer desplazar las nuevas opciones y el Visual Basic sabr que tiene que mostrarlas en otro men. Por tanto, cada vez que necesitamos mostrar un men al hacer "click" en una de las opciones de cualquier men, desplazaremos esas opciones hacia la derecha. Vamos a verlo de forma prctica: Muestra el formulario, haz que se muestre el "diseador de mens", posicinate en la opcin "Salir", pulsa en el botn insertar y escribe en la descripcin, (o caption), &Imprimir, en el nombre del men escribe mnuFicImp, pulsa en siguiente para que acepte lo que hemos escrito, para insertar una nueva lnea, pulsa en el botn insertar y escribe en el caption: &Selecionar impresora y mnuFicImpSelec en el nombre del

344

men; antes de pulsar en siguiente, dale al botn con la flecha hacia la derecha, en esta ocasin tendremos seis puntos suspensivos delante de esta opcin, (me refiero a la lista de abajo), esto indicar que est dos niveles hacia la derecha, es decir que tenemos un men dentro de otro men. Ahora pulsa en los botones siguiente e insertar y escribe &Imprimir y mnuFicImpImp, ya sabes dnde, para que est en el mismo nivel que Seleccionar Impresora, tendrs que pulsar en la flecha que seala a la derecha... para que est en el mismo nivel de mens. Si por casualidad ves que salen ms puntos suspensivos de la cuenta... pulsa en el botn con la flecha a la izquierda para quitar la indentacin que le hayas dado de ms. Cierra el diseador de mens. Muestra el formulario, si pulsas en el men Ficheros y a continuacin en Imprimir..., vers que tenemos un nuevo men, en este caso con las dos opciones que hemos aadido, (ver la figura anterior). As de fcil se crean los sub-mens o mens que se muestran al seleccionar una opcin de un men.

Nota: Cuando tienes opciones ya creadas y quieres insertar nuevas opciones, hay que usar el botn Insertar, pero la "indentacin" que muestra es la misma que tiene el men en el que nos posicionamos antes de insertar, por tanto tendrs que usar las flechas de indentacin para ajustar los niveles de mens.

Mostrar mens emergentes (popupmens)


Antes de ver el cdigo para esta utilidad, que realmente es lo de mens, ya que la intencin de la misma es ver cmo crear y manejar mens, vamos a ver cmo hace que se muestre un men emergente: de esos que se suelen mostrar al pulsar el botn derecho del ratn. Si ests usando Windows 95 o superior, habrs notado que las cajas de texto ya incluyen las opciones habituales de edicin, nosotros no tendremos necesidad de codificar nada para tener disponibles esa caracterstica. Prubalo. Ejecuta con F5 el programa y pulsa en el textbox, vers que se muestra un men emergente. Pero ese men es el del sistema, si queremos que se muestre el nuestro habr que escribir un par de lneas de cdigo. Dnde? En el evento MouseDown del textbox, por tanto escribe esto:

Private Sub txtEditor_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single) ' Si pulsamos el botn derecho... (vbRightButton = 2) If Button = vbRightButton Then ' Mostrar el men de Edicin PopupMenu mnuEdit

345

' Si queremos mostrar en negrita uno de los mens, ' lo indicaremos en el quinto parmetro 'PopupMenu mnuEdit, , , , mnuEditor(cEdCopiar) End If End Sub Ya vimos que uno de los parmetros del evento MouseDown estaba el de saber que botn se est pulsando, por tanto usaremos ese parmetro, que es Button, para saber si es el botn derecho, (vbRightButton que tiene un valor igual a 2), y si es as, mostramos nuestro propio men. Para ello usamos la instruccin PopupMenu, en el primer parmetro indicamos el nombre del men que contiene las opciones que queremos mostrar, del resto de los parmetros, en principio slo nos podra interesar el ltimo, que indica que men debe mostrarse seleccionado. En nuestro caso el men a mostrar es mnuEdit que es del que cuelgan las opciones de este men. Con PopupMenu se puede usar cualquier men, incluso si no est visible. Esa caracterstica se suele usar cuando queremos mostrar mens en nuestra aplicacin pero queremos que se muestren solamente como mens emergentes, en otra ocasin veremos esto. Una vez que un men emergente se ha mostrado funciona de igual manera que si no fuese emergente se que es "lgico", pero lo aclaro por si las moscas es decir cuando se selecciona una opcin, se ejecuta el cdigo que hayamos escrito en el evento click de esa opcin. Un detalle, puede que al pulsar el botn derecho en el textbox, no se muestre el men emergente, casi con toda seguridad tendrs que pulsarlo por segunda vez, si no recuerdo mal, esto no ocurra con el VB4...

Teclas de acceso rpido en los mens.


Ya te coment que se pueden asignar teclas de acceso rpido a las opciones del men, no todas, pero casi por ejemplo, no se pueden asignar F10 ni Alt+F4, pero eso no es inconveniente, ya que se pueden detectar de otra forma, aunque ahora mismo no es lo que nos interesa Cmo se aaden estos accesos rpidos? Pues con el diseador de mens aunque tambin se puede hacer mediante cdigo. Pero vamos a usar el "diseador". A estas alturas ya debes saber cmo mostrar ese dialogo verdad? Pues mustralo y vamos a aadir inicialmente ese tipo de accesos a las opciones del men de edicin. Selecciona la opcin "Cortar", en el cuadro de dilogo hay una lista con las teclas que podemos usar, (en ingls es Shorcut), selecciona Ctrl+X de la lista desplegable. Haz lo propio con Copiar (Ctrl+C), Pegar (Ctrl+V) y Seleccionar todo (Ctrl+A), cierra el cuadro de dilogo y pulsa en el men edicin, vers que se han aadido al Caption esas teclas, pero

346

no slo estn "mostradas", sino que si ejecutas el programa vers que estn operativas y que no es necesario hacer nada extra... simplemente pulsar esas combinaciones de teclas y se ejecutar el cdigo asociado a esa opcin del men.

Para comprobarlo, vamos a codificar la opcin de seleccionar todo: Muestra el form y selecciona el men edicin, pulsa en cualquiera de las opciones y se mostrar la ventana de cdigo, (tambin podras haberla seleccionado directamente, pero as parece que ibas a hacer otra cosa, je, je ), el cdigo en este evento ser el siguiente: Private Sub mnuEditor_Click(Index As Integer) ' Cuando se selecciona un elemento del men Edicin ' se entra en este evento, el ndice nos indicar ' el elemento seleccionado. ' Existen unas constantes para usarlas en lugar del nmero, ' por si aadimos o quitamos algunos Select Case Index Case cEdDeshacer ' Case cEdCortar ' Case cEdCopiar ' Case cEdPegar '

347

Case cEdSeleccionarTodo With txtEditor .SelStart = 0 .SelLength = Len(.Text) End With Case cEdBuscar ' Case cEdBuscarSig ' Case cEdReemplazar ' End Select End Sub Ejecuta la aplicacin y escribe varias lneas en el texbox, pulsa la tecla control y sin soltarla pulsa la tecla a, (esto como habrs observado en otras ocasiones, se simplifica diciendo: pulsa Ctrl+A), y ya est! todo el texto seleccionado!. Por supuesto que tambin puedes ir al men edicin y clickear en la opcin seleccionar todo. Te explico un poco el cdigo que hace que seleccione todo el texto: Le indicamos que la posicin de inicio del texto seleccionado sea la primera posicin: .SelStart = 0 Le indicamos que el texto seleccionado sea de igual longitud que todo el texto que hay escrito: .SelLength = Len(.Text) Si no hubisemos usado el With txtEditor... End With, tendramos que haber especificado el nombre del objeto, el cdigo sera este otro: ' Case cEdSeleccionarTodo txtEditor.SelStart = 0 txtEditor.SelLength = Len(txtEditor)

Ahora vamos a codificar un par de opciones del men edicin, lo vamos a hacer con cdigo en Visual Basic; de eso se trata no?, aunque podramos apoyarnos en llamadas al API de Windows, es decir: usar funciones propias del Windows para hacerlo, pero por ahora lo vamos a dejar, entre otras cosas para que sepas como funciona y sobre todo como se accede al portapapeles, (ClipBoard), desde el Visual Basic.

Como acceder al portapapeles (ClipBoard)


348

Las opciones que estarn relacionadas con el portapapeles son: Cortar, Copiar y Pegar; antes de codificar estas opciones en el evento mnuEditor_Click, veamos cmo manipular el clipboard. Los mtodos que vamos a usar de este objeto son: Clear para borrar el contenido del portapapeles GetText para recuperar el texto que haya SetText para asignar un texto GetFormat, para saber si el tipo de formato est disponible en el portapapeles los tipos de formatos se averiguan con las constantes predefinidas en el VB5: vbCFLink vnculo DDE vbCFText formato texto, el que a nosotros nos interesa vbCFBitmat formato bmp vbCFMetafile formato wmf (metafile) vbCFDib mapa de bits independientes del dispositivo, habitualmente todos los grficos soportan este formato vbCFPalette paleta de colores vbCFRtf formato RTF (rich text) GetData obtener los datos del portapapeles SetData Asignar datos al portapapeles Por ahora vamos a trabajar con los cuatro primeros mtodos: Con GetFormat, sabremos si hay algn texto en el portapapeles, en caso de que as sea, al mostrar el men, habilitaremos la opcin Pegar y si no hay texto la deshabilitaremos. De igual manera habilitaremos o no las opciones de Cortar y Copiar, pero en este caso comprobaremos si hay texto seleccionado Dnde se hace esto? Todas estas comprobaciones se hacen en el evento click del men edicin, (del que "cuelgan" las otras opciones), ya que este evento se dispara cuando pulsamos en l y es entonces cuando de muestran las opciones. Private Sub mnuEdit_Click() ' Por defecto, las opciones estn deshabilitadas mnuEditor(cEdCortar).Enabled = False

349

mnuEditor(cEdCopiar).Enabled = False mnuEditor(cEdPegar).Enabled = False ' Comprobamos si hay texto en el portapapeles If Clipboard.GetFormat(vbCFText) Then ' Hay texto, habilitamos la opcin de pegar mnuEditor(cEdPegar).Enabled = True End If ' Si hay texto seleccionado, habilitamos Cortar y Copiar If txtEditor.SelLength Then mnuEditor(cEdCortar).Enabled = True mnuEditor(cEdCopiar).Enabled = True End If End Sub Cuando muestres el men de edicin, seguramente "notars" cmo los item cambian de estado pero eso es lo menos importante, ya que lo que interesa es que estn habilitados los que tengan que estarlo. Ahora que hemos habilitado y/o deshabilitado las distintas opciones de edicin, vamos a usarlas. Segn vimos, el men de edicin est en un array y por tanto comparten el mismo nombre de men y segn el ndice, har referencia a uno u otro, para acceder a las distintas opciones, usamos constantes... ' '... Case cEdCortar ' Copiamos el texto seleccionado en el portapapeles Clipboard.SetText txtEditor.SelText ' y lo quitamos del textbox txtEditor.SelText = "" Case cEdCopiar ' Simplemenente copiamos el texto seleccionado en el portapapeles Clipboard.SetText txtEditor.SelText Case cEdPegar ' Ponemos en el textbox el texto que haya en el portapapeles txtEditor.SelText = Clipboard.GetText()

350

'... Para el caso de deshacer, podemos usar el API de Windows, realmente para todas estas opciones tambin, e incluso no necesitamos ni siquiera codificar nada, ya que el propio Windows se encarga de hacerlo pero lo pongo aqu para que sepas hacerlo ya que puedes aadir otras opciones, como buscar, reemplazar, etc.

Usar el API, para deshacer


La funcin del API para este menester es: SendMessage, la declaracin es esta: (aunque pueden existir otras con diferentes parmetros) Nota: Esta declaracin y el valor de las constantes que voy a dar son para Windows de 32 bits, si quieres saber los valores y la declaracin para 16 bits, en la seccin API de mis pginas lo tienes... (esto es por si ests leyendo esta entrega desde otro sitio distinto al de mis pginas) Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" _ (ByVal hWnd As Long, ByVal wMsg As Long, _ ByVal wParam As Long, lParam As Any) As Long Ahora necesitamos unas constantes para las distintas "tareas": Para saber si se puede deshacer: ' Private Const EM_CANUNDO = &HC6 Private Const EM_UNDO = &HC7

Y para el resto de las opciones que hemos visto sera de esta forma: Private Const WM_CUT = &H300 Private Const WM_COPY = &H301 Private Const WM_PASTE = &H302 Private Const WM_CLEAR = &H303 Private Const WM_UNDO = &H304 Para saber si se puede deshacer:

351

If SendMessage(txtEditor.hWnd, EM_CANUNDO, 0&, ByVal 0&) Then mnuEditor(cEdDeshacer).Enabled = True End If Fjate que el ltimo parmetro tiene "antepuesto" al valor CERO la palabra ByVal, esto es porque en la declaracin de la funcin se ha especificado como As Any. Para deshacer, tambin usamos la misma funcin del API, pero en este caso le indicamos que "deshaga": ' Case cEdDeshacer Call SendMessage(txtEditor.hWnd, WM_UNDO, 0, ByVal 0&)

Usar el API para Cortar, Copiar y Pegar: Para los casos de Cortar, Copiar y Pegar, se usaran los "mensajes" apropiados, es decir: WM_CUT, WM_COPY y WM_PASTE. En todos los casos, se usa el hWnd del txtEditor para indicarle a la funcin de Windows, que debe operar sobre esa "ventana", por tanto todas las operaciones se realizarn en la ventana de la cual indicamos el "manejador" de ventanas (handle), que el VB nos proporciona mediante la propiedad de slo lectura hWnd. Todos los controles que "actan" como ventanas, tienen la propiedad hWnd. Como puedes comprobar no tiene nada extrao ni raro, es decir que esto del API de Windows es como todo: para saber lo que hay que hacer hay que saber cmo hacerlo y esa es la intencin de este cursillo, intentar ensearte a hacer cosas con el Visual Basic... e incluso con cosas que no es Visual Basic propiamente dicho... Vamos a ponerlo todo junto y veamos el cdigo que tenemos hasta ahora en el formulario: ' '----------------------------------------------------------------------------' Editor para el curso bsico (19/Oct/98) ' Entrega 29, publicada el 25/Abr/99 ' Entrega 30, publicada el 02/May/99 ' ' Guillermo 'guille' Som, 1998-99 '----------------------------------------------------------------------------Option Explicit

352

' Constantes para el men de Edicin. ' Los valores se corresponden con el ndice de mnuEditor Const cEdDeshacer = 0 Const cEdCortar = 2 Const cEdCopiar = 3 Const cEdPegar = 4 Const cEdSeleccionarTodo = 6 Const cEdBuscar = 8 Const cEdBuscarSig = 9 Const cEdReemplazar = 10 ' Funcin del API de Windows de 32 bits de mltiple uso Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" _ (ByVal hWnd As Long, ByVal wMsg As Long, _ ByVal wParam As Long, lParam As Any) As Long ' Constantes para saber si se puede deshacer y deshacer Private Const EM_CANUNDO = &HC6 Private Const EM_UNDO = &HC7 ' Las constantes para cortar, copiar, pegar, etc Private Const WM_CUT = &H300 Private Const WM_COPY = &H301 Private Const WM_PASTE = &H302 Private Const WM_CLEAR = &H303 Private Const WM_UNDO = &H304 Private Sub Form_Resize() '-----------------------------------------------------------------'NOTA: ' ' ScaleWidth y ScaleHeight devuelven el tamao "interno" del form o control.

' Width y Height devuelven el tamao externo del form o control. '------------------------------------------------------------------

353

' Slo cuando no est minimizado el formulario If WindowState <> vbMinimized Then ' Si esto no funciona, que de seguro no funcionar, ' tendrs que ponerlo en el picStatus_Resize 'lblStatus.Width = picStatus.ScaleWidth - 60 ' Ajustar el tamao del TextBox 'txtEditor.Width = ScaleWidth ' Ajustar el alto, hay que tener en cuenta el alto ' del picture 'txtEditor.Height = ScaleHeight - picStatus.Height ' Posicionar el textBox en la parte superior izquierda 'txtEditor.Move 0, 0 ' Esto es lo mismo que lo anterior txtEditor.Move 0, 0, ScaleWidth, ScaleHeight picStatus.Height End If End Sub Private Sub mnuEdit_Click() ' Por defecto, las opciones estn deshabilitadas mnuEditor(cEdDeshacer).Enabled = False mnuEditor(cEdCortar).Enabled = False mnuEditor(cEdCopiar).Enabled = False mnuEditor(cEdPegar).Enabled = False ' Para saber si se puede deshacer: If SendMessage(txtEditor.hWnd, EM_CANUNDO, 0&, ByVal 0&) Then mnuEditor(cEdDeshacer).Enabled = True End If ' Comprobamos si hay texto en el portapapeles If Clipboard.GetFormat(vbCFText) Then ' Hay texto, habilitamos la opcin de pegar mnuEditor(cEdPegar).Enabled = True End If ' Si hay texto seleccionado, habilitamos Cortar y Copiar

354

If txtEditor.SelLength Then mnuEditor(cEdCortar).Enabled = True mnuEditor(cEdCopiar).Enabled = True End If End Sub Private Sub mnuEditor_Click(Index As Integer) ' Cuando se selecciona un elemento del men Edicin ' se entra en este evento, el ndice nos indicar ' el elemento seleccionado. ' Existen unas constantes para usarlas en lugar del nmero, ' por si aadimos o quitamos algunos Select Case Index Case cEdDeshacer Call SendMessage(txtEditor.hWnd, WM_UNDO, 0, ByVal 0&) Case cEdCortar ' Copiamos el texto seleccionado en el portapapeles Clipboard.SetText txtEditor.SelText ' y lo quitamos del textbox txtEditor.SelText = "" ' Si usamos el API: 'Call SendMessage(txtEditor.hWnd, WM_CUT, 0, ByVal 0&) Case cEdCopiar ' Simplemenente copiamos el texto seleccionado en el portapapeles Clipboard.SetText txtEditor.SelText ' Si usamos el API: 'Call SendMessage(txtEditor.hWnd, WM_COPY, 0, ByVal 0&) Case cEdPegar ' Ponemos en el textbox el texto que haya en el portapapeles txtEditor.SelText = Clipboard.GetText() ' Si usamos el API: 'Call SendMessage(txtEditor.hWnd, WM_PASTE, 0, ByVal 0&)

355

Case cEdSeleccionarTodo txtEditor.SelLength = txtEditor.SelLength = Len(txtEditor) With txtEditor .SelStart = 0 .SelLength = Len(.Text) End With End Select End Sub Private Sub mnuFicSalir_Click() ' Terminar el programa Unload Me End Sub Private Sub picStatus_Resize() ' Slo cuando no est minimizado el formulario If WindowState <> vbMinimized Then ' Aqu se ajustar el tamao del label ' cuando cambie el del picStatus lblStatus.Width = picStatus.ScaleWidth - 60 End If End Sub Private Sub txtEditor_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single) ' Si pulsamos el botn derecho... (vbRightButton = 2) If Button = vbRightButton Then ' Mostrar el men de Edicin PopupMenu mnuEdit ' Si queremos mostrar en negrita uno de los mens, ' lo indicaremos en el quinto parmetro 'PopupMenu mnuEdit, , , , mnuEditor(cEdCopiar) End If End Sub Ya slo nos queda codificar las opciones de Abrir y Guardar, que veremos en la siguiente entrega y algunas opciones como buscar, reemplazar, que no se si veremos en la siguiente o en otra... Nos vemos Guillermo

356

Nerja, 14/Ene/99 21:10 (el original)

Curso Bsico de Programacin en Visual Basic


Mens (el resto) y los dilogos comunes
Nos quedamos pendientes de codificar las opciones del men de ficheros. Las opciones que tenemos son: Nuevo --Abrir... Guardar Guardar como... --Imprimir... Seleccionar impresora... Imprimir --Salir

Pero vamos a aadir otra: Mezclar Esta opcin insrtala entre abrir y guardar, el nombre del men es mnuFicMezclar y la letra M es la que debe estar subrayada (escribe &Mezclar en el caption) Para que sirve mezclar? Para intercalar otro fichero en el texto actual; ahora vamos a ver qu necesitamos para escribir el cdigo de estas opciones. Cuando se pulsa Abrir, (o en cualquiera de las opciones en las que se necesite el nombre de un fichero), lo que el usuario espera es que se le pregunte el fichero a editar. Se puede hacer de forma simple, usando el InputBox o un formulario de nuestra cosecha que simplemente pregunte un nombre de fichero, pero eso no es lo que estamos acostumbrados a ver lo que necesitamos es poder mostrar un:

Cuadro de dilogos comunes


Es decir, un cuadro de dilogo, (no se porqu lo llaman de dilogo, ya que no habla), con el que poder seleccionar un fichero de cualquier directorio o unidad de disco. El VB incluye un control para hacer esto. Pero para poder usarlo, primero tiene que estar disponible. Para ello, en el men proyecto, selecciona componentes y te mostrar un cuadro de

357

dilogo con los controles que tenemos registrados en el sistema, selecciona: Microsoft Common Dialog Control (ComDlg32.ocx, control de dilogos comunes) y pulsa aceptar. En la barra de herramientas, (la que est a la izquierda), vers que hay un icono como este: Muestra el formulario y haz doble clic en ese icono, y se insertar en el formulario, desde este momento ya est disponible, el nombre por defecto de este control es: CommonDialog1, por tanto para acceder a las propiedades y mtodos tendremos que usar, como es costumbre en todos los controles, ese nombre seguido de un punto y despus la propiedad o mtodo. Por ejemplo para abrir: With CommonDialog1 .DialogTitle = "Seleccionar un fichero para abrirlo" .ShowOpen NombreFichero = .FileName End With Ahora que "sabemos" cmo se usa este control, vamos a usarlo "correctamente" y no es porque lo otra forma no sea correcta, que lo es, me refiero a que nos sea "til" en nuestro proyecto. Bien, estamos dispuestos a entrar de lleno en lo que es la codificacin del programa; ya hemos visto otros "listados", pero este es algo ms complicado... aunque no tiene porqu serlo... Revisemos lo que tenemos y, sobre todo, que utilidad tendr todo este tinglado, s que despus de dos entregas ya se tendra que saber pero... -La intencin es crear un formulario con una serie de mens, una caja de texto en la que poder escribir y poder manipular el texto escrito, pudiendo guardarlo en el fichero de texto y permitirnos asignar el texto contenido en cualquier otro fichero, adems de realizar ciertas tareas de "edicin" en el texto contenido en esa caja de texto. Pero nos falta un pequeo detalle: cmo saber el nombre del fichero con el que queremos "nombrar" lo que hemos escrito o como saber el nombre del fichero que usamos para asignarlo al textbox, es decir necesitamos una variable para guardar un nombre de fichero. Ahora hay otro pequeo detalle: dnde necesitamos usarla? Me explico: necesitamos una variable local a un solo procedimiento? o necesitamos una variable que pueda ser "vista" en todos los procedimientos? Recuerdas lo dicho en la entrega 26? Creo que nuestra variable para almacenar el nombre del fichero debe tener una "cobertura" a nivel de mdulo, ya que se usar tanto en los procedimientos de abrir como en los de guardar. Por tanto declrala en la seccin general del formulario:

358

Private NombreFichero As String Y ahora te tendra que seguir mostrando cdigo, pero, creo que parte de lo que hay que hacer ya lo hemos hecho... voy a comprobarlo y te lo digo... vuelvo enseguida! Si se ha hecho, en la entrega quince para ms seas, as que solo te dir "lo nuevo", pero el resto tendrs que hacerlo por tu cuenta, aunque te dir que es lo que debes hacer y dnde; de esta forma tendrs ejercicios obligatorios... si no los resuelves, no tienes programa, je, je. Antes de ver el cdigo de este mini-editor, vamos a ver un nuevo sistema de nombrar constantes: Enum Nota: esto slo existe en la versin 5 o superior. La enumeracin se usa para constantes de forma correlativa, aunque eso puede cambiarse. Por ejemplo: Enum eEdicion Deshacer Sep1 Cortar Copiar End Enum Segn esto, Deshacer vale 0, Sep1=1, Cortar=2, etc. Osea el primer nombre de constante valdr cero y las dems irn incrementando el valor de uno en uno. Si no queremos que se comporte de esta forma, es decir que empiece con cero, podemos asignar el valor que queramos: Enum ePrueba Primero = -1 Segundo Tercero End Enum Ahora los valores son: Primero= -1, Segundo= 0, Tercero= 1 Tambin podemos asignar los que queramos:

359

Enum ePrueba2 Uno = 1 Dos Tres Nulo = 0 Cuatro = 4 Cinco End Enum Fjate que al indicarle Uno=1, se empieza por uno y el siguiente valor ser dos, etc., hasta que volvamos a asignar un nuevo valor, en este caso Nulo=0, Cuatro vale 4 y los siguientes, si no se le indica lo contrario, van aumentando de uno en uno. Otra cosa que se suele hacer con esto de las enumeraciones, siempre que los valores asignados sean correlativos, es indicar cual es el valor menor y cual el mayor; as se pueden usar en ciertos bucles y para ciertas comprobaciones en las que necesitemos que un valor determinado est entre los dos valores indicados: Enum eEdicion edPrimero edDeshacer = edPrimero edSep1 edCortar edCopiar edPegar edSep2 edSeleccionarTodo edSep3 edBuscar edBuscarSig edReemplazar edUltimo = edReemplazar End Enum En este caso, se asignan dos constantes: edPrimero y edUltimo para indicar los valores mnimo y mximo del rango de las constantes. Recuerda que cada vez que se asigna un valor a una constante de una enumeracin, si en la siguiente no se le indica explcitamente un valor, se incrementa el valor anterior en uno. Por tanto, edPrimero y edDeshacer valdrn cero, edSep1 vale 1, edCortar vale dos y as sucesivamente. Otra cosa a saber sobre las enumeraciones, es que los valores siempre son numricos del tipo long. '5 '2 '3

360

Cuando creamos una enumeracin, estamos creando un nuevo tipo de variable... o casi, lo importante es que podemos crear variables de la enumeracin: Dim miVarEnum As ePrueba Al asignar un valor a una variable declarada de esta forma, el Visual Basic nos indicar los nombres (o valores posibles) Por ejemplo:

es decir, nos muestra una lista despegable con los valores posibles. Tambin podemos usar esos nombres de forma independiente, sin necesidad de crear una variable especial: Dim x As Long x = Primero Es decir, se asignar a la variable x lo que vale la constante Primero (-1) Un dato importante: no deberas usar el mismo nombre de constante en diferentes enumeraciones, ya que el Visual Basic podra confundirse... aunque te avisar de que hay un "conflicto". Si tenemos estas dos enumeraciones: Enum ePrueba Primero = -1 Segundo Tercero End Enum Enum ePrueba1 Primero = -1 Otro = Primero Siguiente Ultimo = Siguiente End Enum

361

Y asignamos x = Primero, nuestro querido Visual Basic nos dir que se ha detectado un nombre ambiguo, tal como nos muestra este mensaje:

Para solucionarlo podemos hacer dos cosas: ---Usar nombres diferentes en las constantes enumeradas, (lo ms recomendable). ---Indicar el nombre de la enumeracin: x = ePrueba1.Primero Para terminar esto, decirte que puedes usar nombres de constantes con espacios, aunque esto es algo que est bien cuando creamos "controles", para que los nombres de las constantes de las propiedades sean descriptivas: Enum ePrueba3 [Fecha Actual] [Fecha Anterior] End Enum Fjate en el uso de corchetes. Cuando se usan estos valores, el propio Visual Basic se encarga de asignarlo con los corchetes: unaFecha = [Fecha Actual] Y si los asignamos nosotros directamente, hay que poner el nombre de la "constante" dentro de los corchetes, sino el Visual Basic entender que son dos nombres diferentes sin ningn tipo de operacin de por medio... lo cual se convertir en un "bonito" error... El nico "defectillo" que tienen las constantes de las enumeraciones, es que los nombres usados no mantienen el estado de maysculas / minsculas, es decir que si declaramos Primero en un Enum y despus escribimos ese nombre en minsculas se cambiar la declaracin a minsculas: x = primero Pero an as, si tenemos Option Explicit podemos detectar valores no "creados"; aunque para mi gusto, se debera "respetar" el estado de maysculas/minsculas de la declaracin.

362

Un ltimo comentario y ya termino..., al igual que ocurre con los procedimientos en los que no se indica "explcitamente" que es privado, sern pblicos por defecto. As que, si no tienes intencin de hacer pblicas las enumeraciones, declralas como Private. Ahora veamos parte del cdigo nuevo, el resto ser el que se mostr en la entrega anterior. Los valores de las constantes para el men de edicin las vamos a tener en una enumeracin. Por tanto si la versin que ests usando es anterior a la 5, tendrs que declararlas como constantes normales, (tal y como se mostr en la entrega 30). En la parte de las declaraciones del formulario: ' Constantes para el men de Edicin. ' Los valores se corresponden con el ndice de mnuEditor Private Enum eEdicion cedPrimero cedDeshacer = cedPrimero cedSep1 cedCortar cedCopiar cedPegar cedSep2 cedSeleccionarTodo cedSep3 cedBuscar cedBuscarSig cedReemplazar cedUltimo = cedReemplazar End Enum Private NombreFichero As String Private Modificado As Boolean el texto ' Nombre del fichero ' Para indicar si se ha cambiado

Al iniciarse el formulario asignaremos los filtros para la seleccin del tipo de ficheros en el cuadro de dilogo. El filtro se asigna a la propiedad Filter del CommonDialog y el formato a usar ser: descripcin del tipo, el tipo, descripcin, tipo, etc. cada uno de estos "datos" estar separado por el signo | (ALT+124) o Alt Gr y 1. Private Sub Form_Load() ' Asignar el filtro para el dilogo comn

363

CommonDialog1.Filter = "Textos (*.txt)|*.txt|Todos (*.*)|*.*" End Sub Cada vez que escribamos algo en el textbox habr que indicarle a nuestro programa de que se ha cambiado, por tanto asignaremos True a la variable Modificado: Private Sub txtEditor_Change() Modificado = True End Sub Ahora veremos el cdigo de algunas de las opciones del men de ficheros, el resto lo veremos en la siguiente entrega, junto con todo el cdigo... Private Sub mnuFicAbrir_Click() ' Comprobar si el texto se ha modificado ' si es as, guardarlo o no, segn la respuesta del usuario ' ***ejercicio*** With CommonDialog1 .DialogTitle = "Seleccionar un fichero para abrilo" .FileName = NombreFichero .ShowOpen If Len(.FileName) Then NombreFichero = .FileName ' Abrir el fichero y asignarlo al textbox ' ***ejercicio*** Modificado = False End If End With End Sub

Private Sub mnuFicGuardar_Click() ' Si no se ha asignado el nombre al fichero, preguntar por l If Len(NombreFichero) = 0 Then mnuFicGuardarComo_Click

364

Else GuardarFichero End If End Sub

Private Sub mnuFicGuardarComo_Click() ' Preguntar el nombre del fichero y guardarlo With CommonDialog1 .DialogTitle = "Guardar el fichero" .FileName = NombreFichero .ShowSave If Len(.FileName) Then NombreFichero = .FileName GuardarFichero End If End With End Sub

Private Sub mnuFicMezclar_Click() ' Preguntar el nombre del fichero a mezclar With CommonDialog1 .DialogTitle = "Fichero a mezclar" .ShowOpen If Len(.FileName) Then ' Leer el fichero y guardarlo en una variable Dim nFic As Long Dim sMerge As String Dim sTmp1 As String, sTmp2 As String nFic = FreeFile Open .FileName For Input As nFic sMerge = input$(LOF(nFic), nFic) Close nFic ' Tomar lo que hay hasta la posicin del cursor With txtEditor

365

sTmp1 = Left$(.Text, .SelStart) sTmp2 = Mid$(.Text, .SelStart + 1) .Text = sTmp1 & sMerge & vbCrLf & sTmp2 End With End If End With End Sub

Private Sub mnuFicNuevo_Click() ' Comprobar si se ha modificado el fichero actual ' ***ejercicio*** txtEditor = "" Modificado = False End Sub

Private Sub GuardarFichero() ' Guardar el contenido del textbox en NombreFichero ' ***ejercicio*** Modificado = False End Sub Como te imaginars donde pone ***ejercicio*** es lo que tienes que hacer, ya sabes que en la entrega quince se hizo algo parecido. Por lo dems, nos queda la parte de: seleccionar impresora, imprimir, buscar y reemplazar, pero eso lo dejaremos para otra ocasin... aunque si no quieres esperar, psate por la seccin "Mis Utilidades" y busca la entrada que dice "Un procedimiento genrico para imprimir" En la prxima entrega tambin veremos algo sobre el control de errores... cosa que ser necesaria "controlar" para los casos en que se pulse "Cancelar" en el cuadro de dilogo de seleccin del nombre del fichero. As que, paciencia... y a esperar... si puedes... y si no puedes... pues eso... Hasta la prxima Nos vemos Guillermo

366

Curso Bsico de Programacin en Visual Basic


Que tal? Creas que ya no volvera por aqu verdad? pues te has vuelto a equivocar... espero que te cueste trabajo librarte de m... as que... a aguantar al Guille que an me queda cuerda "pa" rato... espero! En esta entrega terminaremos de ver el cdigo del Editor ese con el que llevamos varias entregas "enfangao", si no recuerdo mal, (mirando de reojo al final de la entrega anterior), lo que tenemos pendiente es: Usar deteccin de errores, al menos al acceder a los cuadros de dilogos, Seleccionar la impresora a usar e imprimir el contenido del texto que hemos escrito... adems de las "respuestas" a los ejercicios "camuflados"... ya veremos en que queda todo esto... (es que los ejercicios yo an no los he resuelto... je, je, en fin...) Empecemos por:

La deteccin de errores al usar los cuadros de dilogos


Cuando pulsas en el botn cancelar del cuadro de dilogo no hay forma de saber que se ha pulsado ese botn... pero lo lgico es hacer algo en particular si as ha sido... verdad? (si bwana), pues eso... vamos a ver cmo "detectar" que se ha pulsado en Cancelar... Para lograr esto, usaremos la instrucciones: On Error Resume Next Cuando Visual Basic se encuentra con esta instruccin continuar a pesar de que ocurran errores... de que sirve el que contine cuando se produce un error? La verdad es que de nada si no tenemos en cuenta ese "detalle", por tanto, si usamos esa instruccin, tendremos que ser consecuentes con nuestros actos... (Guille, eso te ha quedado en plan sermn... no se si lo aguantar...) La forma de saber que se ha producido un error es comprobarlo... cmo? usando la propiedad Number del objeto Err, o lo que es lo mismo la propiedad por defecto del objeto Err, por tanto podemos hacerlo de dos formas: If Err.Number Then... o If Err Then... El resultado es el mismo: si se ha producido un error se ejecutar el cdigo que est despus del Then...

367

Veamos esto que te estoy contando en el cdigo de abrir y de camino vemos la respuesta: ' Private Sub mnuFicAbrir_Click() ' Comprobar si el texto se ha modificado ' si es as, guardarlo o no, segn la respuesta del usuario Dim ret As Long If Modificado Then ret = MsgBox("El fichero se ha modificado, quieres guardarlo?", vbYesNoCancel) ' Si hemos contestado "Si" If ret = vbYes Then ' Guardarlo mnuFicGuardar_Click ' Si pulsamos el botn Cancelar, salimos del procedimiento ElseIf ret = vbCancel Then Exit Sub End If End If ' Usar deteccin de errores para saber si se ha pulsado en cancelar On Error Resume Next With CommonDialog1 ' Esto har que VB devuelva un error al pulsar Cancelar .CancelError = True ' .DialogTitle = "Seleccionar un fichero para abrilo" .FileName = NombreFichero .ShowOpen ' Si no se ha producido ningn error, ' es que NO se ha pulsado en Cancelar If Err.Number = 0 Then If Len(.FileName) Then NombreFichero = .FileName ' Abrir el fichero y asignarlo al textbox Dim nFic As Long

368

Dim sTmp As String nFic = FreeFile Open .FileName For Input As nFic sTmp = Input$(LOF(nFic), nFic) Close nFic ' Asignarlo al textbox txtEditor.Text = sTmp Modificado = False End If End If End With ' Es buena costumbre volver a ponerlo a cero... Err = 0 End Sub Como ves no tiene mayor problema comprobar si el contenido del fichero ha cambiado y preguntar si queremos guardarlo, etc. (me estoy refiriendo al ejercicio que haba al principio del procedimiento de abrir), en el procedimiento de Guardar se comprueba si hay algn nombre asignado y si no es as, se preguntar por ese nombre... de eso se encargan los procedimientos de Guardar y Guardar como... Para hacer la pregunta he usado un MsgBox, pero con tres opciones: Si, No y Cancelar... por si nos arrepentimos y no queremos guardar el contenido del textbox con el nombre por defecto, por ejemplo... En cuanto a la deteccin de errores... primero ponemos las instrucciones esas que vimos hace un rato... en el Cuadro de dilogos se asigna a True la propiedad .CancelError, con esto se le dice al Visual Basic que si se pulsa en Cancelar, produzca un error detectable, (el nmero 32755), por tanto, despus del .ShowOpen comprobamos si NO se ha producido un error, en ese caso quiere decir que se puede continuar, ya que si se produce un error, no hacemos nada y se contina con el resto del cdigo... que por cierto es ninguno, ya que el cdigo restante est dentro del If Then... salvo el volver a poner el nmero del objeto Err a cero, para que no se quede ningn nmero de error "colgado" y pueda interferir en otros procedimientos. Sigue este link y aprenders un poco ms sobre esto de la deteccin de errores.

En cuanto a la respuesta de cmo abrir el fichero y asignarlo al textbox, ya tenias la respuesta en la parte de mezclar... o casi, pero como ves es bastante simple y no hay que hacer nada del otro mundo. Simplemente abrimos el fichero, leemos el TOTAL del contenido del mismo y lo asignamos a una variable y despus lo asignamos al TextBox, aunque podramos haberlo asignado

369

directamente... pero es que tengo costumbre de usar variables intermedias... cosas... en fin... Fjate que despus de abrir el fichero se asigna el valor False a la variable Modificado, esto es para que se sepa que el contenido no se ha modificado despus de asignar el contenido al textbox... aunque creo que esto ya lo expliqu en otra ocasin... no? la verdad es que no me acuerdo, as que mejor son dos que ninguna... Veamos ahora:

Cmo seleccionar la impresora a usar por nuestra aplicacin.


Para hacer esto, usaremos tambin el Common Dialog que tenemos insertado en nuestro formulario: ' Private Sub mnuFicImpSelec_Click() ' Seleccionar la impresora a usar (23/Ene/00) ' La deteccin de errores es por si no hay impresora instalada On Error Resume Next With CommonDialog1 .DialogTitle = "Seleccionar impresora" .Flags = cdlPDPrintSetup .ShowPrinter End With Err = 0 End Sub El truco est en usar el valor cdlPDPrintSetup como valor para la propiedad Flags del cuadro de dilogo y despus, por supuesto, usar el mtodo ShowPrinter; cuando el control de dilogos comunes se encuentra con ese "flag" sabe que debe mostrar el cuadro de dilogo de configurar impresora, desde el cual podemos seleccionar otra de las impresoras instaladas en el sistema. Nota: Los valores a usar con Flags puedes averiguarlos pulsando F1 sobre la propiedad Flags, la ayuda te dar una lista de los valores posibles.

Ahora veamos:

Cmo imprimir el contenido de la caja de textos. 370

En esta ocasin tambin usaremos el Cuadro de Dilogo pero, para saber cuantas copias quiere el usuario imprimir, si la quiere en horizontal o vertical, etc., etc. (aunque esto ltimo se suele hacer al configurar la impresora, no al imprimir). ' Private Sub mnuFicImpImp_Click() ' Imprimir el contenido del TextBox en la impresora (23/Ene/00) On Error Resume Next ' Averiguamos cuantas copias quiere el usuario y dejamos que elija otras cosas, ' el propio cuadro de dilogo nos lo permitir hacer... With CommonDialog1 .CancelError = True .DialogTitle = "Imprimir" ' cdlPDHidePrintToFile archivo ' cdlPDNoPageNums etc. ' CdlPDUseDevModeCopies Dejar al SO que se encargue de imprimir las copias ' soporte, estar ' copias. .Flags = cdlPDHidePrintToFile Or cdlPDNoPageNums Or cdlPDUseDevModeCopies .ShowPrinter ' Si no se ha cancelado If Err = 0 Then imprimir. ' En la propiedad .Copies estar el nmero de copias a En caso de que la impresora no lo deshabilitada la opcin del nmero de No mostrar desde que pgina imprimir, No mostrar el botn de imprimir en un

' Hay casos en los que las impresoras "automticamente" usan ese valor, el nmero de ' por tanto si no queremos hacer un bucle para imprimir

' copias solicitadas, podemos dejar que sea el propio sistema el que ' se encargue de esa cuestin... ' El problema, cuando la impresora no permite imprimir varias copias

371

' Para simplificar, dejaremos que sea el propio O.S. el que se encargue ' ' Imprimimos el contenido del textbox... simple, verdad? Printer.Print "" Printer.Print txtEditor.Text Printer.EndDoc End If End With End Sub Tambin podramos hacerlo ms complicado, de forma que podamos controlar cada una de las lneas a imprimir... no es que tenga ninguna utilidad prctica, pero as sabes como controlar el contenido de cada una de las lneas de un TextBox Multiline, por ejemplo puedes usarlo para hacer que el texto est justificado, etc... aunque no se si esa parte la veremos en el curso bsico... ya veremos.

Cmo controlar cada lnea de un TextBox Multiline


Esta tarea est contenida en un procedimiento, el cual recibe como parmetro un TextBox Multiline el cual contendr el texto a imprimir; el hacerlo de esta forma, en lugar de usar directamente el control txtEditor, es para los casos en que necesitemos una rutina genrica o bien porque tengamos ms textboxes en nuestro proyecto... Para usarlo simplemente se hara: ImprimirPorLinea txtEditor Este es el contenido del mencionado procedimiento. ' Private Sub ImprimirPorLinea(qControl As TextBox) ' Este procedimiento tomar cada lnea de un textbox multiline (23/Ene/00) ' y lo imprimir en la impresora predeterminada ' ' El parmetro qControl, ser el TextBox a usar, en este caso no es necesario ' ya que slo tenemos un TextBox, pero si se usaran varios... ' sera un procedimiento de uso genrico... ' Dim i As Long, k As Long

372

Dim L1 As Long, L2 As Long ' Constantes para usar con SendMessage Const EM_GETLINECOUNT = &HBA Const EM_LINEFROMCHAR = &HC9 Const EM_LINELENGTH = &HC1 ' Nmero de lneas del TextBox k = SendMessage(qControl.hWnd, EM_GETLINECOUNT, 0, 0&) Printer.Print "" For i = 0 To k - 1 ' Primer carcter de la lnea actual L1 = SendMessage(qControl.hWnd, EM_LINEINDEX, i, 0&) + 1 ' Longitud de la lnea actual L2 = SendMessage(qControl.hWnd, EM_LINELENGTH, L1, 0&) ' Imprimimos el trozo de texto que representa a una lnea Printer.Print Mid$(qControl.Text, L1, L2) Next ' Le indicamos que ya no hay ms que imprimir Printer.EndDoc End Sub Esto es todo, al menos por ahora, aunque an queda por hacer la parte de Buscar y Reemplazar, pero eso lo dejaremos para otra ocasin, ya que si no se iba a alargar ms de la cuenta el tema y no es plan, como adelanto te dir que seguramente, lo programaremos usando clases, que ya va siendo hora de que entremos de lleno en ese mundo tan desconocido para unos y casi tan mgico para otros... o casi... an as, espero que no tengas queja... ya que esta entrega ha valido por dos... o ms... ahora, a esperar a la siguiente... paciencia, paciencia... Nos vemos Guillermo

Curso Bsico de Programacin en Visual Basic

Cmo detectar errores en Visual Basic?


373

Cuando quieras que el Visual Basic "ignore" los errores que se produzcan en tu aplicacin o en parte de ella, usa: On Error Resume Next Esto har que si se produce un error, se contine ejecutando el cdigo como si nada hubiese ocurrido. Por supuesto que la recomendacin es que compruebes si se ha producido un error, ya que no es bueno dejar que los errores ocurran sin ms. Para ello tendrs que chequear el valor de la propiedad Number del objeto Err, (que al ser la propiedad por defecto no es necesario especificarla), si ese valor es cero quiere decir que no se ha producido un error; veamos un ejemplo: On Error Resume Next ' Error 13 producir un error de tipos (Type Mismatch) Error 13 If Err.Number Then MsgBox "Se ha producido el siguiente error:" & vbCrLf & _ Err.Number & ", " & Err.Description End If Pero si haces esto, procura hacer un poco de limpieza... ya que, si desde este procedimiento llamas a otros procedimientos que a su vez tienen la instruccin On Error Resume Next y no has "limpiado" el valor del nmero del error... cualquier comprobacin que hagas de ese valor dar como resultado que se muestre el mensaje. Veamos un par de ejemplos: Para crear el programa de pueba, crea un nuevo proyecto, aade tresd botones (Command1, Command2 y Command3), y pega este cdigo: Private Sub Command1_Click() ' Ejemplo para detectar errores en Visual Basic Dim i As Integer On Error Resume Next i = MsgBox("Pulsa SI para producir un error en este evento," & vbCrLf & _ "pulsa en NO para llamar al procedimiento Command2_Click" & vbCrLf & _ "pulsa en Cancelar para llamar al procedimiento Command3_Click", vbYesNoCancel)

374

If i = vbYes Then ' Error 13 producir un error de tipos (Type Mismatch) Error 13 ElseIf i = vbNo Then ' El error producido en el procedimiento Command2 est controlado, ' por tanto no se mostrar el mensaje del final Command2_Click Else ' Esto producir un error en Command3, pero se detectar aqu Command3_Click End If If Err Then MsgBox "Se ha producido el siguiente error:" & vbCrLf & _ Command1_Click" End If End Sub Err.Number & ", " & Err.Description, , "En

Private Sub Command2_Click() On Error Resume Next ' Error 76, (Path not found) Error 76 If Err Then ' Este error est comprobado dentro de este procedimiento, por tanto no mostrar nada End If ' Limpiamos el valor del error Err = 0 End Sub

Private Sub Command3_Click()

375

' Este procedimiento produce un error nmero 5 Error 5 ' Este mensaje NUNCA se mostrar MsgBox "El valor de Err.Number es: " & Err.Number & vbCrLf & _ "Aqu no se notar que se ha producido un error..." & vbCrLf, , "En Command3_Click" End Sub Veamos que es lo que hace este cdigo y porqu. Cuando pulses en el Command1 te mostrar un mensaje pidiendote que selecciones el tipo de prueba que quieres hacer, para probar cada una de ellas, tendrs que pulsar varias veces en ese botn, una para cada una de las tres posibilidades. Si pulsas en "SI", el error se producir en este mismo evento y el mensaje del final nos indicar que se ha producido el error nmero 13. Cuando pulses en "NO", se llamar al procedimiento Command2_Click en el que se produce un error 76, pero que el propio procedimiento se encarga de gestionar y "limpiar", por tanto, no ocurrir, al menos aparentemente, nada. Por ltimo, al pulsar en "Cancelar", se llama al procedimiento Command3_Click, el cual produce el error 5, pero no detecta los errores; pero como el Visual Basic "sabe" que an hay una rutina "interceptadora" de errores en funcionamiento, la del Command1, deja de ejecutar el cdigo errneo y vuelve a la siguiente instruccin que haya en el procedimiento Command1... Despus de estas tres pruebas, pulsa en el Command2. Nada ocurre, ya que el cdigo detecta los posibles errores. Cuando pulses en el Command3, vers que el Visual Basic se detiene mostrandonos una ventana de error, esto ocurre porque no hay ninguna rutina de deteccin de errores en funcionamiento y cuando no la hay... el Visual Basic nos muestra la suya propia y detiene el programa. Ahora cambia el cdigo del Command3_Click por este otro: ' Private Sub Command3_Click() On Error Resume Next ' Este procedimiento produce un error nmero 5

376

Error 5 ' Ahora si que se mostrar este mensaje MsgBox "El valor de Err.Number es: " & Err.Number & vbCrLf & _ "Aqu no se notar que se ha producido un error..." & vbCrLf, , "En Command3_Click" End Sub Como vers, al no "limpiar" el valor de la propiedad Err.Number, el valor se mantiene; y a pesar de que se haya detectado el error en ese evento, al volver de nuevo al cdigo del Command1, se mostrar el mensaje de que hay error... y adems el mensaje que tenemos en el evento Command2_Click, el cual antes no se mostraba.

Resumiendo: Si detectas los errores con Resume Next, acostumbrate a dejar el valor de Err.Number a cero antes de que acabe y/o antes de salir del procedimiento. Recuerda que para salir de un procedimiento puedes usar Exit Sub, Exit Function o Exit Property. Tambin debes saber que, cuando acaba un procedimiento, la rutina que gestiona los errores tambin acaba, pero, como has podido comprobar, el valor del error permanece asignado.

Otras formas de detectar errores


Acabamos de ver la forma ms "recomendable" de detectar errores, ya que al hacerlo de esta manera, nos obligamos a chequear si se ha producido un error... aunque esta no es la nica, ya que existe otra forma: On Error Goto NmeroLnea Si Visual Basic se encuentra con esta instruccin y se produce un error, pasar a ejecutar el cdigo que est en la lnea indicada por NmeroLnea, (no tiene porqu ser un nmero, puede ser y de hecho es lo ms recomendable, una etiqueta), por tanto si se especifica ese formato, NmeroLnea debe existir... (ahora veremos un ejemplo) Antes de continuar, recordaros que la deteccin de errores se procesa en el ltimo procedimiento en el que se ha especificado una intruccin On Error... por tanto si hay algn procedimiento con una "deteccin" de errores activa y se produce un error en otro procedimiento, puede que nos encontremos que el error "parece" que no se produce en el procedimiento que "realmente" se ha producido... que lio verdad? Aclaremos este punto. Supongamos que tenemos el siguiente cdigo:

377

' Private Sub Command4_Click() ' Ejemplo para detectar errores en Visual Basic Dim i As Integer On Error Goto HayError i = MsgBox("Pulsa SI para producir un error en este evento," & vbCrLf & _ "pulsa en NO para llamar al procedimiento Command5_Click", vbYesNo) If i = vbYes Then ' Error 13 producir un error de tipos (Type Mismatch) Error 13 ElseIf i = vbNo Then ' Esto producir un error en Command5, pero se detectar aqu Command5_Click End If ' Es conveniente NO entrar en la rutina de deteccin de errores por "error" Exit Sub ' Etiqueta para cuando se produzca un error HayError: MsgBox "Se ha producido el siguiente error:" & vbCrLf & _ Err.Number & ", " & Err.Description, , "En Command4_Click" End Sub

Private Sub Command5_Click() ' En este procedimiento no hay rutina de deteccin de errores ' Este procedimiento produce un error nmero 5 Error 5 End Sub

Al pulsar en el Command4, se muestra un cuadro de dilogo, si pulsas "SI", se producir un error dentro de ese procedimiento y se "saltar" a la etiqueta indicada en On Error

378

Goto ..., en este caso HayError, y se mostrar el mensaje de que se ha producido un error... Si pulsas en "NO", se llamar al procedimiento Command5_Click, se producir un error y, como en ese procedimiento no hay rutina de deteccin de errores, el Visual Basic pasa a la anterior que hubiese activa, la de Command4_Click, y all es donde se muestra el mensaje de aviso. Pero si pulsas directamente en el Command5, vers que el error es detectado directamente por el Visual Basic, mostrndonos un mensaje y, en caso de que fuese un ejecutable, acabando la aplicacin. Esto es porque en el cdigo del procedimiento Command5_Click no hay ninguna rutina de deteccin de errores y tampoco hay ninguna otra rutina "pendiente" que controle errores, cosa que si es cierta cuando se llama al procedimiento Command5_Click desde el Command4_Click. Recuerda que esto slo ocurre cuando se llaman a procedimientos dentro de otros procedimientos. Fjate del Exit Sub que hay antes de llegar a la etiqueta "HayError", esto es necesario hacerlo para que, en caso de que no se produzca un error, no se entre en la parte en la que se detectan los errores. Dentro de la parte en la que se detectan los errores, podemos usar instrucciones como: Resume Next, con esto tendramos algo parecido al On Error Resume Next, ya que cuando el VB se encuentra con un Resume Next, contina en la siguiente instruccin a la que produjo el error. Resume NmeroLnea, en esta ocasin se continuar con el cdigo que haya a partir de la etiqueta (o nmero de lnea) NmeroLnea... Si Visual Basic se encuentra con una instruccin Resume y no hay un error, mostrar una queja indicndones que se ha encontrado con un Resume sin que haya error...

Cuando queramos "dejar" de detectar errores en un procedimiento ,


usaremos esta instruccin: On Error Goto 0 Esto no indica que se vaya a la lnea "0", (aunque exista), sino que deje de detectar errores... en ese procedimiento, ya que si hay algn procedimiento de nivel superior... si que se detectar el error... Veamos un ejemplo: ' Private Sub Command6_Click() ' Esta variable se usar para mostrar mensajes en la rutina de deteccin Dim Mensaje As String

379

' Controlamos los posibles errores On Error GoTo HayError2 ' no puede haber dos etiquetas con el mismo nombre ' Producimos un error... Mensaje = "Instruccin: Error 10" Error 10 ' Llamamos al procedimiento Command7_Click Mensaje = "Instruccin: Command7_Click" Command7_Click

Exit Sub HayError2: MsgBox "'" & Mensaje & "'" & vbCrLf & vbCrLf & _ "Se ha producido un error: " & vbCrLf & _ Err.Number & " - " & Err.Description ' Continuar por la siguiente instruccin Resume Next End Sub

Private Sub Command7_Click() ' On Error Resume Next ' Este mensaje de error ser ignorado, (por el On Error Resume Next anterior) Error 13 ' Dejamos de detectar errores On Error GoTo 0 ' A ver que pasa con este otro error Error 15

380

' Este cdigo nunca se ejecutar MsgBox "Un mensaje desde Command7_Click" & vbCrLf & "que nunca se mostrar" End Sub Un detalle: Si en un mismo mdulo vamos a usar varias veces On Error Goto NmeroLnea, no podemos usar el mismo nombre para la etiqueta... si no lo tenemos en cuenta, el Visual Basic nos lo recordar. El cdigo de Command6_Click no debera tener ningn problema, as que pasamos al del Command7_Click: Al principio tenemos un On Error Resume Next, por tanto cualquier error que se produzca ser ignorado... La prueba: producimos un Error 13 y no passa nada... Nos encontramos ahora con On Error Goto 0, esto dejar de "detectar" errores en este procedimiento, (este detalle es importante saberlo, ya que slo se dejan de detectar en este procedimiento... no en el resto... cosa que puede producirnos algn que otro quebradero de cabeza al pensar que se dejar de detectar errores y ser el propio Visual Basic el que se encargue de mostrarnos el error; esto se usa sobre todo cuando se est "debugueando" un programa, pero al final nos encontramos que haba una rutina "superior" en la que si que se detectan los errores y... en fin... que puede que nos liemos ms de lo que yo ya te estoy liando... lo dicho... si te aclaras con toda esta parrafada... me lo explicas!) Sigamos: tenemos que despus de dejar de detectar los errores, producimos un error: Error 15, pero como en este procedimiento ya no hay rutina de deteccin de errores, (recuerdas? la cancelamos con el On Error Goto 0), el Visual Basic "busca" alguna rutina activa y si la hay la enva a esa rutina, que en nuestro ejemplo est en el procedimiento Command6_Click. Por tanto el mensaje ese que hay al final del Command7_Click nunca se ejecutar... Ni siquiera si se pulsa directamente en el Command7, ya que al no existir ninguna rutina a un nivel superior, ser el propio Visual Basic el que se encargue de detener el programa al encontrarse con un error no detectado...

Una cosa ms: No hay ninguna forma de crear rutinas genricas para deteccin de errores... es decir no se puede crear un procedimiento genrico para detectar errores... al menos hasta la versin 6 del Visual Basic, en futuras versiones... puede ser que lo hagan... pero... eso ya se ver...

Confo que tengas claro todo esto de la deteccin de errores... si no es as... pues... pero para ti...

381

Curso Bsico de Programacin en Visual Basic

Aunque no es normal que con el calor que hace por estas latitudes en estas fechas me vuelva "trabajador", (seguramente ser por el "mono" de no haberme puesto delante del ordenador por culpa del "virus" ese que me atac hace unos meses), aqu te traigo una nueva entrega de este cursillo que va a ser ms largo (en fechas) que... no se me ocurre ahora ningn ejemplo, pero bueno, ya me entiendes, sobre todo teniendo en cuenta que lo empec en Abril del 97... cuanto tiempo ha pasado ya! Pero lo importante es que aqu estamos de nuevo con una otra entrega del cursillo bsico de Visual Basic, en este caso, vamos a acabar con lo que quedaba pendiente del editor que nos ha servido de ejemplo en las ltimas entregas. Lo que vamos a hacer en este caso es crear nuestro propio dilogo de buscar y reemplazar y tambin veremos el cdigo que habra que usar para realizar esas operaciones sobre el texto escrito.

Dilogo de Buscar y Reemplazar para el Editor Antes de empezar a ver el cdigo de este cuadro de dilogo, vamos a hacer unos pequeos cambios en el formulario del editor: -- Cambia los nombre del men de edicin de mnuEditor a mnuEdicion, (es que es ms lgico) -- El cdigo de mnuEdicion_Click debe quedar as: (despus veremos porqu) Private Sub mnuEdicion_Click(Index As Integer) '------------------------------------------------------------------------' Usando el cdigo del mdulo MgsDBR es ms cmodo (03/Jul/00) ' ya se encarga de todo... '------------------------------------------------------------------------' Set LineaEstado = lblStatus MgsDBR.menuEdicion Index

382

' End Sub

Si ya tuvisemos el cdigo que ahora veremos, eso sera todo lo que habra que hacer para que funcionasen todas las opciones del men de Edicin... fcil?, no, simple, ya que el cdigo simplemente est escrito en otro sitio... pero escrito est... que conste! y a mi me consta, que lo he escrito yo... je, je.

-- El cdigo de comprobacin que hay en el evento mnuFicSalir_Click lo he pasado al del Form_QueryUnload, para que tambin se pregunte si se pulsa en el botn de cerrar el formulario, (la "x" que hay arriba a la derecha) Por tanto esos dos eventos quedaran as: Private Sub mnuFicSalir_Click() ' Terminar el programa ' ' La comprobacin de si hay que guardar el fichero est en el ' evento Form_QueryUnload, para que tambin sirva si se pulsa en la "x" ' del formulario. ' Unload Me End Sub

Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer) ' Al terminar el programa, ' comprobar si se ha modificado el fichero... (22/Ene/00) ' ' Pero slo se debera comprobar si (03/Jul/00) ' se pulsa en el botn "x" del formulario ' o si se cierra por medio de cdigo, (con Unload) ' Dim ret As Long ' Slo si se cierra por medio de nuestro cdigo o por cerrar el formulario If UnloadMode = vbFormCode Or UnloadMode = vbFormControlMenu Then

383

If Modificado Then ret = MsgBox("El fichero se ha modificado, quieres guardarlo?", vbYesNoCancel) ' Si hemos contestado "Si" If ret = vbYes Then ' Guardarlo mnuFicGuardar_Click ' Si pulsamos el botn Cancelar, salimos del procedimiento ' y por tanto no terminamos el programa. ElseIf ret = vbCancel Then Exit Sub End If End If End If End Sub Veamos ahora ese cdigo, aunque antes, una imagen del aspecto del formulario (en tiempo de diseo) que nos servir para buscar y reemplazar, adems de para usarlo como un ImPutBox.

El formulario de Buscar y Reemplazar Para que este dilogo funcione, necesitamos, adems del propio formulario, el cdigo de un mdulo BAS, que es realmente el que hace casi todo el trabajo. Veamos primero el cdigo del formulario: '----------------------------------------------------------------------------' Form genrico para dilogo Buscar/Reemplazar (03/Jul/00) ' Se necesita el mdulo MgsDBR.bas ' ' Guillermo 'guille' Som, 1996-2000

384

'----------------------------------------------------------------------------Option Explicit Private Const NumeroMaximoDeItems = 200 Private bBuscandoEnCombo As Boolean

Private Sub cmdCancel_Click() ActualizarCombo iFFAccion = cFFAc_Cancelar Unload Me End Sub Private Sub cmdFindNext_Click() ActualizarCombo sFFBuscar = txtFind.Text sFFPoner = "" iFFAccion = cFFAc_BuscarSiguiente Unload Me End Sub Private Sub cmdReplace_Click() ActualizarCombo sFFBuscar = txtFind.Text sFFPoner = txtReplace.Text If Len(sFFPoner) = 0 Then iFFAccion = cFFAc_Buscar Else iFFAccion = cFFAc_Reemplazar End If

385

Unload Me End Sub Private Sub cmdReplaceAll_Click() ActualizarCombo sFFBuscar = txtFind.Text sFFPoner = txtReplace.Text If Len(sFFPoner) = 0 Then iFFAccion = cFFAc_Buscar Else iFFAccion = cFFAc_ReemplazarTodo End If Unload Me End Sub Private Sub Combo1_Change(Index As Integer) Static YaEstoy As Boolean If bBuscandoEnCombo Then Exit Sub On Local Error Resume Next If Index = 0 Then txtFind = Combo1(0).Text Else txtReplace = Combo1(1).Text End If Err = 0 End Sub Private Sub Combo1_Click(Index As Integer) If bBuscandoEnCombo Then Exit Sub

386

If Combo1(Index).ListIndex Then Combo1(Index).Text = Combo1(Index).List(Combo1(Index).ListIndex) End If If Index = 0 Then txtFind = Combo1(Index).Text Else txtReplace = Combo1(Index).Text End If End Sub Private Sub Form_Load() ' Si no se ha especificado ningn nombre de fichero de configuracin If sFFIni = "" Then ' Asignar el nombre del fichero INI. ' ' Se podra hacer as: 'sFFIni = App.Path & "\BuscReemp.ini" ' pero si el programa es el directorio raiz, por ejemplo en C:, ' tendramos esto: 'C:\\BuscReemp.ini' y dara error ' ' Asi que nos creamos una funcin que devuelva el path pero sin ' la barra del final. sFFIni = AppPath & "\BuscReemp.ini" End If ' Posicionar en el centro de la ventana principal Move (Screen.Width - Width) \ 2, (Screen.Height - Height) \ 2 Combo1(0).Clear Combo1(1).Clear ' En un sub, para que acepte el tag de los combos. ' Si se dejaba en el Form_Load, no se actualizaban los valores de inicio 'IniciarCombo

387

Timer1.Interval = 100 Timer1.Enabled = True End Sub Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer) ' Si se cierra por el controlbox o ' cualquier forma distinta del propio cdigo, asumir que se ha cancelado. If UnloadMode <> vbFormCode Then iFFAccion = cFFAc_Cancelar End If End Sub Private Sub Form_Unload(Cancel As Integer) Dim n As Integer Dim vTmp As String Dim sTmp As String Dim i As Integer Dim j As Integer Dim sTag As String ' Si no se ha cancelado... If iFFAccion <> cFFAc_Cancelar Then ' Guardar el contenido de los combos en el fichero INI ActualizarCombo For i = 0 To 1 n = Combo1(i).ListCount sTag = Trim$(Combo1(i).Tag) If n > NumeroMaximoDeItems Then n = NumeroMaximoDeItems GuardarIni sFFIni, sTag, "NumEntradas", CStr(n) For j = 0 To n - 1 vTmp = "Entrada" & CStr(j) sTmp = Combo1(i).List(j) GuardarIni sFFIni, sTag, vTmp, sTmp Next Next End If

388

Set gsDBR = Nothing End Sub Private Sub ActualizarCombo() '----------------------------------------------------' Esta rutina actualiza el contenido de los dos combos, ' si la entrada en el Combo.Text no est, la incluye. ' Se podra usar la llamada al API de Windows. '----------------------------------------------------' Actualizar el contenido del Combo Dim sTmp As String Static k As Integer ' bBuscandoEnCombo = True For k = 0 To 1 sTmp = Combo1(k).Text If Len(Trim$(sTmp)) Then ' El valor devuelto no nos interesa Call ActualizarLista(sTmp, Combo1(k)) End If Next bBuscandoEnCombo = False End Sub Private Sub IniciarCombo() Dim j As Integer Dim i As Integer Dim n As Integer Dim vTmp As String Dim sTmp As String Dim sTag As String ' Asignar los valores anteriores del combo For i = 0 To 1 sTag = Trim$(Combo1(i).Tag) n = 0 n = LeerIni(sFFIni, sTag, "NumEntradas", n) If n > NumeroMaximoDeItems Then n = NumeroMaximoDeItems

389

' For j = 0 To n - 1 vTmp = "Entrada" & CStr(j) sTmp = LeerIni(sFFIni, sTag, vTmp, "") If Len(sTmp) Then Combo1(i).AddItem sTmp End If Next Next End Sub Private Sub Timer1_Timer() ' Asignar los valores anteriores del combo Timer1.Enabled = False ' IniciarCombo End Sub Private Function AppPath() As String ' Devolver el path actual sin la barra final de directorio ' ' Si el ltimo caracter es la barra de directorio, If Right$(App.Path, 1) = "\" Then ' devolver todos los caracteres menos el ltimo. AppPath = Left$(App.Path, Len(App.Path) - 1) Else ' sino, devolver el path normal AppPath = App.Path End If End Function ' Ya no necesitaremos ms este evento!!!

Ahora veamos el contenido del mdulo: gsDBR.bas: ' '----------------------------------------------------------------------------' gsDBR.bas Mdulo para el dilogo de Buscar y Reemplazar (03/Jul/00)

390

' ' (c)Guillermo 'guille' Som, 1997-2000 '----------------------------------------------------------------------------Option Explicit ' Control en el que se mostrar lo que el dilogo est haciendo ' Se tendr que usar con SET, por ejemplo: Set LineaEstado = Label1 Global LineaEstado As Control ' ' Variables y constantes globales (o pblicas) para buscar/reemplazar ' ' Constantes para el men de Edicin ' ' Es recomendable tener un men de edicin con estas opciones ' y en este mismo orden. ' Public Enum emnuEdicion mEdDeshacer = 0 mEdCortar = 1 mEdCopiar = 2 mEdPegar = 3 ' Const mEdSep1 = 4 mEdBuscarActual = 5 mEdBuscarSigActual = 6 mEdReemplazarActual = 7 ' Const mEdSep2 = 8 mEdSeleccionarTodo = 9 End Enum ' ' Global sFFBuscar As String textboxes) Global sFFPoner As String ' Global iFFAccion As Integer hemos hecho ' Indicar que es lo que ' para salir del dilogo, ' La cadena a buscar (de los ' La cadena a poner

391

' ver las siguientes constantes: ' ' Constantes para la accin a realizar Global Const cFFAc_Cancelar = True Global Const cFFAc_IDLE = 0 Global Const cFFAc_Buscar = 1 Global Const cFFAc_BuscarSiguiente = 2 Global Const cFFAc_Reemplazar = 3 Global Const cFFAc_ReemplazarTodo = 4 Global Const cFFAc_Aceptar = 5 ' Global sFFIni As String ' ' '--------------------------' Funciones Globales del API Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" _ (ByVal hWnd As Long, ByVal wMsg As Long, _ ByVal wParam As Long, lParam As Any) As Long Private Declare Function PostMessage Lib "user32" Alias "PostMessageA" _ (ByVal hWnd As Long, ByVal wMsg As Long, _ ByVal wParam As Long, ByVal lParam As Long) As Long ' Declaracin de las constantes, para usar con SendMessage/PostMessage Global Const WM_CUT = &H300 Global Const WM_COPY = &H301 Global Const WM_PASTE = &H302 ' Global Const EM_CANUNDO = &HC6 Global Const EM_UNDO = &HC7 '-------------------------------------------------' Profile.bas ' Autor: ' Fecha inicio: 24/Feb/97 04:05 (24/Feb/97) Guillermo Som Cerezo, 1997 ' Archivo de configuracin

392

' ' Mdulo genrico para las llamadas al API ' usando xxxPrivateProfileString '-------------------------------------------------' ' Declaraciones privadas para guardar y leer ficheros INIs Private Declare Function GetPrivateProfileString Lib "Kernel32.dll" Alias "GetPrivateProfileStringA" _ (ByVal lpApplicationName As String, ByVal lpKeyName As Any, _ ByVal lpDefault As String, ByVal lpReturnedString As String, _ ByVal nSize As Long, ByVal lpFileName As String) As Long Private Declare Function WritePrivateProfileString Lib "Kernel32.dll" Alias "WritePrivateProfileStringA" _ (ByVal lpApplicationName As String, ByVal lpKeyName As Any, _ ByVal lpString As Any, ByVal lpFileName As String) As Long

'--------------------------------------------------------------------------' Procedimiento equivalente a SaveSetting de VB. ' SaveSetting ' En VB/32bits usa el registro. En VB/16bits usa un archivo de texto.

' GuardarIni al usar las llamadas del API, siempre se escriben en archivos de texto. '--------------------------------------------------------------------------Public Sub GuardarIni(ByVal lpFileName As String, ByVal lpAppName As String, ByVal lpKeyName As String, ByVal lpString As String) ' Guarda los datos de configuracin ' Los parmetros son los mismos que en LeerIni ' Siendo lpString el valor a guardar ' Dim LTmp As Long LTmp = WritePrivateProfileString(lpAppName, lpKeyName, lpString, lpFileName) End Sub '---------------------------------------------------------------------------

393

' Funcin equivalente a GetSetting de VB. ' GetSetting ' En VB/32bits usa el registro. En VB/16bits usa un archivo de texto.

' LeerIni al usar las llamadas del API, siempre se escriben en archivos de texto. '--------------------------------------------------------------------------Public Function LeerIni(ByVal lpFileName As String, ByVal lpAppName As String, ByVal lpKeyName As String, Optional ByVal vDefault) As String 'Los parmetros son: 'lpFileName: 'lpAppName: 'lpKeyName: 'vDefault: ' ' Dim lpString As String Dim LTmp As Long Dim sRetVal As String 'Si no se especifica el valor por defecto, 'asignar incialmente una cadena vaca If IsMissing(vDefault) Then lpString = "" Else lpString = vDefault End If 'Longitud mxima permitida '(antes 255) sRetVal = String$(32367, 0) LTmp = GetPrivateProfileString(lpAppName, lpKeyName, lpString, sRetVal, Len(sRetVal), lpFileName) If LTmp = 0 Then LeerIni = lpString Else LeerIni = Left(sRetVal, LTmp) End If (25/Ene/98) La Aplicacin (fichero INI) La seccin que suele estar entrre corchetes Clave Valor opcional que devolver si no se encuentra la clave.

394

sRetVal = "" End Function

Public Function ActualizarLista(ByVal sTexto As String, cList As Control, Optional vTipoBusqueda, Optional vAddLista) As Long 'Esta funcin comprobar si el texto indicado existe en la lista 'Si no es as, lo aadir 'El valor devuelto, ser la posicin dentro de la lista -1 si hay "fallos" ' 'Para buscar en el List/combo usaremos una llamada al API '(si ya hay una forma de hacerlo, para que re-hacerla?) ' 'Constantes para los combos Const CB_FINDSTRINGEXACT = &H158 Const CB_FINDSTRING = &H14C Const CB_SELECTSTRING = &H14D 'Constantes para las Listas Const LB_FINDSTRINGEXACT = &H1A2 exactamente igual Const LB_FINDSTRING = &H18F de la cadena Const LB_SELECTSTRING = &H18C de la cadena ' Dim lTipoBusqueda As Long Dim bTipoBusqueda As Integer parte, 2=desde el principio Dim bAddLista As Boolean Dim L As Long 'Si se busca palabra completa o parcial, 'por defecto COMPLETA If IsMissing(vTipoBusqueda) Then bTipoBusqueda = False Else bTipoBusqueda = vTipoBusqueda End If 'Si se debe aadir o no, por defecto SI '0= Exacta, 1= cualquier 'Busca la cadena 'Busca en cualquier parte 'Busca desde el principio

395

If IsMissing(vAddLista) Then bAddLista = True Else bAddLista = vAddLista End If 'Si el control es un Combo If TypeOf cList Is ComboBox Then If bTipoBusqueda = 1 Then lTipoBusqueda = CB_FINDSTRING ElseIf bTipoBusqueda = 2 Then lTipoBusqueda = CB_SELECTSTRING Else lTipoBusqueda = CB_FINDSTRINGEXACT End If 'Si el control es un list ElseIf TypeOf cList Is ListBox Then If bTipoBusqueda = 1 Then lTipoBusqueda = LB_FINDSTRING ElseIf bTipoBusqueda = 2 Then lTipoBusqueda = LB_SELECTSTRING Else lTipoBusqueda = LB_FINDSTRINGEXACT End If Else 'no es un control List o Combo, salir ActualizarLista = -1 Exit Function End If If cList.ListCount = 0 Then 'Seguro que no est, as que aadirla, si viene al caso... L = -1 Else L = SendMessage(cList.hWnd, lTipoBusqueda, -1, ByVal sTexto) End If 'Si no est, aadirla

396

If L = -1 Then If bAddLista Then 'Con el 0 se aade al principio de la lista cList.AddItem sTexto, 0 L = ActualizarLista(sTexto, cList, bTipoBusqueda, bAddLista) End If End If ActualizarLista = L End Function Public Function gsReemplazar(sBuscar As String, sPoner As String, Optional vModo, Optional vCaption) As Integer 'Prepara el dilogo de Reemplazar Dim iModo As Integer Dim sCaption As String If IsMissing(vModo) Then iModo = cFFAc_Reemplazar Else iModo = vModo End If If IsMissing(vCaption) Then sCaption = "Reemplazar" Else sCaption = CStr(vCaption) End If iFFAccion = cFFAc_IDLE With gsDBR 'Por ahora no se muestra en reemplazar .Caption = sCaption .cmdFindNext.Default = False .cmdFindNext.Visible = False .cmdReplaceAll.Default = True .Combo1(0).Text = sBuscar .Combo1(1).Text = sPoner 'Mostrar el form y esperar a que se tome una accin ( 6/Sep/97)

397

.Show vbModal 'Do ' ' End With 'Devolver la cadena a reemplazar y buscar sBuscar = sFFBuscar sPoner = sFFPoner 'Si tanto buscar como poner estn en blanco, devolver cancelar If Len(Trim$(sBuscar)) = 0 Then If Len(Trim$(sPoner)) = 0 Then iFFAccion = cFFAc_Cancelar End If End If 'Devolver la accin gsReemplazar = iFFAccion End Function Public Function gsBuscar(sBuscar As String, Optional vModo, Optional vCaption) As Integer 'Prepara el dilogo para buscar Dim iModo As Integer Dim sCaption As String Dim bCompleta As Boolean Dim bAtras As Boolean If IsMissing(vModo) Then iModo = cFFAc_Buscar bCompleta = False bAtras = False End If 'Slo permitir buscar y buscar-siguiente Select Case iModo Case cFFAc_Buscar, cFFAc_BuscarSiguiente 'est bien, no hay nada que hacer Case Else iModo = cFFAc_Buscar End Select .Show DoEvents

'Loop Until iFFAccion

398

If IsMissing(vCaption) Then sCaption = "Buscar" Else sCaption = CStr(vCaption) End If iFFAccion = cFFAc_IDLE With gsDBR .Caption = sCaption .cmdReplace.Visible = False .lblReplace.Visible = False .cmdReplaceAll.Visible = False .Combo1(1).Visible = False .Combo1(1).Enabled = False .cmdFindNext.Left = .cmdReplaceAll.Left If iModo = cFFAc_BuscarSiguiente Then .cmdFindNext.Caption = "Siguiente" DoEvents End If .Combo1(0).Text = sBuscar 'Mostrar el form y esperar a que se tome una accin .Show vbModal 'Do ' ' End With 'Devolver la cadena seleccionada/introducida sBuscar = sFFBuscar 'Devolver la accin gsBuscar = iFFAccion End Function .Show DoEvents

'Loop Until iFFAccion

Public Sub gsPedirUnValor(ByVal spuvTitulo As String, _ ByVal spuvMensaje As String, _ ByVal spuvPregunta As String, _

399

ByRef spuvValor As String, _ ByVal spuvBoton As String) '------------------------------------------------------------------------' Rutina de propsito general para pedir un valor 23/May/96) ' ' Los parmetros son: ' ' ' ' ' ' spuvBoton spuvTitulo spuvMensaje spuvPregunta spuvValor El ttulo de la ventana El texto a mostrar como explicacin El texto con la pregunta a realizar El texto a mostrar en la caja de texto, tambin se usa para devolver la respuesta El texto a poner en el botn de aceptar (00.22

'------------------------------------------------------------------------With gsDBR .Caption = spuvTitulo .Combo1(0).Visible = False .lblBuscar.Width = .ScaleWidth - 120 .lblBuscar = spuvMensaje .Combo1(0).Visible = False .cmdReplace.Visible = False .cmdFindNext.Default = False .cmdFindNext.Visible = False .lblReplace = spuvPregunta .cmdReplaceAll.Default = True .cmdReplaceAll.Caption = spuvBoton If Len(Trim$(spuvValor)) Then .Combo1(1).Text = spuvValor Else If .Combo1(1).ListCount Then .Combo1(1).ListIndex = 0 End If End If .Show vbModal End With spuvValor = sFFPoner

400

End Sub

Private Sub AccionBuscar(Index As Integer) '------------------------------------------------------------------------' Procedimiento genrico para realizar bsquedas (31/Ago/97) ' ' Valores "externos" necesarios: ' ' mostrar ' ' ' ' Index El parmetro que apuntar a los ndices del men de edicin que deber tener estas ' opciones: ' ' ' ' ' ' ' ' ' ' ' ' ' Estas constantes estn declaradas en la enumeracin emnuEdicion ' '------------------------------------------------------------------------Static sBuscar As String Static lngUltimaPos As Long Dim lngPosActual As Long Deshacer Cortar Copiar Pegar ---(separador) Buscar Buscar Siguiente Reemplazar ---(separador) Seleccionar Todo Ctrl+A Ctrl+B o Ctrl+F F3 Ctrl+H Ctrl+Z Ctrl+X Ctrl+V Ctrl+P LineaEstado un control para mostrar mensajes temporales Hacer un set a una etiqueta en la que se el progreso de la bsqueda: Set LineaEstado = lblStatus

401

Dim sTmp As String Dim tText As TextBox 'Control On Error Resume Next Set tText = Screen.ActiveForm.ActiveControl ' Si no es un cuadro de texto, salir If Not (TypeOf tText Is TextBox) Then Err = 0 Exit Sub End If If LineaEstado Is Nothing Then ' Poner a cero el nmero de error, ya que esto nos dar ' la "pista" de que todo haya ido bien Err = 0 ' intentarlo con lblStatus, si no existe, salir... Set LineaEstado = Screen.ActiveForm.lblStatus ' Si se produce un error, es que no podemos usar "LinaEstado" If Err Then Err = 0 ' salir del procedimiento Exit Sub End If End If ' Guardar el valor mostrado, antes de entrar a esta rutina LineaEstado.Tag = LineaEstado ' para procesar las otras acciones adicionales Select Case Index Case mEdBuscarActual ' Si hay texto seleccionado... With tText If .SelLength > 0 Then sBuscar = Trim$(.SelText) End If End With ' Para "personalizar" la seccin de bsqueda... (15/Abr/97)

402

gsDBR.Combo1(0).Tag = "Buscar_" '& sUsuario If gsBuscar(sBuscar, , "Buscar en el campo actual") > cFFAc_IDLE Then sBuscar = Trim$(sBuscar) If Len(sBuscar) Then sBuscar & "..." LineaEstado = "Buscando en el campo actual " & DoEvents lngUltimaPos = 0& lngPosActual = InStr(tText, sBuscar) If lngPosActual Then lngUltimaPos = lngPosActual + 1 ' posicionarse en esa palabra: With tText .SelStart = lngPosActual - 1 .SelLength = Len(sBuscar) End With Else Beep MsgBox "No se ha hallado el texto buscado", vbOK + vbInformation, "Buscar en el campo actual" End If ' posicionarse en ese control tText.SetFocus End If End If Case mEdBuscarSigActual 'Si no hay nada hallado con anterioridad 'o no se ha procesado la ltima bsqueda en este control If Len(sBuscar) = 0 Or lngUltimaPos = 0& Then AccionBuscar mEdBuscarActual Else LineaEstado = "Buscando " & sBuscar & "..." DoEvents lngPosActual = InStr(lngUltimaPos, tText, sBuscar) If lngPosActual Then lngUltimaPos = lngPosActual + Len(sBuscar) 'posicionarse en esa palabra: With tText

403

.SelStart = lngPosActual - 1 .SelLength = Len(sBuscar) End With Else lngUltimaPos = 1& Beep MsgBox "No se ha hallado el texto buscado.", vbOK + vbInformation, "Buscar en el campo actual" End If ' posicionarse en ese control tText.SetFocus End If Case mEdReemplazarActual ' Si hay texto seleccionado... With tText If .SelLength > 0 Then sBuscar = Trim$(.SelText) End If End With sFFBuscar = sBuscar sFFPoner = "" ' Personalizar las secciones de buscar/reemplazar gsDBR.Combo1(0).Tag = "Buscar_" '& sUsuario gsDBR.Combo1(1).Tag = "Reemplazar_" '& sUsuario iFFAccion = gsReemplazar(sFFBuscar, sFFPoner, , "Reemplazar en el campo actual") If iFFAccion <> cFFAc_Cancelar Then Screen.ActiveForm.MousePointer = vbHourglass DoEvents sBuscar = Trim$(sFFBuscar) If Len(sFFBuscar) <> 0 And Len(sFFPoner) <> 0 Then If iFFAccion = cFFAc_Reemplazar Or iFFAccion = cFFAc_ReemplazarTodo Then LineaEstado = "Reemplazando " & sBuscar & "..." DoEvents lngUltimaPos = 0& lngPosActual = InStr(tText, sBuscar) If lngPosActual Then

404

lngUltimaPos = lngPosActual + Len(sBuscar) sTmp = tText sTmp = Left$(sTmp, lngPosActual - 1) & sFFPoner & Mid$(sTmp, lngPosActual + Len(sFFBuscar)) tText = sTmp ' Si slo es reemplazar uno... If iFFAccion = cFFAc_Reemplazar Then ' posicionarse en la palabra modificada: With tText .SelStart = lngPosActual - 1 .SelLength = Len(sFFPoner) End With ' Dejar el puntero del ratn como estaba Screen.ActiveForm.MousePointer = vbDefault ' Salir Exit Sub End If ' Cambiar todas las coincidencias en el msmo text lngUltimaPos = 1 Do lngPosActual = InStr(lngUltimaPos, sTmp, sFFBuscar) If lngPosActual Then lngUltimaPos = lngPosActual + 1 sTmp = Left$(sTmp, lngPosActual - 1) & sFFPoner & Mid$(sTmp, lngPosActual + Len(sFFBuscar)) tText = sTmp End If Loop While lngPosActual ' ' posicionarse en la ltima palabra modificada With tText .SelStart = lngUltimaPos - 2 .SelLength = Len(sFFPoner) End With

405

DoEvents Else Beep MsgBox "No se ha hallado el texto buscado.", vbOK + vbInformation, "Buscar en el campo actual" End If ' Si se ha reemplazado todo, no debe estar esta palabra... lngUltimaPos = 0& End If End If Screen.ActiveForm.MousePointer = vbDefault DoEvents End If Case mEdSeleccionarTodo With tText .SelStart = 0 .SelLength = Len(.Text) End With End Select LineaEstado = LineaEstado.Tag End Sub

Public Sub menuEdi() ' Habilitar las opciones disponibles Dim Habilitada As Boolean Dim i As Integer ' Dim elForm As Form ' Los separadores no se pueden deshabilitar!!! On Local Error Resume Next Set elForm = Screen.ActiveForm ' Asegurarnos que es un textbox If TypeOf Screen.ActiveForm.ActiveControl Is TextBox Then 'ok, todo bien...

406

Habilitada = True Else 'no poder hacer estas cosas Habilitada = False End If For i = mEdDeshacer To mEdSeleccionarTodo elForm!mnuEdicion(i).Enabled = Habilitada Next ' ' Algunos chequeos para las opciones de edicin: If Habilitada Then ' Si no se puede deshacer, no habilitarlo If SendMessage(Screen.ActiveForm.ActiveControl.hWnd, EM_CANUNDO, 0, ByVal 0&) Then elForm!mnuEdicion(mEdDeshacer).Enabled = True Else elForm!mnuEdicion(mEdDeshacer).Enabled = False End If ' Comprobar si hay algo que pegar... If Clipboard.GetFormat(vbCFText) Then elForm!mnuEdicion(mEdPegar).Enabled = True Else elForm!mnuEdicion(mEdPegar).Enabled = False End If ' Si hay texto seleccionado, habilitamos Cortar y Copiar If Screen.ActiveForm.ActiveControl.SelLength Then elForm!mnuEdicion(mEdCortar).Enabled = True elForm!mnuEdicion(mEdCopiar).Enabled = True Else elForm!mnuEdicion(mEdCortar).Enabled = False elForm!mnuEdicion(mEdCopiar).Enabled = False End If End If Err = 0 End Sub

407

Public Sub menuEdicion(Index As Integer) Dim sTmp As String Select Case Index Case mEdDeshacer '------------------------------------------------------------' IMPORTANTE: ' En ambos casos se podra usar SendMessage, ' pero en el caso de EM_CANUNDO, NO servira PostMessage, ' porque esta funcin slo devuelve un valor de ' si se ha puesto o no en la cola de mensajes de windows. '------------------------------------------------------------'Si se puede deshacer... If SendMessage(Screen.ActiveForm.ActiveControl.hWnd, EM_CANUNDO, 0, ByVal 0&) Then 'Deshacerlo! Call PostMessage(Screen.ActiveForm.ActiveControl.hWnd, EM_UNDO, 0, ByVal 0&) End If Case mEdCopiar Call PostMessage(Screen.ActiveForm.ActiveControl.hWnd, WM_COPY, 0, ByVal 0&) Case mEdCortar Call PostMessage(Screen.ActiveForm.ActiveControl.hWnd, WM_CUT, 0, ByVal 0&) Case mEdPegar Call PostMessage(Screen.ActiveForm.ActiveControl.hWnd, WM_PASTE, 0, ByVal 0&) Case mEdBuscarActual AccionBuscar mEdBuscarActual Case mEdBuscarSigActual AccionBuscar mEdBuscarSigActual Case mEdReemplazarActual AccionBuscar mEdReemplazarActual Case mEdSeleccionarTodo AccionBuscar mEdSeleccionarTodo End Select End Sub

408

Como te dije al principio, para usar el cuadro de dilogo, solamente hay que llamar al procedimiento menuEdicion con el ndice de la accin que queremos realizar, para el caso de Buscar sera un valor de 5 o usar la constante mEdBuscarActual. Pero esto est bien para buscar texto dentro del TextBox que tiene actualmente el foco, si quieres usarla para otras cosas, por ejemplo buscar en una base de datos, tendrs que crearte tu propio cdigo, (si tienes pereza, uedes esperar a que nos toque la parte de las bases de datos o echarle un vistazo a la cuarta entrega del Proyecto Paso a Paso que tengo en mis pginas, no es se usa este dilogo, pero te puede dar una idea). En el procedimiento AccionBuscar tienes la forma en que se puede llamar a este formulario para que muestre el cuadro de dilogo y usar los valores elegidos por el usuario.

Hasta aqu hemos llegado, a ver que preparo para la prxima entrega, ya que estoy un poco "liado" (entindase por liado: confundido, sin claridad mental... jo!), sobre que es lo que pondr, ya que no me decido entre "algo" bsico de tratamiento de bases de datos y empezar con el "escabroso" tema de los mdulos de clases (para crear objetos en Visual Basic). En fin... ya veremos que es lo que te encuentras. Mientras tanto, disfruta con lo que hay y espero que te sea provechoso. Nos vemos Guillermo

Curso Bsico de Programacin en Visual Basic


Te creas que ya no iba a seguir con el curso bsico verdad? Pues te has vuelto a equivocar... S que ltimamente no soy muy "constante" en esto de las entregas del Curso Bsico, pero... alguna que otra vez me dejo "ver". Y ya lo ests "viendo", aqu estamos de nuevo, con una nueva entrega del Curso Bsico, el tema en esta ocasin ser: Las bases de datos (por fin!) Aunque empezaremos de forma muy bsica, aunque en entregas posteriores veremos cosas ms avanzadas, todo se andar, as que no pierdas la esperanza de que algn siglo de estos (el que viene?) acabe con el dichoso Curso Bsico de Programacin en Visual Basic... La verdad es que tengo que darme prisa, ya que, si me descuido, la nueva versin del VB (VB.NET) estar en la calle y muchas de las cosas dichas hasta ahora no sean vlidas... aunque los conceptos "bsicos" seguirn siendo vlidos, no as los conceptos "particulares" de cada una de las versiones del Visual Basic... en su momento veremos esas diferencias, por ahora hagamos como que no hay ninguna nueva versin que cambie "radicalmente" la forma de "pensar" del Visual Basic actual. Los ejemplos que voy a utilizar, est hechos con la versin 6.0 del VB, aunque

409

funcionarn, (o al menos deberan funcionar), perfectamente con las versiones 4.0 y superiores... confiemos en ello. Y ya, sin ms dilacin, empecemos con el acceso a bases de datos desde el Visual Basic, para ello usaremos como base de ejemplo la incluida en todas las versiones del VB: Biblio.mdb, si no la tienes, necesitars un poco de imaginacin, espero que eso no sea un problema. (Guille, aqu tendras que haber dicho algo como... "para una mente tan inteligente como la tuya", de esa forma, le daras coba al personal y se mosqueara menos contigo, por no tener la base de ejemplo, cuando vas a aprender?) Despus del comentario del "otro Guille", empecemos con:

El diseo del formulario


Hay quin se queja de que no explico de forma "simplona" cmo crear los formularios y aadir los controles que se usan, en esta ocasin, "intentar" hacerlo... a ver si lo consigo. Crea un nuevo proyecto normal, automticamente se aadir un formulario llamado Form1. Pulsa F4 para que se muestre la ventana de propiedades y en la propiedad Caption, escribe: Entrega 34, acceso a datos, vers que en la barra de "caption" del formulario se muestra lo escrito. Ahora vamos a aadir un Control Data que ser el que nos permita acceder a la base de datos que necesitemos usar, para ello, pulsa en el icono: que est en la barra de herramientas del IDE del Visual Basic, si no est visible dicha barra de herramientas, puedes mostrarla de la siguiente forma: (te recuerdo que estoy usando la versin inglesa del Visual Basic, as que si las traducciones son errneas... chale un poco de imaginacin...) En el men Ver (View), pulsa en la opcin Barra de Herramientas (Toolbox). Para aadir cualquiera de los controles que estn en la mencionada barra de herramientas, simplemente tienes que hacer una doble pulsacin (doble-click) en el icono deseado y se aadir al formulario. En este caso, se aadir un Data Control llamado Data1 y el aspecto en el formulario ser este: Seleccinalo (aunque ya debe estar seleccionado) y arrstralo, (es decir: deja pulsado el botn derecho mientras lo mueves), hasta la parte superior del formulario, (para dejar espacio libre al resto de controles que aadiremos a continuacin)

Configurando el Data Control (o Control Data)


Ahora vamos a indicarle al Data1 dnde est la base de datos que queremos usar. Selecciona el Data1 que hemos aadido al formulario, simplemente haz un "click" en dicho control, (debera seguir seleccionado, salvo que hayas pulsado con el ratn en el formulario), pulsa F4 para mostrar la ventana de propiedades y en dicha ventana pulsa en la propiedad DatabaseName, en la columna de la izquierda te mostrar un botn con los tres puntos suspensivos que indican que se mostrar un dilogo, (esto ltimo es una "convencin", que nos indica que cuando se seleccione esa opcin, en este caso al hacer click en el botn, se mostrar un cuadro de dilogo), para seleccionar un fichero de bases de datos. El fichero que vamos a usar, Biblio.mdb, normalmente est en el mismo directorio en el que est instalado el Visual Basic, por tanto, tendrs que "localizar" dicho directorio, normalmente suele estar en Archivos de programa\Microsoft Visual Studio\VB98 en el caso del VB6, en el caso del VB5 puede estar en Archivos de programa\DevStrudio\VB o simplemente en DevStudio\VB, de la unidad de

410

arranque, por defecto en C. En el caso del VB4, no recuerdo ahora en que directorio se instalaba... Una vez seleccionada la base de datos, ya disponemos de una conexin, mediante el control Data a dicha base de datos. Pero, (como es habitual, siempre hay un pero), en casi todas las bases de datos suelen existir varias "tablas" que contienen datos. Para seleccionar una de las tablas, vuelve a mostrar la ventana de propiedades del control Data y selecciona la propiedad RecordSource, vers que en la cuadrcula de la derecha hay una lista desplegable, en ella se muestran las tablas disponibles, en nuestro ejemplo usaremos la de Autores, por tanto selecciona dicho elemento de la lista, puede que en lugar de llamarse Autores, (si la base de datos no est traducida), se llame Authors. Ahora si que tenemos configurado nuestro control Data para que muestre los datos almacenados en una tabla de una base de datos. Para continuar con nuestro ejemplo, vamos a aadir otros controles con los cuales poder mostrar la informacin contenida en dicha tabla de autores. Vamos a aadir tres etiquetas (Label) y tres cajas de texto (TextBox) En la barra de herramientas haz doble-click en el icono de las etiquetas: , (si posicionas el puntero del ratn sobre dicho "icono", vers que te muestra un ToolTip con el texto Label), se aadir un nuevo control al formulario llamado Label1, posiciona dicha etiqueta en la parte izquierda del formulario, debajo del control Data1 y vuelve a repetir la operacin dos veces ms, (posiciona cada una de las etiquetas debajo de la anterior), para obtener un total de tres etiquetas: Label1, Label2 y Label3 respectivamente. Para aadir las tres cajas de texto que necesitamos, repite la operacin, pero en este caso el control que debes elegir de la barra de herramientas es: (TextBox). Alinalos a la derecha de cada una de las etiquetas anteriores. Ahora tendremos tambin estos tres controles: Text1, Text2 y Text3, el aspecto del formulario sera el siguiente:

Vamos a cambiar el tamao de las etiquetas y las cajas de texto. Selecciona la primera etiqueta (Label1) y haz que el alto de la misma sea 315, esto puedes hacerlo de dos formas: 1.) Usando el el ratn, arrastra hacia arriba desde la parte inferior del control (al seleccionar se muestran unos cuadros que indican que puedes cambiar el tamao arrastrando en cualquiera de ellos, segn la posicin de dicho "cuadro" servir para el ancho, alto o ambos) 2.) Escribiendo dicho tamao en la ventana de propiedades, en este caso en la propiedad Height.

411

Para que las tres etiquetas tengan el mismo tamao, podemos hacerlo tambin de dos formas, para que se cambien de tamao todas las etiquetas a un mismo tiempo, en lugar de ir cambiado cada una de ellas por separado: 1.) Selecciona las tres etiquetas y escribe el tamao en la propiedad Height de la ventana de propiedades. 2.) Selecciona las etiquetas Label3, Label2 y por ltimo Label1, (manteniendo pulsado la tecla Ctrl y haciendo un simple Click con el ratn), abre el men Formato (Format) y selecciona la opcin Hacer del mismo tamao (Make same size), de dicho men, selecciona Altura (Height). En este procedimiento, es importante que el ltimo control seleccionado sea el que indique el tamao con el que queremos igualar el resto de los controles. Seguramente todo esto ya lo sabrs, pero... (de alguna forma hay que hacer que esta entrega sea ms larga, verdad Guille?) A continuacin vamos a cambiar el tamao de las cajas de texto: Selecciona Text1 y haz que el ancho sea: 2895. Selecciona los otros dos controles e igulalos en el ancho... imagnate cmo... El aspecto final ser este otro:

Una vez diseado el "aspecto" del formulario, (eres libre de adecuarlo a tus gustos particulares), vamos a indicarle al Visual Basic que nos muestre informacin de la tabla de autores en cada una de las cajas de texto: Selecciona las tres cajas de texto, pulsa en la ventana de propiedades y selecciona la propiedad DataSource, con esta propiedad indicamos que Data control queremos usar con cada caja de texto, (o con cualquier otro control que tenga la mencionada propiedad). En la lista desplegable, selecciona el nico elemento que hay: Data1 (o el nombre que le hayamos dado al control data). Ahora vamos a "enganchar" cada una de los textboxes con un registro de la mencionada tabla de autores: Selecciona el control Text1 y en la ventana de propiedades selecciona DataField, de la lista desplegable selecciona Au_ID. En los otros controles, selecciona Author para el Text2 y Year Born para el Text3. Ahora asignemo al caption de las etiquetas los datos que nos mostrar, asigna el Caption de cada una de las tres etiquetas, (ya sabes, pulsa en la etiqueta, muestra la ventana de propiedades y modifica la propiedad Caption), con estos valores: ID: para el Label1, Autor: para el Label2 y Ao nacimiento: para el Label3.

412

Y ya est!
Pulsa F5 para ejecutar el proyecto y vers que se muestra el primer registro, (seguramente en el ao de nacimiento Year Born, no te muestre nada hasta que llegues al registro 73.) Para mostrar el resto de registros, pulsa en los botones de "desplazamiento" del control data, dichos controles sirven para ir al: Primero, anterior, siguiente y ltimo respectivamente. Si escribes o modificas lo que se muestra, tambin se modificar en la base de datos. Fjate que no hemos usado ni una lnea de cdigo, entre otras cosas porque todo lo hemos realizado en tiempo de diseo. Pero ahora vamos a ver cmo hacer que todo esto funcione igual, pero en lugar de hacerlo en tiempo de diseo, lo haremos en tiempo de ejecucin, es decir: al ejecutar el proyecto, aunque la asignacin de la propiedad DataSource de las cajas de texto hay que hacerlo en tiempo de diseo, ya que no se puede hacer en tiempo de ejecucin . Para ello usaremos otro formulario, con los mismos controles que el actual, pero sin "conectarlos" a ninguna tabla de una base de datos ni nada de eso. Antes guarda el proyecto actual, yo lo he llamado Basico34.vbp, el formulario lo he guardado como fBasico34.frm Crea un nuevo proyecto, aade un formulario, en ste aade un Data control, tres etiquetas y tres cajas de texto, selecciona las tres cajas de texto y en la ventana de propiedades selecciona DataSource y asigna el Data1, adems de asignar el tamao, tanto de las cajas de texto como de las etiquetas. Ya deberas saber que cuando se ejecuta una aplicacin de Visual Basic y por defecto se muestra un formulario, al mostrarse dicho formulario, se ejecuta, (entre otros), el evento Form_Load, ser en este evento donde le indiquemos al Visual Basic que base de datos usaremos, que tabla y que campos. Veamos el cdigo, para poder introducir este cdigo, haz doble click en el formulario, por defecto se mostrar el evento Form_Load. ' Private Sub Form_Load() ' Indicarle el path de la base de datos ' ACUERDATE DE PONER EL PATH CORRECTO! Data1.DatabaseName = "C:\Program Files\Microsoft Visual Studio\VB98\BIBLIO.MDB" ' ' Indicarle que tabla queremos usar Data1.RecordSource = "Authors" ' ' Asignar a cada uno de los texboxes el campo de la tabla Text1.DataField = "Au_ID" Text2.DataField = "Author"

413

Text3.DataField = "Year Born" End Sub

Pulsa F5 y vers que tambin funciona. Para ir abriendo boca de lo que seguir, vamos a aadir una caja de texto en la cual se podr escribir un nmero y al pulsar INTRO se mostrar el autor que tenga ese nmero como Au_ID, para ello aade una nueva caja de texto, no asignes el DataSource, ya que este texbox no estar ligado a la base de datos. La posicin y el tamao de esa caja de texto que he puesto es la siguiente: Left= 1500, Top= 120, Width = 615 y 315 de altura. El nombre es Text4. Lo que vamos a hacer es que cuando se pulse INTRO en ese control, se convertir el valor introducido a un nmero y se har una bsqueda en el contenido del Data1, aqu te muestro el cdigo: ' Private Sub Text4_KeyPress(KeyAscii As Integer) ' Se buscar slo cuando pulsemos INTRO Dim nReg As Long ' ' Comprobar si la tecla pulsada es Intro: vbKeyReturn o 13 que es lo mismo If KeyAscii = vbKeyReturn Then ' Esta asignacin evita que suene un BEEP KeyAscii = 0 ' Convertir el contenido de TextBox en un nmero nReg = Val(Text4) ' Buscar la primera coincidencia en el recordset del Data1 ' en el campo Au_ID Data1.Recordset.FindFirst "Au_ID = " & nReg End If End Sub

Para realizar la bsqueda he usado FindFirst, esto har que se muestre (o asigne como registro activo) el primer registro que coincida con lo indicado, en este caso el Au_ID que tenga como valor el nmero indicado en la variable nReg o sea el valor introducido en el Text4. En caso de que no se halle el valor buscado, no se alterar el registro actual, es decir "aparentemente" no pasar nada y todo se quedar tal y como estaba... o bien se pondr en el primer registro, creo que es esto ltimo lo que ocurre...

414

Ahora vamos a buscar en el campo Autor. A diferencia del campo Au_ID, que es numrico y slo buscamos una coincidencia "exacta", el campo Autor es del tipo String y en l podramos buscar una coincidencia exacta, al igual que en el caso del ID o bien "algo" que est contenido en dicho campo, tal es el caso de que queramos buscar la primera coincidencia de una autora llamada Jane; como habrs comprobado, la informacin del autor se muestra en el formato Apellido, Nombre, por tanto si queremos buscar el nombre sera ms bien complicado, ya que lo que buscamos est al final... Para ello podemos usar los signos de comodines ? (interrogacin) y * (asterisco). -El primero de ellos sirve para indicarle que no tenga en cuenta el caracter que est en la posicin indicada por la interrogacin, por tanto, si buscamos esto: J?an, mostrar tanto Juan como Jean, es decir, dar por bueno cualquier secuencia que empiece por "J", tenga cualquier caracter y acabe por "an". -En el segundo caso, el del asterisco, si ste est al principio de lo escrito, le estaremos indicando que encuentre todo lo que tenga al final el texto indicado: *Jane, buscar el primer registro que acabe con Jane, pero si especificamos el asterisco al final: Jane*, mostrar todos los registros que empiecen por Jane, por ltimo, si usamos dos asteriscos, uno al principio y otro al final: *Jane*, nos mostrar el primero que "contenga" el nombre indicado, no importando los caracteres que tenga antes o despus. Cuando busquemos datos exactos la comparacin la haremos con el signo igual (=), pero si queremos usar la bsqueda con comodines no nos servir el signo igual, ya que hay que indicarle que queremos usar una comparacin ms verstil, en este caso usaremos LIKE. Hay que saber que al buscar en campos del tipo String (cadenas de caracteres), hay que usar los apstrofes para indicar el principio y final de la cadena buscada, si no lo hiciramos, se producira un error. Despus de lo dicho, el cdigo de bsqueda quedara de la siguiente forma: ' Private Sub Text4_KeyPress(KeyAscii As Integer) ' Se buscar slo cuando pulsemos INTRO Dim nReg As Long ' ' Comprobar si la tecla pulsada es Intro: vbKeyReturn o 13 que es lo mismo If KeyAscii = vbKeyReturn Then ' Esta asignacin evita que suene un BEEP KeyAscii = 0 ' Convertir el contenido de TextBox en un nmero nReg = Val(Text4) ' Buscar la primera coincidencia en el recordset del Data1 ' en el campo Author Data1.Recordset.FindFirst "Author Like '" & Text4.Text & "'" End If End Sub

415

Fjate que no he usado ningn comodn, por tanto tendrs que escribirlos t, por ejemplo, para buscar la primera Jane, tendrs que escribir: *Jane en la caja de texto y pulsar Intro. Haz la prueba, ejecuta el proyecto y escribe *Jane en el Text4 y pulsa Intro, te mostrar el registro 1897, (Lenser, Jane), ahora escribe *Jane* y tras pulsar Intro, te mostrar el registro 840 que contiene Janet, (Hamlin, Janet), escribe *J y vers que se muestra el registro nmero 1242, (Baer, J) para terminar estas pruebas, escribe J* y en esta ocasin se mostrar el registro 1, (Jacobs, Russell) Y ahora un ejercicio: Aade dos controles Options para que podamos buscar tanto por el nmero del ID como por el nombre del Autor, el aspecto del formulario sera este:

El Option1 ser el indique que se busque por el ID y Option2 ser para buscar por el campo Author. La solucin en la prxima entrega.

Bueno, hasta aqu ha llegado esta primera entrega referente al acceso a datos. En futuras entregas veremos cmo realizar bsquedas (o consultas), as como otras cosas ms, entre ellas acceder a una base de datos sin el Data Control y tambin al acceso a bases de datos del tipo ADO (activeX Data Objects). Pero eso ser en otra ocasin, hasta entonces, que lo programes bien! Nos vemos Guillermo P.S. Si quieres bajarte los ejemplos usados (y la solucin al ejercicio), pulsa este link. (basico34_cod.zip 4.35 KB) La pgina con las soluciones de esta entrega.

416

Curso Bsico de Programacin en Visual Basic

Aqu tienes la respuesta a la entrega 34. ' Private Sub Text4_KeyPress(KeyAscii As Integer) ' Se buscar slo cuando pulsemos INTRO Dim nReg As Long ' ' Comprobar si la tecla pulsada es Intro: vbKeyReturn o 13 que es lo mismo If KeyAscii = vbKeyReturn Then ' Esta asignacin evita que suene un BEEP KeyAscii = 0 ' Convertir el contenido de TextBox en un nmero nReg = Val(Text4) ' Buscar la primera coincidencia en el recordset del Data1 If Option1.Value Then ' en el campo Au_ID Data1.Recordset.FindFirst "Au_ID = " & nReg End If If Option2.Value Then ' en el campo Author Data1.Recordset.FindFirst "Author Like '" & Text4.Text & "'" End If End If End Sub

Curso Bsico de Programacin en Visual Basic


417

Se que hay muchos programadores de VB a los que no les gusta usar el Datacontol para acceder a las bases de datos; pero tambin se que usar este control es ms fcil que usar cdigo directo, aunque esto ltimo tambin lo vamos a ver a lo largo de este curso bsico, incluso lo vamos a mezclar con el Datacontrol, ya que se puede acceder a bases de datos de las dos formas de forma conjunta. En esta entrega vamos a ampliar el ejemplo usado en la entrega anterior, para aadirle algunas opciones nuevas, para aadir nuevos registros y para eliminar registros existentes, por tanto, si quieres conservar intacta la base de datos Biblio.mdb, te recomiendo que hagas una copia de la misma. Tambin vamos a usar la opcin de buscar que puse como ejercicio, por tanto, si no te has ledo las soluciones de la entrega 34, es conveniente de que le eches un vistazo. Vamos a empezar por mejorar la bsqueda en la base de datos:

Buscar en una base de datos.


En esta ocasin vamos a aadir un botn buscar y buscar siguiente, para que podamos seguir buscando a partir del ltimo registro encontrado. En el ejemplo anterior, se buscaba al pulsar Intro en la caja de textos, pero ahora vamos a crear un procedimiento Buscar, el cual, segn el parmetro recibido, buscar la primera coincidencia o seguir buscando desde el ltimo dato hallado. Antes de aadir un nuevo botn, vamos a modificar el cdigo actual para usar el nuevo procedimiento Buscar. Crea un nuevo procedimiento, en el men Herramientas (Tools), selecciona Aadir procedimiento..., llmalo Buscar y haz que sea privado, ya que no tiene ningn sentido que sea pblico, porque slo se usar desde el formulario. Tambin puedes copiar y pegar el siguiente cdigo: ' Private Sub Buscar() ' Procedimiento para buscar el dato indicado (12/Feb/01) Dim nReg As Long ' ' Buscar la primera coincidencia en el recordset del Data1 ' If Option1.Value Then nReg = Val(Text4) ' Data1.Recordset.FindFirst "Au_ID = " & nReg End If ' en el campo Au_ID ' Convertir el contenido de TextBox en un nmero

418

If Option2.Value Then '

' en el campo Author

Data1.Recordset.FindFirst "Author Like '" & Text4.Text & "'" End If End Sub Ahora hay que modificar el evento KeyPress del control Text4, para que llame al nuevo procedimiento. Borra el cdigo que haba anteriormente en ese evento y sustityelo por este otro: ' Private Sub Text4_KeyPress(KeyAscii As Integer) ' Se buscar slo cuando pulsemos INTRO ' ' Comprobar si la tecla pulsada es Intro: vbKeyReturn o 13 que es lo mismo If KeyAscii = vbKeyReturn Then ' Esta asignacin evita que suene un BEEP KeyAscii = 0 ' Llamamos al procedimiento Buscar: Buscar End If End Sub Prubalo, para que veas que todo funciona como antes.

Ahora, vamos a aadir un botn para que busque el primer registro que coincida con lo escrito, esto es para hacer lo mismo que cuando pulsas Intro en el Text4, pero para el usuario ser ms lgico que el hecho de tener que pulsar Intro. Por tanto, aade un nuevo botn, (no te preocupes por ahora dnde colocarlo en el formulario, ya lo haremos dentro de poco), cmbiale el nombre a cmdBuscar y el Caption a Buscar, (por defecto ser Command1) y escribe o aade este cdigo (tambin puedes hacerlo copiando y pegando): ' Private Sub cmdBuscar_Click() ' Simplemente llamamos al procedimiento Buscar: Buscar End Sub Poca cosa, verdad? Pues es igual de efectivo que pulsando Intro en el Text4, pruebalo para que veas que funciona.

419

Creo que ya es hora de que nos vayamos complicando la vida... Vamos a aadir un botn para seguir buscando a partir del ltimo dato hallado: Aade un nuevo botn, llmalo cmdBuscarSig y en el Caption pones: Buscar siguiente, (seguramente el texto lo escribir en dos lneas, pero no te preocupes). El cdigo a usar en el evento Click de ese nuevo botn sera prcticamente el mismo que en el de Buscar, aunque antes debemos aadir un nuevo procedimiento para que busque el siguiente dato al ltimo que busc: ' Private Sub BuscarSiguiente() ' Procedimiento para buscar el dato indicado (12/Feb/01) Dim nReg As Long ' ' Buscar la siguiente coincidencia, a partir del ltimo hallado ' If Option1.Value Then nReg = Val(Text4) ' Data1.Recordset.FindNext "Au_ID = " & nReg End If If Option2.Value Then ' Data1.Recordset.FindNext "Author Like '" & Text4.Text & "'" End If End Sub Si te fijas, el cdigo es prcticamente el mismo que el del procedimiento Buscar, lo nico que cambia es que aqu se usa FindNext en lugar de FindFirst. Es decir FindFirst busca el primer dato que coincida con lo buscado y FindNext el siguiente al ltimo que se busc. Para probar este nuevo procedimiento, en el evento cmdBuscarSig_Click, escribe: BuscarSiguiente. As es como quedara ese evento: Private Sub cmdBuscarSig_Click() ' Buscar el siguiente registro BuscarSiguiente End Sub ' en el campo Author ' en el campo Au_ID ' Convertir el contenido de TextBox en un nmero

Como hemos visto, el cdigo usado en los dos procedimientos de bsqueda son

420

prcticamente iguales, as que vamos a unificarlos para crear un slo procedimiento de bsqueda, de esta forma, refrescars tu memoria y sabrs algo ms de parmetros en procedimientos, as como parmetros opcionales.

Parmetros opcionales.
Como sabrs, (y si no lo sabes, te lo cuento yo ahora), a partir de la versin 4 de Visual Basic se pueden usar parmetros opcionales en los procedimientos y funciones. Esto quiere decir que podemos usar el procedimiento de varias formas, indicando todos los parmetros o slo los que realmente son necesarios. Por ejemplo, en el caso en que estamos ahora, el procedimiento Buscar podra tener un parmetro opcional para indicarle si es la primera bsqueda o la siguiente. No voy a entrar en demasiadas explicaciones, ya que este tema lo veremos de forma ms amplia en otra entrega, sobre todo porque tiene sus pormenores, o lo que es lo mismo, existen diferencias entre las versiones 4 y posteriores (al menos hasta la 6) de Visual Basic e incluso en las dos ltimas se puede usar de dos formas diferentes... En esta ocasin voy a usar el formato de las versiones 5 y 6, en estas versiones los parmetros opcionales pueden ser de un tipo de datos diferente a Variant y tambin pueden indicrsele un valor por defecto, para que, si no se especifica, tenga el valor indicado; por supuesto, si no le indicamos el valor, tendrn el valor que ese tipo de datos tengan por defecto, por ejemplo el tipo Boolean tendr un valor False si no se indica el valor. Cuando se indican parmetros con tipo, a diferencia de los parmetros del tipo Variant, no se puede usar IsMissing para comprobar si el parmetro se ha especificado o no, pero, eso es otro tema... Este es el cdigo del procedimiento Buscar y a continuacin te indico cmo llamarlo desde el evento Click del botn cmdBuscarSig: ' Private Sub Buscar(Optional ByVal Siguiente As Boolean = False) ' Procedimiento para buscar el dato indicado (12/Feb/01) ' Si Siguiente = True, se busca a partir del registro activo ' Si no se indica, (valdr False), buscar el primer registro Dim nReg As Long ' ' Buscar la primera coincidencia en el recordset del Data1 ' If Option1.Value Then nReg = Val(Text4) ' ' Si se busca el siguiente dato If Siguiente Then Data1.Recordset.FindNext "Au_ID = " & nReg ' en el campo Au_ID ' Convertir el contenido de TextBox en un nmero

421

Else Data1.Recordset.FindFirst "Au_ID = " & nReg End If End If If Option2.Value Then ' ' Si se busca el siguiente dato If Siguiente Then Data1.Recordset.FindNext "Author Like '" & Text4.Text & "'" Else "'" End If End Sub Data1.Recordset.FindFirst "Author Like '" & Text4.Text & End If ' en el campo Author

Private Sub cmdBuscarSig_Click() ' Buscar el siguiente registro Buscar True End Sub

Puedes borrar el procedimiento BuscarSiguiente, ya que no es necesario. Tampoco es necesario modificar el cdigo de los eventos Text4_KeyPress ni el de cmdBuscar_Click, ya que esos dos eventos llaman al procedimiento Buscar con el valor predeterminado y al ser opcional, no es necesario indicarlo... Si pruebas el nuevo cdigo, te dars cuenta de que, la primera vez, pulsando tanto en Buscar como en Buscar siguiente, encuentra lo mismo, esto es debido a que FindNext, busca a partir de la ltima posicin hallada y en el caso de buscar por primera vez, busca desde el principio. En el caso de que cambies el texto buscado y pulses en Buscar siguiente, se mostrar el prximo registro, desde el ltimo hallado, que contenga dicho texto, (estas pruebas hay que hacerlas en el campo Author, ya que no tienen ningn sentido hacerlo en el ID del autor). Para buscar el resto de autores con el nuevo texto, tendrs que pulsar en Buscar para que empiece a buscar desde el principio. Fjate en el cdigo, se da por hecho de que Siguiente tiene un valor, el cual, (al ser del tipo Boolean), puede ser False o True. En caso de que sea True, es decir se ha especificado con ese valor, se buscar con FindNext y en caso de no especificarse o de hacerlo con el valor False, se usar FindFirst. Lo mismo da: Buscar False que Buscar (sin parmetros) Esto ltimo: lo de no especificarse o si se especifica con el valor False, es algo que hay

422

que tener en cuenta si el parmetro fuese de tipo Variant (cosa obligatoria si usas VB4), ya que IsMissing slo nos informa si el parmetro no se ha especificado, pero si se especifica, puede hacerse con un valor False o True, por tanto, tambin habra que tenerlo en cuenta... Ya s que dije que lo iba a dejar para otra entrega... pero, que haces si ests usando VB4 dejar aqu el curso? As que vamos a ver el cdigo de Buscar para usar con VB4 o con las versiones 5 y 6 pero usando el tipo Variant. ' Private Sub Buscar(Optional ByVal vSiguiente As Variant) ' Procedimiento para buscar el dato indicado (12/Feb/01) ' ' Si Siguiente = True, se busca a partir del registro activo ' Si no se indica, (valdr False), buscar el primer registro ' ' Cuando el parmetro es de tipo Variant, ' no se puede indicar un valor por defecto, ' as que vamos a usar una variable interna para indicar el valor del parmetro Dim Siguiente As Boolean ' Dim nReg As Long ' ' Asignar correctamente el valor del parmetro indicado ' Si no se especifica, le asignamos el valor por defecto, en este caso: False If IsMissing(vSiguiente) Then Siguiente = False Else ' Si se indica, asignarlo a la variable interna Siguiente = CBool(vSiguiente) End If ' El resto del cdigo es igual que antes ' Buscar la primera coincidencia en el recordset del Data1 ' If Option1.Value Then nReg = Val(Text4) ' en el campo Au_ID ' Convertir el contenido de TextBox en un nmero

423

' ' Si se busca el siguiente dato If Siguiente Then Data1.Recordset.FindNext "Au_ID = " & nReg Else Data1.Recordset.FindFirst "Au_ID = " & nReg End If End If If Option2.Value Then ' ' Si se busca el siguiente dato If Siguiente Then Data1.Recordset.FindNext "Author Like '" & Text4.Text & "'" Else "'" End If End Sub Fjate que he cambiado el nombre del parmetro y he creado una variable interna que es la que posteriormente se usa para saber si se busca desde el primero o desde el anterior. En el caso de que no se haya especificado el parmetro, la comprobacin de IsMissing se cumple, por tanto aqu le indicaremos el valor que queramos que tenga por defecto, en nuestro ejemplo no es necesario asignar ningn valor, ya que False es el valor que tendr la variable Siguiente si no le asignamos ningn valor. Por tanto en este ejemplo, podramos haber hecho slo lo siguiente, sin necesidad de usar el IsMissing: Siguiente = CBool(vSiguiente) Pero lo he mostrado de la forma recomendable, para que sepas qu hacer en el caso de que el valor por defecto fuese otro del que Visual Basic asigna a las variables por defecto. Data1.Recordset.FindFirst "Author Like '" & Text4.Text & End If ' en el campo Author

Una vez visto el procedimiento de bsqueda, vamos a aadir dos nuevos botones para Aadir y Eliminar registros. Recuerda lo que te dije al principio: haz una copia de la base de datos para que las pruebas no afecten al contenido de la misma. Aade dos nuevos botones y asignales los nombres cmdAdd y cmdBorrar, as como los captions Aadir y Eliminar, el cdigo ser el siguiente: ' Private Sub cmdAdd_Click() ' Aadir un nuevo registro

424

Data1.Recordset.AddNew End Sub Private Sub cmdBorrar_Click() ' Eliminar el registro actual Data1.Recordset.Delete End Sub

Tanto cuando se aade como cuando se borra, se debera mover el registro actual para que los cambios tengan efecto en la base de datos, ya que si se aade un nuevo registro y el mismo no se actualiza, se pierde. Para probarlo, ejecuta el proyecto, pulsa en Aadir y directamente sin hacer nada ms cierra el ejecutable, (pulsando en la x del formulario o ventana). Ejecuta de nuevo el proyecto y si pulsas en el botn de ir al ltimo registro del Datacontrol, vers que no hay ningn registro nuevo. Ahora vuelve a pulsar en Aadir y escribe lo que quieras en las cajas de texto del Nombre y Ao de nacimiento y pulsa en el botn de ir al primer registro y despus al ltimo (ya sabes que me refiero a los botones del Datacontrol), vers que ahora si se muestra dicho registro y si pulsas en el botn de ir al anterior, vers que se salta un nmero... ese es el que haba reservado para nosotros, pero al no actualizar los datos, se perdi en el limbo... Ser por estos detalles que algunos programadores prefieran usar cdigo puro y duro en lugar del Datacontrol? Puede... pero no creo que sea por este motivo... ya que esto mismo se puede mejorar y no es necesario abandonar el Datacontrol para que todo funcione ms o menos como debiera... Primero vamos a hacer que los nuevos datos tengan algo, para ello, le asignaremos la cadena Nuevo al nombre del autor y haremos que se desplace al ltimo registro, para que los datos sean permanentes, (si no modificamos algunos de los campos, el nuevo registro se perdera) En el caso de Eliminar, vamos a movernos al primer registro, para que el registro activo sea uno con informacin. Este sera el cdigo de los eventos Click de los dos botones: ' Private Sub cmdAdd_Click() ' Aadir un nuevo registro Data1.Recordset.AddNew ' Aadimos algn texto, para que no se pierda este registro Text2 = "Nuevo" ' Movemos al ltimo registro para que los cambios se hagan permanentes ' y se muestre el nuevo registro Data1.Recordset.MoveLast End Sub

425

Private Sub cmdBorrar_Click() ' Eliminar el registro actual Data1.Recordset.Delete ' Movemos al primer registro para que los cambios se hagan permanentes ' (tambin podriamos haberlo movido al ltimo registro) Data1.Recordset.MoveFirst End Sub Toma nota de la forma en que se hara para mover al primer o al ltimo registro del Recordset. Tambin habra que tener en cuenta si hemos sobrepasado el principio o el final de los registros, cosa que ocurre cuando no hay ms registros, para ello podemos comprobar tanto EOF (End Of File, final del fichero) como BOF (Beginnig Of File, principio del fichero) y en caso de que sea cierto (True), avisar o simplemente no hacer nada... Esto slo habra que hacerlo al eliminar registros, ya que al aadir se supone que habr al menos el que aadimos... ' Private Sub cmdBorrar_Click() ' ' Comprobar que hay registros, porque si no hay, dar error If (Data1.Recordset.EOF Or Data1.Recordset.BOF) Then ' Avisar de que no hay registros Else ' Eliminar el registro actual Data1.Recordset.Delete ' ' Movemos al primer registro para que los cambios se hagan permanentes ' (tambin podriamos haberlo movido al ltimo registro) Data1.Recordset.MoveFirst End If End Sub

Y esto es todo por hoy. En la prxima entrega veremos cmo acceder a bases de datos usando el ADO datacontrol. Nos vemos Guillermo

426

P.S. Si quieres bajarte los ejemplos usados, pulsa este link. (basico35_cod.zip 3.11 KB)

Curso Bsico de Programacin en Visual Basic


En esta entrega veremos cmo usar el ADO Datacontrol para acceder a las bases de datos del tipo MDB (Access). El ejemplo usado ser parecido al de la entrega anterior, aunque con unos pequeos cambios, para usar el tipo de acceso a bases de datos ADO, en lugar de DAO. Me imagino que habrs ledo por ah las diferencias entre ADO y DAO, cosa que aqu no voy a hacer... porque si lo hiciera, seguramente acabara aburriendo hasta... a mi mismo. As que pasar a las explicaciones bsicas de cmo usarlo, que es la intencin de este curso bsico, aunque, cuando lo crea conveniente, profundizar en los temas que crea conveniente, as que... vamos al tema y veamos...

Cmo usar el ADO datacontrol.


Empecemos desde cero. Crea un nuevo proyecto, se crear un proyecto con un formulario. En el men Proyecto/Componentes... selecciona Microsoft ADO Data Control -seguramente ir acompaado de la versin del control, en mi caso me indica que es 6.0 (SP4) (OLEDB)Cuando pulses en aceptar, para cerrar el cuadro de dilogo de aadir componentes, te mostrar un nuevo objeto en la barra de herramientas. Asegrate de que tienes abierto el formulario y haz doble-click en dicho control, de esta forma se aadir un ADO datacontrol al formulario. Lo primero que hay que hacer es configurarlo para acceder a una base de datos, pero todo esto se puede hacer mediante cdigo, as que no voy a explicarte cmo hacerlo desde el IDE de Visual Basic. Aade el siguiente cdigo al evento LOAD del formulario:

' Indicar el path correcto de la base de datos ' ACUERDATE DE PONER EL PATH CORRECTO! Const sPathBase As String = "C:\Program Files\Microsoft Visual Studio\VB98\BIBLIO.MDB" ' ' Crear la conexin manualmente ' Con "Provider=Microsoft.Jet.OLEDB.4.0;" se permite abrir bases de datos de Access 2000

427

With Adodc1 .ConnectionString = _ "Provider=Microsoft.Jet.OLEDB.4.0;" & _ "Data Source=" & sPathBase & ";" ' Indicarle de que tabla vamos a leer los datos .RecordSource = "Authors" End With

Nota: He de aclarar que las pruebas sobre acceso a bases de datos con el ADO datacontrol estn realizadas con el Visual Basic 6.0 y el Service Pack 4 (SP4). Si el cdigo anterior te produjera un error, primero comprueba que est escrito correctamente (hay que empezar por lo ms simple), si sigue sin funcionar, prueba a cambiar el 4.0 que est entre comillas por 3.51, si an as tampoco funciona... prueba a instalar el ADO, creo que se instala automticamente con el Internet Explorer 5.5, si a pesar de todo esto tampoco funciona... no se que aconsejarte, aparte de que consigas el VB6 y el SP4... o tambin que pases de esta entrega... Te explico un poco el cdigo anterior. La constante es para que sea fcil modificarla si prefieres usar otra base de datos. El With Adodc1 es para indicarle al VB que vamos a modificar varias propiedades del ADO datacontrol, aqu supongo que no has modificado el nombre de dicho control, si lo has hecho, cambia Adodc1 por el nombre que hayas puesto. El mtodo .ConnectioString, segn dice la ayuda del Visual Basic: Contiene la informacin que se utiliza para establecer una conexin a un origen de datos. Osea, es lo que le dice al ADO datacontrol de que base de datos obtener los datos y tambin de que tipo es, en este caso es una base de datos del tipo Microsoft.Jet, o lo que es lo mismo, del tipo Access. Por tanto Provider= indica de que tipo de base de datos se trata y Data Source= el path de la base de datos en cuestin, (fjate que se usa la constante con el path y nombre de la base de datos). Por ltimo a la propiedad .RecordSource le indicamos que es lo que queremos que maneje el datacontrol, en este caso una tabla de la base de datos; aunque tambin podramos haberle indicado una consulta, pero esto ltimo lo veremos en otra ocasin. Ahora vamos a aadir unos cuantos controles al formulario, para poder trabajar con los datos manejados por el ADO datacontrol. Los controles que usaremos sern 3 etiquetas y 3 cajas de texto, pero ambos sern un array de controles, (en el ejemplo de las entregas 34 y 35 usamos controles independientes, pero creo que es ms cmodo usar array de controles).

Cmo crear un array de controles.


A estas alturas ya deberas saber cmo hacerlo, pero te voy a refrescar la memoria. Aade una etiqueta al formulario (se crear Label1). Seleccinala, (simplemente haz click en ella)

428

Del men Edicin, selecciona Copiar, (tambin puedes hacerlo con el botn derecho del ratn). Del men Edicin, selecciona Pegar, se mostrar un mensaje preguntando si quieres crear un array de Label1, contesta que si. Vuelve a pegar (Edicin/Pegar) Y ya tienes tres etiquetas con ndices que van desde cero a dos. Aade un TextBox y haz la misma operacin que con la etiqueta: Seleccinalo; cpialo; pgalo, contesta que SI al mensaje de que quieres crear un array; vuelve a pegar. Al final tendrs tres cajas de Texto llamados Text1 con ndices de cero a dos. Posiciona los controles de forma que las etiquetas estn alineadas con las cajas de texto, no te preocupes por el Caption de las etiquetas, ya que lo modificaremos desde cdigo. Aade unos cuantos controles ms, para que podamos hacer bsqueda, etc. tal y como se hizo en las dos entregas anteriores. Al final deberamos tener los siguientes controles: Control Label1 Text1 Label2 Text2 Option1 Option2 cmdBuscar cmdBuscarSig cmdSalir cmdAdd cmdBorrar Adodc1 ndices array) 0a2 0a2 Ninguno Ninguno Ninguno Ninguno Ninguno Ninguno Ninguno Ninguno Ninguno Ninguno del array (o ninguno si no es un

(no (no (no (no (no (no (no (no (no (no

es es es es es es es es es es

un un un un un un un un un un

array) array) array) array) array) array) array) array) array) array)

Y en esta imagen tienes una idea de cmo estn dispuestos:

El formulario de prueba de la entrega 36

429

Aqu te muestro el cdigo usado para que funcione este invento, como comprobars, es prcticamente el mismo que el usado en las entregas anteriores, con unas cuantas aadiduras para que funcione correctamente con el ADO datacontrol. Por tanto creo que no necesita ms explicaciones que las que se incluyen en el propio cdigo. El cdigo del procedimiento Buscar es el que tiene ms cambios, entre otras cosas porque la forma de buscar en ADO es diferente al de DAO, en este ltimo (DAO), se usan FindFirst, FindNext, etc. y en ADO slo existe Find. Tambin he usado un marcador en la rutina de buscar; por si no se hallan datos, que el registro activo sea el mismo que estaba antes de buscar. chale un vistazo a los comentarios que hay en el cdigo. ' '----------------------------------------------------------------------------' Prueba de ADO Datacontrol para la entrega 36 (14/Feb/01) ' ' Guillermo 'guille' Som, 2001 '----------------------------------------------------------------------------Option Explicit Private Sub Adodc1_MoveComplete(ByVal adReason As ADODB.EventReasonEnum, ByVal pError As ADODB.Error, adStatus As ADODB.EventStatusEnum, ByVal pRecordset As ADODB.Recordset) ' Mostrar el ID del registro actual ' si se pasa del primero o del ltimo, dar error On Error Resume Next ' Mostrar el ID del registro actual usando el recordset pasado como parmetro Adodc1.Caption = "Registro actual: " & pRecordset.AbsolutePosition ' Si da error, indicarlo (20/Sep/99) If Err Or pRecordset.BOF Or pRecordset.EOF Then Adodc1.Caption = "Ningn registro activo" ' Habra que moverlo a un registro con informacin ' *** Dejarlo comentado *** activos ' para que el procedimiento de bsqueda avise si no hay datos 'Adodc1.Recordset.MoveFirst

430

End If Err = 0 End Sub Private Sub cmdAdd_Click() ' Aadir un nuevo registro Adodc1.Recordset.AddNew ' Aadimos algn texto, para que no se pierda este registro Text1(1) = "Nuevo" ' Actualizamos los datos Adodc1.Recordset.Update ' Hacemos que se "recargue" los datos del recordset Adodc1.Refresh ' Movemos al ltimo registro para que los cambios se hagan permanentes ' y se muestre el nuevo registro Adodc1.Recordset.MoveLast End Sub Private Sub cmdBorrar_Click() ' Borrar el registro actual ' Se comprueba que haya algn registro activo, ' para ello se comprueba que no hayamos pasado del principio o el final del Recordset ' ' Comprobar que hay registros, porque si no hay, dar error If (Adodc1.Recordset.EOF Or Adodc1.Recordset.BOF) Then ' Avisar de que no hay registros Adodc1.Caption = "Ningn registro activo" Else ' Eliminar el registro actual Adodc1.Recordset.Delete ' ' Movemos al primer registro para que los cambios se hagan permanentes ' (tambin podriamos haberlo movido al ltimo registro) Adodc1.Recordset.MoveFirst End If

431

End Sub Private Sub cmdBuscar_Click() ' Buscar el primer registro que coincida con el dato buscado Buscar End Sub Private Sub cmdBuscarSig_Click() ' Buscar el siguiente Buscar True End Sub Private Sub cmdSalir_Click() Unload Me End Sub Private Sub Form_Load() ' Text2 = "" Option2.Value = True ' ' Indicar el path correcto de la base de datos ' ACUERDATE DE PONER EL PATH CORRECTO! Const sPathBase As String = "C:\Program Files\Microsoft Visual Studio\VB98\BIBLIO.MDB" ' ' Crear la conexin manualmente ' Con "Provider=Microsoft.Jet.OLEDB.4.0;" se permite abrir bases de datos de Access 2000 With Me.Adodc1 .ConnectionString = _ "Provider=Microsoft.Jet.OLEDB.4.0;" & _ "Data Source=" & sPathBase & ";" ' Indicarle de que tabla vamos a leer los datos .RecordSource = "Authors" End With ' Indicar el DataSource de los Textboxes ' ya que con ADO se puede asignar en tiempo de ejecucin Dim i As Long

432

For i = 0 To 2 Set Text1(i).DataSource = Adodc1 Next ' Asignar los campos Text1(0).DataField = "Au_ID" Text1(1).DataField = "Author" Text1(2).DataField = "Year Born" ' Mostrar en las etiquetas el campo a usar For i = 0 To 2 Label1(i).Caption = Text1(i).DataField & ":" Next End Sub Private Sub Text2_KeyPress(KeyAscii As Integer) ' Se buscar slo cuando pulsemos INTRO ' ' Comprobar si la tecla pulsada es Intro: vbKeyReturn o 13 que es lo mismo If KeyAscii = vbKeyReturn Then On Error Resume Next ' Esta asignacin evita que suene un BEEP KeyAscii = 0 ' Buscar End If End Sub Private Sub Buscar(Optional ByVal Siguiente As Boolean = False) ' Procedimiento para buscar el dato indicado (18/Ene/01) ' Si Siguiente = True, se busca a partir del registro activo Dim nReg As Long Dim vBookmark As Variant ' En ADO debe ser Variant, no vale un String Dim sADOBuscar As String ' ' Iniciamos la deteccin de errores On Error Resume Next '

433

' Buscar la primera coincidencia en el recordset del Data1 If Option1.Value Then ' Convertir el contenido de TextBox en un nmero nReg = Val(Text2) ' en el campo Au_ID sADOBuscar = "Au_ID = " & nReg End If If Option2.Value Then ' en el campo Author sADOBuscar = "Author Like '" & Text2.Text & "'" End If ' Guardar la posicin anterior, por si no se halla lo buscado... vBookmark = Adodc1.Recordset.Bookmark ' If Siguiente = False Then ' Buscar desde el principio Adodc1.Recordset.MoveFirst Adodc1.Recordset.Find sADOBuscar Else ' Busca a partir del registro actual Adodc1.Recordset.Find sADOBuscar, 1 End If ' Devolver un error si no se halla lo buscado ' aunque no siempre es as... If Err.Number Or Adodc1.Recordset.BOF Or Adodc1.Recordset.EOF Then Err.Clear MsgBox "No existe el dato buscado o ya no hay ms datos que mostrar." ' Posicionar el recordset en la posicin guardada Adodc1.Recordset.Bookmark = vBookmark End If End Sub

Y con esto acabamos la introduccin a las bases de datos. En futuras entregas veremos otras cosillas sobre el acceso a datos, aunque antes de seguir con el acceso a base de datos, empezaremos a ver un nuevo tipo de mdulo de cdigo: las clases. As que... preprate para lo que viene. Nos vemos Guillermo

434

P.S. Si quieres bajarte los ejemplos usados, pulsa este link. (basico36_cod.zip 4.21 KB)

Curso Bsico de Programacin en Visual Basic


Con la futura versin de Visual Basic, (la 7 o Visual Basic .Net), casi a la vuelta de la esquina, (se espera que para antes de que acabe este ao 2001, est en el mercado), es conveniente de que sepas de que va todo esto de las clases, no me refiero a "dar clases", (ensear a alguien), sino a los mdulos de clases, esos ficheros con la extensin .cls De todas formas, creo que ya iba siendo hora de atacar el tema de las clases, entre otras cosas porque creo que es conveniente conocer cmo usarlas en nuestras aplicaciones, ya que pueden aliviarnos la tarea de crear y re-usar cdigo; pero lo ms importante es porque, si se usan de la forma adecuada, pueden permitirnos crear aplicaciones "troceadas" de forma que podamos mejorar partes de esos trozos sin necesidad de cambiar nada del resto del cdigo que las usan... (no voy a adelantarme, as que, tendrs que esperar un poco para que realmente puedas entender de que estoy hablando).

Nota: Como ya coment en la entrega 30, la versin, (o versiones), de Visual Basic que hace mejor uso de las clases, sobre todo de las colecciones, es la versin 5, (y mejor an la 6, aunque con la versin 5 ser ms que suficiente), por tanto, si ests usando una versin anterior a la 5, puede que algunos de los ejemplos que muestre no funcione correctamente, sobre todo en el tema de las propiedades por defecto y en el manejo de colecciones propias. De todas formas, confo en que todo lo que explique te quede claro, incluso si usas la versin 4, ya que con versiones anteriores, no podrs usar los mdulos de clases, simplemente porque Visual Basic no sabe de que se trata! Si no tienes el Visual Basic 5 o superior, puedes bajarte del sitio de Microsoft una versin reducida, que aunque no te permite crear ejecutables, si que puedes probar todo lo que aqu veamos, aunque sea en el propio entorno integrado (IDE). Si no lo han cambiado de sitio, dicha versin de VB: la VB5CCE (Visual Basic 5 Control Creation Edition), la puedes encontrar en la siguiente direccin: http://msdn.microsoft.com/vbasic/downloads/tools/cce/default.aspx Nota del 30/Jun/2003: He actualizado el link del VB5CCE. Si vuelven a cambiarlo de sitio, es posible que en la direccin indicada no lo encuentres, as que tendrs que buscar en el sitio de Microsoft por "Control Creation Edition" y suerte! Tambin te puedes pasar por la pgina VB-Resumen de mi sitio y all seguramente el link est ms actualizado.

435

Ya sin ms prembulos, vamos a empezar con el tema que nos interesa:

Los mdulos de clases


Seguramente habrs odo hablar sobre Programacin Orientada a Objetos, (OOP Object Oriented Programing), Herencia, Polimorfismo, Encapsulacin o simplemente que existen un tipo de fichero en Visual Basic, (a partir de la versin 4 inclusive), llamado mdulos de clases, (esos fichero tienen la extensin .cls) Nota: Aunque no lo sepas, ya has estado trabajando con clases, ya que los formularios son realmente mdulos de clases de un tipo especial. Hasta ahora, todo el cdigo que hemos estado utilizando, sobre todo los procedimientos, los hemos podido usar de forma directa, sin tener que hacer nada especial. Por ejemplo, en el cdigo de la entrega anterior usbamos el procedimiento Buscar para realizar una bsqueda en la base de datos, dicho procedimiento estaba "escrito" en el propio formulario, por tanto la "visibilidad" o mbito del mismo est limitado a dicho formulario. El tema de la visibilidad ya se trat en la entrega 26, en dicha entrega vimos un ejemplo de un formulario y un mdulo BAS y cmo podamos acceder a unas variables y a un procedimiento declarados con el "modificador" Public. Tambin vimos cmo, podamos acceder a variables con el mismo nombre declaradas en el formulario y el mdulo BAS, simplemente indicando el nombre del mdulo delante de la variable en cuestin, por tanto, te recomiendo que, si no tienes claro esto de la visibilidad de las variables y los procedimientos, le eches un vistazo a dicha entrega. Simplemente, a ttulo recordatorio, te dir que la visibilidad es el mbito que tienen las variables y los procedimientos, es decir en que parte del cdigo son accesibles (visibles) y en cuales no. Para usar los mdulos de clases es muy importante tener estos conceptos claros, ya que de no ser as, vas a estar ms perdido que una chiva en un garaje y no te enterars de nada... te aviso.

Clases = Objetos (y viceversa)


Como te deca antes, sin saberlo, has estado usando objetos todo el tiempo. Un formulario es un tipo especial de clase, un botn, (CommandButton), es un objeto, (internamente es una clase). Todos los objetos tienen la caracterstica de tener sus propios mtodos, propiedades e incluso eventos. Por ejemplo, el mencionado botn, tiene propiedades, como Caption, Visible, etc, Tambin tiene eventos como Click, MouseMove, etc. Y tambin tiene mtodos, por ejemplo Refresh, Move, etc. Lo mismo ocurre con los formularios, tambin tienen mtodos, propiedades y eventos. Pero todos estos objetos ya existen, simplemente los usamos y ya est. Lo que en esta entrega (y en otras ms) aprenderemos es cmo poder crear nuestros propios objetos, con sus mtodos, propiedades y eventos, y todo ello gracias a los mdulos de clases. Nota: En el resto de esta entrega, usar tanto la palabra objeto como la palabra clase, estas se referirn tanto a los objetos ya existentes, (ms comnmente conocidos como controles), como a los que nosotros podamos crear, ya sean tanto objetos de

436

cdigo como controles propiamente dicho, (en futuras entregas aprenderemos a crear nuestros propios controles ActiveX u OCX). Slo espero que no te confundas.

Clases/Objetos = Encapsulacin:
La ventaja de usar clases en nuestros proyectos es que podemos encapsular todo el cdigo y tratarlas como si de cajas negras se tratasen, es decir, sabemos que es lo que hacen, cmo usarlas, pero no es imprescindible saber el cdigo que se usa para que todo funcione. Por tanto una de las caractersticas principales que un objeto (o clase) debe tener es que no dependa del exterior, es decir, que sea autosuficiente: todo la informacin que necesite se debe suministrar mediante las propiedades que dicha clase u objeto exponga al exterior, (mediante las propiedades pblicas) y de igual manera, toda la informacin que un objeto deba mostrar al mundo exterior, se haga mediante las propiedades, mtodos y eventos que dicha clase exponga al exterior, (declarados como pblicos). Bien, para entrar en calor, creo que lo mejor es ver un ejemplo. Asi que abre el Visual Basic, crea un nuevo proyecto EXE y sigue estos pasos para aadir un mdulo de clase: Abre el men Proyecto y pulsa en: Aadir Mdulo de Clase, te mostrar un cuadro de dilogo mostrndote varias tipos de mdulos de clase, pulsa en la primera que te muestra: Mdulo de Clase, pulsa en Abrir y se aadir un nuevo fichero, si te fijas en la ventana de propiedades, tendr como nombre: Class1, (al menos as es como se llama en la versin inglesa de Visual Basic, que es la que yo uso). Ese nombre ser el que identificar al objeto cuando queramos acceder a l, (realmente ser el que usaremos para crear variables que accedan a dicho objeto), por tanto es importante darle un nombre con el cual podamos, ms o menos, identificar el uso para el que se ha creado dicho objeto. Por tanto vamos a darle un nombre a la clase, y como en este ejemplo vamos a usar el objeto para almacenar los datos de un "colega" nuestro, le daremos el nombre: cColega. Nota: Tambin puedes nombrarla Colega, la "c" es para indicar que se trata de una clase; no es una norma obligatoria, pero... es ampliamente usada por muchos programadores de Visual Basic, (entre ellos yo); otros usan el prefijo de la "c" slo para el nombre del fichero, as que queda a tu criterio usarla en el nombre de la clase o no. Esta clase tendr varias propiedades, una para cada uno de los datos que manipular, el Nombre, el e-mail y la fecha de nacimiento, adems de un mtodo que nos indicar que edad tiene dicho colega. Veamos cmo aadir propiedades y mtodos a una clase:

Aadir propiedades a una clase (u objeto):


Para crear las propiedades, podemos hacerlo de dos formas:

437

1. La forma ms simple, es declarando las propiedades como variables pblicas: Cuando declaramos una variable Public en un mdulo de clase, dicha variable se convierte en una propiedad. 2. La otra forma es creando un procedimiento Property. La ventaja de usar los procedimientos Property, es que podemos hacer comprobaciones extras que con las variables sera imposible hacer. Por ejemplo, al asignar un valor a la propiedad e-mail, se podra comprobar que dicho valor contenga el signo arroba (@); en el caso la fecha de nacimiento, se podra validar que fuese una fecha correcta y en cualquiera de estos casos poder producir un error e incluso un evento para avisar de que algo no se ha hecho bien. Al asignar un valor a una variable, dicho valor se asigna sin ms, no se comprueba nada de nada, simplemente se asigna, sin embargo al usar un procedimiento para hacer dicha asignacin podemos hacer las comprobaciones necesarias para asegurarnos que lo que se est asignando es lo correcto. Cuando usamos procedimientos Property para almacenar valores de propiedades, nos vemos obligados a usar una variable que mantenga el valor asignado a dicha propiedad, esa variable se suele ocultar del mundo exterior declarndola Private, de esta forma esa variable slo es visible dentro del cdigo del mdulo de clase. Recuerdas lo que te dije del mbito o visibilidad de las variables? Veamos el cdigo que se podra usar para implementar las propiedades de la clase cColega: Para el nombre del colega, simplemente declaramos una variable pblica: Public Nombre As String Para el e-mail crearemos una variable privada para guardar internamente la direccin de e-mail: (como no podemos usar el signo - en el nombre de una variable, tendremos que usar email, sin signo de separacin) Private m_email As String m_ es para indicar que es una variable declarada a nivel de mdulo y por tanto visible en todo el mdulo en el que se ha declarado. Ahora tenemos que crear el procedimiento Property. ste podemos crearlo de dos formas, usando la opcin Aadir procedimiento... del men de Herramientas o escribiendo el cdigo directamente... vamos a usar el primer mtodo: Selecciona Aadir Procedimiento del men Herramientas, te mostrar un cuadro de dilogo preguntando que tipo de procedimiento quieres crear y con que nombre. Selecciona la opcin Property (Propiedad), en mbito (Scope) selecciona Public y escribe email en la caja de texto. Se crearn dos nuevos procedimientos: Public Property Get email() As Variant End Property Public Property Let email(ByVal vNewValue As Variant)

438

End Property Cada uno de estos dos procedimientos son: Property Get para devolver el valor de la propiedad y Property Let para asignar un nuevo valor a la propiedad. Nota: Por defecto el tipo de datos asignado a un procedimiento Property es del tipo Variant, por tanto habr que modificarlo para que sea del tipo que vamos a usar. En el procedimiento que se usa para devolver el contenido actual de la propiedad, simplemente devolvemos el contenido de la variable privada:

Public Property Get email() As String ' Devolver el contenido de esta propiedad, ' el cual est almacenado en la variable privada email = m_email End Property En el procedimiento que se usa para asignar un nuevo valor a la propiedad, es donde tenemos que hacer la comprobacin de que se asigna un valor que representa una direccin de correo electrnico, (realmente slo se comprueba que tenga el signo @)

Public Property Let email(ByVal NewValue As String) ' Comprobar que el nuevo valor a asignar tenga el signo @ If InStr(NewValue, "@") = 0 Then ' Asignamos los datos del error, pero no lo producimos With Err .Number = 13 .Description = "El valor asignado a email no es una direccin de correo electrnico." .Source = "cColega.email = " & NewValue ' Para producir el error, usariamos '.Raise 13 End With Else ' Asignamos el nuevo valor a la variable privada m_email = NewValue End If End Property

439

En este procedimiento se comprueba que la cadena tenga el signo arroba, si no es as, la comparacin realizada se cumple y se asigna al objeto Err un error indicando lo que se ha hecho mal; si quitas el comentario que hay delante de .Raise, el error se produce en la clase y de esa forma no se detectara en la aplicacin que use la clase. En caso de que la cadena asignada a la propiedad tenga el signo @, el nuevo valor se asignar a la variable privada. Dentro de un poco veremos el cdigo usado para asignar los valores a las propiedades y manejar los errores que se produzcan. Antes vamos a codificar la propiedad FechaNacimiento. Como esta propiedad contendr una fecha, lo lgico sera que el tipo de datos fuese Date, el problema o la ventaja, segn se mire, es que si asignamos un valor errneo a dicha propiedad, ser el propio Visual Basic el que se encargue de avisarnos de que no es correcta. Pero si queremos detectar el valor errneo dentro del procedimiento o bien usar algn tipo de conversin "inteligente" de una fecha que supuestamente es errnea, (por ejemplo: permitir introducir la fecha con distintos signos de separacin y asignarlo en el formato que internamente queramos), entonces lo mejor sera usar otro tipo de datos, por ejemplo del tipo String; como siempre, quedar a tu criterio el usar un tipo de datos u otro. Sea como fuere, necesitaremos una variable privada para mantener el valor asignado, en este caso he declarado una variable de esta forma: Dim m_FechaNacimiento As Date El declarar una variable de tipo Date no implica, ni obliga a, que tengamos que usar ese tipo de datos en los procedimientos de la propiedad, lo nico obligatorio es que tanto el valor devuelto por el procedimiento Property Get sea del mismo tipo de datos que el de la variable usada en el procedimiento Property Let. Es decir, no podemos tener un procedimiento Property devolviendo un tipo de datos y recibiendo otro diferente, independientemente del tipo de datos que tenga la variable privada usada para contener el valor. La forma ms fcil de "codificar" ese procedimiento sera de esta forma: Public Property Let FechaNacimiento(ByVal NewValue As String) ' Si no es una fecha correcta... If IsDate(NewValue) = False Then ' Asignar un error With Err .Number = 13 .Description = "El valor asignado a FechaNacimiento no es una fecha vlida." .Source = "cColega.FechaNacimiento = " & NewValue End With Else m_FechaNacimiento = NewValue End If End Property

440

Esta otra es algo ms complicada, en ella se permite aceptar fechas en diferentes formatos as como tambin en formatos sin separadores, estos son algunos de los "valores" aceptados para la fecha 13/04/2001: 130401, 13042001, 134, 1304, 13/4, 13/4/01, 13/4/2001, 13.4.01, 13.04.01, 13-04-01, 13-4, etc. e incluso valores como 0413, 04.13.01 (en este caso porque no hay duda de que 13 no puede ser un mes...).

Public Property Let FechaNacimiento(ByVal NewValue As String) Dim i As Long Dim s As String ' ' Comprobar si se usan puntos como separador ' si es as, cambiarlos por / Do i = InStr(NewValue, ".") If i Then Mid$(NewValue, i, 1) = "/" End If Loop While i ' ' Comprobar si se usan - como separador ' si es as, cambiarlos por / Do i = InStr(NewValue, "-") If i Then Mid$(NewValue, i, 1) = "/" End If Loop While i ' s = "" Do i = InStr(NewValue, "/") If i Then s = s & Right$("0" & Left$(NewValue, i - 1), 2) & "/" NewValue = Mid$(NewValue, i + 1) End If Loop While i NewValue = s & NewValue ' If InStr(NewValue, "/") Then

441

If Len(NewValue) = 5 Then ' Si es igual a 5 caracteres, es que falta el ao NewValue = NewValue & "/" ElseIf Len(NewValue) < 3 Then ' Si es menor de 3 caracteres es que falta el mes NewValue = NewValue & "/" & CStr(Month(Now)) & "/" End If ElseIf Len(NewValue) < 3 Then NewValue = NewValue & "/" & CStr(Month(Now)) & "/" Else s = "" For i = 1 To 2 s = s & "/" & Mid$(NewValue, (i - 1) * 2 + 1, 2) Next s = s & "/" & Mid$(NewValue, 5) NewValue = s End If NewValue = Trim$(NewValue) ' ' Comprobar si tiene una barra al principio, si es as, quitarla If Left$(NewValue, 1) = "/" Then NewValue = Mid$(NewValue, 2) End If ' Si tiene una barra al final, es que falta el ao If Right$(NewValue, 1) = "/" Then NewValue = NewValue & CStr(Year(Now)) End If ' ' Convertir la fecha, por si no se especifican todos los caracteres ' Nota: Aqu puedes usar el formato que ms te apetezca NewValue = Format$(NewValue, "dd/mm/yyyy") ' ' Si no es una fecha correcta... If IsDate(NewValue) = False Then ' Asignar un error With Err .Number = 13

442

.Description = "El valor asignado a FechaNacimiento no es una fecha vlida." .Source = "cColega.FechaNacimiento = " & NewValue End With Else m_FechaNacimiento = NewValue End If End Property Bien, todo esto que hemos visto hasta ahora es aplicable a cualquier tipo mdulo, no solo a los de clases, (por ejemplo, en ciertas ocasiones es bastante til usar procedimientos Property en los formularios). Ahora vamos a ver cmo podemos usar esta clase en un proyecto y poder comprobar cmo se detectan los posibles errores si la asignacin no es correcta. Para probar, vamos a usar el formulario que tenemos en el proyecto que hemos creado y en el cual debe estar la clase que acabamos de ver. Nota: Deja tu mente en blanco, y presta atencin a lo que sigue... si an asi no consigues entenderlo... apntate a algn curso de meditacin trascendental y despus sigue leyendo...

Cmo usar las clases, (objetos), en un proyecto.


(Ahora viene el meollo de la cuestin) Si los procedimientos que acabamos de crear estuviesen en un mdulo BAS, los usaramos sin ms, pero, como dichos procedimientos estn "incluidos" en un mdulo de clase, o en un objeto, (segn prefieras llamarlo), antes tenemos que tener una variable que sea del mismo tipo que dicho objeto y adems crearlo. Ya que los procedimientos, (Sub, Function o Property), declarados en un mdulo de clase slo existen, o son accesibles o son visibles, porque pertenecen a la clase (u objeto). Es decir, no es suficiente tener una variable del tipo especificado, sino que le tenemos que indicar al Visual Basic que lo cree... que le de vida... (complicado? un poco, no todo va a ser coser y cantar... que te crees?) Todos los procedimientos e incluso las variables pblicas declaradas en un mdulo de clase "pertenecen" a dicha clase y se convierten en propiedades y mtodos de dicha clase y por tanto slo son accesibles gracias a que dicha clase "existe". Veamos cmo crear una variable del tipo cColega: Dim oColega As cColega Esto simplemente le indica al Visual Basic que la variable oColega es del tipo de cColega, pero nada ms. Si intentramos usarla, nos dara un error diciendo que: "la variable de objeto no est establecida" Y es cierto... ya que, lo nico que hemos hecho es indicar que oColega puede "apuntar" a un objeto del tipo cColega, pero no le hemos dicho "qu" objeto es.

443

Para clarificar un poco las cosas: (si es que todo esto se puede clarificar...) Cuando aadimos un control a un formulario, Visual Basic crea una variable, (con el nombre que le hemos dado a dicho control), y de forma automtica, (sin que nos enteremos), crea un nuevo objeto que lo asigna a dicha variable; sin embargo con las clases, somos nosotros los que tenemos que decirle que cree un nuevo objeto y lo asigne a dicha variable. Por tanto, despus de declarar una variable para que contenga el objeto, hay que crear un objeto nuevo y asignarlo a dicha variable, todo esto se hace de la siguiente forma: (aunque no siempre tiene porqu ser as... Guille! no compliques ms la cosa, que bastante complicada est ya...), Set oColega = New cColega Esto le indica al VB que cree un nuevo objeto del tipo cColega: New cColega y dicho objeto lo asigne a la variable oColega: Set oColega = Nota: Para asignar un objeto a una variable, se usa SET, si no usamos Set, lo que estaramos asignando a dicha variable sera el contenido de la propiedad por defecto. Por ejemplo, para guardar en una variable el control Text1, haramos esto: Dim unTextBox As TextBox Set unTextBox = Text1 Pero para guardar en una variable el contenido de la propiedad por defecto de Text1, (es decir el contenido de la propiedad Text), haramos esto otro: Dim sUnTexto As String sUnTexto = Text1 Que es lo mismo que hacer: sUnTexto = Text1.Text

Seguramente dirs... tan complicado es de entender? Si est "chupao" Me gustara creer que no te ha parecido tan complicado y que lo has entendido al 100%, pero... No te preocupes! Te aseguro que acabars entendindolo! ya que si no lo haces, te quedars estancado con el Visual Basic 6 y no podrs disfrutar de lo que la nueva versin ofrece... je, je, je... as que... si no lo has entendido... ponte las pilas y aplcate al mximo. Veamos ahora el cdigo del formulario con el cdigo para usar la clase. Este formulario tiene tres etiquetas, tres cajas de texto: Text1(0), Text1(1) y Text1(2) un botn llamado cmdAsignar y un ListBox.

Private Sub cmdAsignar_Click() ' Dimensionamos una variable del tipo del objeto (clase) Dim oColega As cColega ' Creamos un nuevo objeto y lo asignamos a la variable Set oColega = New cColega ' ' Detectamos los errores que se produzcan On Error Resume Next

444

' ' Asignamos a la propiedad Nombre el contenido de Text1(0) oColega.Nombre = Text1(0).Text ' Asignamos a la propiedad email el contenido de Text1(1) oColega.email = Text1(1).Text ' Si se produce un error If Err Then ' Mostrar el mensaje de aviso MsgBox "Se ha producido un error en: " & Err.Source & vbCrLf & Err.Description ' Posicionar el cursor en la caja de textos del email Text1(1).SetFocus ' Limpiamos el error Err = 0 ' Salimos de este procedimiento Exit Sub End If ' ' Asignamos la fecha de nacimiento oColega.FechaNacimiento = Text1(2).Text If Err Then ' Mostrar el mensaje de aviso MsgBox Err.Number & ", se ha producido un error en: " & Err.Source & vbCrLf & Err.Description ' Posicionar el cursor en la caja de textos del email Text1(2).SetFocus ' Limpiamos el error Err = 0 ' Salimos de este procedimiento Exit Sub End If ' ... resto del cdigo List1.AddItem oColega.Nombre & " - " & oColega.FechaNacimiento End Sub

Otras formas de declarar una clase.


Para terminar esta entrega, (y dejarte que recapacites sobre la "existencialidad" de los objetos), vamos a ver otra forma de "crear" un objeto al mismo tiempo que lo declaramos: Dim oColega As New cColega

445

Esto lo explico, entre otras cosas, para que sepas que se puede hacer, ya que seguramente lo habrs visto en los ejemplos que acompaan al Visual Basic as como en algunos listados que hayan podido caer en tus manos, incluso (mea culpa) en listados que yo mismo he escrito, aunque prometo que NUNCA ms lo volver a hacer... e incluso te pedira que me dijeras en que listados, (siempre que sean mos), has visto esa forma de declarar las clases, para que pueda corregirlo... Te recomiendo y te rogara, (y si pudiera, te obligara), encarecidamente de que NUNCA crees clases de esta forma. Por qu no es recomendable crear las clases de esta forma, si es ms fcil e incluso ms lgico? Porque crear una clase usando New al mismo tiempo que se declara un objeto aade ms cdigo, (aunque nosotros no lo veamos), adems de que no tenemos el control total sobre cuando se crea el objeto, ya que el objeto se crea cuando intentamos usarlo, no cuando "explcitamente" le indicamos que lo cree... Por qu aade ms cdigo? Porque Visual Basic nunca sabe si el objeto est creado o no, as que, junto a cada acceso al objeto aade una comprobacin para saber si dicho objeto est creado o no, ya que si no est creado, "tiene" que crear uno nuevo para que podamos usarlo.

Cuando se destruye un objeto?


(es decir: cuando deja de existir un objeto?) Un objeto, (o una variable que apunte o haga referencia a un objeto), tiene la misma duracin o mbito que cualquier variable. Por ejemplo: un objeto declarado dentro de un procedimiento existe mientras dure dicho procedimiento, cuando el procedimiento termina, el objeto "desaparece", se esfuma, deja de existir.

Cmo podemos destruir explcitamente un objeto?


(es decir: cuando podemos hacer que un objeto desaparezca porque nosotros queremos que desaparezca?) Si queremos que una variable que apunta a un objeto deje de hacer referencia a ese objeto, tendremos que asignarle el valor Nothing a dicha variable, (usando Set): Set oColega = Nothing. No es por complicarte la vida, pero no siempre todo esto es cierto... ya que el Visual Basic se encarga de saber cuando se puede "liberar" un objeto, para ello lleva su propia "cuenta" de cuando un objeto debe estar "vivo" o cuando debe acabar con su existencia... todo esto es aplicable tambin a los formularios, ya que, como dije al principio, los formularios tambin son tambin clases u objetos, con un tratamiento diferente, pero clases al fin y al cabo. Para muestra un botn. Crea un nuevo proyecto Exe, aade un segundo formulario, (el Form1 se crea junto con el proyecto), aade una etiqueta al segundo formulario (Form2) que ocupe el ancho del mismo. Aade este cdigo al Form2:

446

'----------------------------------------------------------------------------' Segundo formulario para "probar" que no todo es lo que parece (14/Abr/01) ' ' Guillermo 'guille' Som, 2001 '----------------------------------------------------------------------------Option Explicit ' Valor interno para la propiedad del Form2 Private m_FechaCreacion As Date Private Sub Form_Load() ' Asignar el valor de la fecha de creacin ' (aunque realmente es la fecha en que se carga el formulario) m_FechaCreacion = Now ' Label1 = "Form creado el: " & m_FechaCreacion End Sub Public Property Get FechaCreacion() As Date Let) ' Propiedad de slo lectura (no existe el procedimiento Property FechaCreacion = m_FechaCreacion End Property En el primer formulario aade dos etiquetas (Label1 y Label2) y dos botones (Command1 y Command2) y aade este cdigo: '----------------------------------------------------------------------------' Formulario para "probar" que no todo es lo que parece (14/Abr/01) ' ' Guillermo 'guille' Som, 2001 '----------------------------------------------------------------------------Option Explicit

447

Private Sub Command1_Click() ' Mostramos el Form2 Form2.Show ' Mostrar la fecha de creacin Label1 = Form2.FechaCreacion End Sub Private Sub Command2_Click() ' Descargar el formulario Unload Form2 ' Si no se asigna Nothing al Form2, ' la fecha mostrada ser la misma que antes... es decir, an existe el objeto 'Set Form2 = Nothing ' ' Una vez descargado, accedemos a la propiedad de la fecha de creacin: Label2 = "El Form2 se cre el: " & Form2.FechaCreacion End Sub Ejecuta el proyecto y pulsa en el Command1 para que se muestre el Form2. Pulsa en el Command2 para descargar el Form2. Fjate que la fecha mostrada es la misma que cuando se cre dicho formulario. Esto prueba que el formulario, a pesar de estar descargado, an existe, ya que conserva el valor de la variable interna y si esa variable existe es que el formulario an est en algn lugar de la memoria. Ahora quita el comentario que hay delante de Set Form2 = Nothing y vuelve a ejecutar el proyecto. Pulsa primero en Command1 y despus en Command2 (igual que antes) Fjate que la fecha mostrada al cerrar el formulario es otra, (realmente es 00:00:00) Esto, aunque parezca que prueba algo... realmente no prueba nada, salvo que la fecha, (guardada en la variable privada), no est asignada. Y no est asignada, porque al asignar Nothing al "objeto" Form2, ste se destruye y con l se destruye la variable. Pero al acceder de nuevo a la propiedad, el Form2 se vuelve a crear ya que, Visual Basic usa una variable, (llamada Form2), como si estuviese declarada con New, ( es decir VB hace algo parecido a esto: Dim Form2 As New Form2), por tanto, cada vez que usamos dicha variable, si el objeto no existe, Visual Basic vuelve a crearlo, pero el valor de la propiedad no se asigna, ya que la asignacin se hace en el evento Load y ese evento slo se produce al cargarse el formulario de forma explcita con Load o al mostrarlo con Show. El evento que siempre se ejecuta al crearse un objeto (o clase), es Initialize, en el caso de los formularios es: Form_Initialize. Por tanto, si en ese evento se asigna el valor de la fecha de creacin a la variable m_FechaCreacion...

448

Private Sub Form_Initialize() m_FechaCreacion = Now End Sub Que valor se mostrar en Label2 despus de asignar Nothing a Form2? 1.- La misma que haba al mostrar el formulario, (la que se muestra en Label1). 2.- El valor 00:00:00 3.- Un valor posterior al mostrado en Label1. Solucin: 3.- Un valor posterior al mostrado en Label1, ya que al acceder a una propiedad de una clase que no existe, es decir, que an no est creada, dicha clase se crea y por tanto se produce el evento Initialize de la clase y es en este evento cuando se asigna el valor de la fecha actual. Si quieres, quita la asignacin que hay en el evento Form_Load del Form2 y vers que an as, el valor es diferente. Te das cuenta del "peligro" de usar New al declarar un objeto? Por qu lo hace Visual Basic con los formularios a pesar de saber que es peligroso? Seguramente para facilitarnos las cosas, pero... (por suerte, esto cambia en la nueva versin de Visual Basic: VB.NET) Para terminar, (seguramente esto har que lo entiendas mejor... en eso confo...), vamos a ver que es lo que ocurre si nos "saltamos" la auto creacin que hace Visual Basic de la variable que contiene la clase Form2, (recuerda que internamente y sin que nos enteremos, declara una variable de esta forma: Dim Form2 As New Form2) Cmo nos saltamos esta auto creacin? Creando nosotros mismos una variable que apunte a la clase Form2, tal y como haramos con cualquier otra clase. Por tanto modifica el cdigo del Form1 para que sea como el que te muestro: (el cdigo del Form2 queda igual) '----------------------------------------------------------------------------' Formulario para "probar" que no todo es lo que parece (14/Abr/01) ' ' Guillermo 'guille' Som, 2001 '----------------------------------------------------------------------------Option Explicit ' ' Variable del tipo Form2 Private elForm2 As Form2 Private Sub Form_Load() ' Creamos el objeto Set elForm2 = New Form2

449

End Sub Private Sub Command1_Click() ' Mostramos el Form2 elForm2.Show ' Mostrar la fecha de creacin Label1 = elForm2.FechaCreacion End Sub Private Sub Command2_Click() ' Descargar el formulario Unload elForm2 ' Destruimos el objeto (lo eliminamos de la memoria) Set elForm2 = Nothing ' ' Si una vez descargado, accedemos a la propiedad de la fecha de creacin: ' ESTO PRODUCIRA UN ERROR, YA QUE LA VARIABLE elForm2 NO EXISTE! Label2 = "El Form2 se cre el: " & elForm2.FechaCreacion End Sub Ejecuta el proyecto y haz la misma prueba que antes, primero pulsa en Command1 y despus en Command2. En esta ocasin se mostrar un error de que la variable no est establecida. Lo comprendes? Cuando asignamos Nothing a una variable que apunta a un objeto, dicha variable se libera de cualquier referencia, es decir, se destruye el objeto al que apuntaba y por tanto ya no est asignada. Aunque todo este rollo te lo he mostrado con formularios en lugar de clases creadas por nosotros, es por dos motivos: El primero es para que lo puedas entender mejor, espero! El segundo es para que te des cuenta que un formulario realmente es una clase. La moraleja de todo esto es para que "desistas" en usar variables de clases dimensionadas con New. Segunda moraleja: si usas formularios con variables creadas por ti mismo, NO USES NUNCA LA VARIABLE CREADA POR VISUAL BASIC, ya que si lo haces, todo lo aprendido se va al garete... y si no, prueba esta lnea, ponla justo despus de la asignacin a Nothing que hacemos a la variable elForm2 en el evento Command2_Click: Label2 = "El Form2 se cre el: " & Form2.FechaCreacion Al usar la variable creada por Visual Basic, no se producir ningn error, pero el objeto sigue estando en el limbo del VB... Je, je, je... que me gusta liar las cosas...

450

Y hasta aqu hemos llegado por hoy... (mucho folln, verdad?) No te preocupes si an no tienes las cosas muy claras, ya que en las prximas entregas seguiremos trabajando con clases. Y ya sabes que la mejor forma de aprender es practicando, as que, practicaremos y practicaremos hasta que aprendas. Nos vemos Guillermo

Curso Bsico de Programacin en Visual Basic


Espero que la entrega anterior no te "desclasificara" y te dieran ganas de dejar esto de la programacin... Se supone que sigues interesndote esto de programar con el Visual Basic, sino, no estaras aqu... As que, sigue con la mente abierta e intenta asimilar todo esto, ya que, slo es el principio... dentro de poco se complicar ms. En esta entrega vamos a ver un objeto del Visual Basic que nos permitir agrupar otros objetos, me refiero al objeto Collection. Con ese objeto podremos crear colecciones de clases, (o de cualquier tipo de dato que queramos, incluso datos de diferente tipo mezclados).

Qu es una coleccin de clases?


Para simplificarlo, te dir que una coleccin es casi como un array. Las colecciones las usaremos cuando necesitemos almacenar varios objetos individuales, para poder acceder a cualquiera de ellos cuando lo necesitemos. En el ejemplo de la entrega anterior, si quisiramos tener los datos de varios colegas, podramos usar una coleccin, aunque tambin podramos hacerlo con un array. La ventaja que tiene una coleccin frente a un array es que no tenemos que preocuparnos de redimensionarla cada vez que queramos aadir un nuevo objeto a dicha coleccin. Las colecciones se adaptan de forma automtica a la cantidad de datos que contenga dicha coleccin. Un ejemplo? S, creo que es lo mejor, ya que as lo comprenders mejor. Y para que veas la diferencia, vamos a ver tambin un ejemplo usando un array. Crea un nuevo proyecto de tipo EXE, se aade un formulario, asgnale a la propiedad Name el nombre fEntrega38. Aade cuatro etiquetas, a la ltima cmbiale el nombre y haz que se llame lblInfo. Aade tres cajas de texto, formando un array: Text1(0), Text1(1) y Text1(2). Aade tres commandButtons con estos nombres: cmdAsignar, cmdMostrar y cmdMostrarArray y con los siguientes captions: Asignar, Mostrar y Mostrar array. Por ltimo aade un ListBox, deja el nombre por defecto (List1)

451

Nota: Para crear el array de tres TextBoxes, aade un TextBox, asgnale 0 (cero) a la propiedad Index. Cpialo y pgalo, cuando te pregunte si quieres crear un array, dile que si. Vuelve a pegar de nuevo para tener tres cajas de textos. Aade la clase cColega que se us en la entrega anterior: Proyecto/Aadir mdulo de clase... y selecciona en la solapa "existente" el mdulo de clase cColega.cls Este es el aspecto del formulario en tiempo de diseo:

Aade el siguiente cdigo y pulsa F5 para probarlo. Despus te explico un poco... Escribe varios datos y pulsa en Aadir. Para mostrar los datos, pulsa en el botn Mostrar o en el botn Mostrar Array.

'----------------------------------------------------------------------------' Entrega 38 (colecciones) (01/Jun/01) ' ' Guillermo 'guille' Som, 2001 '----------------------------------------------------------------------------Option Explicit ' Coleccin para almacenar los datos de los colegas Private mColegas As Collection ' Array para almacenar los datos de los colegas Private maColegas() As cColega Private Sub cmdAsignar_Click()

452

' Dimensionamos una variable del tipo del objeto (clase) Dim oColega As cColega ' Creamos un nuevo objeto y lo asignamos a la variable Set oColega = New cColega ' ' Detectamos los errores que se produzcan On Error Resume Next ' ' Asignamos a la propiedad Nombre el contenido de Text1(0) oColega.Nombre = Text1(0).Text ' Asignamos a la propiedad email el contenido de Text1(1) oColega.email = Text1(1).Text ' Si se produce un error If Err Then ' Mostrar el mensaje de aviso MsgBox "Se ha producido un error en: " & Err.Source & vbCrLf & Err.Description ' Posicionar el cursor en la caja de textos del email Text1(1).SetFocus ' Limpiamos el error Err = 0 ' Salimos de este procedimiento Exit Sub End If ' ' Asignamos la fecha de nacimiento oColega.FechaNacimiento = Text1(2).Text If Err Then ' Mostrar el mensaje de aviso MsgBox Err.Number & ", se ha producido un error en: " & Err.Source & vbCrLf & Err.Description ' Posicionar el cursor en la caja de textos del email Text1(2).SetFocus ' Limpiamos el error Err = 0 ' Salimos de este procedimiento Exit Sub End If '

453

' Aadimos el colega a la coleccin ' (no se comprueba que ya exista ese dato en la coleccin) mColegas.Add oColega ' Mostrar el nmero de colegas que hay en la coleccin lblInfo.Caption = "Hay " & mColegas.Count & " colegas en la coleccin" ' ' Para aadirlo en el array, debemos saber cuantos datos hay ' para poder crear un nuevo elemento en el array y aadirlo Dim i As Long ' Averiguamos cuantos datos hay (el cero no lo tenemos en cuenta) i = UBound(maColegas) ' incrementamos el nmero en uno i = i + 1 ' redimensionamos el array para poder almacenar los datos del nuevo colega ' (usamos Preserve para mantener los datos anteriores) ReDim Preserve maColegas(i) ' Aadimos el nuevo colega al array ' (como asignamos un objeto, hay que usar Set) Set maColegas(i) = oColega End Sub Private Sub cmdMostrar_Click() ' Mostrar el contenido de la coleccin en el List1 ' ' una variable temporal para acceder a cada uno de los colegas Dim oColega As cColega ' List1.Clear ' Recorremos todos los colegas que haya en la coleccin For Each oColega In mColegas List1.AddItem oColega.Nombre & " - " & oColega.FechaNacimiento Next ' ' ' ' ' Tambin se puede hacer de esta otra forma: Dim i As Long '

454

'

For i = 1 To mColegas.Count

' List1.AddItem mColegas(i).Nombre & " - " & mColegas(i).FechaNacimiento ' Next End Sub Private Sub cmdMostrarArray_Click() ' Mostrar el contenido del array en el List1 ' Dim i As Long ' List1.Clear ' Recorremos todos los colegas que haya en el array ' (el ndice cero no lo tenemos en cuenta) For i = 1 To UBound(maColegas) List1.AddItem maColegas(i).Nombre & " - " & maColegas(i).FechaNacimiento Next End Sub Private Sub Form_Load() ' Datos de prueba Text1(0).Text = "Guillermo" Text1(1).Text = "mensaje@elguille.info" Text1(2).Text = "7.6.57" ' ' Creamos el objeto coleccin Set mColegas = New Collection ' lblInfo = "" ' ' Creamos un array vacio ReDim maColegas(0) End Sub Private Sub Text1_GotFocus(Index As Integer) ' Seleccionar el texto al entrar With Text1(Index) .SelStart = 0

455

.SelLength = Len(.Text) End With End Sub

La explicacin:

Bsicamente el cdigo es parecido en la entrega anterior, lo nico que ahora hacemos es aadir cada dato de los colegas al array y despus, pulsando en Mostrar o en Mostrar Array, se mostrarn los datos en el ListBox. Veamos cada cosa por separado, en primer lugar te explicar lo de la coleccin. Private mColegas As Collection Esta lnea crea una variable del tipo Collection. En el Form_Load se crea el objeto (recuerdas que no basta con declarar las variables?) Set mColegas = New Collection Para aadir nuevos elementos a la coleccin se usa el mtodo Add mColegas.Add oColega Ahora que sabemos cmo aadir datos a la coleccin, vamos a ver cmo mostrar los datos de esa coleccin. Podemos hacerlo de dos formas, una de la forma clsica, recorriendo cada uno de los elementos de la coleccin mediante un bucle FOR con una variable ndice, para saber cuantos elementos hay en la coleccin usamos la propiedad Count: For i = 1 To mColegas.Count Para acceder a cada uno de los elementos de la coleccin, sabiendo el ndice, usaremos la propiedad Item, sta al ser la propiedad por defecto, no es necesario especificarla: mColegas(i).Nombre Sera lo mismo que: mColegas.Item(i).Nombre La otra forma de recorrer todos los elementos de una coleccin es usando FOR EACH, para ello necesitamos un objeto del tipo contenido en la coleccin, para que devuelva el contenido de cada uno de los elementos: For Each oColega In mColegas Para mostrar los datos de cada uno de los elementos, haremos esto: oColega.Nombre Ya que en cada iteracin del bucle, se devuelve uno de los objetos contenidos en la coleccin. Ahora veamos la explicacin para almacenar los datos de los colegas en un array: Empezamos por declarar un array del tipo cColega, ya que lo que queremos almacenar en dicho array son datos del tipo cColega, (en el caso de la coleccin, no era necesario, ya que el tipo Collection puede almacenar cualquier tipo de datos, siempre usa el tipo Variant y un Variant, como ya deberas saber, permite contener cualquier tipo de objeto): Private maColegas() As cColega

456

Iniciamos el array con cero elementos (realmente uno, pero de ndice cero): ReDim maColegas(0) Cuando asignamos un nuevo colega, tenemos que ampliar el array para que pueda almacenar el nuevo dato, para ello tenemos que saber cuantos elementos tiene el array, incrementarlo en uno y redimensionar el array para que pueda contenerlo, pero sin que pierda los datos que ya tuviera: i = UBound(maColegas) ' incrementamos el nmero en uno i=i+1 ' redimensionamos el array para poder almacenar los datos del nuevo colega ' (usamos Preserve para mantener los datos anteriores) ReDim Preserve maColegas(i) Por ltimo asignamos al nuevo elemento el dato en cuestin: Set maColegas(i) = oColega Hay que usar SET porque lo que estamos asignando es un objeto, no un valor. Para recorrer los elementos del array, tenemos que usar el sistema clsico, es decir hacer un bucle FOR con una variable ndice que recorra todos los elementos del array, (el elemento cero no lo tenemos en cuenta): For i = 1 To UBound(maColegas) Para mostrar cada uno de los datos, lo haremos de la siguiente forma: maColegas(i).Nombre Fjate que la forma de mostrar los datos de un array es casi igual que cuando se usaba FOR i = en el ejemplo de la coleccin. Como habrs notado, es ms fcil usar el objeto Collection que manejar un array, entre otras cosas, porque no tenemos que preocuparnos de redimensionar nada ni de saber cuantos elementos tiene ni nada de eso... (siempre y cuando usemos FOR EACH) El objeto Collection no slo sirve para almacenar objetos, tambin se puede usar para almacenar datos "normales" como Integer, Strings, etc. Otra de las ventajas del objeto Collection es cuando queremos eliminar un dato: mColegas.Remove Index Simplemente se le indica que ndice queremos eliminar y ya est. Esto mismo sera un poco ms complicado con un array. Si no te lo crees, prueba a eliminar el elemento nmero x del array, (la solucin te la dar en la prxima entrega). Adems de lo visto, con las colecciones podemos impedir que se aadan elementos que ya existan, para ello se puede indicar una Clave junto con el elemento que se aade, (un ejemplo lo tendremos en la prxima entrega): mColegas.Add Objeto, Clave E incluso en que posicin queremos aadirlo: Antes o detrs de un elemento dado: mColegas.Add Objeto, Clave, AntesDe, DespuesDe Por ejemplo, si queremos aadir un objeto despus del tercer elemento: mColegas.Add oColega, , 3

457

Te recomiendo que hagas tus propias pruebas y as vers cmo funciona. Y esto es todo por ahora. En la prxima entrega veremos cmo crear nuestra propias colecciones a las que podremos aadir opciones de guardar los datos en el disco, poder recuperarlos y cualquier cosa que se nos ocurra... la cuestin ser aadir los mtodos que necesitemos... as que, estate pendiente y paciencia... Nos vemos Guillermo

Curso Bsico de Programacin en Visual Basic


En la entrega anterior te promet que en esta ocasin bamos a ver cmo crear nuestras propias colecciones, pero, no va a ser as... como puedes comprobar, algunas veces NO cumplo lo que digo... je, je, je, pero no te preocupes que en la siguiente si que lo vamos a ver... es que no es plan de que te acostumbres a tener todo lo que quieras... Lo que vamos a hacer es aadir ms funcionalidad a la clase normal, y de paso aprendes alguna que otra cosilla, o afianzas lo que ya has aprendido, que de seguro no te vendr mal. Que podemos aadirle? Pues por ejemplo un mtodo o funcin que permita hacer una copia del objeto en cuestin. Con esto, pretendo que comprendas que lo que estamos tratando o manejando no es una "simple" variable, sino un objeto, que como podrs comprobar, si no en estos ejemplos, si en otro cdigo que veas por ah... ya que me imagino que no slo te contentars con lo que yo te muestro, sino que te leers la documentacin del Visual Basic e incluso habrs comprado algn que otro libro sobre el tema este de la programacin; en cualquier caso, debes saber que un objeto, ya sea un mdulo de clase ya sea un control, no se puede copiar as por las buenas. Para que lo entiendas mejor: (espero) Supongamos que tenemos una variable, por ejemplo la variable de tipo string sNombre, que tiene como valor "el Guille". Sigamos suponiendo que queremos hacer una copia de dicha variable, (realmente de su contenido), en otra variable, por ejemplo en sCopia. Para realizar dicha copia, lo nico que hay que hacer es: sCopia = sNombre y a partir de ese momento, el contenido de sCopia ser el mismo que el de sNombre, (en este caso de suposiciones, ser "el Guille"). Si despus cambiamos el contenido de la variable sNombre no afectar al contenido de sCopia, ya que lo que se hizo fue copiar el contenido de sNombre, es decir se cre una nueva cadena y se guard en la variable sCopia. (Si yo fuera un experto en representaciones grficas, te mostrara un dibujito con las variables y sus contenidos, pero como soy un desastre para esto de las

458

"representaciones", sobre todo si son grficas, pues te lo imaginas o simplemente sigues leyendo, que puede ser que hasta te enteres de lo que estoy hablando...) Para probarlo, muestra el contenido de las dos variables, cambia el contenido de sNombre y vuelve a mostrar, vers que cada una de las variables tiene un valor distinto. Cmo? Que quieres ver el cdigo completo... Valeeee! No insistas!, aqu lo tienes: Crea un nuevo proyecto e inserta este cdigo en el evento Form_Load Show ' Por si se te olvida ponerlo... Dim sNombre As String Dim sCopia As String ' Asignamos el contenido a sNombre y hacemos la copia sNombre = "el Guille" sCopia = sNombre ' Mostramos los contenidos Print "El original: " & sNombre Print "La copia: " & sCopia ' Cambiamos el contenido de sNombre sNombre = "Guillermo" ' Volvemos a mostrar Print Print "El original: " & sNombre Print "La copia: " & sCopia Como podrs comprobar, el original cambia, pero la copia permanece igual. Es decir, el que se cambie el original no afecta a la copia, ya que son dos "cosas" distintas. Pero esto mismo no se puede hacer con los objetos. Al menos no de la forma simple de una asignacin. Para que sepas por dnde van los tiros, practiquemos con un ejemplo. Supongamos, (sigo suponiendo, pero en estos casos, deberas "meterte" de lleno en el tema y convertir estas suposiciones en cdigo), que queremos hacer una copia de un objeto del tipo cColega, (la clase que hemos estado usando en las entregas anteriores y de la cual ms tarde te mostrar el cdigo completo, ya que en las entregas anteriores no lo hice, y slo te mostr parte del cdigo y he recibido algunas quejas indicndome que faltaba cdigo... mea culpa!) Pero para no caer en errores de suposiciones, (algunas veces supongo que entiendes lo que digo y puede que "suponga" mal, as que... dejmonos de suposiciones y vayamos a cosas concretas), te voy a mostrar el cdigo completo para que entiendas lo que quiero explicarte... que con tanto suponer y tanta parrafada, no se si te habrs perdido o seguirs en sintona... Declaramos una variable para que contenga un objeto del tipo cColega y le asignamos algn valor: Dim tColega As cColega ' La variable que contendr un objeto del tipo cColega Set tColega = New cColega ' Ahora est preparada para contener datos

459

tColega.Nombre = "Guille" ' Asignamos los datos tColega.email = "mensaje@elguille.info" Ahora creamos otra variable en la que queremos copiar lo que tiene el objeto tColega: Dim CopiaColega As cColega Set CopiaColega = New cColega ' Creamos un nuevo objeto, aunque, como despus vers, no es necesario Si hacemos esta asignacin: CopiaColega = tColega, recibiremos un error, ya que Visual Basic espera que se estn copiando datos normales, en este caso espera que tanto CopiaColega como tColega tengan propiedades por defecto que se puedan copiar de la misma forma que se copian dos variables normales (como vimos en el caso anterior de sNombre y sCopia) Cuando queremos "copiar" objetos, hay que usar SET, (no comento ms, ya que despus lo aclaro), por tanto, lo que se debera hacer es: Set CopiaColega = tColega Pero vamos a comprobar que no es oro todo lo que reluce. Mostramos el contenido de CopiaColega Text1.Text = "Contenido de CopiaColega: " & vbCrLf & CopiaColega.Nombre & " " & CopiaColega.email Ahora cambiamos el nombre de tColega tColega.Nombre = "Guillermo" Y volvemos a mostrar el contenido de CopiaColega Text1.Text = Text1.Text & vbCrLf & _ "Contenido de CopiaColega despus de cambiar el nombre de tColega: " & vbCrLf & _ CopiaColega.Nombre & " " & CopiaColega.email & vbCrLf

Para probar esto que te digo y puedas ver y no tener que crertelo porque yo te lo diga, en el formulario que tienes creado de la prueba anterior... cmo? que no has probado lo anterior? entonces, tendrs que crear un nuevo proyecto... (si quieres, claro) Aade un CommandButton (Command1) y una caja de texto (Text1) con la propiedad MultiLine = True y ScrollBars = Both y pega el cdigo, (s, lo que est en negrita, incluso la asignacin que da el error, para que compruebes que es verdad y que da un error, as vas aprendiendo de los errores...) Pulsa F5 y haz click en el botn, vers que es lo que ocurre. Si todo va como debera ir, te habr mostrado esto: Contenido de CopiaColega: Guille mensaje@elguille.info Contenido de CopiaColega despus de cambiar el nombre de tColega: Guillermo mensaje@elguille.info

O sea que no tenamos una copia... entonces que tenemos? Pues, dos variables que "apuntan" al mismo objeto. Con las consecuencias que esto tiene: que si se modifica el contenido de una de las variables, el otro objeto tambin "aparenta" que se ha modificado... y digo aparenta, porque en realidad slo existe un objeto del tipo cColega en la memoria, pero hay dos variables que apuntan a ese objeto. En otros lenguajes de programacin sera algo as como un puntero.

460

Como puedes comprobar, me repito, (hace un par de entregas vimos algo de esto), pero es para que te quede bien claro el concepto. Entonces... cmo se puede hacer una copia de un objeto? Pues manualmente, copiando cada una de las propiedades del objeto original en la copia. Para este caso si que es necesario declarar el objeto de destino como NEW, ya que, en el caso anterior, no era necesario crear un nuevo objeto cuando lo que "pretendamos" era "apuntar" al mismo objeto en memoria. Prubalo quitando la lnea con este cdigo: Set CopiaColega = New cColega y vers que funciona igual (de mal) La explicacin es: que al ser una variable que simplemente hace referencia a un objeto ya existente, no es necesario crear un nuevo objeto que despus no se va a usar. Veamos el cdigo de la segunda prueba, haciendo copia de cada una de las propiedades. Aade un nuevo botn e inserta este cdigo, despus prubalo y vers que el cambio del original no afecta a la copia, por la sencilla razn de que son dos objetos totalmente diferentes. Private Sub Command2_Click() ' Creamos una variable y le asignamos datos Dim tColega As cColega ' La variable que contendr un objeto del tipo cColega Set tColega = New cColega ' Ahora est preparada para contener datos tColega.Nombre = "Guille" ' Asignamos los datos tColega.email = "mensaje@elguille.info" ' ' Creamos otra variable en la que queremos copiar ' lo que tiene el objeto tColega: Dim CopiaColega As cColega Set CopiaColega = New cColega ' Ahora copiamos cada una de las propiedades: CopiaColega.Nombre = tColega.Nombre CopiaColega.email = tColega.email ' Mostramos el contenido de CopiaColega Text1.Text = "Contenido de CopiaColega: " & vbCrLf & CopiaColega.Nombre & " " & CopiaColega.email ' Ahora cambiamos el nombre de tColega tColega.Nombre = "Guillermo" ' Y volvemos a mostrar el contenido de CopiaColega Text1.Text = Text1.Text & vbCrLf & "Contenido de CopiaColega despus de cambiar el nombre de tColega: " & vbCrLf & CopiaColega.Nombre & " " & CopiaColega.email & vbCrLf ' En este caso no se ha alterado el valor de CopiaColega... como era de esperar End Sub

461

Pues esto mismo es lo que tendramos que hacer en un mtodo que hiciese una copia del objeto: copiar cada una de las propiedades de dicho objeto. Este tipo de mtodos suele llamarse Clone, (por lo de clonacin o copia idntica), y se podra codificar de esta forma:

Public Function Clone() As cColega ' Esta funcin devolver una copia "nueva" de ste mismo objeto (12/Jul/01) ' Dim nuevoColega As cColega ' Set nuevoColega = New cColega ' ' Asignar cada una de las propiedades de esta clase With nuevoColega .email = Me.email .FechaNacimiento = Me.FechaNacimiento .Nombre = Me.Nombre End With ' Devolver el objeto "cloneado" Set Clone = nuevoColega End Function Te explico un poco todo esto: La funcin devuelve un objeto del mismo tipo que la clase, ya que de lo que se trata es de hacer una copia del objeto en s. Se crea una variable temporal del tipo a devolver, (que es del mismo tipo que el objeto o clase), y se asigna un NUEVO objeto a dicha variable. Se asigna al nuevo objeto los valores del objeto que implementa el mtodo Clone, para ello se usa ME, aunque slo para mayor claridad, de sta forma sabes que se est refiriendo al propio objeto en el que se ha creado la funcin. Por ltimo se devuelve una referencia al nuevo objeto creado y con los datos copiados. El SET es necesario por lo que antes te he comentado de que lo que se quiere "copiar" es el objeto entero. En esta ocasin SET Clone = nuevoColega devuelve una referencia al objeto nuevoColega que no es ni ms ni menos que el NUEVO objeto que acabamos de crear. ' Crear un nuevo objeto ' Un objeto del tipo cColega

462

Para usarlo, simplemente asignaremos a la variable de destino lo que la funcin (o mtodo) Clone devuelva: Set CopiaColega = tColega.Clone que no es ni ms ni menos que una "nueva" copia del objeto tColega. Como puedes comprobar en la funcin Clone lo nico que hacemos es "encapsular" lo que antes hicimos manualmente, es decir "ocultamos" lo que la funcin hace para copiar el objeto, ya que al usuario lo nico que le interesa es saber que se mtodo hace una nueva copia del objeto que lo implementa. Y si aadimos nuevas propiedades a la clase, lo nico que tendramos que hacer es aadir el cdigo correspondiente para que las nuevas propiedades tambin se copien al utilizar el mtodo Clone. Veamos el cdigo necesario para "probar" que todo esto que digo funciona. Aade un nuevo botn al formulario y pega el siguiente cdigo: Private Sub Command3_Click() ' Creamos una variable y le asignamos datos Dim tColega As cColega del tipo cColega Set tColega = New cColega datos tColega.Nombre = "Guille" ' ' Creamos otra variable en la que queremos copiar ' lo que tiene el objeto tColega: Dim CopiaColega As cColega ' ' No es necesario crear el nuevo objeto, ya que el mtodo Clone ' devuelve un nuevo objeto 'Set CopiaColega = New cColega ' ' Hacemos una clonacin Set CopiaColega = tColega.Clone ' ' Mostramos el contenido de CopiaColega Text1.Text = "Contenido de CopiaColega: " & vbCrLf & CopiaColega.Nombre & " " & CopiaColega.email ' Ahora cambiamos el nombre de tColega tColega.Nombre = "Guillermo" ' Y volvemos a mostrar el contenido de CopiaColega Text1.Text = Text1.Text & vbCrLf & "Contenido de CopiaColega despus de cambiar el nombre de tColega: " & vbCrLf & CopiaColega.Nombre & " " & CopiaColega.email & vbCrLf ' ' La variable que contendr un objeto ' Ahora est preparada para contener ' Asignamos los datos

tColega.email = "mensaje@elguille.info"

463

' Usando Clone tampoco se altera el objeto copiado ' ' Cambiemos el contenido del nombre de la copia CopiaColega.Nombre = "Pepe" ' Mostramos el contenido de la propiedad Nombre de los dos objetos Text1.Text = Text1.Text & vbCrLf & "Contenido de tColega.Nombre: " & tColega.Nombre Text1.Text = Text1.Text & vbCrLf & "Contenido de CopiaColega.Nombre: " & CopiaColega.Nombre End Sub Ejecuta el programa y pulsa en el botn que acabas de aadir, vers que te muestra esto: Contenido de CopiaColega: Guille mensaje@elguille.info Contenido de CopiaColega despus de cambiar el nombre de tColega: Guille mensaje@elguille.info Contenido de tColega.Nombre: Guillermo Contenido de CopiaColega.Nombre: Pepe Es decir, que los cambios que se hagan a cualquiera de las dos variables sern independientes y no se "mezclarn", por la sencilla razn de que cada una de las variables "apunta" a un objeto diferente.

Otra forma de clonar objetos

Existe otra forma de hacer clonaciones de objetos. Pero para ello hay que tener el Visual Basic 6.0, ya que con las versiones anteriores no funciona. Te dejo el link a la pgina que tiene el cdigo de ejemplo, (y la pgina explicativa), para que sepas cmo hacerlo, por ahora no te doy ms explicaciones que las que pueda haber en los comentarios y en dicha pgina, entre otras cosas porque se tratan temas que an no se han explicado, as que... eso es lo que hay, pero al menos hay algo, que en definitiva es lo importante verdad? Link a la pgina de clonacin de objetos usando Visual Basic 6.0

Y hasta aqu hemos llegado. En la prxima entrega, posiblemente, veremos cmo crear nuestras propias colecciones... espero que en esa ocasin sea as... si es que antes no se me ocurre alguna cosilla nueva... ya veremos! Nos vemos Guillermo P.S.

464

Aqu tienes el cdigo completo de la clase y el proyecto de prueba: basico39_cod.zip 3.93 KB

Curso Bsico de Programacin en Visual Basic

Copiar objetos? S, Gracias!


Con Visual Basic 6.0 es posible copiar objetos, incluso guardarlos como ficheros para recuperarlos posteriormente... (y todo en tiempo de ejecucin, puntualizo). Actualizado: 24/Ago/99 Revisado: 12/Jul/2001 como aadido a la entrega 39 del curso bsico

Parece imposible, verdad? Pero... es cierto! (que es posible copiar objetos, no que es imposible...) Aunque tiene sus inconvenientes... si es que se le puede llamar inconveniente a hacer algo que es "habitual" hacerlo con los controles ActiveX.

Cmo se pueden copiar objetos en Visual Basic? Hasta ahora, la nica forma de copiar objetos es creando un mtodo en la propia clase que devuelva un nuevo objeto con el contenido de las propiedades, (normalmente a ese mtodo se le llama Clone), ni tan siquiera usando lenguajes como C++ se podan hacer copias de objetos... Pero ahora, con la versin 6.0 (y posteriores) de Visual Basic, las clases pblicas tienen una propiedad llamada Persistable la cual puede tomar dos valores, por defecto el valor es 0-NotPersistable, pero si se cambia al valor 1-Persistable, se aaden tres nuevos eventos a la clase: InitProperties, ReadProperties y WriteProperties. Si has creado tus propios controles ActiveX, seguramente habrs usado estos eventos, ya que guardando los valores de las propiedades en el objeto PropertyBag puedes mantener valores en las propiedades diferentes a los predeterminados. Para que nos entendamos: Cuando creas un control ActiveX, puedes hacer que los valores asignados a las propiedades sea "recordados" y no se pierdan en el limbo cada vez que abres el formulario en el que est colocado el control. Imaginate el inconveniente, por no usar otra palabra malsonante, si cada vez que cargas un proyecto previamente guardado, tuvieses que asignar el valor del Caption del formulario... Pues lo mismo que el formulario "recuerda" los valores asignados a las propiedades en

465

tiempo de diseo, se puede hacer con los controles creados por nosotros, y todo ello gracias al objeto PropertyBag y a los eventos ReadProperties -leer los valores de las propiedades- y WriteProperties -guardar los valores de las propiedadesCmo sabe Visual Basic que el valor de una propiedad ha cambiado? No lo sabe, pero, aunque lo supiera, como el valor de una propiedad puede cambiar no slo en los procedimientos Let o Set, Visual Basic pone a nuestra disposicin la instruccin PropertyChanged, de esta forma podemos avisarle que una propiedad ha cambiado y as poder guardar el nuevo valor en el objeto PropertyBag. De todas formas, todo esto est bien explicado en la ayuda del Visual Basic, as que si quieres saber ms sobre la forma de "persistir" los valores de las propiedades... ya sabes... Ahora vamos a pasar al tema que nos interesa: Crear nuevas copias de objetos con Visual Basic Los requisitos necesarios para que un objeto creado por un componente ActiveX sea "duplicable" son: Que la clase sea pblica Que la clase tenga asignado el valor 1 a la propiedad Persistable Que las propiedades que nos interese copiar se almacenen en el objeto PropertyBag, (usando el evento WriteProperties) Si tenemos otros objetos incluidos en la clase y nos interesa copiar tambin esos objetos, estos deben ser tambin "Persistables" En el cdigo que sigue, veremos cmo crear nuevas copias de nuestros objetos. El componente de ejemplo tiene dos clases, una con la propiedad Persistable asignada a 1 y la otra asignada a 0. Por tanto, de una se podr hacer copias y de la otra no, al menos usando el mtodo rpido de copiar los objetos semi-automticamente con el objeto PropertyBag. En el cdigo de ejemplo se muestran dos formas diferentes para hacer copias de objetos: En el mtodo Clone de la clase Persistable se usa el objeto PropertyBag, mientras que en la clase NotPersistable se usa lo que hasta ahora hemos tenido que usar para poder hacer copias de un objeto. En el cdigo del formulario de prueba se muestra el cdigo necesario para hacer copias usando un fichero. De esta forma podemos guardar el contenido de las propiedades en un fichero y posteriormente leerlo para que un nuevo objeto tenga los mismos valores... Las posibles utilidades de esta tcnica la dejo a tu imaginacin... Aqu tienes el cdigo, el cual est lo suficientemente comentado, (al menos eso espero), como para que sea fcilmente comprensible. Nota: Los procedimientos GuardarObjeto y LeerObjeto del formulario, muestran la

466

forma de guardar el contenido de un objeto en un fichero y despus poder recuperarlo para crear un nuevo objeto.
(esto mismo no lo acabo de repetir un poco ms arriba?)

Si quieres ms informacin sobre el tema, consulta: Persistencia en datos de componentes, en la ayuda de Visual Basic (MSDN Library) y para saber ms sobre persistencia en los controles ActiveX: Guardar las propiedades de un control, en Generar un control ActiveX.

El cdigo
El cdigo del formulario de prueba y una "captura" del mismo, as como un poco de explicacin de los controles: (este formulario estar en un proyecto que tendr una referencia al componente que tiene las clases mostradas ms abajo) Cuando pulses en el botn "Copiar NotPersistable usando un fichero", te mostrar un error indicando que no se pueden copiar objetos no persistente, sin embargo, al pulsar en "Copiar NotPersistable", se usa el mtodo Clone de la clase, por tanto si que se podr copiar. Por otro lado, al pulsar tanto en "Copiar Persistable" como en "Copiar Persistable usando un fichero", la propiedad "Comentario" no se copiar, ya que el valor de esa propiedad no se guarda en el objeto PropertyBag. Los botones "Refresh..." harn que se muestren los contenidos de las clases en las cajas de texto. El Frame superior mostrar la clase original (la Persitable o NotPersistable, segn el botn pulsado). El Frame inferior mostrar el contenido de la copia realizada a la clase. "Fecha Creacin" indicar la fecha y hora de creacin de la clase... lo aclaro por si piensas que es la creacin de otra cosa... nunca se sabe!

467

'----------------------------------------------------------------------------' tPersistable (22/Ago/99) ' Revisado para el curso bsico (12/Jul/01) ' ' Prueba de copiar objetos usando la propiedad Persitable ' Para ms informacin ver en la MSDN de Visual Basic 6.0: ' ' ' Guillermo 'guille' Som, 1999-2001 '----------------------------------------------------------------------------Option Explicit Persistencia en datos de componentes

468

' Declaramos las variables de los objetos de prueba Private m_Clase1 As cPersistable Private m_Clase2 As cPersistable Private m_Clase3 As cNotPersistable Private m_Clase4 As cNotPersistable Private Sub cmdAsignar_Click(Index As Integer) ' Asignar el contenido de los TextBoxes a las clases ' El botn de indice 0 asigna los valores a las clases bsicas If Index = 0 Then With m_Clase1 .Nombre = Text1(0) .email = Text1(1) .AoNacimiento = Text1(2) .Comentario = Text1(3) End With With m_Clase3 .Nombre = Text1(0) .email = Text1(1) .AoNacimiento = Text1(2) .Comentario = Text1(3) End With Else ' El ndice 1 asigna los valores a las otras clases (las copias) With m_Clase2 .Nombre = Text1(4) .email = Text1(5) .AoNacimiento = Text1(6) .Comentario = Text1(7) End With With m_Clase4 .Nombre = Text1(4) .email = Text1(5) .AoNacimiento = Text1(6) .Comentario = Text1(7) End With

469

End If cmdAsignar(Index).Enabled = False End Sub Private Sub cmdCopiar_Click() ' Copiar el objeto "Persistable" del 1 en el 2 ' Copiarlo usando Clone ' ---La copia se hace usando el objeto PropertyBag Set m_Clase2 = m_Clase1.Clone ' Mostrar el segundo objeto ' Para probar que realmente son objetos diferentes: ' (si no lo fuesen, mostrara el nombre con la palabra <COPIA>) With m_Clase2 .Nombre = "<COPIA> " & .Nombre End With cmdRefresh_Click 0 End Sub Private Sub cmdCopiar2_Click() ' Copiar el objeto "NotPersistable" del 1 en el 2 ' Copiarlo usando Clone ' ---La copia se hace manualmente, es decir propiedad a propiedad Set m_Clase4 = m_Clase3.Clone ' Mostrar el segundo objeto ' Para probar que realmente son objetos diferentes: ' (si no lo fuesen, mostrara el nombre con la palabra <COPIA>) With m_Clase4 .Nombre = "<COPIA> " & .Nombre End With cmdRefresh_Click 1 End Sub Private Sub cmdCopiarF_Click() '//////////////////////////////////////////////////////////////////// //////

470

' El siguiente cdigo es para copiar objetos usando un fichero intermedio '//////////////////////////////////////////////////////////////////// ////// ' Guardar el objeto 1 If GuardarObjeto(m_Clase1) Then ' Si se pudo guardar es que la clase es "persistente", ' por tanto, leerlo y asignarlo al objeto2 If LeerObjeto(m_Clase2) Then ' Mostrar el segundo objeto With m_Clase2 .Nombre = "<COPIA> " & .Nombre End With End If End If cmdRefresh_Click 0 End Sub Private Sub cmdCopiarF2_Click() '//////////////////////////////////////////////////////////////////// ////// ' El siguiente cdigo es para copiar objetos usando un fichero intermedio ' (esto no funcionar, ya que las propiedades no son "persistentes") '//////////////////////////////////////////////////////////////////// ////// ' ' Guardar el objeto 1 If GuardarObjeto(m_Clase3) Then ' Si se pudo guardar es que la clase es "persistente", ' por tanto, leerlo y asignarlo al objeto2 If LeerObjeto(m_Clase4) Then ' Mostrar el segundo objeto With m_Clase4 .Nombre = "<COPIA> " & .Nombre End With cmdRefresh_Click 1

471

End If End If End Sub Private Sub cmdRefresh_Click(Index As Integer) ' Mostrar el contenido de las clases en los TextBoxes ' Tenemos cuidado de los posibles errores que se produzcan On Local Error Resume Next ' El ndice 0 mostrar los contenidos de las clases Persistentes If Index = 0 Then With m_Clase1 Text1(0) = .Nombre Text1(1) = .email Text1(2) = .AoNacimiento Text1(3) = .Comentario Label2(0) = .Copia.Nombre lblFecha(0) = .FechaCreacin End With With m_Clase2 Text1(4) = .Nombre Text1(5) = .email Text1(6) = .AoNacimiento Text1(7) = .Comentario Label2(1) = .Copia.Nombre lblFecha(1) = .FechaCreacin End With Else ' El ndice 1 mostrar los contenidos de las clases No Persistentes With m_Clase3 Text1(0) = .Nombre Text1(1) = .email Text1(2) = .AoNacimiento Text1(3) = .Comentario Label2(0) = .Copia.Nombre lblFecha(0) = .FechaCreacin End With

472

With m_Clase4 Text1(4) = .Nombre Text1(5) = .email Text1(6) = .AoNacimiento Text1(7) = .Comentario ' Esto producir un error si se copia mediante un fichero Label2(1) = .Copia.Nombre ' lblFecha(1) = .FechaCreacin End With End If cmdAsignar(0).Enabled = False cmdAsignar(1).Enabled = False Err = 0 End Sub Private Sub Form_Load() ' Limpiar las cajas de texto Dim i As Long For i = 0 To Text1.Count - 1 Text1(i) = "" Next Label2(0) = "" Label2(1) = "" ' Crear las clases ' Las dos que se pueden copiar: Set m_Clase1 = New cPersistable ' No es necesario crearla con New 'Set m_Clase2 = New cPersistable ' Las dos que no se podrn copiar: Set m_Clase3 = New cNotPersistable Set m_Clase4 = New cNotPersistable

473

' Unos valores de ejemplo: quin es este? Text1(0) = "Guillermo 'guille'" Text1(1) = "mensaje@elguille.info" Text1(2) = 1957 Text1(3) = "El Guille" ' Asignar los valores a la clase cmdAsignar_Click 0 ' Mostrar los valores cmdRefresh_Click 0 ' El objeto de copia debe ser una clase Persistable Set m_Clase1.Copia = New cPersistable ' Prueba usando un objeto no persistente: ' (esto no funcionar, ya que el objeto no es persistente y por tanto ' no puede guardarse en el PropertyBag) 'Set m_Clase1.Copia = New cNotPersistable m_Clase1.Copia.Nombre = "Guillermo" Set m_Clase3.Copia = New cNotPersistable m_Clase3.Copia.Nombre = "Guillermo" End Sub Private Sub Form_Unload(Cancel As Integer) ' Eliminar los objetos previamente declarados Set m_Clase1 = Nothing Set m_Clase2 = Nothing Set m_Clase3 = Nothing Set m_Clase4 = Nothing Set fPersistable = Nothing End Sub Private Sub Text1_Change(Index As Integer) ' Habilitar el botn adecuado si se cambia el contenido de las cajas Dim queClase As Long

474

queClase = 0 If Index > 3 Then queClase = 1 End If cmdAsignar(queClase).Enabled = True End Sub Private Function GuardarObjeto(queClase As IPruebaPersistable, _ Optional ByVal sFic As String = "CopiaObjeto" _ ) As Boolean '-------------------------------------------------------------------------' Guardar el objeto indicado en un fichero de texto (22/Ago/99) ' ' Se usa el parmetro del tipo IPruebaPersistable ya que esa interface ' est implementada en los dos objetos del componente de prueba ' ' Esta funcin devolver: ' ' False si se produjo error True si todo fue bien

'-------------------------------------------------------------------------Dim varTemp As Variant Dim pb As PropertyBag Dim sPath As String On Error GoTo ErrGuardar ' ' Aadir al path de la aplicacin la barra de directorio sPath = App.Path If Right$(sPath, 1) <> "\" Then sPath = sPath & "\" End If ' Instanciacin de un objeto PropertyBag.

475

Set pb = New PropertyBag ' Guarda el objeto en el PropertyBag mediante WriteProperty. pb.WriteProperty sFic, queClase ' Asigna el contenido del PropertyBag a un variable de tipo Variant. varTemp = pb.Contents ' Lo guarda en un archivo de texto. Open sPath & sFic & ".txt" For Binary As #1 Put #1, , varTemp Close #1 GuardarObjeto = True Exit Function ErrGuardar: MsgBox "Error al guardar el objeto en el fichero:" & vbCrLf & _ sFic & vbCrLf & Err.Number & " - " & Err.Description Err = 0 GuardarObjeto = False End Function Private Function LeerObjeto(queClase As IPruebaPersistable, _ "CopiaObjeto" _ Optional ByVal sFic As String = ) As Boolean '-------------------------------------------------------------------------' Leer el objeto del fichero y asignarlo a la clase indicada (22/Ago/99) ' ' Se usa el parmetro del tipo IPruebaPersistable ya que esa interface ' est implementada en los dos objetos del componente de prueba ' ' Esta funcin devolver: ' ' False si se produjo error True si todo fue bien

476

'-------------------------------------------------------------------------Dim varTemp As Variant Dim byteArr() As Byte Dim pb As PropertyBag Dim sPath As String On Error GoTo ErrLeer ' Aadir al path de la aplicacin la barra de directorio sPath = App.Path If Right$(sPath, 1) <> "\" Then sPath = sPath & "\" End If ' Instanciacin de un objeto PropertyBag. Set pb = New PropertyBag ' Lee el contenido de un archivo en una variable de tipo Variant. Open sPath & sFic & ".txt" For Binary As #1 Get #1, , varTemp Close #1 ' Asigna el valor de la variable Variant a una matriz de bytes. byteArr = varTemp ' Asigna el valor a la propiedad Contents del objeto PropertyBag pb.Contents = byteArr ' Instancia el objeto desde el objeto PropertyBag Set queClase = pb.ReadProperty(sFic) LeerObjeto = True Exit Function ErrLeer: MsgBox "Error al leer el objeto del fichero:" & vbCrLf & _ sFic & vbCrLf & Err.Number & " - " & Err.Description Err = 0 LeerObjeto = False End Function

477

El cdigo de las clases:


(todas son clases pblicas y estn incluidas en un componente ActiveX DLL)

'----------------------------------------------------------------------------' IPruebaPersistable (22/Ago/99) ' Revisado para el curso bsico (12/Jul/01) ' ' Interface para usar en el componente PruebaPersistable ' (esta clase no tiene porqu ser Persistable y aunque lo sea no servir de nada, ' es decir: la clase NotPersistable seguir sin poder copiarse) ' ' Guillermo 'guille' Som, 1999-2001 '----------------------------------------------------------------------------Option Explicit ' Aunque estas propiedades estn declaradas como "variables" pblicas, ' al usar Implements se crearn dos procedimientos: Get y Let ' (Get y Set en caso del objeto Copia) Public Copia As IPruebaPersistable Public Nombre As String Public AoNacimiento As Long Public email As String Public Comentario As String ' Propiedad de slo lectura Public Property Get FechaCreacin() As String End Property

'-----------------------------------------------------------------------------

478

' cPersistable (22/Ago/99) ' Revisado para el curso bsico (12/Jul/01) ' ' Componente para hacer copias de objetos usando la propiedad Persitable ' ' Para poder usar esto de la "persistencia" de las propiedades hay que ' asignar a la propiedad Persistable de la clase el valor 1Persistable ' ' Guillermo 'guille' Som, 1999-2001 '----------------------------------------------------------------------------Option Explicit ' Clase genrica para usar tanto con esta clase como con la otra no persistente ' De esta forma se tiene una misma clase para poder acceder a los mtodos y ' propiedades de cualquier clase que la implemente, ' por ejemplo, el mtodo Copia devuelve un objeto de este tipo Implements IPruebaPersistable ' Valor por defecto del ao de nacimiento Private Const cAoNacimiento As Long = 1999 ' Variables privadas para contener los valores de las propiedades Private m_FechaCreacin As String Private m_Copia As IPruebaPersistable Private m_Nombre As String Private m_AoNacimiento As Long Private m_email As String ' Esta propiedad no es persistente, es decir no se guarda en el PropertyBag Private m_Comentario As String

479

Public Property Get AoNacimiento() As Long ' Se devuelve el valor contenido en la variable privada AoNacimiento = m_AoNacimiento End Property Public Property Let AoNacimiento(ByVal NewValue As Long) ' Se asigna el nuevo valor en la variable privada m_AoNacimiento = NewValue ' y se avisa al Visual Basic de que esta propiedad ha cambiado PropertyChanged "AoNacimiento" End Property Public Property Get Comentario() As String Comentario = m_Comentario End Property Public Property Let Comentario(ByVal NewValue As String) ' Como esta propiedad no la hemos hecho "persistente", ' no se llama a PropertyChanged m_Comentario = NewValue End Property Public Property Get Copia() As IPruebaPersistable ' Como lo que se devuelve es un objeto, ' hay que hacerlo usando Set Set Copia = m_Copia End Property Public Property Set Copia(ByVal NewValue As IPruebaPersistable) ' Esta propiedad devuelve un objeto, por tanto se implementa ' como Set en lugar de Let Set m_Copia = NewValue PropertyChanged "Copia" End Property Public Property Get email() As String email = m_email End Property

480

Public Property Let email(ByVal NewValue As String) m_email = NewValue PropertyChanged "email" End Property ' Propiedad de slo lectura ' por eso slo est el procedimiento Get Public Property Get FechaCreacin() As String FechaCreacin = m_FechaCreacin End Property Public Property Get Nombre() As String Nombre = m_Nombre End Property Public Property Let Nombre(ByVal NewValue As String) m_Nombre = NewValue PropertyChanged "Nombre" End Property ' Este procedimiento se ejecuta cada vez que se crea una instancia de la clase Private Sub Class_Initialize() m_AoNacimiento = cAoNacimiento 'Debug.Print "cPersistable_Initialize" ' Esto hara que se quedara sin espacio en la pila, ' ya que al crear una nueva instancia hara que se creara otra dentro de esa ' y as sucesivamente 'Set m_Copia = New cPersistable End Sub Private Sub Class_InitProperties() ' En los controles ActiveX, este procedimiento slo se ejecuta una vez: ' cuando se inserta el control en el contenedor; ' pero en las clases, se ejecuta cada vez que se crea la clase.

481

' Asignamos la fecha y hora actual a la variable privada m_FechaCreacin = Format$(Now, "dd/mmm/yyyy hh:mm:ss") ' indicamos que la propiedad ha cambiado, ' (slo se avisar aqu, ya que esta propiedad es de slo lectura) PropertyChanged "FechaCreacin" End Sub Private Sub Class_ReadProperties(PropBag As PropertyBag) ' Este procedimiento se ejecuta cada vez que se leen los valores ' guardados en el objeto PropertyBag ' Por si se produce algn error On Local Error Resume Next ' Asignamos los valores almacenados a las variables privadas m_Nombre = PropBag.ReadProperty("Nombre") m_AoNacimiento = PropBag.ReadProperty("AoNacimiento", cAoNacimiento) m_email = PropBag.ReadProperty("email") ' Para que la propiedad Comentario sea persistente, quitar el comentario 'm_Comentario = PropBag.ReadProperty("Comentario") m_FechaCreacin = PropBag.ReadProperty("FechaCreacin") ' Set m_Copia = PropBag.ReadProperty("Copia", Nothing) Err = 0 End Sub Private Sub Class_Terminate() 'Set m_Copia = Nothing ' Debug.Print "cPersistable_Terminate" End Sub Private Sub Class_WriteProperties(PropBag As PropertyBag)

482

' Este evento se ejecuta cada vez que se guardan los valores en el objeto ' PropertyBag On Local Error Resume Next PropBag.WriteProperty "Nombre", m_Nombre PropBag.WriteProperty "email", m_email PropBag.WriteProperty "AoNacimiento", m_AoNacimiento, cAoNacimiento ' Si no se guarda la propiedad Comentario, no se podr "clonar" 'PropBag.WriteProperty "Comentario", m_Comentario PropBag.WriteProperty "FechaCreacin", m_FechaCreacin ' Para que el objeto se pueda guardar, debe ser Persistable PropBag.WriteProperty "Copia", m_Copia, Nothing Err = 0 End Sub ' Los procedimientos implementados delegan en las propiedades de la clase ' Se podran asignar las variables privadas, pero entonces habra que ' "avisar" de los cambios llamando a PropertyChanged, adems de que si en ' los procedimientos se hacen algunas comprobaciones, pues... ' por tanto es mejor llamar a los propios mtodos de la clase. ' Private Property Let IPruebaPersistable_AoNacimiento(ByVal RHS As Long) Me.AoNacimiento = RHS End Property Private Property Get IPruebaPersistable_AoNacimiento() As Long IPruebaPersistable_AoNacimiento = Me.AoNacimiento End Property

483

Private Property Let IPruebaPersistable_Comentario(ByVal RHS As String) Me.Comentario = RHS End Property Private Property Get IPruebaPersistable_Comentario() As String IPruebaPersistable_Comentario = Me.Comentario End Property Private Property Set IPruebaPersistable_Copia(ByVal RHS As IPruebaPersistable) Set Me.Copia = RHS End Property Private Property Get IPruebaPersistable_Copia() As IPruebaPersistable Set IPruebaPersistable_Copia = Me.Copia End Property Private Property Let IPruebaPersistable_email(ByVal RHS As String) Me.email = RHS End Property Private Property Get IPruebaPersistable_email() As String IPruebaPersistable_email = Me.email End Property Private Property Get IPruebaPersistable_FechaCreacin() As String IPruebaPersistable_FechaCreacin = m_FechaCreacin End Property Private Property Let IPruebaPersistable_Nombre(ByVal RHS As String) Me.Nombre = RHS End Property Private Property Get IPruebaPersistable_Nombre() As String IPruebaPersistable_Nombre = Me.Nombre End Property Public Function Clone() As cPersistable

484

' Devuelve una copia de esta clase (23/Ago/99) ' Se usa la tcnica descrita en la ayuda de Visual Basic 6.0 ' para copiar objetos usando ficheros de texto, ' aunque en este caso no sea necesario ningn fichero intermedio... ' ' Nota: ' Anteriormente haba usado una NUEVA variable intermedia, ' pero no es necesario, incluso si la variable a la que se asigna con Clone ' ' Dim pb As PropertyBag ' Instanciacin de un objeto PropertyBag. Set pb = New PropertyBag ' Guarda el objeto en el PropertyBag mediante WriteProperty. pb.WriteProperty "CopiaObjeto", Me ' Instancia el objeto desde el objeto PropertyBag Set Clone = pb.ReadProperty("CopiaObjeto") End Function no se ha creado con NEW

'----------------------------------------------------------------------------' cNotPersistable (22/Ago/99) ' Revisado para el curso bsico (12/Jul/01) ' ' Esta clase tiene asignado el valor 0 a la propiedad Persistable, ' por tanto de esta clase no se podr hacer copias ' ' Para poder usar esto de la "persistencia" de las propiedades hay que

485

' asignar a la propiedad Persistable de la clase el valor 1Persistable ' (ver la clase cPersistable) ' ' Guillermo 'guille' Som, 1999-2001 '----------------------------------------------------------------------------Option Explicit Implements IPruebaPersistable Private m_FechaCreacin As String Private m_Copia As IPruebaPersistable Private m_Nombre As String Private m_AoNacimiento As Long Private m_email As String Private m_Comentario As String Private Sub Class_Initialize() 'Set m_Copia = New IPruebaPersistable m_FechaCreacin = Format$(Now, "dd/mmm/yyyy hh:mm:ss") End Sub Private Sub Class_Terminate() 'Set m_Copia = Nothing End Sub ' Los procedimientos implementados delegan en las propiedades de la clase ' Private Property Let IPruebaPersistable_AoNacimiento(ByVal RHS As Long) Me.AoNacimiento = RHS End Property Private Property Get IPruebaPersistable_AoNacimiento() As Long IPruebaPersistable_AoNacimiento = Me.AoNacimiento End Property

486

Private Property Let IPruebaPersistable_Comentario(ByVal RHS As String) Me.Comentario = RHS End Property Private Property Get IPruebaPersistable_Comentario() As String IPruebaPersistable_Comentario = Me.Comentario End Property Private Property Set IPruebaPersistable_Copia(ByVal RHS As IPruebaPersistable) Set Me.Copia = RHS End Property Private Property Get IPruebaPersistable_Copia() As IPruebaPersistable Set IPruebaPersistable_Copia = Me.Copia End Property Private Property Let IPruebaPersistable_email(ByVal RHS As String) Me.email = RHS End Property Private Property Get IPruebaPersistable_email() As String IPruebaPersistable_email = Me.email End Property Private Property Get IPruebaPersistable_FechaCreacin() As String IPruebaPersistable_FechaCreacin = m_FechaCreacin End Property Private Property Let IPruebaPersistable_Nombre(ByVal RHS As String) Me.Nombre = RHS End Property Private Property Get IPruebaPersistable_Nombre() As String IPruebaPersistable_Nombre = Me.Nombre End Property

487

Public Property Get AoNacimiento() As Long AoNacimiento = m_AoNacimiento End Property Public Property Let AoNacimiento(ByVal NewValue As Long) m_AoNacimiento = NewValue End Property Public Property Get Comentario() As String Comentario = m_Comentario End Property Public Property Let Comentario(ByVal NewValue As String) m_Comentario = NewValue End Property Public Property Get Copia() As IPruebaPersistable Set Copia = m_Copia End Property Public Property Set Copia(ByVal NewValue As IPruebaPersistable) Set m_Copia = NewValue End Property Public Property Get email() As String email = m_email End Property Public Property Let email(ByVal NewValue As String) m_email = NewValue End Property ' Propiedad de slo lectura Public Property Get FechaCreacin() As String FechaCreacin = m_FechaCreacin End Property Public Property Get Nombre() As String

488

Nombre = m_Nombre End Property Public Property Let Nombre(ByVal NewValue As String) m_Nombre = NewValue End Property Public Function Clone() As cNotPersistable ' Devuelve una copia de esta clase (23/Ago/99) Dim NewClase As cNotPersistable Dim NewCopia As IPruebaPersistable Set NewClase = New cNotPersistable Set NewCopia = New IPruebaPersistable ' Esto hara que se quedara sin espacio en la pila 'Set NewCopia = Me.Clone ' As funcionara bien With NewCopia .Nombre = Me.Copia.Nombre .AoNacimiento = Me.Copia.AoNacimiento .Comentario = Me.Copia.Comentario .email = Me.Copia.email ' Cmo se copiara el objeto??? ' El objeto debera tener un mtodo Clone para copiarlo. '.Copia = Me.Copia End With ' Asignar cada una de las propiedades a la nueva copia. ' La desventaja es que, si se tienen muchas propiedades... ' pues es ms trabajo y puede que por despiste se olvide algo... With NewClase .Nombre = m_Nombre .AoNacimiento = m_AoNacimiento .email = m_email .Comentario = m_Comentario Set .Copia = NewCopia End With ' Si se devuelve este mismo objeto,

489

' no se crear una nueva instancia de la clase 'Set Clone = Me Set Clone = NewClase End Function

Si quieres bajar el cdigo completo del ejemplo, pulsa este link. (copiar_objetos.zip 10.1 KB) Nerja, 24 de Agosto de 1999 Revisado el 12/Jul/2001 para el curso bsico

Curso Bsico de Programacin en Visual Basic


Confo en que no acabes tarumba con tantas vueltas que estoy dando... la entrega anterior fue sobre las clases en Visual Basic y esta que te ofrezco ahora sigue los pasos de la entrega 36 sobre el acceso a bases de datos, aunque en esta ocasin sin usar el data control y con DAO. En otra entrega, veremos lo mismo, pero usando ADO... hasta que finalmente nos quedemos con ese tipo de acceso a datos, ya que Microsoft no "quiere" que sigamos usando DAO y lo trata como obsoleto, aunque no lo es tanto... pero como hay que est a la ltima en esto de programacin, habr que hacerles caso... as que tambin ve preparando el cuerpo, ya que a partir del 2002 la "nueva ola" ser .NET y habr que ir "conciencindose" de la nueva tecnologa... pero no te preocupes que tambin podrs aprenderla... para que no te quedes atrs, aunque eso ser en un futuro... talvez no muy lejano...

Cmo acceder a bases de datos DAO, sin el datacontrol.


Empecemos desde el principio... para que te vayas acostumbrando... aunque ya deberas saber crear tus propios formularios y aadir los controles que te indique... aunque prcticamente no te lo haya explicado en el curso, es algo que te ensean en prcticamente cualquier libro e incluso en los "tutoriales" que incluye el Visual Basic. Slo decirte que usando la versin 5CCE de Visual Basic no se pueden acceder a datos, as que tendrs que usar la versin Profesional o la Empresarial... pero si de verdad te quieres dedicar a esto de la programacin... esa sern las versiones de las que deberas disponer... y no me preguntes dnde conseguirlas... ya que las venden en cualquier "tienda" de informtica o en los distribuidores que puedes encontrar en Internet.

490

Vayamos al tema que nos interesa, ya que no es plan de hacer "marketing" gratuito a los distribuidores que "no sponsorizan" este curso bsico... hum! Crea un nuevo proyecto, se crear un proyecto con un formulario. Aade los siguientes controles para que el formulario quede con el aspecto que te muestro a continuacin, (que es parecido al usado en las entregas anteriores de acceso a datos):

Aspecto del formulario en tiempo de diseo Los controles usados son: En la parte superior: cmdMover, un array de 0 a 3 Label1, un array de 0 a 2, Text1, un array de 0 a 2 cmdAdd, cmdActualizar, cmdBorrar El segundo grupo: Label2, Text2, Option1, Option2, cmdBuscar, cmdBuscarSig Elbotn de salir es: cmdSalir En el men Proyecto/Referencias... selecciona Microsoft DAO 3.51 Object Library -aunque tambin puedes seleccionar cualquier otra que empiece por Microsoft DAO... cualquiera de ellas vale... puede que tambin tengas la 3.6 e incluso la 2.5/3.51 Compatibility Library, todo depender de la versin de DAO que tengas instaladaA diferencia de cuando seleccionas un componente, despus de cerrar el cuadro de dilogo no vers nada nuevo en la ventana de herramientas, ya que las referencias no aaden nuevos controles, pero si que aaden nuevas libreras que exponen objetos que podemos usar en nuestra aplicacin... Uno de esos objetos es el objeto Database, el cual se usar para "mantener" una referencia a la base de datos, con la cual podremos abrir recordsets que nos permitirn, entre otras cosas, introducir y modificar los datos de cualquier tabla incluida en la base de datos a la que queramos acceder... Qu es un Recordset? Segn la ayuda de Visual Basic, "Un objeto Recordset representa los registros de una

491

tabla o los registros del resultado de ejecutar una consulta". Una consulta es... eso... una consulta... una especie de bsqueda avanzada, en la que podemos indicar varios criterios de bsqueda... aunque tambin, como vers pronto, es algo ms sencillo que todo eso. No te preocupes que veremos algunos ejemplos de "consultas". Para poder manejar los objetos Database y Recordset, hay que crear unas variables para dichos objetos, por tanto en la seccin de declaraciones del formulario (General/Declaraciones), aade estas lneas: Option Explicit Private db As Database Private rs As Recordset

De esta forma tendremos una variable llamada db que apuntar al objeto Database y otra, llamada rs, que apuntar a un objeto del tipo Recordset. En el evento Form_Load asignaremos los "objetos reales" a esas dos variables. Para poder abrir la base de datos, necesitamos saber el path en el que se encuentra dicha base de datos, para ello vamos a crea una constante, en la cual tendrs que indicar el path correcto, es decir, el sitio exacto en el que se encuentra la base de datos, en nuestro caso ser BIBLIO.MDB, la base de ejemplo que se incluye con Visual Basic. Const sPathBase As String = "C:\Program Files\Microsoft Visual Studio\VB98\BIBLIO.MDB" A continuacin abrimos la base de datos usando la funcin OpenDatabase, la cual devuelve un objeto de la base de datos recin abierta, uno de los parmetros que espera recibir dicha funcin es el path de la base de datos que queremos abrir, en nuestro ejemplo usaremos el contenido de la constante anterior: sPathBase. ' Crear el objeto de base de datos Set db = OpenDatabase(sPathBase) A continuacin creamos el objeto recordset a partir de una "consulta" realizada a la base de datos, en este caso lo que queremos "consultar" son TODOS los campos de la tabla Authors: ' Crear el recordset con la tabla que queremos manipular Set rs = db.OpenRecordset("SELECT * FROM Authors", dbOpenDynaset) La constante dbOpenDynaset le indica al mtodo OpenRecordset que lo que queremos asignar a la variable rs, (de tipo Recordset), es del tipo Dynaset, este tipo de recordset permite mostrar y modificar los datos asignados a dicho recordset; adems de este tipo de recordset, existen otros los cuales slo se pueden usar para "recorrer" los datos o mostrarlos, si as lo prefieres, pero no permiten modificarlos, ese tipo de recordset tambin lo veremos en esta misma entrega. Una vez que tenemos la variable rs, podemos acceder al contenido de la tabla Authors tal y como lo haciamos antes con el control Data. Para que te hagas una idea, rs tendr el mismo funcionamiento que el que tena Data1.Recordset en los ejemplos de las entregas anteriores sobre acceso a datos, en particular las entregas 34 y 35.

492

Como no tenemos un control data, el cual nos permita movernos por los diferentes registros de la tabla, en el formulario hay cuatro botones, que se usarn para esa tarea: movernos tanto al primero, anterior, siguiente o ltimo registro, el cdigo para hacerlo es el que te muestro a continuacin:

Private Sub cmdMover_Click(Index As Integer) On Error Resume Next ' ' Cuando las propiedades BOF y EOF dan como resultado TRUE, ' es que no hay datos, por tanto, salir del procedimiento If rs.BOF = True And rs.EOF = True Then Exit Sub End If ' ' Mover al registro indicado segn el botn pulsado If Index = 0 Then rs.MoveFirst ElseIf Index = 1 Then rs.MovePrevious ElseIf Index = 2 Then rs.MoveNext ElseIf Index = 3 Then rs.MoveLast End If ' ' Si estamos antes del primero. mover al primero If rs.BOF Then rs.MoveFirst ' Si estamos despus del ltimo, mover al ltimo ElseIf rs.EOF Then rs.MoveLast End If ' Si no se ha producido error, mostrar los datos If Err = 0 Then MostrarRegistro End If ' Err = 0 End Sub ' Al ltimo ' Al siguiente ' Al anterior ' Al primero

493

Lo primero que hacemos es comprobar si hay datos en el recordset, para ello comprobamos que ni BOF ni EOF den como resultado un valor verdadero. Cuando BOF es True, significa que el recordset est "antes" del primer registro, por otro lado EOF ser True cuando estemos despus del final de los registros. Cuando estas dos propiedades devuelven un valor verdadero, es que no hay datos en el recordset . A continuacin movemos el "puntero" del recordset al registro indicado, segn el valor de Index sabremos que botn se ha pulsado y por tanto que "accin" tenemos que elegir. En los comentarios puedes ver qu valor corresponde con cada movimiento. Como es posible que el usuario pulse en el botn anterior o siguiente despus de estar al principio o al final respectivamente, tenemos que "controlar" si ya estamos al principio o al final del recordset para posicionarnos en el registro adecuado. Por ltimo, si todo ha ido bien, (no se ha producido un error), llamamos al procedimiento que muestra el contenido del registro activo en las cajas de texto. Esto ltimo lo hace el procedimiento MostrarRegistro, cuyo cdigo es el siguiente:

Private Sub MostrarRegistro() ' Mostrar los datos del registro actual ' A este procedimiento hay que llamarlo cada vez que ' queramos mostrar los datos del registro actual. With rs Text1(0) = .Fields("Au_ID") Text1(1) = .Fields("Author") Text1(2) = .Fields("Year Born") End With End Sub Si le echas un vistazo al cdigo de las pruebas con el control Data, notars que no era necesario llamar expresamente a un procedimiento para que los datos se mostraran, esto era as porque el propio datacontrol se encargaba de actualizar la informacin en los controles que tena "enlazados", pero ahora no tenemos un control que "automatice" esta tarea, as que, tenemos que hacerlo por nuestros medios... por suerte no es tan difcil. Ahora vamos a ver los procedimientos de Aadir un nuevo registro, actualizar uno ya existente y eliminar el registro actual. En el propio cdigo encontrars la explicacin de que es lo que hace cada lnea usada. El cdigo de Aadir un nuevo registro: Private Sub cmdAdd_Click() ' Aadir un nuevo registro With rs .AddNew

494

' Aadimos algn texto, para saber que es un nuevo dato .Fields("Author") = "Nuevo Autor" ' Actualizamos los datos, para que se graben en el recordset .Update End With End Sub El cdigo de Actualizar el contenido de las cajas de texto en el recordset: Private Sub cmdActualizar_Click() ' Guardar el contenido de las cajas de texto With rs ' Antes de actualizar los datos del recordset, ' hay que ponerlo en modo edicin .Edit ' Este campo es autonumrico, as que no asignarlo '.Fields("Au_ID") = Text1(0) + 0 ' Aadimos una cadena vaca al final ' ya que si Text1(1) est vaco, se asignar un valor NULL y dar error .Fields("Author") = Text1(1) & "" sumar 0 ' Idem con el ao de nacimiento, pero como es numrico, se .Fields("Year Born") = Text1(2) + 0 ' Actualizar los datos en el recordset .Update End With End Sub Y por ltimo el cdigo para eliminar un registro... como siempre, borrar es muy fcil... Private Sub cmdBorrar_Click() ' Borrar el registro actual ' Se comprueba que haya algn registro activo, ' para ello se comprueba que no hayamos pasado del principio o el final del Recordset ' ' Comprobar que hay registros, porque si no hay, dar error If Not (rs.EOF Or rs.BOF) Then

495

' Eliminar el registro actual rs.Delete ' ' Movemos al primer registro cmdMover_Click 0 End If End Sub Bien, bsicamente estas tareas son prcticamente iguales a las usadas con el datacontrol, con la salvedad de que hay que mostrar los datos cuando se cambia el registro activo. Ahora veremos el cdigo usado para buscar los datos, el cual es tambin prcticamente lo mismo que cuando se tena el datacontrol. Fjate que la nica diferencia es que en lugar de usar la propiedad Recordset del datacontrol, se usa el recordset "cargado" con los datos y que est contenido en la variable rs. Este es el cdigo de buscar y buscar siguiente, para lo cual usamos los mtodos FindFirst y FindNext respectivamente.

Private Sub cmdBuscar_Click() ' Buscar el primer registro que coincida con el dato buscado Buscar End Sub Private Sub cmdBuscarSig_Click() ' Buscar el siguiente 'Buscar Siguiente:=True Buscar True End Sub Private Sub Buscar(Optional ByVal Siguiente As Boolean = False) ' Procedimiento para buscar el dato indicado (18/Ene/01) ' Si Siguiente = True, se busca a partir del registro activo Dim nReg As Long Dim sBookmark As String Dim sBuscar As String ' ' Iniciamos la deteccin de errores On Error Resume Next '

496

' Buscar la primera coincidencia en el recordset del Data1 If Option1.Value Then ' Convertir el contenido de TextBox en un nmero nReg = Val(Text2) ' en el campo Au_ID sBuscar = "Au_ID = " & nReg End If If Option2.Value Then ' en el campo Author sBuscar = "Author Like '" & Text2.Text & "'" End If ' With rs ' Guardar la posicin anterior, por si no se halla lo buscado... sBookmark = .Bookmark ' If Siguiente = False Then ' Buscar desde el principio .MoveFirst .FindFirst sBuscar Else ' Busca a partir del registro actual .FindNext sBuscar End If ' Devolver un error si no se halla lo buscado ' aunque no siempre es as... If .NoMatch Then Err.Clear MsgBox "No existe el dato buscado o ya no hay ms datos que mostrar." ' Posicionar el recordset en la posicin guardada .Bookmark = sBookmark End If ' Mostrar los datos del registro actual MostrarRegistro End With End Sub

497

Cmo realizar consultas con DAO? Ya hemos visto cmo introducir y modificar registros, tambin hemos visto cmo buscar un dato y seguir mostrando el resto de las coincidencias que se vayan produciendo. Pero hay ocasiones en las que a veces es necesario poder ver TODO el resultado de la bsqueda, incluso a veces nos interesa buscar ms de un dato a la vez. Eso mismo se puede hacer con FindFirst y FindNext, es decir, podemos buscar en ms de un campo del mismo registro el dato o datos que nos interesa a un mismo tiempo. Pero adems de usar recordsets de slo lectura, podemos acelerar esa tarea de "obtener" la informacin que queramos realizando consultas en lugar de ir buscando uno por uno los datos. Independientemente de cmo hagamos la consulta, siempre nos quedar el recurso de poder usar los mtodos FindXXX para buscar a su vez datos dentro del resultado de la consulta, ya que al fin y al cabo el resultado de dicha consulta se almacena en un objeto recordset... que lio! ahora veremos con ejemplos cmo funciona todo esto. Para mostrar el resultado de la "bsqueda" o consulta, vamos a usar un control ListView. Para aadir un control ListView a nuestro proyecto, primero hay que "aadir" el control a la barra de herramientas de Visual Basic, para ello, selecciona la opcin Componentes del men Proyecto y del cuadro de dilogo que te muestra, selecciona Microsoft Windows Common Controls, (pude que a continuacin de este nombre te muestre la versin de Visual Basic, en mi caso, me muestra 6.0, pero tambin la 5.0 (SP2), ya que tengo instalado esas dos versiones de VB. Una vez que has pulsado en Aceptar, vers que se han aadido nuevos controles a la barra de herramientas (ToolBox), selecciona el que representa al Listview y aadelo al formulario, mediante cdigo configuraremos la apariencia. Si quieres puedes crearte un nuevo proyecto para esta "segunda" prueba, ya que, adems de aadir el ListView, tambin vamos a quitar los botones de bsqueda, porque no tiene sentido tenerlos... al menos para el propsito de este ejemplo. El aspecto del formulario sera el siguiente:

El aspecto, en tiempo de diseo, del segundo formulario

498

El cdigo para configurar el ListView en tiempo de ejecucin para mostrar la informacin de la tabla Authors de la base de datos Biblio.mdb lo pondremos en el evento Form_Load y es el siguiente: With ListView1 ' El tipo de Listview que queremos es del tipo "reporte" .View = lvwReport ' Que muestre las lneas de separacin entre datos .GridLines = True ' Que no se puedan modificar los datos del listview .LabelEdit = lvwManual ' Aadimos las cabeceras .ColumnHeaders.Add , , "Au_ID", 900 .ColumnHeaders.Add , , "Autor", 2700 .ColumnHeaders.Add , , "Ao nacimiento", 1500, lvwColumnRight End With Ahora la bsqueda slo se har por el nombre del autor y se mostrarn en la lista todos los autores que coincidan con los datos que queremos buscar. El cdigo del botn Buscar sera el siguiente:

Private Sub cmdBuscar_Click() ' Mostrar los datos en el listview Dim sBuscar As String Dim tRs As Recordset Dim tLi As ListItem ' ' Formar la cadena de la consulta: ' Se busca por el nombre del autor y se muestran clasificados por el nombre sBuscar = "SELECT * FROM Authors WHERE Author LIKE '" & Text2 & "' ORDER BY Author" ' Creamos un recordset del tipo "esttico", el cual no es modificable ' para poder modificarlo, tendra que ser del tipo dbOpenDynamic Set tRs = db.OpenRecordset(sBuscar, dbOpenSnapshot) ' Comprobar que hay datos en el recordset With tRs ' Si no hay datos...

499

If (.BOF And .EOF) Then MsgBox "No se han encontrado los datos buscados" Else ' Mostrar los datos hallados ListView1.ListItems.Clear .MoveFirst Do While Not .EOF Set tLi = ListView1.ListItems.Add(, , .Fields("Au_ID") & "") tLi.SubItems(1) = .Fields("Author") & "" tLi.SubItems(2) = .Fields("Year Born") & "" .MoveNext Loop End If End With End Sub En otra ocasin veremos cmo "disear" un formulario para consultas con distintas opciones de bsqueda, as como para bsqueda en mltiples campos. En la prxima entrega veremos el cdigo que habra que usar para hacer esto mismo pero usando ADO. As que, paciencia y a ser buenos. Nos vemos Guillermo P.S. Este link te permite bajar el cdigo de los dos ejemplos. (basico40_cod.zip 7.98 KB)

Curso Bsico de Programacin en Visual Basic


Aqu tienes la entrega prometida para acceder a bases de datos ADO sin el data control. El ejemplo o cdigo que te muestro, como vers, es prcticamente idntico al mostrado en el ltimo ejemplo de la entrega anterior, salvo en lo que a las referencias a las libreras que hay que usar para poder acceder a las bases de datos ADO, pero esto lo veremos a continuacin, as que prepara el cuerpo y el alma y te enterars...

500

Cmo acceder a bases de datos ADO, sin el datacontrol.


Empecemos desde el principio... para que te vayas acostumbrando... aunque ya deberas saber crear tus propios formularios y aadir los controles que te indique... aunque prcticamente no te lo haya explicado en el curso, es algo que te ensean en prcticamente cualquier libro e incluso en los "tutoriales" que incluye el Visual Basic. (Toc, toc, toc, Guille... esto no es lo mismo que lo que dijiste en la entrega anterior? S, pero como para esta tambin es vlido y ya lo tengo escrito, para que inventarme otra cosa? Pues s... la cosa es buscar alguna excusa para no trabajar! Buenooo, quitar algo para que no sea exactamente "copiado", que es lo que suelen hacer la mayora de la gente cuando escriben libros y otras cosas sobre las nuevas versiones... )

El formulario de prueba: Crea un nuevo proyecto, se crear un proyecto con un formulario. Aade los siguientes controles para que el formulario quede con el aspecto que te muestro a continuacin, (que es el mismo del ltimo ejemplo de la entrega anterior):

Aspecto del formulario en tiempo de diseo Los controles usados son: En la parte superior: cmdMover, un array de 0 a 3 Label1, un array de 0 a 2, Text1, un array de 0 a 2 cmdAdd, cmdActualizar, cmdBorrar El segundo grupo: Label2, Text2, cmdBuscar

501

Un Listview1 para mostrar los resultados de la bsqueda Una etiqueta (LblData) para mostrar algunos mensajes y el botn de salir es: cmdSalir Las referencias necesarias: En el men Proyecto/Referencias... selecciona Microsoft ActiveX Data Objects 2.6 Library -aunque tambin puedes seleccionar cualquiera de las otras que te muestra, (si es que te muestra alguna ms); a mi me aparecen las versiones 2.0, 2.1, 2.5 y 2.6, la nica que no deberas seleccionar es la 2.0 que ya est obsoletaUna vez aadida la referencia a los objetos ADO, puedes usar los objetos expuestos por esta librera. En este ejemplo usaremos dos de esos objetos, que sern los que en la mayora de los casos usemos: El objeto Connection y el objeto Recordset. El primero es el que permite acceder a la base de datos y el segundo ser el que acceda a los datos propiamente dicho, cuando veas el cdigo seguramente lo entenders y si no lo entiendes... es que deberas leerte TODAS las entregas, empezando por la primera! (tendr que excusarse el Guille de alguna forma si no entiendes lo que explica) Los objetos ADO ms comunes: Normalmente, el objeto Connection suele declararse de forma que sea visible en todo el formulario, salvo en el caso de que aadieses algn mdulo BAS y necesitaras usarlo desde ese mdulo BAS, en cuyo caso, deberas declararlo Pblico o Global, pero como por ahora no es necesario... dejemos las cosas estar... El objeto Recordset de ADO produce eventos, por tanto, si necesitamos acceder a esos eventos, declararemos la variable con WithEvents, de esa forma podemos interceptar los eventos que produzca de la misma manera que lo hacemos con el resto de controles (de esto veremos ms en las entregas de las clases) En el procedimiento Buscar, veremos cmo usar otro recordset, pero de la forma tradicional: sin eventos. Escribe este cdigo en las declaraciones generales del formulario: Option Explicit ' En ADO, se usa el objeto Connection para abrir las bases de datos Private cnn As ADODB.Connection ' Necesitamos los eventos si queremos controlar algunas cosillas Private WithEvents rst As ADODB.Recordset Al cargar el formulario, creamos los objetos y asignamos la informacin correspondiente para abrir la base de datos y crear o llenar el Recordset. Como te dije, la base de datos se abre usando el objeto Connection, del cual usaremos el mtodo Open al cual hay que indicarle el "proveedor" y el nombre de la base de datos: Private Sub Form_Load() ' Text2 = ""

502

' ' Indicar el path correcto de la base de datos ' ACUERDATE DE PONER EL PATH CORRECTO! Const sPathBase As String = "C:\Program Files\Microsoft Visual Studio\VB98\BIBLIO.MDB" ' ' Crear los objetos Set cnn = New ADODB.Connection Set rst = New ADODB.Recordset ' ' Crear la conexin manualmente 97 2000 ' Usar "Provider=Microsoft.Jet.OLEDB.3.51;" para bases de Access ' Usar "Provider=Microsoft.Jet.OLEDB.4.0;" With cnn .ConnectionString = _ "Provider=Microsoft.Jet.OLEDB.3.51;" & _ "Data Source=" & sPathBase & ";" .Open End With ' Indicarle de que tabla vamos a leer los datos rst.Open "SELECT * FROM Authors", cnn, adOpenDynamic, adLockOptimistic ' ' Asignar los nombres de los campos a las etiquetas Label1(0).Caption = "Au_ID:" Label1(1).Caption = "Author:" Label1(2).Caption = "Year Born:" ' With ListView1 ' El tipo de Listview que queremos es del tipo "reporte" .View = lvwReport ' Que muestre las lneas de separacin entre datos .GridLines = True ' Que no se puedan modificar los datos del listview .LabelEdit = lvwManual ' Aadimos las cabeceras .ColumnHeaders.Add , , "Au_ID", 900 .ColumnHeaders.Add , , "Autor", 2700 para bases de Access

503

.ColumnHeaders.Add , , "Ao nacimiento", 1500, lvwColumnRight End With ' ' Si hay datos, posicionarlo en el primer registro: cmdMover_Click 0 End Sub En el ejemplo de la entrega anterior, tenamos un procedimiento al que llambamos cada vez que tenamos que actualizar la informacin del registro que estaba activo. Pero como el Recordset de ADO produce eventos, vamos a usar uno de esos eventos: MoveComplete, el cual se produce cada vez que se cambia el registro activo. Como te coment antes, las variables declaradas con WithEvents siguen la misma "nomenclatura" que los eventos de los controles, por tanto, ese evento estar en: rst_MoveComplete (selecciona el objeto rst de la lista desplegable derecha y el evento MoveComplete de la lista de la izquierda), ste es el cdigo para mostrar los datos cada vez que se cambia el registro activo, aunque realmente no sera necesario si hubisemos "ligado" los controles con el recordset... pero de esa forma no tendramos el control total sobre los datos, as que vamos a seguir con esto de usar los eventos: Private Sub rst_MoveComplete(ByVal adReason As ADODB.EventReasonEnum, _ ByVal pError As ADODB.Error, adStatus As ADODB.EventStatusEnum, _ ByVal pRecordset As ADODB.Recordset) ' Cada vez que el registro actual cambie, ' se producir este evento (igual que con el DataControl) With rst '--------------------------------------------------------------------' Nota aclaratoria del 18/Dic/2003: ' Cuando en un Recordset no hay datos, tanto BOF como EOF devuelven True '--------------------------------------------------------------------If .EOF And .BOF Then lblData.Caption = "No hay ningn registro activo" .MoveFirst Else Text1(0) = .Fields("Au_ID") ' Por si el dato es nulo, aadirle una cadena vacia Text1(1) = .Fields("Author") & "" Text1(2) = .Fields("Year Born") & "" End If

504

End With End Sub En este evento podemos usar tanto el objeto rst como el que est en el parmetro: pRecordset, los dos se refieren al mismo objeto. Hacemos una pequea comprobacin de que no nos encontremos con un recordset vaco o que est fuera de los lmites permitidos, (cosa que ocurre cuando queremos pasar al siguiente registro cuando estamos al final (se produce EOF) o cuando pasamos al registro anterior y estamos al principio, (se produce BOF). Si todo va bien, asignamos a las cajas de textos el contenido de los campos correspondientes. Fjate que en algunos de los campos aado una cadena vaca al contenido del campo, esto es para los casos en que esos campos contengan un valor nulo. Si se lo quitas al Year Born, vers cmo se produce un error, ya que en la base de datos Biblio, que es la que usaremos para este ejemplo, ese campo no suele estar asignado. Ahora te muestro el resto del cdigo para los botones de Mover, Nuevo, Actualizar y Eliminar, que es prcticamente igual al de la versin para DAO, con la salvedad de Actualizar, ya que en ADO no es necesario poner un registro (o columna que es como se debera decir), en modo edicin para poder modificarlo, ya que al usar Update, se actualiza el registro. Private Sub cmdMover_Click(Index As Integer) ' Mover segn el botn pulsado 'On Error Resume Next ' With rst If Index = 0 Then .MoveFirst ElseIf Index = 1 Then .MovePrevious ElseIf Index = 2 Then .MoveNext ElseIf Index = 3 Then .MoveLast End If ' If .BOF Or .EOF Then .MoveFirst lblData.Caption = " No hay datos..." Else lblData.Caption = " Registro actual: " & rst("Au_ID") End If ' ltimo ' Siguiente ' Anterior ' Primero

505

End With ' Err = 0 End Sub Private Sub cmdAdd_Click() ' Aadir un nuevo registro rst.AddNew ' Aadimos algn texto, para que no se pierda este registro Text1(1) = "Nuevo" ' Actualizamos los datos rst.Update ' Movemos al ltimo registro para que los cambios se hagan permanentes ' y se muestre el nuevo registro rst.MoveLast End Sub Private Sub cmdActualizar_Click() ' Guardar el contenido de las cajas de texto With rst ' Este campo es auto numrico, as que no asignarlo '.Fields("Au_ID") = Text1(0) + 0 ' Aadimos una cadena vaca al final ' ya que si Text1(1) est vaco, se asignar un valor NULL y dar error .Fields("Author") = Text1(1) & "" sumar 0 ' Idem con el ao de nacimiento, pero como es numrico, se .Fields("Year Born") = Text1(2) + 0 ' Actualizar los datos en el recordset .Update End With End Sub Private Sub cmdBorrar_Click() ' Borrar el registro actual ' Se comprueba que haya algn registro activo, ' para ello se comprueba que no hayamos pasado del principio o el final del Recordset

506

' ' Comprobar que hay registros, porque si no hay, dar error If (rst.EOF Or rst.BOF) Then ' Avisar de que no hay registros lblData.Caption = "Ningn registro activo" Else ' Eliminar el registro actual rst.Delete ' ' Movemos al primer registro para que los cambios se hagan permanentes ' (tambin podramos haberlo movido al ltimo registro) rst.MoveFirst End If End Sub Creo que no necesita informacin adicional, en los comentarios est todo explicado. Para terminar, vamos a ver el cdigo para Buscar datos: Private Sub cmdBuscar_Click() ' Mostrar los datos en el listview Dim sBuscar As String Dim tRs As Recordset Dim tLi As ListItem ' ' Comprobar si tiene caracteres "no vlidos" para ADO: ' NOTA: Replace es una funcin de VB6 sBuscar = Text2 sBuscar = Replace(sBuscar, "*", "%") sBuscar = Replace(sBuscar, "?", "_") ' Text2 = sBuscar ' Formar la cadena de la consulta: ' Se busca por el nombre del autor y se muestran clasificados por el nombre sBuscar = "SELECT * FROM Authors WHERE Author LIKE '" & sBuscar & "' ORDER BY Author" ' Creamos un recordset del tipo "esttico", el cual no es modificable

507

' para poder modificarlo, tendra que ser del tipo dbOpenDynamic Set tRs = cnn.Execute(sBuscar) ' Comprobar que hay datos en el recordset With tRs ' Si no hay datos... '--------------------------------------------------------------------' Nota aclaratoria del 18/Dic/2003: ' Cuando en un Recordset no hay datos, tanto BOF como EOF devuelven True '--------------------------------------------------------------------If (.BOF And .EOF) Then MsgBox "No se han encontrado los datos buscados" Else ' Mostrar los datos hallados ListView1.ListItems.Clear .MoveFirst Do While Not .EOF Set tLi = ListView1.ListItems.Add(, , .Fields("Au_ID") & "") tLi.SubItems(1) = .Fields("Author") & "" tLi.SubItems(2) = .Fields("Year Born") & "" .MoveNext Loop End If End With End Sub Caracteres comodines: Como podrs comprobar el procedimiento de buscar es muy parecido, por no decir casi idntico al usado en DAO, con la salvedad de que hacemos una comprobacin para cambiar los caracteres comodines que hubiese en el texto indicado para la bsqueda, ya que en ADO no son los mismos que en DAO. Segn la ayuda de ADO, se usar el carcter % (tanto por ciento) en lugar del * (asterisco), cuando queramos indicar cualquier cosa que haya en el sitio en que se encuentra dicho carcter, por ejemplo: %jan% encontrar todos los registros que contenga "jan", est en el sitio que est: Janet, Alejandro, etc. Si pusiramos jan%, slo mostrara los que empezaran con jan, etc. Por otro lado, el carcter _ (subrayado bajo) sustituye a la ? (interrogacin), en este caso este comodn significa "cualquier carcter que est en esa posicin", por ejemplo: %r_us % encontrar cualquier palabra que contenga: una r seguida de cualquier cosa, seguida de us. Tal es el caso de Rouse, Marcus, etc.

508

La funcin Replace usada para cambiar esos caracteres es propia de la versin 6 de Visual Basic, en VB5 no existe, as que tendrs que crearte tu propia funcin Replace. Bueno, vale... aqu la tienes: Private Function Replace(ByVal Expresion As String, _ ByVal Encontrar As String, _ ByVal ReemplazarCon As String) As String '--------------------------------------------------------------' Funcin Replace para usar con VB5 o anteriores '--------------------------------------------------------------Dim i As Long, j As Long ' j = 1 Do ' Buscar la cadena indicada en la expresin i = InStr(j, Expresion, Encontrar) nueva ' Si la hemos hallado, quitamos dicha cadena y ponemos la If i Then Expresion = Left$(Expresion, i - 1) & ReemplazarCon & Mid$(Expresion, i + Len(Encontrar)) j = i + 1 End If Loop While i ' Devolver la cadena Replace = Expresion End Function Esto esto amigos! (y para las amigas nada?, es que cuando se habla de forma genrica, se usa el gnero masculino... como si fuese neutro... s, ya, es que algunos que dicen ser muy masculinos, realmente son neutros... a quin te refieres? no, a nadie en concreto... es por aclarar las cosas... Ah! crea...) Dejemos a los Guilles con su discusin y te espero en la prxima entrega, que an no s de que tratar... ser de clases? ser de acceso a datos? ser sobre algn tema nuevo? ya veremos... Nos vemos Guillermo

509

Curso Bsico de Programacin en Visual Basic

Buenas, ya estoy de nuevo por aqu... s, se que han pasado un montn de das... pero... En esta entrega vamos a seguir con las clases, en particular con las colecciones personalizadas. Si quieres refrescarte la memoria, podras volver a las entregas anteriores sobre las clases, segn recuerdo son las entregas 37, 38 y 39. En la entrega 38 se trat el tema de las colecciones, pero de forma genrica, en esta entrega veremos cmo crear nuestra propia clase-coleccin para que maneje elementos del tipo que nosotros queramos. Es habitual que los nombres de las colecciones que contendrn elementos de un tipo definido por nosotros, (en este caso de una clase definida por nosotros, no un tipo de datos creado mediante TYPE), tengan un nombre que sea el plural del nombre del objeto que contendr. Por ejemplo, si la coleccin va a contener elementos del tipo cColega, se llamara cColegas. Esto simplemente es una "recomendacin", ya que el nombre que tenga la coleccin es el que t decidas, al Visual Basic le da igual que se llame cColegas o LosColegas o el nombre que se te venga a la cabeza... siempre y cuando sea un nombre "vlido". Para seguir con la clase definida en la entrega 39, vamos a llamarla cColegas y contendr elementos del tipo cColega. Esta coleccin tendr los mtodos habituales en todas las colecciones, recordmoslos: Add, para aadir nuevos elementos. Remove, para eliminar un elemento en cuestin. Count, para saber cuantos elementos contiene. Item, para acceder a dicho elemento. Adems se podr recorrer la coleccin usando For Each, para ello tendremos que recurrir a un serie de asignaciones algo extraas, pero... que al fin y al cabo nos permita darle esa funcionalidad a nuestra clase. Tambin aadiremos otros mtodos, tales como: Clone, para poder hacer copias de la coleccin, pero no una copia por referencia, sino una copia totalmente independiente de la clase/coleccin, tal como hicimos con la clase cColega. Exists, que devolver un valor Verdadero o Falso, segn un elemento est o no en la coleccin. Clear, que eliminar todos los elementos de la coleccin y la dejar preparada para usarla desde cero. Adems, vamos a aadir un mtodo llamado Equals, que permitir comprobar si la un objeto del tipo de la coleccin es igual a otro objeto de ese mismo tipo... esto es til para poder "comparar" dos objetos. Este mismo mtodo se lo aadiremos a la clase cColega.

510

Dicho todo esto y antes de ver el cdigo de la coleccin personalizada, vamos a crear el mtodo Equals de la clase cColega (la cuestin es siempre hacerse de rogar... este Guille!)

Equals, un mtodo que comprueba si dos objetos son "exactamente" iguales.


Nota: Aqu vamos a ver slo lo que habra que aadir a la clase cColega, el cdigo anterior sigue siendo vlido y es el mismo que el que vimos en la entrega 37, con el aadido del mtodo Clone de la entrega 39. Para hacer las cosas bien, vamos a empezar por crear un nuevo proyecto, al cual le aadiremos una Referencia que nos ser til, sino obligatoria, para cuando vayamos a crear la coleccin. Abre el Visual Basic, crear un proyecto de tipo EXE, (automticamente se aadir un formulario llamado Form1), aade la clase cColega, (te recomendara que hicieras una copia de la clase anterior), y del men Proyecto, selecciona Referencias... del cuadro de dilogo que te muestra, busca el elemento "OLE Automation" y pulsa en el checkBox para seleccionarlo, esta referencia yo suelo aadirla en todos los proyectos. En la siguiente imagen puedes ver el cuadro de dilogo y la referencia que tenemos que seleccionar (recuerda que el idioma de mi Visual Basic est en ingls, as que, seguramente lo que tu veas sea diferente... por el idioma, ya que el resultado es el mismo).

Aadir OLE Automation en Referencias del proyecto

Te recuerdo que esto que acabamos de hacer no tiene nada que ver con el mtodo Equals, sino para que nuestra clase-coleccin se pueda usar con bucles For Each. Ahora s! Veamos el cdigo del mtodo Equals, (realmente una funcin que devolver True o False, segn los elementos comparados sea o no iguales).

511

Primero veremos el cdigo y despus te lo explico para que no te quede duda... aunque es muy simple, como podrs comprobar. Public Function Equals(ByVal unColega As cColega) As Boolean ' Comprueba si el objeto pasado en el parmetro es igual a este objeto Dim iguales As Boolean ' ' Si el tipo pasado no es del tipo cColega, no son iguales If (TypeOf unColega Is cColega) Then ' Comprobar si cada una de las propiedades son iguales, ' al estar anidadas, ' slo sern iguales si llega a la ltima comparacin. With unColega If .email = Me.email Then If .FechaNacimiento = Me.FechaNacimiento Then If .Nombre = Me.Nombre Then iguales = True End If End If End If End With Else ' Esta asignacin no es necesaria, ' ya que el valor por defecto de la variable iguales es False iguales = False End If Equals = iguales End Function Veamos qu es lo que hace esta funcin. Como esta funcin comparar el objeto actual con otro del mismo tipo, (en este caso del tipo cColega), el parmetro pasado debe ser del tipo cColega. Fjate que he usado ByVal, pero lo mismo da si se usa ByRef, ya que el Visual Basic no permitir que el parmetro sea de otro tipo... si lo intentas, recibirs un error del tipo TypeMismatch si el parmetro es un objeto, pero no del tipo cColega, o bien un error indicndote que el parmetro debe ser un objeto. An as... es decir, sabiendo que a esa funcin slo llegarn objetos del tipo cColega, he aadido una comparacin que comprueba que realmente el objeto es del tipo cColega, esto slo es para que sepas cmo comprobaras que el objeto sea de ese tipo... ya que es til si nos decidiramos a cambiar el tipo del parmetro por otro ms genrico, ya sea Object o Variant, (preferiblemente Variant, si lo quieres hacer "sper" genrico); de esta

512

forma, el Visual Basic no dara ningn error y ser el cdigo de la funcin el que se encargue de comprobar si son o no iguales. Esta sera la forma de declarar la funcin, ya que el cdigo contenido en ella seguira siendo el mismo que hemos visto hace unas lneas: Public Function Equals(ByVal unColega As Variant) As Boolean Es tu decisin utilizar una u otra forma, todo depende de cmo quieras que el Visual Basic acte e incluso cmo de cuidadoso debe ser el programador que utilice tu clase... si es el programador el que debe tener en cuenta si el objeto pasado a la funcin es del tipo correcto o no, utiliza la primera versin, pero si quieres "curarte en salud" y que no se produzca un error porque ese programador no ha sido lo suficientemente cuidados, utiliza la segunda. (Guille, creo que si le das tu opinin personal... pues...) Vale, cual te recomiendo que uses? Yo me inclinara por la versin "ms estricta", ya que se supone que ese mtodo sirve para comprobar si dos objetos del tipo cColega son iguales, por tanto el que utilice nuestra clase ser el que deba tener en cuenta que as sea... Pero por otro lado... (se nota que el Guille es Gminis y eso de la doble personalidad...), creo que no hay que dar por hecho que el programador ser cuidadoso, por tanto, en los ejemplos, usaremos la segunda versin, la que tiene el parmetro del tipo Variant. De esta forma, podremos hacer algo como esto: (se supone que tenemos una variable a nivel de mdulo llamada mColega, que es del tipo cColega y que se ha instanciado correctamente) ' comparar un objeto del tipo cColega con otro de otro tipo diferente Dim otro As Variant ' otro = "Pepe" ' If mColega.Equals(otro) Then MsgBox "Los dos objetos son iguales" Else MsgBox "Los dos objetos son diferentes" End If Tambin podramos hacer esto, y funcionara correctamente: ' comparar un objeto del tipo cColega con otro de otro tipo diferente Dim otro As Variant ' Set otro = mColega

513

' If mColega.Equals(otro) Then MsgBox "Los dos objetos son iguales" Else MsgBox "Los dos objetos son diferentes" End If En el primer caso nos dira que son diferentes y en el segundo que son iguales. Nota: En el segundo ejemplo, en el que se asigna a la variable otro el objeto del tipo cColega, tambin se podra usar la primera versin de la funcin Equals. Bien, ya que sabemos cmo comprobar si dos objetos son iguales... (Eh! Guille! que dijiste que ibas a explicar cmo funcionaba esto... y te has enrollao tanto con lo de los tipos... que...) ...esto... pues s... veamos cmo funciona todo el cdigo... Ya ha quedado claro para que usamos el TypeOf: para comprobar si el objeto pasado en el parmetro es del tipo cColega, en caso de que as sea, se comprueba cada una de las propiedades del objeto unColega con el contenido en esta clase, (para ello usamos Me). Como puedes comprobar, he anidado cada comparacin dentro de otra, de forma que slo se comprobar la siguiente si la anterior ha dado como resultado un valor verdadero. En caso de que alguna de esas comparaciones "falle", es decir, la misma propiedad de los dos objetos no sean iguales, el contenido de la variable iguales seguir teniendo el valor que Visual Basic le asigna por defecto, en el caso de las variables de tipo Boolean, ser False. Por tanto la funcin devolver un valor falso... cosa que se hace en la ltima lnea de la funcin al devolver el valor contenido en la variable iguales. Slo decirte, que la parte Else de la comparacin que comprueba si unColega es del tipo cColega, no es necesaria, ya que le estamos asignando el valor que ya tiene por defecto. Por tanto, el cdigo completo de la funcin Equals es este: Public Function Equals(ByVal unColega As Variant) As Boolean ' Comprueba si el objeto pasado en el parmetro es igual a este objeto Dim iguales As Boolean ' ' Si el tipo pasado no es del tipo cColega, no son iguales If (TypeOf unColega Is cColega) Then ' Comprobar si cada una de las propiedades son iguales, ' al estar anidadas, ' slo sern iguales si llega a la ltima comparacin. With unColega If .email = Me.email Then If .FechaNacimiento = Me.FechaNacimiento Then If .Nombre = Me.Nombre Then

514

iguales = True End If End If End If End With End If Equals = iguales End Function Ahora s... por fin! podremos ver lo que realmente tenamos que ver...

Crear clases personalizadas


Vamos a empezar la clase-coleccin desde el principio, para que no te pierdas ningn detalle. Para empezar, aade una nueva clase al proyecto, en la ventana de propiedades, cmbiale el nombre para que sea cColegas. Muestra la ventana del cdigo y aade la siguiente declaracin: Option Explicit Private mCol As Collection El Option Explicit se supone que ya estara... si es que sigues mis consejos... y si no es as... yo de ti me lo pensara "forastero" (musiquilla de spaghetti-western) A continuacin declaramos un objeto del tipo Collection que ser el que se encargue de contener los objetos de nuestra coleccin. Con esta lnea, simplemente declaramos la variable, pero no instanciamos (o creamos) el objeto, eso lo haremos en el evento Class_Initialize, el cual se ejecutar cuando se cree un nuevo objeto de nuestra coleccin. Por otro lado, cuando un objeto se destruye, se ejecuta el evento Class_Terminate, por tanto ser en ese procedimiento donde destruiremos nuestra coleccin. Veamos el cdigo de esos dos procedimientos: Private Sub Class_Initialize() Set mCol = New Collection End Sub Private Sub Class_Terminate() Set mCol = Nothing End Sub Es recomendable que siempre crees los nuevos objetos de forma separada de la declaracin, ya que si declaras la variable de esta otra forma:

515

Private mCol As New Collection Consigues lo mismo, pero tambin aades "sobrecarga" o trabajo extra al Visual Basic, ya que cada vez que se utilice la variable mCol, el VB tendr que comprobar si ya existe una instancia en memoria y si no es as, tiene que crearla... Para que lo comprendas mejor, si tenemos el siguiente cdigo: i = mCol.Count el Visual Basic realmente hara lo siguiente: If mCol Is Nothing Then Set mCol = New Collection End If i = mCol.Count Es decir, comprueba si ya est creada la coleccin y si no es as, crea una nueva instancia, con lo cual, cada vez que se vaya a usar el objeto, Visual Basic tiene que comprobar si ya est creado el objeto en memoria! Creo que esto ya te lo he dicho antes, si no es as... apntatelo y que no se te olvide... Una vez aclarado estoy ya que tenemos el cdigo que declara el objeto Collection que usar nuestra clase-coleccin, empecemos por los mtodos que suelen incluirse en todas las colecciones.

Count, nos indicar cuantos elementos hay en la coleccin, la codificacin de este


mtodo es bien simple, ya que simplemente llamamos al mtodo con el mismo nombre que incorpora la coleccin. Este mtodo (realmente es una funcin), devolver cero si no hay ningn elemento en la coleccin o la cantidad de elementos que tengamos almacenados. Veamos el cdigo del mtodo Count: Public Function Count() As Long Count = mCol.Count End Function

Remove, eliminar un elemento de la coleccin.


El cdigo tambin es bastante simple, ya que utilizaremos el mtodo Remove del objeto mCol: Public Sub Remove(ByVal Index As Variant) mCol.Remove Index End Sub

516

El parmetro lo declaramos con el tipo Variant para que nos permita usar las dos formas que las colecciones de Visual Basic permite: 1- Utilizar un ndice numrico que ser la posicin del objeto que queremos borrar, el primer elemento ser el 1. 2- Utilizar la clave que hemos usado para el objeto que queremos eliminar. En el caso de que el elemento que queremos eliminar no est en la coleccin, se producir un error, por tanto podramos aadir deteccin de errores, para evitar que eso ocurra. Pero, si no se produce un error, el usuario de nuestra clase no sabr que ese elemento no estaba... y por tanto no "prestara" atencin y podra creer que est trabajando de forma correcta, por tanto vamos a dejar el cdigo as, tal como est. Si quieres crear una versin del mtodo Remove que tenga en cuenta lo que te acabo de comentar, tendrs que declarar Remove como una funcin y si se produce un error devolver un valor Falso y en caso de que se borre correctamente el elemento indicado, devolver un valor Verdadero, de forma que se pueda usar de la siguiente forma: ' oColegas es una variable del tipo cColegas If oColegas.Remove(3) Then Text1 = "Se ha borrado correctamente" Else Text1 = "No se ha borrado el elemento indicado" End If La forma de declarar esta versin de Remove no te lo muestro, para que lo hagas como "ejercicio".

Add, para aadir nuevos elementos a la coleccin.


El mtodo para aadir nuevos elementos a la coleccin lo podemos escribir de varias formas: 1- Tal como se hace con las colecciones de Visual Basic: mCol.Add Item, Key, before, after 2- Aadiendo un objeto del tipo que la coleccin contendr y opcionalmente indicando la clave: mCol.Add unColega mCol.Add unColega, Key Aunque esta forma de hacerlo sera igual que el del punto 1, ya que en esa forma de hacerlo, el nico parmetro obligatorio es el primero y los dems opcionales. 3- Como se hace en algunas de las colecciones de VB, en las que se indica algunos de los valores de las propiedades y que devuelva un objeto del tipo cColega. Dim oColega As cColega

517

Set oColega = mColegas.Add("Guille", "mensaje@elguille.info") El cual tambin permitira aadir elementos a la coleccin sin necesidad de "recibir" el objeto: mColegas.Add "otro Guille", "guille@mvps.org" 4- Una mezcla de los otros: -Si el primer parmetro es un objeto del tipo cColega funcionar como lo indicado en el punto 2 -Si el primer parmetro NO es un objeto del tipo cColega, supondremos que es el nombre. De esta forma podramos aadir objetos a la coleccin de cualquiera de estas formas: Set oColega = New cColega oColega.Nombre = "el Guille" oColega.email = "mensaje@elguille.info" oColega.FechaNacimiento = "07/06/1957" ' mColegas.Add oColega mColegas.Add oColega, "mensaje@elguille.info" mColegas.Add oColega, "mensaje@elguille.info", "07/06/1957" mColegas.Add "el Guille", "mensaje@elguille.info" mColegas.Add "el Guille", "mensaje@elguille.info", "07/06/1957" Set oColega = mColegas.Add("el Guille") Set oColega = mColegas.Add("el Guille", "mensaje@elguille.info") Set oColega = mColegas.Add("el Guille", "mensaje@elguille.info", "07/06/1957") Si despus de estos ejemplos no te sabes mi direccin de correo... en fin. Es decir, podemos crear nuevos objetos de formas muy variadas... Pero lo interesante es saber cmo se hace esto? Y la verdad es que no se si sera conveniente mostrarte el cdigo... (venga Guille, no te hagas de rogar) No es por hacerme de rogar ni aparentar "suspense", es que puede ser que te les... as que, lo mejor es mostrarlos todos, para que vayas comprendiendo mejor las opciones. (y as de camino la entrega es ms larga... no sabes n!) 1- Al estilo del objeto Collection de Visual Basic: Public Sub Add(ByVal Item As cColega, _ Optional ByVal key As String, _

518

Optional ByVal before As Variant, _ Optional ByVal after As Variant) mCol.Add Item, key, before, after End Sub Aqu lo que hacemos es recibir cuatro parmetros, los tres ltimos opcionales. El primero debe ser del tipo cColega, ya que esta coleccin slo admitir objetos de ese tipo. 2- Este es como el anterior, pero sin los dos ltimos parmetros, aunque el segundo ser opcional. 3- En esta forma de hacerlo, los parmetros sern equivalentes a las propiedades del objeto, por tanto se usarn para crear un nuevo objeto del tipo cColega y ese nuevo objeto se aadir a la coleccin y se devolver por la funcin. Public Function Add(ByVal elNombre As String, _ ByVal elEmail As String, _ Optional ByVal laFecha As String) As cColega Dim tColega As cColega Set tColega = New cColega ' With tColega .email = elEmail .FechaNacimiento = laFecha .Nombre = elNombre End With ' usamos el email como clave, para asegurarnos de que sea nico mCol.Add tColega, elEmail ' Devolver el objeto recin creado Set Add = tColega End Function 4- Por ltimo, la versin "multi-uso", que se podr usar como cualquiera de las dos anteriores: Public Function Add(ByVal uno As Variant, _ Optional ByVal elEmail As String, _ Optional ByVal laFecha As String) As cColega Dim tColega As cColega ' ' si el tipo del primer parmetro es un objeto del tipo cColega

519

If TypeOf uno Is cColega Then Set tColega = uno ' si se indican los otros parmetros... If Len(elEmail) > 0 Then tColega.email = elEmail End If If Len(laFecha) > 0 Then tColega.FechaNacimiento = laFecha End If Else ' sino, suponemos que el primer parmetro es el nombre Set tColega = New cColega ' With tColega .email = elEmail .FechaNacimiento = laFecha .Nombre = uno End With End If ' usamos el email como clave, para asegurarnos de que sea nico mCol.Add tColega, tColega.email ' ' Devolver el objeto recin creado Set Add = tColega End Function Para conseguir nuestro objetivo, el primer parmetro ser de tipo Variant, con idea de que permita "cualquier" tipo de dato en ese parmetro. A continuacin comprobamos si dicho parmetro es un objeto del tipo cColega, de ser as, se asignar a la variable que hemos creado dentro del procedimiento y opcionalmente se asignarn el resto de los parmetros, siempre que stos se hayan indicado con algo diferente a una cadena vaca. Si el primer parmetro NO es del tipo cColega, vamos a asumir que es el valor que asignaremos a la propiedad Nombre del objeto que queremos crear. En caso de usarla de esta segunda forma, deberamos especificar tambin el email, ya que es esa propiedad la que usamos como clave del objeto, por la sencilla razn de que no tendremos dos colegas con una misma cuenta de correo... Debido a que es la propia clase cColega la que comprueba si lo que se asigna a la propiedad email es una cuenta de correo, si ese segundo parmetro no lo fuera, se producira un error.

Item, nos permitir acceder a un elemento de la coleccin, bien indicando el valor


de la clave o una posicin dentro de la coleccin. Por tanto podramos acceder a un elemento de la coleccin de cualquiera de estas dos formas:

520

mColegas.Item("mensaje@elguille.info").Nombre = "Guille" mColegas.Item(2).Nombre = "Guille" El cdigo ms simple para esta funcin sera el siguiente: Public Function Item(ByVal Index As Variant) As cColega Set Item = mCol(Index) End Function El problema se presentar si el elemento al que queremos acceder no existe en la coleccin. Pero eso podemos solventarlo con algunos de los mtodos que despus aadiremos. Lo que sera interesante es poder usar este mtodo sin tener que indicar la palabra Item, tal como se hace en las colecciones propias de Visual Basic, de forma que esas dos asignaciones pudiramos hacerlas de esta otra forma: mColegas("mensaje@elguille.info").Nombre = "Guille" mColegas(2).Nombre = "Guille" Para conseguirlo, tenemos que indicarle al Visual Basic que el mtodo Item es el mtodo por defecto de la clase. Para ello, tenemos que posicionarnos en la declaracin del procedimiento (aunque no es estrictamente necesario), ahora pulsa en el men Tools>Procedure Attributes... (Herramientas/Atributos de procedimientos), tal como se muestra en esta imagen:

Del cuadro de dilogo que se muestra, selecciona Item de la lista superior, pulsa en el botn Avanzado para que se muestre la otra parte de la ventana y en la lista ID de procedimiento selecciona (Default), tal como se muestra en esta imagen:

521

Para que una propiedad sea la predeterminada Y despus de pulsar Intro para aceptar las selecciones, el mtodo Item ser el predeterminado y por tanto no ser necesario indicarlo para usarlo. Crear un "enumerador" para nuestra coleccin. Para terminar con la coleccin, (no te preocupes que no me he olvidado de que hay ms cosas, me refiero a terminar con los mtodos que se incluyen en las colecciones normales), vamos a crear un mtodo "especial" que permita recorrer los elementos de la coleccin usando For Each. Para conseguir nuestro objetivo, vamos a crear un mtodo que se llame NewEnum y que sea del tipo IUnknown, no voy a entrar en detalles del "por qu", simplemente veremos el cdigo y la forma de "configurar" este procedimiento en el cuadro de dilogo del atributos de procedimientos. Empecemos por el cdigo: Public Function NewEnum() As IUnknown ' Debe ser un miembro oculto y ' el id del procedimiento debe ser -4 ' Set NewEnum = mCol.[_NewEnum] End Function Slo aclararte que los corchetes son necesarios ya que el nombre de la propiedad contiene un carcter no vlido, (empieza por un guin bajo), ste indica que es un miembro oculto... Pero slo con esto no conseguimos nuestro objetivo, tal como se indica en los

522

comentarios, el procedimiento NewEnum debe estar oculto y tener un ID de procedimiento con valor -4, esto lo hacemos mediante el cuadro de dilogo usado para asignar la propiedad predeterminada, tal como podemos ver en la siguiente imagen:

Para crear un enumerador que permita recorrer la coleccin usando For Each Una vez hecho esto, podemos recorrer los elementos de la coleccin de esta forma: ' mostrar el contenido de la coleccin Dim oColega As cColega ' Text1 = "" For Each oColega In mColegas Text1 = Text1 & oColega.Nombre & ", " & _ oColega.email & ", " & _ oColega.FechaNacimiento & vbCrLf Next Aunque tambin podemos hacerlo de la forma clsica: Dim i As Long ' Text1 = ""

523

For i = 1 To mColegas.Count Text1 = Text1 & mColegas(i).Nombre & ", " & _ mColegas(i).email & ", " & _ mColegas(i).FechaNacimiento & vbCrLf Next

Ampliando la funcionalidad de la coleccin.

Bien, con lo visto hasta ahora, tenemos la misma funcionalidad que la mayora de las colecciones de Visual Basic. Vamos a aadir nuevos mtodos.

Clear, eliminar el contenido de la coleccin.


Empezaremos con uno que nos permita eliminar el contenido de la coleccin y as dejarla preparada para aadir nuevos elementos en una coleccin totalmente vaca. Si no tuviramos este mtodo, tendramos que hacerlo de esta forma: Set mColegas = Nothing Set mColegas = New cColegas Y eso es precisamente lo que haremos en el mtodo Clear... es que es la forma ms rpida y "limpia" de eliminar los elementos, pero por supuesto lo que borramos es el contenido de la coleccin privada de nuestra clase-coleccin. Public Sub Clear() Set mCol = Nothing Set mCol = New Collection End Sub

Exists, comprobar si un elemento est incluido en la coleccin.


Para comprobar si un elemento est en la coleccin podemos hacerlo de dos formas, una sera recorriendo cada uno de los elementos de la coleccin y comprobar si el indicado est contenido... pero hay otra forma ms fcil, y puede que ms rpida, que es aprovecharse de que se produce un error cuando se quiere acceder a un elemento y dicho elemento no existe... veamos el cdigo de esta ltima forma: Public Function Exists(ByVal Index As Variant) As Boolean Dim tColega As cColega ' On Error Resume Next ' Set tColega = mCol(Index) ' si se produce un error es que no existe ese elemento

524

If Err.Number <> 0 Then Exists = False Else Exists = True End If End Function Nota: A este mtodo tambin podramos llamarlo Contains, (que es como suele llamarse en las colecciones de la nueva versin .NET de Visual Basic).

Clone, crear una copia "independiente" de la coleccin.

Tal como hicimos con la clase cColega, vamos a crear un mtodo que permita hacer una copia independiente de la coleccin. Te recuerdo que cuando asignamos un objeto a otro, lo nico que conseguimos es crear una nueva referencia que apunta al mismo objeto que ya est creado en la memoria, es decir slo existe un objeto en la memoria. Pero mediante este mtodo creamos otra copia en la memoria, de forma que los cambios realizados en ella no afectarn al original. El cdigo es bien simple, ya que vamos a aprovecharnos de que la clase cColega ya tiene un mtodo que permite crear copias de ese objeto. Veamos el cdigo: Public Function Clone() As cColegas ' Hacer una copia de esta coleccin ' Dim tColega As cColega Dim tColegas As cColegas ' Set tColegas = New cColegas ' ' Aadir a la nueva coleccin una copia de cada elemento For Each tColega In mCol tColegas.Add tColega.Clone() Next ' Set Clone = tColegas End Function Lo que hacemos es crear una nueva coleccin del mismo tipo de la clase, recorremos cada uno de los elementos contenidos y aadimos una copia, con idea de que se cumpla nuestro objetivo, tener copias independientes.

525

Y para terminar (ya era hora!), nos queda por ver cmo sera el cdigo para el mtodo para saber si dos colecciones son iguales:

Equals, saber si dos colecciones son iguales.


El cdigo de este mtodo es algo diferente al de la clase normal, entre otras cosas porque no slo tenemos que comprobar si las propiedades de cada uno de los elementos son exactamente iguales, (aunque de ese detalle se encarga el mtodo Equals de cada uno de los objetos contenidos en la coleccin), sino que tambin tenemos que tener en cuenta otras cosas... aunque al fin y al cabo todo es para poder saber si las dos colecciones son iguales... Veamos el cdigo: Public Function Equals(ByVal compararCon As Variant) As Boolean Dim iguales As Boolean Dim oColegas As cColegas Dim oColega As cColega ' ' Slo sern iguales si es un objeto del tipo cColegas If TypeOf compararCon Is cColegas Then Set oColegas = compararCon ' slo sern iguales si tienen los mismos elementos If oColegas.Count = mCol.Count Then ' asumimos que sern iguales iguales = True For Each oColega In mCol ' si no existe o no son iguales... If oColegas.Exists(oColega.email) = False Then iguales = False Exit For ElseIf oColega.Equals(oColegas(oColega.email)) = False Then iguales = False Exit For End If Next End If End If Equals = iguales End Function Lo primero que hacemos es comprobar que el objeto es del tipo adecuado, si es as, asignamos ese objeto a una variable del tipo cColegas, (esto simplemente es para poder usar las propiedades y mtodos, ya que si usamos la variable pasada como parmetro, al

526

ser del tipo Variant, no nos mostrara los miembros). A continuacin comprobamos que el nmero de elementos de las dos colecciones sean iguales, en caso de que no lo sean, el valor de la variable iguales no cambiar, con lo cual la funcin devolver un valor False. A partir de este punto asignamos un valor verdadero a esa variable, esto es as porque las siguientes comparaciones buscan valores que diferencien a ambas colecciones... Para ello, hacemos un bucle que recorra todos los elementos, comprobamos si cada uno de los elementos existe en la otra coleccin, si no es as, asignamos un valor falso y salimos del bucle. Si el email es igual en los dos objetos, comprobamos si realmente son iguales, en caso de que no lo sean, asignamos un valor falso y salimos del bucle. Puede que pienses que esto se podra haber simplificado usando slo esta comparacin: If oColega.Equals(oColegas(oColega.email)) = False Then iguales = False Exit For End If Pero esto dara error si en la coleccin pasada por parmetro no contiene el objeto que se comprueba. Por qu? Como hemos visto, si hacemos esto: oColegas(laClave), que es lo mismo que si hacemos esto otro: oColegas.Item(laClave), y dicha clave no est contenida en la coleccin, se producir un error, ya que en el mtodo Item de la coleccin no se hace ningn tipo de deteccin de errores, por tanto si el elemento al que se quiere acceder no existe, se produce un error. (Guille eso lo dijiste antes..., ya lo s, pero as seguro que queda ms claro...) Por esa razn se hace primero la comprobacin de que exista el objeto en la coleccin. Y hasta aqu hemos llegado en esta entrega que tanto se ha hecho esperar... aunque confo que con lo que me he enrollado, no te quejes y espero que tampoco te "empaches" demasiado... je, je. Posiblemente, en la prxima entrega veremos ms cosas referentes a las clases... pero no nos precipitemos que si despus cambio de idea... lo mismo me regaas... Nos vemos Guillermo P.S. Aqu tienes el cdigo completo de las clases y un proyecto de prueba: basico42_cod.zip 6.01 KB P.S.2: Aqu te explico "someramente" lo que es eso del For Each, ya que segn parece slo lo dije "de pasada" en la entrega 38.

For Each, otra forma de hacer bucles.


Este tipo de bucles, a diferencia del clsico bucle For al que hay que indicarle una variable numrica, se utiliza con una variable de un tipo de objeto que debe estar contenido en una coleccin, de forma que se pueda recorrer cada uno de los elementos de dicha coleccin que sean del tipo usado junto a For Each. La forma de usarla sera:

527

For Each elemento In laColeccin ' lo que haya que hacer con el elemento Next ...

Curso Bsico de Programacin en Visual Basic

Ha pasado algo ms de un mes desde la entrega anterior, pero al menos estoy en lo que me propuse: publicar al menos una entrega cada mes, para que no te aburras y as tambin evitar que me eches la bronca por olvidarme de actualizar el Curso Bsico de Visual Basic... s, que algunos me regaan y todo... snif! En esta entrega vamos a continuar con las clases, aunque esta entrega tratar algo que puede que te vuelva un poco ms "turuleta" de lo que ya puedas estar... e incluso puede que genere ms trfico de correo a mi cuenta del que a mi me gustara, ya que puede ser que no te enteres de nada... no, no te estoy llamando ignorante... ni se me ocurrira! pero es que de lo que aqu vamos a tratar es algo que puede que al principio te confunda y acabes por pensar que... no me entero de nada! abandono! y no es eso lo que me gustara, no quiero que abandones... slo piensa que si el Guille se ha enterado... cualquiera se puede enterar! y no lo digo por decir, que a pesar de lo que muchos se creen soy ms torpe que... en fin... no se con que "bicho torpe" compararme, pero seguro que alguno existir... je, je. En serio... si no te enteras, no desesperes... que tarde o temprano acabars por enterarte... te lo aseguro!

Un repaso para ir entrando en calor...


Vamos a repasar lo que hasta ahora hemos visto, no te asustes, no vamos a repasar el contenido de las 42 entregas anteriores, slo lo relacionado con las clases y la Programacin Orientada a Objetos (o lo que ms se le parezca, ya que el Visual Basic no es realmente un lenguaje orientado a objetos, entre otras cosas porque no soporta la herencia, cosa que si hace su primo Visual Basic .NET) Visual Basic no es un lenguaje orientado a objetos, eso ya lo sabemos, (y seguro que algunos no los habrn recordado en ms de una ocasin), pero hay que entender un poco de ese tema para sacarle el mximo rendimiento a esto de las clases en Visual Basic, as que, de forma resumida, vamos a ver que caractersticas de la OOP (Object Oriented Programming, es que prefiero usar las siglas esas en lugar de POO, ya que parece que estoy hablando de un ro de Italia que suele salir en los crucigramas...) que nuestro querido Visual Basic soporta: Encapsulacin: Esta caracterstica de la OOP es la facultad de unificar el cdigo y los datos que la clase u

528

objeto contiene, as como ocultar el cdigo que maneja dicha informacin. La encapsulacin nos ayuda a olvidarnos de cual es la implementacin realizada en los procedimientos (mtodos y propiedades) de una clase, para que slo nos preocupemos de cmo usarlos. Polimorfismo: Polimorfismo viene del griego y significara algo as como: muchas-formas. Todos los mtodos implementados en las clases deben tener una forma de comportarse, as como ser consistente con la informacin que debe tratar, ya que, como te he comentado antes, la encapsulacin es la caracterstica que permite ocultar cmo estn implementados (o codificados) los mtodos y propiedades de las clases y el polimorfismo sera el contrato firmado para que esos procedimientos se utilicen de forma adecuada. Bueno, no es realmente eso, pero a ver si as lo entiendes, ya que, creo que esto no se entiende hasta que se "demuestra", a ver si con la siguiente "definicin" queda algo ms claro: Se dice que una clase es polimrfica cuando podemos usar sus mtodos y propiedades sin importarnos qu objeto los implementa. Ya que Polimorfismo significa eso: mltiples formas, es decir, si un objeto implementa el mtodo Show, podemos usar ese mtodo siempre de la misma forma sin importarnos el objeto que estemos usando, si esto no es as, no estaremos utilizando de forma correcta el Polimorfismo. El Polimorfismo en Visual Basic se puede usar de dos formas diferentes, segn se compruebe si el procedimiento (o miembro de la clase) pertenece al objeto que lo utiliza, en tiempo de diseo o en tiempo de ejecucin, esto tambin es conocido como compilacin temprana (early binding) o compilacin tarda (late binding) respectivamente. Adems de estas dos caractersticas, aclaremos los siguientes puntos: Mtodos: Los mtodos son las acciones que una clase puede ejecutar, normalmente son procedimientos de tipo Sub o Function. Para que sepamos cuando un procedimiento es un mtodo, debemos pensar si dicho procedimiento realizar una accin sobre los datos que la clase maneja. Esta aclaracin es porque en algunos sitios te dirn que las funciones pueden ser propiedades, pero una funcin siempre es un mtodo, aunque en ocasiones estn implementadas de forma que puedan hacernos pensar lo contrario. Propiedades: Las propiedades de una clase (u objeto) son las caractersticas de esa clase. Las propiedades podran ser tambin las caractersticas de los datos que la clase maneja (o manipula). como deberas saber, las propiedades las podemos declarar de dos formas: creando un procedimiento de tipo Property o bien declarando una variable pblica. No te queda claro? Por ejemplo, si nuestra clase "maneja" a nuestros colegas, (realmente maneja o trata la informacin de nuestros colegas, no a nuestros colegas... aunque algunas veces sera interesante que pudisemos manejar a un colega y "encapsularlo" dentro de una clase, para que nos deje tranquilos... je, je), las propiedades seran los datos o caractersticas que tenemos de nuestros colegas, por ejemplo: el nombre, el correo electrnico, la edad, si est loco... por la msica, por ejemplo, etctera... Mientras que los mtodos podran ser las acciones que queremos realizar sobre esos datos: que los muestre, que los borre o que los cambie, incluso podramos hacer que esa informacin se almacenara en un archivo o en una base de datos, incluso enviarles de forma automtica un mensaje cuando sea su cumpleaos, etc.

529

Ahora empecemos con los quebraderos de cabeza. Cmo podemos usar la caracterstica de la Encapsulacin? Aqu no tenemos que hacer nada especial, el mero hecho de crear un mtodo o una propiedad en una clase ya implica que estamos usando la caracterstica de la encapsulacin. Cmo podemos usar la caracterstica del Polimorfismo? Como te coment antes, hay dos formas de implementar (usar) el polimorfismo, segn se haga en tiempo de diseo o en tiempo de ejecucin. Empecemos viendo cmo hacerlo de la forma "menos recomendada", aunque algunas veces ser la nica, pero te recomiendo que la evites siempre que puedas. Si declaras una variable de tipo Object, (e incluso uno de tipo Variant), podrs asignarle cualquier objeto a dicha variable y acceder a los miembros del objeto, veamos un ejemplo: Si tenemos un objeto del tipo cColega (el cdigo lo vimos en la entrega 37 y se ampli en la entrega 39), podramos tener algo como esto: Private Sub cmdLate_Click() ' declaramos las variables Dim unColega As cColega ' tambin se puede declarar como As Variant Dim o As Object ' ' creamos el objeto de tipo colega Set unColega = New cColega ' y le aadimos algunos datos unColega.Nombre = "Guille" unColega.email = "mensaje@elguille.info" ' ' asignamos el colega (unColega) al objeto (o) Set o = unColega ' mostramos el nombre del colega MsgBox "El colega es: " & o.Nombre ' End Sub Esto funcionara como se espera: mostrara el nombre correcto. Esto es Polimorfismo, aunque se use compilacin en tiempo tardo (late-binding), es decir, el runtime del Visual Basic no sabe nada de la propiedad Nombre del objeto referenciado por la variable o, pero intenta acceder a dicha propiedad y como "sabemos" que la tiene, se muestra el nombre que hemos asignado. En caso de que asignemos a esa variable un objeto que no tenga la propiedad Nombre,

530

el cdigo se compilar correctamente, y no ser hasta que est ejecutndose cuando el VB se de cuenta de que el objeto referenciado por la variable o no tiene una propiedad llamada Nombre, con lo que se producir un error en tiempo de ejecucin. Por ejemplo, si en lugar de acceder a la propiedad Nombre (que si la tiene), queremos mostrar los apellidos mediante este cdigo: MsgBox "El colega es: " & o.Apellidos Como resulta que el objeto referenciado por la variable o (que es del tipo cColega), no tiene una propiedad llamada Apellidos, el runtime de Visual Basic nos mostrar este mensaje cuando llegue a esa lnea de cdigo:

Error en tiempo de ejecucin al acceder a una propiedad que no existe Pero hasta que no llegue a esa lnea no sabremos que haba algo mal. Por supuesto, podemos usar deteccin de errores para que ese mensaje no se muestre, pero eso slo har que no se muestre, no que no se produzca, que es al fin y al cabo lo que debemos evitar. Para poder solventar este tipo de problemas, podemos hacer dos cosas: 1- La que te he dicho antes, utilizar deteccin de errores. 2- Usar un objeto del tipo especfico, as siempre sabremos si dicho objeto tiene la propiedad a la que queremos acceder y si no la tiene, el VB nos avisar en tiempo de diseo, antes de que se compile el programa. Pero, (recuerdas que siempre hay un pero?), qu hacemos si queremos crear un procedimiento que reciba un parmetro de tipo genrico y lo mismo nos sirva para un objeto del tipo cColega como otro de otro tipo distinto?, por ejemplo, si hemos creado una clase llamada cColega2 y que tambin tiene la propiedad Nombre y otras de las incluidas en cColega, adems de algunas nuevas. Pues nada... ya que esto en VB no se puede hacer de forma "limpia"... o si lo prefieres de forma natural o coherente... Lo nico que podramos hacer es usar como parmetro de ese procedimiento una variable de tipo Object, que acepta cualquier tipo de objeto, (incluso formularios y controles), y hacer una serie de comprobaciones, para saber si dicho objeto es de un tipo u otro y usar una variable adecuada a dicho tipo. Veamos el cdigo de la clase cColega2 (versin simplificada) y el de ese procedimiento: La clase cColega2: '-----------------------------------------------------------------------------

531

' cColega2 (21/Dic/02) ' Clase ampliada del tipo Colega ' ' Guillermo 'guille' Som, 2002 '----------------------------------------------------------------------------Option Explicit Public Nombre As String Public Apellidos As String ' Public email As String Public FechaNacimiento As Date

El procedimiento, (que estar, por ejemplo en un formulario): Private Sub mostrar(elObjeto As Object) If TypeOf elObjeto Is cColega Then ' si el objeto es de tipo cColega ' mostrar slo el nombre MsgBox "El colega es: " & elObjeto.Nombre ElseIf TypeOf elObjeto Is cColega2 Then ' si el objeto es de tipo cColega2 ' mostrar el nombre y apellidos MsgBox "El colega es: " & elObjeto.Nombre & " " & elObjeto.Apllidos End If End Sub Lo que aqu hacemos es: recibir un objeto de cualquier tipo como parmetro, despus comprobamos si es del tipo cColega y mostramos el nombre, si es del tipo cColega2, mostramos el nombre y los apellidos. Pero este cdigo, para mi gusto no sera la mejor solucin, todava podemos equivocarnos al teclear cualquiera de las propiedades y no nos daremos cuenta hasta que estemos ejecutando el programa. Si eres una persona observadora, te habrs fijado que en lugar de escribir Apellidos, he escrito Apllidos y esa no es una propiedad de la clase cColega2.

532

Para solucionar ese inconveniente podemos hacer algo como esto: Private Sub mostrar2(elObjeto As Object) If TypeOf elObjeto Is cColega Then Dim colega1 As cColega Set colega1 = elObjeto ' si el objeto es de tipo cColega ' mostrar slo el nombre MsgBox "El colega es: " & colega1.Nombre ElseIf TypeOf elObjeto Is cColega2 Then Dim colega2 As cColega2 Set colega2 = elObjeto ' si el objeto es de tipo cColega2 ' mostrar el nombre y apellidos MsgBox "El colega es: " & colega2.Nombre & " " & colega2.Apellidos End If End Sub Con esto, aunque tengamos que escribir ms cdigo, evitaremos usar propiedades no existentes, ya que ser el propio IDE de Visual Basic el que nos muestre las propiedades y mtodos de cada objeto, tal como podemos comprobar en las siguientes imgenes:

Los miembros de cColega

533

Los miembros de cColega2

Bien, dirs... esto est muy bien, pero... esto es polimorfismo? Pues... si y no... realmente no, aunque podra ser... La verdad es que no. Al menos si entendemos polimorfismo como una de las caractersticas de la programacin orientada a objetos. Pero ni el polimorfismo nos solucionara el problema suscitado por el uso de dos clases (u objetos) diferentes en el mtodo mostrar o mostrar2.

Otra cosa sera que tuvisemos un mtodo Mostrar en cada una de las clases y en el procedimiento mostrar del formulario se usara dicho mtodo para que se muestre lo que se tenga que mostrar, en el caso de cColega slo se mostrara el Nombre y en el caso de cColega2 se mostrara el Nombre y Apellidos, veamos cmo declarar esos mtodos en cada una de las clases y cmo se usara: El mtodo Mostrar de la clase cColega: Public Sub Mostrar() MsgBox "El nombre es: " & Nombre End Sub

El mtodo Mostrar de la clase cColega2: Public Sub Mostrar() MsgBox "El nombre es: " & Nombre & " " & Apellidos End Sub

El mtodo mostrar3 del formulario de prueba:

534

Private Sub mostrar3(elObjeto As Object) If TypeOf elObjeto Is cColega Then Dim colega1 As cColega Set colega1 = elObjeto ' llamar al mtodo Mostrar del objeto colega1.Mostrar ElseIf TypeOf elObjeto Is cColega2 Then Dim colega2 As cColega2 Set colega2 = elObjeto ' llamar al mtodo Mostrar del objeto colega2.Mostrar End If End Sub

La llamada al mtodo mostrar3: Private Sub cmdMostrar3_Click() ' declaramos las variables Dim unColega As cColega Dim o As Object ' ' creamos el objeto de tipo colega Set unColega = New cColega ' y le aadimos algunos datos unColega.Nombre = "Guille" unColega.email = "mensaje@elguille.info" ' ' asignamos el colega (unColega) al objeto (o) Set o = unColega ' lo mostramos mostrar3 o ' ' creamos un objeto del tipo cColega2... Dim unColega2 As cColega2 Set unColega2 = New cColega2 ' y le asignamos algunos datos

535

unColega2.Nombre = "Guille" unColega2.Apellidos = "Som Cerezo" unColega2.email = "mensaje@elguille.info" ' lo mostramos mostrar3 unColega2 End Sub

Aqu tenemos que observar que el cdigo de Mostrar de las dos clases es muy parecido, pero cada uno muestra los datos que el que escribi la clase quiera... a nosotros no debe importarnos, lo nico que nos interesa es que llamando al mtodo Mostrar se mostrarn los datos (esto es encapsulacin). Por otro lado, en el mtodo mostrar3 usamos dichos mtodos para que se muestre el mensaje que corresponda (esto podra ser polimorfismo, pero an no lo es). Para que realmente pudiramos aprovecharnos de las caractersticas del polimorfismo, deberamos poder crear, (en el formulario), un mtodo mostrar que nos permitiera llamar al mtodo Mostrar del objeto pasado como parmetro sin preocuparnos de si el objeto es del tipo cColega o cColega2. Esto lo podemos solucionar mediante las interfaces. Una interfaz es una especie de plantilla que podemos aplicar a nuestras clases. En esa plantilla se definen los mtodos y propiedades, as como la forma en que deben implementarlas las clases que quieran firmar un contrato con esa interfaz. Las clases que usen esa interfaz tendrn una definicin de dichas propiedades y mtodos tal y como lo indica la plantilla. Y para que sirve todo eso? Para que, si una clase implementa dicha interfaz, podamos llamar a los miembros declarados en ella de forma genrica, sin necesidad de usar un objeto intermedio o genrico. Por ejemplo, supongamos que tenemos una interfaz llamada IColega, si dicha interfaz (o interface si lo prefieres), tiene un mtodo Mostrar y tanto la clase cColega como cColega2 la implementan, podramos mostrar los datos del colega usando el siguiente mtodo del formulario: Private Sub mostrar4(elObjeto As Object) If TypeOf elObjeto Is IColega Then Dim elColega As IColega Set elColega = elObjeto elColega.Mostrar Else MsgBox "El objeto no es del tipo IColega" End If End Sub

536

Aqu comprobamos si el objeto es del tipo IColega, si es as, se llama al mtodo Mostrar, despus de hacer una asignacin del objeto pasado como parmetro a una variable del tipo IColega, en caso de que no sea de ese tipo, se mostrar un mensaje de aviso. La declaracin de la interfaz (realmente es una clase) IColega sera algo como esto: '----------------------------------------------------------------------------' IColega (21/Dic/02) ' Interfaz para las clases de tipo Colega ' ' Guillermo 'guille' Som, 2002 '----------------------------------------------------------------------------Option Explicit Public Sub Mostrar() ' no es necesario escribir cdigo ' slo es necesario definirlo End Sub Como podemos comprobar, la declaracin de una interfaz no tiene porqu contener cdigo, slo es necesario indicar cmo se deben declarar los miembros que dicha clase contenga. Si tanto la clase cColega como cColega2 han firmado un contrato para utilizar el mtodo Mostrar tal y como lo indica la interfaz IColega, es decir, si esas dos clases "implementan" dicha interfaz, podremos usar el siguiente procedimiento para llamar al procedimiento mostrar4 del formulario: Private Sub cmdMostrar4_Click() ' declaramos las variables Dim unColega As cColega Dim o As Object ' ' creamos el objeto de tipo colega Set unColega = New cColega ' y le aadimos algunos datos unColega.Nombre = "Guille" unColega.email = "mensaje@elguille.info" ' ' asignamos el colega (unColega) al objeto (o)

537

Set o = unColega ' lo mostramos mostrar4 o ' ' creamos un objeto del tipo cColega2... Dim unColega2 As cColega2 Set unColega2 = New cColega2 ' y le asignamos algunos datos unColega2.Nombre = "Guille" unColega2.Apellidos = "Som Cerezo" unColega2.email = "mensaje@elguille.info" ' lo mostramos mostrar4 unColega2 ' ' incluso podramos llamar al mtodo con otro objeto mostrar4 Command1 End Sub En este caso, el cdigo es casi como el del tercer ejemplo (cmdMostrar3), slo que en lugar de llamar a mostrar3, se llama a mostrar4. Tambin hemos aadido una nueva llamada al mtodo mostrar4, pero pasndole como parmetro cualquier otro objeto, en este caso hemos pasado un parmetro del tipo Command, que por supuesto no implementa IColega, con lo cual se mostrar un aviso indicndonos que no implementa esa interfaz. Seguramente la pregunta que te hars es: Puedo usar ese cdigo directamente y se usar el mtodo Mostrar de esas dos clases? La respuesta es: No. A pesar de que tanto la clase cColega como cColega2 tengan un mtodo llamado Mostrar no significa que hayan firmado un contrato con la interfaz IColega. Por tanto, no podramos usar ese cdigo, al menos tal y como estn declaradas esas dos clases. Para poder usar ese cdigo, las dos clases deberan firmar un contrato con IColega. Entonces, cmo firmo ese contrato para las clases cColega y cColega2? Utilizando la instruccin Implements. Cuando usamos la instruccin Implements, la cual hay que usarla en la parte de las declaraciones de la clase que queremos que la implemente, seguida del nombre de la interfaz a implementar, se crea un nuevo objeto en dicha clase, al cual podemos acceder, desde el IDE de VB, si pulsamos en la lista desplegable de la izquierda, tal como hacemos, por ejemplo, cuando queremos acceder al cdigo de un evento de un formulario, tal como podemos ver en la siguiente captura:

538

Para que se pueda mostrar ese nuevo objeto, tendremos que aadir la siguiente lnea a ambas clases: Implements IColega Despus de hacer esto, tendremos el objeto IColega en esa lista desplegable y al seleccionarla, en la derecha nos mostrar los mtodos que dicha interfaz contiene, en este caso slo tendr uno: Mostrar. Y si lo seleccionamos se mostrar el siguiente cdigo: Private Sub IColega_Mostrar() End Sub Dentro tendremos que escribir el cdigo que se usar cuando se acceda a esta clase mediante un objeto del tipo IColega. Pero como lo que queremos es que se use el cdigo que ya hemos escrito, simplemente llamamos al mtodo Mostrar escrito en nuestra clase, con lo cual, el cdigo completo quedara as: Private Sub IColega_Mostrar() Me.Mostrar End Sub Si la clase IColega tuviera ms de un mtodo, tendramos que escribir cdigo en todos los procedimientos que dicha clase contenga, ya que si implementamos una interfaz, debemos implementar todos los mtodos y propiedades de dicha interfaz. Implementar una interfaz es un compromiso, en el que nos comprometemos a hacer las cosas tal y como dicha interfaz requiere. Te voy a mostrar el cdigo completo de la clase cColega2 para que no te quede duda. '----------------------------------------------------------------------------' cColega2 (21/Dic/02)

539

' Clase ampliada del tipo Colega ' ' Guillermo 'guille' Som, 2002 '----------------------------------------------------------------------------Option Explicit Implements IColega Public Nombre As String Public Apellidos As String ' Public email As String Public FechaNacimiento As Date Public Sub Mostrar() MsgBox "El nombre es: " & Nombre & " " & Apellidos End Sub Private Sub IColega_Mostrar() Me.Mostrar End Sub Fjate que el mtodo IColega_Mostrar est declarado como Private, pero eso no debe preocuparte. Si hacemos lo mismo con la clase cColega, podremos usar el cdigo mostrado anteriormente sin ningn tipo de problema. Para finalizar, aclararte que cuando usamos lo siguiente: Dim elColega As IColega Set elColega = elObjeto elColega.Mostrar A la variable elColega, (que es un objeto del tipo IColega), realmente le estamos asignando slo una parte del cdigo de la clase representada por elObjeto, la parte que implementa dicha interfaz. Es decir, si la variable elObjeto fuese del tipo cColega, no estaramos asignando todo el objeto, slo una parte del mismo. Esto podemos comprobarlo si queremos acceder a la propiedad Nombre: s = elColega.Nombre

540

Esto dara error, ya que el objeto elColega, (que como sabemos es del tipo IColega), no tiene una propiedad llamada Nombre. Slo me queda decirte que en Visual Basic todas las clases se pueden usar con la instruccin Implements. Es decir, no tenemos porqu crear una clase especfica para crear una interfaz. Pero te recomiendo que lo hagas as, para que sea ms evidente y sobre todo porque as es como tendrs que hacerlo cuando "migres" a la versin .NET de Visual Basic. Ahora s, hasta aqu hemos llegado en esta ocasin. En la prxima entrega seguiremos viendo ms cosas relacionadas con las clases y, casi con seguridad, un ejemplo ms prctico sobre la ventaja de usar esto de la implementacin de interfaces. Pero eso ser... casi con seguridad, el ao que viene, mientras tanto... espero que pases unas Felices Fiestas... s, aunque no seas creyente... que no hay que ser creyente para, aunque sea una vez al ao, desear cosas buenas a la gente... je, je. Nos vemos Guillermo

Aqu tienes el fichero zip con el cdigo usado en esta entrega: basico43_cod.zip 5.02 KB

Curso Bsico de Programacin en Visual Basic


Algunos de los que me escriben sobre este Curso Bsico de Programacin con Visual Basic, me suelen preguntar cuando se acabar el curso, creo que no se refieren a cuando me hartar yo de escribir, sino a cuando pienso darlo por terminado. La verdad es que a esa pregunta no tengo respuesta, por la sencilla razn de que este curso lo escribo al "vuelo", es decir, no tengo un planteamiento previo del contenido, la verdad es que a algunos le puede extraar, pero... es cierto, no tengo un planteamiento sobre el contenido, por eso en algunas entregas te digo que en la siguiente hablar de un tema y al final acabo hablando de otro, en algunas ocasiones, totalmente distinto al previsto. En esta entrega voy a seguir con el tema que propuse en la entrega anterior, es decir, vamos a seguir hablando del polimorfismo y las interfaces, pero tambin trataremos otro tema que, casi con seguridad te ser de utilidad en algunas aplicaciones: cmo hacer una ventana de configuracin que sea algo "inteligente", es decir, que tengamos un botn Aplicar y si modificamos el contenido de las opciones presentadas, ese botn se habilite, pero si despus de hacer cambios, volvemos a dejar todo como estaba originalmente, ese botn se deshabilite. Para conseguirlo, usaremos una clase en la que estarn los valores de cada una de las opciones que la ventana de configuracin va a manejar, de forma que se use esa clase para comunicarse con el formulario que llame a la ventana de configuracin. Tambin crearemos una clase/interfaz, que nos sirva para implementarla tanto en el formulario de configuracin como en la clase usada para mantener esos datos. Seguramente, cuando veas el cdigo, pensars que no era necesario usar clases ni interfaces, y estars en lo cierto, pero... como el tema que estamos tratando en estas

541

ltimas entregas, es precisamente las clases, el polimorfismo y las interfaces... pues eso... As que, sin ms prembulos, empecemos, (o mejor dicho), sigamos con esta entrega. Antes de empezar a mostrarte el cdigo, voy a explicarte un poco el planteamiento que vamos a hacer, para que te resulte ms fcil seguir el cdigo que te mostrar. Tendremos una clase con una serie de propiedades, que en este caso simplemente son para poder hacer las pruebas y el ejemplo, seguramente no ser de mucha utilidad, pero con ella veremos los conceptos, para que puedas aplicar los tuyos propios, ya que, lo que me gustara es que "siempre" te quedes con la base de lo que te explico, no que uses el cdigo directamente, ya que con ello no llegars a aprender, y de lo que aqu se trata es que aprendas a hacer las cosas para que despus lo apliques como ms te convenga. Como te comentaba en la entrega anterior, en la versin de Visual Basic de la que trata este curso, (la 6.0 y anteriores, realmente hasta la 5, ya que las anteriores no aceptan la implementacin y otras de esas moneras de la OOP), se puede usar cualquier clase como una interfaz a implementar. Esa clase puede contener cdigo, aunque cuando se implementa ese cdigo "desaparece" y slo se queda "la firma" o estructura de la clase, es decir los mtodos y propiedades pblicas que contenga. Tambin te recomend que en lugar de usar una clase para realizar la implementacin, crearas una clase en la que slo estn definidos los miembros de la interfaz, por tanto aqu vamos a seguir las recomendaciones que te di en la entrega anterior, para que no digas que es muy fcil recomendar como hacer las cosas y despus hacerla de una forma diferente. Una cosa que debes saber, no se si ya te lo he dicho, es que cuando declaramos una variable pblica en una clase, esa variable se convierte automticamente en una propiedad. Por tanto, si slo declaramos las variables, en lugar de crear propiedades propiamente dichas, cuando implementemos esa clase, se crearn los correspondientes procedimientos Property Let y Property Get. Te digo esto para que no te extraes cuando te ocurra. De todas formas, en los ejemplos que voy a utilizar en esta entrega, definir los miembros de la interfaz de la forma "correcta", es decir usando Property Let/Get en lugar de declarar las variables, realmente slo se declararn las propiedades de lectura (Get), ya que no es necesario declarar las de escritura (Let), entre otras cosas, porque no se van a usar.

La aplicacin que vamos a usar de ejemplo, tendr un formulario principal y otro que ser el que se utilice para configurar ciertos valores, adems tendremos dos clases, una de ellas ser la interfaz y la otra, la clase propiamente dicha, en la que se almacenarn los datos que usaremos. Seguramente, cuando veas el cdigo y el diseo de los formularios, te parecern iguales o muy parecidos, esto es as, para simplificar, ya que no es plan de hacer una aplicacin demasiada "completa" para ver el "corazn" de lo que aqu te quiero explicar. Por supuesto, tendrs que hacer algo por tu propia cuenta si quieres que el ejemplo sea ms "sofisticado", ya que, como te he comentado al principio, lo que me interesa ensearte es cmo hacerlo, no darte el cdigo completo para que lo copies y pegues... es que si no pones de tu parte, no llegars a aprender... ya que considero que con todo lo que hasta ahora he explicado en este curso bsico, deberas tener la "base" suficiente para que no te resulte difcil investigar por tu cuenta y riesgo, de forma que amples los conocimientos que, se supone, hasta ahora has ido acumulando.

542

Veamos el aspecto del formulario principal, as como el de configuracin. Como te he comentado, el formulario principal tendr una serie de controles, los cuales se usarn para almacenar unos datos y esos datos se podrn modificar con el de configuracin, para que el funcionamiento "aparente" de los dos formularios no sea el mismo, el formulario principal leer y almacenar los datos en un fichero de texto. Pero quiero que pongas en funcionamiento tu imaginacin e imagines que el formulario principal hace ms cosas... as no te parecer que los dos formularios hacen lo mismo. De todas formas, la parte de Leer y Guardar te las dejo a ti como ejercicio, as comprobars si has aprendido algo... Este es el aspecto de los dos formularios que usaremos en este ejemplo:

Fig. 1, el formulario principal

Fig. 2, el formulario de configuracin

543

En el formulario de configuracin, el botn Aplicar se habilitar o deshabilitar segn se hayan o no modificado los datos, cuando se pulse en dicho botn, se asignarn los nuevos valores a una variable basada en una clase. Cuando se pulse en Aceptar, tambin se asignarn esos valores y se ocultar el formulario para que el formulario principal tome nuevamente el control. Cuando se pulse en Cancelar, se descartarn los cambios y se ocultar el formulario para poder volver al formulario principal. Nota (o truco): Si a la propiedad StatUpPosition del formulario de configuracin le asignas el valor 1 - CenterOwner, har que se muestre centrado con respecto a la posicin del formulario principal.

Asignar los botones predeterminados de un formulario para Cancelar y Aceptar Cuando se hace un formulario al estilo del mostrado para la configuracin, el botn Cancelar se suele asociar con la tecla ESC y el de Aceptar con la tecla Intro, de forma que cuando se pulse cualquiera de esas dos teclas se ejecuten las acciones indicadas en esos botones. Esta funcionalidad se consigue asignando unas propiedades que todos los botones (CommandButton) tienen: Para asignar un botn como predeterminado (se ejecuta el evento Click al pulsar Intro), hay que asignar un valor True a la propiedad Default. Por otro lado, para que un botn acte como receptor de la tecla Escape, hay que asignar un valor True a la propiedad Cancel. Despus veremos el cdigo de este formulario, aunque antes hay que saber cmo hacer que se muestre dicho formulario... y esperar a que se elijan las opciones.

Cmo llamar a un formulario desde otro Aunque esto ya lo hemos visto, (creo), vamos a verlo nuevamente, aunque sea de pasada. Para que un segundo formulario se muestre, podemos hacerlo de varias formas, aunque la forma en que dicho formulario se comportar slo puede ser de dos formas distintas: 1- Que se muestre, pero que el formulario que lo ha llamado (o mostrado) siga ejecutndose, es decir no se espera a que el segundo formulario termine. Esto puede ser til cuando necesitamos que ese segundo formulario acte de forma independiente del primero, pero ese no es el comportamiento esperado cuando se muestra un formulario, como en nuestro caso, que se vaya a usar como configuracin. 2- Que se muestre, pero que hasta que no se cierre u oculte, el formulario que lo ha llamado no contine. Este caso es el que nos interesa, adems de que es el que normalmente utilizan los formularios de configuracin, tambin llamados "cuadros de dilogo".

544

Para el primer caso se usara lo siguiente: NombreFormulario.Show o tambin de esta forma: NombreFormulario.Show vbModeless Es decir, mustralo pero en forma no-modal (o no como cuadro de dilogo). Para el segundo caso, habr que indicarle al mtodo Show que se quiere mostrar en modal, es decir como un cuadro de dilogo, para ello habr que usar: NombreFormulario.Show vbModal Los valores de vbModal y vbModeless realmente son constantes de Visual Basic cuyos valores son uno y cero respectivamente. Como te he comentado, la principal diferencia entre estas dos formas de mostrar un formulario, es que en modo no-modal, se mostrara el formulario, pero se continuara en la siguiente lnea, por ejemplo: Form2.Show MsgBox "Despus de mostrar el formulario" En este caso, se mostrara el formulario indicado (Form2) y justo despus de mostrarse, se ejecutara la instruccin MsgBox y el formulario seguira abierto. Pero si lo mostramos de forma modal: Form2.Show vbModal MsgBox "Despus de mostrar el formulario" La instruccin MsgBox no se ejecutara hasta que no se cerrara u ocultara el Form2. Es decir, esperara a que se cierre el formulario antes de continuar con la siguiente instruccin. En el ejemplo que veremos aqu se usa esta segunda forma.

Una vez hechas estas aclaraciones, veamos el cdigo, empezando por la interfaz IConfig, la cual se implementar tanto en la clase "real" cConfig, como en el formulario de configuracin, al cual llamaremos fConfig.

La clase (interfaz) IConfig: '----------------------------------------------------------------------------' IConfig (06/Ene/03) ' Interfaz para usar con un formulario de configuracin

545

' ' Guillermo 'guille' Som, 2003 '----------------------------------------------------------------------------Option Explicit

Public Property Get Valor1() As String End Property Public Property Get Valor2() As String End Property Public Property Get Valor3() As String End Property Public Property Get Opcion1() As Boolean End Property

Como puedes comprobar, slo se definen los procedimientos de lectura (Property Get), pero no se utiliza ningn cdigo "operativo", ya que esta clase slo se usa como "plantilla".

La clase cConfig: '----------------------------------------------------------------------------' cConfig (06/Ene/03) ' Clase para usar con un formulario de configuracin ' ' Guillermo 'guille' Som, 2003 '----------------------------------------------------------------------------Option Explicit

546

Implements IConfig Public Valor1 As String Public Valor2 As String Public Valor3 As String Public Opcion1 As Boolean Private Property Get IConfig_Opcion1() As Boolean IConfig_Opcion1 = Opcion1 End Property Private Property Get IConfig_Valor1() As String IConfig_Valor1 = Valor1 End Property Private Property Get IConfig_Valor2() As String IConfig_Valor2 = Valor2 End Property Private Property Get IConfig_Valor3() As String IConfig_Valor3 = Valor3 End Property Public Sub CopiarIConfig(ByVal newValue As IConfig) ' hacer una copia del parmetro en los valores de la clase Me.Opcion1 = newValue.Opcion1 Me.Valor1 = newValue.Valor1 Me.Valor2 = newValue.Valor2 Me.Valor3 = newValue.Valor3 End Sub Public Function Clone() As cConfig Dim tConfig As cConfig Set tConfig = New cConfig ' tConfig.CopiarIConfig Me '

547

Set Clone = tConfig End Function Public Function Equals(ByVal compararCon As cConfig) As Boolean Dim b As Boolean ' If compararCon.Opcion1 = Me.Opcion1 Then If compararCon.Valor1 = Me.Valor1 Then If compararCon.Valor2 = Me.Valor2 Then If compararCon.Valor3 = Me.Valor3 Then b = True End If End If End If End If ' Equals = b End Function Public Sub Guardar(ByVal fichero As String) ' guardar en el fichero indicado los valores de las propiedades de la clase ' ' el cdigo est omitido para que lo hagas como ejercicio ' End Sub Public Sub Leer(ByVal fichero As String) ' leer del fichero indicado los valores de las propiedades de la clase ' ' el cdigo est omitido para que lo hagas como ejercicio ' End Sub

En esta clase, hemos implementado IConfig, para que al acceder a esa "parte" del cdigo, se obtengan los datos de las propiedades. Tambin se han implementado algunos mtodos, los cuales servirn para Leer y Guardar informacin en un fichero de disco, (ese cdigo es tarea tuya), por otro lado, tenemos un mtodo que nos permitir efectuar una copia del objeto: Clone, otro que comprobar si el

548

contenido de esta clase es idntico al de otra del mismo tipo: Equals y por ltimo, un mtodo, (CopiarIConfig), que recibe como parmetro un objeto del tipo IConfig, que se usar para asignar a las propiedades de la clase los valores indicados en el parmetro, este mtodo se usar para "actualizar" el contenido de las propiedades de la clase con otro objeto externo del tipo IConfig o que implemente la interfaz IConfig. Fjate que el mtodo Clone, hace uso de ese mtodo para realizar la copia de los datos del objeto actual en la nueva variable que devuelve.

El cdigo del formulario principal: '----------------------------------------------------------------------------' fEntrega44 (06/Ene/03) ' Pruebas para la entrega 44 del Curso Bsico de VB ' ' Guillermo 'guille' Som, 2003 '----------------------------------------------------------------------------Option Explicit Private mConfig As cConfig Private sFic As String Private Sub Form_Load() ' el nombre del fichero de datos sFic = App.Path & "\Prueba44.txt" ' Set mConfig = New cConfig ' asignar los valores de prueba al objeto mConfig.Valor1 = "El valor1" mConfig.Valor2 = "El valor2" mConfig.Valor3 = "El valor3" mConfig.Opcion1 = True ' mostrarlos en los controles del formulario asignarConfig mConfig ' End Sub Private Sub cmdCerrar_Click() Unload Me

549

End Sub Private Sub cmdGuardar_Click() ' guardar los datos en un fichero mConfig.Guardar sFic End Sub Private Sub cmdLeer_Click() ' leer los datos del fichero indicado mConfig.Leer sFic ' ' '$POR HACER: actualizar los controles con los valores de mConfig ' ' End Sub Private Sub cmdConfig_Click() ' no es necesario crear una variable para acceder al formulario ' pero es una buena prctica, la cual te recomiendo encarecidamente. Dim fc As fConfig ' Set fc = New fConfig ' cargamos el formulario para que se inicialicen los valores que pudiera ' haber en el evento Form_Load Load fc ' ' llenar el combo del formulario de configuracin ' con algunos datos Dim i As Long ' For i = 1 To 10 fc.Combo1.AddItem "Valor de prueba nmero " & CStr(i) Next ' ' Asignamos los valores a la clase mConfig.Valor1 = Text1

550

mConfig.Valor2 = Text2 mConfig.Valor3 = Text3 If Check1.Value = vbChecked Then mConfig.Opcion1 = True Else mConfig.Opcion1 = False End If ' ' usando un procedimiento fc.AsignarIConfig mConfig ' ' mostramos el formulario de forma modal para que "espere" ' hasta que se haya cerrado u ocultado fc.Show vbModal ' ' comprobamos si se ha cancelado la configuracin If fc.Cancelado = False Then ' slo asignar los valores si no se ha cancelado ' ' asignamos los nuevos valores a la copia local de este formulario ' si se pasa como parmetro del tipo IConfig, funcionar usando el formulario asignarConfig fc ' End If ' ' descargamos el formulario de configuracin y Unload fc ' eliminamos el objeto de la memoria Set fc = Nothing End Sub Private Sub asignarConfig(ByVal newValue As IConfig) ' actualizamos el contenido de la clase mConfig.CopiarIConfig newValue ' ' actualizamos el contenido de los controles Text1 = newValue.Valor1

551

Text2 = newValue.Valor2 Text3 = newValue.Valor3 If newValue.Opcion1 Then Check1.Value = vbChecked Else Check1.Value = vbUnchecked End If End Sub

Veamos con detalle el cdigo usado: Declaramos una variable del tipo cConfig para almacenar los datos (mConfig). Declaramos una variable que tendr el nombre del fichero en el que se guardarn los datos (sFic). En el evento Form_Load, asignamos el nombre del fichero, el cual estar en el mismo directorio que la aplicacin, para ello utilizamos App.Path, a continuacin creamos una nueva instancia del objeto mConfig, le asignamos unos valores "de prueba" y llamamos al mtodo asignarConfig para que se reflejen en los controles los valores de la clase. Cuando se pulsa en el botn Cerrar, se descarga el formulario principal, con lo cual tambin se termina la aplicacin: Unload Me. Al pulsar en el botn Guardar, se llama al mtodo Guardar de la clase cConfig y se le pasa como parmetro el nombre del fichero en el que se guardar el contenido de esa clase. Fjate que ser el propio mtodo Guardar de la clase el que se encargue de abrir ese fichero y guardar el contenido de las propiedades. Cuando se pulse en el botn Leer, se llama al mtodo Leer del objeto mConfig, (que es del tipo cConfig), el cual se encargar de leer la informacin del fichero pasado como argumento (o parmetro), ese mtodo (el de la clase) ser el que se encargue de, adems de leer la informacin del fichero, comprobar si ese fichero existe, etc. Una vez ledos los datos, se tendr que reflejar esa informacin en los controles del formulario, esto tambin es algo que debes hacer como ejercicio, slo comentarte que la solucin es muy simple y ya est hecha... o casi. Cuando el usuario pulse en el botn Configurar, tendremos que mostrar el formulario de configuracin, al cual tenemos que indicar los valores originales a mostrar y una vez cerrado, de forma "aceptable", esos datos hay que asignarlos a la clase (en este caso) del formulario principal. Nota: Aqu estamos usando una clase para mantener la informacin en el formulario principal, en otras ocasiones, es posible que esa informacin est asignada a valores de variables "normales", pero el planteamiento que te estoy explicando es para usar clases e interfaces, por tanto, he elegido ese mtodo o forma de comunicacin entre los formularios y los datos a configurar. Vamos a hacer una pequea "parada" o repaso ms detallado sobre el cdigo usado en el evento del botn Configurar, ya que aqu hay un par de cosillas, creo que, "interesantes". La primera de ellas es que hemos declarado una variable para usar el formulario de configuracin. Realmente no es necesario ni obligatorio hacerlo de esta forma, aunque te recomendara que te acostumbraras a hacerlo as. La forma ms sencilla, hubiera sido usar directamente el nombre que le hemos asignado

552

al formulario: fConfig, ya que el Visual Basic declara y crea una variable "global" con dicho nombre. Por tanto, si eliminas o comentas las lneas que hay antes del Load fc y cambias todos los fc por fConfig, el programa seguir funcionando igual. Al crear una variable (fc) del tipo del formulario, lo que estamos haciendo, entre otras cosas, es comprobar que los formularios realmente son y actan como clases. Y se "instancian" o crean de la misma forma que cualquier variable del tipo de una clase, es decir usando Set variable = clase. Seguramente te preguntars por qu esa complicacin extra? Pues, porque s... porque as puedes comprobar que puedes usar un formulario igual que cualquier otra clase y tambin porque as es ms evidente cual es nuestra intencin, si ese cdigo lo ve cualquier otra persona, sabr exactamente cual es tu intencin. Recuerda que quiero ensearte "buenos modales" en esto de la programacin, por tanto, siempre tendrs la libertad de hacerlo de la forma que ms te guste: como el Guille te recomienda o "a las bravas", tu eres quin tiene la ltima palabra. Despus de esta pequea aclaracin sobre modales, sigamos examinando el cdigo: Despus de cargar el formulario en la memoria (Load fc), asignamos una serie de valores al ComboBox, esto lo hago as, para que sepas que puedes acceder a cualquier control de un formulario desde otro y asignar o recuperar cualquiera de los valores que dicho control tenga. El siguiente paso es asignar a las propiedades de la variable mConfig el contenido de los controles del formulario principal. Fjate que en el formulario principal estamos usando una caja de textos para el contenido de la propiedad Valor3 y en el de configuracin, (como comprobars dentro de poco), se est usando un ComboBox. Una vez que la variable mConfig tiene asignado los valores de las propiedades, la pasamos con parmetro del mtodo AsignarIConfig que hemos implementado en el formulario de configuracin, esto har, (como vers dentro de poco), que se asignen los valores tanto a la variable del tipo cConfig que hay en ese formulario, como a los controles. Hecho esto, el formulario de configuracin tendr toda la informacin que necesita, por tanto lo mostramos de forma modal (como cuadro de dilogo), usando fc.Show vbModal, de esta forma, el cdigo del formulario principal "esperar" a que el formulario de configuracin desaparezca de la faz de la pantalla, para continuar. Cuando vuelve, comprobamos si se ha cancelado, en caso de que no se haya cancelado, la propiedad Cancelado tendr un valor False y por tanto tendremos que aceptar los nuevos valores. Esa propiedad (Cancelado) es una propiedad que nosotros (en este caso yo) hemos definido en el formulario de configuracin, tal como podrs comprobar cuando veamos el cdigo que falta. Si no se ha cancelado, asignaremos a la variable mConfig del formulario principal los valores que se han asignado en el de configuracin, para ello utilizamos el mtodo asignarConfig, al cual se le pasa como parmetro el formulario (fc), pero ese mtodo acepta un objeto del tipo IConfig, por tanto Visual Basic filtrar el formulario para que slo se "vea" la parte implementada por la interfaz IConfig. Por ltimo se descarga el formulario (para que no siga en memoria) y se librera la memoria utilizada por la variable fc, aunque esto ltimo no es necesario, ya que ser el propio VB el que se encargue de hacer esa limpieza, pero esto es algo que yo acostumbro a hacer... cosas mas! Para acabar con la explicacin del cdigo del formulario principal, slo nos queda ver el procedimiento asignarConfig. Este procedimiento (o mtodo privado) del formulario, recibe como parmetro un objeto del tipo IConfig, por tanto cualquier objeto de ese tipo o que lo implemente, se puede usar como parmetro. Lo que se hace en ese procedimiento, es hacer una copia en la variable mConfig, (por

553

medio del mtodo CopiarIConfig), de los datos contenidos en el objeto pasado como parmetro, as nos aseguramos de que la variable siempre est actualizada. Tambin se asignan esos valores a los controles para que sea "visual" para el usuario. Si algo de lo que acabamos de ver no te ha quedado muy claro, espera a ver el resto del cdigo.

El cdigo del formulario de configuracin: '----------------------------------------------------------------------------' fConfig (06/Ene/03) ' Pruebas para la entrega 44 del Curso Bsico de VB ' ' Guillermo 'guille' Som, 2003 '----------------------------------------------------------------------------Option Explicit Implements IConfig Private mConfig As cConfig Public Cancelado As Boolean Private Sub Check1_Click() comprobarCambiado End Sub Private Sub cmdAceptar_Click() cmdAplicar_Click Cancelado = False Hide End Sub Private Sub cmdAplicar_Click() ' asignar los valores a la clase mConfig.Valor1 = Text1 mConfig.Valor2 = Text2 mConfig.Valor3 = Combo1.Text

554

If Check1.Value = vbChecked Then mConfig.Opcion1 = True Else mConfig.Opcion1 = False End If ' cmdAplicar.Enabled = False End Sub Private Sub cmdCancelar_Click() Cancelado = True Hide End Sub Private Sub Combo1_Change() comprobarCambiado End Sub Private Sub Form_Load() Set mConfig = New cConfig cmdAplicar.Enabled = False End Sub Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer) Cancelado = True End Sub Private Sub Form_Resize() If WindowState <> vbMinimized Then Line1.X1 = 90 Line1.X2 = ScaleWidth - 90 Line2.X1 = 90 Line2.X2 = ScaleWidth - 90 End If End Sub Private Property Get IConfig_Opcion1() As Boolean IConfig_Opcion1 = (Check1.Value = vbChecked)

555

End Property Private Property Get IConfig_Valor1() As String IConfig_Valor1 = Text1 End Property Private Property Get IConfig_Valor2() As String IConfig_Valor2 = Text2 End Property Private Property Get IConfig_Valor3() As String IConfig_Valor3 = Combo1.Text End Property Private Sub Text1_Change() comprobarCambiado End Sub Private Sub Text2_Change() comprobarCambiado End Sub Private Sub comprobarCambiado() Dim datosCambiados As Boolean ' If mConfig.Valor1 <> Text1 Then datosCambiados = True End If If mConfig.Valor2 <> Text2 Then datosCambiados = True End If If mConfig.Valor3 <> Combo1.Text Then datosCambiados = True End If If mConfig.Opcion1 <> (Check1.Value = vbChecked) Then datosCambiados = True End If '

556

cmdAplicar.Enabled = datosCambiados End Sub Public Sub AsignarIConfig(ByVal newValue As IConfig) ' esta asignacin slo funcionar si newValue es del tipo cConfig 'Set mConfig = newValue mConfig.CopiarIConfig newValue ' Text1 = mConfig.Valor1 Text2 = mConfig.Valor2 Combo1.Text = mConfig.Valor3 If mConfig.Opcion1 Then Check1.Value = vbChecked Else Check1.Value = vbUnchecked End If comprobarCambiado End Sub En el formulario de configuracin tenemos una variable (mConfig) del tipo cConfig, que ser la que reciba los valores a "configurar"... (cuantas configuraciones!), tambin implementa la interfaz IConfig, adems de tener una propiedad llamada Cancelado, (recuerda que las variables de una clase declaradas como pblicas se convierten en propiedades), que nos servir de "seal" para saber si el usuario ha cancelado o no la "configuracin"... Fjate que en este formulario el nombre de la variable con los datos, tambin se llama mConfig, pero esto es slo "pura coincidencia" (o mejor dicho, para no tener que calentarme la cabeza pensando en otro nombre), ya que no tiene porqu llamarse igual que la variable usada en el formulario principal. Adems deberas saber que esa variable no se podr "conectar" ni confundir con la del otro formulario, por la sencilla razn de que ambas estn declaradas como "privadas", (Private), por tanto slo visibles dentro de cada formulario. En el evento Click de los botones Aceptar y Cancelar, se asigna el valor apropiado a la propiedad Cancelado, adems de que se oculta (mediante Hide) el formulario, de esta forma se devuelve el control al formulario principal y se conservan los datos, ya que al no cerrarlo, lo seguimos teniendo en memoria. En el caso del botn Aceptar, llamamos al procedimiento cmdAplicar_Click, para que se asignen los valores a la variable mConfig, esto es para simular el efecto de pulsar en el botn Aplicar, ya que ese botn se encarga de actualizar los datos de la variable con el contenido de los controles. En cuanto al cdigo de los eventos que se disparan cuando se cambia alguno de los controles con los datos, simplemente llaman al procedimiento comprobarCambiado en el cual se hacen las comprobaciones pertinentes para saber si se debe o no habilitar el botn Aplicar. De esta forma, si el usuario escribe o cambia cualquiera de esos valores, dicho botn estar habilitado (el contenido ha cambiado) o deshabilitado, para reflejar que

557

el contenido de esos controles se ha dejado como estaba, realmente se comprueba si estaba como antes de haber pulsado en Aplicar. Creo que el cdigo del procedimiento comprobarCambiado no necesita ninguna explicacin, (al menos detallada), ya que lo nico que se hace es comprobar si el contenido de las propiedades de la clase coinciden con el contenido de los controles. Si acaso, explicarte cmo funciona esta lnea: If mConfig.Opcion1 <> (Check1.Value = vbChecked) Then Aqu lo que se comprueba es si el valor de Opcion1 (que es de tipo Boolean) es distinto del resultado de comprobar si el valor de Check1 es igual a vbChecked. Esa expresin devolver True o False dependiendo de que el valor sea o no vbChecked, es decir que est o no "marcado". Esto ltimo lo tienes un poco ms claro en el cdigo del evento Click del botn Aplicar, ya que all se hace una comprobacin ms "especfica" y detallada. El mtodo pblico AsignarIConfig es el que se encarga de asignar los valores a la variable interna del tipo cConfig y a los controles con los valores que se hayan indicado, este mtodo se llama desde el formulario principal para asignar los valores iniciales de la configuracin. Para terminar, en el evento Load del formulario se crea una nueva instancia de la variable "privada" que contendr los datos de configuracin, adems de deshabilitar el botn Aplicar. El evento QueryUnload se produce justo antes de que el formulario se cierre, por tanto este evento se producir si el usuario pulsa en la "x" para cerrarlo, si es as, asignamos un valor False a la propiedad Cancelado, aunque, realmente no es necesario hacerlo, pero... para que quede evidente que si se cierra, se supone que se ha cancelado. En el caso del evento Resize, se comprueba si no se ha minimizado y si es as, se posicionan las lneas que hacen de separador 3D, (las lneas que estn encima de los tres botones).

Espero que todo haya quedado claro y comprensible, no slo por los comentarios que he hecho al cdigo, sino por los propios comentarios que el cdigo contiene. En caso de que no te hayas enterado... pues lete de nuevo esta entrega y sobre todo la anterior, para ver si as te aclaras un poco... (es que si te digo que me preguntes, no te vas a esforzar en entenderlo y me dar la impresin de que "realmente" no quieres esforzarte, as que... no me preguntes!). Recuerda que tienes que hacer los ejercicios que te he mencionado, la solucin la veremos en la siguiente entrega, la cual an no se de que tratar... de clases? de bases de datos? se acabar ya el curso? Pues, tendrs que esperar, como mnimo al mes que viene o a finales de este, segn me de... je, je. Mientras tanto, busca algo que leer o repsate las 43 entregas anteriores... que no es plan de que se te olviden las cosas por mi tardanza en escribir nuevas entregas. Nos vemos Guillermo

Aqu tienes el fichero zip con el cdigo usado en esta entrega: basico44_cod.zip 5.50 KB

558

Curso Bsico de Programacin en Visual Basic

Bueno, parece ser que me estoy "portando bien", ya que esta nueva entrega est dentro de los plazos que me impuse hace poco: publicar como mnimo una entrega cada mes. S, ya se que deberan ser ms entregas por mes, pero al menos es mejor tener una entrega al mes que no tener que estar varios meses sin ninguna nueva... as que no te quejes mucho... je, je.

Las soluciones de la entrega 44


Empezaremos viendo las soluciones al ejercicio propuesto en la entrega (44) anterior, adems de algo que no estaba explcitamente propuesto, pero que si no lo has hecho, no te funcionara bien. Este sera el cdigo a usar en los mtodos Guardar y Leer de la clase cConfig. Empezaremos por el mtodo Guardar que es el ms simple y despus veremos el mtodo Leer en el que habr que hacer algunas comprobaciones extras: Public Sub Guardar(ByVal fichero As String) ' guardar en el fichero indicado los valores de las propiedades de la clase ' ' el cdigo est omitido para que lo hagas como ejercicio ' SOLUCIN: Dim nFic As Long ' On Error Resume Next ' asignar un "canal" libre nFic = FreeFile ' abrir el fichero para escritura en el canal indicado Open fichero For Output As nFic ' guardar las cuatro propiedades de esta clase Print #nFic, Me.Valor1 Print #nFic, Me.Valor2 Print #nFic, Me.Valor3 Print #nFic, Me.Opcion1 ' cerrar el canal abierto Close nFic ' End Sub Como puedes comprobar, el cdigo es de lo ms simple. Una vez abierto el fichero indicado, se guardan los valores de las cuatro propiedades de la clase. Usamos On Error Resume Next, por si el disco estuviera lleno o no disponible... Veamos ahora el cdigo de Leer.

559

Public Sub Leer(ByVal fichero As String) ' leer del fichero indicado los valores de las propiedades de la clase ' ' el cdigo est omitido para que lo hagas como ejercicio ' SOLUCIN: Dim nFic As Long Dim s As String ' ' comprobar si existe el fichero ' si no existe, se sale del procedimiento y no se hace nada ' If Dir$(fichero) = "" Then ' Exit Sub ' End If ' ' NOTA: ' Es posible, que si el nombre del fichero est mal formado ' (no es un nombre vlido), se produzca un error. ' En ese caso se podra usar el siguiente cdigo, ' o usar una funcin ExistFile como se ha mostrado en entregas anteriores. Dim i As Long ' On Error Resume Next i = Len(Dir$(fichero)) ' Si se produce un error en la lnea anterior, es que no existe. ' Adems de que si i vale cero, es que no existe. If Err.Number <> 0 Or i = 0 Then Exit Sub End If ' ' ' asignar un "canal" libre nFic = FreeFile ' abrir el fichero para lectura en el canal indicado Open fichero For Input As nFic ' leer los cuatro valores en el mismo orden en el que se guardaron Line Input #nFic, s Me.Valor1 = s Line Input #nFic, s Me.Valor2 = s Line Input #nFic, s Me.Valor3 = s Line Input #nFic, s ' Nota: ' Esta asignacin tambin puede dar error si se ha manipulado el fichero, ' para evitar ese error, podemos usar On Error Resume Next para evitar ' que el programa se detenga. ' ' Como resulta que ya tenemos un On Error activo, no ser necesario ' indicarlo nuevamente, pero si la comprobacin de que el fichero existe ' se hace desde una funcin, habra que quitar el comentario. 'On Error Resume Next Me.Opcion1 = CBool(s) ' cerrar el canal abierto Close nFic ' End Sub Este tampoco es complicado, aunque algo ms "largo" que el anterior, entre otras cosas porque se hace una comprobacin de que el fichero indicado existe y otra de que el valor asignado a la

560

propiedad Opcion1 es el correcto. En teora no habra que hacer estas comparaciones, o al menos no habra porqu usar On Error, ya que se supone que el nombre del fichero es correcto, independientemente de que dicho fichero exista o no y por otro lado, cuando se lea ese fichero los datos estarn guardados de forma correcta. Pero siempre es preferible prevenir. Ahora veamos el cdigo del formulario principal, tanto del evento producido al guardar como al leer: Private Sub cmdGuardar_Click() ' guardar los datos en un fichero ' ' SOLUCIN: ' (aunque no estaba como ejercicio, si no se hace, no se guarda correctamente) ' ' Asignamos los valores a la clase y guardarlos mConfig.Valor1 = Text1 mConfig.Valor2 = Text2 mConfig.Valor3 = Text3 If Check1.Value = vbChecked Then mConfig.Opcion1 = True Else mConfig.Opcion1 = False End If ' mConfig.Guardar sFic End Sub Private Sub cmdLeer_Click() ' leer los datos del fichero indicado mConfig.Leer sFic ' ' '$POR HACER: actualizar los controles con los valores de mConfig ' SOLUCIN: asignarConfig mConfig ' ' End Sub En el caso del evento producido al pulsar en el botn Guardar, antes de llamar al mtodo correspondiente de la clase, hay que asignar los valores a las propiedades de dicha clase. Para el evento del botn Leer lo tenemos ms fcil ya que todo el trabajo de asignar el contenido de la clase en los controles del formulario se hace por medio del procedimiento asignarConfig. Espero y confo en que lo hayas hecho bien... y si no es as... pues tampoco pasa nada, ya que por eso te doy la solucin... je, je.

A la vuelta con la Encapsulacin


Como has podido comprobar, al "encapsular" ciertas tareas a realizar por una clase, (en este caso las acciones de leer y guardar la informacin), el cdigo de nuestros proyectos ser ms fcil de mantener, ya que si por alguna circunstancia necesitamos cambiar la forma de acceder a ese

561

fichero, bien porque hayamos aadido alguna nueva propiedad o porque haya cambiado el tipo de datos, tan slo tendremos que cambiar el cdigo de esos procedimientos, de forma que en el resto del cdigo no tengamos que hacer ningn cambio. Por esa razn, es importante y sobre todo aconsejable, que en la medida de lo posible, hagamos que sea el cdigo incluido en la propia clase el que se encargue de manejar la informacin o los datos que dicha clase manipular. Ya que, al menos eso es lo que se supone, la propia clase es la que "mejor" sabe cmo manipular los datos que contiene. Si, por ejemplo, tienes que crear una funcin o procedimiento para clasificar esos datos, siempre ser ms conveniente que el cdigo que se encargue de realizar esa clasificacin est contenido en la propia clase que usar funciones o procedimientos externos. Hacindolo de esta forma, sobre todo, ganamos ms legibilidad en nuestro cdigo.

Siguiendo con el tema de la encapsulacin y la forma de manejar los datos o la informacin de las clases, en ocasiones nos podemos encontrar con la necesidad de que sea la propia clase la que nos avise de que ha sucedido algo. Puede ser interesante que, por ejemplo, si la clase usada en el proyecto de la entrega anterior detecta algn tipo de error al leer o guardar los datos, nos lo comunique. O bien, que si son muchos los datos que tiene que guardar o leer, nos vaya mostrando cada uno de los datos que est procesando. Esto lo podemos conseguir mediante los eventos.

Definir Eventos en las clases


Hasta ahora hemos estado usando los eventos de los formularios y los controles. Ya sabes que esa es la forma en la que el propio sistema operativo se comunica con esos controles y formularios, de forma que podamos saber cuando se ha pulsado en un control o se ha movido el ratn o... cualquiera de las otras cosas que Visual Basic tenga que saber... ya que, aunque Windows comunica muchas cosas a los controles y formularios, no le comunica TODO lo que ocurre... pero, bueno, ese ser el tema de otra entrega, lo que ahora nos interesa saber es que podemos crear nuestros propios eventos en nuestras clases y que podemos usarlos desde cualquier otro sitio. Lo primero que debemos aprender es cmo definir y "disparar" dichos eventos en nuestras clases. Para que los objetos creados a partir de nuestras clases puedan comunicarse con dicha clase mediante eventos, tenemos que definirlos. De la misma forma que existen instrucciones o palabras clave para definir una funcin o un procedimiento, existe una instruccin que nos permite indicarle a nuestro querido VB que queremos crear un evento, la instruccin que usaremos ser: Event seguida del nombre del evento y opcionalmente los parmetros que deba tener. Por ejemplo: Event Prueba(ByVal mensaje As String) Nota: De forma predeterminada, los eventos son pblicos, as que, es opcional indicar esa instruccin. Esta lnea definira un evento llamado Prueba que recibe un parmetro por valor de tipo String. Si tuvisemos un objeto declarado con la clase que contiene ese evento, lo usaramos de la siguiente forma: Private elObjeto_Prueba(ByVal mensaje As String) ' ... el cdigo

562

End Sub Es decir, de la misma forma que hasta ahora hemos estado usando los eventos. Fjate que tambin se sigue la nomenclatura estndar de usar el nombre del objeto un guin bajo y el nombre del evento; aunque este es un detalle del que tenemos que despreocuparnos, ya que es el propio entorno de desarrollo el que se encarga de dar nombre a los eventos de la forma correcta.

Cmo lanzar un evento


Una vez que hemos declarado un evento en una de nuestras clases, nos queda por saber cmo hacer que dicho evento se lance, es decir cmo hacemos para "avisar" al cdigo que usa nuestra clase de que dicho evento se ha producido. Esto se consigue usando la instruccin RaiseEvent seguida del nombre del evento y del parmetro (o parmetros) que tenga dicho evento. Por ejemplo, si queremos lanzar ese evento, haramos algo como lo siguiente: RaiseEvent Prueba("El evento prueba") Cuando escribimos la instruccin RaiseEvent, el IDE de Visual Basic nos muestra los eventos que podemos lanzar, en la siguiente figura, podemos ver que nos muestra los tres eventos que tenemos definidos:

Fig.1, el IDE de VB muestra los eventos que la clase puede producir. Como sabrs, cuando no indicamos si el parmetro es por valor o por referencia, se supone que es por referencia, por tanto, en el caso del evento ElementoSeleccionado, el parmetro index ser ByRef y en los otros dos, tal y como se indica en la declaracin mostrada en la figura, son del tipo ByVal. Nota: Como te dije antes, cuando declaramos un evento en una clase, ese evento est definido como pblico, pero el que est definido como pblico no significa que se pueda lanzar desde cualquier sitio, ya que slo se puede usar RaiseEvent nombreEvento() desde la propia clase en la que se ha definido el evento.

Cmo indicar que una clase debe interceptar eventos


Debido a la forma "especial" en la que Visual Basic define los procedimientos de evento, no basta con declarar una variable del tipo de la clase que produce los eventos para poder usarlos.

563

Me explico, para que lo comprendas mejor: Supongamos que tenemos una clase llamada cConEventos, la cual produce los tres eventos mostrados en la figura 1. Si declaramos una variable de ese tipo en otra parte de nuestro proyecto, lo normal es que lo hagamos de esta forma: Dim mConEvento As cConEventos Despus creamos el objeto en la memoria (lo instanciamos) usando New: Set mConEvento = New cConEventos A partir de este momento podemos usar dicho objeto, ya que se ha creado en la memoria. Esto ya lo tenemos claro, verdad? ya que al fin y al cabo es la forma "habitual" de declarar e instanciar una clase. Pero esto no nos permitira usar los eventos declarados en la clase, a pesar de que tengamos definidos los procedimientos Sub de los eventos, los cuales se definiran de la siguiente forma: Private Sub mConEvento_Aviso(ByVal elAviso As String) ' End Sub Private Sub mConEvento_ElementoSeleccionado(index As Integer) ' End Sub Private Sub mConEvento_Prueba(ByVal mensaje As String) ' End Sub Por qu? Por la sencilla razn de que si bien la clase produce eventos, Visual Basic no sabe que queremos usarlos, es decir, esos tres procedimientos que, al menos en teora, deberan interceptar los eventos no los interceptarn. Para que Visual Basic se entere de que la clase se va a usar para procesar o interceptar los eventos, hay que declararla usando la instruccin WithEvents. Sabiendo esto, cuando queramos usar una clase que produce eventos, tenemos que declararla de la siguiente forma: Dim WithEvents mConEvento As cConEventos A partir de ese momento, nuestro querido (y, algunas veces, tozudo) Visual Basic sabe que nuestra intencin es usar los eventos de esa clase. Adems, una vez declarada una variable de esta forma, podemos comprobar que dicha variable se muestra en la lista desplegable de los objetos que producen eventos, (la que est en la izquierda del panel de cdigo), tal como podemos ver en la siguiente imagen:

564

Fig. 2, Lista con los objetos que producen eventos. Una vez seleccionado el objeto en la lista desplegable de la izquierda, podemos ver los eventos que dicha clase produce, para ello debemos desplegar la lista de la derecha. En la siguiente figura podemos ver los tres eventos que la hipottica clase cConEventos produce:

Fig. 3, Los eventos de la clase seleccionada en la lista. Los eventos se muestran de forma alfabtica, pero el que toma el foco, (el que se crea nada ms que mostrar ese objeto), es el primero que hayamos definido en la clase. En este caso, el evento Prueba. A partir de este momento podemos usar los eventos que queramos, no es necesario tener que codificarlos todos, slo los que nos interese interceptar. Cuando mostramos la lista con los eventos, el IDE de Visual Basic nos muestra en negrita los que ya tienen cdigo. Si declaramos una clase con WithEvents y esa clase no produce eventos, dicha clase no se mostrar en la lista de clases que podemos usar para declarar esa variable, adems de que al ejecutar la aplicacin, el Visual Basic nos mostrar un error de que dicha clase no produce eventos, tal y como podemos comprobar en la siguiente figura:

565

Fig. 4, Error al declarar con WithEvents una variable que no produce eventos.

Resumen de cmo definir y usar eventos personalizados:


Resumamos un poco todo esto, para que quede ms o menos claro cmo definir y usar eventos en las clases: 1- Para que la clase tenga eventos, debemos definirlos con la instruccin Events. 2- Para lanzar un evento en nuestra clase, debemos usar RaiseEvent seguida del evento a lanzar. 3- Para poder usar los eventos de una clase desde otra parte de nuestro proyecto, debemos declarar dicha clase con la instruccin WithEvents. 4- Una vez definida la variable a partir de una clase que produce eventos, debemos escribir el cdigo de los procedimientos de los eventos (siempre sern del tipo Sub) en la forma habitual: NombreVariable guin bajo NombreEvento, por ejemplo: Sub mConEventos_Prueba(parmetros)

Para ver en la prctica todo esto que se ha comentado, vamos a crear un proyecto en el que definiremos una clase que produce eventos y los interceptaremos en un formulario. Para ello vamos a crear un nuevo proyecto, al que aadiremos una clase llamada cConEventos que definir dos eventos, los cuales se producirn al llamar a un mtodo de esta misma clase. Uno de esos eventos se producir mientras se aaden nuevos datos a un array y el otro al terminar de aadir dichos datos, devolviendo el nmero total de elementos. Veamos el cdigo de esa clase: Option Explicit Private mDatos() As String ' Event NuevoDato(ByVal elDato As String) Event DatosCreados(ByVal total As Long) Public Sub CrearDatos() Dim i As Long ' ReDim mDatos(10) For i = 0 To 10 mDatos(i) = "El dato nmero " & CStr(i) RaiseEvent NuevoDato(mDatos(i))

566

Next RaiseEvent DatosCreados(11) End Sub Como podemos comprobar, los dos eventos que nuestra clase emitir al "receptor" de utilice dicha clase, sern: NuevoDato el cual tiene un parmetro de tipo String que representar al nuevo dato que se est manipulando y DatosCreados cuyo parmetro de tipo Long nos indicar el nmero total de datos que la clase acaba de crear. Esos dos eventos se lanzan (o disparan) en el mtodo CrearDatos, que ser el nico que podremos usar desde cualquier variable declarada con el tipo de la clase cConEventos. El cdigo usado en este ltimo procedimiento es simple, pero te lo detallo para que no tengas problemas de comprensin... (s, ya se que lo has entendido, pero...) -Definimos una variable que usaremos para el bucle For. -Redimensionamos el array para que tenga 11 elementos, de cero a diez. -Hacemos un bucle para que se repita desde 0 a 10. -En cada ciclo del bucle, asignamos un valor al elemento i (usando la variable contadora del bucle) del array mDatos y -lanzamos el evento NuevoDato en cuyo parmetro indicamos el contenido del elemento que acabamos de asignar. -Continuamos repitiendo el bucle hasta que estn asignados los once elementos. -Por ltimo, lanzamos el evento DatosCreados, en cuyo parmetro indicamos el valor once. Para poder usar esta clase desde el formulario creado en el proyecto, al cual aadiremos un CommandButton al que llamaremos crearDatosCmd y un ListBox llamado List1, tambin definiremos la clase usando WithEvents, crearemos una nueva instancia en el evento Load del formulario y al pulsar en ese botn, llamaremos al mtodo CrearDatos. Debido a que la variable estar definida con WithEvents, tendremos que escribir el cdigo en cada uno de los dos eventos para poder comprobar que todo esto que te estoy contando realmente funciona. En uno de ellos, el que se produce al aadir un nuevo elemento al array, haremos que el parmetro indicado en el evento se aada al ListBox y cuando se produzca el evento DatosCreados, mostraremos un mensaje que nos avise de cuantos elementos se han creado, para mostrar ese mensaje usaremos la instruccin MsgBox. Te atreves a codificar todo esto que te acabo de decir? No me digas que no, que me enfado... Bueno, vale... te doy unas pistas: Nota: Si lo vas a hacer por tu cuenta y no quieres pistas, no leas lo que sigue... aunque dependiendo de la resolucin de tu monitor, es posible que veas el resto del cdigo... as que intentar dejar unas cuantas lneas en blanco para que no puedas ver el cdigo y dems pistas... Pero me gustara que lo intentaras antes de ver la solucin...

Vale, la solucin te la muestro en una pgina aparte... as no tendrs la excusa de que lo has visto sin querer...

567

Ampliar un formulario con eventos personalizados


Como ya te he comentado en otras ocasiones, los formularios realmente son clases, con un tratamiento especial, pero clases al fin y al cabo. Y como hemos podido comprobar, podemos definir nuestros propios eventos en las clases, por tanto, si la lgica y los silogismos no fallan, podemos definir eventos en los formularios. La forma de declarar nuevos eventos en un formulario sera usando Event y el nombre del evento, es decir, de la misma forma que lo haramos en cualquier otra clase. Para poder usar este "formulario ampliado", tendramos que declararlo tambin con WithEvents. Como sabemos, Visual Basic nos permite usar los formularios sin necesidad de crear una variable que los referencie, ya que, de forma oculta crea una variable con el nombre que le hemos dado en tiempo de diseo. Por ejemplo, si en nuestro proyecto tenemos dos formularios y uno de ellos se llama Form2, podemos acceder a ese formulario usando ese nombre. Pero tambin vimos en la entrega anterior que podemos definir una variable del tipo de un formulario y acceder a dicho formulario por medio de esa variable. Usando este sistema es la forma en la que podemos aprovecharnos de las caractersticas "ampliables" de los formularios. Una vez que declaramos un evento en un formulario, cuando usamos ese formulario con WithEvents, los nicos eventos a los que podremos acceder sern los que estn declarados de forma explcita. Sin embargo, si declaramos una variable de tipo genrico Form con la instruccin WithEvents, podremos usar los eventos de dicho formulario. Comprobemos que todo esto es cierto. Para ello, crearemos un nuevo proyecto al que aadiremos un segundo formulario. El primer formulario (Form1) tendr un botn (mostrarForm2Cmd) y una etiqueta (Label1). El segundo formulario (Form2) slo tendr un botn (lanzarEventoCmd). Veamos primero el cdigo del segundo formulario, ya que es algo ms simple: Option Explicit Event Prueba(ByVal mensaje As String) Private Sub lanzarEventoCmd_Click() RaiseEvent Prueba("El evento prueba") End Sub Como puedes comprobar, declaramos un evento llamado Prueba que tiene un parmetro de tipo String. Cuando se pulse en el botn de ese formulario, se lanzar el evento Prueba. Ahora veamos el cdigo del formulario principal (Form1). Option Explicit Private WithEvents mForm As Form Private WithEvents mForm2 As Form2 Private Sub Form_Load() ' creamos una nueva instancia

568

Set mForm2 = New Form2 ' asignamos a la variable mForm una referencia al objeto recin creado Set mForm = mForm2 End Sub Private Sub mForm_Click() ' Este evento se producir al hacer una pulsacin en el Form2 Label1 = "Evento desde Form2: Form_Click" End Sub Private Sub mForm_Load() ' Esto evento se producir al cargarse el Form2 Label1 = "Evento desde Form2: Load" End Sub Private Sub mForm2_Prueba(ByVal mensaje As String) ' Este evento se producir desde el Form2 Label1 = "Evento desde Form2: " & mensaje End Sub Private Sub mostrarForm2Cmd_Click() ' mostrar el nuevo formulario a la derecha del principal mForm2.Move Me.Left + Me.Width + 60, Me.Top ' mostrar el formulario mForm2.Show End Sub Como te he comentado antes, si declaramos con WithEvents una variable del tipo especfico Form2, slo podremos acceder a los eventos que nosotros hayamos definido, por esa razn he declarado otra variable con WithEvents: mForm que permitir acceder a los eventos "genricos" del formulario, en este caso slo interceptamos dos, pero igualmente podramos acceder al resto. En el evento Load de este formulario asignamos a la variable mForm2 una nueva instancia del formulario Form2 y a continuacin asignamos tambin el objeto apuntado por esa variable a la variable mForm de forma que tanto una como la otra variable estarn apuntando al formulario Form2. No te extrae que se pueda realizar esa asignacin, ya que esto es polimorfismo... recuerdas? La clase Form2 es en realidad un formulario (del tipo Form), por tanto estamos asignando a la variable mForm la parte del Form2 que es del tipo Form, es decir todo excepto el evento que nosotros hemos definido. Cuando se produzcan los eventos Load y Click del formulario Form2, se producirn los dos eventos interceptados con la variable mForm, los cuales mostrarn este hecho en la etiqueta Label1. Por otro lado, cuando se produzca el evento Prueba, se interceptar por medio del evento mForm2_Prueba. Por ltimo, cuando se pulse en el botn para mostrar el formulario Form2, ste se posicionar a la derecha del formulario principal y a continuacin se mostrar. Cuando ejecutes el proyecto, podrs comprobar que al mostrarse por primera vez el formulario Form2, en la etiqueta se mostrar el mensaje de que se ha producido el evento Load, ese mensaje slo se mostrar la primera vez que pulsemos en dicho botn, o cada vez que pulses en dicho botn y el segundo formulario no est cargado en la memoria. Esto ltimo puedes comprobarlo cerrando el segundo formulario y volviendo a pulsar en el botn. Si aades este cdigo al Form1, se mostrar un mensaje cuando se cierre el segundo formulario, as podrs comprobar mejor eso que te acabo de comentar. Private Sub mForm_Unload(Cancel As Integer)

569

' Este mensaje se mostrar al cerrar el segundo formulario Label1 = "El segundo formulario se ha descargado." End Sub Si adems pulsas en el segundo formulario se producir el evento Click y se mostrar el mensaje correspondiente, lo mismo ocurrir cuando pulses en el botn, aunque en ese caso el evento que se producir ser el que hemos definido.

Espero que con todo esto que te he comentado tengas ms claro cmo definir y lanzar eventos en las clases, adems de saber cmo poder interceptar esos eventos desde otras partes del proyecto. En la prxima entrega veremos cmo definir una clase que ample el funcionamiento de un control. De esa forma tendrs la posibilidad de ampliar el funcionamiento de los controles y adaptarlos a tus necesidades. Nos vemos Guillermo

Aqu tienes el fichero zip con el cdigo usado en esta entrega: basico45_cod.zip 4.97 KB

Curso Bsico de Programacin en Visual Basic


Se que hay muchos programadores de VB a los que no les gusta usar el Datacontol para acceder a las bases de datos; pero tambin se que usar este control es ms fcil que usar cdigo directo, aunque esto ltimo tambin lo vamos a ver a lo largo de este curso bsico, incluso lo vamos a mezclar con el Datacontrol, ya que se puede acceder a bases de datos de las dos formas de forma conjunta. En esta entrega vamos a ampliar el ejemplo usado en la entrega anterior, para aadirle algunas opciones nuevas, para aadir nuevos registros y para eliminar registros existentes, por tanto, si quieres conservar intacta la base de datos Biblio.mdb, te recomiendo que hagas una copia de la misma. Tambin vamos a usar la opcin de buscar que puse como ejercicio, por tanto, si no te has ledo las soluciones de la entrega 34, es conveniente de que le eches un vistazo. Vamos a empezar por mejorar la bsqueda en la base de datos:

Buscar en una base de datos.


En esta ocasin vamos a aadir un botn buscar y buscar siguiente, para que podamos seguir buscando a partir del ltimo registro encontrado. En el ejemplo anterior, se buscaba al pulsar Intro en la caja de textos, pero ahora vamos

570

a crear un procedimiento Buscar, el cual, segn el parmetro recibido, buscar la primera coincidencia o seguir buscando desde el ltimo dato hallado. Antes de aadir un nuevo botn, vamos a modificar el cdigo actual para usar el nuevo procedimiento Buscar. Crea un nuevo procedimiento, en el men Herramientas (Tools), selecciona Aadir procedimiento..., llmalo Buscar y haz que sea privado, ya que no tiene ningn sentido que sea pblico, porque slo se usar desde el formulario. Tambin puedes copiar y pegar el siguiente cdigo: ' Private Sub Buscar() ' Procedimiento para buscar el dato indicado (12/Feb/01) Dim nReg As Long ' ' Buscar la primera coincidencia en el recordset del Data1 ' If Option1.Value Then nReg = Val(Text4) ' Data1.Recordset.FindFirst "Au_ID = " & nReg End If If Option2.Value Then ' Data1.Recordset.FindFirst "Author Like '" & Text4.Text & "'" End If End Sub Ahora hay que modificar el evento KeyPress del control Text4, para que llame al nuevo procedimiento. Borra el cdigo que haba anteriormente en ese evento y sustityelo por este otro: ' Private Sub Text4_KeyPress(KeyAscii As Integer) ' Se buscar slo cuando pulsemos INTRO ' ' Comprobar si la tecla pulsada es Intro: vbKeyReturn o 13 que es lo mismo If KeyAscii = vbKeyReturn Then ' Esta asignacin evita que suene un BEEP ' en el campo Author ' en el campo Au_ID ' Convertir el contenido de TextBox en un nmero

571

KeyAscii = 0 ' Llamamos al procedimiento Buscar: Buscar End If End Sub Prubalo, para que veas que todo funciona como antes.

Ahora, vamos a aadir un botn para que busque el primer registro que coincida con lo escrito, esto es para hacer lo mismo que cuando pulsas Intro en el Text4, pero para el usuario ser ms lgico que el hecho de tener que pulsar Intro. Por tanto, aade un nuevo botn, (no te preocupes por ahora dnde colocarlo en el formulario, ya lo haremos dentro de poco), cmbiale el nombre a cmdBuscar y el Caption a Buscar, (por defecto ser Command1) y escribe o aade este cdigo (tambin puedes hacerlo copiando y pegando): ' Private Sub cmdBuscar_Click() ' Simplemente llamamos al procedimiento Buscar: Buscar End Sub Poca cosa, verdad? Pues es igual de efectivo que pulsando Intro en el Text4, pruebalo para que veas que funciona. Creo que ya es hora de que nos vayamos complicando la vida... Vamos a aadir un botn para seguir buscando a partir del ltimo dato hallado: Aade un nuevo botn, llmalo cmdBuscarSig y en el Caption pones: Buscar siguiente, (seguramente el texto lo escribir en dos lneas, pero no te preocupes). El cdigo a usar en el evento Click de ese nuevo botn sera prcticamente el mismo que en el de Buscar, aunque antes debemos aadir un nuevo procedimiento para que busque el siguiente dato al ltimo que busc: ' Private Sub BuscarSiguiente() ' Procedimiento para buscar el dato indicado (12/Feb/01) Dim nReg As Long ' ' Buscar la siguiente coincidencia, a partir del ltimo hallado ' If Option1.Value Then ' en el campo Au_ID

572

' Convertir el contenido de TextBox en un nmero nReg = Val(Text4) ' Data1.Recordset.FindNext "Au_ID = " & nReg End If If Option2.Value Then ' Data1.Recordset.FindNext "Author Like '" & Text4.Text & "'" End If End Sub Si te fijas, el cdigo es prcticamente el mismo que el del procedimiento Buscar, lo nico que cambia es que aqu se usa FindNext en lugar de FindFirst. Es decir FindFirst busca el primer dato que coincida con lo buscado y FindNext el siguiente al ltimo que se busc. Para probar este nuevo procedimiento, en el evento cmdBuscarSig_Click, escribe: BuscarSiguiente. As es como quedara ese evento: Private Sub cmdBuscarSig_Click() ' Buscar el siguiente registro BuscarSiguiente End Sub ' en el campo Author

Como hemos visto, el cdigo usado en los dos procedimientos de bsqueda son prcticamente iguales, as que vamos a unificarlos para crear un slo procedimiento de bsqueda, de esta forma, refrescars tu memoria y sabrs algo ms de parmetros en procedimientos, as como parmetros opcionales.

Parmetros opcionales.
Como sabrs, (y si no lo sabes, te lo cuento yo ahora), a partir de la versin 4 de Visual Basic se pueden usar parmetros opcionales en los procedimientos y funciones. Esto quiere decir que podemos usar el procedimiento de varias formas, indicando todos los parmetros o slo los que realmente son necesarios. Por ejemplo, en el caso en que estamos ahora, el procedimiento Buscar podra tener un parmetro opcional para indicarle si es la primera bsqueda o la siguiente. No voy a entrar en demasiadas explicaciones, ya que este tema lo veremos de forma ms amplia en otra entrega, sobre todo porque tiene sus pormenores, o lo que es lo mismo, existen diferencias entre las versiones 4 y posteriores (al menos hasta la 6) de Visual Basic e incluso en las dos ltimas se puede usar de dos formas diferentes... En esta ocasin voy a usar el formato de las versiones 5 y 6, en estas versiones los parmetros opcionales pueden ser de un tipo de datos diferente a Variant y tambin pueden indicrsele un valor por defecto, para que, si no se especifica, tenga el valor indicado; por supuesto, si no le indicamos el valor, tendrn el valor que ese tipo de datos

573

tengan por defecto, por ejemplo el tipo Boolean tendr un valor False si no se indica el valor. Cuando se indican parmetros con tipo, a diferencia de los parmetros del tipo Variant, no se puede usar IsMissing para comprobar si el parmetro se ha especificado o no, pero, eso es otro tema... Este es el cdigo del procedimiento Buscar y a continuacin te indico cmo llamarlo desde el evento Click del botn cmdBuscarSig: ' Private Sub Buscar(Optional ByVal Siguiente As Boolean = False) ' Procedimiento para buscar el dato indicado (12/Feb/01) ' Si Siguiente = True, se busca a partir del registro activo ' Si no se indica, (valdr False), buscar el primer registro Dim nReg As Long ' ' Buscar la primera coincidencia en el recordset del Data1 ' If Option1.Value Then nReg = Val(Text4) ' ' Si se busca el siguiente dato If Siguiente Then Data1.Recordset.FindNext "Au_ID = " & nReg Else Data1.Recordset.FindFirst "Au_ID = " & nReg End If End If If Option2.Value Then ' ' Si se busca el siguiente dato If Siguiente Then Data1.Recordset.FindNext "Author Like '" & Text4.Text & "'" Else "'" End If End Sub Data1.Recordset.FindFirst "Author Like '" & Text4.Text & End If ' en el campo Author ' en el campo Au_ID ' Convertir el contenido de TextBox en un nmero

574

Private Sub cmdBuscarSig_Click() ' Buscar el siguiente registro Buscar True End Sub

Puedes borrar el procedimiento BuscarSiguiente, ya que no es necesario. Tampoco es necesario modificar el cdigo de los eventos Text4_KeyPress ni el de cmdBuscar_Click, ya que esos dos eventos llaman al procedimiento Buscar con el valor predeterminado y al ser opcional, no es necesario indicarlo... Si pruebas el nuevo cdigo, te dars cuenta de que, la primera vez, pulsando tanto en Buscar como en Buscar siguiente, encuentra lo mismo, esto es debido a que FindNext, busca a partir de la ltima posicin hallada y en el caso de buscar por primera vez, busca desde el principio. En el caso de que cambies el texto buscado y pulses en Buscar siguiente, se mostrar el prximo registro, desde el ltimo hallado, que contenga dicho texto, (estas pruebas hay que hacerlas en el campo Author, ya que no tienen ningn sentido hacerlo en el ID del autor). Para buscar el resto de autores con el nuevo texto, tendrs que pulsar en Buscar para que empiece a buscar desde el principio. Fjate en el cdigo, se da por hecho de que Siguiente tiene un valor, el cual, (al ser del tipo Boolean), puede ser False o True. En caso de que sea True, es decir se ha especificado con ese valor, se buscar con FindNext y en caso de no especificarse o de hacerlo con el valor False, se usar FindFirst. Lo mismo da: Buscar False que Buscar (sin parmetros) Esto ltimo: lo de no especificarse o si se especifica con el valor False, es algo que hay que tener en cuenta si el parmetro fuese de tipo Variant (cosa obligatoria si usas VB4), ya que IsMissing slo nos informa si el parmetro no se ha especificado, pero si se especifica, puede hacerse con un valor False o True, por tanto, tambin habra que tenerlo en cuenta... Ya s que dije que lo iba a dejar para otra entrega... pero, que haces si ests usando VB4 dejar aqu el curso? As que vamos a ver el cdigo de Buscar para usar con VB4 o con las versiones 5 y 6 pero usando el tipo Variant. ' Private Sub Buscar(Optional ByVal vSiguiente As Variant) ' Procedimiento para buscar el dato indicado (12/Feb/01) ' ' Si Siguiente = True, se busca a partir del registro activo ' Si no se indica, (valdr False), buscar el primer registro '

575

' Cuando el parmetro es de tipo Variant, ' no se puede indicar un valor por defecto, ' as que vamos a usar una variable interna para indicar el valor del parmetro Dim Siguiente As Boolean ' Dim nReg As Long ' ' Asignar correctamente el valor del parmetro indicado ' Si no se especifica, le asignamos el valor por defecto, en este caso: False If IsMissing(vSiguiente) Then Siguiente = False Else ' Si se indica, asignarlo a la variable interna Siguiente = CBool(vSiguiente) End If ' El resto del cdigo es igual que antes ' Buscar la primera coincidencia en el recordset del Data1 ' If Option1.Value Then nReg = Val(Text4) ' ' Si se busca el siguiente dato If Siguiente Then Data1.Recordset.FindNext "Au_ID = " & nReg Else Data1.Recordset.FindFirst "Au_ID = " & nReg End If End If If Option2.Value Then ' ' Si se busca el siguiente dato If Siguiente Then "'" Data1.Recordset.FindNext "Author Like '" & Text4.Text & ' en el campo Author ' en el campo Au_ID ' Convertir el contenido de TextBox en un nmero

576

Else "'" End If End Sub Fjate que he cambiado el nombre del parmetro y he creado una variable interna que es la que posteriormente se usa para saber si se busca desde el primero o desde el anterior. En el caso de que no se haya especificado el parmetro, la comprobacin de IsMissing se cumple, por tanto aqu le indicaremos el valor que queramos que tenga por defecto, en nuestro ejemplo no es necesario asignar ningn valor, ya que False es el valor que tendr la variable Siguiente si no le asignamos ningn valor. Por tanto en este ejemplo, podramos haber hecho slo lo siguiente, sin necesidad de usar el IsMissing: Siguiente = CBool(vSiguiente) Pero lo he mostrado de la forma recomendable, para que sepas qu hacer en el caso de que el valor por defecto fuese otro del que Visual Basic asigna a las variables por defecto. Data1.Recordset.FindFirst "Author Like '" & Text4.Text & End If

Una vez visto el procedimiento de bsqueda, vamos a aadir dos nuevos botones para Aadir y Eliminar registros. Recuerda lo que te dije al principio: haz una copia de la base de datos para que las pruebas no afecten al contenido de la misma. Aade dos nuevos botones y asignales los nombres cmdAdd y cmdBorrar, as como los captions Aadir y Eliminar, el cdigo ser el siguiente: ' Private Sub cmdAdd_Click() ' Aadir un nuevo registro Data1.Recordset.AddNew End Sub Private Sub cmdBorrar_Click() ' Eliminar el registro actual Data1.Recordset.Delete End Sub

Tanto cuando se aade como cuando se borra, se debera mover el registro actual para que los cambios tengan efecto en la base de datos, ya que si se aade un nuevo registro y el mismo no se actualiza, se pierde. Para probarlo, ejecuta el proyecto, pulsa en Aadir y directamente sin hacer nada ms cierra el ejecutable, (pulsando en la x del formulario o ventana). Ejecuta de nuevo el proyecto y si pulsas en el botn de ir al ltimo registro del Datacontrol, vers que no hay ningn registro nuevo.

577

Ahora vuelve a pulsar en Aadir y escribe lo que quieras en las cajas de texto del Nombre y Ao de nacimiento y pulsa en el botn de ir al primer registro y despus al ltimo (ya sabes que me refiero a los botones del Datacontrol), vers que ahora si se muestra dicho registro y si pulsas en el botn de ir al anterior, vers que se salta un nmero... ese es el que haba reservado para nosotros, pero al no actualizar los datos, se perdi en el limbo... Ser por estos detalles que algunos programadores prefieran usar cdigo puro y duro en lugar del Datacontrol? Puede... pero no creo que sea por este motivo... ya que esto mismo se puede mejorar y no es necesario abandonar el Datacontrol para que todo funcione ms o menos como debiera... Primero vamos a hacer que los nuevos datos tengan algo, para ello, le asignaremos la cadena Nuevo al nombre del autor y haremos que se desplace al ltimo registro, para que los datos sean permanentes, (si no modificamos algunos de los campos, el nuevo registro se perdera) En el caso de Eliminar, vamos a movernos al primer registro, para que el registro activo sea uno con informacin. Este sera el cdigo de los eventos Click de los dos botones: ' Private Sub cmdAdd_Click() ' Aadir un nuevo registro Data1.Recordset.AddNew ' Aadimos algn texto, para que no se pierda este registro Text2 = "Nuevo" ' Movemos al ltimo registro para que los cambios se hagan permanentes ' y se muestre el nuevo registro Data1.Recordset.MoveLast End Sub Private Sub cmdBorrar_Click() ' Eliminar el registro actual Data1.Recordset.Delete ' Movemos al primer registro para que los cambios se hagan permanentes ' (tambin podriamos haberlo movido al ltimo registro) Data1.Recordset.MoveFirst End Sub Toma nota de la forma en que se hara para mover al primer o al ltimo registro del Recordset. Tambin habra que tener en cuenta si hemos sobrepasado el principio o el final de los registros, cosa que ocurre cuando no hay ms registros, para ello podemos comprobar tanto EOF (End Of File, final del fichero) como BOF (Beginnig Of File, principio del fichero) y en caso de que sea cierto (True), avisar o simplemente no hacer nada...

578

Esto slo habra que hacerlo al eliminar registros, ya que al aadir se supone que habr al menos el que aadimos... ' Private Sub cmdBorrar_Click() ' ' Comprobar que hay registros, porque si no hay, dar error If (Data1.Recordset.EOF Or Data1.Recordset.BOF) Then ' Avisar de que no hay registros Else ' Eliminar el registro actual Data1.Recordset.Delete ' ' Movemos al primer registro para que los cambios se hagan permanentes ' (tambin podriamos haberlo movido al ltimo registro) Data1.Recordset.MoveFirst End If End Sub

Y esto es todo por hoy. En la prxima entrega veremos cmo acceder a bases de datos usando el ADO datacontrol. Nos vemos Guillermo P.S. Si quieres bajarte los ejemplos usados, pulsa este link. (basico35_cod.zip 3.11 KB)

Curso Bsico de Programacin en Visual Basic


Muy buenas, aqu estamos con una nueva entrega del Curso Bsico de Programacin con Visual Basic (el clsico) y dentro del plazo ese que me puse para que no pasara mucho ms de un mes entre cada una de ellas, a ver si lo sigo cumpliendo... En la entrega anterior, (te has fijado que casi en todas las entregas hago una referencia a la entrega anterior?), te dije que en esta nueva veramos cmo ampliar el funcionamiento de un control ya existente. Puede que pensaras que por fin! te iba a explicar cmo crear tus propios controles, pero... no voy a tratar de ese tema, ya que voy

579

a seguir con la creacin/definicin de clases. Aunque el tema ese de crear nuestros propios controles seguramente lo veremos en alguna entrega posterior... y digo "seguramente" porque no se si ese tema (cuantos temas) debera entrar en un "curso bsico", aunque la verdad es que el concepto de curso bsico creo que ya ha sido casi superado... o no? no s, en fin... A lo que vamos. Como vimos en la entrega anterior, podemos definir nuevos eventos en un formulario, en esa ocasin lo que hicimos fue aadir un formulario al proyecto y definir un evento, el cual podramos interceptar desde otro sitio. Pero en el caso de los controles, no podemos hacerlo de esa forma, por la sencilla razn de que no existe una clase especfica de un control en la que podamos aadir esos nuevos eventos. Por tanto, tendremos que crear una clase en la que se pueda manejar un control y ser en esa clase donde definamos nuevos eventos y tambin nuevas propiedades y mtodos. Esto mismo lo podemos hacer con los formularios adems de con prcticamente cualquier control. Para qu ampliar un control? Si realmente te hicieras esa pregunta, no se si decirte que te dediques a otra cosa... pero bueno, supongamos que te intriga el saber qu puede llevarnos a ampliar el funcionamiento de un control... Una de las razones podra ser que no te gusta el funcionamiento estndar de un control y quieres que haga ms cosas o que las que hace, las haga a tu gusto o a la forma que a ti te gustara. Por ejemplo, imagnate que quieres que una caja de textos te muestre los nmeros negativos en color rojo o que te permita indicarle que slo quieres que acepte nmeros o letras o... pueden ser tantas las cosas que se te podran ocurrir, que necesitaramos como mnimo una entrega para nombrarlas. En esta entrega, (no se si me tambin en la siguiente), vamos a ampliar el funcionamiento de una caja de textos no multilnea, a la que aadiremos algn nuevo evento y propiedades, as como tambin algn que otro mtodo... ya veremos qu le aadimos, pero sea lo que sea, te servir para saber cmo hacerlo y despus puedes ampliarlo o modificarlo a tu gusto. Creando una clase para ampliar un TextBox Vamos a empezar haciendo poca cosa con el TextBox ampliado por lo que seguramente te preguntars para qu hacer esto?, la respuesta es bien simple, para que no te compliques con cosas nuevas sin llegar a comprender lo bsico, que al fin y al cabo es de lo que se trata: aprender lo bsico para que ests preparado para hacer lo que quieras... o casi... Para probar lo que te voy a contar a continuacin, necesitaremos crear un nuevo proyecto. Al form que se crea junto con el proyecto, vamos a aadirle dos cajas de textos y cuatro etiquetas. Uno de esos textbox (Text2) ser el que usaremos para ampliarlo... Aunque en esta primera tentativa no lo vamos a ampliar, simplemente lo manipularemos mediante una clase y aprenderemos cmo usar ese control mediante la clase. Para ello, aade un mdulo de clase y dale el nombre cTextBoxEx. Lo primero que tendremos que hacer es crear una variable a nivel de mdulo que nos permita interceptar los eventos de un control de tipo TextBox, para ello aadiremos esta declaracin: Option Explicit ' creamos una variable para manejar el textbox

580

' la declaramos con WithEvents para que interceptemos los eventos ' y podamos adaptarlos a nuestro gusto Private WithEvents mText As TextBox Con esto lo que hacemos es declarar la variable mText para que sea del tipo TextBox, al estar declarada con WithEvents, podremos interceptar los eventos que produzca el textbox que est asociado a esa variable. Nota: Las variables declaradas con WithEvents, pueden ser privadas o pblicas, pero nunca se pueden crear arrays (o matrices) ni usarlas para instanciar directamente la clase con New. A continuacin vamos a declarar varios eventos pblicos, estos eventos estarn disponible en cualquier sitio en el que se utilice esta clase declarada con WithEvents. Estos eventos, simplemente sern unos cuantos de los que ya disponen todos los TextBox, pero vamos a usarlos para que sepas cmo manipularlos y, si as lo deseas, adaptarlos a tu gusto o necesidad. ' Los eventos pblicos que producir la clase ' Aqu se ha usado la misma definicin que los eventos originales ' pero podramos haberlos declarado como se nos antojara. Public Event Change() Public Event GotFocus() Public Event KeyDown(KeyCode As Integer, Shift As Integer) Public Event KeyPress(KeyAscii As Integer) Public Event KeyUp(KeyCode As Integer, Shift As Integer) Public Event LostFocus() Public Event MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single) Public Event MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single) Public Event MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single) Una vez que hemos definido los eventos que nuestra clase podr producir, tenemos que encontrar la forma de producirlos, en esta ocasin los produciremos cuando se produzcan en el objeto que la clase manipular, por tanto, en los eventos producidos en el TextBox, produciremos nuestros eventos. ' los eventos producidos por el TextBox original ' desde estos eventos se lanzarn los que la clase implementa ' por tanto, podemos cambiar ese comportamiento a nuestro gusto Private Sub mText_Change()

581

RaiseEvent Change End Sub Private Sub mText_GotFocus() RaiseEvent GotFocus End Sub Private Sub mText_KeyDown(KeyCode As Integer, Shift As Integer) RaiseEvent KeyDown(KeyCode, Shift) End Sub Private Sub mText_KeyPress(KeyAscii As Integer) RaiseEvent KeyPress(KeyAscii) End Sub Private Sub mText_KeyUp(KeyCode As Integer, Shift As Integer) RaiseEvent KeyUp(KeyCode, Shift) End Sub Private Sub mText_LostFocus() RaiseEvent LostFocus End Sub Private Sub mText_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single) RaiseEvent MouseDown(Button, Shift, X, Y) End Sub Private Sub mText_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single) RaiseEvent MouseMove(Button, Shift, X, Y) End Sub Private Sub mText_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single) RaiseEvent MouseUp(Button, Shift, X, Y) End Sub Es decir, simplemente usamos RaiseEvent seguido del evento que haya que producir. Esto, como puedes comprobar, es lo que habra que hacer o si lo prefieres, es la nica forma de hacerlo, al menos en este caso, ya que los eventos del objeto mText slo se

582

producen en la clase y no fuera de ella, ya que fuera, se producirn los que la propia clase haya implementado, esto lo comprobaremos en un momento. Ahora vamos a ver cmo indicarle a la clase el objeto del tipo TextBox que debe manipular. Para ello, crearemos un procedimiento (mtodo), el cual recibir un parmetro, que ser el TextBox que la clase debe manipular. A ese mtodo lo llamaremos de la misma forma que la clase... S, as es como se hace en C++/C# y que pasa? que podra haberlo llamado New como lo hace el VB.NET? pues no! adems de porque no me gusta, por la sencilla razn de que no podemos tener un mtodo que se llame igual que una palabra clave del Visual Basic. Veamos el "constructor" extra de la clase, y digo "extra", por la sencilla razn de que todas las clases tienen un constructor, es decir, un procedimiento que se ejecuta cuando se crea una nueva instancia de la clase (usando New), ese procedimiento es: Class_Initialize, pero al crear una nueva instancia de la clase, no se puede indicar ningn parmetro, por tanto tendremos que crear un procedimiento al que "forzosamente" haya que llamar para que la clase sepa que TextBox es el que manipular. Tambin veremos el cdigo del destructor de la clase, (Class_Terminate), es decir, el cdigo que se ejecutar cuando la clase ya no est referenciada por ningn objeto. ' IMPORTANTE: ' Hay que usar este mtodo para inicializar el control a ampliar Public Sub cTextBoxEx(ByVal value As TextBox) If TypeOf value Is TextBox Then Set mText = value Else Err.Raise 13, "cTextBoxEx", "El objeto debe ser del tipo TextBox" End If End Sub

Private Sub Class_Initialize() ' este procedimiento se produce al crear una nueva instancia de la clase. End Sub Private Sub Class_Terminate() ' este se produce cuando la clase ya no se utiliza ms. ' aqu liberaremos los recursos que estemos empleando. Set mText = Nothing End Sub

583

Como puedes ver en el comentario del procedimiento cTextBoxEx, es muy IMPORTANTE que se llame a ese mtodo antes de hacer nada con el objeto creado a partir de la clase; esto es as, por la sencilla razn de que si no lo hacemos, la variable mText no estar apuntando a ningn objeto. Tambin puedes comprobar que hago una comprobacin de que el tipo del parmetro sea un TextBox, para ello he usado la siguiente lnea: If TypeOf value Is TextBox Then Realmente no sera necesario, pero... as, si decides cambiar el parmetro a un tipo ms genrico, como por ejemplo Object, te asegurars de que se est asignando un objeto del tipo TextBox, que es el tipo de datos que la clase manipular. En caso de que el objeto no fuera del tipo adecuado, se producira un error indicando ese hecho, para ello usamos el mtodo Raise del objeto Err. En el procedimiento (realmente es un evento) Class_Initialize no hacemos nada, ya que, al menos por ahora, no necesitamos hacer nada; sin embargo en el evento Class_Terminate, asignamos a la variable mText un valor Nothing, para liberar recursos, aunque si no lo hacemos, ser el propio VB el que se encargue de liberar esos recursos, pero, a mi me gusta hacerlo de forma explcita... cosas mas. Ahora veamos el cdigo a usar en el formulario. Recuerda que tenemos dos objetos del tipo TextBox, uno de ellos, el Text2, ser el que nuestra clase manipular. De las cuatro etiquetas que te indiqu, dos de ellas se usarn para mostrar informacin de que se han producido los eventos. Tambin tendremos un botn para cerrar el formulario (cmdCerrar). Veamos el cdigo del formulario: Option Explicit Private WithEvents txtBoxEx As cTextBoxEx

Private Sub cmdCerrar_Click() Unload Me End Sub

Private Sub Form_Load() Set txtBoxEx = New cTextBoxEx txtBoxEx.cTextBoxEx Text2 End Sub

' Text1 ser un control TextBox normal Private Sub Text1_Change() lblTxt1 = " evento Text1_Change" End Sub

584

Private Sub Text1_GotFocus() lblTxt1 = " evento Text1_GotFocus" End Sub Private Sub Text1_KeyPress(KeyAscii As Integer) lblTxt1 = " evento Text1_KeyPress, con KeyAscii= " & CStr(KeyAscii) End Sub

' txtBoxEx ser nuestra clase, que manipular al Text2 Private Sub txtBoxEx_Change() lblTxt2 = " evento txtBoxEx_Change" End Sub Private Sub txtBoxEx_GotFocus() lblTxt2 = " evento txtBoxEx_GotFocus" End Sub Private Sub txtBoxEx_KeyPress(KeyAscii As Integer) lblTxt2 = " evento txtBoxEx_KeyPress, con KeyAscii= " & CStr(KeyAscii) End Sub Como puedes comprobar, este cdigo no tiene ningn misterio, ya que es as como habra que hacerlo y para ayudarnos, el propio Visual Basic nos informa de los eventos que produce el objeto indicado por la variable txtBoxEx, por la sencilla razn de que la hemos declarado con WithEvents. En lo nico que debes tener "cuidado" es hacer lo que se hace en el evento Form_Load, ya que en ese evento se crea la nueva instancia de la clase: Set txtBoxEx = New cTextBoxEx y se asigna el textbox que debe manipular: txtBoxEx.cTextBoxEx Text2.

Aadir nuevos miembros a la clase Ahora que tenemos "la base" de cmo habra que manipular un TextBox desde una clase, vamos a aadir nueva funcionalidad a esa clase, ms que nada para que tenga alguna razn el rizar el rizo, ya que si simplemente queremos tener la misma funcionalidad que tiene un TextBox normal, no hace falta crear ninguna clase intermedia... Empezaremos aadiendo un mtodo ToString, el cual se usar de la misma forma que lo hace su hermano Visual Basic .NET (o las clases de .NET Framework para ser ms precisos).

585

Lo que este mtodo har, ser devolver una cadena del contenido del TextBox, pero, para que tenga alguna "gracia", (ya que eso se puede hacer simplemente llamando a la propiedad Text del objeto), vamos a usar un parmetro, el cual indicar el formato que queramos que devuelva. Por ejemplo, si el contenido de la clase es una fecha, podra interesarnos que devuelva esa fecha en el formato dd/mm/yyyy o si es un nmero que podamos darle "formato", para ello usaremos la funcin Format$ del VB para que aplique el formato que le indiquemos. Veamos el cdigo del mtodo ToString de la clase cTextBoxEx: Public Function ToString(Optional ByVal formato As String = "") As String ' Si se produce algn error, ignorarlo On Error Resume Next ' ' devolvemos el contenido del text con el formato indicado, ' si es que se ha indicado algn formato If formato = "" Then ToString = mText.Text Else ToString = Format$(mText.Text, formato) End If End Function En la declaracin indicamos que el parmetro es opcional, por tanto, esta funcin se puede usar de dos formas: 1- sin indicar el parmetro 2- indicando el formato a aplicar al contenido del textbox En caso de que no se indique el parmetro, se asignar a dicha variable el valor por defecto que se indica en la declaracin, es decir: una cadena vaca. Este hecho se tiene en cuenta en la comparacin que hay dentro de la funcin y se har una cosa u otra, segn se haya o no indicado el parmetro. En el caso de que no se haya indicado, o si se haya indicado, pero sea una cadena vaca, se devolver el contenido de la propiedad Text del TextBox usado internamente. Si se indica un formato, este ser el que se use para devolver el contenido de dicha propiedad. Debido a que es posible que se use un formato errneo, utilizamos una captura de errores, de forma que si se produce un error, simplemente se contine con la siguiente lnea. Como sabemos, si declaramos una funcin o un procedimiento Sub de forma Public, ese procedimiento se convierte en un mtodo de la clase y por tanto lo podemos usar desde cualquier sitio. (S, ya se que esto lo sabes, pero es simplemente para recordrtelo y as poder tener tiempo para poner el ejemplo.) Veamos cmo usarlo desde el cdigo del formulario, para ello, aade un botn (CommandButton) al que llamaremos cmdFormato y aade este cdigo al evento Click: Private Sub cmdFormato_Click() lblTxt2 = txtBoxEx.ToString("dd/mm/yyyy")

586

End Sub Bien, ya tenemos la base y el conocimiento de cmo podemos ampliar un TextBox, ahora slo falta echarle un poco de imaginacin y adaptarlo a nuestras necesidades. Para ampliar ms la clase, vamos a aadirle una propiedad que indicar el tipo de datos que manipular la clase, esos datos podrn ser de tres tipos diferentes: -Normal, aceptar cualquier cosa, tal como lo hace el textbox de forma predeterminada -Numrico, slo aceptar nmeros, estos a su vez podrn ser con y sin decimales -Fecha, aceptar slo fechas o al menos lo intentar... Para indicar estos valores, vamos a declarar una enumeracin pblica en la clase, de forma que la propiedad usada para el tipo de datos, utilice los valores de esa enumeracin. Tambin declararemos una variable privada para mantener el valor de esa propiedad, aunque podramos haber declarado la propiedad como una variable pblica, pero esto no nos permitira hacer ciertas comprobaciones, como por ejemplo, comprobar que el valor asignado a esa propiedad es uno de los valores permitidos. Veamos cmo hacer todo esto y despus utilizaremos esa propiedad para que realmente slo acepte ese tipo de datos: ' enumeracin de los valores posibles para el tipo de datos Public Enum eTipo Normal Enteros Decimales Fecha End Enum ' variables (campos) privados para las propiedades de la clase Private m_Tipo As eTipo Este cdigo lo tendrs que aadir en la parte "General" de la clase y lo que sigue, lo puedes aadir al final del cdigo que ya tenemos: ' las propiedades de la clase Public Property Get Tipo() As eTipo Tipo = m_Tipo End Property Public Property Let Tipo(ByVal value As eTipo) ' aqu podramos comprobar que el tipo asignado sea el correcto Select Case value Case eTipo.Decimales, eTipo.Enteros, eTipo.Fecha, eTipo.Normal

587

' no hacemos nada Case Else value = eTipo.Normal End Select m_Tipo = value End Property El Property Let es el cdigo que se utiliza cuando asignamos un valor a la propiedad, por tanto ser aqu donde hagamos la comprobacin de que el valor asignado sea del tipo correcto, es decir, uno de los valores de la enumeracin. Para ello, usamos un bloque Select Case para comprobar esos valores y en el caso de que no sea uno de los permitidos, asignamos el valor Normal, que es el que tendr esa propiedad de forma predeterminada, por la sencilla razn de que al ser el primer valor de la enumeracin, valdr cero. Si quisiramos que dicho valor predeterminado fuese otro, lo podramos asignar en el constructor de la clase, tal como podemos comprobar a continuacin: Private Sub Class_Initialize() ' este procedimiento se produce al crear una nueva instancia de la clase. ' asignamos el valor predeterminado de la propiedad Tipo: m_Tipo = eTipo.Normal End Sub Pero como te he dicho, no es necesario, por la sencilla razn de que eTipo.Normal vale cero. Y aqu vamos a acabar esta entrega... no sin antes proponerte un ejercicio. Ese ejercicio consiste en tener en cuenta si el tipo de datos que manipular la clase es de tipo Decimal o Entero slo permita que se asignen nmeros... pero cuidado! los nmeros tambin permiten que se indique si es negativo y en caso de que sean nmeros decimales, podra ser necesario que admita valores de tipo "cientfico", es decir que el nmero puede contener la letra E o D, segn sea del tipo Single o Double. La solucin en la prxima entrega.

Curso Bsico de Programacin en Visual Basic


588

Aqu tienes la solucin al ejercicio propuesto en la entrega 46. Realmente lo nico que haba que aadir era el cdigo necesario a la pulsacin de teclas en la clase, para ello tenas que modificar el evento KeyPress del objeto mText que es el que se encarga de manipular el TextBox asociado con esta clase. Lo que hacemos (al menos lo que yo creo que habra que hacer) es: Comprobar el tipo asignado en la propiedad Tipo, de forma que si es del tipo Enteros se compruebe si se han pulsado las teclas: BackSpace (borrar hacia atrs), los signos ms (+), menos (-) y cualquiera de los dgitos de cero a nueve. En el caso de que el valor de la propiedad Tipo sea Decimales se comprueba adems si se ha pulsado la coma, el punto y cualquiera de las letras E o D (tanto en maysculas como en minsculas), de esta forma, aceptaremos cualquier tipo de valor decimal. Tanto si el valor de la propiedad Tipo es Enteros o Decimales, se utiliza un segundo Select Case para comprobar si es una de las teclas aceptadas o no, en caso negativo, asignamos un valor cero al parmetro recibido (KeyAscii) para que no tenga en cuenta la tecla pulsada, y si es una de las teclas aceptadas, simplemente no hacemos nada, con lo cual permitimos que se acepte la pulsacin de esa tecla. Y siempre se obliga a que se produzca el evento KeyPress de esta clase, para ello utilizamos la ltima lnea: RaiseEvent KeyPress(KeyAscii), ya que este ser el evento que la clase que hayamos declarado en el formulario recibir, por tanto en muy importante no olvidar de aadir esta produccin del evento al final del procedimiento. En ese evento ya estar filtrada la tecla pulsada, de forma que si la tecla no es vlida, no se recibir... bueno, si se recibe, pero al ser un valor cero, no se tiene en cuenta. Por ltimo, si el valor asignado a la propiedad Tipo no es ninguno de los dos que hemos comentado, admitir cualquier tecla y esa tecla ser la que se enve al evento del objeto creado en el formulario desde el que se utilice nuestra clase.

El cdigo completo es el siguiente: Private Sub mText_KeyPress(KeyAscii As Integer) Select Case m_Tipo Case eTipo.Enteros Select Case KeyAscii Case 8, 43, 45, 48 To 57 ' Slo admitir teclas consideradas numricas ' El 8 es la tecla Backspace (borrar hacia atrs) ' Los cdigos 43 y 45 son los signos + y respectivamente Case Else

589

' No es una tecla numrica, no admitirla KeyAscii = 0 Beep End Select Case eTipo.Decimales Select Case KeyAscii Case 8, 43 To 46, 48 To 57, 68, 69, 100, 101 ' Slo admitir teclas consideradas numricas ' El cdigo 44 es la coma y el 46 es el punto ' La E y D son para nmeros con notacin cientfica ' (68 y 100 es la E y e, 69 y 101 es D y d) ' El 8 es la tecla Backspace (borrar hacia atrs) ' Los cdigos 43 y 45 son los signos + y respectivamente Case Else ' No es una tecla numrica, no admitirla KeyAscii = 0 Beep End Select End Select RaiseEvent KeyPress(KeyAscii) End Sub

Espero que esto sea lo que ms o menos hayas hecho, aunque no es necesario que sea exactamente como aqu te lo he mostrado, ya que existen otras formas de hacer lo mismo, lo importante es que se hayan hecho estas comprobaciones, me refiero a aceptar las teclas mencionadas, para que se acepten los datos que hemos definido, es decir: nmeros, tanto decimales como enteros.

Otra cosa es aceptar valores de fecha, en ese caso habra que aceptar, adems de las cifras, los separadores de las fechas y tambin podramos hacer que al pulsar Intro en el textbox se comprobara si la fecha es correcta y esas cosas... pero eso lo dejo a tu gusto... si revisas entregas anteriores, vers que tenamos cdigo para comprobar fechas... si no recuerdo mal. De todas formas, es posible que en otra ocasin te de mi solucin para que tengas un punto de referencia de cmo hacerlo, pero eso no ser ahora... ya veremos cuando! Nos vemos Guillermo

590

Curso Bsico de Programacin en Visual Basic

Seguramente pensars que hay muchas cosas que explicar de Visual Basic, y tienes razn, pero debido a que el ttulo de este "tutorial" es Curso Bsico de Programacin en Visual Basic, no pretenders que te lo explique todo... s, ya s que eso es lo que "el Guille" debera hacer... pero... Tampoco te creas que es que quiero "liquidar" ya este curso de Visual Basic... que por ahora no lo voy a dar por terminado, pero... (ya van dos peros), en fin... que tampoco se puede explicar todo... y aunque parezca que no, pero en este curso ya son muchos los conceptos que se han explicado, no todos, lo s, pero si los suficientes como para que te arriesgues "un poco" e investigues por tu cuenta y riesgo... No, que no, que no voy a "pasar" del curso bsico, simplemente te estoy "medio" advirtiendo que no abarcar todos los temas, as que... intenta buscar ms documentacin u otros cursos por Internet y no lo dejes todo de manos del Guille, que no lo vas a tener todo... aunque tambin te recomiendo que eches un vistazo a las muchas cosas que hay publicadas en mi sitio sobre VB que alguna te servir... espero! Bueno, despus de este prrafo "liante" vamos a ver que es lo que podemos hacer... o mejor dicho, que es lo que me gustara explicarte en las entregas que seguirn en un futuro prximo en esto que empieza ahora y que poda ser la segunda parte del Curso Bsico de Programacin con Visual Basic.

Introduccin
Lo que me gustara explicarte son cosas como la creacin de controles ActiveX (OCX) y libreras (DLL) ActiveX, que al fin y al cabo son conceptos bastante similares, cuando las explique vers que es as. Tambin tengo pensado, siguiendo en esta mima lnea de "automatizacin" de Visual Basic, a explicarte cmo modularizar o "trocear" tus aplicaciones en componentes ActiveX, de forma que puedas crear componentes (controles o libreras ActiveX) que sean totalmente funcionales y que puedas usar de forma independiente en distintas aplicaciones. Para no confundirte mucho, vamos a empezar a repasar algunos "conceptos" de los que hablaremos en esta y prximas entregas, para que cuando te encuentres con esos "trminos" no te hagas un lo... (traduzco: no te confundas).

Componentes.

591

Empecemos por explicar qu significa eso de componentes. Segn la documentacin de la MSDN Library que se incluye con Visual Studio 6.0 (VB 6.0), un componente es: Cualquier software compatible con Automatizacin, por lo que puede usarse mediante programacin en una aplicacin personalizada. Incluye controles ActiveX, servidores de Automatizacin basados en Visual Basic y servidores de Automatizacin basados en Visual C. Antes de "explicarte" o aclararte que significa esto, veamos lo que dice la documentacin sobre Automatizacin: Una tecnologa que permite que las aplicaciones proporcionen objetos de una forma coherente a otras aplicaciones, herramientas de programacin y lenguajes de macros. Ahora que tenemos definidos dos de los conceptos que aparecen en la documentacin de Visual Basic, te explicar con lenguaje lo ms llano posible qu significa esto de los componentes. Desde la versin 4.0 de Visual Basic, este lenguaje permite crear aplicaciones compatibles con lo que antes se llamaba automatizacin OLE, (OLE: Object Linking and Embedding, vinculacin e incrustacin de objetos), ese nombre ahora es ms conocido como automatizacin COM (o simplemente como COM). Las siglas COM significan Component Object Model que traducido sera algo as como modelo de objetos componentes. Siguiendo mi bsqueda de definiciones en las diferentes "documentaciones" de Microsoft, me he encontrado tambin con esta otra sobre COM: COM es el "modelo de objetos" fundamental sobre el que se generan OLE (Object Linking and Embedding, Vinculacin e incrustacin de objetos) y los controles ActiveX. COM permite que un objeto exponga su funcionalidad a otros componentes y aloje aplicaciones. Define cmo el objeto se expone a s mismo y cmo funciona dicha exposicin en procesos y en redes. Adems, COM define el ciclo de vida del objeto. No es que no quiera explicarte lo que significan todas estas "siglas", es que prefiero que leas lo que la documentacin "algunas" veces te dice y despus te lo aclaro, para que lo digieras mejor... si es posible. Lo que debe quedarte claro, es que un componente es un "trozo" de cdigo que podemos usar en cualquier aplicacin. La peculiaridad de los componentes es que son totalmente operativos por s mismos, te aclaro esto para que no pienses que al decir que es un trozo de cdigo, una rutina (funcin o procedimiento) podra ser un componente, ya que eso no es totalmente cierto. Los componentes COM son programas que permiten ser utilizados desde otro programas mediante automatizacin. Por regla general, los componentes suelen ser libreras (o si lo prefieres bibliotecas) ActiveX (DLL), controles ActiveX (OCX) e incluso ejecutables ActiveX (EXE). Nota: Puede que en esta y siguientes entregas, simplemente utilice la palabra componente, pero cuando lo haga me referir a componentes de automatizacin

592

(o componentes COM), ya que en el mundo de .NET tambin se usa el trmino componente y su uso no se refiere precisamente a un "objeto" de automatizacin. No hace falta que te diga que no es lo mismo un EXE ActiveX que un EXE normal, por la sencilla razn de que si un EXE normal fuese lo mismo... desde hace tiempo que estaramos creando componentes COM. Igualmente no es lo mismo una DLL normal que una DLL ActiveX, ya que una DLL "normal" simplemente tiene funciones que podemos usar en nuestras aplicaciones, pero estas se usan directamente, sin la intervencin de COM, tal es el caso de las DLLs del API de Windows las cuales podemos usar en cualquier programa. Por otro lado las DLL ActiveX son libreras que se pueden usar slo con lenguajes que puedan trabajar con Automatizacin OLE (o COM). Visual Basic est totalmente "adherido" a la automatizacin, es decir, entiende cmo crear objetos contenidos en componentes COM (de automatizacin) y, lo ms importante, tambin puede crear componentes COM para que puedan ser usados desde otros lenguajes "adheridos" a la automatizacin. Por tanto, Visual Basic podr crear libreras (DLL) de automatizacin (componentes COM), pero no podr crear libreras "normales". Aclaro este punto, porque a pesar de que la extensin sea la misma para una librera normal y una de automatizacin, nuestro querido VB no podr crear ficheros con la extensin DLL que se puedan usar de la misma forma que las libreras del API de Windows o las creadas por compiladores como C/C++ e incluso Delphi. Las libreras creadas por Visual Basic siempre son libreras de automatizacin. Despus de estas aclaraciones, vamos a seguir con las explicaciones de porqu pueden ser tiles estos componentes COM, aunque cuando hable de componentes (o componentes COM) me estar refiriendo normalmente a "cdigo" que podemos usar desde cualquier aplicacin de automatizacin, y ese "cdigo" normalmente estar compilado en la forma de una librera (DLL). Realmente no es "cdigo", al menos como se entiende por cdigo cuando se dice "cdigo fuente", sino a cdigo compilado o cdigo binario, es decir, un cdigo fuente que se ha compilado.

Qu ventajas tiene la creacin y utilizacin de componentes? Si has llegado a esta entrega nmero 47, (sin saltarte ninguna), ya habrs estado usando algunos componentes de automatizacin, por ejemplo ADO (ActiveX Data Objects) es un componente de automatizacin, el cual nos permite crear objetos para poder acceder a las bases de datos. Tambin habrs usado "componentes" en la forma de libreras del API de Windows. Y por supuesto que habrs usado "componentes" en la forma de controles OCX. Por tanto la utilidad de usar "componentes" es obvia, si ya hay alguno que nos sirva para nuestra aplicacin, lo usamos y no tenemos que programar ni una lnea para tener la funcionalidad que ese componente nos ofrece. Por otro lado, la creacin de un componente nos permite escribir un cdigo que "posiblemente" nos servir para usarlo en ms de una aplicacin, adems de que ese mismo componente podemos distribuirlo para que otros programadores puedan usarlo. El "posiblemente" lo he entrecomillado, porque si creamos un componente no quiere decir

593

que "forzosamente" tengamos que usarlo en ms de una aplicacin, ya que es muy posible que simplemente lo usemos una vez y nada ms, pero eso no es un impedimento para que lo "encapsulemos" en un componente (librera DLL o control OCX). Cual sera la ventaja de crear un componente aunque slo nos sirva para una aplicacin? La ventaja es que si en un futuro queremos hacer cambios en el cdigo de ese componente, slo tendremos que distribuir esa librera y no el resto de la aplicacin, incluso (aunque ese tema no lo trataremos en este curso) puede ser que ese componente se ejecute desde un servidor, con lo cual simplemente actualizndolo en el servidor, el resto de las aplicaciones "cliente" que lo utilicen estarn actualizados "a la ltima". La ventaja tambin es evidente, ya que si no tuvisemos el cdigo "troceado", tendramos que compilar toda la aplicacin, de esta forma slo tendramos que compilar el componente y distribuirlo... cuando digo distribuirlo, en la mayora de los casos, simplemente ser "copiar y pegar" esa librera en el directorio que estaba y nada ms. S, ya se lo que estars pensando, y si an no lo ests pensando, este pensamiento te "llegar" cuando sepas algo de la creacin de componentes ActiveX, y como no es plan de esperar el tiempo necesario para que sepas algo de componentes, te explico cual sera ese pensamiento al que me refiero: Pregunta/pensamiento: Si tengo que compilar el componente y distribuirlo, qu problema hay con compilar toda la aplicacin y distribuirla? Respuesta de tu "subconsciente Guille": Pues ninguno. Precisamente por eso te lo "recalco", porque como no hay ninguna diferencia, (salvo que el componente sea uno que resida en un servidor y entonces si que habra diferencia), puedes pensar que para qu complicarme la vida haciendo componentes, cuando es mejor escribir todo el cdigo junto, que con total seguridad me dar menos quebraderos de cabeza? Y si tienes este ltimo pensamiento "pasars" de crear y usar componentes, y si es eso lo que va a ocurrir... que puetas, (ltimamente el Guille se est volviendo algo ms recatado y ahora en lugar de decir coo, dice puetas, que es casi lo mismo pero suena menos grosero), hago yo aqu tratando de explicarte todo esto? Pues eso, aunque no los uses, o no pretendas usarlos, emppate de qu va todo esto de los componentes y "aprende" a usarlos aunque pienses que no te ser realmente til. A la larga lo agradecers, de verdad. Es como lo de usar Option Explicit, cuando te acostumbras a declarar todas las variables, ya no puedes "vivir" sin declararlas, lo mismo que cuando te acostumbras a indentar el cdigo, si no est indentado, parece que ni lo entiendes... (Este Guille no tiene remedio! tena que aprovechar la coyuntura para darte un par de consejillos de los suyos.)

Otra de las cosas que vamos a ir viendo con todo esto de los componentes, sern cosas relacionadas con la programacin orientada a objetos, aunque si bien es cierto que el Visual Basic no es un lenguaje orientado a objetos, podemos hacer cosas que "casi" parezcan... por supuesto estoy hablando del Visual Basic 6.0 (y anteriores), ya que el Visual Basic .NET si que es un lenguaje orientado a objetos. Todo esto es debido a que la creacin de componentes ActiveX nos permitir entrar un "poco" en esa dinmica, por aquello de que la creacin de componentes nos permitir "granular" nuestra aplicacin en trozos (que al fin y al cabo sern clases que se convertirn en objetos). Ya se que esto no ser Programacin Orientada a Objetos, (as en maysculas o en negrita), pero al menos te dar una idea de cmo podra ser... y sobre todo, como es ahora mismo si te atreves a entrar en el mundo de .NET, que al fin y

594

al cabo debera ser el lenguaje que deberas usar, as que... no se que haces perdiendo el tiempo en este curso si ya tenas que estar en el de Visual Basic .NET. Dicho todo esto, quiero que prepares cuerpo y mente para lo que seguir y, si ves que no ests totalmente preparado, (o preparada), es que va siendo hora de que empieces de nuevo por la entrega uno de este curso, te leas la ayuda del Visual Basic o te dediques a otra cosa, ya que... mejor o peor, (segn quien opine), en las 46 entregas anteriores te he explicado un montn de cosas, las cuales, si las has "aderezado" con alguna otra lectura, seguro que te permitir empezar a adentrarte en el mundillo de la programacin con Visual Basic, que al fin y al cabo es lo que este "curso" ha pretendido y pretende.

Bueno, ya est bien de charla y vamos a empezar con algo prctico.

A la vuelta con las clases.


A partir de la entrega 37 empezamos a tratar el tema de las clases, y en las ltimas entregas lo hicimos con ms o menos profundidad, espero que con todo lo dicho sobre ese tema, no tengas dudas sobre qu son las clases y cmo se usan en los proyectos de Visual Basic, ya que todo esto de los componentes ActiveX est muy relacionado con las clases y la creacin de objetos (instanciacin) en la memoria a partir de una clase.

Nota: Te recomiendo que si el tema de las clases no lo tienes claro, te repases las entregas 37, 38, 39, 39.2, 42, 43, 44, 45 y 46, ya que para poder crear componentes de automatizacin es fundamental el conocimiento de cmo definir una clase, adems de cmo crear nuevas instancias (objetos en memoria) de una clase.

De todas formas, vamos a repasar rpidamente los conceptos ms importantes. Repaso rpido sobre las clases. Para escribir el cdigo de una clase, hay que usar un mdulo especial, el cual le indicar al Visual Basic que el cdigo insertado en l es una clase. Este tipo de mdulos tendrn la extensin .cls.

Para poder usar una clase, tenemos que crear una referencia a esa clase, esto se hace declarando una variable cuyo tipo ser el nombre que le hemos dado a la

595

clase. Dim miClase As cClase

Cuando declaramos una variable con el tipo de datos de una clase, realmente no tenemos nada, simplemente una variable "capaz" de manejar un objeto del tipo de la clase.

Para poder usar esa clase, tenemos que crear una nueva instancia en la memoria, esto lo conseguimos mediante un cdigo similar a este: Set miClase = New cClase

Si bien podemos declarar la clase y crear el objeto en una sola instruccin: Set miClase As New cClase Esto no es recomendable y NO DEBERAS HACERLO NUNCA, ya que el rendimiento de la aplicacin se vera afectado, adems de que podra provocar problemas "colaterales" que en ocasiones sera difcil de descubrir. Por qu se vera afectado el rendimiento? Porque cuando declaramos e instanciamos el objeto en la misma lnea, el Visual Basic aadir cdigo extra cada vez que se use esa variable para comprobar si el objeto ya existe, y en caso de que no exista lo crear "automticamente". Qu problemas aadidos podramos tener? Debido a esta auto-creacin de los objetos declarados con As New, puede ser que por "accidente" creemos un objeto sin que ese fuese nuestro propsito. Por ejemplo, si le asignamos un valor Nothing a la variable, (para que el objeto deje de existir), y volvemos a usarlo, (incluso para saber si el contenido es Nothing, lo cual indicara que ya no existe), el Visual Basic lo volver a crear (instanciar), con lo cual nunca sabramos si lo hemos eliminado, ya que al comprobarlo, lo estamos volviendo a crear... Y lo que es peor, con el uso de ese tipo de objetos nos acostumbraramos a "mal usarlos", ya que, al saber que

596

se auto-crean, podramos escribir cdigo que no sera "lgico" (hablando programticamente).

Cualquier "variable" declarada como pblica dentro de una clase, se convierte automticamente en una propiedad de esa clase. Este tipo de "elementos" de las clases, en otros lenguajes se llaman campos, pero el Visual Basic (realmente el COM) le da un tratamiento de propiedad, cosa que podemos comprobar si usamos Implements (ver la entrega 44).

Para asignar un objeto a una variable, siempre debemos usar Set variable = Objeto, ya que el SET es lo que le dice al VB que nuestra intencin es asignar un objeto y no la propiedad que la clase tiene por defecto. El concepto de cmo crear una propiedad (o mtodo) que ser el que se usar por defecto, se explic en la entrega 42. Por ejemplo, si la propiedad por defecto de la clase a la que apunta la variable miClase es Nombre, al hacer esto: miClase = s Lo que estamos haciendo es asignando a la propiedad Nombre del objeto miClase el contenido de la variable s. Pero si la variable s realmente es otro objeto y lo que queremos que se asigne a miClase es el objeto referenciado por s, habra que hacer esto otro: Set miClase = s

Cuando asignamos, mediante Set, un objeto a otro, no estamos haciendo una copia del objeto, lo que le estamos diciendo al compilador es que ahora la variable indicada despus de Set tambin apunte al mismo objeto que existe en la memoria, por tanto slo existir un objeto en memoria, pero existirn varias variables que hagan referencia a ese objeto. Esto es importante, ya que cualquier cambio que hagamos en el objeto

597

referenciado mediante cualquiera de las variables que apuntan a dicho objeto, se reflejar en todas las variables que apunten a ese objeto, ya que slo habr un objeto creado en la memoria. En la entrega 39 y tambin en la 42 se explic esto al hablar del mtodo Clone.

Todas las clases tienen dos procedimientos "especiales" que se usan al estilo de los eventos (al menos por la forma en que se las llama en Visual Basic), que nos pueden servir para saber cuando se crea una clase y cuando se destruye. Si necesitamos hacer algo "justo" cuando se crea una clase, podemos escribir cdigo en el "evento" Class_Initialize, (esto es lo que en OOP se llama el constructor). Si necesitamos hacer algo cuando se destruya la clase (realmente el objeto creado en la memoria), se llama al "evento" Class_Terminate, (esto sera el destructor de la clase... o casi). Estos procedimientos los podemos usar para, por ejemplo, en el caso del "constructor", asignar valores que deben tener las propiedades por defecto, crear objetos que la clase usar, etc. Y en el caso del "destructor" para eliminar las referencias a los objetos que nuestra clase necesite. En las entregas 38 y 42 se usaban estos procedimientos para crear/destruir los objetos del tipo Collection que la clase necesitaba.

Los elementos o miembros que una clase puede contener son: Constantes, en forma de enumeraciones o declaradas directamente con Const. Propiedades, declaradas como variables pblicas o mejor an, (recomendado), declarndolas como procedimientos Property. Mtodos, declarados como procedimientos Sub o Function. Eventos, esta ser la forma de notificar al programa que est usando nuestra clase de que ha ocurrido algo digno de tener en cuenta.

598

Los elementos de una clase pueden estar declarados como Private, Friend o Public. Private, estos elementos slo sern visibles dentro de la propia clase. Friend, estos elementos sern visibles dentro de la clase y por instancias creadas desde cualquier parte de la mima aplicacin (realmente desde el mismo proyecto en el que se encuentre nuestra clase). Public, ser visible en cualquier parte.

Para terminar, recordarte que los formularios tambin son clases, especiales, pero clases al fin y al cabo, y podemos usarlas de la misma forma como usaramos cualquiera de las que nosotros codifiquemos.

Creo que con este repaso, ms o menos, recordars las cosas ms importantes sobre las clases. De todas formas, algunos de estos conceptos los iremos "repasando" a lo largo de esta y siguientes entregas, incluso puede que aparezcan algunos nuevos y si no nuevos, puede que los explique un poco ms a fondo... ya veremos.

Para terminar esta entrega, vamos a crear un componente (una librera ActiveX) y veremos cmo podemos "probarla" y usarla desde otros proyectos.

Crear un grupo de proyectos.


El entorno de Visual Basic (a partir de la versin 5), nos permite tener mltiples proyectos abiertos. Esto nos permite poder crear, por ejemplo, un componente, adems de poder crear un ejecutable "normal" que nos permita usar ese componente. Para no complicarte la vida con este primer intento en la creacin de un componente, vamos a crear una librera Activex. Esta librera tendr una clase (como todas las libreras ActiveX) la cual nos permitir crear un objeto desde una aplicacin normal. Esa clase tendr dos propiedades: Nombre, Versin (esta ltima ser de solo lectura) y un mtodo: HoraActual. Aqu veremos, paso a paso, cmo crear este grupo de proyectos, cmo escribir el cdigo de la clase (que como te imaginars no ser nada del otro mundo) y cmo aadir un nuevo proyecto para que podamos probar la librera creada, adems de cmo referenciar a esa librera, que como comprobars, se hace de la misma forma que con el resto de "componentes" COM (o ActiveX).

599

Empecemos abriendo del Visual Basic, en este ejemplo voy a usar el VB6, si no tienes el VB6, podrs usar el VB5, pero no te servir el VB5CCE, (ver el apndice A), ya que el VB5CCE no permite crear libreras DLL ActiveX, lo siento. En otra ocasin veremos cmo crear un control ActiveX y en esa ocasin si podrs usar el VB5CCE. Cuando nos pregunte el tipo de proyecto a crear, seleccionaremos ActiveX DLL, (ver la figura 1)

Figura 1, Dilogo para crear un nuevo proyecto

Se crear un proyecto llamado Project1 (al menos en la versin inglesa de VB6 que es la que tengo instalada) Ese proyecto tendr una clase, llamada Class1. Lo primero que debemos hacer es cambiar el nombre de la clase y del proyecto. A la clase la vamos a llamar cEntrega47, al proyecto lo llamaremos Entrega47AX. Es importante que el nombre de la clase y el del proyecto se llamen de forma distinta. Para poder cambiar el nombre de la clase, selecciona la clase en el Explorador de proyectos y en la ventana de propiedades, escribe el nombre en la propiedad Name (ver figura 2). Si tienes el VB5, habr menos propiedades mostradas. Haz lo mismo con el proyecto, selecciona el proyecto en el Explorador de proyectos y cambia la propiedad Name, que ser la nica que se muestre.

600

Figura 2, propiedades de la clase Adems de la propiedad Name (nombre) del proyecto, tambin podemos configurar otras cosas, pero eso lo veremos en otra ocasin. Ahora vamos a aadirle los "miembros" que tendr la clase. Nota: Aqu voy a usar "las recomendaciones", aunque en este caso podra usar el camino corto por aquello de que es un ejemplo trivial, no quiero que ya desde este momento te vayas acostumbrando a las "malas" formas de programar. Para crear propiedades, puedes usar el "asistente" del VB, o bien escribir el cdigo directamente. Si has ledo el Apndice A, en el que te explicaba cmo configurar el entorno del VB, sabrs que me gusta tener en la barra de herramientas un botn (o icono) para crear nuevos procedimientos. Pero si no tienes esa opcin en la barra de herramientas, puedes acceder a ella mediante la opcin del men Tools>Add Procedure... (Herramientas>Aadir procedimiento...). Se mostrar un cuadro de dilogo como el mostrado en la figura 3.

Figura 3, Cuadro para aadir una nueva propiedad a la clase

601

Y se crear la propiedad con el nombre que hemos indicado, en este caso, la propiedad Nombre. Fjate que cuando creas una propiedad de esta forma, siempre ser del tipo Variant, (ver figura 4), por tanto tendrs que cambiar el tipo para que sea del adecuado, en este caso ser del tipo String.

Figura 4, el cdigo creado por Add Procedure... El cdigo ser el siguiente: '----------------------------------------------------------------------------' Clase cEntrega47 (22/Ago/03) ' Prueba para el Curso Bsico de Visual Basic ' ' Guillermo 'guille' Som, 2003 '----------------------------------------------------------------------------Option Explicit ' las variables privadas que se usarn con las propiedades Private m_Nombre As String ' La propiedad Nombre Public Property Get Nombre() As String

602

Nombre = m_Nombre End Property Public Property Let Nombre(ByVal newValue As String) m_Nombre = newValue End Property ' La propiedad Version ser de slo lectura Public Property Get Version() As String Version = "1.00.0001" End Property ' El mtodo HoraActual devuelve la fecha y hora actual Public Function HoraActual() As Date HoraActual = Now End Function Ahora vamos a aadir un nuevo proyecto, en este caso ser del tipo Exe normal. En el men File (Archivo) selecciona Add project... (Aadir proyecto...) se mostrar el cuadro de dilogo mostrado en la figura 5 (que es parecido al de la figura 1), selecciona Standard EXE (recuerda que mi VB est en ingls).

Figura 5, Aadir un nuevo proyecto Al pulsar en "Open" (Abrir) se crear un nuevo proyecto, el cual se aadir al Explorador de proyectos (ver figura 6).

603

Cambia el nombre del proyecto para que tenga el nombre tEntrega47, el nombre del formulario lo vamos a dejar como est, es decir, se llamar Form1.

Figura 6, El explorador de proyectos Si te fijas, este grupo de proyectos se llamar Group1. Este nombre puedes cambiarlo cuando lo vayas a grabar o tambin desde el men File>Save Group Project As... (Archivo>Guardar Grupo de Proyectos Como...) Ya tenemos nuestros dos proyectos. Pero el que los dos proyectos estn "juntos" no quiere decir que el uno sepa del otro. Realmente actuarn como si no se conocieran, es decir, no podremos usar la clase cEntrega47 desde el proyecto de prueba, ya que el proyecto de prueba (tEntrega47) no conoce la existencia de la librera Entrega47AX. Para que nuestro proyecto de pruebas se entere de la existencia de esa librera, tendremos que aadir una referencia a la misma, (igual que haramos para aadir una referencia a los objetos de ADO). Por tanto, tendrs que seleccionar ese proyecto en el Explorador de proyectos y en el men Project (Proyecto), seleccionar la opcin References... (Referencias...) y de las que te muestre, selecciona la que nos interesa, ver figura 7:

604

Figura 7, Referencias del proyecto EXE de prueba. A partir de este momento ya podemos crear objetos del tipo cEntrega47, porque el proyecto del ejecutable "sabe" de la existencia del proyecto de la librera. Cosa que podemos comprobar si declaramos, en la ventana de cdigo del formulario, una variable que haga referencia a la clase cEntrega47 (ver figura 8).

Figura 8, Intellisense nos muestra las clases que contiene la librera Entrega47AX Con ese cdigo mostrado en la figura 8, tenemos una variable que sabe lo que hacer con un objeto del tipo cEntrega47, es decir, que puede tener una referencia a un objeto del tipo cEntrega47. Para que "realmente" tenga una referencia a un objeto real de la memoria, tendremos que instanciarlo usando New, (como de costumbre), cosa que haremos en el evento Form_Load del formulario, ahora veremos el cdigo, ya que antes vamos a aadir unos controles al formulario para poder probar la clase definida en el componente.

605

Aade unas etiquetas para que nos muestre la versin y la fecha y hora actual, aunque para que muestre la fecha y hora, tendremos que pulsar en el botn. El aspecto del formulario en tiempo de diseo ser el mostrado en la figura 9:

Figura 9, El formulario en tiempo de diseo Ahora slo queda mostrar el cdigo del formulario y probar que todo funciona bien. Antes de probar que todo funciona bien, veamos el cdigo. '----------------------------------------------------------------------------' Formulario de prueba para la clase cEntrega47 (22/Ago/03) ' Prueba para el Curso Bsico de Visual Basic ' ' Guillermo 'guille' Som, 2003 '----------------------------------------------------------------------------Option Explicit Private prueba47 As Entrega47AX.cEntrega47 Private Sub cmdFechaHora_Click() Label4.Caption = prueba47.HoraActual End Sub Private Sub Form_Load() Set prueba47 = New cEntrega47 Label2.Caption = prueba47.Version End Sub

606

Fjate que la variable declarada a nivel de mdulo (en el formulario), se ha declarado como del tipo Entrega47AX.cEntrega47, pero tambin se poda haber declarado como As cEntrega47, esto no es una caracterstica de nuestro proyecto, sino de todos los componentes ActiveX, ya que no es necesario indicar el "componente" en el que est la clase, salvo que sepamos que puede haber "conflictos de nombres", es decir, dos clases que se llamen de igual forma, pero que estn en distintos componentes, (esto suele ocurrir cuando tenemos referencias a DAO y ADO, en ambos existe un objeto llamado Recordset). En el Form_Load se instancia (se crea el nuevo objeto) usando el nombre de la clase, sin especificar el del componente, esto no es buena prctica, en este caso te lo muestro para que sepas que los dos cEntrega47 son la misma clase, pero para seguir con las buenas prcticas de programacin y, sobre todo, para ser consistentes, deberamos usar la forma larga o corta, pero no mezclarlas. Para probar que todo funciona, tendramos que pulsar F5 o bien en el botn "play" de la barra de herramientas. Pero como resulta que el primer proyecto que hemos agregado al grupo de proyectos es la librera, se mostrar un cuadro de dilogo preguntndote que quieres ejecutar... es decir, que no se puede ejecutar una librera as por las buenas... por tanto deberamos indicarle al Visual Basic que el proyecto que queremos probar es el ejecutable. Para hacer esto, hay que decirle que el proyecto de inicio es tEntrega47, (se mostrar en negrita en el explorador de proyectos), para poder indicar cual ser el proyecto de inicio, tenemos que hacerlo usando el men contextual (pulsa con el botn derecho o secundario del ratn) en el proyecto tEntrega47 y del men que se muestra, selecciona Set As Start Up (ver la figura 10)

Figura 10, indicar cual ser el proyecto de inicio Una vez que le hemos indicado al VB que el proyecto de inicio es tEntrega47, podemos pulsar F5 y se mostrar el formulario (como en cualquier proyecto "normal"), vers que se muestra la versin de la clase (la que hemos dicho que muestre), y si pulsamos en el botn, se mostrar la fecha y hora actual. Y esto es todo por hoy. Espero que, aunque de forma orientativa, ya sepas cmo crear (o al menos probar en el

607

IDE de Visual Basic), un componente ActiveX y lo que es mejor, cmo poder usarlo desde otro proyecto. Slo me queda aclararte que como an no lo hemos compilado, slo podremos crear referencias si ese otro proyecto, que queremos que use el componente, est en el mismo grupo de proyectos que el del componente. En otra ocasin veremos cmo compilarlo y cmo configurar la utilizacin de ese componente, cosa que mucha gente no explica y que puede tener sus ms y menos en cuanto a rendimiento... o casi... Pero eso ser en otra ocasin, que ahora me tengo que ir con la parienta, que est algo pachuchilla con un resfriadillo de estos de verano, y cuando las parientas estn as, necesitan ms cariete de lo normal... Nos vemos Guillermo

608

Vous aimerez peut-être aussi