curso básico de programación en visual basic

728
Curso Básico de Programación en Visual Basic Apéndice A Instalar y configurar el Visual Basic 5.0 Control Creation Edition Introducción En vista de que algunos de los que leéis este Curso Básico de Programación en Visual Basic, (clásico, es decir para versiones anteriores a la versión de .NET), no tenéis el entorno de desarrollo de VB por aquello de que trabajáis también con el Visual Basic incluido en el Office o porque ya no podáis conseguir "legalmente" el Visual Basic, ya que no se sigue vendiendo, he decidido crear un apéndice en el cual te explico cómo 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 mayoría de los casos será igualmente útil y válido para los IDEs de VB5 y VB6, en el caso de VB4 la cosa varía, pero me imagino que a estas alturas serán 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, sólo 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,

Transcript of curso básico de programación en visual basic

Page 1: curso básico de programación en visual basic

Curso Básico de Programación en Visual Basic

Apéndice A

Instalar y configurar el Visual Basic 5.0 Control Creation Edition

Introducción

En vista de que algunos de los que leéis este Curso Básico de Programación en Visual Basic, (clásico, es decir para versiones anteriores a la versión de .NET), no tenéis el entorno de desarrollo de VB por aquello de que trabajáis también con el Visual Basic incluido en el Office o porque ya no podáis conseguir "legalmente" el Visual Basic, ya que no se sigue vendiendo, he decidido crear un apéndice en el cual te explico cómo 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 mayoría de los casos será igualmente útil y válido para los IDEs de VB5 y VB6, en el caso de VB4 la cosa varía, pero me imagino que a estas alturas serán 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, sólo 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 podrán 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 código de algunas de las entregas de este curso, seguramente tendrás que quitar algunos de los valores incluidos en el fichero de proyecto (vbp), por regla general, puedes borrar los que hay

Page 2: curso básico de programación en visual basic

a partir de VersionProductName.Si no quitas esos valores del vbp, el VB5CCE te dará error y no cargará el proyecto.

¿Cómo conseguir el VB5 CCE?

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

Nota del 19/Oct/2006:

Según parece el VB5CCE ya no está, es decir, está desaparecido totalmente.Así que, te recomiendo que te vayas olvidando del VB "clásico" y te pases al .NET.

En .NET tienes el Visual Basic 2005 Express Edition que es totalmente gratuito y operativo al 100%, además 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 también te recomiendo que empieces a ver la sección de WinFX porque será lo que en un futuro próximo se empezará a usar.

Este es el link del curso de Visual Basic .NET (válido para todas las versiones de Visual Basic para la plataforma .NET)

Por supuesto, lo que aprendas con este curso básico 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 están 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 página) y otras cambian de sitio.Por suerte, esta vez aunque la página del VB5CCE ha desaparecido, al menos queda el instalador y la ayuda, que gracias a Ángel Rosario de República Dominicana, ahora puedes bajar.

Estos son los links a la fecha de esta nota:El programa de instalación:http://download.microsoft.com/msdownload/sbn/vbcce/vb5ccein.exe Y esta es la ayuda:http://download.microsoft.com/download/5/2/5/5253a7ea-1310-40d8-b762-625c2019310e/ccehelp.exe

IMPORTANTE: Ver la nota del 19 de Octubre.

Page 3: curso básico de programación en visual basic

Es posible que esa dirección 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 también puede ocurrir.

Una vez que estés en la página de descarga del VB5CCE, tendrás varios links, uno de ellos es el que se baja el programa instalador, pero también habrá otro link para la ayuda y documentación.El programa de instalación del VB5CCE se llama: vb5ccein.exe 7.13 MBEl programa de instalación de la ayuda se llama: ccehelp.exe 2.77 MBTambién hay varios ficheros con documentación, pero aquí no lo vamos a tratar, aunque te recomiendo que también te los descargues, ya que, aunque estén en inglés, de algo te servirán... que después te puedes quejar de que no trato todos los temas...

Instalar el VB5 CCE

Empecemos instalando el VB5CCE, para ello simplemente tendrás que ejecutar el vb5ccein.exe y seguir los pasos que se te indiquen (que serán los habituales en cualquier instalación, 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 tendrás que indicar el directorio (o al menos comprobar que el que te muestra es el correcto).Al instalar el VB5CCE se crearán los accesos directos en el menú de inicio, pero al instalar la ayuda no se crea ningún acceso a dicha ayuda, pero estará instalada correctamente en el directorio VB5CCE de Archivos de Programas (Program Files en sistemas Windows en inglés, como el que yo uso).Si quieres tener un acceso directo a la ayuda, busca el fichero vb5.hlp en el directorio de instalación.

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 espectáculo...

Nada más empezar, te mostrará un cuadro de diálogo como el mostrado en la figura 1:

Page 4: curso básico de programación en visual basic

Figura 1, El cuadro de diálogo de inicio de VB5CCE

Este cuadro de diálogo te permite indicar el tipo de proyecto que quieres crear, si marcas la opción 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 página WEB.Standard EXE (EXE estándar), un programa normal y corriente.CtlGroup (Grupo de proyectos), por defecto crea un control ActiveX y un EXE estándar.

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.

Page 5: curso básico de programación en visual basic

Figura 2, El IDE del VB5 CCE después 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 versión), la configuración 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 versión de VB.

Page 6: curso básico de programación en visual basic

En la figura 2 tenemos varias ventanas acopladas.Arriba está la barra de herramientas con los menús y otros "comandos" (los botones debajo de los menús).A la izquierda tenemos la barra de herramientas con los controles que podemos añadir 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 posición del formulario, pero yo suelo cerrarla.Abajo está la ventana Inmediate, (permite ejecutar comandos directos), ésta se puede mostrar seleccionándola 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 dónde se posicionará el formulario.Por último, en el centro tendremos las ventanas de diseño de formularios/controles, así como las ventanas de código.Éstas se pueden posicionar como queramos y esa posición será recordada cada vez que abramos el proyecto. La información sobre el tamaño y posición se guarda en un fichero con la extensión vbw.

Configurar el IDE de VB5CCE

Empecemos configurando las opciones generales.

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

Figura 3, Opciones, ficha Editor

Page 7: curso básico de programación en visual basic

En la ficha Editor, lo primero que debes seleccionar es Require Variable Declaration, (la he señalado para que no tengas excusas de saber dónde está esa opción), esto hará que en los nuevos proyectos creados se añada a cada módulo (formulario, módulo BAS, clase, etc.) la instrucción Option Explicit, ya sabrás que si quieres llevarte bien conmigo, tendrás que usar siempre esa instrucción, de esta forma te evitarás muchos problemas, además de que así siempre tendrás 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í estarán los puntos más juntos (el valor por defecto es 120).También puedes quitar la marca de Compile On Demand ya que esto hará que siempre se compile todo el código 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.

Page 8: curso básico de programación en visual basic

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 guardarán los cambios realizados en el código... ¡nunca se sabe si se quedará colgado el IDE o no!

Page 9: curso básico de programación en visual basic

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 cómo quedaría el IDE de VB5CCE en modo SDI.

Page 10: curso básico de programación en visual basic

Figura 7, El IDE de VB5CCE en modo SDI

Configurar la barra de herramientas

Ahora vamos a añadir algunas opciones a la barra de herramientas superior.Entre las cosas que añadiremos son:Comentar/quitar comentarios de un bloque de códigoAsignar/quitar/ir a marcadores dentro del códigoBloquear los controles de un formularioCrear un nuevo procedimiento.

Para poder configurar la barra de herramientas, así como los menús del IDE de VB5CCE, tendremos que seleccionar la opción View>Toolbars>Customize..., se mostrará el cuadro de diálogo mostrado en la figura 8:

Page 11: curso básico de programación en visual basic

Figura 8, Cuadro de diálogo Customize, ficha Commands

De la lista de la izquierda (Categories) selecciona Edit, en el cuadro de la derecha se mostrarán las opciones relacionadas con la edición. Selecciona Comment Block (esta opción te permite comentar el bloque de código seleccionado) y arrástralo con el ratón hasta la barra de herramientas, (verás que el cursor del ratón cambia para que sepas dónde vas a soltar el elemento seleccionado), suéltalo en la posición 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 código, (se indicará con una banderita en la barra derecha del código, tal como puedes ver en la figura 9), y después navegar entre las distintas marcas que tengas en el código 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.

Page 12: curso básico de programación en visual basic

Figura 9, Cómo se indican los bookmarks en la ventana de código

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 línea de separación entre las distintas opciones (o comandos), simplemente arrastra hacia la derecha el icono (o botón) que esté a la derecha de

Page 13: curso básico de programación en visual basic

donde quieras dejar esa separación. Por ejemplo, si quieres dejar una separación entre el comando de quitar comentarios y el de asignar marcador, tendrás que arrastrar la banderita azul un poco hacia la derecha.Por otro lado, si quieres quitar alguno de los que ya hay (o hayas añadido), simplemente arrástralos desde la barra hasta el cuadro de diálogo.

Ahora añade el resto de comandos. De las categoría 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

Fíjate que algunas opciones sólo estarán disponibles (habilitadas) cuando se puedan usar.

Bueno, dejemos aquí esta primera parte del Apéndice A, en otra ocasión veremos para que sirven algunas de las opciones de los menús y de los botones de las barras de herramientas, así como algunas otras configuraciones y, sobre todo, cómo agregar controles a un formulario, cómo añadir nuevos elementos al proyecto, cómo añadir nuevos proyectos (para poder crear multi-proyectos), etc., pero eso será en otra ocasión.

Nos vemos.Guillermo

Curso Básico de Programación en Visual Basic

Apéndice A.2Añadir controles a un formulario

Introducción

En esta segunda parte del Apéndice A del Curso Básico, vamos a ver cómo añadir controles a nuestros formularios y cómo configurar esos controles, cambiar los valores de las propiedades, etc.Esto es algo que hace años tendría que haber hecho, lo sé, pero yo es que antes era un poco más inocente que ahora, y pensaba que los lectores de este curso se iban a tomar un poco de más interés en aprender estas cosas usando la información (o documentación) que acompañaba al Visual Basic... y creo que hace algún 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 deberías decir "presuntamente", para que nadie se sienta "acusado"), porque ahora la gente tiene más 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

Page 14: curso básico de programación en visual basic

acompañe "ningún" tipo de documentación... 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 cuestión es que, sea como sea, aquí están estos apéndices, los cuales intentarán "ayudarte" en la medida de lo posible a que aprendas "mejor" este querido, (al menos por mi), lenguaje de programación.

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 tendréis la oportunidad de poder "jugar" con él.En la primera parte de este Apéndice A te expliqué cómo conseguirlo, cómo instalarlo y como configurar algunas de las opciones y barras de herramientas, así que... si aún no te lo has leído, deberías 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 también será válido para el Visual Basic 5.0 e incluso para el Visual Basic 6.0, pero en algunos casos no será "tan evidente" para la versión 4.0, aunque en estas fechas, esa versión ya es más difícil 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... bájate el VB5CCE y prueba con lo que aquí voy a explicar o bien, intenta amoldarte a como el Office te presente el diseñador de formularios...La cuestión 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 diálogo para que seleccionemos el tipo de proyecto que queremos crear, (para más detalles, ver Iniciar el VB5CCE), para este ejemplo, elegiremos un proyecto normal (Standard EXE).

Nota:Si no se muestra el cuadro de diálogo 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 diálogo nunca más; si ese es tu caso, tendrás que crear un nuevo proyecto seleccionando New Project (Nuevo Proyecto) del menú File (Fichero) o bien pulsando la combinación de teclas Ctrl+N.

Al crear un nuevo proyecto de tipo EXE, tendremos un formulario llamado Form1.Por ahora vamos a dejar ese nombre para que sea más fácil 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 código.Para poder añadir controles al formulario o para cambiar la apariencia del mismo, tendremos que tenerlo a la vista, es decir, tendremos que mostrar el "diseñador de formularios".

Page 15: curso básico de programación en visual basic

Cuando queramos usar el diseñador de formularios, tendremos que seleccionar el formulario de la ventana de proyectos y pulsar la combinación de teclas Mayúsculas+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 código, 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 diseño, podemos pulsar F4 para mostrar la ventana de propiedades.En esta ventana (ver figura 2) se mostrarán las propiedades que podemos cambiar mientras estamos "diseñando" el formulario, es decir, no se mostrarán todas las propiedades, ya que algunas de ellas puede que no sean "configurables" en tiempo de diseño, es decir, que no afecten al aspecto visual.También hay que tener en cuenta de que, (como comprobaremos en un momento), en la ventana de propiedades se mostrarán las propiedades del "objeto" que esté seleccionado. En este caso, se mostrará sin problemas las propiedades del formulario por la sencilla razón de que no tenemos ningún otro "objeto" dentro del formulario (ni en la aplicación).

Page 16: curso básico de programación en visual basic

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 información de forma alfabética (Alphabetic) o bien por categorías (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 descripción de la propiedad que tenemos actualmente seleccionada. En el caso de la figura 2, la propiedad seleccionada es Caption y abajo nos dice (en inglés) que esa propiedad sirve para mostrar el texto en la barra de título.

Si quisiéramos que en la barra de títulos del formulario se mostrara otra cosa distinta a lo que actualmente se muestra, simplemente tendríamos que escribir en la casilla que está a la derecha de la propiedad el texto que queremos mostrar.

Las propiedades más importantes (o las más usadas) del formulario las iremos viendo con algo de detalle en este o en otros "apéndices". Lo importante es que sepas cómo poder cambiar los valores y, si te atreves, que vayas probando por tu cuenta y riesgo... y como resulta que ya sabes cómo cambiar (y dónde) cambiar esos valores, ahora no tienes excusa.

Page 17: curso básico de programación en visual basic

Para muestra un botón: La propiedad AutoRedraw nos puede servir cuando queremos mostrar las pruebas hechas en las primeras entregas del curso, en las que teníamos 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.

También decirte que en las versiones más recientes de Visual Basic hay más propiedades que en las versiones anteriores, por ejemplo, en la versión 5 tenemos algunas propiedades que no estaban disponibles en la versión 4.Tal es el caso de la propiedad que le indica a Windows dónde 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 posición 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 más de un formulario.

Pero dejemos las propiedades del formulario, (en otra ocasión intentaremos hacer un repaso de algunas de ellas y el efecto que conseguiremos al aplicar distintos valores), y sigamos viendo otras cosas.

Añadir controles al formulario.

Esta es otra de las cosas que con más asiduidad haremos, ya que no es habitual que un formulario esté vacío, sino que tenga controles.

Los controles más 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 también 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 imágenes (PictureBox).

Empecemos añadiendo una etiqueta, una caja de textos y un botón.Para añadir cualquier control al formulario, usaremos la barra de herramientas que está en la parte izquierda del IDE de Visual Basic (ver figura 3).

Page 18: curso básico de programación en visual basic

Figura 3

Si posicionas el ratón 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, también en la segunda fila y el botón es el que está a la derecha en la tercera fila.

Para añadir un control, podemos hacer doble pulsación (doble-click) sobre el elemento de la barra de herramientas y se añadirá al formulario, posicionándose en el centro del mismo y teniendo el tamaño predeterminado.También podemos pulsar una vez, y a continuación "dibujarlo" en el formulario, dándole el tamaño que queramos y posicionándolo también en el sitio que queramos.Por ahora vamos a elegir el primer método y después cambiaremos el tamaño y posición.

Haz doble-click en la etiqueta, esto añadirá una etiqueta al formulario, el nombre de ese objeto se llamará Label1, para cambiar la posición de la etiqueta Label1, podemos seleccionarla en el formulario y moverla con el ratón. Vamos a situarla en la parte superior izquierda, para que deje el centro del formulario libre, ya que el resto de los controles se posicionarán en el mismo sitio y si dejamos la etiqueta ahí y añadimos nuevos controles, los nuevos controles ocultarán la etiqueta.

A continuación añadiremos 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.

Page 19: curso básico de programación en visual basic

Por último añadiremos un botón, así que pulsa en el icono correspondiente y déjalo donde lo ha posicionado el diseñador de formularios, el nombre que tendrá será Command1.El aspecto del formulario con estos controles será el mostrado en la figura 4.

Figura 4

Fíjate en los nombres de los controles, que son los mismos que los "textos" mostrados. El diseñador 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 número, el cual se va incrementando cada vez que añadimos un nuevo control del mismo tipo. Si añadimos una nueva etiqueta (control Label), ¿qué nombre tendría? (la respuesta dentro de un ratillo).

Cambiar el tamaño y posición de los controles.

Por regla general, no se suelen dejar los controles con el tamaño que el diseñador le ha dado al insertarlos en el formulario, por tanto, será algo habitual el que tengamos que cambiar el tamaño de los controles.

Ese cambio del tamaño de los controles podemos hacerlo de dos formas distintas:

1. Usando el diseñador de formularios y el ratón, para ello simplemente

tendremos que seleccionar el control que queramos redimensionar,

mantener el ratón pulsado en el borde que nos interese usar para

cambiar el tamaño y ajustarlo a nuestro gusto.

Por ejemplo, para cambiar la altura del botón, pulsaremos en ese control,

y desde el borde inferior iremos cambiando el tamaño, por ejemplo, para

que tenga una altura de 375, ver la figura 5. Si queremos cambiar, en

una sola acción, tanto el ancho como el alto, podemos hacerlo desde una

Page 20: curso básico de programación en visual basic

de las esquinas del control, ver la figura 6.

Mientras se está cambiando el tamaño del control, se mostrará un

"ToolTip" con el nuevo tamaño.

2. La segunda forma de cambiar el tamaño es usando la ventana de

propiedades y usar las propiedades Height y Width, las cuales

cambiarán respectivamente la altura y anchura del control.

Figura 5Figura 6

Vamos a cambiar el tamaño de los controles para que tengan los que aquí te indico:La etiqueta (Label1), tendrá un tamaño de 1245 x 255 (ancho x alto)La caja de textos (Text1), tendrá un tamaño de 2895 x 315El botón (Command1), tendrá el tamaño 1245 x 375

Para cambiar la posición de los controles, como puedes imaginarte, también podemos hacerlo de dos formas, arrastrando el control hasta la nueva posición o bien asignando valores a las propiedades Left y Top, las cuales representarán los valores izquierdo y superior respectivamente.

Posiciona la etiqueta en la posición 180, 150 (izquierda, arriba), la caja de textos en la posición 1560, 120 y el botón déjalo en el mismo sitio que estaba: 1740, 1350.

Nota:Como te comenté en la primera parte del Apéndice A, podemos cambiar el espaciado de puntos del grid (o rejilla) del diseñador de formularios, en aquella ocasión 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 diseño a las posiciones que te indico, es posible que tengas que cambiar esos valores, (ver la figura 4 de la primera parte del Apéndice A).

Todos estos cambios son en tiempo de diseño, es decir, mientras estamos "diseñando" el formulario, ya que esos ajustes de tamaño también podemos hacerlo en tiempo de ejecución, es decir, cuando pulsemos F5 para ejecutar la aplicación.El sitio habitual para hacer esos cambios en tiempo de ejecución es en el evento Load del formulario o bien en el evento Resize del mismo. Aunque, la verdad sea dicha, no es común cambiar el tamaño de los controles, salvo en contadas ocasiones, por ejemplo, cuando queremos adaptarlos al nuevo tamaño 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 también las podemos usar en tiempo de ejecución para asignar un nuevo valor en cualquiera de ellas, También

Page 21: curso básico de programación en visual basic

quiero "recordarte" que existe un método en casi todos los controles (incluso en los formularios) que sirve para cambiar la posición y/o el tamaño de los controles de una vez (es decir con una sola instrucción); ese método es: Move y se usa de la siguiente forma:<control>.Move Izquierda, Arriba, Ancho, AltoEl único valor requerido es el primero (posición izquierda), pero si se especifica cualquiera de los otros tres, los anteriores también se deben especificar; aunque los siguientes no tenemos la obligación de indicarlos, además de que seguirán conservando los valores que ya tuvieran. Por ejemplo, si sólo queremos cambiar la posición del control, podemos indicar los dos primeros parámetros de ese procedimiento. Pero si además de la posición queremos cambiar el alto, estamos obligados a indicar también el alto... ya que no podemos dejar ese "hueco".Como consejo, decirte que es más "rápido" usar el método Move que asignar individualmente las propiedades.

Otra cosa, la posición siempre se considera relativa a la posición superior izquierda del formulario, la cual estará indicada por 0,0. El formulario a su vez estará posicionado relativamente a la pantalla (Screen), considerándose igualmente que la posición superior izquierda es la posición 0,0.

Añadir más controles y usar el diseñador para ajustar el tamaño y la posición.

Una vez que tenemos estos controles, pueden ocurrir dos cosas, que lo dejemos como está, porque no necesitemos más controles, o bien que queramos añadir más controles, y puede que hasta ocurra que lo que necesitemos sea añadir más etiquetas y cajas de textos, los cuales queremos que tengan el mismo tamaño que los que ya tenemos.Esto es algo habitual y muy común, por ejemplo si queremos hacer un formulario que pida datos al usuario.Para ver cómo solucionar de una forma fácil y rápida esta situación, vamos a añadir 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 párrafos, es decir, haciendo doble-click sobre el icono en la barra de herramientas, pero también podemos hacerlo de otra forma. Ahora veremos estos dos sistemas, para que practiques un poco y de paso te explico cómo usar algunas de las "facilidades" que nos da el diseñador de formularios.

Empecemos usando la forma "clásica", es decir hacer doble-click sobre el icono de la barra de herramientas.Añade una etiqueta y una caja de textos, estos dos controles estarán ahora posicionados en el centro del formulario, si no has cambiado el botón del sitio que estaba, la caja de textos habrá ocultado tanto la etiqueta como el botón. No te preocupes, ya que esto es algo que puede ocurrir y siempre es bueno saber cómo "salir del paso".Así que, seleccionaremos la caja de textos, para seleccionarla, simplemente pulsa UNA vez en ella, ahora haremos que tenga el mismo tamaño que la otra caja. Mantén 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 tamaño y hacer que las dos tengan el mismo tamaño,

Page 22: curso básico de programación en visual basic

podemos hacerlo de dos formas, (como viene siendo habitual), la primera, sería mostrando la ventana de propiedades y asignado a las propiedades Height y Width un valor, el cual se asignará a los controles que estén seleccionados, (esta es la forma que yo lo he estado haciendo durante un montón de tiempo, incluso trabajando con el VB5 y 6, la fuerza de la costumbre), pero hay otra forma más "práctica" y es mediante el menú Format (Formato), seleccionando el sub-menú Make Same Size (hacer del mismo tamaño), 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.

Figura 7, El menú Format

De ese mismo menú Format, podemos seleccionar Align (alinear) para que estén alineados a la izquierda, por tanto, selecciona la opción 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 estén "juntitos", pero no revueltos, para ello, selecciona Format>Vertical Spacing>Remove y los dos controles estarán 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 botón y "debajo" estará la etiqueta, como resulta que la etiqueta es más alta que el botón, debajo del botón 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 continuación Format>Align>Lefts y por último: Format>Vertical Spacing>Remove.

Page 23: curso básico de programación en visual basic

Truco:También 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 tamaño y con la misma posición izquierda, aunque demasiado pegados con sus compañeros, así que ahora, manualmente, vamos a cambiar la posición. Baja un poquito la segunda caja de textos y posiciona la etiqueta 30 puntos más 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) debería tener 540.

Ahora vamos a añadir una nueva etiqueta y una caja de textos, pero usando otro "truco".En este caso, también queremos que tanto la etiqueta como la caja de textos tengan el mismo tamaño que las dos últimas que acabamos de añadir, por tanto, selecciona la etiqueta y la caja de textos que está a su derecha; ya sabes cómo hacerlo: selecciona la etiqueta y manteniendo pulsada la tecla Control, pulsa en la caja de texto, aunque también puedes hacerlo de otra forma: primero pulsa en cualquier parte del formulario para quitar cualquier selección que hubiera, a continuación, con el ratón, selecciona la etiqueta y la caja de textos, (de la misma forma que seleccionarías varios objetos en cualquier aplicación, 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 botón secundario del ratón) y seleccionando Copy (Copiar).Ahora vamos a pegar lo que hemos copiado, ya sabrás cómo... (ver la figura 9)

Page 24: curso básico de programación en visual basic

Figura 9

Al pulsar en pegar, se nos mostrará un cuadro de diálogo preguntándonos si queremos crear un array de controles, (ver la figura 10), pulsa en el botón NO, ya que no queremos crear ningún array de controles. Primero te preguntará por si quieres crear el array de Label2 y después de Text2 (aunque puede que el orden sea al revés, pero eso no es 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 tendrán el mismo tamaño que los copiados, incluso estarán posicionados "relativamente" a la misma distancia, pero se pegarán en la parte superior del formulario, como estarán los dos seleccionados, podemos arrastrarlos hasta que estén en la posición 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 tendrán como nombre Label3 y Text3, pero mostrará el mismo "texto" que los dos que se usaron para la copia. Para cambiar el texto mostrado, tendrás que asignar un valor a la propiedad Caption de la etiqueta y para cambiar el texto mostrado en la caja de textos, tendrás que cambiar la propiedad Text.

Nota:Como puede ser que te haya ocurrido, sobre todo si no tienes mucha práctica con el ratón o bien porque tu ratón sea muy sensible, es posible que al "intentar" hacer un click en un control, realmente hayas

Page 25: curso básico de programación en visual basic

hecho un doble-click, si es eso lo que te ha ocurrido, verás que se muestra la ventana de código con el evento "predeterminado" de ese control, no te preocupes, simplemente cierra la ventana de código y vuelve a intentarlo.

Ahora ya sabes dónde 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 estás entretenido "jugueteando" con las opciones del menú Format, vamos a dejar aquí esta segunda parte del Apéndice A, entre otras cosas, porque te he explicado lo que quería explicarte: cómo agregar controles a un formulario, poder cambiarle el tamaño y posición, así como explicarte cómo cambiar otras propiedades... además de que ya se ha hecho un poquito larga... (je, je... es una excusa como otra cualquiera).

En otra ocasión veremos más cosillas que podemos hacer con el IDE de Visual Basic, así como otras cosas relacionadas con los controles y formularios.

Espero que estos apéndices vayan rellenando las lagunas que se hayan quedado en las entregas "normales".No te voy a adelantar más cosas que contendrán estos apéndices, pero si te diré que no se quedarán en cosas relacionadas con el IDE (entorno de desarrollo), pero... como siempre, tendrás que esperar para poder saber de qué tratarán... mientras tanto, sigue practicando y leyendo cosas sobre el Visual Basic.

Nos vemos.Guillermo

Curso Básico de Programación en Visual Basic

Tipos de proyectos de VB6, cómo compilarlos, y más...Apéndice B

Una batallita del Guille

Como bien sabrás, este Curso Básico de Programación con Visual Basic empezó siendo "genérico" para todas las versiones que había en aquel "lejano" 18 de Abril de 1997 (sí, hace muchos años ya), pero con el tiempo los temas tratados fueron centrándose más en la versión de 32 bits de Visual Basic 4.0 (con esa versión se podían crear

Page 26: curso básico de programación en visual basic

aplicaciones tanto de 16 como de 32 bits, con las versiones anteriores solo podíamos crear de 16 bits), después 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 serán para las versiones de .NET Framework.En las dos últimas versiones de Visual Basic (5.0 y 6.0) se introdujeron nuevas características que hacían (y siguen haciendo) muy potentes a este lenguaje, que a pesar de tener más de 15 años de edad sigue siendo el favorito de muchos programadores (o desarrolladores, según gustos).En este curso básico, he intentado enseñarte todo lo que puedes hacer con este lenguaje, aunque, como es natural, no lo he explicado todo, y no se si algún día conseguiré explicarlo todo, pero como ves, después de tres años, aún quiero seguir manteniéndolo vivo, ya que a pesar de lo que muchos predecían, la gente no acaba por pasarse a la versión de .NET de este lenguaje, la razón principal es porque aún 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 más.

Introducción

Y en este apéndice, (por no llamarlo entrega, que también podía llamarlo así, pero... he preferido que sea un apéndice, 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 también es válido para el 5.0, incluso para el 4.0, aunque en este último ya no se utiliza, además de que no permite crear todos los tipos de proyectos que veremos en este apéndice).También te explicaré cómo compilar esos diferentes tipos de proyectos y cómo configurarlos, ya que aún hay gente que se "queja" de que no saben a ciencia cierta para que sirven las diferentes opciones que tenemos en la configuración de los proyectos de Visual Basic.

Antes de seguir, te recomiendo que le eches un vistazo a los dos apéndices anteriores, en el primero (el apéndice A.1) te explico cómo crear nuevos proyectos usando Visual Basic 5.0 Crontrol Creation Edition (la versión gratuita de VB5); en el segundo (apéndice A.2) te explico cómo añadir 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 cómo configurar y compilar los distintos tipos de proyectos que podemos crear con Visual Basic 6.0.

Nota:Todas las capturas y explicaciones de este apéndice serán de la versión empresarial de Visual Basic 6.0 en español, pero lo explicado será igualmente válido para el resto de versiones y ediciones, ya que solo 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 cómo los tenemos que configurar.Por tanto, debemos iniciar el VB y por regla general nos mostrará la pantalla mostrada en la figura 1:

Page 27: curso básico de programación en visual basic

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, tendrás que seleccionar Nuevo proyecto del menús de Archivo.

Empezaremos creando uno del tipo EXE estándar, que será el más común, 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 apéndices anteriores.Ahora lo que veremos es cómo "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.

Page 28: curso básico de programación en visual basic

Figura 2. Configurar las propiedades del proyecto

Al seleccionar esa opción tendremos a la vista un cuadro de diálogo como el de la figura 3.

Page 29: curso básico de programación en visual basic

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.Veámosla cada una de ellas.

En la parte superior izquierda tenemos un cuadro de diálogo con el Tipo de proyecto que hemos seleccionado, por defecto tendrá el valor EXE estándar (o el que hayamos elegido al crear el nuevo proyecto), de esa lista podemos elegir cualquier otro que nos interese, que serán los cuatro que te comenté antes:EXE estándar, 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 aplicación.También podemos seleccionar Sub Main, si es que queremos que la aplicación empiece por el método Main. Ese método o procedimiento debe estar declarado en un módulo de tipo BAS, por ahora no hablaremos de este método pero lo haré en otra ocasión, por ejemplo para crear una aplicación de VB6 que no tenga formularios.

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.

Page 30: curso básico de programación en visual basic

Si queremos que nuestra aplicación 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 botón con los res puntos suspensivos que hay a continuación. También 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 extensión .HLP y las que tienen extensión .CHM.

Nota:La forma de crear ficheros de ayuda para VB y cómo usarlas están explicadas en mi sitio, (y desde el índice principal puedes acceder a ellas), aunque es posible que algún día de estos las resuma y las incluya en el curso básico, ya veremos...

La casilla con la Descripción 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 estarán habilitadas o no, en este caso, está habilitada y seleccionada la de Actualizar controles ActiveX, esto vale por si tenemos añadido algún control ActiveX (.ocx) en nuestro proyecto y si se detecta que hay una nueva versión, se actualizará el proyecto para usar esa versión. 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.

Page 31: curso básico de programación en visual basic

Figura 4. Propiedades Generar del proyecto

En esta ficha tenemos los datos del Número de versión, que serán las que se muestren cuando pulsemos en las propiedades del ejecutable que creemos desde VB6. Si marcamos la casilla que hay debajo: Incremento automático, cada vez que creemos un nuevo ejecutable, se incrementará automáticamente el valor de Revisión. Yo normalmente lo tengo desmarcado, ya que añado mis números 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 versión es diferente a las anteriores.

En el grupo Aplicación tenemos el Título del proyecto y el formulario que se usará para el icono de la aplicación. En la lista desplegable que hay junto a Icono se mostrarán los formularios que tengamos y el icono que tenga el seleccionado será el que se use para nuestra aplicación. Por defecto estará el formulario usado como inicio de la aplicación.

En el grupo de Información de la versión tenemos varias opciones en Tipo, según las vayamos seleccionando, podemos escribir el valor en la casilla que hay bajo Valor. Los valores de tipo son los siguientes:

Tipo Descripción

Comentarios Descripción larga de la aplicación

Derechos de autor El copyright

Descripción del Descripción corta de la aplicación

Page 32: curso básico de programación en visual basic

archivo

Marcas comerciales legales

Las marcas comerciales de los productos usados con esta aplicación

Nombre de la compañía

El nombre de la compañía

Nombre del producto El nombre del producto

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

Figura 5. Las propiedades del ejecutable en Windiws XP

Page 33: curso básico de programación en visual basic

En Argumentos de la línea de comandos podemos indicar lo que queramos, de forma que ese valor sea el que se pueda interceptar mediante la función Command$ para simular lo que pasaría si el usuario de nuestra aplicación la indiciara indicando algo a continuación del nombre del ejecutable, ya sea desde una ventana de "DOS" o bien porque le ha agregado opciones después del nombre del ejecutable en un acceso directo.Por ejemplo, si la aplicación se inicia así: proyecto1.exe pepito.doc, el valor de la línea de comandos será "pepito.doc" y ese valor será el que se indique en Command$.Si se escriben más cosas, por ejemplo: proyecto1.exe Erase una vez que se era, todo lo que haya después del nombre del ejecutable será el contenido de la línea de comandos.

La casilla Argumentos de compilación condicional sirve para crear "constantes de compilación", las constantes de compilación son las que podemos usar con: #If CONSTANTE = VALOR Then.Esas constantes se tendrán 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 sería de esta forma: NOMBRE = VALOR : NOMBRE2 = VALOR, es decir, las usamos como si hiciéramos una asignación y si queremos definir más de una, las separamos con dos puntos.Esto es equivalente a definir esas constantes con la instrucción #Const NOMBRE = VALOR en todos los ficheros de nuestro proyecto.

Por último, la casilla Quitar información de controles ActiveX no usados, nos viene muy bien si hemos añadido "componentes" (controles .ocx) a la barra de herramientas de controles, pero que no hemos añadido en ninguno de los formularios, por tanto, cuando compilemos no se harán referencia a esos controles no usados.

Propiedades Compilar

En esa ficha de las configuraciones del proyecto tenemos las opciones para eso, para compilar nuestra aplicación.Las opciones disponibles son las mostradas en la figura 6, y te las detallo un poco a continuación.

Page 34: curso básico de programación en visual basic

Figura 6. Las opciones de compilación del proyecto actual

La primera opción es Compilar a P-Code, seleccionándola haremos que el compilador de VB cree un ejecutable en el que se utiliza "pseudo código". Esta compilación lo que hace es crear un ejecutable más pequeño, ya que prácticamente todo lo que contiene son llamadas a las funciones y código ejecutable del "runtime" de Visual Basic. En la mayoría de los casos, esta será la opción más conveniente, sobre todo si lo que nos interesa es que nuestro ejecutable no ocupe mucho.

El grupo de Compilar a código nativo nos da varias opciones que veremos ahora, la diferencia entre la compilación a código nativo y el P-Code es que en la compilación a código nativo se utiliza un compilador "de verdad", es decir, en lugar de usar funciones que llaman a las que están en el runtime, se sustituyen por código máquina, si bien el runtime de VB sigue utilizándose, ya que no todo el código usado se incluye en el ejecutable. Al compilar en modo nativo, el fichero ejecutable suele ser mucho más grande que el de P-Code, también suele tardar más en compilar, dependiendo de la cantidad de código que tengamos en el proyecto.Y a pesar de lo que puedas pensar no siempre el resultado de compilar a código nativo suele resultar en un ejecutable más rápido, independientemente de como hayas seleccionados las opciones extras de compilar a código nativo.

Cuando seleccionamos la opción de compilar a código nativo, tenemos otras sub opciones que nos sirven para "afinar" dicha compilación, las tres primeras nos permiten optimizar para un código más rápido, lo cual suele ir acompañado de un ejecutable aún más "pesado". La segunda opción es optimizar para código reducido, como te puedes imagina, en este caso se "opta" por reducir el tamaño del ejecutable aunque ello vaya en detrimento de la velocidad, por último, sin optimización, pues eso, no se optimiza ni para código rápido ni para reducido... ¡ni fu, ni fa!

Page 35: curso básico de programación en visual basic

Optimizar para Pentium Pro(tm), pues eso, optimiza el código para los Pentium Pro, en otros Pentium también funcionará, pero puede que el rendimiento sea menor... o eso dice la documentación, en fin...

Lo de Generar información para depuración simbólica, es por si queremos generar un fichero .pdb para poder usar desde Visual Studio (o Visual C++) para depurar el código una vez compilado.

Al pulsar en el botón Optimizaciones avanzadas, nos mostrará el cuadro de diálogo que podemos ver en la figura 7, yo prácticamente 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 recomendación es que no "trastees" aquí, y si lo vas a hacer, léete 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 opción final de la pantalla de configuración de compilación (figura 6), la que indica Dirección 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 extensión .ocx). Esta opción incluye un valor que por defecto es: &H11000000, ese valor será la dirección de memoria en la que se cargará esa DLL, la recomendación es que siempre usemos una diferente para nuestras DLLs, si al cargar la DLL en memoria, esa dirección 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 rogaría 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.

Page 36: curso básico de programación en visual basic

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 cómo funcionan los EXE ActiveX, y no creo que ese este el lugar idóneo 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).

Page 37: curso básico de programación en visual basic

¿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 más 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 también se puede usar como un control ActiveX, en el sentido de que simplemente proporciona esos objetos de automatización pero no se "ve" que se esté ejecutando (o en funcionamiento).Los ejemplos "clásicos" de componentes EXE ActiveX son las aplicaciones como Word o Excel, que tanto pueden hacer de componente ActiveX como de una aplicación normal y corriente.Pues bien, si seleccionamos la opción Independiente, le estamos indicando al compilador de VB que nuestra idea será usar este ejecutable como un EXE normal que es capaz de proporcionar objetos de automatización, es decir, se ejecutará de forma visible pero si la "referenciamos" desde otro proyecto podremos usar las clases que expone públicamente.Por otra parte, si seleccionamos Componente ActiveX, la aplicación se iniciará de forma "oculta", y solo proporcionará esas clases públicas que hayamos indicado en dicho ejecutable.

De la opción Servidor remoto no te voy a hablar, ya que nunca la he usado, así que...

En el cuadro Compatibilidad de la versión tenemos tres opciones y se utilizan para los componentes ActiveX (sean del tipo que sean), estas opciones indicarán cómo tratará el compilador el código 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 opción solo debemos seleccionarla si la queremos empezar "en limpio" un componente, y no será opción 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 usándolos 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 añadimos proyectos normales que lo utilicen, para más detalles sobre todo esto, léete la entrega 47.

Por último tenemos la opción de Compatibilidad binaria, en la que además 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 código "interno" de las clases que tenemos en ese componente, esos cambios se intentarán 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 más

Page 38: curso básico de programación en visual basic

"peliagudo" de lo que en este pequeño 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, dándonos 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 más, léete esto.

Y la última opción de este diálogo 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 nada, ¿que más da lo que contenga?Pues eso.

Compilar el proyecto

Para terminar, veamos cómo compilar el proyecto o proyectos que tengamos.

Como ya sabes, en el entorno de Visual Basic podemos tener más 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 opción Generar <nombre proyecto>... si es un proyecto independiente, bien el único que tengamos abierto o el que tengamos seleccionado si es que tenemos más de uno.En caso de que tengamos varios proyectos, podemos seleccionar la opción Generar grupo de proyectos... que como vemos en la figura 9, estará deshabilitado si no tenemos varios proyectos cargados.

Page 39: curso básico de programación en visual basic

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 extensión será .exe para los proyectos EXE estándar 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 opción nos mostrará un cuando de diálogo como el de la figura 10, en el que indicaremos el nombre que queremos usar y dónde guardarlo. Bueno, ese cuadro de diálogo solo se mostrará si ya hemos compilado (o generado) nuestra aplicación como mínimo 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.

Page 40: curso básico de programación en visual basic

Figura 10. Las opciones de generar proyecto

Si pulsamos en el botón Opciones... nos mostrará el cuadro de diálogo 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 más fácil... ;-))))

Pero no me quejo... o casi... je, je.

¡Hasta la próxima!

Nos vemos.Guillermo

A estas alturas no es fácil hacer un curso de introducción a la programación con Visual Basic. Si fuese a raíz de la primera o segunda versión, incluso con la tercera, sería más fácil. Pero ahora está más crudo, por aquello de que soporta 16 y 32 bits y te enfocas en una u otra dirección o...Pero como el título 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 básicos.Como este curso no se publica sobre algo que ya está terminado, iré haciéndolo sobre la marcha, espero tus indicaciones sobre si voy demasiado rápido o si realmente me estoy pasando en cuanto a 'simpleza'. De cualquier forma, me gustaría oír 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 tenía hechos, de cuando daba clases a niños y a no tan niños, de esto hace ya

Page 41: curso básico de programación en visual basic

10 añillos de nada)Lo advierto, a los que saben algo y a los que lo saben todo (o casi todo), lo que viene a continuación es super básico. Y pensando también en los que por una razón u otra no tienen unos conocimientos que a otros les parecerá absurdo, sé que aún hay gente que no tienen ni 'repajolera' idea de lo que es una expresión, una variable y ni que decir tiene sobre las matrices, los números binarios o la notación hexadecimal...

¿Que es una variable?En cualquier programa siempre necesitaremos hacer cálculos, usar información, procesarla y mostrarla.En la mayoría de los casos, necesitaremos un lugar temporal en el cual guardar parte de esa información, 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 más tarde. Si no seguimos unas normas o usamos unas reglas, de poco nos iba a servir el guardar esa información, si después no podemos almacenarla en ningún sitio, pero ese será tema de otro capítulo... ahora centrémosno 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 información que necesita o utiliza. Así pues, los lenguajes de programación usan también esa memoria para guardar información propia del lenguaje y del programa que queramos realizar.El programa una vez que se está ejecutando puede necesitar también guardar información, aunque esta información sólo estará disponible mientras se esté ejecutando. Esas posiciones o lugares de la memoria donde los programas pueden almacenar información son las variables. El que se llamen de esta forma es porque podemos hacer que el contenido de ese lugar de almacenamiento varíe, es como si tuviésemos una serie de cajas y en ellas pudiésemos guardar cosas, con la salvedad de que en cada caja sólo puede haber una cosa a la vez; aunque también veremos cómo hacer que el contenido pueda variar y que varíe dependiendo de lo que contenía antes...Veamos un ejemplo:Imagínate que quieres guardar tu nombre en una variable, para ello tendríamos 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 programación pone a nuestra disposición 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 tamaño adecuado y que tenga una 'forma', para que el papel no vuele al menor soplo de viento.Esta es la regla básica 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 información.Por tanto, si queremos guardar un nombre, (el nuestro, por ejemplo), en la memoria, podríamos 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 (posición de memoria) algo, debes hacerlo de la siguiente manera:---Pon el nombre con el que quieres llamar a esa parte de la memoria,

Page 42: curso básico de programación en visual basic

---a continuación pones el signo igual (=) y ---después lo que quieras guardar.Por tanto para guardar Guillermo en la variables nombre, tendríamos que hacer (o casi):Nombre = GuillermoPero esto podía llevar a confusión, ya que el Basic no nos dice nada sobre cómo debemos llamar (o si lo prefieres, cómo hay que escribir) el nombre de una variable, por tanto Guillermo también podría ser una variable, (es que el Basic, a pesar de que llevo tantos años 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 información alfabética, tendremos que indicarlo poniendo dicha información dentro de comillas dobles, el ejemplo quedaría así:Nombre = "Guillermo"Ahora no hay confusión posible, hemos seguido lo que el Basic nos ha dicho: variable, signo igual, valor a almacenar.Si queremos guardar un número en una variable, la cosa es más simple:Numero = 7

¿Te estás enterando?

Pero, ¿que ocurre si quisiéramos repetir 7 veces a Guillermo?Podrías hacer esto, multiplicar a Guillermo por 7 (como no hay bastante con uno...)Paliza = "Guillermo" * 7Pero el Basic te diría que eso no está bien, no porque Guillermo (yo) no sea un paliza, sino porque te diría que no coinciden los tipos (Type Mismatch)¿Que son los tipos? Los distintos tipos de datos. (que esfuerzo mental...)Los datos pueden ser, básicamente, de dos tipos:Numéricos: sólo números yAlfanuméricos: cualquier cosa, letras y/o números, 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 número 7 y en el segundo caso, es el literal (o palabra) "7"Así que cuando veas algo entrecomillado, piensa que no es un número, sino una palabra (más vulgarmente llamada cadena de caracteres o string en inglés)Hemos visto que no podemos multiplicar una palabra (cadena) por un número, pero si podemos multiplicar una variable por un número (siempre que la variable sea numérica, por supuesto)Según esto, el Basic debería permitir hacer esto:Guillermo = 5Paliza = Guillermo * 7El Basic tomaría el 5 y lo almacenaría en una variable numérica llamada Guillermo.Después se encuentra con: Paliza = Guillermo * 7 y aquí lo que hace es evaluar la expresión que está después del signo igual, lo calcula y el resultado lo guarda en la variable que está a la izquierda del signo de asignación (=)¿Expresión? Expresión 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 cálculo, de esta forma sabrá que tenemos una operación en la cual queremos multiplicar dos números, una vez que ha evaluado nuestra intención de multiplicar esos dos números, efectuará el cálculo y almacenará el resultado en... si no le decimos dónde, lo hará en una

Page 43: curso básico de programación en visual basic

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 * 2El Basic no sabrá que hacer con el resultado de esta 'expresión' (que por cierto es 10) y nos dirá:o te espabilas o lo tienes crudo conmigo.Así que lo más juicioso sería decirle: vale, vale, guárdalo en una variable, así que:Resultado = 5 * 2 guardaría un 10 en la variable Resultado.También podríamos decirle que nos mostrara el resultado, en lugar de guardarlo en una variable, y aquí llega nuestra primera instrucción: Print. Con ella le decimos al Basic que lo imprima (o sea que los muestre, más adelante veremos dónde), según lo dicho, haciendo esto:Print 5 * 2, el Basic diría que muy bien y mostraría un 10Pero, volvamos al Paliza del Guillermo, es decir al ejemplo de Paliza = Guillermo * 7Si quisiéramos mostrar el valor de Paliza, tendríamos que hacer algo como esto: Print Paliza, y nos mostraría 35, ya que el valor de Paliza sería 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 aún no lo has hecho).Te creará un Form nuevo, que estará vacío.Ciérralo y muestra la ventana de código.Mostrará la parte de las declaraciones Generales del Formulario.Si tiene escrito Option Explicit, (estará en la parte superior), bórralo, más adelante te explicaré para que sirve.Ahora sitúate en Form (selecciónalo de la lista desplegable que está a la izquierda), te mostrará:

Private Sub Form_Load()

End Sub

Sitúate en medio, es decir, en una línea en blanco después del Private... y escribe el ejemplo, quedaría así:

Private Sub Form_Load()

Show

Guillermo = 5

Paliza = Guillermo * 7

Print Paliza

End Sub

Page 44: curso básico de programación en visual basic

Pulsa F5, para ejecutar el programa, y verás 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 botón que tiene una X, o bien pulsa en el botón detener de la barra de herramientas del VB.Sitúate de nuevo en el código del Form_Load, escribe después de la línea del Print, lo siguiente:

Print Guillermo

Pulsa de nuevo F5 y verás que ahora además del 35, hay un 5 debajo. El valor de la variable Guillermo.Pero, ¿que ocurriría si cambiásemos el valor de Guillermo?Añade estas líneas a continuación 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

Después de pulsar F5, te mostrará los siguientes valores (cada número en una línea), 35, 5, 10, 35¿Esperabas que el último fuese 70?Fíjate que cuando asignamos a Paliza el contenido de Guillermo, éste era 5, por tanto el Basic evaluó la expresión 5 * 7 y almacenó el resultado (el 35). Una vez almacenado el resultado, el Basic se olvidó de dónde había sacado ese 5.Si queremos que se 'actualice' el valor de Paliza, tendremos que indicárselo de nuevo al Basic, para que vuelva a evaluar la expresión y hacer la correspondiente asignación. Para ello, pon en medio de los dos últimos prints la siguiente asignación:Paliza = Guillermo * 7Esta 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()

Page 45: curso básico de programación en visual basic

Show

Nombre = "Guillermo"

Print Nombre

End Sub

Pulsa F5 y verás que se muestra el contenido de la variable Nombre, es decir Guillermo.Prueba ahora con esto otro (es un clásico):Print "Hola Mundo"Y para rematar, y de camino ver otra posibilidad del Print, escribe en lugar del Print Nombre:Print "Hola " ; NombreEl punto y coma, se usa para indicarle al Basic que se deben mostrar las cosas una a continuación de la otra.Ahora te habrá mostrado: Hola Guillermo, fíjate que después 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 algún comentario, hazlo pulsando en este link. Así sabré que opinión te merece este primer capítulo del cursillo/tutorial o como prefieras llamarlo.

Nos vemos, (espero que pronto).

Nota: En todos los FORM_LOAD deberás 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 más crudo...

Seguimos con este cursillo básico. Antes de nada una pequeña aclaración, para que los ejemplos que se dieron en la entrega anterior, deberás 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 pequeña puntualización, vamos a la tarea.El tema de hoy será sobre el manejo de entrada de datos y una forma un poco más práctica 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 clásico", no había problemas. Las cosas se producían 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 "saltábamos" a otro sitio, pero era porque nosotros así lo habíamos programado. De una forma u otra teníamos control total sobre lo que podía 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...

Page 46: curso básico de programación en visual basic

Antes vamos a dejar un par de cosillas claras. Según como tengas configurado el entorno de desarrollo, habrás tenido problemas con los ejemplos de la entrega anterior. La razón es que el Visual Basic nos permite controlar un poco mejor el tema de las variables que queremos usar. Ese control lo da la instrucción: Option Explicit. Si ponemos esto en la parte destinada a las declaraciones de cualquier módulo, nos obligará a declarar las variables que vamos a usar en dicho módulo.En el último ejemplo de la entrega anterior, teníamos una variable llamada Nombre, en la cual almacenamos un nombre, por tanto podríamos haberle avisado a Visual Basic que reservara espacio para una variable, y para hacerlo usamos la instrucción DIM, con ésta le indicamos que nos guarde un "cachillo" de la memoria para nuestro uso:Dim NombreTambién nos ofrece una variante con respecto al "basic clásico" y es, precisamente, el tipo de datos variant. Con este tipo podemos asignar a una variable cualquier tipo de dato. Desde un número hasta una cadena de caracteres, pasando por cualquier tipo de objeto que Visual Basic pueda manejar (más o menos).

Los que hayan tenido experiencias anteriores, con Basic u otro lenguaje, sabrán que cada variable debe ser del tipo de datos que queramos asignarle. En VB por supuesto esto también es posible y recomendable.Ya vimos que los tipos de datos podían ser numéricos o de caracteres. Pero dentro de los numéricos, tenemos cuatro tipos básicos: enteros, enteros largos, simples y dobles. Cada uno de ellos tienen unas capacidades determinadas, además de ocupar más memoria o menos, (ahora lo veremos), lo más importante es que los números enteros y entero largo sólo admiten números 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 cómo 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 cómo declararlos y cuanto ocupa:

Tipo Espacio ocupado

Tipo de declaración

Ejemplo

Entero 2 bytes Integer Dim Numero As Integer

Entero Largo 4 bytes Long Dim Numero As Long

Simple 4 bytes Single Dim Numero As Single

Doble 8 bytes Double 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 más 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 máxima de una variable del tipo String será de aproximadamente 32.000 caracteres y la forma de declararla será:Dim Cadena As StringUna 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 sólo se queda el último

Page 47: curso básico de programación en visual basic

valor asignado. Ya lo vimos en la entrega anterior, pero vamos a refrescarlo un poco:

Dim Número As Integer

Número = 5

Print Número

Número = 12

Print Número

En este ejemplo, el último valor almacenado en Número es el 12. El 5 que tenía en un principio se perdió.Pero, ¿que tengo que hacer para sumarle una cantidad al valor almacenado en una variable?Es decir, ¿cómo incrementar el valor de una variable numérica?La respuesta la tienes en cómo se manejan las expresiones y las asignaciones a las variables. Como ya vimos anteriormente, al asignar a una variable una expresión, primero se calcula la expresión y una vez obtenido el resultado, se asigna a la variable.¿Recuerdas lo que ocurría 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 sería 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 solución, asigna ese valor, en este caso 30) a la variable que hay a la izquierda del signo igual.Sabiendo esto, podríamos simplificar la cosa y hacer los siguiente:

N = N * 3

Print N

También obtendríamos 30. Ya que cuando el Basic calcula la expresión de la derecha del signo igual, N vale 10, una vez obtenido el resultado del cálculo lo asigna a la variable de la izquierda del signo de asignación, sin importarle lo más mínimo de que variables es y como en este caso hemos usado la misma, pues se queda el último valor, perdiéndose el que originalmente estaba "guardado". Esto es útil cuando necesitamos "contar" de forma secuencial, para así incrementar el valor de una variable.

Page 48: curso básico de programación en visual basic

Ya veremos alguna "utilidad" para estos casos. Ahora vamos a ver cómo podemos manejar las cadenas de caracteres, es decir las variables de tipo String.Con estas variables ocurre lo mismo que con las numéricas, pero la única operación 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 unirán las dos cadenas en una sola. Para este tipo de operación 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 "teóricamente" no se pueden sumar cadenas, sólo con-catenarlas, veremos cómo 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, números, nombres, etc.Si no le indicamos de forma correcta al VB cual es nuestra intención, podemos confundirle y hacer que el resultado de algo que nosotros dábamos "por hecho", al final se convierte en un pequeño 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 intención es usar el tipo Variant)

Dim Num1

Dim Num2

Num1 = 5

Num2 = 3

Num1 = Num1 + Num2

Print Num1

¿Que imprimirá? Pruébalo y saldrás de duda. Bueno, imprimirá un 8.Ahora veamos este otro ejemplo:

Dim Num1

Dim Num2

Num1 = "5"

Num2 = "3"

Num1 = Num1 + Num2

Print Num1

Fíjate que lo que varía es sólo 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í:

Page 49: curso básico de programación en visual basic

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 número...En esta ocasión, sólo vamos a cambiar la línea de la asignación para dejarla de esta forma:

Num1 = Num1 & Num2

El resultado será 53. Porque le hemos indicado que una las dos cadenas, por tanto al encontrase con esta "operación" ha considerado al número 5 como una cadena, en lugar de un número.Cambia ahora la asignación del Num2, para que sea: Num2 = 3Vuelve a mostrar 53, el signo & tiene unos poderes enormes... y a pesar de ser dos números la única operación que puede realizar es la concatenación de cadenas, por tanto el tipo Variant se convierte por arte de magia en cadena.Pero fíjate si es "fuerte" el poder de convicción que tiene este operador, que aunque cambiemos el tipo de las variables, sigue "convenciendo" al basic que tipo de operación debe hacer. Esto no debería 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 debería producir un error, ya que un Integer no es una cadena.Así que "cuidadín" con las operaciones que realizamos. Ya que si añades esta línea:

Print Num1 * 2

Verás que realmente Num1 tiene guardado un número y el resultado será: 106

Page 50: curso básico de programación en visual basic

¿A dónde nos lleva todo esto? A que debemos usar los signos (operadores) de forma adecuada. Y si nuestra intención es sumar números, 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.

Añade al form dos Label, un TextBox y un botón de comandos. El aspecto será algo parecido al de la siguiente figura:

Añade el siguiente código y después ejecuta el programa, ya sabes F5. Escribe algo en el cuadro de texto y pulsa en el botón.

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 vacía, con lo cual borramos el contenido anterior, que es el que se muestra en la Figura.Hasta que no pulsemos el botón mostrar, no ocurrirá nada y el programa estará esperando a que ocurra algo.Una vez pulsado el botón, 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, imagínate que quieres mostrar el nombre en mayúsculas. Lo único que tendrías que hacer es lo siguiente:

Page 51: curso básico de programación en visual basic

Private Sub Command1_Click()

Label2 = "Hola " & UCase(Text1)

End Sub

Lo que se ha hecho es decirle al VB que convierta en mayúsculas lo que ya está en Text1. Esa es la "utilidad" del UCase.Pero y ¿si quisiéramos que conforme se va escribiendo se vayan convirtiendo los caracteres a mayúsculas?Aquí entrarían más instrucciones/funciones del Visual Basic, así cómo otro de los eventos que pone a nuestra disposición, 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

Pruébalo y verás lo que ocurre. Queda "guay" ¿verdad? Pero no es lo que nosotros pretendíamos. Vamos a intentar remediarlo y de camino vemos nuevas instrucciones/propiedades, en este caso del TextBox.

Private Sub Text1_Change()

Text1 = UCase(Text1)

Text1.SelStart = Len(Text1)

End Sub

La línea que se ha añadido (realmente la habrás 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 número), SelStart es una propiedad del TextBox que entre otras cosas, le indica la posición en la que se insertará el siguiente caracter que se escriba o bien nos puede indicar la posición 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

Page 52: curso básico de programación en visual basic

s = UCase(Chr(KeyAscii))

KeyAscii = Asc(s)

End Sub

Ahora han entrado dos nuevas funciones en acción: Chr, la cual convierte un número en una cadena... realmente convierte un código ASCII en la letra que representa (busca en la ayuda ASCII y léete lo que dice en las opciones que te muestra). Por otra parte Asc hace lo contrario, es decir convierte una letra en el código ASCII. Y lo que nosotros hacemos es: convertir el código de la tecla pulsada, representado por la variable KeyAscii, en una cadena, la pasamos a mayúsculas y después la volvemos a asignar a la variable, para "engañar" al Visual Basic y así hacerle pensar que realmente hemos tecleado una letra en mayúsculas.

Bueno, aquí voy a dejar la cosa, pues creo que con esto es te puedes ir "entreteniendo".

Nos vemos, espero en la próxima 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-básico".

Bien, después de un mes y pico, seguimos con la tercera entrega del curso "super-básico" de programación 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 cómo maneja el Visual Basic las variables, si a esta "libertad" (aunque más bien es libertinaje), le añadimos que no nos obliga a nada, es decir el VB nos está diciendo: "puedes usar las variables para lo que quieras, cómo quieras (o casi) y cuando quieras"Y esto en principio podría parecer una buena cosa, pero realmente es un mal hábito, que muchos de los que venís del BASIC, ya tenéis formado y creo que ahora sería 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 declaración de variables" (Require Variable Declaration), esto añadirá a cada nuevo módulo (FRM, BAS o CLS) la siguiente instrucción: Option Explicit, de esta forma tendrás la obligación de declarar cada una de las variables que uses en el programa. Y tu preguntarás: ¿Para que obligar a que se declaren las variables? La respuesta es bien sencilla: para que las declares... (algunas veces me asombro de la lógica tan aplastante de mis comentarios)Bromas aparte, es recomendable que declares las variables que vayas a usar y te diría más: no sólo es bueno declarar las variables, sino que mejor aún es declararlas del tipo adecuado.Ya vimos que hay diferentes tipos de variables, no sólo de tipos genéricos como podrían ser para almacenar caracteres y números, sino que dentro de las numéricas

Page 53: curso básico de programación en visual basic

hay varios tipos, y cada uno de ellos tiene una razón de ser.En mis tiempos del BASIC normalito, es decir del MS-DOS, no existía esta obligación de declarar "forzosamente" las variables y cuando estabas escribiendo un programa (proyecto que lo llaman ahora), grande, acababas "inevitablemente" usando más 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 veía 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 ocurría era que estaba "desperdiciando" memoria, por no tener un control de las variables que estaba usando; lo malo era que se podían escribir erróneamente los nombres de las variables de forma que al final, el programa no funcionaba bien porque al escribir un nombre de variable, habíamos cambiado el nombre... era frustrante y algunas veces te volvías 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 recomendación del día: cuando ejecutes un programa, hazlo con Control+F5, de esta forma se hace una compilación completa y "más o menos" exhaustiva del código, avisándote cuando hay algo que no "cuadra", con el VB3 no había problemas, ya que siempre se hacía la compilación 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 compilación completa

La tercera recomendación no es obligatoria, siempre que sigas la que voy a dar después, esta es una norma que también he usado desde mis tiempos de MS-DOS (aunque reconozco que últimamente no la pongo en práctica, ya que hago lo que después comentaré en la cuarta recomendación).En todos los módulos, antes sólo eran BAS, ponía al principio la siguiente línea:DEFINT A-Zde esta forma le indicaba al BASIC que mi intención era usar todas las variables del tipo Integer (entero), (realmente después usaba del tipo que me daba la gana, pero mi primera intención era no complicarme la vida con la mayoría de las variables), cuando quería usar una variable diferente de Integer, le indicaba "explícitamente" de que tipo era y así me obliga a usar la mayoría de ellas de este tipo que a la larga es o era el más usado, ya que para hacer bucles (ya te explicaré en un ratillo que es eso de los bucles y cómo hacerlos en VB) y otros cálculos "normales", era más que suficiente y en la mayoría de los casos: más rápido.

En Basic, y por supuesto todavía 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 hacía 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

Page 54: curso básico de programación en visual basic

Cada una de estas variables es de un tipo distinto, las cuatro primeras numéricas, la quinta para almacenar cadenas de caracteres y la última del tipo por defecto del VB: Variant que como su nombre indica (aunque en inglés), es Variante y puede almacenar prácticamente cualquier cosa, objetos incluidos, (ya veremos los objetos en otra ocasión). 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 además de declarar las variables de esta forma, también 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, tendrás que declararla como en el primer ejemplo.Aún queda otro carácter para otro tipo de datos numérico, el tipo Currency que se puede declarar con @. Este tipo ocupa 8 bytes y permite guardar números de tipo moneda, es decir números no enteros, pero con un número determinado y fijo de decimales, ahora no recuerdo, pero en la ayuda o en los manuales podrás ver la "retaila" de números que cada tipo admite.

Para terminar con las recomendaciones de hoy, voy a indicarte algo que debes tener en cuenta cuando declaras variables y que aún los más expertos caen en la trampa.

Además de declarar las variables con Dim, poniendo cada declaración en una línea, cosa que por otro lado queda bastante claro y es como suelo hacerlo, aunque últimamente estoy volviendo a coger malos hábitos... ¿será la edad?También se pueden declarar más 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 Doublepor supuesto también valdría de esta otra forma:Dim Numero%, NumeroLargo&, otroNum!, Nombre$, Numerazo#Y si me apuras, también 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 también podremos declarar variables de esta forma:Dim unaVariable, otraVariable, terceraVariablePero, 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:Serán del tipo Variant o del especificado con el DEFINT A-Z (es decir Integer)

Page 55: curso básico de programación en visual basic

Voy a suponer que la tercera recomendación no la estás poniendo en práctica, por tanto serían del tipo Variant.

Pero fíjate que podrías caer en el error, sobre todo si has programado algo en C, de pensar que esta línea:Dim Numero, otroNumeroInt, elTercero As Integero esta otra:Dim Numero As Integer, otroNumeroInt, elTerceroestán declarando tres números Integer y no es así, lo que se está declarando sería, en el primer caso:Numero y otroNumeroInt como Variant y elTercero como entero.en el segundo caso sólo Numero sería del tipo entero y las otras dos variables del tipo Variant.

Sería "ideal" que fuese como aparenta, pero el VB no hace estas "virguerías", ni incluso en la versión 5.

Por tanto, cuando declares variables, fíjate bien de que tipo son las que estás declarando, para no llevarte sorpresas, sobre todo con los redondeos y errores de desbordamiento...Un desbordamiento se produce cuando asignamos a un número un valor mayor del que está capacitado para almacenar, así si un entero sólo 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, tendrás 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 código se escucha entre el público...), con este código podrás comprobar la velocidad de los bucles con los distintos tipos de variables y así poder comprobar cual es la más adecuada.Crea un nuevo proyecto y asigna unos cuantos Labels (6 en total) y un botón.Cuando ejecutes este programilla, puedes ir tranquilamente a tomar café, porque se tomará su tiempo...En teoría nos mostrará el tiempo que emplea en hacer unos bucles con tipos diferentes de datos. Para que sea fiable, deberás especificar unos valores altos, ya que con números pequeños no es demasiado fiable, e incluso con números altos tampoco... la cosa era poner algo de código para "rematar" el capítulo de hoy...En la próxima 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 inútil que además de consumir recursos del sistema y hacernos perder el tiempo, no vale para nada... (es que después de probarlo, me he dado cuenta de que o todos los formatos son prácticamente igual de rápidos o yo he estado "engañado" durante todo este tiempo...)

Option Explicit

Page 56: curso básico de programación en visual basic

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 = "Duración con Integer: " & timer2

DoEvents

timer1 = Timer

For nLng = minBucle To maxBucle

Contar CInt(nLng), Label2

Next

timer2 = CDbl(Timer - timer1)

Label2 = "Duración con Long: " & timer2

DoEvents

timer1 = Timer

For nSng = minBucle To maxBucle

Contar CInt(nSng), Label3

Next

timer2 = CDbl(Timer - timer1)

Label3 = "Duración con Single: " & timer2

DoEvents

Page 57: curso básico de programación en visual basic

timer1 = Timer

For nDob = minBucle To maxBucle

Contar CInt(nDob), Label4

Next

timer2 = CDbl(Timer - timer1)

Label4 = "Duración con Double: " & timer2

DoEvents

timer1 = Timer

For nCur = minBucle To maxBucle

Contar CInt(nCur), Label5

Next

timer2 = CDbl(Timer - timer1)

Label5 = "Duración con Currency: " & timer2

DoEvents

timer1 = Timer

For nVar = minBucle To maxBucle

Contar CInt(nVar), Label6

Next

timer2 = CDbl(Timer - timer1)

Label6 = "Duración con Variant: " & timer2

DoEvents

Command1.Caption = "Calcular"

End Sub

Private Sub Contar(valor As Integer, etiqueta As Control)

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

Page 58: curso básico de programación en visual basic

End Sub

Te espero en la próxima entrega. Y como te digo y seguiré diciéndote: espero que me mandes tus comentarios, para saber si sólo lo estáis leyendo tú y alguien más... es que si no me dices nada, me aburro y acabaré por dejarlo...

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 también otras cosas, que espero que vaya en beneficio de los que estáis 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 asignación: Num1 = Num1 + Num2, debería ser: Num1 = Num1 & Num2 (sé que te habrás dado cuenta del detalle, sobre todo por la explicación posterior), este "desliz" ya está corregido, así que si lo has leído después del 30 de Junio (del 97), ya está como tenía que estar.La siguiente aclaración, es un despiste, no demasiado grande, ya que doy por hecho de que algo habrás leído sobre VB, bien los manuales, bien la Ayuda o algún 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 código hice este comentario:

O sea esto es lo que se dice un programa inútil que además de consumir recursos del sistema y hacernos perder el tiempo, no vale para nada... (es que después de probarlo, me he dado cuenta de que o todos los formatos son prácticamente igual de rápidos o yo he estado "engañado" durante todo este tiempo...)

Y la cosa es que antes de hacer este ejemplo, yo creía que algunos tipos de datos eran más rápidos que otros, (y a pesar de que el ejemplo demuestra, o casi, lo contrario, sigo creyéndolo...). La cosa es que en 32 bits un Long debería ser más rápido que el resto. Y los enteros más rápidos 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 "morrúo" (o cabezón) que soy, y en esta tabla (pulsando en el link), tienes los diferentes valores en distintos equipos y con distintas versiones de VB.Verás 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 código 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 módulo de código,

Page 59: curso básico de programación en visual basic

pulsando en Código en la ventana en la que se muestra los módulos 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 código de otro sitio, simplemente con pegarlo, se va a su sitio.En el caso de querer añadir al código, una función o procedimiento se puede hacer de varias formas, lo acabo de decir, pero lo repito un poco más claro:

1. Directa: Escribir el código 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 función/procedimiento y lo pegas

en la ventana de código...

3. Por menú de VB: Según las distintas versiones de VB, será un menú u

otro, deberás especificar el nombre del procedimiento o la función,

marcando la casilla correspondiente. En VB4/VB5 verás que aparte de los

Procedimientos (Sub) y las Funciones (Function) hay también

Propiedades (Property), estas las veremos en otra ocasión. También

verás que puedes declararlas Públicas o Privadas. Esto no es posible en

VB3, al menos en los procedimientos y funciones de los formularios.

En otra ocasión veremos todas estas cosas y con ejemplos, que es lo que "mola".Bueno, toda esta "retahíla" venía a cuento de cómo introducir código en los eventos de los controles o del formulario y cómo crear nuestras propias instrucciones (esto es lo que más 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 código, realmente está "repetido", o casi...

Option Explicit

Esto nos obliga a declarar todas las variables que usemos en el módulo, ponerlo es una recomendación, incluso te la impondría como norma. Para que salga de forma automática en cada nuevo módulo, selecciona del menú Tools/Advanced la opción Declare variables required (o algo parecido, que viene a significar Requiere la declaración de variables)

Siguiendo nuestro recorrido por el código, no encontramos con:

Page 60: curso básico de programación en visual basic

Private Sub Command1_Click()

Lo de Private significa que sólo se puede acceder a este procedimiento desde dentro del módulo en el que está declarado. Es decir no se puede llamar desde otro form o módulo BAS.Sub indica que es un procedimiento, no una función, ni una propiedad. Los Subs actúan como instrucciones propias del lenguaje. Las funciones también, pero devuelven un valor, mientras que los subs no devuelven nada, lo que cogen se los quedan ellos, aunque en su momento veremos que también 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 módulo.Los paréntesis sin nada dentro, indica que este procedimiento no recibe parámetros; los parámetros lo veremos dentro de un "ratillo"Toda esta línea es la descripción del procedimiento y cuando se le llame, bien desde nuestro propio código, bien porque se pulse sobre el botón, se ejecutará todo lo que esté dentro de él. El final del procedimiento está marcado por End Sub.

Las líneas con DIM indican que estamos declarando las variables y lo que se especifica después del AS es el tipo de variable, los cinco primeros son de cada uno de los tipos numéricos soportados (otro día veremos otro tipo cuasi-numérico), el sexto es Variant, el multi-uso, el que vale para todo.

Veamos ahora que es lo que se hace con esta línea:Dim timer1#, timer2 As DoubleAquí 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 podían indicar mediante unos caracteres especiales, (estos tipos son los heredados de versiones anteriores al Visual Basic 2, en esa versión, se introdujo el tipo Variant), en este caso # es lo mismo que Double, por tanto se podría haber hecho también de cualquiera de estas tres formas:Dim timer1#, timer2#Dim timer1 As Double, timer2#Dim timer1 As Double, timer2 As Double

Ahora fíjate que esta otra no haría la misma tarea:Dim timer1, timer2 As DoubleEsto funcionaría 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 algún sitio, no voy a decir dónde, porque lo mismo fue un "lapsus" del autor, pero decía que de esta forma declaraban tres variables de tipo Integer:Dim i, j, k As Integer

Sigamos nuestra andadura, ahora veamos esta declaración/asignación:Const minBucle = 1, maxBucle = 10Aquí lo que se declaran son dos constantes, éstas a diferencia de las variables, no pueden cambiar de valor, de ahí su nombre, por tanto permanecerán 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

Page 61: curso básico de programación en visual basic

después 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 explicación del "por qué" usar constantes. Además de "esclarecer" los listados, los hace más fáciles de comprender, también nos permite modificar un valor en un sólo sitio, con lo que ganamos en "confianza", al asegurarnos de que no omitiremos alguno de los sitios dónde 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, tendrías que cambiar en seis sitios esos valores, pero al declararlos como constantes, sólo cambiando el valor asignado, tenemos todo el trabajo hecho. Esto además de ser más fiable y legible, nos puede ahorrar algún que otro quebradero de cabeza y si además le añadimos que no ocupan espacio extra, salvo en la tabla de símbolos, una vez compilado el programa sólo se "compilarán" 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 aburriríamos, sólo lo haré cuando lo crea conveniente o bien si lo solicitas, en este caso, no tendré más remedio que cumplir tus deseos...

Command1.Caption = "Calculando..."Cambiamos el texto mostrado en el botón, para avisarnos de que está haciendo algo...

timer1 = TimerAsignamos el valor de la función Timer a la primera de las dos variables que usaremos para calcular el tiempo empleado por cada uno de los bucles. Esta función devuelve el número de segundos transcurridos desde la media noche.

For nInt = minBucle To maxBucleEsto 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.

Deberíamos ver primero la declaración del procedimiento Contar, para entender lo que hace la línea que viene después del For.

Private Sub Contar(valor As Integer, etiqueta As Control)Declaramos un procedimiento privado llamado Contar (actúa como una instrucción más del VB, ya que no representa a ningún control ni evento), entre los paréntesis están declarados los dos parámetros que espera recibir en la llamada, el primero es un número 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 línea:Contar CInt(nInt), Label1Contar es el nombre del procedimiento y a continuación se deben indicar los parámetros que espera recibir. En este caso no sería necesario CINT ya que lo que hace esta función es convertir el número que se pone dentro de los paréntesis en un número entero y como resulta que nInt es un número

Page 62: curso básico de programación en visual basic

entero... pues ¡no hay nada que convertir!El segundo parámetro es el control Label1, ya sabes que tenemos 6 etiquetas en nuestro programa desde Label1 a Label6Cuando llegue estos datos al procedimiento Contar, valor tomará lo que valga nInt y etiqueta se adueñará de Label1.

NextContinúa repitiendo el bucle hasta que se alcance el valor máximo, 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 después de la línea FOR, en caso negativo continúa con la línea siguiente al Next (realmente en la siguiente instrucción después del Next, ya veremos esto en otra ocasión)

timer2 = CDbl(Timer - timer1)Asignamos a la segunda variable que usamos para el cálculo del tiempo la diferencia entre el nuevo valor de los segundos transcurridos desde la media noche (Timer) y el valor que tenía timer1, es decir cuantos segundos... antes de empezar el bucle.El CDBL es una fución que devuelve un valor Doble. Es decir hace la resta y ese valor resultante lo convierte en doble.

Label1 = "Duración con Integer: " & timer2Esta asignación realmente es Label1.Caption, si se omite la propiedad, Visual Basic usa la que tiene por defecto, que según los manuales, suele ser la propiedad que se usa con más frecuencia. En este caso el Caption, es decir lo que se muestra en la etiqueta.

DoEventsEsta es una instrucción "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 confusión... realmente no es tan drástico, pero casi...DoEvents, detiene la ejecución 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 verás lo que ocurre, o debería ocurrir... que ya a estas alturas no me sorprendería nada que se mostrara...

El resto del programa es idéntico a este bucle, pero usando distintas variables y las demás etiquetas. El caso es que Contar espera una variable de número entero y un control, en el caso del control es obvio que se están poniendo las distintas etiquetas y en el caso del número se convierte a entero, porque eso es lo que espera nuestra instrucción y si no lo hacemos así, se quejará.

Ya sólo queda ver una línea del procedimiento Contar:etiqueta.Caption = valor * elMaximo + unDobleunDoble 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 continuación se encargará de que se muestre ese contenido. Bueno, también se asigna valor * elMaximo, es decir que cuando valor valga 1, se estará

Page 63: curso básico de programación en visual basic

mostrando 1000 + unDoble, realmente para hacer un contador se tendría 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 instrucción. Por tanto Contar se llamará tantas veces como dure el bucle en el que se encuentra.

Creo que queda todo más o menos claro y aunque este código 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 adivináis que es lo que hace... así os servirá de ejercicio, cosa que algunos me habéis pedido, pero que aún no es el momento de hacerlos.

En las declaraciones generales añade esta declaración:Dim Contando As Integer

En Contar, añade lo siguiente después del DoEvents:If Contando = 0 Then Exit For

Al principio del Command1_Click, añade estas líneas:

If Contando Then

Command1.Caption = "Calcular"

Contando = 0

DoEvents

Exit Sub

End If

Contando = 1

En cada uno de los bucles, pon esto después de llamar a Contar...If Contando = 0 Then Exit Sub

Y antes del End Sub añade esto otro:Command1.Caption = "Calcular"Contando = 0

Bueno, ahí dejo esto y como ejercicio podrías añadir dos controles TextBox para especificar los valores de maxBucle y elMaximo, de forma que según 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 botón.Como pista te diré que las variables usadas y/o declaradas dentro de un procedimiento son sólo visibles dentro de ese procedimiento. No te quejarás del "pedazo" de pista que te he dado...

Page 64: curso básico de programación en visual basic

A disfrutar y hasta la próxima entrega... ¿cuando? Eso ni se sabe...Y ya sabes que espero tus comentarios sobre el cursillo este de marras...

Nos vemos.

Hoy no voy a pasar lista, porque me imagino que los que faltan estarán aprovechando el puente este de Santiago o estarán preparando las maletas para irse de vacaciones... ¡que suerte! ¡Quién pudiera estar en la costa! con las playas "abarrotadas" de gente... je, je... yo no estoy de vacaciones, pero tengo la playa a menos de 50 metros de mi casa......que no se te pongan los dientes largos y límpiate las babas... para tu consuelo te diré que aún no he pisado la arena de la playa... O 8-|Vale, vale, también puedes irte al campo con los mosquitos... ¿se nota que me gusta más la playa? y que conste que la sierra que tenemos por esta zona es una maravilla... no, no soy del Patronato de Turismo...

Bueno, una vez dado un "repaso" veraniego, vamos al código, que es lo que te ha traído a esta página.

Hoy no tengo nada que rectificar de la entrega anterior... salvo varios errores tipográficos... De todas formas si lees estas entregas y hay posteriores, sería conveniente que fueses a la siguiente y te leyeras el principio, ya que aparte de poner algún comentario "chorra", suelo dar un repaso a los "errores" de la entrega anterior.

Ahora viene el contenido real de la quinta entrega.

No voy a dar la solución al problema/ejercicio planteado en la entrega anterior, voy a dejar que la deduzcas. Para que tengas base suficiente, te voy a contar un poco cómo funciona el Visual Basic y por extensión todas las aplicaciones de Windows.En la segunda entrega creamos un programa que mostraba un form en el que teníamos una caja de texto (TextBox), un botón (CommandButton) y dos etiquetas (Label).Cuando, después de pulsar F5 o CRTL+F5, se ejecuta la aplicación, de lo único que podemos tener certeza es que se ejecutará el código que se encuentra en el procedimiento Form_Load. Este procedimiento (sub) en concreto es lo que se llama un evento, y se produce cada vez que el formulario (form) se carga (load) en la memoria. Antes de entrar en detalles del porqué podemos tener la certeza de que ese código se va a ejecutar, tengo que seguir con mi 'ponencia' de cómo funcionan las aplicaciones Windows.

Se dice, (otros lo dicen y yo me lo creo), que Windows es un sistema o entorno operativo 'dominado' por los eventos. Esto ya lo dejé caer al principio de la segunda entrega. En Windows cada vez que movemos el ratón, pulsamos una tecla, tocamos una ventana o cualquiera de los controles que están en ellas, se produce un evento. Cuando se 'dispara', (los anglosajones usan 'fire' para indicar que se produce el evento), uno de estos eventos, Windows le dice al form, (de forma bastante rápida, aunque en algunas ocasiones no tanto como nos hubiera gustado), que se ha movido el ratón y que el ratón ha pasado por encima de tal ventana o de ese control y así con todas las cosas. La verdad, no es de extrañar que de vez en cuando falle el sistema, ¡¡¡es que no para de disparar!!! y algunos disparos se le puede escapar y...

...que chiste más malo, ¿verdad? Ya pensabas que el comentario ese del 'fire' era porque "el Guille se cree que está traduciendo un artículo de VB Online"...

Page 65: curso básico de programación en visual basic

Lo que quiero dejar claro es que a diferencia de los lenguajes que funcionan en MS-DOS, en Windows no podemos 'predecir' cual será el código que se va a ejecutar. No debes 'planificar' tu programa dando por sentado que... "después de esto el usuario 'tiene' que hacer esto otro y así yo podré hacer una comprobación para..." ¡ NO ! Aquí (en Windows) no existe la programación lineal, no des nunca por hecho que esto es lo que va a ocurrir..., porque casi con toda seguridad ¡no ocurrirá!Veremos cómo podemos 'controlar' que algunas cosas se hagan cuando nosotros queramos, pero esto será sólo cuando el usuario de nuestra aplicación realice una 'acción' que estaba prevista; también codificaremos para que se ejecute parte del código cuando el usuario no haga lo que habíamos previsto que hiciera. Pero eso lo iremos viendo poco a poco...

Todo programa de Windows tiene un punto de entrada; en el caso de Visual Basic, puede ser bien un formulario o bien un procedimiento de un módulo BAS, (de debe llamarse obligatoriamente Main); los módulos los veremos en otra ocasión.Normalmente las aplicaciones suelen tener más de un formulario y algún que otro módulo. Pero tenga uno o ciento, siempre hay un único punto de entrada (o de inicio). Por regla general suele ser un formulario. En nuestro ejemplo sólo tenemos un form en el proyecto, por tanto no hay duda alguna de cual será el punto de entrada del programa.Perdona si me extiendo en esto, pero tanto si tú lo sabes como si no, creo que tú ahora lo sabes... (es difícil esto de escribir una cosa para tanta gente con distintos niveles...)Cuando Windows inicia el programa, 'debe' cargar el formulario en la memoria. Y desde el momento que se prepara para hacerlo, ya está con los 'tiritos' y mandando 'mensajes' a la ventana (todo formulario es una ventana), pero no sólo le está avisando a la nuestra que la acción ha empezado, sino que lo hace prácticamente a los cuatro vientos; si otra aplicación quiere enterarse de lo que ocurre en Windows, sólo tiene que conectarse a la 'mensajería' de éste entorno operativo y leer las 'noticias'... pero esto ya es complicarse la vida y todavía no nos la vamos a complicar tanto... o más... (pensará alguno después de respirar aliviado...)Lo que ahora interesa es saber que el 'evento' Form_Load se produce cuando esta ventana pasa del anonimato a la vida pública, aunque no la veamos, estará en la memoria de Windows y se producirá el primer evento del que tenemos 'certeza', por tanto este es un buen sitio para poner código de inicialización.Realmente el Form_Load no es lo primero que puede ocurrir al iniciarse un formulario, pero por ahora vamos a pensar que sí; porque sino esta entrega no la termino hasta después del verano... ¡¡¡que me gusta darle vueltas a las cosas!!!

Ahora que se ha cargado el form en la memoria... ¿que ocurrirá? Pues, si el usuario se va a tomar unas cañas: nada. Sólo ocurrirá algo cuando interactuemos con el form, es decir, le demos razones a Windows para 'pegar tiritos'.Nuestra aplicación, (te recuerdo que tenía, entre otras cosas, un textbox y un botón), esperará a que se produzca algunas de las acciones para las que está preparada.Y la pregunta es ¿que es lo que puede ocurrir? Para saber 'todas' las cosas que pueden ocurrir en nuestra ventana, (no recuerdo si has pulsado F5 o no), finaliza el programa y muestra la ventana de código.

En la parte superior de la ventana hay dos listas desplegables, la de la izquierda tiene todos los controles, (en otra ocasión veremos que no siempre es así), que tenemos en el form, incluido éste, además de uno especial que se llama General.Pulsa en la lista de la izquierda, para que se despliegue y te mostrará esto:

Page 66: curso básico de programación en visual basic

Estos son los cinco controles, incluyendo el form, que pueden recibir mensajes de Windows. Pulsa en cualquiera de ellos. En la lista de la derecha están todos los procedimientos (eventos) soportados por el control de la izquierda. Cada control mostrará los eventos que VB y/o Windows permite que se produzcan. Estos ocurrirán por distintos motivos, cada uno tiene su 'tarea', nos pueden avisar de que se ha pulsado el ratón, que se acaba de pulsar una tecla, que se ha modificado lo que antes había, etc.

Una llamada a la precaución.Los eventos son 'procedimientos' y no sólo se puede llegar a ellos porque se produzca una acción del usuario o del propio Windows y si me apuras, incluso de Visual Basic... Nosotros podemos 'provocarlos' ¿cómo? simplemente haciendo una llamada al SUB que queramos o actuando en el control de manera que ocurra alguno de los eventos soportados.Por ejemplo, en el Form_Load tenemos que se asignan cadenas vacías tanto al Label2 como al Text1:Label2 = ""Text1 = ""Cuando VB asigna una cadena vacía (o cualquier otra cosa), al Label2 está borrando el contenido del 'Caption' de esta etiqueta y asignando algo nuevo, es decir lo está cambiando. En nuestro programa no ocurre nada, salvo que se borra lo que allí hubiera, pero realmente se está produciendo el evento Label2_Change, porque hemos cambiado el contenido. VB sabe que no hemos escrito código para manejar esta situación, así que... hace la vista gorda y simplemente cambia el contenido del Label2.Caption sin hacer nada más.Pero al asignar una cadena vacía al Text1, también se borra el contenido y se produce un Change, sólo que en este caso, al no tener Caption, lo que se borra es el Text; de todas formas sea como fuere, se produce el evento Text1_Change. Nuestro querido amigo Visual Basic, sabe que hemos escrito código para este evento y sabe que tiene que hacer lo que allí se dice...En este caso, nuestro código se ejecuta, pero realmente no hace nada de interés o por lo menos nada que se pueda apreciar de forma visual. No voy a explicar para que sirve ese código, porque ya lo hice en su día, lo que espero es que hoy lo entiendas mejor...¿Cómo? que no sabes de qué código estoy hablando... pues del ejemplo de la segunda entrega, creo que ya lo dije al principio... a ver si prestamos más atención y dejamos de pensar en las vacaciones... ¡estos niños!

El código interesante es el que se ejecuta cuando se pulsa en el botón:Label2 = "Hola " & Text1Aquí se asigna al Caption del Label2 lo que hay después del signo igual, en este caso actúa 'casi' como una variable. Y ya sabrás que antes de asignar un valor a una variable, se procesa todo lo que está después del signo igual, por tanto, Visual Basic tomará el contenido del Text1, es decir lo que se haya escrito y lo unirá (&) a la palabra "Hola ", una vez hecho esto, lo almacena en el Caption del Label2.

Page 67: curso básico de programación en visual basic

Y si en lugar de guardarlo en un control label, lo asignáramos a una variable... y si en lugar de escribir nuestro nombre, escribiésemos un número... y si la variable en la que guardamos ese número se llamara, por ejemplo, maxBucle o elMaximo...Pues que tendríamos una parte resuelta de la tarea esa que puse como ejercicio en la entrega anterior.Pero, este form de la segunda entrega no nos sirve. Tendremos que cargar el de la vez pasada y añadirle un par de cajas de textos y un par de etiquetas, para que indique lo que se debe introducir en cada textbox; el aspecto sería este:

Pero nos encontramos con un problema: ¿cómo puedo asignar un valor a maxBucle, si las constantes no pueden cambiar de valor? Fácil, conviértela en variable. Pero debes recordar la pista que di al final: "Las variables declaradas dentro de un procedimiento son solamente visible dentro de ese procedimiento".De este tipo de variables se dice que son locales. Alumno: ¿Que significa esto?Guille: Que sólo pueden usarse dentro del procedimiento en el que se han DIMensionado o declarado.A: Vale, "mu bonito" y ¿que pasa?G: Esto... que no pueden usarse en otro sitio...

¿Recuerdas la recomendación de usar Option Explicit?Pues gracias a Option Explicit, se solucionan el 99% de los fallos 'involuntarios' con las variables... y no exagero!!!Es super-fácil escribir de forma incorrecta el nombre de una 'bariable' y no vengas con el cuento de que a tí nunca te ocurre, porque no me lo voy a creer... bueno, de ti si, pero no todos son tan minuciosos como tú... (para que nadie se sienta ofendido/a, quiero que veas en esto que acabo de poner... la intención que tiene, es decir que me dirijo a más de un "ti"... ya sé que no eres tan torpe como para no haberlo 'captado', pero con esta aclaración me quedo más tranquilo.)

Según cómo y dónde se declare una variable, su 'visibilidad' o área de cobertura, será diferente... también se puede usar la palabra ámbito... es que como en las emisoras de radio se habla de la cobertura... pues...Una variable puede tener estas coberturas:--Privada o Local a nivel de procedimiento (Sub, Function, etc.)--Privada o Local a nivel de módulo (FRM, BAS, etc.)--Pública o Global a nivel de aplicación (en este tipo hay una forma especial de usar las variables que veremos en otra ocasión)

Page 68: curso básico de programación en visual basic

Explicando los dos primeros puntos.Cuando declaramos o dimensionamos una variable 'dentro de' un procedimiento, ésta sólo es visible en ese procedimiento; fuera de él no es conocida y cualquier uso que se intente hacer de ella, producirá un error... si has sido obediente y has usado el Option Explicit. En caso de que no hayas puesto la 'obligación' de declarar todas las variables, te llevarás una sorpresa de que no tiene el valor esperado.A: ¡JA!G: ¿No te lo crees? Vale. Vamos a comprobarlo.Abre un proyecto nuevo, pon un textbox y un botón..Abre la ventana de código, borra el Option Explicit.En el Form_Load haz esta asignación: Incredulo = "No me lo creo"En el Command1_Click escribe esto otro: Text1 = IncreduloPulsa F5 y haz click en el botón...¿Que ha pasado?(Tengo que comprobarlo, para no meter la pata, pero se supone que el texto se borrará sin poner nada...)Bien, eso ocurre porque la variable usada en el Form_Load no tiene nada que ver con la usada en el Command1_Click.Con esto comprobamos o demostramos que podemos tener variables diferentes con el mismo nombre. La única condición es que no pueden estar juntas, aunque hay un truco para juntarlas sin que ellas se enteren...

En este último ejemplo, nuestra intención es tener una variable que sea 'conocida' en todo el form. Cuando necesitemos variables con un ámbito a nivel de módulo, tenemos que declararla o dimensionarla en la sección de las declaraciones generales; ya sabes, en la lista izquierda de la ventana de código seleccionas General y en la de la derecha Declarations ( Declaraciones).Muestra la ventana de código y en General/Declaraciones escribe:Option Explict 'retornamos a las buenas costumbresDim Incredulo As StringPulsa F5 para ejecutar el programa, pulsa en el botón y... ¡AHORA SI!Tenemos una variable que puede ser 'vista' en todo el form. Ya puedes usar 'Incredulo' donde quieras, ahora siempre será la misma variable y contendrá lo último que se le haya asignado.

A partir de la versión 4 del VB, entra en juego una nueva palabra, 'Private', que suele usarse en las declaraciones de las variables a nivel de módulo, de esta forma es más fácil entender la intención de la misma; por tanto la declaración anterior podemos hacerla también así:Private Incredulo As 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 procedimientosComo indica el encabezado de este nuevo párrafo, cosa que ya he comentado antes, podemos tener distintas variables con el mismo nombre en distintos procedimientos; para ello, sólo hay que dimensionarlas y el VB las almacenará en distintas posiciones de la memoria para que no se 'mezclen'.

Page 69: curso básico de programación en visual basic

En la entrega anterior, teníamos un procedimiento llamado Contar que se usaba para eso, contar...En este ejemplo vamos a usar un sub también llamado contar, para ver en acción todo esto que estoy diciendo.Sitúate en la parte General/Declarations de la ventana de código y escribe o "copia/pega" lo siguiente:

Private Sub Contar()

Dim i As Integer

For i = 1 To 2

Print "i en contar= "; i

Next

End 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 cambiásemos el nombre y se llamaran NombreProcedimiento_Variable.Sé que puede ser una forma 'rebuscada' de explicarlo, pero así te haces una idea.

Todas las variables declaradas en un procedimiento, sólo son visibles en ese procedimiento. Si has usado QuickBasic o el compilador Basic de Microsoft (que usaba el QuickBasic Extended QBX), esto ya existía y también existía la forma de hacer que una variable declarada en un procedimiento, fuese visible fuera de él; para ello declarabas la variable como Shared (compartida); pero en VB eso NO EXISTE. La única forma de compartir una variable es declarándola en la sección General de las declaraciones.

Prueba ahora esto. Sustituye el procedimiento Contar por este otro:

Private Sub Contar(j As Integer)

Dim i As Integer

Page 70: curso básico de programación en visual basic

For i = 1 To j

Print "i en contar= "; i

Next

End Sub

Aquí hacemos que Contar reciba un parámetro y el valor que recibe lo usamos como el límite final del bucle For, es decir contará desde UNO hasta el valor de j.Sustituye la llamada a Contar del Form_Load por esta:Contar iLe damos a Contar el parámetro i. Por tanto cada vez que se llame a este procedimiento le estamos diciendo que i es el valor máximo que tomará el bucle For que tiene dentro.¿Cómo reaccionará? ¿Se confundirá? ...

No, no voy a dejarlo para la siguiente entrega, es tan obvio que lo voy a explicar ahora mismo:Al procedimiento Contar le da igual que se use una variable llamada i o cualquier otra, incluso un número. Lo único que necesita y espera, es recibir un valor numérico (del tipo Integer) y lo asignará a la variable j. Por tanto no ocurrirá nada extraño. Ejecuta el programa y fíjate en lo que ocurre. Sé que lo has deducido, eso está bien... vas aprendiendo... 8-)¿Cómo? ¿Que tú aún no lo has captado? Pues dímelo... (mejor no me lo digas y repásalo de nuevo...)

Otra cosa sería pretender usar una variable declarada a nivel de módulo, dentro del procedimiento, que tuviese el mismo nombre.Si te has quedado 'con la copla', tu mismo sabrás la respuesta... ¡Efectivamente! Si dentro de un procedimiento tenemos una variable dimensionada con el mismo nombre de una declarada a nivel de módulo o a nivel global, (para usarla en cualquier sitio de la aplicación), tendrá 'preferencia' la variable local... Ésta 'tapará', ocultará o como prefieras decirlo a cualquier otra variable del mismo nombre...

En la próxima entrega veremos más casos y cosas de las variables. Comprobaremos cómo usarlas a nivel Global. Pero por ahora vamos a terminar con el programa que teníamos planteado en la entrega anterior:Aunque realmente deberías saber cómo solucionarlo...Lo que seguramente no sabrás, es cómo hacer que estas variables tomen el valor...

De acuerdo, lo explicaré. Carga el ejemplo de la cuarta entrega.Hay varias soluciones a este problema; una sería usar variables locales, esta decisión no 'justifica' el 'pedazo' de pista que te di... pero esto es lo que hay.

La línea Const minBucle = 1, maxBucle = 10. Debe quedar así:

Const minBucle = 1

Dim maxBucle As Integer

Esto hará que maxBucle deje de ser una constante y pase a ser una variable, con lo cual podremos asignarle cualquier valor.

Page 71: curso básico de programación en visual basic

Pero, ¿cómo le asignamos el valor?Vamos a dar por sentado que lo que se escriba en el Text1 será el valor que debe tener maxBucle; entonces lo único que haremos es asignar a maxBucle lo que se escriba en el Text1...Vale, pero ¿dónde? Pues... por ejemplo, después de la declaración, así que en la siguiente línea al Dim maxBucle... escribe los siguiente:maxBucle = Text1Esto en teoría no daría problemas, al menos en condiciones normales, ya que el contenido de un textbox es del tipo Variant y ya vimos que un Variant puede almacenar cualquier cosa, por tanto si es un número 'intentará' convertir al tipo de la variable que recibirá el valor.Esto no siempre funciona, sobre todo si el contenido del Text1 no es numérico. Por ahora vamos a hacerlo simple, si el usuario (en este caso tú), escribe algo no numérico lo vamos a considerar CERO... o casi...Cambia la asignación anterior por esta otra...¡¡¡ ALTO !!! Antes de hacerlo, pruébalo e intenta escribir una palabra en lugar de un número... ¿que ocurre?Pues que VB no se complica la vida y te dice que 'nones'... (realmente dice Type Mismatch... Error de Tipos, es decir que lo que has escrito no es capaz de convertirlo a Integer)... así que escribe esto otro:maxBucle = Val(Text1)Con esto lo que hacemos es convertir el contenido del Text1 a un VALor numérico y lo asignamos en la variable...¿Problemas? Que el valor sea mayor del que se puede guardar en un Integer, pero eso ya no es asunto de esta entrega...

Ahora nos queda convertir elMaximo en variable y asignarle el valor que hay en el Text2. ¡Efectivamente! hacemos lo mismo, sólo que esta vez dentro del procedimiento Contar, por tanto la declaración Const elMaximo = 1000&, la tienes que quitar y poner estas dos líneas:Dim elMaximo As IntegerelMaximo = Val(Text2)Aquí el único inconveniente es que esta asignación se hace cada vez que se entra en este procedimiento... y eso, amigo mío, no es un buen estilo de programación... Sobrecargamos de forma innecesaria al procesador... ten en cuenta que la conversión a número y la asignación ¡¡¡se ejecuta cada vez que se entra en Contar!!!Lo mejor para este caso sería declarar elMaximo como variable a nivel de módulo. Por tanto, borra el Dim elMaximo... del sub Contar y colócalo en la parte de las declaraciones generales del form.Ahora... ¿dónde asignamos el valor para evitar la sobre-carga? Ya que tenemos la variable a nivel de módulo, ésta será 'vista' en todos los procedimientos del formulario, por tanto lo lógico sería hacerlo en el Command1_Click, ya que cuando nos interesa a nosotros saber cuanto tenemos que contar es precisamente cuando pulsamos en el botón...Pero... ¿dónde exactamente?, después de Contando = 1

Bien, ahora está la cosa mejor... haz tus pruebas y si aún no lo tienes claro... pregúntame, (te digo lo de antes: mejor no me preguntes y repásalo todo de nuevo...)

Prácticas y ejercicios¿Quieres algo para practicar?Este ejercicio se lo ponía a mis alumnos, cuando daba clases de BASIC, hace ya unos 10 años o más... y consistía en pedir el nombre, pedir la edad y mostrar el nombre tantas veces como años tengamos...

Page 72: curso básico de programación en visual basic

Claro que con el BASIC del MS-DOS era más directo y se sabia cuando se debía empezar a mostrar el nombre, para solventar esto, se mostrará el nombre 'edad' veces cuando se pulse en un botón. El aspecto del form sería algo así:

No creo que sea complicado, así que vamos a complicarlo un poco más:Mostrar el nombre 'edad' veces, dentro de un label, para ello el label deberá ocupar la parte izquierda del form.Y una tercera versión, sería lo mismo que esta última, pero cada vez que se muestre el nombre se haga en una línea diferente.

La pista: En la segunda entrega vimos de pasada el CHR. Pues decirte que si añadimos a una variable un CHR(13), lo que hacemos es añadirle un retorno de carro, es decir lo que venga después se mostrará en la siguiente línea... siempre que se 'concatene'. También existe una constante definida en VB4 o superior que es vbCrLf, esto es un retorno de carro (Cr) y un cambio de línea (Lf)

¡Que te diviertas!

Como ya es costumbre al final de cada entrega, espero tus comentarios y opiniones sobre el curso, además de aceptar críticas, que seguro que algo habrá que no entiendes o que no te gusta como está explicado.

Con esto acaba el tema por ahora... no, no se acaba el curso, no te alarmes; la próxima entrega será... pues, es que... yo creo que... si no... un día de estos, pero seguro que en el caluroso mes de Agosto.

Nos vemos.

He preferido poner la solución a los ejercicios en una página separada, así creo que será mejor.

Solución al primero, preguntar el nombre, preguntar la edad y mostrar el nombre "edad" veces:Tenemos dos TextBoxes: Text1 y Text2, un botón: Command1, el código sería:

Private Sub Command1_Click()

Dim i As Integer

Dim j As Integer

Page 73: curso básico de programación en visual basic

Dim Nombre As String

j = Val(Text2)

Nombre = Text1

For i = 1 To j

Print Nombre

Next

End Sub

El segundo es un poco más complicado, pero no tanto, espero.Sólo hay que asignar el nombre al Label, suponiendo que fuese Label3, sería 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 línea diferente, hay que añadirle a continuación un retorno de carro y cambio de línea, en VB4 hay una constante definida para ello: vbCrLf, en el VB3 habría que declararla de esta forma:

Dim vbCrLf As StringvbCrLf = Chr$(13) & Chr$(10)

Este sería el código:

Private Sub Command1_Click()

Dim i As Integer

Dim j As Integer

Dim Nombre As String

j = Val(Text2)

Page 74: curso básico de programación en visual basic

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 irás quedándote con la copla.

Ya sabes que mi segundo nombre es: DESPISTE, si no lo sabías, ahora lo sabes...

Y es que con tanta explicación 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 explicación de la utilidad de todo ese código es, en términos generales, para cancelar en cualquier momento el programa.¿Cómo?Hemos declarado Contando a nivel de módulo, 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 vacía); a los números 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 botón 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 'expresión' que se usa después del IF se cumple o no...En caso de que se cumpla (sea cierta), se ejecutará todo lo que haya después de THEN... algo así:

IF <EXPRESIÓN> THEN <INSTRUCCIONES>

También puede usarse de esta otra forma:

IF <EXPRESIÓN> THEN

<INSTRUCCIONES>

[<MÁS INSTRUCCIONES>]

END IF

En la primera forma de uso, las instrucciones se ponen a continuación de THEN como en el caso de:If Contando = 0 Then Exit ForEn la otra forma de usarlo, las instrucciones se ponen en las siguientes líneas y podemos escribir tantas líneas como queramos. Todas se intentarán procesar...Esto para el caso de que al evaluar la expresión se cumpla como verdadero.En las ocasiones en las que no se cumple la expresión se hará lo siguiente, según la

Page 75: curso básico de programación en visual basic

forma de usar el IF/THEN:--En el primer método, se procesan las instrucciones que hay en la siguiente línea y se continúa a partir de ahí el programa.--En el segundo caso, se busca END IF y se continúa por la siguiente línea...

Más adelante, en otra entrega, veremos otras formas de usar IF...THEN...

Ahora voy a explicar un poco esto de la evaluación de las expresiones.El IF espera encontrar un valor CERO (FALSO) o un valor distinto de cero (por extensión VERDADERO)En If Contando = 0 Then Exit For, la expresión es Contando = 0, aquí no se está usando la asignación, 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 lógica 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 símbolo distinto para el igual, según sea una asignación o una comparación.En Pascal (Delphi) la asignación es := y la comparación es =En C, C++, la asignación es = y la comparación == (esto del == puede 'sonarte' si has hecho algo con JavaScript)

En el caso de If Contando Then, al no haber una expresión 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 condición y se procesa todo lo que viene a continuación; pero si la variable vale CERO, el IF lo interpretará como FALSO y pasa a ejecutar lo que haya a continuación de End If.

Por tanto, (volviendo por fin al listado), la primera vez que se pulse en el botón Command1 y se compruebe esto:If Contando ThenNo se cumplirá la condición, ya que el contenido de Contando es cero, y se pasará a lo que hay después del End If, es decir a:Contando = 1y se continúa con el programa como antes de añadir todas estas cosas...

Pero, si pulsas de nuevo en el botón, 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 módulo conserva el último valor que hayamos asignado), y esta vez al evaluar:If Contando Thensi se cumple, así que se procesan las líneas que vienen a continuación... entre ellas está Contando = 0 y DoEvents que vuelve a permitir a Windows que las otras cosas que antes de pulsar en el botón continúen ejecutándose, (esto se hace de forma asíncrona, es decir, Windows se da por enterado y da los avisos (mensajes) cuando quiera), pero continúa con la siguiente instrucción 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 más de esto a lo largo del curso...)

Page 76: curso básico de programación en visual basic

En caso de que hayamos pulsado el botón cuando aún no había terminado todo lo que este Sub estaba haciendo, se continúa igual que si se hubiese hecho PAUSA y después 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...También es posible que al pulsar por segunda vez en el botón, se estuviese dentro del Sub Contar, en este caso, también se evaluaría la expresión y se saldría del procedimiento... así que también dejaría 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.

Solución de los ejercicios de la Quinta EntregaPulsa este link para ver la solución de los ejercicios de la quinta entrega (aunque el nombre la página sea: basico06_sol)

Como hemos visto en el apéndice de la entrega anterior, la instrucción IF... THEN... nos permite tomar decisiones según el valor de una variable o el resultado de una expresión. En esta entrega veremos como sacarle rendimiento a esta instrucción.Pero antes de entrar en detalles, veamos cómo 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 BasicLas instrucciones en Basic no tienen porqué estar cada una en una línea. Se pueden escribir varias instrucciones en la misma línea, pero separando cada una de ellas con el signo : (dos puntos).Cuando VB encuentra los dos puntos, deja de 'interpretar' la instrucción y pasa a la acción, una vez traducido a su lenguaje interno, toma lo que hay después del signo : y sigue su camino en busca de más instrucciones o el final de la línea.

Veámoslo de forma práctica:Nombre = "Pepe" : Print NombreEsta línea tiene dos instrucciones: una asignación y una instrucción 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...

Ya vimos que IF comprueba la expresión que viene a continuación, si es cierta, ENTONCES procesa lo que haya después de THEN. En caso de ser en la misma línea, interpretará todas las instrucciones que estén a continuación; en caso de ser un bloque IF... THEN... END IF, ejecutará todo lo que esté dentro de ese bloque. Ahora bien, si la expresión es falsa pasa a la siguiente línea, tanto si es o no un bloque. En el caso del bloque la siguiente línea a interpretar será la que esté después de END IF.

En los tiempos del BASIC interpretado de MS-DOS, era habitual encontrar las líneas 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 "mogollón" de instrucciones en la misma

Page 77: curso básico de programación en visual basic

línea...Ahora, salvo en contadas excepciones, escribo cada instrucción en una línea. Y te recomiendo que hagas lo mismo, tu código ganará en claridad y si alguna vez vuelves a verlo, te será más fácil de entender.

Después de este pequeño respiro, veamos cómo estaría formada una línea de VB para usar con un IF... THEN...

[instrucciones:] IF <expresión> THEN <instrucciones si es cierto [:más instrucciones...]>

A continuación de THEN podemos incluir cuantas instrucciones queramos, separadas por dos puntos.Estas sólo se ejecutarán cuando la expresión sea cierta. Si el resultado de la expresión es falso, se obvia 'todo' lo que hay después de THEN y se pasa a la siguiente línea.Espero que lo hayas asimilado y que no te indigestes con lo siguiente...

Pero, (...), existe otra instrucción que PUEDE acompañar al IF... THEN... y es para los casos en los cuales el resultado de la expresión sea FALSO.Si, ya sé que dije que cuando es falso se pasa a la siguiente línea, pero eso es cuando no se usa la cláusula ELSE.Con ésta, la definición de la instrucción "tomadora de decisiones" quedaría así:

IF <expresión> 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 código, usar el bloque:

IF <expresión> THEN

<si se cumple>

ELSE

<si no se cumple>

END IF

Sé que esto puede ocupar más líneas de código, pero nuestro "coco" lo agradecerá, ya que es más fácil de comprender, sino veamos un ejemplo:

IF numero > limite THEN

Print "tu número es grande"

ELSE

Print "OK, McKey!"

END IF

Page 78: curso básico de programación en visual basic

Ahora veámoslo en una sóla línea:

IF numero > limite THEN Print "tu número es grande" ELSE Print "OK, McKey!"

En este ejemplo, aún queda claro, pero lo podríamos complicar con más instrucciones para ambos casos, es decir, para cuando la expresión es cierta y también cuando es falsa.

En los tiempos del BASIC que venían incorporados con los ordenadores, cada línea del programa había que numerarla, ya que todo lo que se escribía sin número de línea, se ejecutaba inmediatamente; al igual que ocurre con lo que se escribe en la ventana Inmediate del Visual Basic.Los números de líneas se usaban, además de porque era obligatorio, para cambiar el orden de ejecución, sobre todo después de una comparación. De esta forma, aún sin tener bloques IF/THEN/ELSE/END IF, se podían simular.¿Cómo se lograba?Usando una instrucción que muchos creen que es "indecente, antisocial, etc."

La estrella del Basic: (redoble de tambores) "GOTO"A partir de hoy, más de un purista de la programación no me dirigirá la palabra... pero no me importa...

Se ha "denostado" (injuriado) con exageración el uso de esta instrucción.Realmente es una instrucción de "bifurcación", es decir, sirve para "IR A" otro sitio de nuestro programa.Su uso ha sido el más criticado de los NO PARTIDARIOS del Basic y siempre han basado sus críticas en el exagerado uso del GOTO en todos los programas Basic. Pero aclaremos que C también tiene esta instrucción y que cualquier programa en código máquina (ensamblador) está "plagado" de JMP que para el caso es lo mismo que el sufrido GOTO, realmente una instrucción GOTO número_linea se convierte en JMP número_linea.

No voy a recomendar el uso del GOTO, para nada, ya que hoy día es innecesario su uso. Antes no teníamos más remedio, porque el BASIC no disponía de instrucciones para poder estructurar el código. Pero sería una tontería querer hacer creer que no existe esta instrucción. Sabiendo que está y cómo podemos evitarla, es preferible a decir que no existe y si por casualidad la descubres... a que la uses.Por tanto, insisto en mi recomendación, (de esta forma los PURISTAS volverán a dirigirme la palabra), NO USES EL GOTO, ni aún cuando creas que no tienes más remedio... aunque, aquí entre nosotros, algunas veces es más cómodo usarlo... pero que no se entere nadie...

Este es un programa de los de antes, sirve para mostrar en pantalla los números del 1 al 10 y sin usar el FOR/NEXT

10 A = 1

20 Print A

30 A = A + 1

40 IF A <= 10 THEN GOTO 20

'Con el Commodore este programa se solía escribir así:

Page 79: curso básico de programación en visual basic

10 A=1

20 PRINTA:A=A+1:IFA<=10GOTO20

'Sin ESPACIOS NI NADA... TODO APELMAZADO... ¿que más daba usar el GOTO?

Imagine there's no heaven... (es que está sonando J. Lennon... un momento...)

En este ejemplo, es obvio que podríamos sustituirlo con:

10 For A = 1 To 10

20 Print A

30 Next

El NEXT hace lo mismo que la asignación y la comparación.Pero hay otras maneras, para ello existe una serie de instrucciones que funcionan de manera similar, veamos otros ejemplos con más instrucciones para hacer bucles, seguiré usando los números de línea por aquello de la "nostalgia", pero salvo en el primer ejemplo, en los demás no es necesario.

10 A = 1

20 While A <= 10

30 Print A

40 A = A + 1

50 Wend

El WHILE/WEND ya casi ni se usa, porque tienen un sustituto más versátil, ahora lo veremos, pero el uso sería:

WHILE <expresión>

<instrucciones si se cumple>

WEND

Es decir, MIENTRAS la expresión sea cierta, repite todo lo que haya hasta el WEND.Por supuesto podemos ponerlo todo en una sola línea: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 expresión 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 hay entre WHILE y WEND. Estas instrucciones ya existían en el GWBASIC (el basic de los PCs)

Page 80: curso básico de programación en visual basic

Hay que tener cuidado con esto de que la expresiones evalúan el cero como FALSO y cualquier otro valor como VERDADERO, por ejemplo:

A = 1 o

While A While 1

Print A Print A

A = A + 1 A = A + 1

Wend Wend

En ambos casos el bucle sería "infinito", realmente se detendría en un momento dado, ya que llegaría a "desbordarse" el valor máximo y en ese momento el programa acabaría por producirse un error... pero prueba esto y verás:

While 1

Print "Hola Mundo"

Wend

Esto nunca finalizará, salvo que pulses CRTL+BEAK (o INTERrumpir), para detener el programa.

Más instrucciones para hacer bucles

Con el QuickBasic 3.0, (yo no llegué a tenerlo, pero creo que fue ahí dónde se introdujo), entró en funcionamiento una nueva forma de hacer bucles:DO... LOOPEl último ejemplo podríamos escribirlo así:

Do

Print "Hola Mundo"

Loop

Pero la "gracia" de este tipo de bucle es que podemos usar dos nuevas cláusulas 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

Loop

Page 81: curso básico de programación en visual basic

La ventaja es que WHILE se puede poner tanto después de DO como a continuación de LOOP.Si lo usamos como DO WHILE <expresión>... la forma de actuar es igual que WHILE/WEND, es decir, se evalúa la expresión y sólo en caso de que sea cierta, se ejecuta lo que está dentro del bucle, es decir entre DO y LOOP.Pero si evaluamos la expresión en LOOP, se ejecutará todo lo que hay tras el DO, como mínimo una vez y se seguirá repitiendo si se cumple la condición. De esta forma, como he dicho, se ejecutará el contenido del bucle, como mínimo 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, también se ejecutará, al menos, una vez.A = 11 : Do : Print A: A = A + 1: Loop While A <= 10Que mal queda en una sola línea, ¿verdad?

Pero con DO/LOOP también puede usarse UNTIL, en este caso, el bucle se repetirá HASTA que se cumpla la expresión

A = 1

Do Until A > 10

Print A

A = A + 1

Loop

Fíjate que la expresión ha cambiado de <= (menor o igual) a > (mayor), ya que ahora se evalúa de esta forma:Hasta que A sea mayor que diez, REPITE todo hasta LOOP.Por supuesto también podemos usarlo después del LOOP:

A = 1

Do

Print A

A = A + 1

Loop Until A > 10

Aquí hago la misma aclaración que antes, si el valor inicial de A es más de 10 se ejecutará como mínimo una vez.Realmente para contar de forma secuencial y prácticamente para casi todo tipo de

Page 82: curso básico de programación en visual basic

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 trás", es decir de mayor a menor... quién sabe, lo mismo necesitas hacer un programa que cuente al revés...

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 comparación A >= 1 no se cumplirá, por tanto dejará de repetirse el bucle, pero esto también 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 evalúa 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 atrás, necesitamos un nuevo peldaño (esto en inglés quedaría "clavado"), en la escala evolutiva del FOR/NEXT (ahí queda eso!!!)Ya sin coñas, se necesita la palabra STEP para indicarle que no queremos ir de uno en uno de forma ascendente, en nuestro ejemplo lo usaríamos así:

For A = 10 To 1 Step -1

Print A

Next

De esta forma contará desde 10 hasta 1, restando uno en cada repetición.Pero, ¿que hacer si queremos usar otros valores?Simplemente ponerlo después de STEP, por ejemplo:For A = 10 To 1 Step -1For A = 1 To 10 Step 3, etc, etc.

Page 83: curso básico de programación en visual basic

Insisto, todo esto está muy bien, pero en la práctica usaremos otras cosas además 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 salía del procedimiento, ¿recuerdas el Exit For?Para salir de los bucles podremos Exit y a continuación For, Do, etc. Pero NO podremos salir de un bucle WHILE/WEND.Ya veremos ejemplos para estos casos y otros que surgirán más 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 botón (CommandButton) haga un bucle entre dos valores que habrás 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 tendrás que hacer ninguna comparación para obtener el resultado, la solución es tan SIMPLE que seguramente la descartarás "porque no puede ser tan fácil"

Por supuesto, me gustaría que los bucles los hicieras tanto con FOR/NEXT y DO/LOOP. Ya puestos, podrías hacerlo con el WHILE/WEND e incluso con el GOTO...

¡Feliz programación!

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 montón de horas que le dedico a cada una de las entregas... si, aunque no te lo creas, le dedico más de 5 horas a cada entrega...

Si no pasa nada raro, seguramente en este mismo mes habrá una nueva entrega.Nos vemos.Guillermo

Hola fans del curso básico, es que poco a poco se están apuntando más 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, compañeros de clase, gente de tu curro y demás personal que te encuentres por la calle, 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 compráis 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 ponía, ya

Page 84: curso básico de programación en visual basic

que últimamente no recibo preguntas ni dudas ni quejas porque algo estuviera mal... salvo algún que otro despistadillo que ha empezado por la primera entrega, hace los ejercicios y me "mailea" diciéndome 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 páginas, guardarlas en el disco duro y después leerla al montón de días...

Bueno, después 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 también se puede hacer de esta forma:

i = A

Do While i <= B

Print i

i = i + 1

Loop

' Y de esta también

i = A

While i <= B

Print i

i = i + 1

Wend

2.)

Private Sub Command1_Click()

Page 85: curso básico de programación en visual basic

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.)

' Añadir estas líneas:

Dim D As Integer

'...

'...For

D = D +1

'...

Next

Label1 = "Número de repeticiones: " & D

Y eso es todo, no pongo otras posibles formas de obtener los resultados, porque tu mismo habrás comprobado si están bien o no.

Ahora empezamos con la entrega real, es decir la séptima.Ya disponemos de instrucciones suficientes para empezar a "profundizar" en las cosas más difíciles... o casi.Ya sabes que hay que usar Option Explicit en todos los módulos de código... Esto no es obligatorio, pero si no quieres que perdamos la amistad, usalo. Gracias.

Page 86: curso básico de programación en visual basic

También cómo usar las variables y los diferentes tipos, puedes hacer bucles, tomar decisiones, realmente es más correcto decir: hacer que VB tome decisiones, ya sabes cómo pedir datos al usuario y también cómo mostrarlos. Sabes crear tus propias instrucciones... ¡Jo! ¡Cuanto sabes! Me tienes "anonadado"

Pero aún no sabes una cosa: cómo crear Funciones¿Qué son las funciones?Para simplificar, te diré que una función 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.¿Cómo declarar/codificar una función?Ámbito Function Nombre ([parámetros]) As TipoDónde Ámbito puede ser Public o Private, dependiendo de la "cobertura" o visibilidad. Ya sabes, Private sólo es visible en el propio módulo en el que se encuentra definido y Public es en todo el proyecto, incluso fuera de él...Los parámetros son los valores que esta función puede necesitar para "cumplir" su misión. Éstos son opcionales, es decir puede tenerlos o no, incluso si tiene, puede ser uno o varios, para declarar varios parámetros 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 función!, ya que no forman parte del lenguaje Basic...El tipo es para saber que tipo de dato devolverá la función.

El valor devuelto por una función lo podemos usar para asignarlo a una variable: a = MiFunción()o incluirlo en una expresión: If MiFunción() + 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 función podrías devolver un valor cero para indicar que no existe el archivo y un valor distinto de cero indicaría que el archivo en cuestión existe. Por tanto, podríamos usarlo de esta forma:If Existe(NombreArchivo) Then ...Ya que estamos puestos, veamos cómo hacer esta función de forma simple y así te explico una cosa muy importante de toda función: ¡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 mísmo nombre que la función.Ya vimos que LEN devuelve el número de caracteres de la cadena que ponemos entre los paréntesis; si, LEN también es una función, pero incluida en el propio Visual Basic.Dir$ es otra función del VB que devuelve el nombre de un archivo, (sólo el nombre), o una cadena vacía, en caso de que no haya ninguno en la dirección pasada por el parámetro que se ha usado. Para saber más de esta función, así como de otras, puedes buscar en la ayuda...

Page 87: curso básico de programación en visual basic

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, según se use en un expresión o en una asignación.Pero además de estos signos, podemos usar en nuestras expresiones unos operadores lógicos, estos son: AND, OR y NOTPodríamos desear hacer una comparación y comprobar si varias cosas se cumplen, por ejemplo:If A>10 And Len(Nombre)<>0 Then ...Para que esta expresión 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 ...cumpliéndose cualquiera de las dos, se acepataría como válido.Cuando el If se procesa, se toma todo lo que hay entre IF y THEN y se considera como una sóla expresión.Si quieres puedes asignar a una variable el resultado de una expresión, 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 comparación, las dos hubiesen devuelto un valor verdadero.

El tercer operador lógico (Not) sirve para negar algo, es decir invertir el valor contenido en una variable, o casi...If Not A>10 Then ...

Page 88: curso básico de programación en visual basic

Parece lógico el resultado, ¿verdad?, si no se cumple que A sea mayor que diez, será cierto; comprobémoslo:

Dim A As Integer

'¿recuedas que tienes que poner Show?

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 después se hace Not 0 que da como resultado -1 (verdadero), por tanto se cumple la condición.Ya vimos que el valor devuelto por una variable se puede usar en una comparación, 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

También 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 esperábamos... Cuando hicimos Not 0 era evidente, ya que se convierte en -1, pero Not 5 no se convierte en

Page 89: curso básico de programación en visual basic

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, sólo decirte que la responsable de todo es la notación binaria... los ceros y unos que dicen que es el lenguaje nativo de los cacharros estos... talvez más adelante tratemos un poco de la notación binaria, pero no por ahora... recuerdo que en mis tiempos del GwBasic la usaba bastante, incluso tenía rutinas para "representar" en ceros y unos un número...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

' Recibe una cadena que será un número decimal

' Devuelve ese número representado por ceros y unos

'

Dim i As Integer

Dim lngNum As Long ' Long, por si las moscas

Dim sTmp As String ' Cadena temporal

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

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 sólo puede ser 0 ó 1, no se complica demasiado la vida. Prueba a escribir el valor 0 y el valor -1 y conviertelo a notación binaria, fijate lo que el Visual Basic normalmente ve.

Prueba con el tema de la notación binaria, así sabrás realmente cómo funciona todo esto de las comparaciones (por dentro).Lo que nunca falla es completar la expresión, por ejemplo si haces esto:

Page 90: curso básico de programación en visual basic

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 sería más fácil, o al menos más inteligible, hacerlo así:

If A = 0 Then

Print A; "es CERO"

Else

Print A; "NO es CERO"

End If

Es que algunas veces se puede uno complicar la vida más de lo necesario...

Cuando quieras comprobar un valor devuelto por cualquier expresión, puedes hacerlo asignándolo a una variable o bien mostrando el valor: Print Not A

Cuando se usa AND pra evaluar varias partes de una expresión 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 lógico, algunas veces puede llevar a confusión e incluso producir efectos no deseados en el programa.Prueba con esta nueva versión de la función 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 más corto, pero talvez menos evidente:

' Label1 = Archivo & IIf(Existe, " Si", " No") & " existe"

DoEvents

End Function

Private Sub Form_Load()

Page 91: curso básico de programación en visual basic

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

End Sub

En el ejemplo comprobarás que a pesar de que la segunda parte de la comparación 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 expresión a pesar de que la primera A>10 es FALSA. Imagínate que en lugar de ser una función rápida, hubiese sido otra cosa que tardara un poquito más de la cuenta...Para casos como estos, (la verdad es que no son demasiado habituales), deberías 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

Page 92: curso básico de programación en visual basic

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 más largo y haya que usar más código, pero en ocasiones es más "resultón".Usando este nuevo "estilo", sólo se comprobará si existe el archivo cuando A sea mayor que diez.Lo que debes sacar en claro de todo esto es que después de un THEN puedes "anidar" más expresiones IF...THEN...ELSE. Incluso se puede usar en una sola línea, sólo que el resultado "visual" del código 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 podríamos usar el caracter _ que se puede usar en VB para separar líneas largas, pero es como si estuviese toda en la misma línea, así que la línea anterior, se quedaría así:

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"

Fíjate 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 sólo estético y para VB todo se trata de una misma línea, por tanto tendrá un límite de caracteres posibles a usar, el límite que VB le ponga, que creo que es 1024... pero no me hagas demasiado caso...

Antes he mencionado la palabra "anidación", ésta se usa para indicar que una serie de instrucciones están dentro de otras. En este caso hemos anidado dos IF... THEN, pero lo más habitual es hacerlo con los bucles (FOR, DO, etc.), veámoslo:

Dim i%, j%, c%

For i = 1 To 10

Page 93: curso básico de programación en visual basic

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 podrán continuar los de fuera.En el ejemplo, por cada repetición 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 impresión de que el programa se ha quedado "colgado", prueba a poner otro bucle dentro del j y cambia los valores máximo 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 también en los bucles, podríamos necesitar que el Visual Basic nos mostrara alguna indicación de que está "ocupado", por ejemplo cambiando la forma del cursor del ratón, 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 seaMousePointer = vbDefault ' 0 si usas VB3

Pero algunas veces el cursor no se cambia... para asegurarnos que cambie, usa el DoEvents después de asignar el valor para el reloj de arena. De esta forma permitimos que Windows procese sus mensajes (¿recuerdas?) y así tiene ocasión de cambiar el puntero del ratón.

Bueno, hasta aquí llega esta entrega. No hay ejercicios, sólo te pediría que revisaras la ayuda y te leyeras lo que allí pone referente a las instrucciones que vamos viendo... aunque me imagino que tendrás 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, más que nada para saber si voy bien encaminado y no me pierdo, lo que pretendo es que se entienda y que aprendas...

Ya me contarás.Nos vemos en la próxima entrega, que espero que no sea tan tardona como esta...

Ya estamos de nuevo por aquí, esta vez he procurado ser un poco más rápido para que no pierdas el hilo...(realmente he sido demasiado rápido, pero no te acostumbres)

Page 94: curso básico de programación en visual basic

El problema de escribir las entregas, no es porque no sepa que poner... a este nivel aún se bastantes cosas... ejem! Lo que ocurre es que normalmente suelo hacerlo en papel... quien lo diría ¿verdad?Y es porque no tengo ordenador en mi "piso" y allí es dónde aprovecho, por aquello de la tranquilidad, para concentrarme en las cuatro chorradillas que voy a decir... después toca pasarlo a "limpio" y como no soy demasiado diestro en esto de escribir a máquina, hice rabona cuando tenía clases de mecanografía, pues algunas veces tardo más de la cuenta. Pero como ahora estoy en unas vacaciones "virtuales", he aprovechado y le he mangado el portátil a mi jefe, con la excusa de que se lo voy a limpiar de "basura" y todo ese rollo.

Vale, no me enrollo más con mis cosillas y vamos a pasar a un tema que, por su extensión, 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í debería ser, ya que nadie me ha preguntado sobre el tema), pero hay veces que necesitamos más.Imagínate que quieres saber cuantas veces por hora te rascas la cabeza intentando comprender lo que hay en mis páginas...Podríamos tener unas cuantas variables, una por cada hora del día, con nombres como: Hora1, Hora2, etc. y cuando te arrasques a las 22 horas, harías esto:Hora22 = Hora22 + 1Con lo que aumentas en uno el contenido de la variable Hora22... y tu dirás... ¿que problema hay? Ninguno, pero es que se me ha ocurrido contarte esto como podría haberte contado otra cosa. Pero imagínate que quieres sumar las veces que te has rascado la cabeza en todo el día, podrías hacer:VecesDia = Hora1 + Hora2 + ...+ Hora24Tampoco hay problema, sumamos todas las variables (24 en este caso) y guardamos el total en la variable VecesDia.Pero la cosa se va complicando, ¿verdad? ¿No lo crees? Pues toma un poco más de complicación:¿Cómo harías para saber la hora en que más veces te has rascado la cabeza?No te doy la solución... 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 después no me preguntes si está bien o no... sería una pérdida de tiempo, ya que, como vas a ver dentro de muy pocas líneas, hay una forma de hacerlo bastante más simple.

Toda esta retahíla, 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 aún cuando ese algo sea lo que yo escribo...

Un poco de historia (la mía)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 inglés que tenía 50 juegos para el Vic-20, sabía que funcionaba, pero no sabía cómo...Durante un montón de tiempo, (a lo mejor fue una o dos semanas, es que antes los días duraban más que ahora), usé métodos parecidos, sin saber porqué funcionaba... sólo sabía que funcionaba y simplemente lo aceptaba. Después cayó en mis manos un libro, esta vez en español, y me puse a leer la parte que explicaba esto de las

Page 95: curso básico de programación en visual basic

"matrices", antes no eran arrays... la verdad es que lo leí como 20 veces o más... y parecía que nunca iba a lograr asimilarlo, es que soy bastante duro de mollera y antes, que era más joven, calculo que unas 14 veces más joven que ahora, tenía la cabeza más dura. En aquella ocasión si que me hubiese venido bien el programilla este de rascarme la cabeza...Esto es lo que decía, el susodicho libro:

"El lenguaje BASIC permite definir otro tipo de variables numéricas:A(1), A(2)...A(N)se llaman variables con índice y están formadas por un nombre de variable, [...], seguido de un número natural entre paréntesis. [...] 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 párrafo ese está sacado de un libro que fue de los pocos que tuve, al menos en castellano, de los ingleses sólo me interesaban los listados, que era lo único que prácticamente traían. La cosa es, que a pesar de eso, hasta aprendí un poco, a duras penas, sobre todo porque en aquellos tiempos no tenía a quién preguntarle, ni quién me explicara algunas de las muchas dudas que tenía... y si no tenía más dudas era porque tampoco había 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 más practicas... o terminas por aburrirte o aprendes...

Vamos al tema, pero sin teorías, las teorías se las dejo a los eruditos... ellos saben cómo explicar "bien" las cosas... Y que conste que no tengo nada en contra de las teorías, lo que ocurre, al menos a mi, es que prefiero entender las cosas de forma práctica, que soy "mu" torpe yo y si no lo veo funcionar, no me entero...

En el ejemplo ese de las horas, nos vendría muy bien que pudiésemos usar otra variable para la hora de nuestro "picor" y hacer algo como:HoradelPicor = HoradelPicor + 1, dónde "delPicor" sería la hora en que nos rascamos la cabeza, así si "delPicor" es 22, incrementar Hora22. Sí, lo reconozco, una vez intenté hacerlo, creía que se podía hacer así:delPicor = 22HoradelPicor = HoradelPicor + 1Pero el basic no hacía lo que yo quería, ni siquiera me daba error, si en aquellos tiempos hubiese existido el Option Explicit, me habría 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 creía, lo único que cambiaba era la forma... total, por un par de paréntesis...Hora(delPicor) = Hora(delPicor) +1Con esto le decimos al Basic: coge lo que hay guardado en la variable que está en la posición delPicor del array Hora...

Vale, captado. Me estoy lanzando y aún 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 número (í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 más veces? Vale, admito que tampoco he sido

Page 96: curso básico de programación en visual basic

demasiado claro, es que realmente no es tan fácil 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 paréntesis?" (en caso de que sea una expresión en lugar de un número o una variable, la evaluaría primero y usaría el resultado), una vez que sabe cuanto vale lo que está dentro del paréntesis... "ahora cojamos, del array, el contenido de la variable que está en esa posición" (para el basic un array no es más que una serie de variables que tienen el mismo nombre y lo único que varía es "la dirección" 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 "variabilísticos".Ahora fíjate cómo podemos sumar las veces que nos hemos rascado la cabeza a lo largo del día:For i= 1 to 24vecesDia = vecesDia + Hora(i)NextCada 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 más veces la cabeza te lo dejo como ejercicio, sólo te diré que si haces esto:masVeces = Horas(HoraMasVeces)sabrás 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 demás, en cualquier parte.Imagínate que en la posición 22 del array Hora, es decir en Hora(22) tenemos guardado un valor 15, al hacer esto:Print Hora(22) * 10mostraría 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.Y tu dirás: esto mismo es lo que hace con las demás 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 día, las páginas del Guille y que sabes que serán tres veces diarias, lo hay masoquistas... Podrías hacer algo como esto:Vez(1) = 20: Vez(2) = 22: Vez(3) = 23 '...(de nueve a diez vas a cenar)con lo cual tendrías un array con tres índices. El valor de cada una de las variables del array "Vez", sería la hora en que "guilleas" ... y si quieres incrementar los "rascones" de la hora que está en la posición H, (que puede ser 1, 2 ó 3):Ahora = Vez(H) ' Si H vale 1, Ahora sería igual a 20Hora(Ahora) = Hora(Ahora) + 1Pero esto podrías hacerlo ahorrándotela variable Ahora, sería así:Hora(Vez(H)) = Hora(Vez(H)) + 1¿Complicado? Pues si... que quieres que te diga... pero dejémoslo estar...

Veamos cómo 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 más de 10 "posiciones", aunque también puede ser que lo leyera en algún sitio, no importa...Para decirle al Basic que vas a usar tres variables en el array Vez, hay que hacerlo de

Page 97: curso básico de programación en visual basic

esta forma:Dim Vez(3)Ahora lo que necesitamos es un array para guardar los picores de las 24 horas del día:Dim Hora(24)

Bueno, ya sabes casi todo lo que tienes que saber de los arrays... ahora cómprate un buen libro "teórico" y estúdiatelo... o léete lo que dice el manual del Visual Basic... que también puede valer.

¿Aún sigues por ahí...?Bueno, ya que insistes, te explicaré algunas cosillas más...

Los arrays son variables, algo especiales, pero variables al fin y al cabo.Por tanto, podemos tener arrays numéricas, de carateres y en definitiva de cualquier tipo que el Basic permita, lo único que tenemos que hacer es indicárselo al reservar memoria:Dim Vez(3) As IntegerDim Amigos(1000) As StringDim 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 posición más, no por nada en especial, sino porque empieza a contar desde cero; por ejemplo en el array Vez, el índice más bajo que podríamos usar es el 0 y el más alto el 3.Esto ha sido así desde siempre... aunque en un intento de "cambiar" las cosas, un listillo dijo: "El Basic debería empezar a contar desde uno" y se sacaron una nueva instrucción de la manga, desde mi punto de vista lo podrían 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 yOPTION BASE 0 para empezar por CERO, esta será la predeterminada.

Por tanto si usamos este código:Option Base 1Dim Vez(3) As IntegerCrea un array con tres variables (del 1 al 3)

Más adelante, otro listillo, (este fue un poco más 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. Imagínate que tu sólo te rascas la cabeza de 10 a 23, puedes dimensionar así el array Hora:Dim Hora(10 To 23) As IntegerDe esta forma sólo "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 posición 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 aplicación tiene información importante pendiente de guardar, o se encontraba en medio de un proceso largo de cálculo... realmente si que será grave... en otra ocasión veremos cómo 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 posición anterior o posterior a las que tenemos declarada.

Page 98: curso básico de programación en visual basic

Pero... ¿y si necesito más 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 había almacenado...

Cuando yo empecé con el Basic (¿otra batallita?, habrá que seguirle la corriente...), no existía el ReDim. Pero si existía una forma de conseguir esto mismo. El truco consistía en "borrar" el array creado y volver a dimensionarlo... también recuerdo que me dio muchos quebraderos de cabeza adaptar mi código (escrito en el intérprete GwBasic... la GW ¿será Gates, William?) a un compilador... eso de usar varias veces el DIM no lo digería 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 HoraCon esto conseguimos lo mismo que con ReDim Vez(5) As Integer:Erase VezRedim Vez(5) As Integer

Pero, y si no quisiéramos perder los valores anteriores...Pues copia los datos en otro Array temporal, borras el primero, lo vuelves a dimensionar con el nuevo número de elementos y a continuación copias los datos del array temporal en el array que acabas de redimensionar, después borras el array temporal, ya que no lo necesitarás...No, no es necesario tantas cosas, pero esto es lo que había que hacer con el VB antes de la versión 3 y con el QuickBasic antes de la versión 4, ahora sólo harás esto:ReDim Preserve Vez(5)Además 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, (podría haber hecho un chiste malo con esto del Preserve, pero me abstengo...), es que puedes ampliar o reducir el número de variables de un array... supón que después 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 sólo reserves la memoria que vas a necesitar.

En la siguiente entrega veremos más cosas de los arrays, así como algunas otras instrucciones, pero no te voy a adelantar nada, no sea que después no esté lo que tengo pensado y te mosquees.

Ahora vamos al apartado de los ejercicios, que además del que te dije casi al principio, te voy a poner otro más:1.) Tienes un array con un número 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 comentármelo, de esta forma no seguiré enrollándome con cosas que no hayan quedado "super claras". Esto será en la próxima entrega, que espero sea dentro de muy poquito, (aprovechando las vacaciones virtuales estas que tengo), así que si vas a comentarme algo, hazlo pronto.

Page 99: curso básico de programación en visual basic

Nos vemos.

Hola, ya ves que no puedo dedicarle todo el tiempo que yo quisiera a esto del curso básico. 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 más aplicado y no tardar tanto entre cada entrega porque a lo mejor no cumplo y después me echas la bronca...

De todas formas intentaré hacer un pequeño esfuerzo... y con la ayuda y compresión de todos vosotros espero conseguirlo... ¿cómo? pues muy fácil, me gustaría 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 días los dedico por completo a la maquetación de un periódico semanal de información local que editamos en nuestra empresa... y no sólo la maquetación sino que me tengo que encargar de llevar y recoger la filmación y hay que hacerlo fuera de Nerja, por lo que, a la "paliza" de la autoedición, hay que añadirle la paliza del viaje de ida/espera (algunas veces larga)/vuelta.Así que si estás interesado en echarme un cable, dímelo y te pondré en la lista de colegas "respondones" del Guille...Gracias por adelantado.

Después de la presentación y el "comecoco", vamos con las soluciones de los ejercicios.

Solución a los ejercicios de la Octava entrega

El primero:

'Poner este código en el Form_Load

Dim Hora(24) As Integer

Dim i As Integer, Mayor As Integer

'Llenar el array con números...

'(en esta entrega veremos cómo 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 número mayor es:"; Mayor

Un poco de explicación, ya que no creo que sea suficiente con enseñar la solución.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

Page 100: curso básico de programación en visual basic

lo veremos en esta misma entrega.La cuestión es comparar el contenido de cada una de las horas con la variable que guardará el número mayor. Al principio esta variable, como ya deberías saber, tiene el valor CERO, así que cuando se haga la comparación If Hora(i)>Mayor Then, si la variable que está en la posición "i" del array "Hora" tiene un valor mayor que cero, se cumplirá la condición y se pasará a asignar ese valor a la variable que contendrá el número mayor de los 24 que tenemos en el array.El bucle continúa y cada vez que se cumpla la condición 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... prepárate para la solución 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 'Sólo si no vale cero

If Menor = 0 Then 'Si aún no tiene un valor

Menor = Hora(i) 'se lo asignamos

Else

If Hora(i) < Menor Then 'Si el contenido de Hora(i) es menor

Menor = Hora(i) 'lo asignamos como menor

End If

End If

End If

Next

Print "El número menor es "; Menor

Este está más o menos explicado en los comentarios, pero voy a dejártelo un poco más claro:La cuestión 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 aún no le hemos asignado ningún 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 número más pequeño, por tanto lo asignamos para que siempre "Menor" tenga el número menor (valga la redundancia).Pero y si quisiéramos tener en cuenta también el CERO... Pues que tendríamos que hacerlo de otra forma, ya que esta es sólo para el caso expuesto... te dejo que lo pienses, pero no es demasiado difícil, incluso más simple que esta solución, lo que

Page 101: curso básico de programación en visual basic

ocurre es que entran en juego pequeños 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 sólo será un pequeño 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:

Números Aleatorios

En algunos casos vamos a necesitar generar números aleatorios, (números sacados al azar, al menos en teoría...), y si no necesitas usar números aleatorios, vamos a usarlos en algunos de los ejercicios, así que voy a explicar cómo va esto:La función que se usa para generar números aleatorios es: RNDEsta función devuelve un número 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 algún "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 mayoría 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 serán de 1 a 6.Si queremos valores del 65 al 90 la expresión sería esta: x = Int(Rnd * 26) + 65. Ya que Int(Rnd * 26) producirá un número 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 código ASCII de una letra de la A a la Z, (en mayúsculas), ya que los códigos ASCII de las letras son: A=65, B=66... Z=90 en caso de que sean mayúsculas, para obtener los valores en minúsculas sólo hay que añadirle 32 y ya los tendremos porque a=97, b=98... z=122.En estos rangos se excluye la eñe, tanto mayúsculas como minúsculas y las vocales acentuadas... es que los señores 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 Niño se llamaba Alex Nino, al menos en los créditos de los comics USA...El problema de los números aleatorios, es que no son tan aleatorios, es decir cada vez que se inicia el programa produce el mismo número, (al menos en los basics anteriores, he leído que ahora el VB4 no genera la misma secuencia cada vez que se inicia el programa, eso lo veremos después), vamos a ver un ejemplo para que lo compruebes, esto lo he comprobado con el VB2 (que es el que tengo en el portátil que me he llevado a casa para escribir las entregas, hasta que mi jefe me lo pida... que será en pocos días, puede ser), se supone que en los demás funcionará igual, pero como te digo para el VB4 hay un "truco" que veremos después... cuando lo compruebe, je.Escribe esto en el Form_Load del nuevo proyecto que habrás 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

Page 102: curso básico de programación en visual basic

End Sub

A mí me ha mostrado 17.63869, ejecútalo varias veces y verás que siempre muestra el mismo número... Existe una forma de solucionar esta falta de "aleatoriedad" y es cambiando la "semilla" que se usa como base para la imPLANTAción de números aleatorios, para ello se usa Randomize seguido de un número, pero si el número es el mismo... no conseguimos nada... Prueba poniendo Randomize 5 después 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 número "fijo" como semilla para la generación de nuevos números, el número producido siempre es el mismo, esto está bien para hacer pruebas fijas o cuando queremos "dárnosla" de mago con los colegas que saben menos que nosotros, ya que podemos "predecir" los números que mostrará en una secuencia seguida... con este "truco" dejaba "alucinado" a los chavales a los que le daba clases... sólo tenía que memorizar una serie de números y se quedaban "alucinados" cuando les decía el que iba a salir... claro que después tenía 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 función que devuelve el número de segundos transcurridos desde la media noche (TIMER) y usando esta función como "semilla" lograremos producir números que "casi" será aleatorios... al menos serán más difíciles de "pronosticar", cambia el Randomize 5 por Randomize Timer y verás que ya no se produce el mismo número 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 más "compatible".¿Que tal un jueguecito para practicar?Hay que hacer un programa que genere un número entre uno y cien y hay que intentar adivinarlo...Si el número 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 cómo hacerlo...Para preguntarte el número y guardarlo en la variable N, haz esto:N = Val(InputBox("Escribe un número entre 1 y 100")).El InputBox muestra una pantalla preguntando y devuelve lo que escribamos o una cadena vacía si pulsamos en cancelar, el Val convierte esa cadena en número.Para avisar que el número N, (el que nosotros le decimos al VB), es menor o mayor, cambiar xxxx por lo que corresponda:MsgBox "El número " & CStr(N) & " es xxxx"De esta forma se mostrará un cuadro de diálogo indicando si vamos bien encaminados o no...

Ya mejoraremos o ampliaremos este "ejercicio" para hacer más cosas, incluso que el ordenador averigüe el número... que sin ningún tipo de "suerte" lo adivinará en 5 ó 6 veces, lo mismo que tu deberás hacer si sigues algunas normas o trucos... sólo decirte lo de "divide y vencerás" (no sé porqué me ha dado ahora por esa cita...)

En la próxima entrega veremos más cosas sobre los arrays...

Si te atreves podrías hacer los siguientes cambios al "problemilla" planteado anteriormente:1. Comprobar que el número introducido en el InputBox esté entre 1 y 100, en caso de que no sea así, volver a preguntar.

Page 103: curso básico de programación en visual basic

2. Si se escribe CERO mostrar el número que el VB había "pensado" y terminar.3. Cuando lo acertemos que nos indique en cuantos intentos lo hemos conseguido.4. Un programa que sea al revés, es decir: que nosotros pensemos un número del 1 al 100 y el VB intente adivinarlo, para ello deberá mostrarnos un número y nosotros indicarle si lo ha acertado.5. Otro igual, pero indicándole si nuestro número es Menor, Mayor o es correcto... habrá que darle las mismas oportunidades... (este es el que tiene el "truco" del divide y vencerás...En los casos 4 y 5 que muestre también el número 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 función:

If MsgBox("Mi número 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 diálogo con dos opciones "SI" y "NO", el número 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 podría hacer así:

If MsgBox("Mi número 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 inglés, es más intuitivo. Esto de los MsgBox lo veremos en una entrega "especial"

Las soluciones están en este link... no quiero que estés esperando las soluciones hasta la próxima entrega... para que veas que algunas veces soy un poco "más considerado"

Bueno, creo que esta entrega no debería tener demasiadas dudas... en los manuales o la ayuda viene explicado esto de los números aleatorios, pero si quieres dejarme algún comentario, hazlo e intentaré aclararte cualquier duda... pero no lo hagas sobre las soluciones, ya que las obtienes en el link del párrafo anterior.

Que lo "randomices" bien...Nos vemos.

Pues aquí están las soluciones, creo que esta forma de darlas será la mejor, ya que no tendrás que esperar a que esté lista la siguiente entrega para saber si has conseguido resolver los problemillas/ejercicios con éxito...

Page 104: curso básico de programación en visual basic

El juego básico:

Dim n As Integer

Dim x As Integer

Randomize Timer

x = Int(Rnd * 100) + 1

Do

n = Val(InputBox$("Escribe un número del 1 al 100"))

If n = x Then Exit Do

If n < x Then

MsgBox "El número " & CStr(n) & " es menor."

Else

MsgBox "El número " & CStr(n) & " es mayor."

End If

Loop

MsgBox "Lo has acertado."

1. Comprobar que el número 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:

Dim n As Integer

Dim x As Integer

Randomize Timer

x = Int(Rnd * 100) + 1

Do

Do

n = Val(InputBox$("Escribe un número del 1 al 100"))

Loop While n < 1 Or n > 100

If n = x Then Exit Do

If n < x Then

MsgBox "El número " & CStr(n) & " es menor."

Else

MsgBox "El número " & CStr(n) & " es mayor."

End If

Loop

Page 105: curso básico de programación en visual basic

MsgBox "Lo has acertado."

2. Si se escribe CERO mostrar el número que el VB había "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 número del 1 al 100"))

If n = 0 Then

MsgBox "Mi número 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 número " & CStr(n) & " es menor."

Else

MsgBox "El número " & 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

Page 106: curso básico de programación en visual basic

Do

Do

n = Val(InputBox$("Escribe un número del 1 al 100"))

If n = 0 Then

MsgBox "Mi número 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 número " & CStr(n) & " es menor."

Else

MsgBox "El número " & CStr(n) & " es mayor."

End If

Loop

MsgBox "Lo has acertado en " & CStr(v) & " veces."

4. Ahora al revés, es decir: que nosotros pensemos un número del 1 al 100 y el VB intente adivinarlo, para ello deberá mostrarnos un número y nosotros indicarle si lo ha acertado.

Dim x As Integer

Dim v As Integer

Randomize Timer

Do

x = Int(Rnd * 100) + 1

v = v + 1

If MsgBox("Mi número es: " & CStr(x) & Chr$(13) & "¿He acertado?", 4) = 6 Then

MsgBox "Lo he acertado en " & CStr(v) & " veces."

Exit Do

End If

Page 107: curso básico de programación en visual basic

Loop

5. Otro igual, pero indicándole si nuestro número es Menor, Mayor o es correcto... habrá que darle las mismas oportunidades... (este es el que tiene el "truco" del divide y vencerás...

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 números aleatorios

Do

x = (z - a) / 2 + a

v = v + 1

If MsgBox("Mi número 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 número del ordenador es mayor

'debe estar entre x-1 y a

z = x - 1

Else

'El número del ordenador es menor

'debe estar entre x+1 y z

a = x + 1

End If

End If

Loop

Ahora a esperar a la décima entrega que será dentro de poquito... un mes, un año... ¿quién sabe?

Page 108: curso básico de programación en visual basic

Pues aquí están las soluciones, creo que esta forma de darlas será la mejor, ya que no tendrás que esperar a que esté lista la siguiente entrega para saber si has conseguido resolver los problemillas/ejercicios con éxito...

El juego básico:

Dim n As Integer

Dim x As Integer

Randomize Timer

x = Int(Rnd * 100) + 1

Do

n = Val(InputBox$("Escribe un número del 1 al 100"))

If n = x Then Exit Do

If n < x Then

MsgBox "El número " & CStr(n) & " es menor."

Else

MsgBox "El número " & CStr(n) & " es mayor."

End If

Loop

MsgBox "Lo has acertado."

1. Comprobar que el número 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:

Dim n As Integer

Dim x As Integer

Randomize Timer

x = Int(Rnd * 100) + 1

Do

Do

n = Val(InputBox$("Escribe un número del 1 al 100"))

Loop While n < 1 Or n > 100

If n = x Then Exit Do

If n < x Then

MsgBox "El número " & CStr(n) & " es menor."

Else

Page 109: curso básico de programación en visual basic

MsgBox "El número " & CStr(n) & " es mayor."

End If

Loop

MsgBox "Lo has acertado."

2. Si se escribe CERO mostrar el número que el VB había "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 número del 1 al 100"))

If n = 0 Then

MsgBox "Mi número 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 número " & CStr(n) & " es menor."

Else

MsgBox "El número " & 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

Page 110: curso básico de programación en visual basic

Randomize Timer

x = Int(Rnd * 100) + 1

Do

Do

n = Val(InputBox$("Escribe un número del 1 al 100"))

If n = 0 Then

MsgBox "Mi número 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 número " & CStr(n) & " es menor."

Else

MsgBox "El número " & CStr(n) & " es mayor."

End If

Loop

MsgBox "Lo has acertado en " & CStr(v) & " veces."

4. Ahora al revés, es decir: que nosotros pensemos un número del 1 al 100 y el VB intente adivinarlo, para ello deberá mostrarnos un número y nosotros indicarle si lo ha acertado.

Dim x As Integer

Dim v As Integer

Randomize Timer

Do

x = Int(Rnd * 100) + 1

v = v + 1

If MsgBox("Mi número es: " & CStr(x) & Chr$(13) & "¿He acertado?", 4) = 6 Then

Page 111: curso básico de programación en visual basic

MsgBox "Lo he acertado en " & CStr(v) & " veces."

Exit Do

End If

Loop

5. Otro igual, pero indicándole si nuestro número es Menor, Mayor o es correcto... habrá que darle las mismas oportunidades... (este es el que tiene el "truco" del divide y vencerás...

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 números aleatorios

Do

x = (z - a) / 2 + a

v = v + 1

If MsgBox("Mi número 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 número del ordenador es mayor

'debe estar entre x-1 y a

z = x - 1

Else

'El número del ordenador es menor

'debe estar entre x+1 y z

a = x + 1

End If

End If

Loop

Page 112: curso básico de programación en visual basic

Ahora a esperar a la décima entrega que será dentro de poquito... un mes, un año... ¿quién sabe?

Hola, ya estoy de nuevo por estos lares... para seguir dándole caña al curso básico.La entrega anterior fue un pequeño alto en el camino antes de continuar nuestra andadura por los escabrosos caminos de los arrays... ¡UF! ¿Quién 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 dimensión), si en el ejemplo de la octava entrega, el de los rascamientos, quisieras saber las veces que te has rascado cada día del mes... tendríamos otra vez el problema, podríamos usar un array para cada día del mes: Dia1(24), Dia2(24)... pero de nuevo tendríamos complicaciones para algunos cálculos...Por suerte para nosotros, existe otra forma de usar los arrays.En nuestro caso nos serviría el que los arrays tuviesen dos dimensiones, al estilo de una tabla con filas para cada día del mes y columnas para cada una de las horas del día en cuestión, para hacer esto, dimensionaremos un array de esta forma:Dim Dias(31, 24) As IntegerPara 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) = 1y por supuesto, podemos usarlo con bucles FOR:

For Dia = 1 To 31

For Hora = 1 To 24

RascadasMes = RascadasMes + Dias(Dia, Hora)

Next

Next

Después que estos dos bucles terminen, la variable RascadasMes tendrá el total de veces que nos hemos rascado cada uno de los días del mes que estamos procesando.Si queremos almacenar las rascadas de cada día de cada mes de un año, No Problem!Dim Meses(12, 31, 24) As IntegerDe esta forma solucionaríamos en problema, ya que al añadir una tercera dimensión, podemos usar esta para cada uno de los meses, por ejemplo, el total de veces que nos hayamos rascado a las 22 horas del día 30 del mes 9 (septiembre), estaría en: Meses(9, 30, 22)

Reconozco que este ejemplo de las rascada no es útil, pero por lo menos hemos visto cómo usar los arrays.Recuerda que los arrays pueden ser de cualquier tipo: Integer, String, Double, etc.

Page 113: curso básico de programación en visual basic

¿Cuántos elementos tiene un array?

En algunas ocasiones podemos necesitar saber el número 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 sería 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 más de una dimensión, tendremos que especificar la dimensión 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) 'Devolvería 1MayorMes = Ubound(Dias, 1) 'Devolvería 31MenorHora = LBound(Dias, 2) 'Devolvería 0MayorHora = UBound(Dias, 2) 'Devolvería 23

Redimensionando arrays multidimensionales

Veamos ahora cómo funciona el Redim y Redim Preserve con los arrays con varias dimensiones: igualSí, da lo mismo que el array tenga una o muchas dimensiones. Lo único que debemos saber es que no podemos cambiar el número de dimensiones, aunque sí el número de elementos de cada una de las dimensiones.Un ejemplo:Tenemos inicialmente esta declaración: Dim Meses(1 To 6, 1 To 31, 0 To 11) As Integery necesitamos ampliar la última dimensión 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 sólo dos y eso, no está permitido.Ni al revés 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)Erase DiasDim Dias(12, 31, 24)El problema es que perdemos los datos... cosa que, en caso de necesidad, podríamos 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á más adelante... todavía 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 asimilándolos... espero conseguirlo.

Page 114: curso básico de programación en visual basic

Nota:Hay que tener en cuenta que la única dimensión que podemos redimensionar en un array multidimensional es la última.

¿Cuantas dimensiones puede tener un array?

Si la mente no me falla, el número de dimensiones es 256. ¿Quién necesita tantas?Los valores menor y mayor de los índices están 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 deberías comprobarlo en los manuales.

Unas cadenas, por favor

Ya he comentado en la octava entrega que los arrays también 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 sólo podremos almacenar valores numéricos enteros. Incluso cuando lo Redimensionemos deberá tener el mismo tipo con el que en un principio lo habíamos dimensionado. Este inconveniente se solucionará en una próxima 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 códigos ASCII de la A es el 65 y la Z el 90.Ahora os pondré una forma "fácil" de clasificar ese array, la parte de la asignación es la que tú tendrás que hacer.

'

Const MaxCadenas = 100

Dim cadena(1 To MaxCadenas) As String

Dim c As Integer

Dim sTmp As String

Dim i As Integer

Dim j As Integer

Randomize Timer

list1.Clear

'Asignar los valores

'...Escribe aquí tu código...

Page 115: curso básico de programación en visual basic

'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 método 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...Supón que en la posición cadena(1) tienes almacenado "HOLA" y en la posición 2 está la palabra "AMIGO"La condición 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 comparación 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.También deberás saber que los números están 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á después que la ZSi quieres saber los valores ASCII de los caracteres "más o menos" stándard, haz este bucle:

'Códigos ASCII

For i = 32 To 122

Debug.Print i; Chr$(i)

Next

Page 116: curso básico de programación en visual basic

Ahora los ansiados ejercicios, (realmente ha sido cortita esta entrega ¿verdad?)

Para los ejercicios, usando este trozo para guardar números aleatorios en un array unidimensional, espero que no tengas problemas para guardarlos en un array multidimensional.

T = Int(Rnd * 31) + 20 'Número 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 explicación...)

3. Modificar el ejemplo anterior para que el número de veces que te rascas

valga (aleatoriamente) de 100 a 1000 y saber también cual de estas

horas tiene el valor menor (en caso de que haya varios, sólo tienes que

averiguar uno de ellos)

Para que no te compliques mucho la vida, decirte que con un par de líneas, puedes averiguar el mayor o el menor... no sea que quieras hacer un mogollón de comparaciones.

¡A disfrutarlo!

Esta entrega no da más de sí, no es que haya querido hacerla deprisa y corriendo, es que realmente lo "básico" está aquí explicado, si quieres profundizar más, ya sabes dónde buscar información... en los libracos esos que venían con tu VB.Si aún así, piensas que no te has enterado... repásatela unas dieciséis veces más...

Nos vemos.

10/Ene/98: Las soluciones de esta entrega.

Pues se me fue la olla más de la cuenta... y a pesar de que algunos me lo han recordado... en fin... aquí están las soluciones de los ejercicios de la décima entrega.

Page 117: curso básico de programación en visual basic

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í.

[repetición 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 números aleatorios en un array unidimensional, espero que no tengas problemas para guardarlos en un array multidimensional.

T = Int(Rnd * 31) + 20 'Número 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 explicación...)

3. Modificar el ejemplo anterior para que el número de veces que te rascas

valga (aleatoriamente) de 100 a 1000 y saber también cual de estas

horas tiene el valor menor (en caso de que haya varios, sólo tienes que

averiguar uno de ellos)

Para que no te compliques mucho la vida, decirte que con un par de líneas, puedes averiguar el mayor o el menor... no sea que quieras hacer un mogollón de comparaciones.

Las soluciones:

Page 118: curso básico de programación en visual basic

Primero: He hecho un pequeño cambio al ejemplo que se usaría, para que también se incluya la HORA CERO, esta sería una de las formas de conseguirlo:

'Ejercicio 1

Dim T%, i%, H%, Horas%(0 To 23)

Randomize

T = Int(Rnd * 31) + 20 'Número 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 algún 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

Page 119: curso básico de programación en visual basic

'

'===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

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 empezarán por CERO, debemos asignar un valor "inexistente" a los valores de las variables que contendrán las horas de inicio y de mayor valor... ¿Por qué? Porque el Basic asigna automáticamente el valor cero a las variables numéricas y como estas variables pueden tener un valor de CERO (0) a Veintitrés (23), el valor por defecto puede ser un valor válido... así que asignándole -1 (menos uno), nos aseguramos que no tendrá un valor que pueda confundirnos... ¿lo captas? si no es así... ya te enterarás cuando te ocurra... ;-)

El segundo: Usando la parte de asignación 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

Page 120: curso básico de programación en visual basic

UltimaHora = NoAsignado

'Las horas van de 0 a 23

For H = 0 To 23

'Si esta hora tiene algún 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 solución no tiene mayor inconveniente, una vez comprendida la primera solución.

El tercero: Para que el número aleatorio que se asigna esté entre 100 y 1000, cambia la asignación 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 sumándole 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 habrás comprobado que eso de averiguar el valor menor no es tan "lógico" como parece...La explicación 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:

Page 121: curso básico de programación en visual basic

1. Si HoraValorMenor aún 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 valores negativos... ya que si así fuera, al tener un valor cero,

éste sería mayor que cualquier número negativo...

'Ejercicio 3

Dim T%, i%, H%, Horas%(0 To 23)

Randomize

T = Int(Rnd * 901) + 100 'Número 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 algún valor, hacer las comprobaciones

If Horas(H) Then

'

'===Para saber la hora que tiene el valor menor===

'

Page 122: curso básico de programación en visual basic

'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...

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 pongámoslo 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 'Número 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

Page 123: curso básico de programación en visual basic

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

HoraValorMenor = NoAsignado

'Las horas van de 0 a 23

For H = 0 To 23

'Si esta hora tiene algún 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

Page 124: curso básico de programación en visual basic

'

'===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...

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:

Page 125: curso básico de programación en visual basic

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

End If

MsgBox sTmp

¿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é González Casares [email protected] por haberse entretenido en corregir los errores tipográficos 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.Quizás te preguntarás 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 décima entrega.

Y ya vamos al tema de hoy:

Nuestras propias variables

Page 126: curso básico de programación en visual basic

Ya has visto prácticamente la totalidad de tipos de variables que Visual Basic soporta, también has visto cómo agrupar variables para crear arrays.Ahora voy a explicar cómo 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 dirían los English-speaken esos)

En un UDT podemos incluir variables que estén relacionadas, realmente puedes incluir casi todo lo que se te ocurra, pero no tendría mucho sentido incluir cosas que no tengan relación, vamos, digo yo.Imagínate que quieres hacer una utilidad que dibuje puntos en la pantalla y necesitas tener la posición de, digamos 1000 puntos, una solución sería declarar dos arrays, una para la posición 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, haríamos algo como esto:

PuntoY(N) = y: PuntoX(N) = x

Y si lo que pretendemos es averiguar la posición del punto A, lo sabríamos así:

y = PuntoY(A): x = PuntoX(A)

Simplemente estaríamos usando un array para la fila (PuntoY) y otro para la columna (PuntoX), para simplificar todo esto, podemos crear un tipo en el cual tendríamos almacenado la posición de cada punto, para ello, hay que hacer una declaración 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 ¿cómo asignamos los valores?De una forma muy especial, para acceder a cada uno de los datos que puede

Page 127: curso básico de programación en visual basic

almacenar nuestra variable tendremos que especificar el nombre de la variable, un punto y a continuación la variable interna que nos interese...Veámoslo:

unPunto.X = 100

unPunto.Y = 20

Para saber el valor guardado en la X de nuestra variable lo sabríamos así: columna = unPunto.X

Siempre usando el punto después de la variable interna, esto puedes encontrártelo en algunos libros o manuales, usando la expresión: "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 refería al principio:

Dim Puntos(1000) As tPunto

Para almacenar la posición del punto N:

Puntos(N).X = x: Puntos(N).Y = y

Y seguro que ahora sabrás como obtener la posición del punto A.Pero también 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 sólo sirven para "mezclar" variables del mismo tipo, sino que puedes tener variables de varios tipos, incluso variables de tipos definidos... Sí, un verdadero lío...

Espero que después de leerte esta entrega y con los ejemplos que veremos en las próximas, (cuando le toque el turno al manejo de ficheros), se te aclararán las dudas.

Otro ejemplo clásico

Page 128: curso básico de programación en visual basic

Este es uno de los más usados para este tipo especial de variables y la verdad es que es también el más usado, enseguida sabrás porqué.

Situación: Tener los datos de todos los colegas y otros que no lo son tanto.Datos: Nombre y apellidos, dirección, teléfono fijo, teléfono móvil, dirección

e-mail, URL y cualquier otra cosa que se te ocurra.El Tipo: Para un caso como este, (simplificando un poco), podríamos usar

este tipo definido:

Type tColega

Nombre As String

Apellidos As String

Direccion As String

Poblacion As String

Edad As Integer

VecesQueLeHeMandadoUnMailYNoContesta As Long 'Esto por si soy yo 8-)

End Type

Fíjate en el último "campo" del tipo, es una chorrada (aunque más 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, podrías 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 más de 40 letras, sólo que las primeras 40 tienen significado... es decir que si al supercali... ese le añades más letras, sólo reconocerá las 40 primeras.Por ejemplo:SupercalifragilisticoespialidosoChitiChitiBangBangVolandoSupercalifragilisticoespialidosoChitiChitiBangBangParadoPara 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 aún 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 estarás usando), es conveniente "definir" la longitud máxima de las cadenas de caracteres, por ejemplo:Reservar 20 caracteres para el nombre ó 50 para la dirección, en este caso la declaración de las variables se harían así:

Page 129: curso básico de programación en visual basic

Dim Nombre As String * 20

Dim Direccion As String * 50

Y si están en un tipo definido:

Type tFijos

Nombre As String * 20

Direccion As String * 50

End Type

La ventaja de hacerlo así: al tener una longitud fija, podemos acceder a cualquier registro haciendo unos pequeños cálculos... aunque de esto se encarga de forma automática el propio Basic y como he dicho antes: lo veremos más después.

Vamos con algunos ejemplos.

Ya tenemos definido el tipo tColega, si queremos usarlo sólo 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 demás campos se haría igual.

Ahora, se nos presenta la situación 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 número uno:

misColegas(1).Nombre = "Maria de las Mercedes"

Para mostrarlos, simplemente hacemos un bucle que recorra este array y asunto concluido:

Page 130: curso básico de programación en visual basic

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 sabrás, que el índice de un array, el numérico ese que se pone dentro de los paréntesis, puede ser cualquier expresión numérica, que de como resultado un valor que esté dentro de los límites 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 "regañará" el VB diciéndote: Index Out of Range (o sea: T'as pasao, colega)

Un alto en el camino.

Toma papel y lápiz, porque esto es una nueva instrucción.

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 después el campo.Pues a partir del VB4, este asunto se ha simplificado y no sólo para los tipos definidos, ya verás, en un futuro no muy lejano, calculo que antes del año 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 enterarás bien...

La palabra mágica es: WITH

No te voy a hacer una presentación formal de esta instrucción, ya tienes el manual del VB o la ayuda y allí seguro que estará bien "definida", vamos a ver cómo 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 fácil! 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 estás refiriendo a misColegas(i), porque esa es la variable que has usado después de With.

Page 131: curso básico de programación en visual basic

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 más, te enterarás 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 tendrían que hacer con el VB3, sería esto:

Text1.SelStart = 0

Text1.SelLength = Len(Text1.Text)

Como comprobarás, está más claro si se usa el With

Además, se pueden anidar varios Withs... unos dentro de otros, pero siempre el PUNTO hará referencia al último que se ha puesto, esta situación 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 pequeño programa en el cual se introducirán los datos y todas esas cosas... Pero eso será mañana (hoy realmente, pero más tarde)

En seguida nos vemos.

Y estas son las soluciones a los dos ejercicios propuestos.

1.- Poder modificar uno de los colegas.

Este es el código a insertar en el Command3_Click:

Page 132: curso básico de programación en visual basic

Código para modificar un colega

El truco será primero mostrar sólo un colega y a continuación, modificar los campos que queramos, el código del Command2_Click quedaría así, para que al escribir un número en el Text7, sea este colega el que se muestre, en caso de que el número introducido sea menor que 1 o mayor que el número actual de colegas, se mostrarán todos.

Page 133: curso básico de programación en visual basic

Los cambios al Command2_Click

Lo prometido es deuda y a pesar de las numerosas y continuas desconexiones (cortes en la línea, ruido, etc, etc), aquí está el "final" de la undécima entrega... ¿Te creías que era el final del curso...? Pues aún te queda mucho que aguantar...

Vamos a continuar la entrega con un ejemplo.

Crea un nuevo proyecto, añade 6 labels y 6 textboxes, un par de commandbuttons...

¿Cómo? Que no sabes cómo hacerlo... hum!

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:

Page 134: curso básico de programación en visual basic

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... sitúala en la esquina superior izquierda, para ello púlsala con el botón izquierdo del ratón y arrástrala hasta arriba y a la izquierda... Pulsa otras cinco veces... y ve colocándolas 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 operación, pero los sitúa junto a cada una de las etiquetas anteriores. Una vez terminado todo el proceso, deberás tener seis etiquetas y seis cajas de texto.

Por último pulsa en el botón 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:

Page 135: curso básico de programación en visual basic

El form de prueba, con todos los controles

Hay una forma más rápida de hacerlo... y que además te permite, si quieres, crear arrays de controles.

Sería pulsando en el Label una vez, a continuación en el TextBox. Los seleccionas y le das a Edición/Copiar, también con el botón derecho del ratón.¿Cómo los seleccionas para poder copiarlo? Pulsa el Label, pulsa la tecla Control, dejando pulsada la tecla control, pulsa en el TextBox, verás que se quedan los dos "resaltados", ahora suelta la tecla Control, en el menú de Edición, selecciona Copiar (o Copy si tienes la edición inglesa).Ya están copiados en la memoria del VB, ahora en el menú Edición 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... Sitúa los nuevos controles debajo de los anteriores y repite la operación, pero en esta ocasión sólo tienes que volver a pegar... ya que aún siguen copiados en memoria.Ahora ocurrirán dos cosas, dependiendo de si le dijiste que SI o NO a la creación de arrays del Label, en caso de haberle dicho SI, sólo 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 después te interrogará sobre el TextBox1...Repítelo hasta que tengas 6 controles de cada en el form y después 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 serán 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.

Page 136: curso básico de programación en visual basic

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.

Ahora abre el panel de código, para hacer esto... cosa que a estas alturas ya deberías saber, es pulsando en el botón "View Code", ese que está en la ventana del proyecto, sí, esa... la de la esquina superior derecha.

Escribe esto:

El código de las declaraciones

Con esto acabamos de declarar el tipo definido, una constante con el número máximo 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 botón 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 más corto... por ejemplo: Veces.

El siguiente código lo pones en el Form_Load, para que se ejecute cuando inicies el proyecto.

Page 137: curso básico de programación en visual basic

Código que se ejecutará al iniciarse el proyecto

Esto otro lo escribes en el Command1_Click:

Código que se ejecutará al pulsar en el Command1

Y por último, escribe esto en el Command2_Click:

Page 138: curso básico de programación en visual basic

Código que se ejecutará al pulsar en Command2

Con esto, ya puedes escribir los datos correspondientes y después pulsas en Nuevo Colega, los datos escritos se asignarán a cada uno de los campos del tipo definido, como estamos usando un array, hay que especificar el número 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 número máximo, nos mostrará un mensaje indicándonos que ya no hay espacio para más.

El botón 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: Añade un nuevo TextBox, un nuevo CommandButton, en el

caption del botón escribes: "Modificar" y el número que introduzcas en el

nuevo TextBox será el colega a modificar.

2. Mostrar los colegas a partir de un número determinado,

por ejemplo, si en ese TextBox escribes 5, mostrar desde el 5º hasta el

último introducido, puedes usar el mismo botón Mostrar, de forma que si

pones 0 ó 1, te muestre todos.

Las soluciones están en este link, (es que como no lo haga ahora, se me olvida...), pero no hagas trampas e inténtalo primero... ya sabes que sólo tú sabrás si estás jugando limpio.

Y hasta aquí ha llegado esta undécima entrega, espero que no te impacientes hasta la siguiente, pero para que te vayas haciendo el cuerpo, vamos a tratar

Page 139: curso básico de programación en visual basic

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

Antes de empezar, recordarte que ya están las soluciones de la décima entrega. Te recomiendo que las veas y "las estudies", ya que tiene "truco"... y sería conveniente que te enteraras bien del porqué... Échale un vistazo y verás porqué te lo digo.

Ya vamos al tema de esta entrega... la número doce, (duodécima), es que es un lió esto de los números ordinales, así que usaré números normalitos, que todos los entendemos mejor...

Con esta entrega, empiezo la serie de manejo de datos almacenados en disco. Así que presta atención a esta y las siguientes entregas, para que esto del manejo de la información almacenada en disco te sea fácil de usar.

Además de las bases de datos, que también veremos en este curso básico... o en las secuelas que pueda tener... existe una forma de almacenar los datos que nuestra aplicación pueda manejar. Porque de qué serviría hacer, por ejemplo, un programa tipo editor de textos si no pudiésemos almacenar lo que se ha escrito...No, aún no te voy a explicar cómo hacer un editor de textos, antes hay que ver algunas cosillas más, 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 características en la forma de acceder a los datos.El más básico y también el más empleado, es el secuencial; con este tipo de fichero, los datos se almacenan y recuperan de forma secuencial, es decir: un dato después de otro...El inconveniente que tiene esta forma de almacenar los datos, es que para recuperar lo que esté en la décima posición, 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, después la segunda, tercera, etc., hasta llegar a la última.Para recuperar estos datos, actuaríamos de igual forma, pero asignando al array los datos leídos del disco. Recuerda que para asignar una posición 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 entenderás cuando veamos los otros tipos de accesos.También permite que puedas almacenar distintos tipos de datos... no te

Page 140: curso básico de programación en visual basic

preocupes, ya sabes que en estas entregas todo está "demostrado"... más o menos, ya que la teoría está bien para los teóricos, pero para los torpes como yo... lo mejor es la práctica... no es que quiera llamarte torpe... pero, así me siento menos solo... jé, jé.

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 posición, sin tener que leer primero todos los anteriores... Pero, no todo es perfecto... también tiene un "pequeño" 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 ocuparán el mismo espacio en el disco, aunque unas tengan más caracteres "válidos" que otras... También se pueden mezclar números y cadenas de caracteres... pero eso tiene también 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 información almacenada de la forma que queramos, todo dependerá de la longitud de la variable que usemos para acceder al fichero... todo estas cosas quedarán explicadas y aclaradas en esta o en próximas 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 información de los ficheros sin usar instrucciones del VB?

Cada vez que quieras abrir un fichero, tienes que usar un número de "canal" por el que VB nos suministrará la información, este canal. El canal se indica por medio de un número de 1 a 255. Gracias a este número, el Basic se comunica con el sistema operativo para acceder a los datos. El Basic nos facilita la tarea de conseguir ese número, con idea de que no usemos una línea que esté en uso... La instrucción, en realidad es una función, para conseguir un número de canal libre, es: Freefile. Esta función devuelve un número 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 NumFicCon esta línea, abrimos el fichero Prueba.txt de forma secuencial para poder escribir en él.Una vez que tenemos una "vía" de comunicación, podremos escribir información usando una versión un poco maquillada de la instrucción Print... El maquillaje es el número de canal con el que podemos acceder al fichero:Print #NumFic, "Lo que sea"#NumFic es el número de fichero (o canal) por el que accedemos al fichero abierto y después de ese número, usamos una coma y a continuación lo que queremos guardar en el fichero.Cuando hayamos acabado de guardar cosas, tendremos que cerrar el fichero

Page 141: curso básico de programación en visual basic

que hemos abierto, para poder liberar ese canal abierto y así poder usarlo en otra ocasión, 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 información que aún tiene "temporalmente" almacenada en una memoria intermedia que usa para que el acceso a datos sea, al menos en teoría, más rápido. A esta memoria intermedia se le llama "buffer". El VB la usa para ir guardando la información que vamos a grabar físicamente 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 búfer para los ficheros secuenciales y aleatorios puede ser de 32767 bytes como máximo, antes con el Basic del DOS el valor por defecto era de 128 bytes y el máximo 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 ocasión de comprobarlo.

Todo esto está muy bien, pero si quieres especificar esa longitud... ¿cómo y/o dónde se especifica?Ahora sabrás cómo y dónde. Para ello vamos a ver cómo se usa al completo la orden OPEN y sus posibilidades de uso.

Open RutaAcceso [For Modo] [Access acceso] [tipo de bloqueo] As [#]númerofichero [Len=longitudregistro]

Lo que está entre corchetes son parámetros opcionales.Fíjate 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 explicación de cada uno de estos parámetros 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 explicación de la ayuda o del manual...

Vamos a ver lo que nos interesa de esta instrucción:

RutaAcceso

El path completo, o a medias, de dónde 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)

Modo Output, para ficheros de salida, es decir para guardar los

datos.

Si el fichero existe, lo borrará (sobrescribirá) y si no

existe, lo creará.

Input, para leer los datos de un fichero ya existente.

Page 142: curso básico de programación en visual basic

Append, como el Output, pero añadiendo la información

al final del fichero, si este ya existe.

Random, para acceso aleatorio.

Binary, para acceso binario.

As NúmeroFichero

Aquí se indica el número de fichero (canal) por el que accederemos a la información.El signo de número (#) es opcional. Y NúmeroFichero, puede ser una variable o una constante.

Las otras opciones ya las veremos, ahora nos centraremos en las cosas que son más fáciles, siempre hay tiempo para complicarse la vida, así que nos la complicaremos más adelante, cuando ya tengamos un poco de idea de todo este follón...

RutaAcceso, a estas alturas deberías 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 información en el disco, tendrás que saber en que parte del disco la quieres guardar, incluso en que disco quieres almacenarla. Y lo más importante, cómo 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 información diferente almacenada en el disco.Para empezar, debes saber que tienes que usar un nombre en el que almacenar la información que quieres "conservar", para después 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 información, 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 año almacenados en distintos ficheros, uno para cada mes. Podrías hacer algo como esto: darle a cada fichero un nombre diferente o bien usar la "extensión" 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 sólo tienes que decirle que el fichero se llama: A:\rascones.eneSi tienes la intención de guardar cada grupo de ficheros en carpetas (directorios) diferentes, también puedes indicarselo en la ruta esta de acceso: C:\Datos\A1998\rascones.enePor supuesto para poder hacer esto último debes tener un disco C (¿quién no lo tiene?), un directorio A1998 que está dentro de otro llamado Datos que está a su vez en el directorio raíz 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.

Page 143: curso básico de programación en visual basic

¿Que error? El número 76: Path not found (No se ha encontrado la ruta de acceso)Hay más errores, muchos, pero estos ya te los irás encontrando y en su momento veremos cómo poder detectarlos.

Veremos también cómo crear las rutas esas de acceso, en caso de que no existan, para así asegurarnos que existen antes de guardar la información en el disco... pero todo a su debido tiempo...

Ahora lo que vamos a ver es unos ejemplos de cómo guardar información y después poder "leerla", ya que esto es lo más básico y lo que en principio debemos saber.

¿Cómo guardar la información?Ya te he dicho antes de que con Print se puede guardar la información en el disco, veamos cómo:Print #NumFic, NombrePrint #NumFic, 125

También podemos guardar esta misma información así:Print #NumFic, Nombre, 125

Es decir que si quieremos guardar varias cosas con una misma instrucción, lo haremos usando una coma como separador. De esta forma cada cosa que esté separada se guardará en el disco en "líneas" distintas.

¿Cómo leer la información?Para poder leer la información, además de abrir el archivo para lectura modo INPUT, hay que usar una de estas instrucciones:Input #NumFic, VariableTambién con: Line Input #NumFic, variable.

La diferencia entre el Input y el Line Input la veremos dentro de un ratillo.Antes tendremos que ver cómo acceder a ese nombre y a ese número 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 pretendías... Vamos a verlo con un ejemplo. Crea un nuevo proyecto en el VB y añade dos botones (CommandButton), escribe este código y pruebas:

Private Sub Command1_Click()

Dim Nombre$, Num%

Dim NumFic%

Nombre = "Pérez, Pepito"

Num = 22

Page 144: curso básico de programación en visual basic

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

Close nF

MsgBox "Cadena= " & Cadena & vbCrLf & _

"Número= " & Numero

End Sub

Ahora pulsa en F5 y dale primero al botón Command1, después le das al Command2 y verás que no te muestra lo esperado.En lugar de mostrar:Cadena= Pérez, PepitoNúmero= 22

Te ha mostrado:Cadena= PérezNúmero= 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" Pérez y a la variable Numero el "número" Pepito... que al no ser un número, le ha dado el valor cero...

Esto es debido a que cuando INPUT lee los datos espera una coma o el final de línea para "distinguir" entre los diferentes datos a asignar a las variables indicadas.Vale, dirás, pongámoslo en distintas líneas y así los leerá correctamente:

Input #nF, CadenaInput #nF, Numero

Page 145: curso básico de programación en visual basic

Pero con esto no lo solucionarás, pruébalo y verás que tengo razón.¡De gente desconfiada está el mundo lleno! ...no te he dicho que daría el mismo resultado... HUM!

Bien, ¿cómo solucionarlo?¿Lo has adivinado? Pues eso mismo, usando el Line Input... Pero con Line Input no se pueden especificar más de una variable en la misma instrucción... así que ponlas en dos líneas.

Line Input #nF, CadenaLine Input #nF, Numero

¡OPS! ¿Que ha ocurrido?Si has pulsado simplemente F5, te habrá dado un error al pulsar en el segundo botón... Y si has pulsado Control+F5, te habrá indicado, con el mismo error, que los tipos no coinciden, (si usas la versión inglesa: Type Mismatch)

Esto es debido a que Line Input sólo puede leer cadenas de caracteres, mejor dicho sólo se pueden usar variables de tipo string (cadena), ya que esta instrucción lee todo lo que hay en la línea actual del fichero abierto, hasta el final de la línea.

¿Cómo solucionarlo?Usando una variable intermedia o simplemente usando el INPUT normal para leer el número.Veamos cómo sería de las dos formas:

Line Input #nF, CadenaInput #nF, Numero

Dim sTmp$

Line Input #nF, CadenaLine Input #nF, sTmpNumero = Val(sTmp)

Ahora si que tendremos el resultado correcto:Cadena= Pérez, PepitoNúmero= 22

¡EXACTO! Tampoco nos ha mostrado esto... ¿Por qué?Muy sencillo, realmente no es sencillo, sino que después de que me haya ocurrido como dos millones de veces, resulta hasta lógico... 8-(Si miras el contenido del fichero C:\Prueba.txt, te darás cuenta de que el contenido de este fichero es:Pérez, 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 tabulación, 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 líneas del fichero.Así que cambia el código del Command1, para que en lugar de un sólo Print,

Page 146: curso básico de programación en visual basic

haya dos:

Print #NumFic, NombrePrint #NumFic, Num

Ahora todo debe funcionar bien.Vale, pruébalo si no te fías... ¿Tenía yo razón? Pues claro, ...ya que lo he comprobado antes... ;-)

Si no sabemos el tipo de datos que tenemos almacenado, lo mejor es usar la instrucción Line Input y así nos curamos en salud, pero si sabemos que, por ejemplo, todos los datos son numéricos y se han almacenado sin usar comas...

Mejor un ejemplo:En este caso vamos a guardar en un array una serie de números aleatorios, los vamos a guardar en un fichero y después 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.Añade un Label que ocupe prácticamente todo el Form, salvo la parte de abajo, en la que pondrás dos botones.Pega el código este que te pongo, pulsa F5 y primero pulsa en el Command1, para después pulsar en el Command2

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 = "Números guardados en el disco"

Page 147: curso básico de programación en visual basic

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 números al label:

Label1 = ""

For i = 1 To 10

Label1 = Label1 & MasNumeros(i) & vbCrLf

Next

End Sub

Este es un ejemplo sencillo de cómo asignar datos a un array, guardarlos en el disco y después leerlos.

Y hasta aquí hemos llegado...

Como ejercicio, haz un programa que al pulsar en un botón, te pida diez nombres, los guarde en un fichero y después pulsando en otro botón los muestre en un label.No es necesario que uses un array para guardar los datos, pero podrías hacer dos versiones, con y sin un array.

Como pista te recordaré que la función InputBox puede servirte para esto de preguntar, ya que el valor que devuelve es la cadena de caracteres introducida en la caja de diálogo que muestra.Esto del InputBox ya lo vimos en la novena entrega, o en las soluciones, pero te explico brevemente cómo funciona:variable$=InputBox("Escribe un nombre")¿Facil, verdad? Pues esa es toda la pista que te voy a dar.

Page 148: curso básico de programación en visual basic

Ahora se "legal" y no veas las soluciones de esta entrega hasta que lo hayas hecho tú.

En la página de las soluciones tienes un "extra", así que aunque sepas cómo 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 verás cuando... no quiero prometer que será pronto, que después me regañas... así, que... ¡a esperar!

Si hay algo que no entiendas o simplemente quieres hacer algún comentario sobre esta entrega o cualquier otro tipo de peloteo o lo que te de la gana decirme sobre el curso básico, usa este link... Pero no lo aproveches para las consultas... ¡que te conozco rosco!

Nos vemos pronto.Guillermo

Como ejercicio, haz un programa que al pulsar en un botón, te pida diez nombres, los guarde en un fichero y después pulsando en otro botón los muestre en un label.No es necesario que uses un array para guardar los datos, pero podrías hacer dos versiones, con y sin un array.

Solución 1: Sin usar Array:

Private Sub Command1_Click()

Dim i%

Dim nFic%

Dim Nombre$

'Solución 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 número " & CStr(i))

Print #nFic, Nombre

Next

Close nFic

End Sub

Page 149: curso básico de programación en visual basic

Private Sub Command2_Click()

Dim i%

Dim nFic%

Dim Nombre$

'Solución 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

Solución 2: Usando un Array:

Private Sub Command1_Click()

Dim i%

Dim nFic%

Dim losNombres(1 To 10) As String

'Solución 1, sin array

'Primero preguntamos los nombres

For i = 1 To 10

losNombres(i) = InputBox("Escribe el nombre número " & 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

Page 150: curso básico de programación en visual basic

Close nFic

End Sub

Private Sub Command2_Click()

Dim i%

Dim nFic%

Dim variosNombres(1 To 10) As String

'Solución 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

'mostrar los nombres

Label1 = ""

For i = 1 To 10

Label1 = Label1 & variosNombres(i) & vbCrLf

Next

End Sub

Fíjate que con esta segunda forma, si usas un array a nivel de módulo o global, puedes mostrar los datos cuando quieras, leyéndolos una vez y después mostrándolos en cualquier ocasión, hasta que asignes nuevos datos.Lo mismo ocurre al pedir esos nombres y después guardándolos en el disco cuando quieras.

En el siguiente listado, puedes ver un ejemplo (simple) de esto que te digo.Fíjate 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

Page 151: curso básico de programación en visual basic

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%

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 leídos correctamente"

Else

Label1 = "No existe el fichero de nombres"

End If

Page 152: curso básico de programación en visual basic

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) = ""

Next

'También se puede hacer así: (esto es más rápido)

'ReDim losNombres(1 To 10)

'Preguntamos los nombres

For i = 1 To 10

losNombres(i) = InputBox("Escribe el nombre número " & CStr(i))

Next

End Sub

Private Sub Form_Load()

Label1 = ""

End Sub

Page 153: curso básico de programación en visual basic

Desde luego que los días 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 pequeño esfuerzo para acelerar los plazos de entrega, sino, puede que te aburras o decidas cambiar de profe...La solución sería: disponer de un portátil... pero... en fin... Esto de la esponsorización no da para tanto... lo mismo a ti te sobra uno... ejem!

Bueno, después de la "necesaria" introducción, vamos a lo que te ha traído por aquí; ya que me imagino que no es precisamente el saber que "necesito" un portátil... esto... si es un Pentium 200, con un mínimo 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 refrán: "El que no llora... no mama..." ;-)

¡Guille! ¡Déjate de chorradas y vamos al tema! (esta es la voz de mi conciencia: mi otro yo)

En la entrega anterior ya vimos cómo guardar y recuperar información 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 función INPUT, ahora verás que no se usa de igual forma de como usamos anteriormente la "instrucción", recuerda que esta nueva, es una función y las funciones siempre devuelven un valor. La que antes usamos era una instrucción y las instrucciones hacen algo, pero no devuelven valores... ya verás que esto mismo se puede aplicar a las funciones; ya que estamos en ello, y a título de curiosidad, si no quieres usar el valor devuelto por una función, cosa que se hace muchas veces con las llamadas al API de Windows, deberás hacerlo anteponiéndole a la función la instrucción 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 cómo usar esas dos funciones:variable = LOF(#canal)Esta función devuelve, en bytes, el tamaño del fichero abierto con el canal indicado dentro del paréntesis.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 después hacemos lo que tengamos que hacer... Que sólo 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

Page 154: curso básico de programación en visual basic

Open sFic For Input As nFic

tamFic = LOF(nFic)

Close nFic

MsgBox "El tamaño 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 función FileLen, ésta se usa poniendo el nombre del fichero entre los paréntesis y también devuelve el tamaño en bytes:(Tanto una función como la otra devuelven un valor LONG)

Dim sFic As String

sFic = InputBox("Nombre del fichero:", "Mostrar tamaño", "C:\Autoexec.bat")

If Len(sFic) Then

MsgBox "El tamaño de " & sFic & vbCrLf & "es de " & FileLen(sFic) & " bytes"

End If

Este trozo de código te preguntará, (usando InputBox), el nombre de un fichero, por defecto te mostrará C:\Autoexec.bat y si se ha escrito algo, mostrará el tamaño en bytes.Fíjate que el valor devuelto por una función no sólo se puede asignar a una variable, sino que también 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 cómo "funciona" la función INPUT:Esta función devuelve una cadena con el contenido de un fichero que esté abierto... realmente devuelve el número de caracteres que le indiquemos y esos caracteres los tomará del fichero abierto con el "canal" indicado, veamos cómo usarla:

cadena = Input$(numCar, #canal)

El signo dólar ($), lo uso para saber que estoy trabajando con una función que devuelve un valor de cadena...La mayoría 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 sólo es válido 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 lío!!!

Page 155: curso básico de programación en visual basic

Ahora vamos a ver cómo 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

Fíjate que uso un MsgBox para mostrarlo, en caso de que el contenido del fichero sea "demasiado" grande, el botón Aceptar no se verá... tienes dos opciones: pulsar Intro o ESC, de esta forma quitarás el mensaje de la pantalla, a pesar de que no tenga "visible" el botón Aceptar.

Un par de detalles, la función Input$() devuelve una cadena de caracteres, el tamaño máximo de caracteres que admite estará delimitado por el sistema operativo y sobre todo por la versión del VB, en 32 bits este tamaño "casi" no tiene límite, pero en 16 bits, será de 64 KB como máximo.El otro detalle es que la comprobación que se hace para saber si existe el fichero, no es a 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 solución, ahora mismo te diré cual es, pero antes voy a desglosarte el funcionamiento de Len(Dir$(...))Fíjate que aquí se usan dos funciones:

Len(Cadena) Esta función devuelve el número de caracteres de la cadena indicada

Dir$(sFichero) Esta otra, lo que devuelve es el nombre del primer fichero que coincida con la "especificación" indicada en sFichero, en caso de que no haya coincidencias, devolverá una cadena vacía.

Por tanto, si no existe el fichero, Dir$ devuelve una cadena vacía y la longitud de una cadena vacía es CERO, así que en la comparación, If Len(Dir$

Page 156: curso básico de programación en visual basic

(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 quizás no sepas es que en Dir$(sFic), sFic puede contener signos comodines (? y/o *), para indicar cualquier tipo de fichero. Esto se suele usar también en las rutinas de búsqueda que se hacen en las bases de datos, así que mejor que te vayas enterando cómo usarlo y "buscar" por ahí información, ya que aquí te voy a explicar un poco el significado, para que lo puedas usar al indicar los ficheros.Como "añadido", decirte que en el sistema de búsqueda del Windows 95, también 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 interrogación (?) se usa para indicar que no nos importa el carácter que haya en esa posición.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 Mostrará todos los ficheros que empiecen por Auto y que la

extensión sea .batDir Auto*oDir Auto*.*

Todos los ficheros que empiecen por Auto, sin importar ni la extensión ni nada, ya que al usar .* se indica que cualquier cosa es válida.Es recomendable usar la segunda forma.

Dir A?t*.* 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 después de la T, por tanto si tuviésemos ficheros llamados: Arte.txt, Autoexec.bat, Automovil.doc, Arial.ttf, nos devolvería los tres primeros, porque coinciden en lo indicado.

Dir Dato??98.txt Nos devolverá todos los ficheros que tengan en las 4 primeras letras la palabra dato y en las posiciones 7 y 8 el número 98, la extensión será txt. Además en las posiciones 5 y 6 podrá tener cualquier cosa, pero deberá tener 8 caracteres.Así que Dato0198.txt, dato1298.txt sería nombre encontrados, pero no lo sería: data0198.txt ni dato0298.doc, en el primer caso, porque empieza con la palabra data y en el segundo porque la extensión no es txt

Una vez visto, por encima, esto de los signos comodines, vamos a ver cómo hacer nuestra forma de comprobar si existe un ficheros, algo más fiable y segura.Para ello, vamos a crear una función que se llame: Existe y esta función devolverá FALSO (cero) si el fichero no existe y en caso de que exista, devolverá VERDADERO (-1)Esta función podremos usarla de cualquiera de estas dos formas:

If Existe(unFichero) Then

Page 157: curso básico de programación en visual basic

'Si existe, hacer lo que corresponda

End If

If Not Existe(unFichero) Then

'El fichero en cuestión 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 NextCon estas instrucciones, se le indica al Visual Basic que en caso de que se produzca un error, "pase" de ese error y continúe con la siguiente instrucción.

Esto está bien, pero... ¿cómo sabremos que se ha producido el error? ya que, si continua... pues... eso, no se detiene...Respuesta: Usando la función ERR.

Esta función devuelve el número del error que se haya producido, o cero si no se produjo error.A partir del VB4, esta función se puede sustituir por la propiedad Number del objeto Err, por tanto Err.Number también nos dirá que número de error se ha producido, ten en cuenta que Number es la propiedad por defecto de este objeto.Pero como quiero que este curso básico sea lo más genérico posible y no se incline sólo 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 versión 3 del VB, porque esta "consideración" que estoy teniendo puede que cambie... de hecho seguramente pondré cosas que sólo estarán disponibles a partir de la versión 4... y cuando la cosa vaya avanzando más, incluso sólo cosas de la versión 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 más rodeos, veamos cómo quedaría la función Existe, si te fijas es casi igual que la que puse hace ya algunas entregas, pero usando la detección de errores:

Private Function Existe(ByVal unFichero As String) As Boolean

On Local Error Resume Next

Existe = Len(Dir$(unFichero))

If Err Then

Existe = False

Page 158: curso básico de programación en visual basic

End If

Err = 0

On Local Error GoTo 0

End Function

Te explico cada una de las líneas:

Private Function Existe(ByVal unFichero As String) As Boolean

Esta es la declaración de la función, el Private es por si lo quieres usar en un form, o en cualquier otro módulo, pero que sólo sea visible para el código de ese módulo.Si quieres que esté visible en todo el proyecto, cosa recomendable para este tipo de funciones, cambia el Private por Public y escribe el código en un módulo BAS. Así podrás usarlo en cualquier form o módulo que tengas en tu proyecto.

Veamos porqué he declarado el parámetro de esta forma: Byval unFichero As StringEste es el parámetro que le pasamos a la función, es decir: el fichero o especificación que queremos comprobar si existe. Lo de especificación es porque puedes usar esta función para saber si existen archivos que empiecen por la A, asignando al parámetro 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 parámetro, con lo cual evitaremos que el código de nuestra función lo modifique; ya que al usar una copia, no tenemos acceso al original, esto también acelera el manejo de nuestra función.

El As Boolean del final, es el tipo de dato que devolverá nuestra función, también se podría usar Integer, de esta forma, si estás usando el VB3, podrás usar la función, sin mayor problema.

On Local Error Resume Next

Esta es la instrucción que pone en marcha la detección de errores.Local es para indicarle al VB que sólo esté operativa dentro de esta función, sirve para que, en caso de que externamente haya otro mecanismo de detección de errores, el que esté operativo sea el que acabamos de declarar... cuando abandonemos la función seguirá funcionando la otra rutina que hubiera.

Existe = Len(Dir$(unFichero))

Asigna a Existe la cantidad de caracteres devueltos por la función DIR. Al ser del tipo Boolean, el VB automáticamente asigna Verdadero o Falso.En el caso de que la función 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 función.

Page 159: curso básico de programación en visual basic

¿Seguro?Veamos un ejemplo:

If Not Existe("algo.txt") Then

MsgBox "El fichero indicado no existe"

End If

Prueba esto cambiando el tipo de dato devuelto por la función Existe, cuando lo pongas como Integer, siempre te dirá que no existe el fichero...La explicación de porqué ocurre esto, ya lo vimos anteriormente, y es porque NOT Un_Número, devuelve un número, no un cero... Y recuerda que, cuando Existe es del tipo Integer, devuelve el número de caracteres...

Si quieres que sólo devuelva 0 ó -1 para que sea igual que False/True, podrías hacer esto otro:Existe = Len(Dir$(unFichero)) <> 0De esta forma se evalua la expresión 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 algún error al intentar buscar el fichero en cuestión.

En el supuesto de que se produzca un error, se pasará a la línea después del IF, asignando FALSE al valor que devolverá la función. Como sabrás o te habrás imaginado, el valor que debe devolver una función se asigna al nombre de la función, en otros lenguajes se usa RETURN valor, por ejemplo en C o en el JavaScript...Por tanto, Existe = False, sólo se asignará cuando se produzca un error.

Err = 0

Esta es una de esas recomendaciones "obligatorias" que yo haría siempre que uses el Resume Next, en este caso no es necesario porque el código de la función termina ahí, pero si hubiese más código, ese valor de error seguiría asignado a Err y cualquier otra comparación posterior al objeto Err, podría "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 línea "libera" la detección de errores de esta función, en nuestro caso, tampoco es necesaria, ya que al finalizar un procedimiento, cualquier rutina de detección de errores se elimina. Esto, al igual que lo dicho anteriormente, sirve si queremos dejar de detectar errores en las siguientes líneas de nuestra rutina. Como ya no hay más líneas de código, la verdad es

Page 160: curso básico de programación en visual basic

que no es necesaria, pero suelo usarla siempre, costumbres que tiene uno de saber cuando dejo de interceptar los errores.

Bien, ya tenemos nuestra función a prueba de usuarios inexpertos o con malas intenciones... también para los olvidadizos...¿Por qué me miras?¿Es que nunca has intentado acceder a la unidad A cuando aún no has insertado un disquete?Pues yo sí... je, je...

Vamos a probarla.

Crea un nuevo proyecto, agrega un módulo bas.Asegúrate que tiene Option Explicit al principio, ya sabes que esto es para que cualquier error tipográfico 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 escríbela nuevamente, la declaración de la función Existe, asegúrate que sea pública y no privada.

Cierra el módulo y asegúrate que el Form1 esté visible y en modo de diseño, es decir que se vea el Form. Añade un Label, un TextBox y un CommandButton.Modifica el caption del label para que contenga este texto: Fichero a comprobar:En el caption del botón escribe: Comprobar.Sitúa 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: *.basIgualmente escribe algún 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 ningún 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

Page 161: curso básico de programación en visual basic

puede que mañana mismo esté en línea... es que si continúo, se me va a pasar la hora y quiero que sea hoy día diez el día 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 algún comentario sobre esta entrega o sobre el curso básico en general... o tienes ese portátil que me haría tan feliz... y me lo quieres regalar, claro, usa este linkTe repito por enésima 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

No ha sido al día 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 especificación 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 sólo lo hemos visto, pero sin entrar en detalles sobre él: el ListBox.También vamos a usar el DIR$ de otra forma... una de las varias disponibles...

Crea un nuevo proyecto, añade un ListBox, un Label, un TextBox y un CommandButton, el aspecto sería más o menos así:

Haz dobleclick en el Command1 y escribe:

Page 162: curso básico de programación en visual basic

Private Sub Command1_Click()

Dim sTmp As String

On Local Error Resume Next

sTmp = Dir$(Text1.Text)

If Err = 0 Then

Do While Len(sTmp) 'Repetir mientras haya ficheros

List1.AddItem sTmp 'Lo añadimos a la lista

sTmp = Dir$ 'Asignar el siguiente fichero

Loop

End If

Err = 0

End Sub

Ejecuta el programa y escribe *.* en el TextBox, pulsa en el botón y te mostrará en el listbox todos los archivos del directorio actual.

Vamos a ver que es lo que hace el código:

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 ThenSi no se produce un error...

Do While Len(sTmp)...se entra en el bucle, pero sólo si el contenido de sTmp no es una cadena vacía. Recuerda que DIR$ devuelve una cadena vacía si no se ha encontrado un fichero que coincida con lo indicado...

List1.AddItem sTmpEsto añade al ListBox un nuevo elemento con el contenido de sTmp

sTmp = Dir$Fíjate que DIR$ se usa sin indicarle nada más, úsalo de esta forma si quieres que siga comprobando si hay más ficheros que coincidan con la especificación indicada la vez anterior que se le pasó un parámetro. Si el contenido del TextBox tenía algún signo comodín, Dir$ devolverá el siguiente fichero que coincida, en caso de que no queden más ficheros "coincidentes", devolverá una cadena vacía.

LoopRepite el bucle si se cumple la condición que pusimos después de DO WHILE, es decir: continuará si la longitud, número de caracteres, de sTmp NO ES CERO.

Page 163: curso básico de programación en visual basic

Prueba, sin cerrar el programa, con varias cosas, por ejemplo: *.vbp, *.bas, etc.

¿Que pasa?Si estás indicando varias cosas que buscar, y las encuentra, te darás cuenta que el listbox se va llenando... es decir, además de lo nuevo, que estará al final, sigue lo anterior...

¿Cómo solucionarlo?Borrando el contenido del listbox.

Para borrar el contenido del listbox, usa esto:List1.Clear

¿Dónde debo ponerlo?En nuestro ejemplo, yo lo pondría justo después del On Local Error..., o antes, da igual, ya que lo que se pretende es que se borre al hacer CLICK en el botón.Por supuesto no lo pongas dentro del DO...LOOP, ya que no serviría para lo que queremos... puesto que se borraría 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 ahorraría pulsaciones. Si al pulsar INTRO se simulara el CLICK del botón, nos ahorraría el tener que "desplazarnos" a ese botón para que muestre lo que ha encontrado y si queremos seguir mostrando más cosas, el tener que desplazarnos nuevamente al TextBox.

Para conseguir esto, todo lo que tenemos que hacer, es indicarle al VB que el botón sea un botón "por defecto".Muestra el form, y pulsa una vez en el botón, te mostrará la ventana de propiedades de este control, busca la propiedad DEFAULT y márcala como TRUE, verás que el botón 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 especificación...

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, añade al listbox todos los ficheros hallados... mejor dicho, para que no hagas trampas, añade al listbox el contenido del array, es decir todos y cada uno de los ficheros previamente asignados.3.- Añade otro botón al form y al pulsar en él, que guarde en un fichero, (por ejemplo: hallados.txt), todos los ficheros hallados, es decir los que estén 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 asignación al array, puedes hacerla de dos formas:UNA: Usando un número máximo de ficheros a asignar.DOS: Usando un número variable, es decir que se añadan sólo la cantidad de ficheros hallados.

Y si haces los ejercicios de las dos formas posibles, mejor aún.

Page 164: curso básico de programación en visual basic

Si ves que te atrancas y no sabes por dónde hincarle el diente, aunque sea postizo, échale un vistazo a la entrega ocho y a la entrega doce, en ellas encontrarás la solución a estos ejercicios... bueno, la solución, lo que se dice la solución: 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 días!Es que algunas veces... ¡Soy malo!

Pues nada, a esperar... que de seguro será poco, pero mientras tanto...

Y recuerda que espero tu comentario, no me preguntes si lo estás haciendo bien o no, que de eso se encargará la página con las soluciones, sólo pregúntame 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 páginas...

Nos vemos.Guillermo

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, añade al listbox todos los ficheros hallados... mejor dicho, para que no hagas trampas, añade al listbox el contenido del array, es decir todos y cada uno de los ficheros previamente asignados.3.- Añade otro botón al form y al pulsar en él, que guarde en un fichero, (por ejemplo: hallados.txt), todos los ficheros hallados, es decir los que estén en el array.

Vamos a ver la solución al primero de los ejercicios de las dos formas que propuse, es decir usando un número fijo de ficheros y uno variable.

En el caso del número fijo, he asignado un valor de 50, como ese valor está en una constante, sólo tendrás que cambiar el valor de la constante para cambiar el número 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

Page 165: curso básico de programación en visual basic

'sólo asignarlo si tenemos espacio reservado

If nFic < MaxFicheros Then

nFic = nFic + 1

'Lo añadimos al array

sFicheros(nFic) = sTmp

End If

sTmp = Dir$ 'Asignar el siguiente fichero

Loop

End If

Err = 0

End Sub

Usando un número variable de ficheros.En esta ocasión 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) 'Repetir mientras haya ficheros

nFic = nFic + 1

'Adecuar el tamaño del array a los ficheros leidos

ReDim Preserve sFicheros(nFic)

'Lo añadimos al array

sFicheros(nFic) = sTmp

sTmp = Dir$ 'Asignar el siguiente fichero

Loop

End If

Err = 0

End Sub

Page 166: curso básico de programación en visual basic

En el segundo ejercicio, hay que guardar el contenido del array, en este caso, el array debe estar declarado a nivel de módulo, 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á sólo visible en ese procedimiento...Por tanto el Dim sFicheros() As String debes ponerlo en la parte de las declaraciones del form.Este código deberás agregarlo después de asignar todos los ficheros al array, justo después 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 número 1. Te lo digo por dos razones, la primera es para que no lo confundas con la letra L minúscula 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 aplicación no tiene abierto ningún otro fichero, puedo permitirme el lujo de hacerlo así, de forma directa.

El tercer ejercicio, no debería 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 código añadelo justo después de guardar los datos en el disco, aunque también puedes ponerlo después, siempre que esté después 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 sólo por el List1.Clear, sino porque al hacer Redim Preserve el número de elementos del array se adapta al

Page 167: curso básico de programación en visual basic

valor de nFic y este valor empieza siempre por cero, así que siempre se tendrá en el array el número correcto de ficheros.La asignación que hago para ponerlo a cero, no es necesaria, ya que cuando se dimensiona una varible numérica, ésta variable contiene inicialmente un valor cero.Pero imaginate que no haces esa asignación o que quieres asegurarte que el contenido del array se "libere" antes de empezar a asignarle datos... para ello tendrías que usar: ERASE sFicheros, con esta instrucción borramos el contenido del array. En el caso de que el número de elementos del array fuese fijo, a lo que se llama un array estático, simplemente se borraría el contenido del array, pero seguiría existiendo el array con las 50 "dimensiones" creadas. Si, por el contrario, el array es dinámico, es decir que podemos cambiar el tamaño 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.

Parece ser que pocos hicisteis caso de mi comentario, en la entrega trece, sobre el portátil, lo mismo es que sois supersticiosos, pero la cuestión es que sigo sin él... así que no te quejes de que tarden tanto las entregas sobre el curso básico...

La verdad es que me hubiese gustado encontrarme con un mensajillo de un alma caritativa que me hubiese dicho que ponía a mi disposición el susodicho portátil, pero me aguantaré sin él... por lo menos espero los comentarios sobre lo que te parece el curso, aunque más espero comentarios sobre las cosas que no entiendas, ya que la intención es que sean claras y que consigas aprender a programar con este lenguaje, que a pesar de que muchos dicen que es fácil, 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 difíciles... 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 pequeña 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 más adelante... supongo...

Realmente el acceso secuencial va a pintar poco en esto que vamos a hacer, pero nos servirá para ir "tomándole" cariño a unos controles que usaremos en el 99.9% de nuestras aplicaciones.

Para esta tarea, crea un nuevo proyecto y añade un label, dos textbox y dos commandbutton.

Distribúyelos de esta forma:

Page 168: curso básico de programación en visual basic

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 serán Label1, Text1, cmdAbrir para el botón de Abrir y cmdGuardar para el de guardar.

El problema de los textbox es que no soportan más de 64KB, aunque en teoría si, pero en la práctica lo que soportan son unos 32000 caracteres, que en la versión de 32 bits suponen esos 64KB, por si no lo sabes en Windows de 32 bits cada carácter está representado por dos bytes.Para solventar ese "pequeño" inconveniente, vamos a dar por hecho de que sólo admite 32000 caracteres, de esta forma también nos servirá el programa en VB de 16 bits.

A este proyecto hay que agregarle un módulo BAS que tenga la función Existe que vimos en la entrega trece, si no quieres añadir un nuevo módulo, simplemente copia y pega esa función en este formulario.Si la función la usas desde un módulo BAS, debe ser pública, si la pegas en este formulario, puede ser privada. En el formulario también puede ser pública, pero lo que nos interesa es que pueda accederse desde el form, así que no es necesario declararla de esa forma.

Ahora añade el siguiente código para el botón abrir:

Private Sub cmdAbrir_Click()

'Abrir

Page 169: curso básico de programación en visual basic

Dim i As Long, tamFic As Long

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 harás... ¿como ejercicio? ¡efectivamente!

Este es el código del botón 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

Fíjate que código más corto... ¡así da gusto hacer programas!Aunque tampoco está preparado para cualquier impedimento...

Vamos a añadirle una serie de mejoras, entre ellas el que avise, al guardar, si es que vamos a sobrescribir un fichero existente.Otra mejora sería 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 más caracteres de los "permitidos"... aunque este lo dejaré para que lo hagas tú.

La razón de poner estas mejoras por separado, es decir, contártelo antes de hacerlo, es para darte la oportunidad de que lo hagas por tu cuenta... Ahora no

Page 170: curso básico de programación en visual basic

recuerdo si tienes la base suficiente para hacerlo, pero... podría ser... compruébalo por tu cuenta.

Antes de darte la solución 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, sólo voy a "ampliar" tus conocimientos... (que bien te ha quedado eso Guille)

Cuando usamos un textbox multiline, es decir en el que podemos escribir varias líneas 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 línea, a esto se le llama wordrap o algo así, que viene a significar que no se corten las palabras al cambiar de línea... para hacer esto mismo en el BASIC normalito del MS-DOS, había que crear una rutina para comprobar cuando había que mandar una palabra a la siguiente línea... dejemos los viejos tiempos y continuemos con los nuevos...Pero si lo que quieres es que cada línea escrita se quede en la misma línea hasta que pulses intro, deberás indicárselo al Visual Basic, diciéndole que sólo añada al textbox un scroll horizontal, pruébalo y decide...

¿Cómo añadirle los scrollbars... no pienses que tienes que usar los controles que el Visual tiene para eso, sólo debes modificar la propiedad ScrollBars del control Text2. Tienes varias opciones:

0- None Ningún scrollbar

1- Horizontal Sólo el scroll horizontal, para cambiar de línea debes pulsar Intro

2- Vertical Sólo el scroll vertical, esto es como sin scrolls, pero nos permite navegar hacia abajo.

3- Both Ambos scrolls, pues eso, una mezcla de los dos.

Si los compruebas, verás 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á automáticamente haya o no un intro para separar cada línea, la diferencia, es que con el scroll vertical, podemos navegar fácilmente hacia la parte no visible del texto escrito.Cuando especificamos el Scroll horizontal, tanto con el valor 1, como con el 3, ya te darás cuenta de que cada línea está separada, siempre que hayas pulsado Intro para cambiar de línea. También puedes usar Shift+Intro o Control+Intro para efectuar un cambio de línea.Para comprobarlo, haz el textbox más pequeño, al menos en anchura, por ejemplo ajústalo al tamaño del Text1 y escribe varias líneas, 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 sólo lectura, al menos en tiempo de ejecución, o sea que no puedes cambiar que scrolls deben mostrarse una vez que el programa está en funcionamiento.Me imagino que te habrás fijado que en el Notepad (Bloc de Notas) que incluye el Windows, existe una opción para ajustar las líneas automáticamente, es decir para usar los dos scrolls o sólo el vertical.¿Cómo 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

Page 171: curso básico de programación en visual basic

(Vertical) y el otro asignando el valor 3 (Both)Para probarlo, deberás crear un array del Text2, para ello, cópialo y vuelve a pegarlo, te preguntará si quieres tener una matriz de este control, contéstale que sí. Al Text2(0), el que tiene el valor Index igual a CERO, asígnale la propiedad ScrollBars a 2 y al otro, el valor 3. Sitúalos en la misma posición del form, para que uno esté encima del otro, no te preocupes de cual está encima, eso lo controlaremos nosotros.Añade un nuevo botón, escribe en el Caption: Ajustar líneas y dale el nombre cmdAjustar.Declara una variable a nivel de módulo para saber cual es el TextBox que está activo:Dim QueText2 As IntegerEn el Form_Load escribe esto:Text2(0).Zorder para que se ponga encima del otro TextBox.En el evento Click del nuevo botón añade este código:

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 tendrás que cambiar el código 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 línea larga y pulsa Intro para crear otras, pulsa en el botón de ajustar líneas y verás el efecto.

Si quieres tener esta posibilidad en este programa, deberás recordar de cambiar cualquier uso de Text2 por Text2(QueText2), ya que si no lo haces, el Visual Basic se encargará de recordártelo...

Page 172: curso básico de programación en visual basic

Bueno, creo que el rollito este ha sido más largo de lo que tenía 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 línea, es que sino, me va a romper todo el esquema que tenía 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 después 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 código necesario, este deberá estar en el botón de Guardar... (elemental mi querido Watson)

Private Sub cmdGuardar_Click()

'Guardar

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("ATENCIÓN, 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

Page 173: curso básico de programación en visual basic

Fíjate en el MsgBox, al usar & _ es para que el VB pueda mostrar en líneas distintas lo que se debería escribir en una misma.Después usamos vbQuestion para que muestre la interrogación y vbYesNo es para que nos muestre los botones SI / NO.Si pulsamos en Si, no se cumple la condición y si pulsamos en NO, si que se cumple, por tanto asignamos un valor falso a la variable SobreEscribir para que en la siguiente comparación 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 asignación que hacemos es la darle un valor FALSO, en caso de que no queramos guardar el contenido.

Como habrás podido comprobar, para poder guardarlo con otro nombre, deberás escribir ese nombre en el Text1.

Para la segunda mejora, necesitaremos una variable a nivel de módulo, así que añade esta declaración en la sección de las declaraciones generales del formulario:Dim Modificado As Boolean

Como recordarás 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. Usaremos este evento para saber cuando se ha cambiado el contenido del Texto escrito.Añade este código 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á también 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 código, más o menos sería algo así:

If Modificado Then

If MsgBox("El texto se ha modificado..." & vbCrLf & _

"¿Quieres guardarlo?", vbQuestion + vbYesNo) = vbYes Then

'Guardarlo

'...

End If

Page 174: curso básico de programación en visual basic

End If

Realmente no sería 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 conseguiríamos nuestro objetivo.

Por suerte, la solución a este inconveniente es tan simple como la de usar una variable, a nivel de módulo, para guardar el nombre del fichero abierto o guardado por última vez.

Nota:Fíjate que uso variables a nivel de módulo para algunas cosas, de esta forma estas variables, como ya deberías saber, estarán disponibles en cualquier parte del módulo actual, en este caso: el formulario.

Añade esta declaración en la parte general de las declaraciones del formulario:Dim sFichero As String

Ahora modifica el código para que podamos usar esta variable:

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 & _

"¿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

Page 175: curso básico de programación en visual basic

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

Fíjate 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 debería de dejar de valer verdadero cuando lo guardemos, así que añade al final del procedimiento que guarda el fichero, esta asignación:Modificado = False

Realmente deberás ponerla después del Close i y dentro de la comparación que decide si se debe guardar o no el contenido del Text2.

También 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 variable cuando se guardó un fichero.Esto deberás hacerlo una vez que se guarde el fichero, es decir, si no hemos cancelado la grabación.

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 conservaríamos el nombre del fichero, antes de haber modificado el contenido del Text1 para asignar un nuevo nombre...

Aunque parte de este "come-coco" se solucionaría con el uso de un cuadro de diálogo que preguntara el nombre del fichero a abrir o a guardar, de esta forma no sería necesario dejar siempre un TextBox para que se pueda escribir el

Page 176: curso básico de programación en visual basic

nombre.Aún así, necesitaríamos una variable para conservar el nombre del fichero...

Bien, vamos a probar esto... a ver si funciona bien...

Como podrás comprobar, no está todo lo "refinado" que quisiéramos... Después 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 "cambiándolo", por tanto se produce el evento Change y se asigna el valor de la variable Modificado, así que también tendremos que asignar un valor FALSE a esta variable después de abrir el fichero y asignarlo al Text2, es decir al final del procedimiento Abrir.De esta forma sólo se activará la "alarma" cuando realmente se modifique el texto.Este valor asignarlo justo después de cerrar el fichero o después 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 automáticamente 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 también ocurre al cerrar la aplicación, es decir el evento Unload.En este evento también se puede poner una comprobación 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, añade este código en el evento Form_Unload:

Private Sub Form_Unload(Cancel As Integer)

If Modificado Then

If MsgBox("El texto se ha modificado..." & vbCrLf & _

"¿Quieres guardarlo?", vbQuestion + vbYesNo) = vbYes Then

'Asignar el anterior

Text1.Text = sFichero

'guardarlo...

cmdGuardar_Click

End If

End If

Set Form1 = Nothing

End Sub

Page 177: curso básico de programación en visual basic

Aunque en el caso de que no hayamos usado ningún nombre, el contenido de sFichero, no sea adecuado, así que también podríamos tener un valor por defecto en esta variable, que sería el que se mostrase al iniciarse el programa, por tanto vamos a añadir 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

Fíjate 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 más cuando nos topemos con las clases y la creación 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 ocasión.

Ahora los ejercicios:

1.- Como ya he comentado, los TextBoxes no soportan todos los caracteres que quisiéramos, para redondear, digamos que soportan 32000. Esto es casi cierto en VB de 16 bits, realmente admiten 32767 (32 KB).En 32 bits en teoría 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 función del API de Windows que permite asignar 64 KB a un TextBox, en 32 bits no tiene ningún efecto ya que, como he repetido más de una vez... admite 64 KB.Para no liarte más de lo que ya puedas estar, vamos a dar por sentado que sólo 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 más 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 sólo 32000 caracteres del fichero que tenga más de esos... así al menos podrás editar esa parte de los ficheros grandes, cosa que no es recomendable, sobre todo si lo guardas, ya que podrías "cargarte" un fichero que puede ser necesario... El que avisa...

Aunque, para curarte en salud, podrías impedir que se guardase un fichero del cual no se hayan leído todos los caracteres que tuviese... por tanto, un tercer ejercicio:

3.- Si el fichero abierto tiene más de 32000 caracteres, leer sólo 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 solución de los cuales te pongo en este link, además

Page 178: curso básico de programación en visual basic

en la página de las soluciones tendrás el código para usarlo con los dos Textboxes para permitir el ajuste de línea.

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 próxima.Por supuesto que también puedes usar ese link para darme "ánimos" a que continúe con el curso... o si te sobra ese portátil y me lo quieres regalar..., pero, por favor, no lo uses para hacerme consultas... después no te quejes si no te las contesto, que algunos aprovecháis el rollo ese del peloteo para soltar alguna consultilla... que ya nos vamos conociendo...

Nos vemos.Guillermo

1.- Comprobar que no tenga más de 32000 caracteres y en caso de que el fichero los tenga, no abrirlo.

'Esta es la parte que se encarga de abrirlo, el código 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)

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 tamaño es menor o igual al máximo indicado, si es así, abrir el fichero, por tanto, sólo tendremos que poner la parte que asigna al Text2 el contenido del fichero, dentro de la comparación.

2.- En caso de que sea mayor de 32000 caracteres, leer sólo los 32000 primeros.

Page 179: curso básico de programación en visual basic

La solución es casi como la anterior, bueno, casi, es decir tendremos que hacer una comparación y en caso del que sea cierta, asignar a la variable que indica el número de caracteres a leer el valor máximo 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 condición, asignamos este valor a esa variable, de esta forma sólo se leerán esos caracteres.

3.- Si el tamaño es mayor de 32000 no poder guardarlo (y como extra: no poder editarlo)

Para conseguir esto, debemos aprovechar el código usado en la segunda solución, de forma que si es mayor de 32000, podamos asignar un flag, para saber que no se debe guardar.También podríamos 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 debería permitir la escritura, ahora veremos cómo hacer estas cosas.

Veamos las cosas por partes:

'Asignamos el nombre del fichero

sFichero = Text1.Text

If Existe(sFichero) Then

Text2.Text = ""

Page 180: curso básico de programación en visual basic

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 tamaño no es el leido,

'no permitir escribir

Text2.Locked = True

'si además 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 solución, aún 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 automática al asignarlos con estas constantes.

Ahora pasemos a ver cómo evitar que se guarde, cuando se abra parcialmente un fichero.Para ello necesitaremos otra variable a nivel de módulo: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

Page 181: curso básico de programación en visual basic

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)

'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

'Si el tamaño no es el leido,

'no permitir escribir

Text2.Locked = True

'si además cambiamos el color... mejor

Text2.ForeColor = vbGrayText

'No permitir que se guarde

PoderGuardar = False

Page 182: curso básico de programación en visual basic

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

'Sólo se ejecuta el código 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("ATENCIÓN, el fichero ya existe." & vbCrLf & _

"¿Quieres sobrescribirlo?", vbQuestion + vbYesNo) = vbNo Then

'Hemos contestado que no, así que...

SobreEscribir = False

End If

End If

'Si no existe o se quiere sobrescribir...

If SobreEscribir Then

i = FreeFile

Open Text1.Text For Output As i

Print #i, Text2.Text

Close i

'Ya hemos guardado las modificaciones

Modificado = False

Page 183: curso básico de programación en visual basic

sFichero = Text1.Text

End If

End If

End Sub

Pues creo que esto es todo...Una cosa que me gustaría que hicieras, al menos así aprenderías más, es que intentaras entender las soluciones, no te limites a copiarlas, sin saber el porqué de su uso... por supuesto que no sólo hay una solución y si la que tu has encontrado, sirve para lo mismo, pues mejor aún.

Aquí tienes el código 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 sólo fuese uno...

Que lo disfrutes y prueba a experimentar otras posibilidades...

Nos vemos.Guillermo

Habrás notado que ya no aparecen los links al principio de esta entrega, ello se debe a que he añadido una nueva página, desde la cual puedes linkar con todas las entregas, además 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 "depurándolas".La intención 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 sólo un primer intento, que espero ir mejorando, para que te sea fácil encontrar la palabra o tema que te interesa.

Decirte que esa lista la he "sacado" con una utilidad, que seguramente pondré pronto en mis páginas y que trata de mostrar cada una de las "palabras" que aparecen en los ficheros que se procesen... además de esa utilidad, he tenido que ir leyéndome cada una de las palabras y tomando nota, por lo tanto alguna se me ha podido escapar...

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 deberían tenerla... no, no es una contradicción, 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 operación:Posición_Actual = (Número_Registro -1) * Tamaño_del_Registro + 1Pero no te asustes... tú no tienes que hacer ningún cálculo... de eso se encarga el Visual Basic.

Page 184: curso básico de programación en 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 serviría 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 ocasión es un poco más importante, más que nada porque, como ya he dicho, tenemos la obligación 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 pequeño detalle antes de continuar, cuando abrimos un fichero secuencial para lectura (Input), el Visual Basic o el sistema operativo, sólo 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), sólo que en esta ocasión sólo 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 cómo, 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 As String * 30

Edad As Integer

email As String * 50

End Type

Declaramos una variable de este "nuevo" tipo de datos, que será la que usaremos para acceder al fichero:

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.

Page 185: curso básico de programación en visual basic

No siempre hay que hacerlo así, podríamos 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, podríamos usar un string con esa longitud para acceder al fichero en cuestión y...

¡Toc, toc! ¡¡¡Guille!!!¿Sí?¿No crees que te estás precipitando?¿Yo? ¿Por qué?Porque estás hablando de acceder así o asao a los datos y aún no has explicado cómo 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 instrucción: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 más fácil, ya que al menos a ellos esas dos palabras tienen algo de sentido...Después de cualquiera de estas instrucciones se indica el número de fichero (#canal), seguido por el número 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 estén definidos al crear el tipo.Todas estas "precauciones" se consiguen con datos "estáticos", es decir que permanecen sin cambios, al menos en lo que a tamaño se refiere. Los "dinámicos" son los que pueden cambiar "dinámicamente" de tamaño.Aclaremos estos puntos antes de continuar.

Una variable declarada de esta forma:Dim cadenaEstatica As String * 50Siempre tendrá 50 caracteres, incluso si hacemos esta asignación:cadenaEstatica = ""Con esto no eliminamos las 50 celdas de memoria, sino que la rellenamos de espacios...

Page 186: curso básico de programación en visual basic

Prueba con este código:Crea un nuevo proyecto y añade 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 sería este:

Es decir que una cadena estática (o de longitud fija), siempre tendrá el número de caracteres que le indicamos al declararla.Sin embargo una cadena dinámica sólo tendrá los caracteres que le asignemos.

Prueba este código:

Private Sub Form_Load()

Dim cadenaDinamica As String

Show

Page 187: curso básico de programación en visual basic

cadenaDinamica = "Hola"

Print cadenaDinamica & "Pepe"

cadenaDinamica = ""

Print cadenaDinamica & "Pepe"

Print

Print "Longitud de la cadena:"; Len(cadenaDinamica)

Print "Asc(cadenaDinamica) ="; Asc(cadenaDinamica)

End Sub

Habrás obtenido algo parecido a esto:

O sea "Illegal Function Call", este error lo da al intentar conseguir el código ASCII de la cadena... pero como la cadena está vacía... 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 vacía a un string dinámico, lo dejamos "seco", es decir sin nada en el interior...

Lo mismo ocurre con los arrays, ya sean de caracteres o numéricos.Los estáticos siempre conservan el número de elementos, incluso cuando se eliminan, (al menos eso es lo que parece), con Erase. Aunque esto ya lo vimos, no está de más recordarlo: Al "eliminar" con Erase un array dinámico, lo eliminamos de la memoria, pero en los estáticos, simplemente ponemos el contenido a cero, (o a cadenas vacías si son de cadenas dinámicas).Pruébalo:

Page 188: curso básico de programación en visual basic

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 número de elementos que va a contener, siempre con constantes, estos arrays son estáticos, porque siempre contendrán ese número de elementos.Sin embargo los dinámicos, se declaran sin decirles cuantos elementos van a contener y después se usa Redim o Redim Preserve para cambiar el número de elementos.

Veamos un ejemplo de un tipo definido con un array estático:

Private Type t_colegas2

Nombre As String * 30

Edad As Integer

email(1 To 3) As String * 50

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 dinámicos, 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 "precaución"... no accederás con "fiabilidad" a los datos contenidos en ese fichero... después no digas que no te advertí...

De todas formas, vamos a comprobarlo...Escribe este código en un form:

'Este código en las declaraciones del form

Option Explicit

Private Type t_colegas

Nombre As String * 30

Edad As Integer

email As String * 50

End Type

Dim unColega As t_colegas

Page 189: curso básico de programación en visual basic

Private Type t_colegaDinamico

Nombre As String

Edad As Integer

email As String

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 = "[email protected]"

End With

Put #nFic, 1, unColega

'ahora lo hacemos con el colega dinámico

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 encontrarás con un "bonito" error número: 59 Bad Record Length o La longitud de registro es incorrecta, si usas la versión 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.

Page 190: curso básico de programación en visual basic

Pero... ¿por qué? si, aparentemente, la longitud es la misma...Pues eso, que sólo es en apariencia... si en la ventana "Inmediato" escribes esto, saldrás 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: 82Pero en nuestro amigo dinámico, la longitud es 10, independientemente de que "en teoría" tenga almacenados esos 82 caracteres del "estático". Lo que ocurre es que al ser dinámico, 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 dirección de memoria en la que están 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 número dos o tres... dónde hablamos de lo que ocupa cada variable... los enteros (Integer) ocupan 2 bytes, independientemente de cómo esté declarada la variable... lo mismo ocurre con los otros datos numéricos.

Como te decía, 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 tamaño especificado a la hora de abrirlo.

Un poco de complicación: Vamos a ver un ejemplo, "ilustrativo" más que práctico.Ya he comentado que con una cadena de longitud fija podemos acceder a un registro. Ahora comprobarás lo "cómodo" que es usar los tipos definidos. Pero este ejemplo lo pongo para que sepas cómo se almacenan los datos numéricos 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 cómo se almacena, simplemente asignas el valor a la variable numérica y del resto se ocupa el VB.

Viendo el ejemplo anterior, es fácil almacenar un entero, si usamos un tipo definido, pero ¿cómo lo haremos si la variable en la que se almacenará los datos es una cadena de longitud fija?

Page 191: curso básico de programación en visual basic

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 siguientes, es decir: bytes 31 y 32, para el email desde la posición 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 años"

laEdad = 23

elEmail = "[email protected]"

unaCadena = elNombre & laEdad & elEmail

Put #nFic, 2, unaCadena

Close nFic

No conseguirás el resultado esperado... en lugar de 23 años, tendrá: ¡¡¡ 13106 !!! Dudo mucho que llegue a conseguir esa edad... ni aún siendo un gnomo...Si queremos guardar un número debemos convertirlo antes... el problema es que "NO HAY FUNCIONES DE CONVERSIÓN"Antes si que las había... cuando digo antes, me refiero al Basic del MS-DOS.Para cada tipo de dato numérico existían un par de funciones, una para convertir el número en una cadena y el otro para convertir la cadena en un número...Y no me estoy refiriendo a las funciones que convierten los números en letras y las cadenas en números... aunque pueda parecerte una contradicción... no es lo mismo convertir un número en una cadena normal que en una cadena para almacenar en el disco como si de un número se tratase...

Page 192: curso básico de programación en visual basic

A saber, STR o CSTR convierten un número en una cadena, es decir que si el número es 23, lo convierten en "23" en el caso de CSTR y en " 23" si se usa STR, fíjate en el espacio que hay antes del 2 en el segundo caso.Pero cuando se debe convertir un número 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 ejemplo este de "23" está claro... pero y si el número es 1998? Tendríamos una cadena de 4 caracteres...

Como te decía, en los tiempos del MS-DOS, existían las funciones MKI$ y CVI para convertir los números 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 están...Aunque podemos fabricárnoslas... pero, como no es plan de "romperse" el coco con una chorradilla de estas... sólo vamos a usar el ejemplo de los números enteros.

¿Cómo se usarían?En el ejemplo anterior haríamos esto:

elNombre = "Pepe de 23 años"

laEdad = Mki(23)

elEmail = "[email protected]"

unaCadena = elNombre & laEdad & elEmail

Put #nFic, 2, unaCadena

La función se haría 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 complicación ¿verdad?Pues como estas... muchas más... pero eso antes, en el MS-DOS... desde que hay

Page 193: curso básico de programación en visual basic

"mogollón" de espacio de almacenamiento... he perdido de vista muchas de estas cosas de "manejo" de números a "bajo nivel"...Pero antes había que ahorrar cuantos bytes se pudieran...

La cuestión es que se divide el número en dos partes, la alta que se consigue dividiendo el entero entre 256 y la baja, que es el resto que queda... después de haberlo dividido por 256... que de eso es de lo que se encarga el MOD...Después 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 al valor que devolverá la función... (recuerda que una función devuelve un valor cuando se asigna ese valor a su nombre...)

Bien... un lío... ¿verdad?Pues ahora más líos...¿Cómo se asigna esto al revés? Es decir: ¿Cómo se convierte una cadena de 2 bytes en un entero?Con los tipos definidos no hay que hacer nada... ya sabes, que hace la conversión automáticamente.Pero si usamos una cadena de longitud fija... ya es otro cantar...

Vamos a ver la declaración de la función "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 cómo "trabajan" alguna de estas funciones usando "puntos de interrupción", es decir que quieres que el VB se detenga en una línea determinada, (para ello deberás posicionarte en la línea, que no debe ser un comentario, y pulsar F9, al llegar a esa línea el Visual se detiene), deberás 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

Page 194: curso básico de programación en visual basic

Dim laEdad23 As Integer

Show

laEdad23 = 23

nFic = FreeFile

Open "miscolegas.dat" For Random As nFic Len = Len(unColega)

'Asignamos los datos

With unColega

.Nombre = "Pepe"

.Edad = 22

.email = "[email protected]"

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 años"

laEdad = Mki(laEdad23)

elEmail = "[email protected]"

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

Page 195: curso básico de programación en visual basic

'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

'De esta manera está la cosa más 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, posición_de_inicio, numero_de_caracteres)Lo que significa que devolverá el número de caracteres indicados por numero_de_caracteres empezando por la posición: posición_de_inicio. En caso de que no se especifique el número de caracteres, devolverá toda la cadena desde la posición indicada hasta el final de la misma.

Y ya que estamos con estas funciones... voy a explicarte las dos usadas en la función 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.

Page 196: curso básico de programación en visual basic

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 posición 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

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 posición i... sólo 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 cuestión es que ahora sabes unas instrucciones nuevas...

Y ya que estamos...Veamos cómo se puede usar también MID$, lo que ocurre es que esta "función" también es una "instrucción", al igual que ocurría con INPUT, según se use delante o detrás del signo igual, se convierte en función o en instrucción.

Ya sabes que una función devuelve un valor y se puede usar en una expresión, pero una instrucción "hace" algo y no devuelve ningún valor ni se puede usar en una expresión.

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

Page 197: curso básico de programación en visual basic

elNombre = "Pepe de 23 años"

laEdad = Mki(laEdad23)

elEmail = "[email protected]"

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) = "[email protected]"

Put #nFic, 2, unaCadena

Es decir, al igual que la función con el mismo nombre, la instrucción MID$ sustituye en "unaCadena" los caracteres indicados por el inicio y la longitud por los que están después 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 tenía prevista de 7 páginas "manuscritas" sólo he usado 2 y poco más...

Para completar esta "extraña" entrega... extraña por "no prevista"... vamos a "rematarla" con unos ejercicios, que en este caso serán de manejo de cadenas con las funciones que acabamos de ver...

Los ejercicios:

1- Haz que se muestren los caracteres de una cadena al revés.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", divídela 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 más la vida... que ya vendrán complicaciones posteriores.

Page 198: curso básico de programación en visual basic

Pulsando en este link tienes las soluciones.

Para terminar, sólo 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 tendría que recordarte que ese link sólo 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

1- Haz que se muestren los caracteres de una cadena al revés.

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 revés", 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", divídela en las dos palabras.

'

Dim unaCadena As String

Dim i As Integer

Dim palabra1 As String

Dim palabra2 As String

unaCadena = "Hola Mundo"

Page 199: curso básico de programación en visual basic

Cls

For i = 1 To Len(unaCadena)

If Mid$(unaCadena, i, 1) = " " Then

palabra1 = Mid$(unaCadena, 1, i)

'También así:

'palabra1 = Left$(unaCadena, i)

palabra2 = Mid$(unaCadena, i + 1)

'Realmente deberíamos 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 odiarán por esto), bien asignando a la variable i el valor máximo que puede tener: Len(unaCadena); en el caso de que la variable tenga más 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 asignación por esta otra:unaCadena = "Hola Mundo desastroso"

Y comprueba lo que te digo...

Más adelante veremos otras formas de "obtener" cada una de las palabras de una cadena...

Ya sólo queda esperar a la próxima entrega... en la que "remataremos" lo del acceso aleatorio ese...

Nos vemos.Guillermo

Page 200: curso básico de programación en visual basic

A estas alturas ya estarás acostumbrado a mis "desvaríos", 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 cuestión 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 ningún plan nacional de estudios... pues eso...Lo que si que me interesa saber es que no te pierdes con tantos desvaríos... es decir, que sigues la línea... 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, también 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 desvaríos, 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 más saber algo más. Ten en cuenta que la mayoría 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, cómo tomar caracteres de una cadena desde el principio (LEFT), desde el final (RIGHT) y desde cualquier punto (MID); también podemos saber cuantos caracteres tiene una cadena (LEN) e incluso convertir un número en cadena (CSTR) o una cadena en número (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 lío Guille? A que es conveniente que lo sepas, nada más, 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 añadiéndole al final el signo dólar ($), ya sabes que ese signo es el que se emplea para indicar que una variable es de cadena. Por tanto LEFT$, MID$ y RIGHT$ devolverán siempre una cadena, mientras que LEFT, MID y RIGHT lo que devuelven es un Variant de subtipo String... un lío... pero ya te digo que no debes preocuparte.

Nota del 24/Ago/2003:Repasando esta entrega quiero hacer un inciso, que aunque en el párrafo anterior ha quedado "medio-claro", quiero que se clarifique totalmente, ya que en ese párrafo 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 dólar ($), de esa forma le estás "confirmando" al Visual Basic que quieres usar cadenas de caracteres y no un dato del tipo Variant con "sub-tipo"

Page 201: curso básico de programación en visual basic

String. Un Variant siempre es más "pesado" que un tipo String, (lo de pesado quiere decir que necesitará de más "tratamiento" por parte del VB).

Hay un montón de funciones para esto del manejo y conversión de las cadenas, en esta entrega veremos algunas de ellas, las que usaremos más a menudo. Si eres de espíritu 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 fíes 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 traducción... en fin... por no fiarte, no te fíes ni de lo que yo te diga, siempre comprueba las cosas, así saldrás de dudas... y si no sales de dudas, pues... peor "pa tí"

Creo que voy a complicarte un poco más la vida, pero no te espantes, no va a ser en vano.

Además de los tipos de datos que hemos vistos hasta ahora, hay otro que sirve para esto de las cadenas, o casi, ya que se podría 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 estás usando el VB de 16 bits sólo hay un byte por cada uno de los caracteres. El tipo en cuestión es BYTE y en ese tipo sólo se puede almacenar un valor que va desde 0 a 255, es decir un byte, la lógica algunas veces es aplastante...Así que, si le echas un vistazo a la ayuda, te encontrarás con algunas funciones que tienen al final una letra B, eso significa que devuelve valores tipo Byte, así por ejemplo LENB, devolverá el número de bytes que tiene una cadena.

Prueba esto, verás que si lo usas con el VB de 16 bits te devuelve una cosa distinta a lo que haría en 32 bits:

Print Len("Hola Mundo")

Print LenB("Hola Mundo")

Lo que se mostrará, (recuerda de ponerle un Show, si escribes este código en el evento Load del form), será 10 y 20 si usas el VB de 32 bits, (recuerda que el VB5 sólo 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 también 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 aún usar los arrays del tipo byte, como si fuesen cadenas.Vamos a comprobarlo, pon estas líneas de código en el evento Load de un form:

Page 202: curso básico de programación en visual basic

Show 'para que se muestre

Dim aByte() As Byte

aByte = "Hola Mundo"

Print aByte

Como comprobarás 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, también puedes asignarlo a un Label y se mostrará el contenido de ese array como si fuese una cadena.Esto mismo, por supuesto también es aplicable al Visual Basic de 32 bits.

Aclaración para los que trabajéis con 16 bits:Para no complicar mucho las cosas, si estás usando 16 bits, comprueba lo que aquí se diga y saca tus propias conclusiones.A pesar de que aún hay sistemas funcionando de 16 bits y gente que por tanto programa para esos sistemas, creo que el enfoque adecuado de este curso debería concentrarse en los sistemas de 32 bits, de todas formas, la mayoría de las cosas "realmente" válidas servirán tanto para 16 como para 32 bits.Pero voy aún más 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 programación enfocada a objetos, (que pronto empezaremos a tocar), que aunque la mayoría de esas cosas sean válidas para el VB4, en algún momento dejará de serlo y sólo serán aplicables al VB5 y posteriores...Quiero dejar esto claro, para que después no haya sorpresas... de todas formas, recalco que esto es un curso básico y como tal, servirá para prácticamente cualquier versión de VB, aunque mi "despiste" seguramente me llevará a tocar cosas sólo 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 aún 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

Page 203: curso básico de programación en visual basic

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 número máximo 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 cómo de 32 bits mostrará cero.

Si te preguntas que pasaría si se añadiera a las declaraciones generales del Form la instrucción: 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 afirmación 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 harías con cualquier otra de cualquier otro tipo de datos. Lo que ocurre es que deberías tener la precaución de que el número de elementos fuese par si lo que pretendes es que contenga "algo parecido" a una cadena.Vamos a seguir probando un poco más, escribe este código:

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 sería 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, más o menos, eso de que cada caracter de 32 bits ocupa dos bytes, el primero es el normal y el segundo es el extendido...

Page 204: curso básico de programación en visual basic

Para "rematar" esto de los arrays de bytes, una última prueba, si usas el VB4-16 deberás modificarlo un poco, ya que sólo tienes un byte por cada caracter y realmente es menos "instructivo" que si estás 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 serán 30 los bytes que tiene este array, es decir 15 caracteres, que van desde A (65) hasta O (letra o mayúscula, 79), por eso se imprimen estos quince caracteres: ABCDEFGHIJKLMNO.

Para terminar con estas pruebas, escribe esto a continuación 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 CDEFíjate que el MidB empieza por un número impar, si lo hicieras por uno par, el VB te mostraría una serie de interrogaciones... porque no sabe que es lo que quieres hacer con eso... más o menos...Si te preguntas que es lo que ocurriría si en lugar de MidB(a, 5, 6) escribieras esto otro: MidB(a, 0, 6), te encontrarías con un error del Visual Basic, ya que el argumento desde, debe ser un valor 1 hasta la longitud de la cadena.

Page 205: curso básico de programación en visual basic

Bien, ya sabes algo más, 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 tendrás a mano la ayuda para poder "recordar" que están ahí.

Más funciones de manejo de cadenas, por favor.

Convertir números a cadenas:Cuando quieras convertir un número en una cadena, además de usar la "conversión automática" 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 sólo el número, sin espacios, mientras que la segunda, en caso de que sea un número positivo, devolverá el número 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 verás, en el primer caso se mostrará: i= 10, en el segundo: i=10, es decir que Str$ le añade el espacio que tiene los números positivos. Si el valor de i fuese -10 mostraría 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 configuración 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

Page 206: curso básico de programación en visual basic

Cambiar a mayúsculas / minúsculas:Para hacer esto, usaremos las funciones: LCase y UCase, la primera convierte la cadena a minúsculas y la segunda a mayúsculas:

a = "Un niñito cálido"

Print LCase(a)

Print UCase(a)

Aunque sea una chorradilla de frase, es para que compruebes que los caracteres acentuados y la eñe también se convierten.En los números, esto de la conversión no tiene sentido, por tanto no les afectará esto de la conversión y permanecerán 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 función es lo mismo que si hiciéramos esto: Ltrim$(RTrim$(unaCadena)), que es lo que hacía yo antes de que el Visual Basic incluyese esta función...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 actúan como se esperaba...

Averiguar el código de los caracteres:Ya sabes que cada "letra" que puede tener una cadena de caracteres está representada por un código, así la A es el código 65, la Z es el 90, la a es el 92 y la z es el 132. El código del espacio es 32.Para averiguar los códigos de los caracteres y para asignar a una cadena cualquier código, convertido en el correspondiente caracter, se usan las funciones Chr$ y Asc.La primera devuelve el caracter correspondiente al número que se le pasa como parámetro.La segunda hace la operación inversa, nos dice cual es el código del caracter que se le ha indicado.Veamos un ejemplo:

Print Asc("Z")

Print Chr$(90)

Page 207: curso básico de programación en visual basic

El primero nos dirá que 90 es el código de la Z mayúsculas 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 más de una caracter, sólo nos devuelve el valor del primero:

Print Asc("Hola")

Imprimirá 72, es decir el valor de la H mayúscula.

Llenar una cadena con una cantidad de caracteres:Para esto, podemos usar un bucle y en cada iteración del mismo incrementar el contenido de una cadena o bien podemos usar dos de las funciones que el Visual Basic pone a nuestra disposición: 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 parámetros, el primero indicando la cantidad de caracteres que queremos y el segundo el código de ese caracter: a = String$(10, 65) nos dará diez letras A mayúsculas.También podemos pasarle en el segundo parámetro una cadena, por tanto el ejemplo anterior se podría escribir así:a = String$(10, "A")Si la cadena que le pasamos tiene más de un caracter, nos devuelve sólo el primer caracter de esa cadena:a = String$(10, "Pepe")Es decir sólo 10 letras P.

Comparar el contenido de dos cadenas:Ya sabes que las cadenas se pueden comparar igual que se hacen con los números: usando el signo igual.If "Hola" = "hola" ThenEs un ejemplo "chorra", pero se puede hacer.Esta comparación debería devolver FALSE, ya que la H inicial en una de las cadenas está en mayúsculas y en la otra en minúscula. 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 mayúsculas y minúsculas. Para ello tendremos que agregar en la parte general del módulo, donde está el Option Explicit, la siguiente instrucción:Option Compare TextDe esta forma, las comparaciones realizadas siempre serán independiente de que sean mayúsculas o minúsculas, incluso si están mezcladas las mayúsculas con las minúsculas.Por defecto el Visual Basic hace lo que se llama una comparación binaria, es decir que comprueba el código de cada uno de los caracteres que componen las cadenas; también 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 estás haciendo las comparaciones de esa forma, añade esta línea:Option Compare Binary

Las comparaciones también se pueden efectuar para saber cual es mayor o menor, el VB se guiará por el código de cada caracter comparado, deberás

Page 208: curso básico de programación en visual basic

tener en cuenta que los números están antes que las letras, por tanto "1234" sería menor que "ABCD" y "VWXYZ" será mayor que "DEFGH"

Pero además de efectuar las comparaciones de esta forma, existe una función que sirve precisamente para eso: StrCompEsta función permite que las comparaciones se hagan de distinta forma:Usando el valor por defecto, es decir el indicado en Option Compare,usando siempre comparación binaria, (distingue entre mayúsculas y minúsculas)y usando siempre comparación "textual", (no hace distinción entre mayúsculas y minúsculas)

Los parámetros que usa esta función son los siguientes: cadena1, cadena2 y opcionalmente el tipo de comparación a realizar.Si este último parámetro no se especifica, se efectuará la comparación según se indique en Option Compare.En otro caso, los valores pueden ser: 0 (comparación binaria) ó 1 (comparación textual).Existe otro valor que es usado para las bases de datos de Access, pero que nunca lo he llegado a probar... sólo lo he visto cuando he leido la ayuda.

Los valores que devolverá esta función serán:0 si las dos cadenas son iguales,-1 si la primera cadena es menor que la segunda1 si la primera cadena es mayor que la segunda.

Debes tener en cuenta que "HOLA" es menor que "hola", si la comparación es binaria, lo mismo con "Hola", ya que la H mayúscula tiene un código menor que la h minúscula.

Veamos un ejemplo:

Dim b As String

a = "Hola"

b = "hola"

Print "Usando el Option Compare: "; StrComp(a, b)

Print "Usando comparación textual: "; StrComp(a, b, vbTextCompare)

Print "Usando comparación binaria: "; StrComp(a, b, vbBinaryCompare)

Los valores de las constantes vbTextCompare y vbBinaryCompare ya están 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í tendrás una referencia más a mano... además de

Page 209: curso básico de programación en visual basic

servirte de recordatorio, por si lo has olvidado.La concatenación consiste en añadir a una cadena el contenido de otras cadenas o caracteres.

Como sabrás si quieres incrementar el valor de una variable numérica, haces esto: Num = Num + 1El basic interpretará primero la expresión, o lo que haya después 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 sólo 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 había además de un espacio y el contenido de "b"

Este tipo de concatenación también 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, podrías 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 & " " & b

El resultado sería el mismo.

Nota:Siempre que quieras "sumar" cadenas, deberías usar el signo & en lugar de +, ya que el primero es evidente que "suma" cadenas (concatena), mientras que el segundo, aunque también "suma", debería usarse sólo para "sumar" números.

Page 210: curso básico de programación en visual basic

Averiguar la posición de una cadena dentro de otra:Para esto usaremos la función Instr, esta función espera dos parámetros como mínimo, aunque permite cuatro:Instr( [posInicio,] Cadena1, Cadena2[, tipo_comparación])Las dos cadenas, son las que usará para devolver la posición 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 posición sexta, al menos ahí es dónde empieza.Si se especifica posInicio, empezará a comprobar a partir de esa posición inclusive, por tanto:Print Instr("Hola mundo", "o") imprimirá 2Print Instr( 6, "Hola mundo", "o") imprimirá 10, ya que busca una letra "o" a partir de la posición 6.

El último parámetro: tipo_comparación es para realizar comparaciones binarias o de texto, como ya hemos visto anteriormente, si este parámetro tiene la misma función que en StrComp. Es decir si no se especifica, la comparación se hace según el Option Compare y si se indica, se hace según lo indicado.Si especificas este parámetro, deberás indicar también la posición 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 comparación textual) o un 2 si tenemos esa opción de comparaciónEn los demás 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" minúscula y en la primera cadena tenemos dos de esa letra, otro gallo cantaría si tanto la primera como la segunda cadena fuesen variables y no sabemos lo que va a contener.

Cuando sea imprescindible saber la posición de una cadena dentro de otra, teniendo en cuenta el que haya que distinguir o no entre las mayúsculas y minúsculas, es recomendable que uses el tipo de comparación que quieres realizar.Si no lo haces, al menos recuerda o comprueba que tipo de comparación se realizarán por defecto en ese módulo.

Bien, creo que ya tienes a tu disposición casi todas las funciones de manejo de cadenas que puedas necesitar.

Page 211: curso básico de programación en visual basic

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 debería 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 cómo 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 podría poner muchos... pero sólo voy a ponerte uno y con ese tendrás "pa rato"Escribe una función que devuelva la posición de una cadena dentro de otra, pero comprobando esa posición por el final.Es decir que Instr("Hola Mundo","o") devolvería 2, pero con nuestra función devolvería 10.Podríamos llamarla RInstr y se usaría así: RInstr("Hola mundo","o")Haz una segunda versión en la cual se le pase como primer parámetro la posición por la que se empezará a comprobar, de esta forma: RInstr2(6,"Hola mundo","o") nos dará un valor 2En las soluciones veremos cómo podemos indicar parámetros opcionales en nuestros procedimientos y crearemos una única función que se use igual que Instr, es decir pasando como primer parámetro opcional la posición de inicio de la búsqueda.

Y siguiendo la "sana" costumbre, te pido tu opinión sobre esta entrega.

Nos vemos.GuillermoP.S.Aquí tienes las soluciones a esta entrega

Empezaremos viendo la parte fácil del ejercicio, es decir la que nos muestra la posición cuando la función espera sólo dos parámetros, 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))

Page 212: curso básico de programación en visual basic

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 posición de la primera cadena Len(s1) hasta el principio, ya que lo que necesitamos es encontrar la posición de s2 empezando a "mirar" desde el final.A continuación guardamos en sTmp un trozo de cadena que va desde la posición actual y que contiene tantos caracteres como tenga la segunda cadena, si te fijas, el bucle se podría hacer también de esta forma:For i = Len(s1) - Len(s2) + 1 To 1 Step -1Ya que no nos sirve comparar el último caracter con la cadena s2 cuando esta última tiene más de un caracter, por ejemplo.Esto es inapreciable en cadenas cortas, pero en otras más largas, acortará el tiempo del bucle.

Para el segundo caso, hay que saber que los procedimientos (sub y function) permiten parámetros opcionales, estos se indican con la palabra Optional delante de los parámetros que vayan a ser opcionales.La única pega es que cuando un parámetro es opcional los que le siguen también tienen que ser opcionales.Entonces ¿cómo indicar que el parámetro opcional sea el primero?Pues... más o menos fácil... aunque con truco.Cuando un parámetro es opcional, se puede usar la función IsMissing para comprobar si se ha especificado o no ese parámetro.Esto es cierto siempre que el parámetro opcional sea de tipo Variant sin un valor por defecto. En el VB4 todos los parámetros opcinales deben ser Variant y sin valores por defecto, pero en VB5 (y posteriores) los parámetros opcionales pueden ser del tipo que queramos, además de poder tener un valor por defecto, es decir, si no se especifica, se le da un valor "predeterminado".Ahora vamos a usar sólo los parámetros sin valores predeterminados, pero más adelante seguramente lo usaremos.

Sabiendo que con IsMissing podemos averiguar si se ha especificado o no el parámetro en opcional, haremos lo siguiente:Si se especifican los tres parámetros, el primero será la posición por la que se empezará a comprobar.Si no se especifica el último parámetro, engañaremos al Visual Basic, diciéndole que los dos parámetros introducidos son la primera y la segunda cadena...Ya ves que sólo es echarle un poco de "cabeza" al asunto...

Vamos a ver la declaración de esta función:

Page 213: curso básico de programación en visual basic

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 parámetros

s1 = CStr(v1) 'La primera cadena

s2 = CStr(v2) 'la segunda cadena

posIni = Len(s1) 'el último caracter de la cadena

Else

posIni = CLng(v1) 'la posición por la que empezar

s1 = CStr(v2) 'la primera cadena (segundo parámetro)

s2 = CStr(v3) 'la segunda cadena (tercer parámetro)

End If

'Valor inicial de la búsqueda, 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 número de caracteres que tenga la segunda cadena

sTmp = Mid$(s1, i, Len(s2))

'Si son iguales...

If sTmp = s2 Then

'esa es la posición

RInstr = i

Exit For

End If

Next

End Function

Page 214: curso básico de programación en visual basic

Ahora quedaría hacer una función que comtemple las mismas opciones que tiene el InStr normal, es decir que se pueda especificar un cuarto parámetro que nos indique si la comparación se hace de una forma u otra.

¿Que te parece esto como un ejercicio nuevo?Sabía que contestaría de forma afirmativa... je, je...

Pues inténtalo... y si quieres ver la solución, selecciona el contenido de la caja negra esa que hay al final y verás...

A ver si ya terminamos en la próxima entrega el acceso aleatorio a los ficheros.

Nos vemos.Guillermo

Para ver la solución, selecciona todo el contenido del siguiente cuadro y verás...

Nota:He quitado lo de la selección 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

Page 215: curso básico de programación en visual basic

'Si no se especifican los tres parámetros

s1 = CStr(v1) 'La primera cadena

s2 = CStr(v2) 'la segunda cadena

posIni = Len(s1) 'el último caracter de la cadena

Else

posIni = CLng(v1) 'la posición por la que empezar

s1 = CStr(v2) 'la primera cadena (segundo parámetro)

s2 = CStr(v3) 'la segunda cadena (tercer parámetro)

End If

'Valor inicial de la búsqueda, 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 número de caracteres que tenga la segunda cadena

sTmp = Mid$(s1, i, Len(s2))

'Si se especifica el tipo de comparación

If Not IsMissing(v4) Then

'Se usa StrComp con ese parámetro

If StrComp(sTmp, s2, v4) = 0 Then

'esa es la posición

RInstr = i

Exit For

End If

Else

'Aquí también se podría usar

'If StrComp(sTmp, s2) = 0 Then

'Si son iguales...

If sTmp = s2 Then

'esa es la posición

RInstr = i

Exit For

End If

Page 216: curso básico de programación en visual basic

End If

Next

End Function

A diferencia del Visual Basic, no dará error si se especifica el cuarto parámetro y no se especifican los demás, por la sencilla razón de que si sólo se especifican 3, el cuarto nunca llega a tener un valor... (elemental mi querido Guille).De lo que tendrás que tener "precaución", al menos si quieres que funcione todo bien, es de indicar los valores que se esperan... ya que sino es así, no conseguirás tu propósito... o al menos no el deseado.

Ya es hora de seguir con los ficheros de acceso aleatorio, después del alto en el camino para ver cómo se manejan las cadenas de caracteres en Visual Basic, aún no hemos visto todas las funciones, pero si las más comunes. No te preocupes... no voy a continuar en esta entrega con esas funciones... ya le llegarán el turno...

¡Por fin! Habrás exclamado... y con razón... pero son cosas que debes saber... y como de lo que se trata es de saber más y/o mejor, pues... nunca están de más...

No recuerdo exactamente en que punto me quedé en las explicaciones sobre el acceso aleatorio de la entrega dieciséis, así que... si ves que me repito, pues... ¡salud! y a seguir adelante...

Una cosa que hay que tener superclarísimo 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 automática por nosotros? Por tanto, te aconsejo que "siempre" uses variables definidas, ya que no hay un motivo válido para no usarlas.

En los siguientes ejemplos, vamos a usar el tipo definido que usamos en la entrega dieciséis, 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

Page 217: curso básico de programación en visual basic

numColega = Rnd * 25 + 1

Label1(3) = "número 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

Close nFic

End Sub

Aquí vemos los pasos que normalmente se realizan con cualquier tipo de fichero...--Se asigna a una variable un número de fichero libre (ya sabes: el canal por el cual VB se comunicará con el disco)--Se abre el fichero en cuestión, recuerda que las extensiones que uses son a tu antojo, no hay necesidad de usar un tipo de extensión específica, salvo que hagas un programa que "entienda" los datos contenidos en ese tipo de extensión y puedas abrir los ficheros con sólo hacer doble click... pero esto es un tema para más adelante...--Se asigna a la variable el dato a guardar y--Se guarda...

Este ejemplo no sería práctico, ya que puede que salga el mismo número más de una vez y se perderían los datos anteriores.Porque debes saber que, cuando se guarda información 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 habrás 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 información en el registro 7 aunque antes no hayas guardado en ninguno de los 6 anteriores.

¿El problema?Que si lees información de un registro en el que no has guardado información anteriormente... puedes encontrarte con "basura", y de hecho la encontrarás...¿Por qué?Porque accedes a una parte del disco que, posiblemente tenía guardada alguna otra información... aprovecho esto, para decirte que, cuando borras un fichero del disco, este fichero no se borra, al menos no se borra la información que contenía.¿Cómo solucionar este problemilla?Hay varios métodos, el que yo normalmente usaba, (ahora casi no trabajo con ficheros

Page 218: curso básico de programación en visual basic

de acceso aleatorio), era guardar información "vacía" en unos cuantos registros y cuando esos estaban ocupados, guardaba otro puñado y así.Algunas veces, si sabía que el fichero iba a tener un número limitado de registros, los grababa todos con datos vacíos, es decir cadenas con sólo espacios y números con valor cero.En otras ocasiones tenía un campo del registro al que le asignaba un valor, si al leer el registro, tenía ese valor "predeterminado", quería decir que ya contenía información válida, si no era así, ponía los campos con valores "vacíos" y así evitaba la basura.

Si quieres comprobarlo... así de paso me sirve para que veas un ejemplo de cómo 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

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) = "número 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", deberás probar algunas veces más que las que hayas probado antes... ¿por qué? porque estamos usando números aleatorios y al no existir un "Randomize", la secuencia de números aleatorios siempre es la misma cada vez que ejecutas el programa... ¿lo recuerdas?

Page 219: curso básico de programación en visual basic

Bien, aparte del asuntillo este de la "basura", no tiene ningún misterio esto del acceso aleatorio... pero aún no hemos terminado, no queda mucho para que te toque "trabajar", pero todavía tienes un respiro... si a que te explique más 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 ningún 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 podrás comprobar... que algunas veces no hay que fiarse de todo lo que yo diga...El número de registros es el resultado de dividir la longitud del fichero por la longitud de cada registro...numRegistros = Lof(nFic) \ Len(unColega)Fíjate que uso \ para dividir. Las divisiones realizadas con este signo dan como resultado números enteros, mientras que el habitual / realiza una división de coma flotante... es decir que puede tener decimales.Como un registro no puede estar formado por "nosecuantos caracteres y pico", en este caso es recomendable el uso de la división de números enteros, entre otras cosas porque también es más rápida...

Por tanto este código nos informaría del número de registros y haría 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 número 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 número 16.Es decir, el VB mantiene un "puntero" al siguiente registro. El bucle anterior podría haber quedado así:

Page 220: curso básico de programación en visual basic

For i = 1 To numRegistros

Get #nFic, , unColega

'Mostrar el contenido del registro

'...

Next

De todas formas, siempre suelo especificar el número de registro al que quiero acceder, así me parece que estoy más seguro de dónde 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, añadir o cambiar la longitud de alguno de los campos?Pues que lo tenemos más bien chungo... estaremos metidos en un pequeño lio...Una vez que hemos definido el tamaño 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 conversión.

Y ese podría ser el ejercicio de esta entrega:Realizar una pequeña 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", sólo podemos hacerlo con variables de longitud fija, ya sean tipos definidos o cadenas.Se podría 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.

Page 221: curso básico de programación en visual basic

Otra cosa es que hagamos esto otro:

'Esta cadena siempre tendrá este número 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 funcionaría bien la cosa, ya que el VB tiene la información que necesita...La verdad es que hecho de menos una instrucción que tenía el Basic del MS-DOS y era que podías definir una serie de variables para acceder a los registros, incluso podías 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 opción para este tipo de ficheros.

Para el ejercicio, se supone que tenemos un fichero con registros declarados de esta forma:

Private Type t_Colega

Nombre As String * 30

Edad As Integer

email As String * 50

End Type

Y lo queremos convertir en registros que tengan esta otra:

Private Type t_Colega

Nombre As String * 30

Edad As Integer

email As String * 50

URL As String * 128

End Type

Por supuesto, queremos que los registros que haya sigan conservando los datos que tuviesen... sino, ¿que clase de rutina conversora sería?Acuérdate de lo que digo en muchas ocasiones, no pienses en complicarte la vida, siempre procura ir a lo simple, ya que en la mayoría de las ocasiones ese es el camino correcto...

Page 222: curso básico de programación en visual basic

Cuando veamos la siguiente entrega, o al menos cuando veamos los ficheros de acceso binario, (mi intención es que sea en la próxima entrega, pero ya sabes...), crearemos otra utilidad de conversión más flexible que esta, es decir, una rutina que convierta un fichero a un formato que pueda ser definido por el usuario en tiempo de ejecución.

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.¿Cómo?¿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 oído voces que me decían: ¡queremos hacerlo nosotros!Y eso es lo que hay...

Para esta aplicación 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 número del registro actual, es decir en el que se almacenarán los datos o del que se leerán 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 número once cuando se usaron arrays de tipos definidos...

El aspecto del "programilla" sería algo así:

No he añadido nada para mostrar todos los registros... pero ya tendremos tiempo de hacerlo.

Bueno, pues hasta aquí hemos llegado...Las soluciones de la entrega 18 están en este link.

Page 223: curso básico de programación en visual basic

Y a pesar de parecer un poco pesado, realmente me gustaría recibir tu opinión sobre esta entrega.

Nos vemos.Guillermo

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 solución sería 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

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)

Page 224: curso básico de programación en visual basic

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

'Si quieres eliminar el fichero anterior y cambiarle el nombre

'hazlo después de cerrar los ficheros

End Sub

Este es el listado completo del segundo ejercicio:

'------------------------------------------------------------------

'Ejercicio de la entrega 18 (26/Abr/98)

'

'©Guillermo 'guille' Som, 1998

'------------------------------------------------------------------

Option Explicit

'Tipo para usar en el fichero

Private Type t_Colega

Nombre As String * 30

Edad As Integer

Page 225: curso básico de programación en visual basic

email As String * 50

End Type

'Esta variable se usará para acceder a los datos

Dim m_unColega As t_Colega

'Número de registros del fichero

Dim m_numColegas As Long

'Número 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

Private Sub cmdGuardar_Click()

Dim nFic As Long

'Sólo si el número del colega es el indicado en Text4

'de esta forma sólo 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 número de colegas

Page 226: curso básico de programación en visual basic

m_numColegas = CuantosColegas()

m_Modificado = False

'Posicionar el cursor en el número 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 habría que tenerlo en cuenta... lo he dejado preparado

'con la variable m_Modificado

'Te dejo que hagas las comparaciones pertinentes...

'...

'Sólo leer si no se está añadiendo uno nuevo

If m_elColega <= m_numColegas Then

m_elColega = Val(Text4)

'Pero que no se lea un valor "no válido"

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 también

'se mostrarán en la caja de texto

'Para comprobarlo, quita el Trim$ y verás lo que

'ocurre cuando el nombre tiene menos caracteres...

Page 227: curso básico de programación en visual basic

With m_unColega

Text1 = Trim$(.Nombre)

Text2 = .Edad

Text3 = Trim$(.email)

End With

Close nFic

m_Modificado = False

Text1.SetFocus

Else

'si el número no es válido...

Text4.SetFocus

m_elColega = 0

End If

End If

End Sub

Private Sub cmdNuevo_Click()

'¿Comprobar si se ha modificado?

'...

'Añadir un nuevo colega,

'sólo 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 también la variable el registro actual,

'aunque realmente no es necesario...

Page 228: curso básico de programación en visual basic

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 asignación fallará si el path es el directorio raiz

'por tanto se debería 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

'También de esta otra forma... algo menos "clara"

m_sFicColegas = App.Path & _

IIf(Right$(App.Path, 1) = "\", "", "\") & _

"Colegas.dat"

'Inicialmente leer el número de registros

'lo pongo en una función 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 = ""

Page 229: curso básico de programación en visual basic

Text3 = ""

Text4 = ""

'Para empezar no se ha modificado

m_Modificado = False

End Sub

Private Function CuantosColegas() As Long

'Esta función se encarga de informarnos del número de registros

'que tiene el fichero

'Usarlo sólo cuando queremos saber esta información 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) = "Número de colegas:" & CuantosColegas

Err = 0

End Function

Private Sub Form_Unload(Cancel As Integer)

'Por si se quedó o estaba el fichero abierto...

Close

Set Form1 = Nothing

End Sub

Private Sub Text1_Change()

Page 230: curso básico de programación en visual basic

'Si en lugar de usar tres TextBox distintos se usara un array

'sería más cómodo, ya que sólo se pondrá esta asignación

'en un sólo 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 próxima 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

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 portátil, aún 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 automática... 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 yankinglish... pero no es plan... en fin, tendré que seguir buscando las teclas y después 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 día de playa, además de que es fiesta local y esas cosas pa que los catetillos podamos ir a darnos un remojón 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 más potente y a la vez la más complicada... o casi.Con el acceso binario podemos acceder a cualquier punto del fichero y, lo más importante, leer o guardar la cantidad de caracteres que queramos.

Page 231: curso básico de programación en visual basic

Antes de entrar en detalles, veamos cómo 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 sucedía en el modo aleatorio (random), se tiene acceso tanto de lectura como de escritura. También se usan las instrucciones GET y PUT para leer o escribir la información, pero a diferencia del acceso aleatorio, no estamos obligados a usar una variable de longitud fija, (además de longitud previamente especificada a la hora de abrir el fichero), si esto fuese así, no habría diferencia con el acceso aleatorio... así que esta entrega casi ni existiría...

¿Cómo leemos datos de un fichero de acceso binario?

Ya te he comentado que se usa GET para leer datos, la cuestión está en cómo indicarle al Visual Basic la cantidad de caracteres a leer...Pues hagamos la pregunta: ¿Cómo 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 leerán tantos caracteres como "capacidad" tenga la variable usada. Si sólo quisiéramos leer sólo un caracter, esta variable tendría una longitud de un caracter... (algunas veces alucino con mi lógica tan contundente, en fin...)La ventaja es obvia: no es necesario estar atados a un número fijo de caracteres, simplemente asignándole una cantidad de caracteres a la variable usada, es suficiente.El problema puede surgir a la hora de determinar la posición desde la que leeremos esos caracteres.Ves, nada es perfecto, y si no controlamos el tema, pues, tendremos algún que otro quebradero de cabeza.

La posición tendremos que indicarla nosotros mismos, aunque también podemos dejar que sea el propio Visual el que se encargue de este tema, todo dependerá de lo que queramos hacer.

Ya vimos en el acceso aleatorio que el cálculo de la posición de cada registro podíamos dejarlo de forma automática, es decir que sea el propio VB el que "decida" la posición. Realmente el VB no decide nada, ya que es una característica de GET y PUT, si no se le indica la posición, usa la "predeterminada" y esa posición se ajusta automáticamente cada vez que se lee o escribe información, el cálculo se hace tomando la última posición y añadiéndole la longitud del dato. En el caso de los ficheros aleatorios esa posición es "ficticia" (o relativa), ya que el VB convierte la posición real dentro del fichero en número 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 posición tres del fichero, el número de caracteres leídos estará indicado por la longitud de la variable A$

Como ya comenté antes, la ventaja es que no estamos obligados a leer un número determinado de caracteres y el inconveniente es que hay que saber lo que estamos

Page 232: curso básico de programación en visual basic

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.Añade un commandbutton y escribe el siguiente código:

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

Ejecuta la aplicación (pulsando F5), pulsa en el Command1, y verás que todo funciona bien.Ahora añade lo siguiente antes del Get nFic, , sCadena:sCadena = Space$(5)Y ejecuta de nuevo el programa.Como verás sólo se han leido los cinco primeros caracteres de lo que se guardó anteriormente. Es decir sólo mostrará Prueb, porque la cadena usada para leer tiene esa cantidad de caracteres.Este es un detalle que deberás recordar, así que apúntatelo.

Page 233: curso básico de programación en visual basic

La ventaja es que podemos guardar y leer distintos tipos de datos mezclados.Por ejemplo, si sabemos que tenemos un tipo definido y después 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 código anterior o crea un nuevo proyecto y añade un command y el siguiente código:

'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

'leer los datos guardados

nFic = FreeFile

Open sFic For Binary As nFic

Get nFic, , unColega

Page 234: curso básico de programación en visual basic

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 pequeño desastre, ya que no lees lo que esperas leer. Osea que no uses esto del acceso binario "al voleo", sino pensándolo 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 código EOF (caracter ASCII número 26) o cuando ya no hay más caracteres en el fichero; sin embargo con el acceso binario sólo se "acaban" cuando no quedan más 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 código 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

'leer como secuencial

nFic = FreeFile

Page 235: curso básico de programación en visual basic

Open sFic For Input As nFic

Do While Not EOF(nFic)

Line Input #nFic, sCadena

Print sCadena

Loop

Close nFic

End Sub

Como habrás comprobado, se ha leído todo lo que había en el fichero, incluso cosas que había de pruebas anteriores.Ahora engañemos al VB y hagamos que piense que un fichero se ha acabado antes de que se acabe de forma "real".Sustituye el código 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

'Añadimos un código 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

Do While Not EOF(nFic)

Page 236: curso básico de programación en visual basic

Line Input #nFic, sCadena

Print sCadena

Loop

Close nFic

End Sub

Ahora sólo se ha mostrado lo guardado antes del código almacenado en la variable sEOF.Pero el fichero continúa teniendo lo que antes tenía. Lo que ocurre es que cuando se abre un fichero secuencial y el VB se encuentra con el código 26, piensa que se debe haber terminado el fichero en cuestión.Ahora vamos a leerlo como binario... Por supuesto, sabiendo lo que se ha guardado y cómo 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

Page 237: curso básico de programación en visual basic

nFic = FreeFile

Open sFic For Binary As nFic

'Se leen sólo 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 sólo leyó 5 de los 20 caraceteres que tenía

Get nFic, , sCadena

'también 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 fíjate en el detalle de que hemos tenído que leer los códigos "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...Habrás observado una línea de más y un caracter extraño 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 ningún curso y no hay regla fija. En la mayoría de las ocasiones que uso el acceso binario, es cuando quiero leer información 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 leería 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 función llamada Input$, a la que se le indica el número 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 están en el rago ASCII del 32 al 255 (en algunos casos hasta el código 127).Veamos la reacción del VB ante un caso "no deseado", es decir leer lo que no debemos leer:

Page 238: curso básico de programación en visual basic

'Se supone que has probado los ejemplos anteriores y que existe el fichero indicado

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 pretendía era leer el fichero de una vez. Pero como se ha abierto el fichero como secuencial, el VB ha detectado que se la leído un código de fin de fichero, precisamente antes de que se acabe el fichero y nos avisa con un magnífico 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 función 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

Page 239: curso básico de programación en visual basic

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 ningún caracter especial y se lee hasta dónde se le indique.

Creo que es suficiente por hoy.

Hay más 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?Consistía 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 sólo era posible si conocíamos de antemano el tamaño del registro nuevo (y, por supuesto, la longitud de cada campo). Ahora con lo que sabes del acceso binario y unas cuantas miles de líneas de código, podrás hacerlo para convertir cualquier fichero a un nuevo formato y así poder usarlo de forma genérica.

Es decir, tenemos un fichero con una serie de campos y queremos cambiar la estructura de ese fichero y, por supuesto, traspasar la información existente al nuevo formato. El usuario de esta utilidad, tendrá que saber la estructura del fichero de origen y también saber la nueva estructura a la que quiere convertir el susodicho fichero de datos.Para no complicar demasiado la cosa, vamos a usar sólo 10 campos, pero se podrían permitir más...

El aspecto del form en tiempo de diseño sería el siguiente:No te preocupes por los "campos" que faltan en el destino, enseguida te explico cómo 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 ejecución y posicionarlos adecuadamente, o casi...Veamos el aspecto del form y después veremos el código usado para "crear" esos controles:

Page 240: curso básico de programación en visual basic

El código:

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 posición 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

Page 241: curso básico de programación en visual basic

Next

'Borrar el contenido de los TextBoxes

For i = 0 To 9

txtTam(i).Text = ""

txtDestTam(i).Text = ""

Next

End Sub

La cuestión está en que al pulsar en el botón de convertir el fichero, antes se hayan asignado los valores correspondientes.La información que hay que pasarle a la aplicación de cada campo, es la siguiente: tamaño de cada campo.Lo que hay que saber es el tamaño de los números 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 botón: Los Integers ocupan dos bytes, los Longs cuatro bytes, etc.

La cuestión 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 mañana a las ocho...Este es el link a la solución de esta entrega, recuerda no hacer trampas e intentarlo primero.(De todas formas, el link no te llevará a ningún sitio, ya que aún no la he puesto... je, je...)

Nota 25/Jun/98: Ya está operativo el link de la solución al ejercicio.

Y continuando con la sana costumbre de recibir tus preciados, y la mayoría de las veces aduladores, comentarios, cosa que agradezco y que dicho sea de paso, es la única razón por la que sigo con el curso básico... 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

Ahora sí que está la solución de la entrega 19, la verdad es que si no lo has conseguido, no debes preocuparte demasiado, no era tan "simple" como podría parecer, ya que se necesita de un poco de "tablas" y manejo en esto de la programación, así que si estás dispuesto a ser sincero, por favor envíame un mensaje diciendo si lo conseguiste o no, esto me ayudará a saber si tengo que poner cosas más sencillas o dedicarme a enseñar 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.

Page 242: curso básico de programación en visual basic

Este es el listado completo de la solución 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 difícil aún no ha empezado... ;-)

Esta es una foto del programa en ejecución y el listado del mismo:

'------------------------------------------------------------------

'Ejercicio para la entrega 19 (24/Jun/98)

'(solución)

'

'©Guillermo 'guille' Som, 1998

'------------------------------------------------------------------

Option Explicit

Private Sub Form_Load()

Dim i As Integer

'Para probar uso el fichero de colegas.dat

Page 243: curso básico de programación en visual basic

'el tamaño de cada campo era: 30, 2, 50

'Private Type t_Colega

' Nombre As String * 30

' Edad As Integer

' email As String * 50

'End Type

'

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 posición 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

Page 244: curso básico de programación en visual basic

Private Sub cmdConvertir_Click()

'Variables para los nombres y números de ficheros

Dim nFic As Long, nFic2 As Long

Dim sFic As String, sFic2 As String

'Estos arrays controlarán los tamaños de cada campo

Dim aOrigen() As Long

Dim aDestino() As Long

'Número de campos en cada fichero

Dim nOrigen As Integer

Dim nDestino As Integer

'Tamaños de los registros

Dim tOrigen As Integer

Dim tDestino As Integer

'Las cadenas que contendrán los datos

Dim sOrigen As String

Dim sDestino As String

'Número 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 "¡ATENCIÓN! No existe el fichero de origen."

txtOrigen.SetFocus

Exit Sub

End If

'Asignamos el nombre del fichero de destino

sFic2 = Trim$(txtDestino)

'Se asignarán los tamaños de cada registro, se dejará

Page 245: curso básico de programación en visual basic

'de comprobar cuando el contenido del textbox sea cero.

'Si se usara un TextBox con el número de campos, la cosa

'sería más fácil de controlar, pero...

'

'Empezamos por el origen

For i = 0 To 9

If Val(txtTam(i)) = 0 Then

'ya no hay nada más que comprobar

Exit For

Else

nOrigen = nOrigen + 1

ReDim Preserve aOrigen(nOrigen)

'asignamos el tamaño del campo nOrigen

aOrigen(nOrigen) = Val(txtTam(i))

'ajustamos el tamaño 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 más que comprobar

Exit For

Else

nDestino = nDestino + 1

ReDim Preserve aDestino(nDestino)

'asignamos el tamaño del campo nDestino

aDestino(nDestino) = Val(txtDestTam(i))

'ajustamos el tamaño total del registro

tDestino = tDestino + aDestino(nDestino)

End If

Next

'

'Ya tenemos la información suficiente,

'

'Por si da error al acceder a los ficheros

Page 246: curso básico de programación en visual basic

On Local Error GoTo ErrorConvertir

'Abrimos los ficheros en modo binario

nFic = FreeFile

Open sFic For Binary As nFic

'Averiguar el número de registros de este fichero

tamFic = LOF(nFic)

numReg = tamFic \ tOrigen

'Comprobar que el tamaño especificado concuerda con el fichero

'Si el número de registros multiplicado por el tamaño de cada

'registro es diferente al tamaño del fichero...

If numReg * tOrigen <> tamFic Then

MsgBox "Los tamaños especificados en los campos de origen" & vbCrLf & _

"no concuerdan con el tamaño 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 tamaño

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 tamaño de

'los campos de origen más el tamaño de los nuevos campos,

'si el número de campos de destino es diferente,

'simplemente se rellenará la cadena con espacios

sDestino = ""

'

'Esta variable contendrá la posición dentro del registro

Page 247: curso básico de programación en visual basic

'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))

sDestino = sDestino & sTmp

'ajustamos el tamaño de la posición dentro del registro

'de origen

posReg = posReg + aOrigen(i)

Next

'Ahora hay que rellenar la cadena de destino con espacios

'suficientes hasta completar el número de caracteres

'que se han especificado.

'

'El TRUCO está en añadirle a la cadena de destino la

'cantidad de caracteres totales y sólo 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 información de los formatos usados:

'

'Uso un formato standard INI para que se pueda leer de forma

'fácil, 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]"

Page 248: curso básico de programación en visual basic

Print #nFic, "Fichero=" & sFic

Print #nFic, "Número de campos=" & nOrigen

For i = 1 To nOrigen

Print #nFic, "Tamaño Campo" & CStr(i) & "=" & aOrigen(i)

Next

Print #nFic, ""

'Datos de destino:

Print #nFic, "[Datos de Destino]"

Print #nFic, "Fichero=" & sFic2

Print #nFic, "Número de campos=" & nDestino

For i = 1 To nDestino

Print #nFic, "Tamaño Campo" & CStr(i) & "=" & aDestino(i)

Next

Close

'Avisamos de que todo acabó bien

MsgBox "Se ha convertido el fichero de forma satisfactoria," & vbCrLf & _

"La información 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, sería el siguiente:

[Datos de Origen]

Fichero=colegas.dat

Número de campos=3

Page 249: curso básico de programación en visual basic

Tamaño Campo1=30

Tamaño Campo2=2

Tamaño Campo3=50

[Datos de Destino]

Fichero=colegas2.dat

Número de campos=4

Tamaño Campo1=40

Tamaño Campo2=2

Tamaño Campo3=50

Tamaño Campo4=128

Nos vemos.Guillermo

Si quieres los listados del programilla, para verlo más cómodamente, los puedes bajar pulsando en este link.

Para compensar un poco el que hayan pasado casi dos meses entre la entrega 18 y la 19, te doy esta del tirón... no es por nada, es que ya la tenía lista y realmente tenía 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 más ganas... espero.

Ya hemos visto las distintas formas de acceder a los ficheros, ahora vamos a ver una instrucción que puede sernos útil cuando decidamos "movernos" dentro del fichero.Me explico: cuando se trató el acceso secuencial, comenté que la información había que leerla de forma secuencial, es decir un dato después de otro, bueno, pues esto es cierto sólo a medias. No empieces a pegar saltos de alegría, porque tampoco es para tanto. El tema está en que si lees la información de forma "seguida", entonces si que es así, pero, si te entra hipo, puedes acceder a cualquier parte del fichero. ¿Cómo? Pues con la siguiente instrucción que te voy a presentar ahora...

A ver, instrucción ven, que te voy a presentar... venga, no te de vergüenza, estamos en confianza... (es que dice que no le gusta su nombre), aquí está...

Ñoras, ñores, damas y caballeros, les presento a: SEEK

Esta instrucción (que también es una función) se usa, en modo instrucción, para posicionarnos en cualquier parte del fichero abierto, tanto para leer como para escribir.Si se usa como función, nos devuelve la posición actual, es decir en la que nos encontramos, del fichero.Veamos cómo usarla:Seek #numFic, posición y también variable = Seek(#numFic)

El valor devuelto es de tipo Long.Dependiendo del modo en el que esté abierto el fichero habrá que "interpretar" el

Page 250: curso básico de programación en visual basic

valor de distinta forma:Para los ficheros de tipo secuencial y binario, nos da la posición en bytes (o caracteres).Para los ficheros abiertos como aleatorios (random), nos da la posición en número de registros.

Cuando lo usamos en modo instrucción, 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 posición indicada. Ni que decir tiene que el valor de la posición debe ser un valor legal. Es decir, no podemos posicionarnos en la posición CERO ni en una posición NEGATIVA, ya que no es "legal".

El uso de esta función/instrucción 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 pequeña 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 están "enmarcadas" entre corchetes y a continuación 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 protected]

La sección se llama "elGuille" y los dos campos son "nombre" y "email"Realmente para acceder a este tipo de ficheros no necesitaríamos usar Seek, ya que podemos acceder secuencialmente, pero vamos a ver cómo podemos "posicionarnos" en una sección en concreto, después de haber leído el contenido y haber tomado "buena nota" de las posiciones.

La utilidad de ejemplo, nos va a mostrar todas las secciones disponibles y después podremos acceder rápidamente a una posición en concreto.Veamos el aspecto del formulario y el código correspondiente:

Page 251: curso básico de programación en visual basic

'Esta fecha no está mal, es que ya lo tenía "manuscrito" desde entonces...

'Ejemplos del curso básico, ejemplo de Seek (26/May/98)

'

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 botón de leer contenidos

cmdLeerContenido.Enabled = False

Text1 = ""

List1.Clear

'Creamos el fichero de ejemplo

sFic = "basico_20.ini"

Page 252: curso básico de programación en visual basic

nFic = FreeFile

Open sFic For Output As nFic

Print #nFic, "[elProfe]"

Print #nFic, "Nombre=Guillermo"

Print #nFic, "[email protected]"

Print #nFic, ""

Print #nFic, "[Alumnos]"

Print #nFic, "Cantidad=2"

Print #nFic, "Nombre_01=Pepito"

Print #nFic, "[email protected]"

Print #nFic, "Nombre_02=Juanita"

Print #nFic, "[email protected]"

Print #nFic, ""

Print #nFic, "[Fecha]"

Print #nFic, "Fichero creado el día=26/May/1998"

Print #nFic, "Fichero actualizado el día=" & Format$(Now, "dd/mmm/yyyy")

Close

End Sub

Private Sub cmdLeerContenido_Click()

'Leemos el contenido de la sección 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

Page 253: curso básico de programación en visual basic

Do While Not EOF(nFic)

Line Input #nFic, sCadena

If Left$(sCadena, 1) = "[" Then

'nada más 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

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 posición actual, justo después de leer,

'de esta forma nos indicará la posición a partir de la

'cual leeremos el contenido...

Posicion = Seek(nFic)

'Si es una sección

If Left$(sCadena, 1) = "[" Then

'incrementamos el número de secciones

nSecciones = nSecciones + 1

'redimensionamos el array

ReDim Preserve aSecciones(nSecciones)

'asignamos los valores al array

Page 254: curso básico de programación en visual basic

With aSecciones(nSecciones)

.Nombre = sCadena

.Posicion = Posicion

End With

'añadimos esta sección 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 botón

cmdLeerContenido.Enabled = True

Else

MsgBox "No hay secciones en el fichero: " & sFic

End If

End Sub

Private Sub List1_DblClick()

'También podemos ver el contenido de una sección

'haciendo doble-click

cmdLeerContenido_Click

End Sub

Te explico un poco cómo funciona todo esto. Ya que de lo que se trata no es sólo de ver código 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 sección y la posición dentro del fichero.Creamos un array dinámico (es decir redimensionable) de este nuevo tipo de datos, en él guardaremos cada una de las secciones halladas en el fichero procesado. También dimensionamos una variable que contendrá el número de secciones halladas, aunque sólo se usa mientras se lee la información 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, están disponibles en todo el formulario.

Page 255: curso básico de programación en visual basic

En el Form_Load, que es el punto de entrada de nuestra utilidad, además de asignar el nombre del fichero y guardar un contenido de ejemplo, borramos el contenido del Text1 y el List1, además deshabilitamos el botón de leer el contenido de una sección, para que no se use hasta que no haya datos.

Al pulsar en el botón que lee las secciones, primero borramos lo que hubiese antes en el array de secciones y asignamos cero a la variable que contiene el número de secciones.Después de abrir el fichero, en modo secuencial, vamos leyendo línea por línea, en cuanto nos encontramos con una línea que empieza por corchete [, quiere decir que hemos encontrado una sección, por tanto, incrementamos la variable que lleva la cuenta de las secciones halladas, redimensionamos el array usando Preserve para no perder la información antes almacenada, y asignamos la información del nombre y la posición que hemos obtenido con Seek.Fíjate que la lectura de la posición se hace después de haber leído la sección del fichero, esto es así, porque lo que necesitamos saber es la posición que viene a continuación de la sección, ya que después de la sección es cuando vienen las claves. (En realidad, se asigna siempre después de leer una línea, pero a nosotros sólo nos interesa su valor cuando hemos encontrado una sección).Seguimos leyendo hasta encontrar el final del fichero.

¿Por qué se ha usado el acceso secuencial?Por la sencilla razón 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 extensión INI.Además de que al no saberse la longitud de los datos que contiene, pero que si sabemos que cada línea termina con un retorno de carro (o cambio de línea), es más cómodo usar el Line Input # para leer toda la línea; el modo binario no nos sería de utilidad, salvo que leyésemos el fichero caracter por caracter, cosa que ralentizaría el proceso.

Para acceder a una sección en concreto, cosa que ocurre al pulsar en el botón cmdLeerContenido, simplemente abrimos el fichero, posicionamos el "puntero" en el sitio adecuado y leemos lo que haya en el fichero hasta que encontremos otra sección, (que empezará por un corchete), o hasta que lleguemos al final del fichero.También he puesto código para que al hacer doble-click en un elemento del List1, se lea el contenido de la sección correspondiente, para ello lo único que se hace es llamar al evento Click del botón 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 programación 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 sección. De forma que el fichero sólo se lea una vez y cuando se quiera mostrar el contenido de una sección se use el contenido del array.La verdad es que no es nada fácil, pero tampoco pienses que es tan complicado como para no poder resolverlo, al menos deberías intentarlo y no coger el camino fácil de ver la solución, entre otras cosas, porque lo que se pretende con estos ejercicios es que cojas "soltura" en la programación y si además de soltarte te quedas con las "buenas" costumbres, pues mejor.

Page 256: curso básico de programación en visual basic

¿A que buenas costumbres me refiero?A usar Option Explicit en todos los módulos, para de esta forma declarar las variables antes de usarlas y a "indentar" el código para que te sea más fácil seguirlo...Después del sermón 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 sección 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 más complicado que almacenar simplemente la clave y el contenido, a la larga te ayudará a manipular mejor las cadenas de caracteres y también le darán mayor utilidad al código que se cree con este ejercicio.

Como segundo ejercicio, haz lo mismo, pero en lugar de almacenar en el array cada clave y su contenido, usa Seek para "recordar" la posición de cada una de las claves de cada sección para después poder acceder a esa parte del fichero para leer lo que nos interesa.

Este segundo ejercicio es un poco más complicadillo, ya que necesitará usar de forma correcta Seek, tanto en modo función como en modo instrucción.Este es el tipo de datos que tendrás que usar:

'

Private Type tSecciones

Nombre As String

NumClaves As Integer

Contenidos() As Long

End Type

Private aSecciones() As tSecciones

Page 257: curso básico de programación en visual basic

Fíjate que aquí sólo guardamos en Contenidos la posición de cada clave dentro del fichero.

Suerte y no desesperes si no lo consigues, no me gustaría perder a todos mis alumnos de golpe... creo que no lo soportaría.

Las soluciones de los dos ejercicios están en este link.

Y ya sólo 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 pregúntame, pero sólo relacionado con lo que estamos viendo, ¿vale?

Nos vemos.Guillermo

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:

'

'Ejemplos del curso básico, ejemplo de Seek (26/May/98)

'

'Solución 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

Private sFic As String

Page 258: curso básico de programación en visual basic

Private Sub cmdLeerContenido_Click()

'Leemos el contenido de la sección 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

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 sección

If Left$(sCadena, 1) = "[" Then

nClaves = 0

Page 259: curso básico de programación en visual basic

'incrementamos el número de secciones

nSecciones = nSecciones + 1

'redimensionamos el array

ReDim Preserve aSecciones(nSecciones)

'asignamos los valores al array

aSecciones(nSecciones).Nombre = sCadena

'añadimos esta sección 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 más que leer

'restablecemos la posición anterior

Seek nFic, Posicion

Exit Do

Else

Posicion = Seek(nFic)

'Posición del signo igual

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 después del signo

.Contenidos(nClaves).Contenido = Mid$(sCadena, i + 1)

End With

End If

End If

Loop

End If

Page 260: curso básico de programación en visual basic

Loop

Close nFic

If nSecciones Then

'seleccionamos el primer item del listbox

List1.ListIndex = 0

'habilitamos el botón

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 botón 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 protected]"

Print #nFic, ""

Print #nFic, "[Alumnos]"

Print #nFic, "Cantidad=2"

Print #nFic, "Nombre_01=Pepito"

Print #nFic, "[email protected]"

Print #nFic, "Nombre_02=Juanita"

Print #nFic, "[email protected]"

Print #nFic, ""

Page 261: curso básico de programación en visual basic

Print #nFic, "[Fecha]"

Print #nFic, "Fichero creado el día=26/May/1998"

Close

cmdLeerSecciones_Click

End Sub

Private Sub List1_DblClick()

cmdLeerContenido_Click

End Sub

El segundo:

'

'Ejemplos del curso básico, ejemplo de Seek (26/May/98)

'

'Solución a los ejercicios de la entrega 20

'

Option Explicit

Private Type tSecciones

Nombre As String

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 sección seleccionada en el list

Dim nFic As Integer

Dim sCadena As String

Dim nItem As Long

Page 262: curso básico de programación en visual basic

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

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 sección

If Left$(sCadena, 1) = "[" Then

Page 263: curso básico de programación en visual basic

nClaves = 0

'incrementamos el número de secciones

nSecciones = nSecciones + 1

'redimensionamos el array

ReDim Preserve aSecciones(nSecciones)

'asignamos los valores al array

aSecciones(nSecciones).Nombre = sCadena

'añadimos esta sección 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 posición

'antes de empezar a leerlas

Posicion = Seek(nFic)

Line Input #nFic, sCadena

If Left$(sCadena, 1) = "[" Then

'nada más que leer

'restablecemos la posición 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)

With aSecciones(nSecciones)

.NumClaves = nClaves

.Contenidos(nClaves) = Posicion

End With

End If

End If

Loop

End If

Loop

Close nFic

Page 264: curso básico de programación en visual basic

If nSecciones Then

'seleccionamos el primer item del listbox

List1.ListIndex = 0

'habilitamos el botón

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 botón 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 protected]"

Print #nFic, ""

Print #nFic, "[Alumnos]"

Print #nFic, "Cantidad=2"

Print #nFic, "Nombre_01=Pepito"

Print #nFic, "[email protected]"

Print #nFic, "Nombre_02=Juanita"

Print #nFic, "[email protected]"

Print #nFic, ""

Print #nFic, "[Fecha]"

Print #nFic, "Fichero creado el día=26/May/1998"

Page 265: curso básico de programación en visual basic

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 tendrán demasiada complicación para entender el listado.

Nos vemos.Guillermo

Con lo que hemos visto hasta ahora tendrás, o al menos deberías tener, una buena base sobre el lenguaje BASIC (o Visual Basic), pero saber un montón de instrucciones no es suficiente. Por suerte, no sólo hemos visto un "diccionario" del VB.Si has empezado casi de cero, seguramente no te habrá costado adaptarte a la programación en Windows, pero si por el contrario arrastras "conocimientos" de cualquier otro lenguaje que funcionaba bajo MS-DOS, puede que esa adaptación te cueste más, 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 teníamos 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 dura y a la larga poco efectiva, te lo digo por propia experiencia; otra cosa es adaptar ciertas rutinas, esto es más fácil, sobre todo si no interactúa con el usuario.

El decir todo esto es para que desistas en "ventanizar" una aplicación BASIC-DOS, seguramente, salvo que la tuvieras bastante bien estructurada con procedimientos y funciones, te costará más adaptarla que hacerla de nuevo.No voy a explicar cómo adaptar un listado MS-DOS al Windows, sería una pérdida de tiempo, sobre todo si a ti no te interesa, lo que si vamos a "volver" a ver es cómo controlar una aplicación 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 ocasión anterior ya vimos que los programas realizados en Visual Basic, y por extensión 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á más fácil "controlar" el funcionamiento del programa.

Page 266: curso básico de programación en visual basic

Así que, en esta y en las próximas entregas, vamos a darle un repaso a los eventos más usuales. También veremos con detalle, unos cuantos controles, los más habituales, de forma que su uso y aplicación sean "casi naturales" y al final acabes usándolos como si los conocieras de toda la vida.

Un poco de definición.

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 programación Windows. No estamos atados a una programación secuencial (o lineal), como se hacía en MS-DOS. Con ese tipo de programación, nosotros decidíamos lo que iba a ocurrir a continuación, y si no decidíamos, al menos podíamos prever lo que podía suceder.Pero la programación 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 ratón, presionar una tecla, etc.

Para muestra...

...un botón y un label y un textbox y un...

Empecemos por el Label que es más simple.

Las etiquetas (Label) se usan para mostrar información, normalmente este tipo de control no necesita intervención por parte del usuario.Normalmente se suele usar de estas dos formas:

Como etiqueta informativa que acompaña a otro control y que nos indica

la información que ese control nos da o nos pide, según sea el caso.

Por ejemplo, si queremos que el usuario introduzca un nombre,

usaremos un textbox para que escriba en él, pero también pondremos

una etiqueta indicándole que es lo que se espera que escriba.

Como panel o línea informativa, indicándole algún tipo de información al

usuario.

Es habitual que en la parte inferior de un form se añada una etiqueta

que "informe" de lo que se debe hacer o lo que el programa está

haciendo.

También se usan las etiquetas para informar de las opciones que ha

seleccionado o del proceso que la aplicación ha realizado o está a punto

de realizar.

Page 267: curso básico de programación en visual basic

Resumiendo, cada vez que tengas que informar al usuario, usa etiquetas para ello. Siempre que esa "información" no requiera de su intervención.

Las cajas de texto (TextBox) son los controles, por excelencia, para la introducción de información por parte del usuario.Cada vez que el usuario deba escribir lo que nuestra aplicación necesite, se usará un textbox.

Los botones (CommandButton) son los que indicarán, normalmente, al programa que el usuario ha finalizado de introducir la información que se necesita y que debe procesarla o si el usuario cambia de opinión y no quiere hacer lo que se pedía.Es, por tanto, habitual que se usen dos botones para conseguir esto, uno para "aceptar" y otro para "cancelar".También es habitual que se use para pasar a otra pantalla de información.

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 más habitual el listbox).Aunque el ComboBox se puede usar también para la introducción de información, no es lo habitual, en la mayoría de los casos es preferible usar el TextBox, aunque también 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 opción determinada, esta opción debe ser tan "autosuficiente" y sólo 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 sólo 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 más controles, pero al menos estos son los que se usarán más habitualmente, aunque para que la "relación" sea más completa, veremos también otros dos controles que se suelen usar para "contener" y agrupar a los demás controles:

Los PictureBox y Frames, éstos se usan para poner controles dentro, sobre todo el Frame.La primera recomendación para hacer esto, además de tenerlos "físicamente" separados del resto, también es más fácil moverlos a otro sitio cuando estamos diseñando la aplicación o el "interface" de cara al usuario.En otras ocasiones necesitaremos estos contenedores para agrupar los OptionButtons, cuando veamos en profundidad los controles, lo entenderás.

Como últimamente no dispongo de mucho tiempo y además me gusta "chincharos" un poco, dejo aquí esta entrega, así que, permanece pendiente y mientras tanto repásate el manual y la ayuda, de esta forma te será más fácil adaptarte.

Page 268: curso básico de programación en visual basic

En esta ocasión no te voy a pedir ningún 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 próxima entrega veremos ya algunos de los eventos más 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 consideración para conmigo y aguantes hasta que venga lo realmente interesante: los programillas de ejemplo y esas cosas.

Mientras tanto, pórtate bien y no hagas estropicios.

Nos vemos.Guillermo

Ya habrás notado a lo largo de todas estas entregas, que de planificación, "nasti de plasti", osease nada de nada... Y es que las cosas van surgiendo y mezclándose y al final... hasta casi se entiende lo que digo... y eso que es difícil, sobre todo cuando entre una entrega y la siguiente casi han pasado dos meses... pero, como digo, suerte para los que empiezan cuando ya hay un montón de entregas, al menos se las leen del tirón... porque en más de una ocasión me han escrito, sin ánimo de ser demasiados criticones, supongo, que si el que empezara el curso en abril del año pasado, tuviese que esperar a que lo terminase... lo tenía claro... y es cierto, pero confío que los que empezasteis de los primeros, ya hayáis aprendido a leer el manual y la ayuda del Visual Basic, y hasta os hayáis atrevido con algunos de los muchos libros que hay sobre este lenguaje... que si bien, al principio os podrían parecer difíciles de entender, al menos ahora podáis entenderlos, aunque sea un poco...

Como decía al principio, o al menos intentaba decir, el curso no lo tengo planificado y las cosas van surgiendo... la verdad es que ahora que leo los apuntes que tenía, fechados el 9 de julio, no se a que viene todo este rollo, así que mejor lo dejo y seguimos con el tema este de los eventos y los controles que podemos usar con el Visual Basic.

Ya sabes, (y si no lo sabias, te lo cuento yo ahora), que los controles suelen estar incluidos en un formulario, también pueden estar en controles diseñados por nosotros y en otros tipos de "contenedores", (de basura habrá pensado alguno, sobre todo cuando se pone manos a la obra y no sale lo que quiere... no te desesperes, ya tendrás tiempo de pillar buenos cabreos...), pero para simplificar, vamos a pensar que están puestos en un formulario.Esta aclaración viene a cuento para lo que te voy a contar ahora.

Insisto, esto lo tengo manuscrito desde hace dos meses y la verdad es que o recuerdo en que estaba pensando cuando lo escribí, porque nada de lo que viene a continuación tiene que ver con el hecho de que un control esté en un formulario u otro contenedor, pero al menos podrás "intuir" que los controles no tienen porqué estar siempre "puestos" en un formulario.

Cuando se produce un evento en un formulario, producido por el propio Form o por cualquier control contenido en él, se deja de procesar el código que se estaba ejecutando y se pasa a procesar el código contenido en el evento.Es importante que tengas muy presente esto que acabo de decir, ya que es la causa de efectos no deseados.Por supuesto, si el evento que se produce no tiene código asociado, no pasa nada.

Page 269: curso básico de programación en visual basic

Aclaremos esto un poco; si te has fijado en la ventana de código, cuando seleccionas un control de la ventana izquierda, incluso el propio formulario, en la ventana de la derecha te muestra los eventos disponibles, es decir los eventos que Visual Basic podrá interceptar, normalmente no son todos los que se generan, pero sí son los que el propio lenguaje nos permite interceptar y por tanto en cualquiera de ellos podemos añadir nuestro código para hacer algo cuando dicho evento se produzca.Pero esto no quiere decir que tengamos que escribir código en todos ellos, sino sólo en los que nos interesen, pero el hecho de no escribir código en un evento, no quiere decir que no se producirá. Por ejemplo, si no escribes código en el evento LOAD de un form, no impedirá que el form se cargue, se cargará, independientemente de que queramos interceptar ese hecho o no.

Sigamos con lo que pasa cuando se está ejecutando una parte del código y se produce un evento.Por ejemplo, si mientras se procesa el código de un evento, vuelve a producirse ese mismo evento, se vuelve a procesar el código desde el principio y una vez terminado este segundo evento, se continúa por dónde se interrumpió el anterior.Esta es una de las gracias de Windows y todo el tema de los eventos.No te preocupes si no te enteras que pronto veremos ejemplos.

Aunque en muchas ocasiones esto no ocurre mientras nosotros no lo permitamos, hay veces en las que no podemos "predecir" que es lo que ocurrirá, salvo que comprendamos perfectamente cómo funcionan los controles y sepamos cómo y cuando se producen algunos eventos; también puede intervenir la suerte de darnos cuenta de que... "si hago esto con este control, puede ocurrir este o aquel evento"Y esto que os digo es tan cierto como que nadie me ha regalado aún un portátil... je, je.

Vamos a ver un pequeño ejemplo para que salgas de dudas.Crea un nuevo proyecto, cambia la propiedad Autoredraw del form para que sea True, de esta forma, si lo redimensionas podrás ver lo que se ha imprimido en el.Escribe el siguiente código en el evento Click del form:

Private Sub Form_Click()

Dim i As Long

Dim j As Long

Print

For i = 1 To 10

Print i;

For j = 1 To 2000

DoEvents

Next

Next

Print

End Sub

Page 270: curso básico de programación en visual basic

Ejecuta el programa, cuando hagas click en el formulario, verás que se imprimen los números del 1 al 10, van un poco lento, para que puedas hacer lo que te diré ahora, mientras se estén imprimiendo los números, vuelve a hacer click en el form, hazlo más o menos rápido, si tus reflejos no te funcionan como quisieras, cambia el valor 2000 del bucle j con otro mayor, así se irán imprimiendo los números de forma más lenta.

Habrás notado que cuando aún no se ha terminado de imprimir los primeros diez números, (si has pulsado antes de que se terminen de imprimir, claro), se empiezan a imprimir en la siguiente línea desde 1 y cuando dejes de hacer click, irán terminando los bucles... supongamos que has pulsado tres veces, el resultado podría ser este:

1 2 3 4 5 6 7 81 2 3 4 5 61 2 3 4 5 6 7 8 9 107 8 9 109 10

El primer bucle se interrumpió en 8, se inició el segundo, que se interrumpió en 6, se inició el tercero y continuó hasta finalizar, después continuó el segundo, (el que se quedó en 6), y termina, una vez que terminó el segundo, se continua con el primero que se quedó en 8 y por eso se imprimen el 9 y 10.Como notarás, no se han perdido los valores... y el Visual recordó por dónde se quedó... esto es debido a que todas las variables de un procedimiento son locales a este procedimiento, sea un evento o no, (realmente los eventos son SUBs que son llamados por Windows), y se guardan antes de volver a entrar en el procedimiento y una vez que el procedimiento acaba, se desechan... la memoria que se usa para guardar temporalmente los valores de las variables de un procedimiento se llama STACK (o pila del programa), hay que tener en cuenta que esta "pila" no es infinita y puede llegar a llenarse... sobre todo cuando las cosas no se hacen bien. A este tipo de variables locales, también se les llama variables automáticas, por el hecho de que una vez que no se necesitan son automáticamente borradas... cosa que no ocurre cuando las variables se declaran a nivel de módulo o se declaran "estáticas", más adelante veremos más sobre esto.

Lo que este pequeño ejemplo demuestra es lo que te he explicado antes, que se puede "reentrar" en un evento (y por extensión a cualquier procedimiento); si esto mismo se hiciera por código, no porque el usuario interviene con su ratón, tendríamos lo que se llama un procedimiento "recursivo", es decir que se llama a sí mismo... tendremos ocasión de ver algún ejemplo.

El evento más dado a este tipo de "recursividad" es el evento Click. Este evento se produce cada vez que pulsamos con el ratón sobre un control, incluso sobre un formulario, como hemos visto en este ejemplo.Este evento (Click), está presente en la mayoría de los controles, prácticamente en todos.

La verdad es que poder "seguir" esto, imaginándose cómo lo hace el VB, es un poco difícil... vamos a ver otro caso, más usual y que seguramente será más fácil de entender...

Supongamos que tenemos un código que procesa una serie de cosas y ese proceso puede llegar a ser largo. Si ese código lo tenemos en el evento Click de un botón, con idea de que se procese cada vez que se pulsa en el botón, (cosa super-habitual), si no

Page 271: curso básico de programación en visual basic

hacemos nada especial, mientras se esté procesando el código, el form completo se quedará "congelado", una vez que haya terminado el proceso, se continuará "aceptando" nuevos eventos, normalmente se quedarán pendientes de procesar y se ejecutarán a continuación de terminar el proceso largo...Por eso en ocasiones es conveniente usar, como en el ejemplo anterior, la instrucción DoEvents. Con esta instrucción permitimos que Windows notifique "en seguida" de que ha ocurrido otro evento y nos da la posibilidad de procesarlo en ese momento.Eso o al menos indicar al usuario de que tenga paciencia y espere a que se termine de hacer lo que se estaba haciendo, ya que si no lo hacemos puede pensar que el programa se ha quedado "colgado".

Vamos a modificar el código anterior del Form_Click para ver esto que estoy diciendo.Sustitúyelo por este otro:

Private Sub Form_Click()

Dim i As Long

Dim j As Long

For i = 1 To 10

Print i;

For j = 1 To 2000000 'dos millones

Next

Next

Print

End Sub

Ejecuta el programa y haz click, verás que no pasa nada, haz click de nuevo y esta vez si que ocurre, hasta puede que el form se quede en blanco y al rato volverá a la normalidad con las dos ristras de números impresos... o más si no has tenido paciencia suficiente...Una forma de solventar esa espera... es hacer que se muestre un mensaje pidiéndonos que esperemos... pero eso no evitará que volvamos a hacer click en el form... incluso el mensaje no se mostrará hasta que se haya terminado de hacer lo que se estaba haciendo... para que el mensaje se vea enseguida, se pueden hacer dos cosas:Una: usar un DoEvents justo después de imprimir el mensajeDos: usar Refresh para que se "pinte" el control, en este caso el formulario.

...

Print "Un momento por favor..."

Refresh

For i = 1 To 10

...

Page 272: curso básico de programación en visual basic

En ambos casos tendríamos el mensaje mostrado en la pantalla, vale, pero si vuelves a hacer click... se volverá a procesar todo el código, etc... Aunque en esta ocasión, hasta que no finalice, no continúa el siguiente.

¿Cómo podemos evitar la re-entrada en un evento cuando aún no ha terminado?Usando lo que se llama un FLAG (o bandera), es decir una variable que nos indique que ya se está procesando el código y de esta forma poder evitar que se repita si aún no ha terminado.Como ya te he contado antes, las variables locales son automáticas y se desechan una vez finalizado el procedimiento, al ser automáticas no "recuerdan" el valor que tenían antes, salvo en las ocasiones que el VB las guarda en el Stack, pero no son recordadas en las diferentes ocasiones en las que entran en el procedimiento.¿Recuerdas que te comenté lo de las variables estáticas?Pues este tipo de variables son las que podemos usar para prevenir estos casos. Las variables estáticas se declaran de igual forma que las variables normales, salvo que en lugar de usar Dim, se usa Static. Una vez declarada una variable estática, deja de ser automática y VB no guarda una copia cada vez que se entra en el procedimiento, sino que usa la misma cada vez que se procese el código de ese evento. Por tanto cada vez que se "cuela" el código en un evento (o procedimiento), podemos saber si ya hemos estado antes, sin terminarlo o no... para ello habría que hacer esto:

Private Sub Form_Click()

Dim i As Long

Dim j As Long

Static bFlag As Boolean

'Si no estamos en el evento

If bFlag = False Then

'conectamos la bandera

bFlag = True

Print "Un momento por favor..."

Refresh

For i = 1 To 10

Print i;

For j = 1 To 1000000

Next

'Debemos permitir que se procesen los mensajes

DoEvents

Next

Print

'Desconectamos la bandera

Page 273: curso básico de programación en visual basic

bFlag = False

End If

End Sub

Aquí hay dos detalles a tener en cuenta, el primero es el uso de la variable estática, el otro es el DoEvents, sin éste, el uso de la variable estática no serviría para nada, ya que al no permitir a Windows que notifique los eventos, estos se quedan guardados en espera a que terminen los que había pendientes, y para que no se queden guardados, usamos el DoEvents.Prueba a pulsar varias veces en el formulario mientras se muestran los números, verás que no se vuelven a mostrar.

Te explico un poco cómo funciona esto:La primera vez que se entre en el evento, el valor de bFlag valdrá False, esto siempre es así con las variables Booleanas, cuando se quiere averiguar el valor de una variable que no se ha usado, ésta tiene un valor "vacio", cero en las numéricas, cadena vacía en las cadenas y False en el caso de las booleanas.Como vale False, se cumple la condición, por tanto se asigna el valor True a esa variable y se continúa procesando el código.Cuando se llega al DoEvents, Windows procesa los mensajes que tuviese pendiente y si tiene que enviarle uno a este formulario, lo hará.Suponiendo que hemos pulsado otra vez el ratón, al procesarse los mensajes pendientes, Windows envía al form un nuevo evento Click. Entonces se entra de nuevo en este procedimiento, pero esta vez, el valor de bFlag no es False, por tanto no se hace nada, ya que la condición no se cumple y se sale del evento.Cuando se ha salido del evento, se continúa por donde estaba antes y se sigue procesando el bucle, etc.Una vez terminado el bucle i, se asigna de nuevo el valor False a bFlag, para así poder permitir que se procese en otra ocasión el código que hay.Todo esto es posible gracias a que la variable bFlag es estática y el valor almacenado se mantiene entre las distintas llamadas, si no asignáramos el valor False al final, no podríamos entrar más en este evento, ya que nunca se cumpliría la condición, así que hay que tener cuidado con las variables estáticas, si el uso que le queremos dar es parecido al que hemos visto.

Hay ocasiones en las que un evento se "reproduce" muy a pesar nuestro, más que nada porque hay veces en las que no es tan obvio.Por ejemplo, cuando se selecciona un elemento de un ListBox, se produce un evento Click; y si en ese evento Click, tenemos algún código que seleccione elementos del control... podemos tener al Visual Basic bastante atareado... entrando en un evento y desde ese evento entrando a otro y así durante un rato... mientras tanto, nuestro pobre VB guardando las variables automáticas en el Stack (pila), pero llega un momento en el que la pila se llena... y en ese momento nos suelta un mensajillo de esos que nos indican que ya está hasta la coronilla de nosotros y de nuestra mala forma de programar... y se para...

Igual que yo me voy a parar aquí, porque ya está bastante bien de tanto Click y tanto Stack y todas esas cosas...

Al final, la película que te he contado no era exactamente lo que estaba en el guión, pero más o menos lo que te he explicado era lo que quería explicarte...

Page 274: curso básico de programación en visual basic

Para terminar, te diré que en algunos controles, al pulsar Intro se produce también un evento Click, por ejemplo en los CommandButtons. Por supuesto para que se procese ese Intro, el control debe tener el foco.

Bueno, lo dicho, dejemos aquí esta entrega que hay más cosas que hacer.A ver si la próxima no tarda tanto como esta y seguimos viendo... (espera que mire los apuntes, a ver que es lo que hay preparado), ... en las notas del 12 de julio están los eventos del ratón, en las del día 13, los del teclado... en fin, esto promete seguir pesadillo... pero que le vamos a hacer... todo sea para que te enteres un poco de cómo va todo esto de los eventos...

Lo dicho en otras ocasiones: pórtate bien y no hagas estropicios y si quieres mandarme un mensajillo sobre el curso, usa el link este que te pongo.

Nos vemos.Guillermo

Tengo muchas excusas que darte, para justificar este retraso, pero no voy a dártelas, ya que no hará que esta entrega se publicara antes...La verdad es que esto de tanta "teoría" me aburre un poco y no me motiva, pero se que es importante que sepas todo esto de los eventos y demás cosillas, así que voy a intentar seguir, ya que en cuestión 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 sólo de lo que publico en el curso básico, sino que si le echas una visual a los manuales y a la ayuda... algo tendrás 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 algún día lo podrás entender...

Bien, vamos a ver algunos de los eventos que se producen con el ratón.

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 código, en la parte izquierda se selecciona el control o formulario y en la derecha están los eventos que podemos "codificar".No todos los controles procesan los mismo eventos, pero en el caso de los eventos del ratón, casi la mayoría los interceptan... aunque no todos los que quisiéramos, pero al menos algunos...

En estas imágenes 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.

Page 275: curso básico de programación en visual basic

En la lista desplegable de la izquierda están los controles que están contenidos en el formulario, así como la sección "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 ratón por el control, (te recuerdo que los formularios también tienen estos eventos), si se ha presionado un botón y hasta que botón se ha presionado...Vamos a verlos con un poco más de detalle:

Evento MouseDown.

Este evento se produce cuando pulsamos un botón del ratón. Con los parámetros que tiene este evento podemos saber que botón se ha pulsado, la posición dentro del control y si se está pulsando la tecla Shift (Mayúsculas), Control o Alt.

Vamos a ver los parámetros y algunos de los valores disponibles.

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

Button Indica el botón que se está pulsando, puede tener los siguientes valores:1, se está pulsando el botón izquierdo,2, se está pulsando el derecho,4, se está pulsando el botón central.Sólo se controla un botón a la vez.

Shift 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 (mayúsculas)

Page 276: curso básico de programación en visual basic

2, se está pulsando Control4, se está pulsando AltSe permiten combinaciones de estos valores, es decir que si se pulsan varias de esas teclas, los valores serán:3, se están pulsando Shift y Ctrl,5, se están pulsando Shift y Alt,6, se están pulsando Ctrl y Alt,7, se están pulsando las tres teclas.

X, Y Indica las coordenadas de la posición del puntero del ratón dentro del control.Es importante saber que estos valores son sólo dentro del control en cuestión, no referente al formulario o la pantalla.

El evento MouseDown se suele usar, entre otras cosas, para mostrar menús emergentes (PopUpMenus), normalmente controlando que se haya pulsado el botón derecho, (cuando Button vale 2)

Evento MuseUp.

Este evento se produce cuando se suelta el botón, los parámetros son los mismos que para MouseDown, pero en este caso lo que se detecta es cuando se "suelta" el botón pulsado.

Evento Click.

Este evento se produce cuando se hace "click" en un control... es una combinación del MouseDown seguido de un evento MouseUp... debes tener en cuenta que si estás gestionando los eventos MouseDown, MouseUp y Click te encontrarás con que se producen los tres eventos en ese orden, es decir, primero se presiona, después se suelta y por último se produce el Click.

Evento DblClick.

Este como podrás imaginarte, se produce cuando se hace una doble pulsación, doble click.

Si estás controlando estos cuatro eventos, además de hacer tus propias comprobaciones, deberías saber que normalmente se producen en este orden:Primero el MouseDown, seguido de un MouseUp, a continuación un Click seguido de un DblClick y por último un MouseUp.Todo esto lo puedes comprobar con el siguiente código:

'

'Pruebas para la entrega 23 del curso básico (04/Oct/98)

'

Page 277: curso básico de programación en visual basic

Option Explicit

Private Sub Form_Click()

Debug.Print "Se ha pulsado sobre el formulario (Click)"

End Sub

Private Sub Form_DblClick()

Debug.Print "Doble pulsación 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 botón del ratón (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 botón del ratón (MouseUp)"

End Sub

Evento MouseMove.

Este evento se produce cada vez que movemos el ratón por un control o por el formulario.Los parámetros de este procedimiento son los mismos que en los otros referentes al ratón: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 cuestión, ya que si estamos en otro control... quiere decir que no estamos en este... ¡Elemental querido Watson!

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

Page 278: curso básico de programación en visual basic

Y esto es todo por hoy... poco, pero mejor poco que nada... la próxima entrega será sobre los eventos del teclado... que podría haber puesto en esta, pero...Y después veremos algunos otros más... lo que no se es si será en la próxima o en la siguiente... ya veremos.

Y para seguir con la norma... si quieres dejar un mensaje referente al curso básico, puedes hacerlo, para ello usa el siguiente link para que te resulte más fácil y así controle que lo que me quieres decir es referente al curso y no para hacer una consulta, ya que algunos aprovechan la ocasión para preguntar...Este es el link para que me mandes tu comentario.

Nos vemos.Guillermo

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

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

Evento KeyPress.

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

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

Private Sub Text1_KeyPress(KeyAscii As Integer)

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

If KeyAscii = Asc("A") Then

'se ha pulsado la A mayúscula

End If

Page 279: curso básico de programación en visual basic

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

If KeyAscii = vbKeyReturn Then

KeyAscii = 0 'Eliminamos el pitido

End If

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

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

If KeyAscii = vbKeyReturn Then

KeyAscii = 0 'Eliminamos el pitido

'si queremos pasar al siguiente control

'tal como lo haríamos pulsando la tecla TAB:

SendKeys "{TAB}"

End If

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

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

Eventos KeyDown y KeyUp.

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

Page 280: curso básico de programación en visual basic

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

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

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

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

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

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

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

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

Te voy a explicar es cómo suelo "manejar" los controles cuando los inserto en un formulario, que tamaños suelo usar, que posición, etc. Normalmente suelo usar unos tamaños "predefinidos" para los controles:

Page 281: curso básico de programación en visual basic

Los Labels suelo darles una altura de 255 ó 285 si tienen borde, a los TextBoxes 315 y a los CommandButtons 375 ó 405.El ancho dependerá del contenido que vayan a tener, en los botones suelo dejarles el ancho por defecto: 1245

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

Array de controles.

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

Controles diferentes Un Array de Controles

Nombre Cada una tendrá un nombre diferente: Label1, Label2, etc.

Todas tienen el mismo nombre, pero se debe usar un índice para indicar a cual nos referimos.Label1(0), Label1(1), etc.

Eventos Cada control tendrá su propio juego de eventos.

Todos los controles comparten el mismo evento, pero ese evento incluirá un nuevo parámetro que será el índice dentro del array del

Page 282: curso básico de programación en visual basic

control que lo ha producido.

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

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

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

Private Sub Label1_Click(Index As Integer)

'Para saber en que etiqueta se ha hecho click:

Select Case Index

Case 0

'Se ha hecho Click en el label de índice cero

Case 1

'Se ha hecho Click en el label de índice uno

End Select

End Sub

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

Private Sub Text2_GotFocus()

'Seleccionar el texto que haya en el Text2

'Si usamos With, es más cómodo codificar, ya que no hay

'que estar escribiendo el nombre del control.

Page 283: curso básico de programación en visual basic

With Text2

'Posición incial de la selección: cero, el primer caracter

.SelStart = 0

'Seleccionar todo el texto:

'esto se averigua con la longitud del contenido de la

'propiedad Text

.SelLength = Len(.Text)

End With

End Sub

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

Private Sub Text1_GotFocus(Index As Integer)

'Seleccionar el texto que haya en el Text1(Index)

'Si usamos With, es más cómodo codificar, ya que no hay

'que estar escribiendo el nombre del control.

With Text1(Index)

'Posición incial de la selección: cero, el primer caracter

.SelStart = 0

'Seleccionar todo el texto:

'esto se averigua con la longitud del contenido de la

'propiedad Text

.SelLength = Len(.Text)

End With

End Sub

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

Page 284: curso básico de programación en visual basic

Private Sub SeleccionarTexto(unTextBox As TextBox)

'Seleccionar el texto que haya en el TextBox

'Si usamos With, es más cómodo codificar, ya que no hay

'que estar escribiendo el nombre del control.

With unTextBox

'Posición incial de la selección: cero, el primer caracter

.SelStart = 0

'Seleccionar todo el texto:

'esto se averigua con la longitud del contenido de la

'propiedad Text

.SelLength = Len(.Text)

End With

End Sub

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

Private Sub SeleccionarTexto(unTextBox As Contol)

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

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

'Para el Text2:

Private Sub Text2_GotFocus()

'Llamamos al procedimiento, usando como parámetro

'el control Text2

SeleccionarTexto Text2

End Sub

Page 285: curso básico de programación en visual basic

'Para el array Text1:

Private Sub Text1_GotFocus(Index As Integer)

'Llamamos al procedimiento usando el Text1 correspondiente,

'en ese caso hay que indicar el índice.

SeleccionarTexto Text1(Index)

End Sub

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

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

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

Private Sub Form_KeyPress(KeyAscii As Integer)

If KeyAscii = vbKeyEscape Then 'Se ha pulsado ESC

'Descargamos el formulario

Unload Me

ElseIf KeyAscii = vbKeyReturn Then 'Se ha pulsado Intro

'Borramos la tecla pulsada para que no "pite"

KeyAscii = 0

'Enviamos una pulsación TAB

SendKeys "{TAB}"

End If

End Sub

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

El orden de tabulación, es decir que control recibirá el foco cuando se pulse la tecla TAB, estará indicado por el valor de la propiedad TabIndex de cada uno de los controles. Ese valor se cambia de forma automática cuando se modifica el valor de otro control. Al principio tiene el valor según los controles añadidos,

Page 286: curso básico de programación en visual basic

pero si quieres cambiarlo, puedes modificar ese valor, sabiendo que el primer control que recibirá el foco será el que tenga el valor cero y después los que tengan el 1, 2, etc.

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

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

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

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

Pulsa F5 para ejecutar el programa, el foco lo recibirá primero el Text1, el cual se seleccionará. Si pulsas la tecla TAB verás que el foco va cambiando a Text2, Cancelar y Aceptar, para volver a Text, etc.

Page 287: curso básico de programación en visual basic

Prueba pulsando Intro cuando estés en alguno de los TextBoxes y verás que se cambia el foco al siguiente control.

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

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

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

Nos vemos.Guillermo

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

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

Evento KeyPress.

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

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

Private Sub Text1_KeyPress(KeyAscii As Integer)

El parámetro KeyAscii nos indicará el código de la tecla pulsada, este código nos lo da como valor numérico, no como una cadena, es decir que si pulsamos la tecla A mayúscula, valdrá 65, ya que ese es el valor ASCII de ese caracter, (realmente es un valor ANSI, pero...).Para saber el valor de las teclas, puedes pulsar F2 en el IDE del Visual Basic y buscar las constantes de KeyCode o en la ayuda busca la palabra KeyCode, de

Page 288: curso básico de programación en visual basic

todas formas los valores Ascii se pueden saber o usar con la función ASC, por tanto se puede hacer una comparación de este tipo:

If KeyAscii = Asc("A") Then

'se ha pulsado la A mayúscula

End If

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

If KeyAscii = vbKeyReturn Then

KeyAscii = 0 'Eliminamos el pitido

End If

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

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

If KeyAscii = vbKeyReturn Then

KeyAscii = 0 'Eliminamos el pitido

'si queremos pasar al siguiente control

'tal como lo haríamos pulsando la tecla TAB:

SendKeys "{TAB}"

End If

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

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

Page 289: curso básico de programación en visual basic

Eventos KeyDown y KeyUp.

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

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

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

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

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

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

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

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

Page 290: curso básico de programación en visual basic

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

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

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

Array de controles.

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

Controles diferentes Un Array de Controles

Nombre Cada una tendrá un nombre diferente: Label1, Label2,

Todas tienen el mismo nombre, pero se debe usar

Page 291: curso básico de programación en visual basic

etc. un índice para indicar a cual nos referimos.Label1(0), Label1(1), etc.

Eventos Cada control tendrá su propio juego de eventos.

Todos los controles comparten el mismo evento, pero ese evento incluirá un nuevo parámetro que será el índice dentro del array del control que lo ha producido.

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

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

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

Private Sub Label1_Click(Index As Integer)

'Para saber en que etiqueta se ha hecho click:

Select Case Index

Case 0

'Se ha hecho Click en el label de índice cero

Case 1

'Se ha hecho Click en el label de índice uno

End Select

End Sub

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

Page 292: curso básico de programación en visual basic

Private Sub Text2_GotFocus()

'Seleccionar el texto que haya en el Text2

'Si usamos With, es más cómodo codificar, ya que no hay

'que estar escribiendo el nombre del control.

With Text2

'Posición incial de la selección: cero, el primer caracter

.SelStart = 0

'Seleccionar todo el texto:

'esto se averigua con la longitud del contenido de la

'propiedad Text

.SelLength = Len(.Text)

End With

End Sub

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

Private Sub Text1_GotFocus(Index As Integer)

'Seleccionar el texto que haya en el Text1(Index)

'Si usamos With, es más cómodo codificar, ya que no hay

'que estar escribiendo el nombre del control.

With Text1(Index)

'Posición incial de la selección: cero, el primer caracter

.SelStart = 0

'Seleccionar todo el texto:

'esto se averigua con la longitud del contenido de la

'propiedad Text

.SelLength = Len(.Text)

End With

End Sub

Fíjate que el código usado es el mismo que para Text2. Si tuviésemos 20 cajas de texto, tendríamos que repetir este código veinte veces, una vez para cada

Page 293: curso básico de programación en visual basic

control, pero al tener un array sólo habría que ponerlo una vez.Otra forma de no tener que repetir el código un montón de veces, sería creando un procedimiento que hiciera ese trabajo. Veamos el código para ese procedimiento:Antes de ver el código, hay que saber que el procedimiento necesita saber el control en el que se debe seleccionar, por tanto necesitará recibir como parámetro un textbox... veamos el código y espero que captes la forma de hacerlo:

Private Sub SeleccionarTexto(unTextBox As TextBox)

'Seleccionar el texto que haya en el TextBox

'Si usamos With, es más cómodo codificar, ya que no hay

'que estar escribiendo el nombre del control.

With unTextBox

'Posición incial de la selección: cero, el primer caracter

.SelStart = 0

'Seleccionar todo el texto:

'esto se averigua con la longitud del contenido de la

'propiedad Text

.SelLength = Len(.Text)

End With

End Sub

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

Private Sub SeleccionarTexto(unTextBox As Contol)

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

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

'Para el Text2:

Private Sub Text2_GotFocus()

Page 294: curso básico de programación en visual basic

'Llamamos al procedimiento, usando como parámetro

'el control Text2

SeleccionarTexto Text2

End Sub

'Para el array Text1:

Private Sub Text1_GotFocus(Index As Integer)

'Llamamos al procedimiento usando el Text1 correspondiente,

'en ese caso hay que indicar el índice.

SeleccionarTexto Text1(Index)

End Sub

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

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

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

Private Sub Form_KeyPress(KeyAscii As Integer)

If KeyAscii = vbKeyEscape Then 'Se ha pulsado ESC

'Descargamos el formulario

Unload Me

ElseIf KeyAscii = vbKeyReturn Then 'Se ha pulsado Intro

'Borramos la tecla pulsada para que no "pite"

KeyAscii = 0

'Enviamos una pulsación TAB

SendKeys "{TAB}"

End If

End Sub

Page 295: curso básico de programación en visual basic

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

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

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

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

--Selecciona el botón Aceptar, pulsa F4 para mostrar la ventana de propiedades, busca TabIndex y escribe 0, pulsa Intro.--Selecciona el botón cancelar, en la ventana de propiedades seguirá estando seleccionada la misma propiedad, escribe de nuevo cero.

Page 296: curso básico de programación en visual basic

--Haz lo mismo con el resto de los controles: LblInfo, Text2, Edad, Text1, Nombre

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

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

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

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

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

Nos vemos.Guillermo

La entrega de hoy, para quitarnos un poco la resaca de la "celebración" de la anterior, se basará en consejos y observaciones sobre las declaraciones de variables y otras cosillas que deberás 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 intención, 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 algún tiempo con el VB solemos hacerlo, entre otras cosas porque nos da algo de "rabia" que los programadores de otros lenguajes, (léase Pascal/Delphi y C/C++), se rian de que el Visual Basic es muy propenso a errores tipográficos... 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 habrán visto a fondo o no lo han tocado desde la versión 2... pero para eso estamos aquí los que si lo hemos "estudiado" y lo seguimos haciendo, para que los menos "expertos" aprendan lo más rápido posible y sobre todo lo mejor posible...

Como te comentaba muchos de los errores tipográficos se solucionan usando una instrucción, que ya te he comentado en otras ocasiones a lo largo de este curso, interminable, de Visual Basic y no es otra que:

Page 297: curso básico de programación en visual basic

Option Explicit.

Cuando insertamos esta instrucción al principio de un módulo, ya sea del código de un formulario, módulos 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 comprobarás en cuanto leas un poco más de esta entrega.De todas formas, no tienes porqué estar incluyendo esta instrucción en cada módulo, el Visual Basic puede hacerlo por tí de forma automática cada vez que añadas un nuevo módulo o formulario en tu aplicación. Este automatismo no es un valor por defecto en las versiones anteriores al VB6, (en esta versión ya lo es y se agradece, al menos para que los que empecéis lo tengáis por defecto), pero puedes hacer que lo sea, para ello tendrás que marcar la opción 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 versión 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á más fácil solucionar problemas, te puedo asegurar que "muchos problemas".

Vamos a comprobarlo, crea un nuevo proyecto, te añadirá un nuevo formulario (Form1), muestra la ventana de código y si tiene insertado Option Explicit, bórralo. Muestra el form y añade un CommandButton, añade el siguiente código al evento Click de este botón:

Private Sub Command1_Click()

'

Dim SumaActual As Long

Dim i As Long

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 verás que no hace lo que se espera...Ya que si analizas el código, el valor mostrado debería 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 añade Option Explicit al principio

Page 298: curso básico de programación en visual basic

del módulo, es decir en la parte de las declaraciones generales del formulario, vuelve a ejecutar de nuevo el programa, nada ocurre... pulsa en el botón Command1 y verás que si que había un error tipográfico.

Seguramente dirás 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 módulos de otro tipo... entonces ya me contarás si no es mejor contar con "detector de errores tipográficos".

Seguramente te habrás fijado que al ejecutar el programa, (usando F5 o pulsando en el botón de la barra de herramientas), el Visual Basic no te ha avisado del fallo, sólo lo ha hecho cuando has hecho click en el botón, 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 código que hay en ellos no nos avisará del "fallo".

Pero, por suerte, esto se puede evitar... y además de dos formas distintas.La primera es ejecutando la aplicación con Ctrl+F5, es decir pulsando la tecla control y al mismo tiempo F5, de esta forma se analiza todo el código antes de iniciar el programa y si el Visual Basic encuentra algún error, te avisará.La otra forma es indicarle al VB que "siempre" analice el código antes de ejecutarlo, de esta forma es como si siempre pulsáramos Ctrl+F5 aunque sólo se pulse F5. Para que esto sea así, muestra el cuadro de diálogo de Herramientas y en la solapa General busca la casilla "Compile on demand" (o como sea en la versión en español), si está marcada esta opción, quítale la marca, esto en proyectos grandes hace que tarde un poquito más al ejecutarse en el IDE, pero es porque se analiza todo el código antes de empezar la ejecución del programa, yo ya me he acostumbrado a que siempre se compile y lo tengo de esta form, ya que no hay nada que más me moleste que se detenga el programa al entrar en un procedimiento por culpa de un error tipográfico, la verdad es que si esta opción hubiese estado en el Basic del MS-DOS nos hubiera evitado algún que otro quebradero de cabeza, por desgracia llegó tarde, con el Visual Basic para DOS, pero más vale tarde que nunca... aunque ahora casi ni se utiliza...

Para terminar con esto de las ventajas de usar Option Explicit, vamos a ver cómo saber si una variable está en uso o no.Bueno, no es que nos de esa información, pero algo parecido, la verdad es que echo de menos una característica del QuickBasic del MS-DOS y era que al pulsar F1 en una variable, nos indicaba en que módulos se estaba usando dicha variable, pero... aunque no existe esa forma de ver cómo está declarada una variable en el Visual Basic, podemos usar otra forma, que es pulsando Shift+F2; te mostrará la declaración de esa variable... si no te muestra la declaración, es que no está declarada.

A lo que iba, si tenemos declarada una variable, se respeta el "estado" de mayúsculas/minúsculas de ese nombre de variable, por tanto si en el caso del ejemplo anterior, hubiésemos escrito (todo en minúsculas) sumaactual, el VB automáticamente hubiese convertido dicho nombre a SumaActual o como lo hubiésemos 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 declaración:Dim j As Integer y en otra parte de nuestro proyecto esta otra:

Page 299: curso básico de programación en visual basic

Dim J As Integer, el VB convertirá todas las variables "j" al estado de la última declaración que hayamos usado...Pero al menos sabremos que la tenemos declarada y de esto vamos a ver más 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 aplicación trabaje de forma más eficiente... aunque no te voy a hacer ninguna demostración, ya que la demostración la tuviste en la entrega cuatro.La recomendación 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.También puedes especificar el tipo por defecto, esto yo lo hacía 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 sabrás, te recuerdo cómo hacerlo.

Añade justo después del Option Explicit la instrucción 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]]dóde tipo es Int para Integer, Lng para Long, Sng para Single, etc. (busca esta palabra en la ayuda para más información)Los rangos son letras que le indicarán al VB que las variables que empiecen por esas letras sean del tipo indicado, por ejemplo:

DefInt a-c, i, jDefLng 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.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 CurrencyYo así lo tengo más claro, pero puedes hacerlo como prefieras.

Page 300: curso básico de programación en visual basic

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 añadir el DEFxxx correspondiente, tendrás que las variables será del tipo Variant, que entre otras cosas es más lenta y ocupa más memoria. Además de que en un futuro te será más fácil saber el tipo de datos que esas variables contendrán... cosa que agradecerás cuando veas de nuevo el código unos meses o años después...Y para que te sea más fácil recordar que debe almacenar cada variable:

Usa nombres descriptivos para las variables

Pues eso, es más fácil saber que la variable "Nombre" guarda un nombre que si sólo 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 código 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 habrás "oido" hablar de la notación "hungara"... no voy a entrar en detalles de que va esto, pero si decirte más 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 declararían así:Dim intContador As Integer, lngContador As Long, strNombre As String, etc...De esta forma, es fácil saber que strNombre es del tipo String, aunque no tengamos "cerca" la declaración 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 invención mia)Dim iContador As Integer, sNombre As StringCon el tipo Long no siempre uso la "l" (ele minúscula), entre otras cosas porque se confunde con el número "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 públicas no suelo añadirle ningún tipo de identificador, ya que en el caso de los formularios se convierten en propiedades, no entro en más detalles en esto de las propiedades, ya que será el tema de la próxima entrega.

Pero esto de los prefijos no sólo se aplican a las variables, también se hace a los controles y formularios.Por ejemplo:

Prefijo Control Ejemplo

Page 301: curso básico de programación en visual basic

cmd CommandButon cmdSalir

txt TextBoxes txtNombre

lbl Labels lblStatus

chk CheckBoxes chkImpresora

cbo CombosBoxes cboCiudad

lst ListBoxes lstNombres

pic PictureBox picStatus

img Image 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 módulo, las que son visibles en todo el módulo actual, ahora veremos más 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 mayoría de los casos, es éste el que suelo aplicar... y cada vez con más "consistencia", ya que a la larga te ayuda el usar algún tipo de recordatorio del tipo/nivel de visibilidad de los controles y variables.

Para ir acabando con las recomendaciones del día, voy a darte la penúltima:

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 código está reciente, nos acordamos de todo... pero cuando se lee después 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 código y después comentarlo para que la próxima vez que tuviese que modificarlo, me resultara más fácil.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 código cae en manos de otro programador, por ejemplo en un grupo de trabajo y tiene que entender lo que "pretendías" hacer...

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

Y para acabar esta entrega "sermonística" y casi de repaso, vamos a ver el tema de la visibilidad de las variables o el "alcance" que estas tienen dentro del módulo e incluso del proyecto completo.

¿De que va esto de la visibilidad?

Esto va de que cuando se declaran las variables, estas no están visibles en toda la aplicación...

Page 302: curso básico de programación en visual basic

¡AH! que lo que no entiendes es lo de "visible"...Te lo explico con un ejemplo:Supón que tienes un procedimiento que cuenta el número 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 función devolverá el número de veces que está

'la letra sLetra en la cadena sCadena

Dim i As Long 'Variable para el bucle

Dim nVeces As Long 'variable temporal para el número 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 sólo un caracter)

If Mid$(sCadena, i, 1) = sLetra Then

'incrementar el número de veces

nVeces = nVeces + 1

End If

Next

'Devolver el total de veces

CuantasVeces = nVeces

End Function

En este código hay dos variables declaradas: "i" y "nVeces", pues bien, estas variables sólo son visibles dentro de esta función, es decir, que no tendrán nada que ver con otras variables que tengan el mismo nombre, y estén 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 más de un listado en el que se declara una variable en un procedimiento y después 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)

Page 303: curso básico de programación en visual basic

a que lo evitemos... ya que no nos dejará usar el programa si no están las variables "debidamente" declaradas... o al menos declaradas aunque no sea de la forma adecuada... (ver más arriba lo que comento sobre usar el tipo adecuado).

Vamos a verlo con un ejemplillo... para que te des cuenta de lo que podías haberte evitado si usaras el Option Explicit... y si aún no te ha dado tiempo a comprobarlo, porque eres de los que siguen "las reglas", ahora te darás cuenta de que podía haber sido peor tu aprendizaje...

Para este ejemplo, tendrás que crear un nuevo proyecto, se añade por defecto un formulario (Form1), añade un módulo BAS (Module1) y escribe lo siguiente en el módulo BAS:

'En el módulo 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 botón (Command1):

'En el Form1

Option Explicit

'Esta variable es "privada" y visible sólo en este formulario

Private nVeces As Long

Private Sub Command1_Click()

'Declaramos una variable "privada",

'y por tanto sólo es "visible" dentro de este procedimiento

Dim sNombre As String

Page 304: curso básico de programación en visual basic

'Asignamos a la variable el contenido del Text1

sNombre = Text1

'incrementamos las veces que se ha pulsado en el botón

nVeces = nVeces + 1

'mostramos la información

MostrarNombre

End Sub

Ejecuta el programa y verás que cuando pulses en el Command1, no te muestra el nombre y las veces siempre serán cero... aunque pulses varias veces.

¿Por qué ocurre esto si las variables sNombre y nVeces están declaradas como públicas en el módulo BAS?Esto es así, porque a pesar de haber declarado esas dos variables como públicas en el módulo 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 más fácil de comprender, a saber, cuando el Visual Basic entra en el procedimiento Command1_Click, declara una variable de tipo cadena llamada sNombre, esa variable sólo es visible dentro de este procedimiento, es decir que cuando se use, el VB usará la memoria que ha reservado al encontrarse esa declaración, 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 más amplia que la de sNombre, ya que al estar declarada a nivel de módulo, es visible en todo el formulario, pero al ser privada sólo será visible, (o accesible, o disponible, como prefieras), dentro del código que esté en el propio formulario y no fuera de él.Por tanto, al llamar al procedimiento MostrarNombre, (que está declarado como público en el módulo 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 inglés se llama "scope", ámbito o alcance; si lo entiendes mejor es la "cobertura" que tiene esa variable, como la de los teléfonos móviles (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 declaración de una variable, para ver el "alcance" que tienen:

Page 305: curso básico de programación en visual basic

-Declaradas en un procedimiento (Sub, Function o Property) Sólo son visibles en ese procedimiento

-Declaradas Privadas (o con Dim) en un formulario o módulo (BAS, CLS u otro tipo) Sólo son visibles en ese formulario o módulo, incluidos todos los procedimientos de ese módulo, salvo la restricción anterior.

-Declaradas Públicas (o Globales) en un módulo BAS Son visibles en todo el proyecto, salvo cuando se encuentren con variables declaradas en los casos anteriores.

-Declaradas Públicas en formularios o módulos de clase Serán visibles en cualquier sitio, siempre que se preceda con el nombre del formulario o clase... (ya veremos esto en otra ocasión)O sólo dentro de ese módulo, como si fuesen privadas al propio módulo.

Por tanto, las variables que tengan el mismo nombre, se usarán según la última declaración que tenga esa variable y si está al alcance del procedimiento en el que se está usando... que lio, ¿verdad?

Esta cercanía en la declaración de la variable se puede explicar también así:

Primero se usarán las que estén declaradas en el propio procedimiento

Después las que estén en el mismo módulo (o formulario o clase o...

cuando digo módulo, me refiero al "sitio" en el que escribes el código)

Por último, las declaradas como globales o públicas en un módulo BAS

Hay que puntualizar que las variables públicas declaradas en un formulario, módulo de clase o control de usuario, se convierten en propiedades de esos "objetos", y como sabrás para usar una propiedad de un "objeto", debes especificar el nombre del objeto seguido de un punto y después el nombre de la propiedad; aunque cuando se usan en el mismo módulo o formulario, no es necesario usar el nombre del "objeto que las contiene", por tanto, si no se especifica el nombre del módulo, sólo son visibles dentro del mismo módulo en el que se han declarado.

Para que el ejemplo anterior funcionara como debe, tendrás que quitar las declaraciones que hay de las variables sNombre y nVeces en el formulario y sólo dejar las del módulo BAS.Pruébalo y verás que ahora si que se muestra lo que se debe mostrar.

Usar una variable indicando el módulo en el que se han declarado

Ya que he tocado el tema de especificar el nombre del "objeto" delante de las variables, también se pueden usar de esa forma y así evitaríamos el tema este de la visibilidad y alcance de las variables, aunque es algo más "liante"...Esta forma de usarlo fuera del módulo en el que se ha declarado, sólo es válido si las variables son "públicas".Para verlo, un ejemplo:Crea un nuevo proyecto, se creará un formulario (Form1), añade un módulo

Page 306: curso básico de programación en visual basic

BAS (se creará Module1, no le cambies el nombre al módulo)Escribe esto en el módulo BAS y el formulario, según se especifica:

'En el módulo BAS

Option Explicit

Private pVariable As Long

'Esta variable está declarada en el Form1 y en este módulo

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á dónde está declarada

'Esta variable es la declarada en este formulario

unaVariable = 10

'Estas variables están declaradas como públicas en el módulo BAS

otraVariable = 20

gVariable = 30

'Esta otra está declarada como pública en el módulo BAS,

'pero como tiene el mismo nombre que la declarada en el formulario,

'para poder usarla, hay que especificar el nombre del módulo

Page 307: curso básico de programación en visual basic

Module1.unaVariable = 40

'Esta dará error ya que es privada en el módulo BAS

'(Variable no definida)

'pVariable = 50

'si se usa de esta forma:

'Module1.pVariable = 50

'se encontrará la declaración al pulsar Shift+F2,

'pero dará error de que no se encuentra el método

'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

'sólo visible dentro del módulo BAS

'Print "Module1.pVariable ="; Module1.pVariable

End Sub

Creo que los comentarios son aclaradores, así que ejecútalo y verás lo que muestra, prueba a quitar Module1 cuando se use y asigne a "unaVariable" y verás que el primer valor se pierde (como debe ser...).

Bueno, aunque he tardado en publicarla... al fin hemos continuado con las entregas del cursillo básico...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 más vale tarde que nunca... ¿verdad?Confío en que haya servido de algo tanta espera... así que si quieres hacer algún comentario... hazlo pulsando en este link...

Nos vemos.Guillermo

Ha pasado un largo tiempo, ¿verdad? aunque en otras ocasiones las esperas han sido aún más largas, así que no te quejes demasiado... De todas formas, aunque ya lo habrás notado, las últimas entregas que estoy publicando ya las tenía escrita desde hacía un montón 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...

Page 308: curso básico de programación en visual basic

pero más vale tarde que nunca.Y ahora vamos a ver de que va todo esto y a ver si vale la pena, esperar tanto...

Propiedades, métodos... y lo que se me ocurra

Una vez vistos los eventos, al menos los más usuales, vamos a tratar ahora con las propiedades y métodos de los formularios y controles.

¿Que es una propiedad?

Una propiedad, simplificando para que se entienda, es una información 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 extensión a todos los objetos, qué mostrar, cómo mostrarse o hacer cualquier otra cosa... sino hacer, al menos tener información para poder hacer algo...

Veamos más ejemplos, así conocerás algunas propiedades "comunes" a la mayoría de controles:

Width Indica el ancho del control. Cambiando el valor de esta propiedad, cambiamos el ancho del control... como es de suponer...

Height El alto del control, si queremos cambiarlo...

Top La posición superior del control. Normalmente hace referencia al control que lo contiene. Un form normal, hará referencia a la posición en la pantalla.

Left La posición izquierda del control. Mismo comentario que para Top

Visible Si se muestra o no

Enabled Si está disponible o no.

Name El nombre del control u objeto.

Tag 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.

Caption Esta no está presente en todos los controles, pero es típico para los botones command y las etiquetas. Sirve para contener el texto a mostrar.

Text Esta tampoco es común a todos los controles, pero también sirve para mostrar texto, aunque normalmente los controles que la tienen suelen permitir que ese texto se modifique, el caso típico son los TextBox.

Page 309: curso básico de programación en visual basic

Y como estas, muchas más, 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 decírtelo, pero me parece absurdo documentar algo que ya está documentado y aunque no te lo creas bastante bien... aunque habrá algunas propiedades que necesitarán de alguna explicación y ejemplos... y esas, casi seguro, que las veremos.

Ahora vamos con los métodos.

Los métodos no son otra cosa que procedimientos (SUB o FUNCTION).Un método siempre hace algo, a diferencia de las propiedades que, aunque también pueden hacer algo, su papel suele ser parecido a una variable; por ejemplo, si queremos ocultar un formulario, llamamos al método Hide, que queremos mover el formulario, pues usamos Move con los parámetros 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 métodos, a esta "opinión" no debes darle más importancia de la que tiene y centrarte en lo que realmente te interesa: aprender a usarlas e incluso a crearlas.Para saber más sobre los métodos... te digo lo mismo que con las propiedades, pulsa F1 y leete la ayuda.

Algunas propiedades son sólo de lectura, es decir que no puedes modificarla en tiempo de ejecución, aunque normalmente si las que puedes modificar en tiempo de diseño, es decir cuando estás 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 desvarío 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 sólo lectura cuando ejecutamos el programa, pero mientras "diseñamos" el proyecto, nos permite cambiar ese valor.

Como el título de esta entrega decía: Propiedades, métodos 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, aún 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 vendría mal seguir leyendo).

Cuando apareció el VB4, además 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 también funcionan las de 16 bits; sin embargo el Windows 3.xx sólo soporta aplicaciones de 16 bits, aunque con un "apaño" permite ejecutar algunas de 32 bits... aunque no las de VB... ¿a que venía 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 podría contar cómo llamaban la gente de Microsoft a

Page 310: curso básico de programación en visual basic

esta versión del VB, pero como lo que se, lo he leído 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 módulo: el módulo de clase, el cual tiene como extensión .clsAdemás de esta nueva extensión, los proyectos de VB también 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 extensión MAK, además de ser usada por las versiones anteriores de VB, también las usaban (y usan) los compiladores de Microsoft, tanto para BASIC como para C/C++ y lo más frustrante del compilador del BASIC de MS-DOS, es que existía una utilidad llamada MAKE, (para crear los ejecutables), que también las usaba... y algunas veces era un follón...Dejemos el cuento, que algunas veces me paso con mis "películas"...

Para ir calentando motores, ya que no creerás que te voy a explicar cómo usar este nuevo tipo de módulos... bueno, no te lo voy a explicar por ahora, pero si dentro de poquitas entregas...Vamos a ver cómo usarlo de forma "muy" genérica, para que al menos sepas que puedes crear tus propias "propiedades" y métodos... gracias a:

Los módulos de clase

Aunque sin necesidad de "conocer" las clases, puedes crear tus propiedades y métodos 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 módulo de clase:Si tienes un proyecto abierto, en el menú "project" (proyecto), selecciona añadir módulo de clase.Esto creará una clase llamada Class1.Cámbiale el nombre y haz que se llame cNombre.En el código escribe esto:

Public Nombre As String

Public AñoNacimiento As Integer

Con esto acabamos de crear dos propiedades, ahora vamos a ver cómo usarlas.Antes vamos a crear un método que calculará la Edad:

Public Function Edad() As Integer

Edad = Year(Now) - AñoNacimiento

End Function

Cuando queramos saber la edad que tenemos (o que tiene la clase), simplemente le restamos al año actual nuestro año de nacimiento... La función Now devuelve un valor que es la fecha actual (día y hora), por su lado Year

Page 311: curso básico de programación en visual basic

toma sólo el año de la fecha indicada...PST! Aquí puedes hacer trampas, por ejemplo si quieres quitarte algunos añillos:

Edad = Year(Now) - AñoNacimiento -10

'Cambia el 10, por lo que quieras "rejuvenecer"

Ahora en serio, vamos a ver cómo usar esta clase.A diferencia de los Form y módulos BAS, las variables (propiedades) y procedimientos (métodos) 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 módulo 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:

Dim unNombre As New cNombre

Con esta declaración 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, deberías usarla. En lugar de declarar y crear una clase de esa forma, usa este otro código:

Dim unNombre As cNombre

Set unNombre = New cNombre

En la entrega correspondiente veremos con más profundidad todo esto de las clases y la creación de los objetos en la memoria, la asignación 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 hacíamos con los tipos definidos:

unNombre.Nombre ="Guillermo"

unNombre.AñoNacimiento = 1957

'Para mostrar la edad de este cuarentón, haremos:

Page 312: curso básico de programación en visual basic

MsgBox unNombre.Nombre & " tiene " & unNombre.Edad & " años..."

Esto imprimirá: (suponiendo que estamos en 1999)Guillermo tiene 42 años... (aunque debería decir "tendrá", pero bueno...)

Ahora analiza el código y deduce cómo funciona... ya que lo vamos a dejar aquí... así parecerá una teleserie y estarás pendiente de la pantalla... je, je... esto es la "revancha" por reírte de mi edad.

Nos vemosGuillermoP.S.No te preocupes por la "cortedad" de esta entrega... pronto habrás más...P.S. 2Si hubiese querido, me podría haber enrollado mostrándote un montón de propiedades y métodos con lo cual habría "rellenado" un montón de espacio..., pero, como te dije antes, pulsa F1 y mira lo que te dice la ayuda del Visual Basic.

Cada vez que miro las fechas en que están escritas las entregas y en la que las publico, me da no se qué... pero es que no puedo hacer más...

Aunque ahora, gracias a Encarnica, esto puede que cunda más...¿Quién 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 también se encarga de otros menesteres administrativos... y ahora, además, en sus ratos libres, se atreve a pasarme a máquina 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 también las gracias, porque no tiene, pero si quieres agradecérselo, 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 acostumbráis a hacer cada vez que os encontráis 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, módulos y algo más...

Ya habrás visto que cada vez que se crea un nuevo proyecto, por regla general, se añade un formulario (Form1.frm) en este formulario podemos añadir algunos controles para “interactuar” con el usuario… es decir, que el usuario puede escribir en las cajas de texto, pulsar botones, etc. También, hemos visto, aunque sea por encima, cómo controlar y poder “interceptar” algunos eventos…Pues, ya está… si sabes todo eso, ¿para que seguir?.

En nuestros proyectos, podemos tener más de un formulario, por ejemplo, uno podría ser el principal, el que hace la mayor parte del trabajo, otro podría

Page 313: curso básico de programación en visual basic

mostrarnos mensajes de aviso o el resultado de cualquier ora acción…La cuestión 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 número y nos informará de las veces que hemos jugado, etc. También 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, además 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 más, que usaremos para jugar, aunque todo esto no sería necesario, pero así es más fácil y manipulamos más 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:Añadiendo un módulo bas, y declarando una variable pública, para que sea visible en todo el proyecto.Otra forma sería añadiendo una variable pública al formulario principal… También sería visible en todo el proyecto, aunque para acceder a ella tendríamos que indicar el nombre del formulario…

Vamos a usar el segundo método, ya que al ser el formulario que se usará como punta de entrada, siempre estará disponible.Veamos el código 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, tendrás que hacerlo con ese nombre: “frmPrincipal”.

Muestra la ventana de código de este formulario y añade 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 usándolo 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ú dirás lo que quieras, pero esto de mandar, hacer poco y además ser el “principal”… en fin… cosas de la vida…

Vamos a añadir un nuevo formulario, que se llamará frmNombre.Para hacerlo, selecciona el menú Proyecto, Añadir formulario, selecciona uno

Page 314: curso básico de programación en visual basic

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, “más o menos”, indican.Veamos el código:

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

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 deberías tomar nota, así que… toma nota y quédate con la copla…

Con Show mostramos el formulario en cuestión, fíjate que se usa NombreFormulario.Show, lo cual llama a un método 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 método se carga de forma automática, esto lo veremos en más ocasiones, así que... tranquilízate, toma aire, que todo llega…

El método Show acepta algunos parámetros, que yo conozca son dos, ambos son opcionales y por el momento sólo veremos el primero de ellos.Éste primer parámetro del método 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 aplicación hasta que deje de mostrarse; es como si toda la aplicación, (la nuestra), se congele y sólo preste atención 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 existía esta forma de mostrar los formularios, me dio algún que otro come-coco…

Por suerte, aprendí que la tecla F1 servía para algo más que estar al lado de F2...

Page 315: curso básico de programación en visual basic

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 parámetro, 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 información al formulario principal. Ahora veremos como.

El formulario frmNombre tiene este aspecto:

Un textbox para el nombre del usuario, un botón aceptar y otro cancelar…Los nombres de estos controles son: txtNombre, cmdAceptar y cmdCancelar.Podríamos haber usado la función InputBox para hacer esto, pero no es lo mismo…El botón cancelar simplemente “descarga” el formulario de la memoria con: Unload Me.El botón aceptar también descarga el formulario, pero antes de eso, asigna el nombre introducido en la “propiedad” Nombre del formulario principal, fíjate en el código 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 cuestión, si no se usara… ¿cómo sabríamos qué formulario es? La única excepción es cuando esas propiedades se usan dentro del mismo formulario… ahora lo veremos.

Pero esto no está bien… o casi… si dejásemos 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 añade el control al formulario, en este caso “Text1”, nos daríamos cuenta que sería “más lógico” que se mostrase el nombre del usuario actual, si hubiese alguno, por tanto se debería mostrar el nombre y así permitir usar ese nombre o escribir uno nuevo. Esto lo podemos hacer de dos formas:Una: cómo la propiedad Nombre del form principal es pública, podemos asignarla al textbox cuando se cargue el formulario que “pide” el nombre, es decir en el evento Load de frmNombre:

Page 316: curso básico de programación en visual basic

Private Sub Form_Load()

txtNombre = frmPrincipal.Nombre

End Sub

La otra forma, sería 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 código, en este caso, la asignación 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 método Show dentro del código del evento Form_Load del formulario mostrado, en el caso de que se haga, el VB nos dará un error indicándonos que no se puede mostrar un formulario que se está mostrando en forma modal.

Espero que lo tengas claro, ya que te voy a complicar un poco más la vida, aunque antes veamos unas consideraciones:

Habrás oído de “reutilización de código” y esas otras cosillas relacionadas con la Programación 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 “polémica”.Si usamos el formulario tal como lo “he” codificado, no podrás usarlo de forma genérica, (reutilizarlo), ya que “dentro” de ese código 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, también podría ser un control… pero olvídate de esto para que no te líes demasiado…

Bien, ¿cómo lo solucionamos?

Si has leído algo sobre la programación orientada a objetos, puede que te hayas encontrado con “palabras” como encapsulación de…¿Qué significa esto? Sin entrar en demasiados detalles “didácticos”, encapsular sería tener la “autonomía” suficiente para no depender del exterior y aún 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?

Page 317: curso básico de programación en visual basic

En este ejemplo sólo existe una “propiedad” llamada txtNombre que se encargará de “devolver” el nombre introducido… y también debería indicar que se ha cancelado… aunque esto te lo dejo, (por ahora), a ti… ya veremos la respuesta más adelante.

Veamos el código tal como estaría después de todo esto que te he explicado.Empecemos por el final, veamos el código 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 código del form principal (frmPrincipal):

Option Explicit

Public Nombre As String

Private Sub cmdNombrer_Click()

frmNombre.txtNombre = Nombre

frmNombre.Show vbModal

Nombre = frmNombre.txtNombre

Unload frmNombre

End Sub

Dirás que nos hemos complicado demasiado… antes era más corto… no todo van a ser ventajas, aunque si por añadir dos líneas de códigos ganamos un formulario que es independiente… tu decides…Aunque, personalmente, este último código lo escribiría así:

Page 318: curso básico de programación en visual basic

Private Sub cmdNombrer_Click()

With frmNombre

.txtNombre = Nombre

.Show vbModal

Nombre = .txtNombre

End With

Unload frmNombre

End Sub

Ahora tenemos más líneas de código, pero ganamos en claridad… además si te acostumbras a usar With, (según dicen), el código es más rápido, al menos cuando se hacen referencias a objetos con distintos “niveles”, este no es el caso, pero ya te toparás con casos de múltiples referencias… todo llegará…

Antes de seguir “encontrando” pegas, veamos porqué ha cambiado Unload Me por Hide:

Unload Descarga un formulario de la memoria, después 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 instrucción.

Hide Simplemente oculta el formulario, pero no lo descarga.

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 código que use frmNombre pueda saber el nombre que se introdujo.Un lío ¿verdad? Pues si te lías con cuatro líneas de código, no sabes lo que te espera... je, je.

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 algún día), sepas programar y por extensión 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 “fáciles”, que las hay… aunque también 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 próxima entrega seguiremos… que ya es tarde y mañana hay que currar…Pincha este link para ver una de las soluciones…

Page 319: curso básico de programación en visual basic

Nos vemos.Guillermo

Nerja, 6/Oct/98, 03:10P.S.UF! Hace ya bastante tiempo, ¿verdad?

Si has notado que la entrega anterior está "inacabada" estás en lo cierto... la verdad es que gracias a uno de los "típicos" 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á ningún trastorno cerebral... y si te lo causa... ¡bienvenido al club!

Esta entrega también ha sido "mecanografiada" por Encarnica... que ya tiene pasadas a limpio hasta la entrega 32... aunque sólo la parte explicativa, ya que el código me lo deja a mi... que ella aún no sabe programar... así que si hay retrasos en la publicación, es sólo culpa mia...

También quiero daros las gracias, en nombre de Encarni por los que la habéis felicitado por "ofrecerse" a pasar a limpio mis notas "ininteligibles". ¡Gracias otra vez Encarni!

Menús, submenús, popupmenús y ajuste de tamaños.

En la entrega de hoy vamos a ver cómo añadir menús a nuestros programas. Para verlo "ejemplarizado" vamos a crear un minieditor (¡por fin!) pero no te hagas ilusiones... será tan simple que casi sólo vamos a utilizar un cuadro de texto y sus posibilidades serán básicamente las que nos de el textbox... bueno, realmente tendrá algunas más... ya veremos.

Para crear el programa, abre un proyecto nuevo, el el form que se crea por defecto, añade un TextBox, asígnale estos valores a las propiedades indicadas:

Propiedad Valor

Multiline True

Scrollbars 3-Both

Left 0

Top 0

Name txtEditor

Multiline para que permita más de una línea de texto; los dos Scrollbars para que podamos escribir líneas de cualquier longitud y que sólo 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 añadirle al formulario un Picture para que nos sirva de Statusbar, para ello añade un picture al proyecto, selecciónalo y asígnale la propiedad Align a 2 - Align Bottom para que se pegue a la parte inferior del formulario. Asígnale

Page 320: curso básico de programación en visual basic

un valor 315 a la propiedad Height, en la propiedad BorderStyle, asígnale un 0 (sin borde). Selecciona del toolbar una etiqueta haz doble click y se insertará en el formulario. Selecciona la etiqueta y córtala (menú edición / cortar). Selecciona el picture y pega la etiqueta (menú edición / pegar). Asígnale 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 llámalo txtEditor, el Picture será picStatus, el Label se llamará lblStatus.

Posicionar los controles automáticamente en el formulario

Antes de empezar a crear los menús, vamos a indicarle al Visual Basic que "posicione" correctamente los controles cuando el formulario cambie de tamaño. Para ello, abre la pantalla del código, selecciona Form de la lista de la izquierda y Resize en la lista de la derecha.

Cada vez que un formulario cambia de tamaño se ejecuta el evento Form_Resize, por tanto este es el sitio en el que tendremos que codificar para adaptar los controles al tamaño adecuado. Lo que vamos a hacer es ajustar el tamaño de la etiqueta al tamaño del Picture y el TexBox para que ocupe todo el tamaño restante.

Pero sólo haremos los cálculos cuando el form no se minimice, ya que si está minimizado, no se ve nada, así que para que vamos a ajustar el tamaño de algo que no se ve; para indicarle al VB que sólo ejecute el código cuando no vaya a minimizar la aplicación usaremos la propiedad WindowState, si esta es diferente de vbMinimized querrá decir que no se ha minimizado, así pues, añade este código:

' Sólo cuando no esté minimizado el formulario

If WindowState <> vbMinimized Then

Y ahora empezaremos a ajustar los tamaños: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:

Page 321: curso básico de programación en visual basic

'

txtEditor.Height = ScaleHeight - picStatus.Height

y ya está. Se supone que en tiempo de diseño 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 más rápido cambiar el tamaño de un control con Move que asignando cada una de las propiedades por separado, ya que se usa un sólo método en lugar de 4 asignaciones a propiedades, así pues, podríamos haber cambiado el tamaño del txtEditor de esta otra forma, aunque seguramente sería menos "instructivo":

'

txtEditor.Move 0, 0, ScaleWidth, ScaleHeight - picStatus.Height

Vamos a probarlo :

Pulsa F5 y cambia el tamaño de la ventana, verás como se "adaptan" los controles… aunque el label no parece enterarse ¿verdad?

Para que el label se ajuste al tamaño del picture, hay que ajustar ese tamaño en el evento Resize del picStatus:

'

Private Sub picStatus_Resize()

' Sólo cuando no esté minimizado el formulario

If WindowState <> vbMinimized Then

' Aquí se ajustará el tamaño del label

' cuando cambie el del picStatus

lblStatus.Width = picStatus.ScaleWidth - 60

End If

End Sub

Algo sobre los tamaños de los controles: Diferencia entre Height/Width y ScaleHeight/ScaleWidth

Page 322: curso básico de programación en visual basic

Como habrás notado, para ajustar el ancho y alto, se está usando ScaleWidth y ScaleHeight, aunque para calcular el alto del textbox también 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 hubiésemos usado Height en el lugar de ScaleHeight, los cálculos no nos hubiesen salido correctos, ya en el valor devuelto por esa propiedad nos indicaría el alto total del form. Ahora mismo, tal como está el programa habría poco diferencia, aunque aún así no se ajustaría perfectamente, "desajuste" que quedaría demasiado evidente en cuanto añadamos menús. (¿menús? 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 más, esto siempre es así cuando hagamos referencia a una propiedad de un objeto, (en este caso un formulario), y el código 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 método, para que el VB sepa a que control nos estamos refiriendo.

Borra la línea que cambiaba el tamaño de la etiqueta en el evento Form_Resize y vuelve a pulsar F5, cambia el tamaño 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 añadir menús a nuestro "mini-editor".

Añadir menús a un formulario

Como habrás observado en todas las aplicaciones de Windows, los menús se muestran en la parte superior de las aplicaciones, esto no es ningún descubrimiento, excepcional pero… la cuestión es que si se muestran en la parte superior ¿tendremos que hacer un nuevo cálculo al cambiar el tamaño del formulario?

La respuesta es: no.

Al añadir menús a nuestro formulario, el tamaño de éste se ajusta automáticamente y no tendremos que tener en cuenta el espacio que ocupa para "ajustar" los controles que tengamos en él. Es decir, que el código del Form_Resire sigue siendo válido con o sin menús.

Una vez aclarado este punto, antes de empezar a añadir menús, otro poco de teoría… pero no te asustes, no es demasiado la teoría, sólo para aclarar "conceptos".

Page 323: curso básico de programación en visual basic

Cuando creamos menús 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 menús principal se muestran los que "cuelgan" de él. Habrás observado que muchos de los elementos de los menús, tanto principales como secundarios, tienen una letra subrayada, eso quiere decir que pulsando Alt más esa letra, se despliega o selecciona esa opción del menú, es como si pulsáramos con el ratón. Cuando empiece con la explicación verás cómo podemos crear nuestras propias letras de acceso, incluso cómo añadir "accesos rápidos" a algunas de las opciones de los menús, todo esto lo veremos ahora mismo.

Cómo añadir menús a nuestro formulario (ahora si)

Para poder "diseñar" los menús, tienes que tener visible el formulario en el que mostraremos los menús, 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 diseño de menús, puedes hacerlo de dos formas: seleccionando del menú Tools (Herramientas) la opción Menu Editor… o

pulsando el icono de la barra de herramientas. Te mostrará un cuadro de diálogo como el que sigue:

Las partes más importantes son:Caption/Descripción que es el texto que se mostrará,Name/Nombre del menú que será donde escribamos el código a ejecutar cuando se seleccione ese menú.

Page 324: curso básico de programación en visual basic

Vamos a empezar por añadir un menú "Fichero", (o archivo si así lo prefieres), en este menú tendremos las opciones de Abrir, Guardar, Guardar como y Salir, después añadiremos otras, según convenga. También tendremos otro menú principal llamado Edición con las clásicas 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 botón Siguiente. Se "limpiarán" 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 diálogo, acostúmbrate a seguirla, de esta forma tus aplicaciones tendrán un aspecto "standard windows" (realmente no es un estándar de windows, sino una norma anterior anterior, pero…)

Después de la opción Abrir vamos a añadir Guardar, por tanto en el caption del menú escribimos &Guardar y en el nombre de esa opción: mnuFicGuardar, en este caso no añadimos los tres puntos seguidos ya que lo habitual en las opciones guardar, es guardar sin preguntar, salvo que aún no se le haya dado nombre al fichero.

Como habrás observado cada vez que añades una opción se va mostrando en la lista inferior. Antes de seguir vamos a ver cómo quedan nuestros menús.

Pulsa el botón "aceptar" del cuadro de diálogo del "diseñador de menús".

¡Sorpresa!Como puedes observar, tenemos tres opciones "principales": Ficheros, Abrir… y Guardar, pero esta no era la intención, ya que Abrir y Guardar sólo se deberían mostrar al seleccionar el menú Ficheros¿Qué ha pasado?Muy fácil, al menos cuando se sabe cómo trabaja esto de los menús, que al no indicarle lo contrario… ¡todos los menús se muestran en la barra principal! ¿cómo podemos crear los submenús (o menús que se muestran al seleccionar un menú)?

Ahora lo veremos, antes de hacerlo, en tiempo de diseño, es decir, sin pulsar F5, pulsa en el menú Fichero. Se mostrará la ventana de código, el combo de en la parte izquierda se mostrará mnuFic y en el de la derecha verás 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 opción. No escribas nada, cierra la ventana de código para volver a mostrar el formulario.

Vamos a hacer que los menús se muestren como deben: al seleccionar Ficheros que se despliegue el menú con las opciones Abrir, Guardar, etc.

Page 325: curso básico de programación en visual basic

Entra en el diseño de menús, (esto tendrás que hacerlo siempre que quieras añadir nuevas opciones de menús o modificar las ya existentes.)

Si te fijas en la lista inferior, verás que las tres opciones que tenemos están alineadas a la izquierda.Selecciona Abrir en la lista inferior, comprobarás que se "rellenan" las casillas con la descripción y el nombre del menú, eso nos indica que está seleccionada esa opción y que cualquier cambio que hagamos, se hará en esa opción. Sé que todo esto es evidente, pero… por si no lo habías captado… ahora pulsa en la flecha que señala a la derecha, esto hará que la opción 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 añadimos al final.

Haz lo mismo con "Guardar"

Cierra el cuadro de diálogo y veras que ahora sólo se muestra el menú Ficheros. ¡Bien! ¡Ya tenemos lo que queríamos! Selecciona ese menú y verás que se muestran las dos opciones que hemos añadido; en esta ocasión no se muestra la ventana de código, pero ya veremos que e evento "sigue operativo".

Pulsa en la opción "Abrir..." y en esta ocasión se mostrará la ventana de código con el procedimiento: "mnuFicAbrir_Click", para comprobar que funciona vamos a añadir un mensaje que se mostrará cuando seleccionemos esta opción:

'

Private Sub mnuFicAbrir_Click()

' Abrir

MsgBox "Esta es la opción Abrir..."

End Sub

Ahora para comprobarlo, pulsa F5 y selecciona el menú Ficheros, se mostrarán las dos opciones que tenemos en este menú: Abrir y guardar. Selecciona Abrir y verás que se muestra el mensaje, lo cual quiere decir que todo está bien.

Cierra la aplicación y vuelve a mostrar el formulario para añadir más opciones a los menús; así que haz que se muestre el diseñador de menús.Selecciona la última de las opciones de la lista y pulsa en el botón Siguiente para que podamos añadir más opciones. Escribe G&uardar como... en la descripción y mnuFicGuardarComo en el nombre. Si esta nueva opción se muestra en la lista totalmente a la izquierda, pulsa en la flecha de identación 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 botón Siguiente, ahora tendrás que escribir un guión, (signo menos), en la descripción del menú, en el nombre del mismo escribe: mnuFicSep1, no sirve de nada, ya que las líneas "divisorias" no se pueden seleccionar, pero deben tener un nombre. Lo que debes "recordar" es que si se indica un "-" en la descripción del menú, estamos indicándole al VB que lo que queremos es que muestre una línea de división. Pulsa en siguiente y escribe:

Page 326: curso básico de programación en visual basic

&Salir y mnuFicSalir (ya no es necesario que te diga dónde debes escribirlo, ¿verdad?)

Ya tenemos las opciones del menú Ficheros, ahora vamos con el menú de edición. Añade este menú a continuación de Salir, escribe &Edición 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 menús, sino que será una opción más del menú Ficheros, y eso no es lo que queremos, por tanto, pulsa en la flecha que señala 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 están sin los puntos suspensivos son las que se mostrarán en la barra de menús. Antes de añadir opciones al menú de edición, 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 añadirla: asegúrate que estemos en modo de diseño de menús.

La nueva opción la vamos a insertar al principio, es decir justo antes de Abrir. Por tanto, selecciona Abrir de la lista inferior, pulsa el botón insertar y podrás escribir la descripción: &Nuevo y el nombre del menú: nmuFicNuevo.

Cada vez que quieras insertar una nueva opción puedes hacerlo de esta forma o bien añadiendo la opción al final y después "situarla" en el lugar correspondiente usando las flechas arriba y abajo.Cuando insertas una opción, usando el botón insertar, la opción insertada tiene la misma "indentación" que la que estaba seleccionada antes de pulsar en insertar. Ya sabes que puedes modificar dicha identación, 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 sólo elimina la opción del menú, no el código que tuviese asociado, lo mismo ocurre cuando eliminamos o cambiamos el nombre de un control: si ya tenía código en algunos eventos, este código sigue estando, pero no en el sitio que debiera… no voy a seguir con esto, ya que lo veremos en otra ocasión, 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 menús se preceden con "mnu" seguido del nombre de menú principal y por último el nombre de la opción. 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 opción "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 menús, 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 código para cada uno de los eventos que queramos interceptar; simplemente usando

Page 327: curso básico de programación en visual basic

el índice podríamos 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 menús: podemos crear un array.

La única diferencia es que se crea de forma un poco más manual y se hace en la "ventana de diseño de menús".

Array de menús

Para ello, se usa el mismo nombre de menú, pero usando un "índice" diferente para cada opción.

El único requisito "obligatorio" es que los elementos de un array de menús 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 opción. Y esto es lo que vamos a hacer nosotros: incluir en un array todos las opciones del menú edición, que serán las siguientes: Deshacer, separación, cortar, copiar, pegar, separación, seleccionar todo; posteriormente añadiremos más opciones… aunque seguramente será en otra entrega.

El nombre del array será: mnuEditor, el primer elemento, de índice cero, será: Deshacer.

Vamos a añadirlo pasito a pasito, para que no tropieces y te "descalabres".

Supongo que ya estarás en el diseñador de menús y que la opción seleccionada es la última: Edición.Pulsa en el botón 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 botón con la flecha a la derecha, para indicar que esta opción pertenece al menú Edición.Pulsa en siguiente, si no se indenta, ya sabes cómo 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...

Cuando hayas introducido todas las opciones, cierra el diseñador de menús… si te da algún tipo de error, puede ser porque no hayas usado el mismo nombre de menú para todas las opciones del menú edición, porque no estén los índices correlativos o porque no estén 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 sería el aspecto:

Page 328: curso básico de programación en visual basic

Recuerda que los elementos de un array de menús deber ser correlativos y estar en el mismo nivel de indentación ("osease" pertenecer al mismo menú).

Si todo está bien, al mostrar el formulario en tiempo de diseño, no en ejecución, y pulsar en el menú edición, verás que se muestra las opciones que hemos escrito. Pulsa en cualquier de ellas y verás que siempre se muestra el mismo evento en la pantalla de código:mnuEditor_Click (Index As Integer)

El parámetro index será el que nos indique cual de las opciones ha sido la que se ha coleccionado.

Para poder hacer cosas diferentes según la opción seleccionada haremos algo como esto:

'

Private Sub mnuEditor_Click(Index As Integer)

Select Case Index

Case 0

' Deshacer

Case 2

' Cortar

' Etc...

'

Page 329: curso básico de programación en visual basic

End Select

End Sub

Pero para que resulte más fácilmente entendible y modificable, en lugar de números, vamos a usar constantes, de esta forma, si añadimos o eliminamos alguna de las opciones del menú, sólo tendremos que cambiar el valor de la constante y el resto del código no habrá que modificarlo.

Así pues, en la ventana de código, selecciona la parte general de las declaraciones y añade esto:

'

' Constantes para el menú de Edición.

' 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 quedarán 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

Page 330: curso básico de programación en visual basic

Con lo cual hemos ganado en "legibilidad" en el código y, aunque aún no lo "sepas", en facilidad a la hora de modificar el código.

El código a usar será el contenido de la siguiente entrega, ya que esta se acaba aquí.

Además del código a usar, veremos cómo añadirle, a las opciones del menú, teclas de acceso rápido, por ejemplo:

Control + X para cortar, etc.

Pero eso será en nuestro siguientes episodio.Permanezca atento a la pantalla… continuará.

Nos vemosGuillermo

Manuscrito original escrito en Las Palmas de Gran Canaria el 26 de Oct de 1998

¿Te extraña ver otra entrega tan pronto?Aprovecha y no te quejes... que ya llegarán nuevas "lagunas"; aunque puede que no tantas... depende... o dependerá del tiempo, ganas, inspiración y hasta motivación... 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 más o menos clara... aunque se que no todo lo claro que a algunos le gustaría...

Quiero aclarar un par de cosillas, para que no te entre la desesperación si no das pie con bola...Desde hace algunas entregas, la versión de VB que hay que usar es como mínimo la 4, (preferiblemente de 32 bits), aunque cada vez voy dirigiéndome más a la versión 5, giro que sobre todo se notará cuando empecemos con el tema de los módulos de clases y esas cosillas orientadas a objetos; así que si tienes un VB anterior a la versión 5, ve planteándote el conseguir una nueva versión... o te perderás algunas cosillas interesantes. El que avisa...

Como viene siendo habitual desde hace un par de entregas, esta también está "tecleada" por la "güena" de Encarni, que ya ha terminado con los manuscritos o garabatos que le di... así que, si tardo en publicarlas, la culpa será sólo mía...

Hablando de publicar... de vez en cuando recibo una petición de "autorización" para usar estas entregas en diferentes sitios, tanto de Internet como para "revistas" de colegios y esas cosillas, gracias a los que lo hacéis y lo único que pido es que se "respete" el contenido, es decir TODO el contenido, incluido los "desvaríos" 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 menús que el tiempo apremia.

Page 331: curso básico de programación en visual basic

Antes de ver el código "operativo" de las opciones que añadimos en la entrega anterior, vamos a seguir aprendiendo cosas de los menús, para tener los conceptos más claros, que al fin y al cabo es lo que interesa.

Menús que contienen menús

Aunque en nuestro pequeño editor no lo vamos a usar, veremos cómo se crean opciones de menús que a su vez muestran otros menús.

Para ello vamos a crear otra opción en el menú ficheros para poder imprimir, pero que nos mostrará un par de opciones: configurar la impresora además de la opción imprimir. El aspecto de este menú sería 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 queríamos mostrar las opciones de uno de los menús principales, simplemente "desplazábamos" 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 práctica:

Muestra el formulario, haz que se muestre el "diseñador de menús", posiciónate en la opción "Salir", pulsa en el botón insertar y escribe en la descripción, (o caption), &Imprimir…, en el nombre del menú escribe mnuFicImp, pulsa en siguiente para que acepte lo que hemos escrito, para insertar una nueva línea, pulsa en el botón insertar y escribe en el caption: &Selecionar impresora… y mnuFicImpSelec en el nombre del menú; antes de pulsar en siguiente, dale al botón con la flecha hacia la derecha, en esta ocasión tendremos seis puntos suspensivos delante de esta opción, (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ú.

Page 332: curso básico de programación en visual basic

Ahora pulsa en los botones siguiente e insertar y escribe &Imprimir y mnuFicImpImp, ya sabes dónde, para que esté en el mismo nivel que Seleccionar Impresora, tendrás que pulsar en la flecha que señala a la derecha... para que esté en el mismo nivel de menús.Si por casualidad ves que salen más puntos suspensivos de la cuenta... pulsa en el botón con la flecha a la izquierda para quitar la indentación que le hayas dado de más.

Cierra el diseñador de menús.Muestra el formulario, si pulsas en el menú Ficheros y a continuación en Imprimir..., verás que tenemos un nuevo menú, en este caso con las dos opciones que hemos añadido, (ver la figura anterior).Así de fácil se crean los sub-menús o menús que se muestran al seleccionar una opción de un menú.

Nota:Cuando tienes opciones ya creadas y quieres insertar nuevas opciones, hay que usar el botón Insertar, pero la "indentación" que muestra es la misma que tiene el menú en el que nos posicionamos antes de insertar, por tanto tendrás que usar las flechas de indentación para ajustar los niveles de menús.

Mostrar menús emergentes (popupmenús)

Antes de ver el código para esta utilidad, que realmente es lo de menús, ya que la intención de la misma es ver cómo crear y manejar menús, vamos a ver cómo hace que se muestre un menú emergente: de esos que se suelen mostrar al pulsar el botón derecho del ratón.

Si estás usando Windows 95 o superior, habrás notado que las cajas de texto ya incluyen las opciones habituales de edición, nosotros no tendremos necesidad de codificar nada para tener disponibles esa característica. Pruébalo. Ejecuta con F5 el programa y pulsa en el textbox, verás 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 líneas de código. ¿Dónde? 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 botón derecho... (vbRightButton = 2)

If Button = vbRightButton Then

' Mostrar el menú de Edición

PopupMenu mnuEdit

' Si queremos mostrar en negrita uno de los menús,

' lo indicaremos en el quinto parámetro

'PopupMenu mnuEdit, , , , mnuEditor(cEdCopiar)

Page 333: curso básico de programación en visual basic

End If

End Sub

Ya vimos que uno de los parámetros del evento MouseDown estaba el de saber que botón se está pulsando, por tanto usaremos ese parámetro, que es Button, para saber si es el botón derecho, (vbRightButton que tiene un valor igual a 2), y si es así, mostramos nuestro propio menú. Para ello usamos la instrucción PopupMenu, en el primer parámetro indicamos el nombre del menú que contiene las opciones que queremos mostrar, del resto de los parámetros, en principio sólo nos podría 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 característica se suele usar cuando queremos mostrar menús en nuestra aplicación pero queremos que se muestren solamente como menús emergentes, en otra ocasión veremos esto.

Una vez que un menú emergente se ha mostrado funciona de igual manera que si no fuese emergente… se que es "lógico", pero lo aclaro por si las moscas… es decir cuando se selecciona una opción, se ejecuta el código que hayamos escrito en el evento click de esa opción.

Un detalle, puede que al pulsar el botón derecho en el textbox, no se muestre el menú emergente, casi con toda seguridad tendrás que pulsarlo por segunda vez, si no recuerdo mal, esto no ocurría con el VB4...

Teclas de acceso rápido en los menús.

Ya te comenté que se pueden asignar teclas de acceso rápido 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…

¿Cómo se añaden estos accesos rápidos? Pues… con el diseñador de menús… aunque también se puede hacer mediante código. Pero vamos a usar el "diseñador".A estas alturas ya debes saber cómo mostrar ese dialogo… ¿verdad? Pues muéstralo y vamos a añadir inicialmente ese tipo de accesos a las opciones del menú de edición.

Selecciona la opción "Cortar", en el cuadro de diálogo hay una lista con las teclas que podemos usar, (en inglés 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 diálogo y pulsa en el menú edición, verás que se han añadido al Caption esas teclas, pero no sólo están "mostradas", sino que si ejecutas el programa verás que están operativas y que no es necesario hacer nada extra... simplemente pulsar esas combinaciones de teclas y se ejecutará el código asociado a esa opción del menú.

Page 334: curso básico de programación en visual basic

Para comprobarlo, vamos a codificar la opción de seleccionar todo:Muestra el form y selecciona el menú edición, pulsa en cualquiera de las opciones y se mostrará la ventana de código, (también podrías haberla seleccionado directamente, pero así parece que ibas a hacer otra cosa, je, je ), el código en este evento será el siguiente:

Private Sub mnuEditor_Click(Index As Integer)

' Cuando se selecciona un elemento del menú Edición

' se entra en este evento, el índice nos indicará

' el elemento seleccionado.

' Existen unas constantes para usarlas en lugar del número,

' por si añadimos o quitamos algunos

Select Case Index

Case cEdDeshacer

'

Case cEdCortar

'

Case cEdCopiar

'

Case cEdPegar

'

Page 335: curso básico de programación en visual basic

Case cEdSeleccionarTodo

With txtEditor

.SelStart = 0

.SelLength = Len(.Text)

End With

Case cEdBuscar

'

Case cEdBuscarSig

'

Case cEdReemplazar

'

End Select

End Sub

Ejecuta la aplicación y escribe varias líneas en el texbox, pulsa la tecla control y sin soltarla pulsa la tecla a, (esto como habrás observado en otras ocasiones, se simplifica diciendo: pulsa Ctrl+A), ¡y ya está! ¡todo el texto seleccionado!.

Por supuesto que también puedes ir al menú edición y clickear en la opción seleccionar todo.

Te explico un poco el código que hace que seleccione todo el texto:Le indicamos que la posición de inicio del texto seleccionado sea la primera posición: .SelStart = 0Le indicamos que el texto seleccionado sea de igual longitud que todo el texto que hay escrito: .SelLength = Len(.Text)Si no hubiésemos usado el With txtEditor... End With, tendríamos que haber especificado el nombre del objeto, el código sería este otro:

'

Case cEdSeleccionarTodo

txtEditor.SelStart = 0

txtEditor.SelLength = Len(txtEditor)

Ahora vamos a codificar un par de opciones del menú edición, lo vamos a hacer con código en Visual Basic; de eso se trata ¿no?, aunque podríamos 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)

Page 336: curso básico de programación en visual basic

Las opciones que estarán relacionadas con el portapapeles son: Cortar, Copiar y Pegar; antes de codificar estas opciones en el evento mnuEditor_Click, veamos cómo manipular el clipboard.

Los métodos 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 vínculo 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 gráficos 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 métodos:Con GetFormat, sabremos si hay algún texto en el portapapeles, en caso de que así sea, al mostrar el menú, habilitaremos la opción 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

¿Dónde se hace esto?

Todas estas comprobaciones se hacen en el evento click del menú edición, (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 están deshabilitadas

Page 337: curso básico de programación en visual basic

mnuEditor(cEdCortar).Enabled = False

mnuEditor(cEdCopiar).Enabled = False

mnuEditor(cEdPegar).Enabled = False

' Comprobamos si hay texto en el portapapeles

If Clipboard.GetFormat(vbCFText) Then

' Hay texto, habilitamos la opción 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 edición, seguramente "notarás" cómo los item cambian de estado… pero eso es lo menos importante, ya que lo que interesa es que estén habilitados los que tengan que estarlo.

Ahora que hemos habilitado y/o deshabilitado las distintas opciones de edición, vamos a usarlas.

Según vimos, el menú de edición está en un array y por tanto comparten el mismo nombre de menú y según 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

Page 338: curso básico de programación en visual basic

Case cEdPegar

' Ponemos en el textbox el texto que haya en el portapapeles

txtEditor.SelText = Clipboard.GetText()

'...

Para el caso de deshacer, podemos usar el API de Windows, realmente para todas estas opciones también, 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 añadir otras opciones, como buscar, reemplazar, etc.

Usar el API, para deshacer

La función del API para este menester es: SendMessage, la declaración es esta: (aunque pueden existir otras con diferentes parámetros)

Nota:Esta declaración y el valor de las constantes que voy a dar son para Windows de 32 bits, si quieres saber los valores y la declaración para 16 bits, en la sección API de mis páginas lo tienes... (esto es por si estás leyendo esta entrega desde otro sitio distinto al de mis páginas)

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 sería de esta forma:

Private Const WM_CUT = &H300

Private Const WM_COPY = &H301

Private Const WM_PASTE = &H302

Private Const WM_CLEAR = &H303

Page 339: curso básico de programación en visual basic

Private Const WM_UNDO = &H304

Para saber si se puede deshacer:

If SendMessage(txtEditor.hWnd, EM_CANUNDO, 0&, ByVal 0&) Then

mnuEditor(cEdDeshacer).Enabled = True

End If

Fíjate que el último parámetro tiene "antepuesto" al valor CERO la palabra ByVal, esto es porque en la declaración de la función se ha especificado como As Any.

Para deshacer, también usamos la misma función 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 usarían 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 función de Windows, que debe operar sobre esa "ventana", por tanto todas las operaciones se realizarán en la ventana de la cual indicamos el "manejador" de ventanas (handle), que el VB nos proporciona mediante la propiedad de sólo lectura hWnd. Todos los controles que "actúan" como ventanas, tienen la propiedad hWnd.

Como puedes comprobar no tiene nada extraño ni raro, es decir que esto del API de Windows es como todo: para saber lo que hay que hacer hay que saber cómo hacerlo… y esa es la intención de este cursillo, intentar enseñarte 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 código que tenemos hasta ahora en el formulario:

'

'------------------------------------------------------------------------------

Page 340: curso básico de programación en visual basic

' Editor para el curso básico (19/Oct/98)

' Entrega 29, publicada el 25/Abr/99

' Entrega 30, publicada el 02/May/99

'

' ©Guillermo 'guille' Som, 1998-99

'------------------------------------------------------------------------------

Option Explicit

' Constantes para el menú de Edición.

' 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

' Función del API de Windows de 32 bits de múltiple 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

Page 341: curso básico de programación en visual basic

Private Sub Form_Resize()

'------------------------------------------------------------------

'NOTA:

' ScaleWidth y ScaleHeight devuelven el tamaño "interno"

' del form o control.

' Width y Height devuelven el tamaño externo del form o control.

'------------------------------------------------------------------

' Sólo cuando no esté minimizado el formulario

If WindowState <> vbMinimized Then

' Si esto no funciona, que de seguro no funcionará,

' tendrás que ponerlo en el picStatus_Resize

'lblStatus.Width = picStatus.ScaleWidth - 60

' Ajustar el tamaño 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 están deshabilitadas

mnuEditor(cEdDeshacer).Enabled = False

Page 342: curso básico de programación en visual basic

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 opción 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

Private Sub mnuEditor_Click(Index As Integer)

' Cuando se selecciona un elemento del menú Edición

' se entra en este evento, el índice nos indicará

' el elemento seleccionado.

' Existen unas constantes para usarlas en lugar del número,

' por si añadimos 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

Page 343: curso básico de programación en visual basic

' 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&)

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

Page 344: curso básico de programación en visual basic

End Sub

Private Sub picStatus_Resize()

' Sólo cuando no esté minimizado el formulario

If WindowState <> vbMinimized Then

' Aquí se ajustará el tamaño 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 botón derecho... (vbRightButton = 2)

If Button = vbRightButton Then

' Mostrar el menú de Edición

PopupMenu mnuEdit

' Si queremos mostrar en negrita uno de los menús,

' lo indicaremos en el quinto parámetro

'PopupMenu mnuEdit, , , , mnuEditor(cEdCopiar)

End If

End Sub

Ya sólo 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 vemosGuillermo

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

Menús (el resto) y los diálogos comunes

Nos quedamos pendientes de codificar las opciones del menú de ficheros.

Las opciones que tenemos son:

Nuevo---Abrir...Guardar

Page 345: curso básico de programación en visual basic

Guardar como...---Imprimir...Seleccionar impresora...Imprimir---Salir

Pero vamos a añadir otra: Mezclar

Esta opción insértala 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 código 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 diálogos comunes

Es decir, un cuadro de diálogo, (no se porqué lo llaman de diálogo, 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 diálogo con los controles que tenemos registrados en el sistema, selecciona: Microsoft Common Dialog Control (ComDlg32.ocx, control de diálogos comunes) y pulsa aceptar.

En la barra de herramientas, (la que está a la izquierda), verás 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 métodos tendremos que usar, como es costumbre en todos los controles, ese nombre seguido de un punto y después la propiedad o método.

Por ejemplo para abrir:

With CommonDialog1

.DialogTitle = "Seleccionar un fichero para abrirlo"

.ShowOpen

NombreFichero = .FileName

End With

Page 346: curso básico de programación en visual basic

Ahora que "sabemos" cómo 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 codificación del programa; ya hemos visto otros "listados", pero este es algo más complicado... aunque no tiene porqué serlo...

Revisemos lo que tenemos y, sobre todo, que utilidad tendrá todo este tinglado, sé que después de dos entregas ya se tendría que saber pero...

-La intención es crear un formulario con una serie de menús, 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, además de realizar ciertas tareas de "edición" en el texto contenido en esa caja de texto.

Pero nos falta un pequeño detalle: cómo 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 pequeño detalle: ¿dónde 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 módulo, ya que se usará tanto en los procedimientos de abrir como en los de guardar. Por tanto declárala en la sección general del formulario:

Private NombreFichero As String

Y ahora te tendría que seguir mostrando código, 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 más señas, así que solo te diré "lo nuevo", pero el resto tendrás que hacerlo por tu cuenta, aunque te diré que es lo que debes hacer y dónde; de esta forma tendrás ejercicios obligatorios... si no los resuelves, no tienes programa, je, je.

Antes de ver el código de este mini-editor, vamos a ver un nuevo sistema de nombrar constantes:

Enum

Nota: esto sólo existe en la versión 5 o superior.

La enumeración se usa para constantes de forma correlativa, aunque eso puede cambiarse.

Page 347: curso básico de programación en visual basic

Por ejemplo:

Enum eEdicion

Deshacer

Sep1

Cortar

Copiar

End Enum

Según esto, Deshacer vale 0, Sep1=1, Cortar=2, etc.

Osea el primer nombre de constante valdrá cero y las demás irán 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

También podemos asignar los que queramos:

Enum ePrueba2

Uno = 1

Dos '2

Tres '3

Nulo = 0

Cuatro = 4

Cinco '5

End Enum

Fíjate 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.

Page 348: curso básico de programación en visual basic

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 mínimo y máximo del rango de las constantes. Recuerda que cada vez que se asigna un valor a una constante de una enumeración, si en la siguiente no se le indica explícitamente un valor, se incrementa el valor anterior en uno. Por tanto, edPrimero y edDeshacer valdrán cero, edSep1 vale 1, edCortar vale dos y así sucesivamente.

Otra cosa a saber sobre las enumeraciones, es que los valores siempre son numéricos del tipo long.

Cuando creamos una enumeración, estamos creando un nuevo tipo de variable... o casi, lo importante es que podemos crear variables de la enumeración:

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:

Page 349: curso básico de programación en visual basic

es decir, nos muestra una lista despegable con los valores posibles.

También 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 deberías usar el mismo nombre de constante en diferentes enumeraciones, ya que el Visual Basic podría 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

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

Page 350: curso básico de programación en visual basic

Para solucionarlo podemos hacer dos cosas:

---Usar nombres diferentes en las constantes enumeradas, (lo más recomendable).---Indicar el nombre de la enumeración: 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

Fíjate 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 ningún tipo de operación 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 mayúsculas / minúsculas, es decir que si declaramos Primero en un Enum y después escribimos ese nombre en minúsculas se cambiará la declaración a minúsculas:

x = primero

Pero aún así, si tenemos Option Explicit podemos detectar valores no "creados"; aunque para mi gusto, se debería "respetar" el estado de mayúsculas/minúsculas de la declaración.

Page 351: curso básico de programación en visual basic

Un último comentario y ya termino..., al igual que ocurre con los procedimientos en los que no se indica "explícitamente" que es privado, serán públicos por defecto. Así que, si no tienes intención de hacer públicas las enumeraciones, decláralas como Private.

Ahora veamos parte del código nuevo, el resto será el que se mostró en la entrega anterior.

Los valores de las constantes para el menú de edición las vamos a tener en una enumeración. Por tanto si la versión que estás usando es anterior a la 5, tendrás 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 Edición.

' 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 ' Nombre del fichero

Private Modificado As Boolean ' Para indicar si se ha cambiado el texto

Al iniciarse el formulario asignaremos los filtros para la selección del tipo de ficheros en el cuadro de diálogo.El filtro se asigna a la propiedad Filter del CommonDialog y el formato a usar será: descripción del tipo, el tipo, descripción, tipo, etc. cada uno de estos "datos" estará separado por el signo | (ALT+124) o Alt Gr y 1.

Private Sub Form_Load()

Page 352: curso básico de programación en visual basic

' Asignar el filtro para el diálogo común

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 código de algunas de las opciones del menú de ficheros, el resto lo veremos en la siguiente entrega, junto con todo el código...

Private Sub mnuFicAbrir_Click()

' Comprobar si el texto se ha modificado

' si es así, guardarlo o no, según 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()

Page 353: curso básico de programación en visual basic

' Si no se ha asignado el nombre al fichero, preguntar por él

If Len(NombreFichero) = 0 Then

mnuFicGuardarComo_Click

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

Page 354: curso básico de programación en visual basic

sMerge = input$(LOF(nFic), nFic)

Close nFic

' Tomar lo que hay hasta la posición del cursor

With txtEditor

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 imaginarás donde pone ***ejercicio*** es lo que tienes que hacer, ya sabes que en la entrega quince se hizo algo parecido.

Por lo demás, nos queda la parte de: seleccionar impresora, imprimir, buscar y reemplazar, pero eso lo dejaremos para otra ocasión... aunque si no quieres esperar, pásate por la sección "Mis Utilidades" y busca la entrada que dice "Un procedimiento genérico para imprimir"

Page 355: curso básico de programación en visual basic

En la próxima entrega también veremos algo sobre el control de errores... cosa que será necesaria "controlar" para los casos en que se pulse "Cancelar" en el cuadro de diálogo de selección del nombre del fichero.

Así que, paciencia... y a esperar... si puedes... y si no puedes... pues eso...

Hasta la próxima

Nos vemosGuillermo

¿Que tal?Creías que ya no volvería por aquí ¿verdad? pues te has vuelto a equivocar... espero que te cueste trabajo librarte de mí... así que... a aguantar al Guille que aún me queda cuerda "pa" rato... ¡espero!

En esta entrega terminaremos de ver el código 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 detección de errores, al menos al acceder a los cuadros de diálogos,Seleccionar la impresora a usar e imprimir el contenido del texto que hemos escrito... además de las "respuestas" a los ejercicios "camuflados"... ya veremos en que queda todo esto... (es que los ejercicios yo aún no los he resuelto... je, je, en fin...)

Empecemos por:

La detección de errores al usar los cuadros de diálogos

Cuando pulsas en el botón cancelar del cuadro de diálogo no hay forma de saber que se ha pulsado ese botón... pero lo lógico es hacer algo en particular si así ha sido... ¿verdad? (si bwana), pues eso... vamos a ver cómo "detectar" que se ha pulsado en Cancelar...

Para lograr esto, usaremos la instrucciones:On Error Resume Next

Cuando Visual Basic se encuentra con esta instrucción continuará a pesar de que ocurran errores...¿de que sirve el que continúe cuando se produce un error?La verdad es que de nada si no tenemos en cuenta ese "detalle", por tanto, si usamos esa instrucción, tendremos que ser consecuentes con nuestros actos... (Guille, eso te ha quedado en plan sermón... no se si lo aguantaré...)

La forma de saber que se ha producido un error es comprobarlo... ¿cómo? 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

Page 356: curso básico de programación en visual basic

If Err Then...

El resultado es el mismo: si se ha producido un error se ejecutará el código que esté después del Then...

Veamos esto que te estoy contando en el código 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, según 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 botón Cancelar, salimos del procedimiento

ElseIf ret = vbCancel Then

Exit Sub

End If

End If

' Usar detección 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 ningún error,

' es que NO se ha pulsado en Cancelar

Page 357: curso básico de programación en visual basic

If Err.Number = 0 Then

If Len(.FileName) Then

NombreFichero = .FileName

' Abrir el fichero y asignarlo al textbox

Dim nFic As Long

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 había al principio del procedimiento de abrir), en el procedimiento de Guardar se comprueba si hay algún 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 detección de errores... primero ponemos las instrucciones esas que vimos hace un rato... en el Cuadro de diálogos 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 número 32755), por tanto, después 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 continúa con el resto del código... que por cierto es ninguno, ya que el código restante está dentro del If Then... salvo el volver a poner el número del objeto Err a cero, para que no se quede ningún número de error "colgado" y pueda interferir en otros procedimientos.

Page 358: curso básico de programación en visual basic

Sigue este link y aprenderás un poco más sobre esto de la detección de errores.

En cuanto a la respuesta de cómo 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 después lo asignamos al TextBox, aunque podríamos haberlo asignado directamente... pero es que tengo costumbre de usar variables intermedias... cosas... en fin...

Fíjate que después 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 después de asignar el contenido al textbox... aunque creo que esto ya lo expliqué en otra ocasión... ¿no? la verdad es que no me acuerdo, así que mejor son dos que ninguna...

Veamos ahora:

Cómo seleccionar la impresora a usar por nuestra aplicación.

Para hacer esto, usaremos también el Common Dialog que tenemos insertado en nuestro formulario:

'

Private Sub mnuFicImpSelec_Click()

' Seleccionar la impresora a usar (23/Ene/00)

' La detección 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 diálogo y después, por supuesto, usar el método ShowPrinter; cuando el control de diálogos comunes se encuentra con ese "flag" sabe que debe mostrar el cuadro de diálogo de configurar impresora, desde el cual podemos seleccionar otra de las impresoras instaladas en el sistema.

Page 359: curso básico de programación en visual basic

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:

Cómo imprimir el contenido de la caja de textos.

En esta ocasión también usaremos el Cuadro de Diálogo 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 diálogo nos lo permitirá hacer...

With CommonDialog1

.CancelError = True

.DialogTitle = "Imprimir"

' cdlPDHidePrintToFile No mostrar el botón de imprimir en un archivo

' cdlPDNoPageNums No mostrar desde que página imprimir, etc.

' CdlPDUseDevModeCopies Dejar al SO que se encargue de imprimir las copias

' En caso de que la impresora no lo soporte, estará

' deshabilitada la opción del número de copias.

.Flags = cdlPDHidePrintToFile Or cdlPDNoPageNums Or cdlPDUseDevModeCopies

.ShowPrinter

' Si no se ha cancelado

If Err = 0 Then

' En la propiedad .Copies estará el número de copias a imprimir.

Page 360: curso básico de programación en visual basic

' Hay casos en los que las impresoras "automáticamente" usan ese valor,

' por tanto si no queremos hacer un bucle para imprimir el número de

' copias solicitadas, podemos dejar que sea el propio sistema el que

' se encargue de esa cuestión...

' El problema, cuando la impresora no permite imprimir varias copias

' 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

También podríamos hacerlo más complicado, de forma que podamos controlar cada una de las líneas a imprimir... no es que tenga ninguna utilidad práctica, pero así sabes como controlar el contenido de cada una de las líneas 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 básico... ya veremos.

Cómo controlar cada línea de un TextBox Multiline

Esta tarea está contenida en un procedimiento, el cual recibe como parámetro 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 genérica o bien porque tengamos más textboxes en nuestro proyecto...

Para usarlo simplemente se haría:

ImprimirPorLinea txtEditor

Este es el contenido del mencionado procedimiento.

'

Private Sub ImprimirPorLinea(qControl As TextBox)

' Este procedimiento tomará cada línea de un textbox multiline (23/Ene/00)

Page 361: curso básico de programación en visual basic

' y lo imprimirá en la impresora predeterminada

'

' El parámetro qControl, será el TextBox a usar, en este caso no es necesario

' ya que sólo tenemos un TextBox, pero si se usaran varios...

' sería un procedimiento de uso genérico...

'

Dim i As Long, k As Long

Dim L1 As Long, L2 As Long

' Constantes para usar con SendMessage

Const EM_GETLINECOUNT = &HBA

Const EM_LINEFROMCHAR = &HC9

Const EM_LINELENGTH = &HC1

' Número de líneas del TextBox

k = SendMessage(qControl.hWnd, EM_GETLINECOUNT, 0, 0&)

Printer.Print ""

For i = 0 To k - 1

' Primer carácter de la línea actual

L1 = SendMessage(qControl.hWnd, EM_LINEINDEX, i, 0&) + 1

' Longitud de la línea actual

L2 = SendMessage(qControl.hWnd, EM_LINELENGTH, L1, 0&)

' Imprimimos el trozo de texto que representa a una línea

Printer.Print Mid$(qControl.Text, L1, L2)

Next

' Le indicamos que ya no hay más que imprimir

Printer.EndDoc

End Sub

Esto es todo, al menos por ahora, aunque aún queda por hacer la parte de Buscar y Reemplazar, pero eso lo dejaremos para otra ocasión, ya que si no se iba a alargar más 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 mágico para otros... o casi... aún así, espero que no tengas queja... ya que esta entrega ha valido por dos... o más... ahora, a esperar a la siguiente... paciencia, paciencia...

Nos vemosGuillermo

Page 362: curso básico de programación en visual basic

¿Cómo detectar errores en Visual Basic?

Cuando quieras que el Visual Basic "ignore" los errores que se produzcan en tu aplicación o en parte de ella, usa:

On Error Resume Next

Esto hará que si se produce un error, se continúe ejecutando el código como si nada hubiese ocurrido.Por supuesto que la recomendación es que compruebes si se ha producido un error, ya que no es bueno dejar que los errores ocurran sin más.Para ello tendrás 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 instrucción On Error Resume Next y no has "limpiado" el valor del número del error... cualquier comprobación 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, añade tresd botones (Command1, Command2 y Command3), y pega este código:

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 & _

Page 363: curso básico de programación en visual basic

"pulsa en NO para llamar al procedimiento Command2_Click" & vbCrLf & _

"pulsa en Cancelar para llamar al procedimiento Command3_Click", vbYesNoCancel)

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 & _

Err.Number & ", " & Err.Description, , "En Command1_Click"

End If

End Sub

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

Page 364: curso básico de programación en visual basic

' Limpiamos el valor del error

Err = 0

End Sub

Private Sub Command3_Click()

' Este procedimiento produce un error número 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 código 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, tendrás que pulsar varias veces en ese botón, 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 número 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 aún hay una rutina "interceptadora" de errores en funcionamiento, la del Command1, deja de ejecutar el código erróneo y vuelve a la siguiente instrucción que haya en el procedimiento Command1...

Después de estas tres pruebas, pulsa en el Command2. Nada ocurre, ya que el código detecta los posibles errores.

Cuando pulses en el Command3, verás que el Visual Basic se detiene mostrandonos una ventana de error, esto ocurre porque no hay ninguna rutina de detección de errores en funcionamiento y cuando no la hay... el Visual Basic nos muestra la suya propia y detiene el programa.

Ahora cambia el código del Command3_Click por este otro:

Page 365: curso básico de programación en visual basic

'

Private Sub Command3_Click()

On Error Resume Next

' Este procedimiento produce un error número 5

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 verás, 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 código del Command1, se mostrará el mensaje de que hay error... y además 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.

También debes saber que, cuando acaba un procedimiento, la rutina que gestiona los errores también acaba, pero, como has podido comprobar, el valor del error permanece asignado.

Otras formas de detectar errores

Acabamos de ver la forma más "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 NúmeroLínea

Si Visual Basic se encuentra con esta instrucción y se produce un error, pasará a ejecutar el código que esté en la línea indicada por NúmeroLínea, (no tiene porqué ser un número, puede ser y de hecho es lo más recomendable, una etiqueta), por tanto si se especifica ese formato, NúmeroLínea debe existir... (ahora veremos un ejemplo)

Antes de continuar, recordaros que la detección de errores se procesa en el último procedimiento en el que se ha especificado una intrucción On Error... por tanto si hay

Page 366: curso básico de programación en visual basic

algún procedimiento con una "detección" 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 código:

'

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 detección 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 detección de errores

Page 367: curso básico de programación en visual basic

' Este procedimiento produce un error número 5

Error 5

End Sub

Al pulsar en el Command4, se muestra un cuadro de diálogo, si pulsas "SI", se producirá un error dentro de ese procedimiento y se "saltará" a la etiqueta indicada en On Error 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 detección 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, verás que el error es detectado directamente por el Visual Basic, mostrándonos un mensaje y, en caso de que fuese un ejecutable, acabando la aplicación.Esto es porque en el código del procedimiento Command5_Click no hay ninguna rutina de detección 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 sólo ocurre cuando se llaman a procedimientos dentro de otros procedimientos.

Fíjate 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 tendríamos algo parecido al On Error Resume Next, ya que cuando el VB se encuentra con un Resume Next, continúa en la siguiente instrucción a la que produjo el error.

Resume NúmeroLínea, en esta ocasión se continuará con el código que haya a partir de la etiqueta (o número de línea) NúmeroLínea...

Si Visual Basic se encuentra con una instrucción Resume y no hay un error, mostrará una queja indicándones que se ha encontrado con un Resume sin que haya error...

Cuando queramos "dejar" de detectar errores en un procedimiento, usaremos esta instrucción:

On Error Goto 0Esto no indica que se vaya a la línea "0", (aunque exista), sino que deje de detectar errores... en ese procedimiento, ya que si hay algún procedimiento de nivel superior... si que se detectará el error...

Page 368: curso básico de programación en visual basic

Veamos un ejemplo:

'

Private Sub Command6_Click()

' Esta variable se usará para mostrar mensajes en la rutina de detección

Dim Mensaje As String

' Controlamos los posibles errores

On Error GoTo HayError2 ' no puede haber dos etiquetas con el mismo nombre

' Producimos un error...

Mensaje = "Instrucción: Error 10"

Error 10

' Llamamos al procedimiento Command7_Click

Mensaje = "Instrucción: 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 instrucción

Resume Next

End Sub

Private Sub Command7_Click()

'

On Error Resume Next

Page 369: curso básico de programación en visual basic

' 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

' Este código nunca se ejecutará

MsgBox "Un mensaje desde Command7_Click" & vbCrLf & "que nunca se mostrará"

End Sub

Un detalle: Si en un mismo módulo vamos a usar varias veces On Error Goto NúmeroLínea, no podemos usar el mismo nombre para la etiqueta... si no lo tenemos en cuenta, el Visual Basic nos lo recordará.

El código de Command6_Click no debería tener ningún 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 sólo se dejan de detectar en este procedimiento... no en el resto... cosa que puede producirnos algún 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 había una rutina "superior" en la que si que se detectan los errores y... en fin... que puede que nos liemos más de lo que yo ya te estoy liando... lo dicho... si te aclaras con toda esta parrafada... ¡me lo explicas!)

Sigamos: tenemos que después de dejar de detectar los errores, producimos un error: Error 15, pero como en este procedimiento ya no hay rutina de detección de errores, (¿recuerdas? la cancelamos con el On Error Goto 0), el Visual Basic "busca" alguna rutina activa y si la hay la envía 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...

Page 370: curso básico de programación en visual basic

Una cosa más: No hay ninguna forma de crear rutinas genéricas para detección de errores... es decir no se puede crear un procedimiento genérico para detectar errores... al menos hasta la versión 6 del Visual Basic, en futuras versiones... puede ser que lo hagan... pero... eso ya se verá...

Confío que tengas claro todo esto de la detección de errores... si no es así... pues... pero para ti...

Nos vemos.Guillermo

¿Cómo aprender Asp.net MVC 4 en poco tiempo?. Curso Profesional.

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 más largo (en fechas) que... no se me ocurre ahora ningún 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 básico 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 diálogo de buscar y reemplazar y también veremos el código que habría que usar para realizar esas operaciones sobre el texto escrito.

Diálogo de Buscar y Reemplazar para el Editor

Antes de empezar a ver el código de este cuadro de diálogo, vamos a hacer unos pequeños cambios en el formulario del editor:

-- Cambia los nombre del menú de edición de mnuEditor a mnuEdicion, (es que es más lógico)

-- El código de mnuEdicion_Click debe quedar así: (después veremos porqué)

Private Sub mnuEdicion_Click(Index As Integer)

'--------------------------------------------------------------------------

Page 371: curso básico de programación en visual basic

' Usando el código del módulo MgsDBR es más cómodo (03/Jul/00)

' ya se encarga de todo...

'--------------------------------------------------------------------------

'

Set LineaEstado = lblStatus

MgsDBR.menuEdicion Index

'

End Sub

Si ya tuviésemos el código que ahora veremos, eso sería todo lo que habría que hacer para que funcionasen todas las opciones del menú de Edición... ¿fácil?, no, simple, ya que el código simplemente está escrito en otro sitio... pero escrito está... ¡que conste! y a mi me consta, que lo he escrito yo... je, je.

-- El código de comprobación que hay en el evento mnuFicSalir_Click lo he pasado al del Form_QueryUnload, para que también se pregunte si se pulsa en el botón de cerrar el formulario, (la "x" que hay arriba a la derecha)Por tanto esos dos eventos quedarían así:

Private Sub mnuFicSalir_Click()

' Terminar el programa

'

' La comprobación de si hay que guardar el fichero está en el

' evento Form_QueryUnload, para que también 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)

Page 372: curso básico de programación en visual basic

'

' Pero sólo se debería comprobar si (03/Jul/00)

' se pulsa en el botón "x" del formulario

' o si se cierra por medio de código, (con Unload)

'

Dim ret As Long

' Sólo si se cierra por medio de nuestro código o por cerrar el formulario

If UnloadMode = vbFormCode Or UnloadMode = vbFormControlMenu Then

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 botón 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 código, aunque antes, una imagen del aspecto del formulario (en tiempo de diseño) que nos servirá para buscar y reemplazar, además de para usarlo como un ImPutBox.

Page 373: curso básico de programación en visual basic

El formulario de Buscar y Reemplazar

Para que este diálogo funcione, necesitamos, además del propio formulario, el código de un módulo BAS, que es realmente el que hace casi todo el trabajo.

Veamos primero el código del formulario:

'------------------------------------------------------------------------------

' Form genérico para diálogo Buscar/Reemplazar (03/Jul/00)

' Se necesita el módulo MgsDBR.bas

'

' ©Guillermo 'guille' Som, 1996-2000

'------------------------------------------------------------------------------

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

Page 374: curso básico de programación en visual basic

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

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

Page 375: curso básico de programación en visual basic

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

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()

Page 376: curso básico de programación en visual basic

' Si no se ha especificado ningún nombre de fichero de configuración

If sFFIni = "" Then

' Asignar el nombre del fichero INI.

'

' Se podría hacer así:

'sFFIni = App.Path & "\BuscReemp.ini"

' pero si el programa es el directorio raiz, por ejemplo en C:,

' tendríamos esto: 'C:\\BuscReemp.ini' y daría error

'

' Asi que nos creamos una función 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

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 código, asumir que se ha cancelado.

If UnloadMode <> vbFormCode Then

Page 377: curso básico de programación en visual basic

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

Set gsDBR = Nothing

End Sub

Private Sub ActualizarCombo()

'-----------------------------------------------------

' Esta rutina actualiza el contenido de los dos combos,

Page 378: curso básico de programación en visual basic

' si la entrada en el Combo.Text no está, la incluye.

' Se podría 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

'

For j = 0 To n - 1

vTmp = "Entrada" & CStr(j)

sTmp = LeerIni(sFFIni, sTag, vTmp, "")

Page 379: curso básico de programación en visual basic

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 ' Ya no necesitaremos más este evento!!!

'

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

Ahora veamos el contenido del módulo: gsDBR.bas:

'

'------------------------------------------------------------------------------

' gsDBR.bas Módulo para el diálogo de Buscar y Reemplazar (03/Jul/00)

'

Page 380: curso básico de programación en visual basic

' (c)Guillermo 'guille' Som, 1997-2000

'------------------------------------------------------------------------------

Option Explicit

' Control en el que se mostrará lo que el diálogo está haciendo

' Se tendrá que usar con SET, por ejemplo: Set LineaEstado = Label1

Global LineaEstado As Control

'

' Variables y constantes globales (o públicas) para buscar/reemplazar

'

' Constantes para el menú de Edición

'

' Es recomendable tener un menú de edición 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 ' La cadena a buscar (de los textboxes)

Global sFFPoner As String ' La cadena a poner

Page 381: curso básico de programación en visual basic

'

Global iFFAccion As Integer ' Indicará que es lo que hemos hecho

' para salir del diálogo,

' ver las siguientes constantes:

'

' Constantes para la acción 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 ' Archivo de configuración

'

'

'---------------------------

' 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

' Declaración de las constantes, para usar con SendMessage/PostMessage

Global Const WM_CUT = &H300

Global Const WM_COPY = &H301

Global Const WM_PASTE = &H302

Page 382: curso básico de programación en visual basic

'

Global Const EM_CANUNDO = &HC6

Global Const EM_UNDO = &HC7

'--------------------------------------------------

' Profile.bas (24/Feb/97)

' Autor: Guillermo Som Cerezo, 1997

' Fecha inicio: 24/Feb/97 04:05

'

' Módulo genérico 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.

'----------------------------------------------------------------------------

Page 383: curso básico de programación en visual basic

Public Sub GuardarIni(ByVal lpFileName As String, ByVal lpAppName As String, ByVal lpKeyName As String, ByVal lpString As String)

' Guarda los datos de configuración

' Los parámetros son los mismos que en LeerIni

' Siendo lpString el valor a guardar

'

Dim LTmp As Long

LTmp = WritePrivateProfileString(lpAppName, lpKeyName, lpString, lpFileName)

End Sub

'----------------------------------------------------------------------------

' Función 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 parámetros son:

'lpFileName: La Aplicación (fichero INI)

'lpAppName: La sección que suele estar entrre corchetes

'lpKeyName: Clave

'vDefault: Valor opcional que devolverá

' si no se encuentra la clave.

'

Dim lpString As String

Dim LTmp As Long

Dim sRetVal As String

'Si no se especifica el valor por defecto,

'asignar incialmente una cadena vacía

Page 384: curso básico de programación en visual basic

If IsMissing(vDefault) Then

lpString = ""

Else

lpString = vDefault

End If

'Longitud máxima permitida (25/Ene/98)

'(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

sRetVal = ""

End Function

Public Function ActualizarLista(ByVal sTexto As String, cList As Control, Optional vTipoBusqueda, Optional vAddLista) As Long

'Esta función comprobará si el texto indicado existe en la lista

'Si no es así, lo añadirá

'El valor devuelto, será la posición 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

Page 385: curso básico de programación en visual basic

Const CB_SELECTSTRING = &H14D

'Constantes para las Listas

Const LB_FINDSTRINGEXACT = &H1A2 'Busca la cadena exactamente igual

Const LB_FINDSTRING = &H18F 'Busca en cualquier parte de la cadena

Const LB_SELECTSTRING = &H18C 'Busca desde el principio de la cadena

'

Dim lTipoBusqueda As Long

Dim bTipoBusqueda As Integer '0= Exacta, 1= cualquier 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 añadir o no, por defecto SI

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

Page 386: curso básico de programación en visual basic

'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 añadirla, si viene al caso...

L = -1

Else

L = SendMessage(cList.hWnd, lTipoBusqueda, -1, ByVal sTexto)

End If

'Si no está, añadirla

If L = -1 Then

If bAddLista Then

'Con el 0 se añade 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

Page 387: curso básico de programación en visual basic

'Prepara el diálogo 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 ( 6/Sep/97)

.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 acción

.Show vbModal

'Do

' .Show

' DoEvents

'Loop Until iFFAccion

End With

'Devolver la cadena a reemplazar y buscar

sBuscar = sFFBuscar

sPoner = sFFPoner

Page 388: curso básico de programación en visual basic

'Si tanto buscar como poner están 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 acción

gsReemplazar = iFFAccion

End Function

Public Function gsBuscar(sBuscar As String, Optional vModo, Optional vCaption) As Integer

'Prepara el diálogo 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

'Sólo 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

If IsMissing(vCaption) Then

sCaption = "Buscar"

Else

sCaption = CStr(vCaption)

End If

Page 389: curso básico de programación en visual basic

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 acción

.Show vbModal

'Do

' .Show

' DoEvents

'Loop Until iFFAccion

End With

'Devolver la cadena seleccionada/introducida

sBuscar = sFFBuscar

'Devolver la acción

gsBuscar = iFFAccion

End Function

Public Sub gsPedirUnValor(ByVal spuvTitulo As String, _

ByVal spuvMensaje As String, _

ByVal spuvPregunta As String, _

ByRef spuvValor As String, _

ByVal spuvBoton As String)

'--------------------------------------------------------------------------

Page 390: curso básico de programación en visual basic

' Rutina de propósito general para pedir un valor (00.22 23/May/96)

'

' Los parámetros son:

' spuvTitulo El título de la ventana

' spuvMensaje El texto a mostrar como explicación

' spuvPregunta El texto con la pregunta a realizar

' spuvValor El texto a mostrar en la caja de texto,

' también se usa para devolver la respuesta

' spuvBoton El texto a poner en el botón de aceptar

'--------------------------------------------------------------------------

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

Page 391: curso básico de programación en visual basic

End Sub

Private Sub AccionBuscar(Index As Integer)

'--------------------------------------------------------------------------

' Procedimiento genérico para realizar búsquedas (31/Ago/97)

'

' Valores "externos" necesarios:

' LineaEstado un control para mostrar mensajes temporales

' Hacer un set a una etiqueta en la que se mostrará

' el progreso de la búsqueda:

' Set LineaEstado = lblStatus

'

' Index El parámetro que apuntará a los índices

' del menú de edición que deberá tener estas opciones:

'

' Deshacer Ctrl+Z

' Cortar Ctrl+X

' Copiar Ctrl+V

' Pegar Ctrl+P

' ---(separador)

' Buscar Ctrl+B o Ctrl+F

' Buscar Siguiente F3

' Reemplazar Ctrl+H

' ---(separador)

' Seleccionar Todo Ctrl+A

'

' Estas constantes están declaradas en la enumeración emnuEdicion

'

'--------------------------------------------------------------------------

Page 392: curso básico de programación en visual basic

Static sBuscar As String

Static lngUltimaPos As Long

Dim lngPosActual As Long

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 número 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 (15/Abr/97)

Select Case Index

Case mEdBuscarActual

Page 393: curso básico de programación en visual basic

' Si hay texto seleccionado...

With tText

If .SelLength > 0 Then

sBuscar = Trim$(.SelText)

End If

End With

' Para "personalizar" la sección de búsqueda...

gsDBR.Combo1(0).Tag = "Buscar_" '& sUsuario

If gsBuscar(sBuscar, , "Buscar en el campo actual") > cFFAc_IDLE Then

sBuscar = Trim$(sBuscar)

If Len(sBuscar) Then

LineaEstado = "Buscando en el campo actual " & sBuscar & "..."

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 búsqueda en este control

If Len(sBuscar) = 0 Or lngUltimaPos = 0& Then

Page 394: curso básico de programación en visual basic

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

.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")

Page 395: curso básico de programación en visual basic

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

lngUltimaPos = lngPosActual + Len(sBuscar)

sTmp = tText

sTmp = Left$(sTmp, lngPosActual - 1) & sFFPoner & Mid$(sTmp, lngPosActual + Len(sFFBuscar))

tText = sTmp

' Si sólo 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 ratón como estaba

Screen.ActiveForm.MousePointer = vbDefault

' Salir

Exit Sub

End If

Page 396: curso básico de programación en visual basic

' Cambiar todas las coincidencias en el mísmo 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

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

Page 397: curso básico de programación en visual basic

.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...

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 edición:

If Habilitada Then

' Si no se puede deshacer, no habilitarlo

Page 398: curso básico de programación en visual basic

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

Public Sub menuEdicion(Index As Integer)

Dim sTmp As String

Select Case Index

Case mEdDeshacer

'-------------------------------------------------------------

Page 399: curso básico de programación en visual basic

' IMPORTANTE:

' En ambos casos se podría usar SendMessage,

' pero en el caso de EM_CANUNDO, NO serviría PostMessage,

' porque esta función sólo 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

Page 400: curso básico de programación en visual basic

Como te dije al principio, para usar el cuadro de diálogo, solamente hay que llamar al procedimiento menuEdicion con el índice de la acción que queremos realizar, para el caso de Buscar sería 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, tendrás que crearte tu propio código, (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 páginas, no es se usa este diálogo, 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 diálogo y usar los valores elegidos por el usuario.

Hasta aquí hemos llegado, a ver que preparo para la próxima entrega, ya que estoy un poco "liado" (entiéndase por liado: confundido, sin claridad mental... ¡jo!), sobre que es lo que pondré, ya que no me decido entre "algo" básico de tratamiento de bases de datos y empezar con el "escabroso" tema de los módulos 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

Te creías que ya no iba a seguir con el curso básico ¿verdad? Pues te has vuelto a equivocar... Sé que últimamente no soy muy "constante" en esto de las entregas del Curso Básico, pero... alguna que otra vez me dejo "ver". Y ya lo estás "viendo", aquí estamos de nuevo, con una nueva entrega del Curso Básico, el tema en esta ocasión será:

Las bases de datos (¡por fin!)

Aunque empezaremos de forma muy básica, aunque en entregas posteriores veremos cosas más avanzadas, todo se andará, así que no pierdas la esperanza de que algún siglo de estos (¿el que viene?) acabe con el dichoso Curso Básico de Programación en Visual Basic...La verdad es que tengo que darme prisa, ya que, si me descuido, la nueva versión del VB (VB.NET) estará en la calle y muchas de las cosas dichas hasta ahora no sean válidas... aunque los conceptos "básicos" seguirán siendo válidos, 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 versión que cambie "radicalmente" la forma de "pensar" del Visual Basic actual.Los ejemplos que voy a utilizar, está hechos con la versión 6.0 del VB, aunque funcionarán, (o al menos deberían funcionar), perfectamente con las versiones 4.0 y superiores... confiemos en ello.

Y ya, sin más dilación, 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, necesitarás un poco de imaginación, espero que eso no sea un problema. (Guille, aquí tendrías que haber dicho algo como... "para una mente tan inteligente como la tuya", de esa forma, le darías coba al personal y se mosquearía menos contigo, por no tener la base de ejemplo, ¿cuando vas a

Page 401: curso básico de programación en visual basic

aprender?)

Después del comentario del "otro Guille", empecemos con:

El diseño del formulario

Hay quién se queja de que no explico de forma "simplona" cómo crear los formularios y añadir los controles que se usan, en esta ocasión, "intentaré" hacerlo... a ver si lo consigo.

Crea un nuevo proyecto normal, automáticamente se añadirá 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, verás que en la barra de "caption" del formulario se muestra lo escrito.Ahora vamos a añadir 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 versión inglesa del Visual Basic, así que si las traducciones son erróneas... échale un poco de imaginación...) En el menú Ver (View), pulsa en la opción Barra de Herramientas (Toolbox).Para añadir cualquiera de los controles que están en la mencionada barra de herramientas, simplemente tienes que hacer una doble pulsación (doble-click) en el icono deseado y se añadirá al formulario. En este caso, se añadirá un Data Control

llamado Data1 y el aspecto en el formulario será este: Selecciónalo (aunque ya debe estar seleccionado) y arrástralo, (es decir: deja pulsado el botón derecho mientras lo mueves), hasta la parte superior del formulario, (para dejar espacio libre al resto de controles que añadiremos a continuación)

Configurando el Data Control (o Control Data)

Ahora vamos a indicarle al Data1 dónde está la base de datos que queremos usar.Selecciona el Data1 que hemos añadido al formulario, simplemente haz un "click" en dicho control, (debería seguir seleccionado, salvo que hayas pulsado con el ratón 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 botón con los tres puntos suspensivos que indican que se mostrará un diálogo, (esto último es una "convención", que nos indica que cuando se seleccione esa opción, en este caso al hacer click en el botón, se mostrará un cuadro de diálogo), 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, tendrás 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 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 conexión, 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

Page 402: curso básico de programación en visual basic

RecordSource, verás que en la cuadrícula 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 añadir otros controles con los cuales poder mostrar la información contenida en dicha tabla de autores.

Vamos a añadir 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 ratón sobre dicho "icono", verás que te muestra un ToolTip con el texto Label), se añadirá 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 operación dos veces más, (posiciona cada una de las etiquetas debajo de la anterior), para obtener un total de tres etiquetas: Label1, Label2 y Label3 respectivamente.Para añadir las tres cajas de texto que necesitamos, repite la operación, pero en este

caso el control que debes elegir de la barra de herramientas es: (TextBox). Alinéalos a la derecha de cada una de las etiquetas anteriores. Ahora tendremos también estos tres controles: Text1, Text2 y Text3, el aspecto del formulario sería el siguiente:

Vamos a cambiar el tamaño 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 ratón, arrastra hacia arriba desde la parte inferior del control (al seleccionar se muestran unos cuadros que indican que puedes cambiar el tamaño arrastrando en cualquiera de ellos, según la posición de dicho "cuadro" servirá para el ancho, alto o ambos)2.) Escribiendo dicho tamaño en la ventana de propiedades, en este caso en la propiedad Height.

Para que las tres etiquetas tengan el mismo tamaño, podemos hacerlo también de dos formas, para que se cambien de tamaño todas las etiquetas a un mismo tiempo, en lugar de ir cambiado cada una de ellas por separado:

Page 403: curso básico de programación en visual basic

1.) Selecciona las tres etiquetas y escribe el tamaño 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 ratón), abre el menú Formato (Format) y selecciona la opción Hacer del mismo tamaño (Make same size), de dicho menú, selecciona Altura (Height). En este procedimiento, es importante que el último control seleccionado sea el que indique el tamaño con el que queremos igualar el resto de los controles.

Seguramente todo esto ya lo sabrás, pero... (de alguna forma hay que hacer que esta entrega sea más larga, ¿verdad Guille?)

A continuación vamos a cambiar el tamaño de las cajas de texto:Selecciona Text1 y haz que el ancho sea: 2895.Selecciona los otros dos controles e iguálalos en el ancho... imagínate cómo...

El aspecto final será este otro:

Una vez diseñado el "aspecto" del formulario, (eres libre de adecuarlo a tus gustos particulares), vamos a indicarle al Visual Basic que nos muestre información 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 Año nacimiento: para el Label3.

¡Y ya está!

Page 404: curso básico de programación en visual basic

Pulsa F5 para ejecutar el proyecto y verás que se muestra el primer registro, (seguramente en el año 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, también se modificará en la base de datos.

Fíjate que no hemos usado ni una línea de código, entre otras cosas porque todo lo hemos realizado en tiempo de diseño.

Pero ahora vamos a ver cómo hacer que todo esto funcione igual, pero en lugar de hacerlo en tiempo de diseño, lo haremos en tiempo de ejecución, es decir: al ejecutar el proyecto, aunque la asignación de la propiedad DataSource de las cajas de texto hay que hacerlo en tiempo de diseño, ya que no se puede hacer en tiempo de ejecución.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, añade un formulario, en éste añade 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, además de asignar el tamaño, tanto de las cajas de texto como de las etiquetas.

Ya deberías saber que cuando se ejecuta una aplicación 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 código, para poder introducir este código, 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"

Page 405: curso básico de programación en visual basic

Text3.DataField = "Year Born"

End Sub

Pulsa F5 y verás que también funciona.

Para ir abriendo boca de lo que seguirá, vamos a añadir una caja de texto en la cual se podrá escribir un número y al pulsar INTRO se mostrará el autor que tenga ese número como Au_ID, para ello añade una nueva caja de texto, no asignes el DataSource, ya que este texbox no estará ligado a la base de datos.La posición y el tamaño 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 número y se hará una búsqueda en el contenido del Data1, aquí te muestro el código:

'

Private Sub Text4_KeyPress(KeyAscii As Integer)

' Se buscará sólo 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 asignación evita que suene un BEEP

KeyAscii = 0

' Convertir el contenido de TextBox en un número

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 búsqueda 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 número 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...

Page 406: curso básico de programación en visual basic

Ahora vamos a buscar en el campo Autor.

A diferencia del campo Au_ID, que es numérico y sólo buscamos una coincidencia "exacta", el campo Autor es del tipo String y en él podríamos 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 habrás comprobado, la información del autor se muestra en el formato Apellido, Nombre, por tanto si queremos buscar el nombre sería más bien complicado, ya que lo que buscamos está al final...Para ello podemos usar los signos de comodines ? (interrogación) y * (asterisco).-El primero de ellos sirve para indicarle que no tenga en cuenta el caracter que esté en la posición indicada por la interrogación, 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 después.Cuando busquemos datos exactos la comparación la haremos con el signo igual (=), pero si queremos usar la búsqueda con comodines no nos servirá el signo igual, ya que hay que indicarle que queremos usar una comparación más versátil, en este caso usaremos LIKE.Hay que saber que al buscar en campos del tipo String (cadenas de caracteres), hay que usar los apóstrofes para indicar el principio y final de la cadena buscada, si no lo hiciéramos, se produciría un error.Después de lo dicho, el código de búsqueda quedaría de la siguiente forma:

'

Private Sub Text4_KeyPress(KeyAscii As Integer)

' Se buscará sólo 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 asignación evita que suene un BEEP

KeyAscii = 0

' Convertir el contenido de TextBox en un número

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

Page 407: curso básico de programación en visual basic

End Sub

Fíjate que no he usado ningún comodín, por tanto tendrás que escribirlos tú, por ejemplo, para buscar la primera Jane, tendrás 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 verás que se muestra el registro número 1242, (Baer, J)para terminar estas pruebas, escribe J* y en esta ocasión se mostrará el registro 1, (Jacobs, Russell)

Y ahora un ejercicio:Añade dos controles Options para que podamos buscar tanto por el número del ID como por el nombre del Autor, el aspecto del formulario sería este:

El Option1 será el indique que se busque por el ID y Option2 será para buscar por el campo Author.

La solución en la próxima entrega.

Bueno, hasta aquí ha llegado esta primera entrega referente al acceso a datos.En futuras entregas veremos cómo realizar búsquedas (o consultas), así como otras cosas más, entre ellas acceder a una base de datos sin el Data Control y también al acceso a bases de datos del tipo ADO (activeX Data Objects).Pero eso será en otra ocasión, hasta entonces, ¡que lo programes bien!

Nos vemosGuillermoP.S.Si quieres bajarte los ejemplos usados (y la solución al ejercicio), pulsa este link. (basico34_cod.zip 4.35 KB)

La página con las soluciones de esta entrega.

Page 408: curso básico de programación 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 también se que usar este control es más fácil que usar código directo, aunque esto último también lo vamos a ver a lo largo de este curso básico, 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 añadirle algunas opciones nuevas, para añadir 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. También vamos a usar la opción de buscar que puse como ejercicio, por tanto, si no te has leído las soluciones de la entrega 34, es conveniente de que le eches un vistazo.

Vamos a empezar por mejorar la búsqueda en la base de datos:

Buscar en una base de datos.

En esta ocasión vamos a añadir un botón 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, según el parámetro recibido, buscará la primera coincidencia o seguirá buscando desde el último dato hallado.

Antes de añadir un nuevo botón, vamos a modificar el código actual para usar el nuevo procedimiento Buscar.

Crea un nuevo procedimiento, en el menú Herramientas (Tools), selecciona Añadir procedimiento..., llámalo Buscar y haz que sea privado, ya que no tiene ningún sentido que sea público, porque sólo se usará desde el formulario.También puedes copiar y pegar el siguiente código:

'

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 ' en el campo Au_ID

' Convertir el contenido de TextBox en un número

nReg = Val(Text4)

'

Data1.Recordset.FindFirst "Au_ID = " & nReg

End If

If Option2.Value Then ' en el campo Author

'

Page 409: curso básico de programación en visual basic

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 código que había anteriormente en ese evento y sustitúyelo por este otro:

'

Private Sub Text4_KeyPress(KeyAscii As Integer)

' Se buscará sólo cuando pulsemos INTRO

'

' Comprobar si la tecla pulsada es Intro: vbKeyReturn o 13 que es lo mismo

If KeyAscii = vbKeyReturn Then

' Esta asignación evita que suene un BEEP

KeyAscii = 0

' Llamamos al procedimiento Buscar:

Buscar

End If

End Sub

Pruébalo, para que veas que todo funciona como antes.

Ahora, vamos a añadir un botón 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á más lógico que el hecho de tener que pulsar Intro.Por tanto, añade un nuevo botón, (no te preocupes por ahora dónde colocarlo en el formulario, ya lo haremos dentro de poco), cámbiale el nombre a cmdBuscar y el Caption a Buscar, (por defecto será Command1) y escribe o añade este código (también 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.

Page 410: curso básico de programación en visual basic

Creo que ya es hora de que nos vayamos complicando la vida...

Vamos a añadir un botón para seguir buscando a partir del último dato hallado:Añade un nuevo botón, llámalo cmdBuscarSig y en el Caption pones: Buscar siguiente, (seguramente el texto lo escribirá en dos líneas, pero no te preocupes).El código a usar en el evento Click de ese nuevo botón sería prácticamente el mismo que en el de Buscar, aunque antes debemos añadir 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

' Convertir el contenido de TextBox en un número

nReg = Val(Text4)

'

Data1.Recordset.FindNext "Au_ID = " & nReg

End If

If Option2.Value Then ' en el campo Author

'

Data1.Recordset.FindNext "Author Like '" & Text4.Text & "'"

End If

End Sub

Si te fijas, el código es prácticamente 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 quedaría ese evento:

Private Sub cmdBuscarSig_Click()

' Buscar el siguiente registro

BuscarSiguiente

End Sub

Page 411: curso básico de programación en visual basic

Como hemos visto, el código usado en los dos procedimientos de búsqueda son prácticamente iguales, así que vamos a unificarlos para crear un sólo procedimiento de búsqueda, de esta forma, refrescarás tu memoria y sabrás algo más de parámetros en procedimientos, así como parámetros opcionales.

Parámetros opcionales.

Como sabrás, (y si no lo sabes, te lo cuento yo ahora), a partir de la versión 4 de Visual Basic se pueden usar parámetros opcionales en los procedimientos y funciones.Esto quiere decir que podemos usar el procedimiento de varias formas, indicando todos los parámetros o sólo los que realmente son necesarios.Por ejemplo, en el caso en que estamos ahora, el procedimiento Buscar podría tener un parámetro opcional para indicarle si es la primera búsqueda o la siguiente.No voy a entrar en demasiadas explicaciones, ya que este tema lo veremos de forma más 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 ocasión voy a usar el formato de las versiones 5 y 6, en estas versiones los parámetros opcionales pueden ser de un tipo de datos diferente a Variant y también pueden indicársele un valor por defecto, para que, si no se especifica, tenga el valor indicado; por supuesto, si no le indicamos el valor, tendrán 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 parámetros con tipo, a diferencia de los parámetros del tipo Variant, no se puede usar IsMissing para comprobar si el parámetro se ha especificado o no, pero, eso es otro tema...

Este es el código del procedimiento Buscar y a continuación te indico cómo llamarlo desde el evento Click del botón 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 ' en el campo Au_ID

' Convertir el contenido de TextBox en un número

nReg = Val(Text4)

'

Page 412: curso básico de programación en visual basic

' 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 ' en el campo Author

'

' Si se busca el siguiente dato

If Siguiente Then

Data1.Recordset.FindNext "Author Like '" & Text4.Text & "'"

Else

Data1.Recordset.FindFirst "Author Like '" & Text4.Text & "'"

End If

End If

End Sub

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 código 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 código, te darás 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 posición 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 próximo registro, desde el último hallado, que contenga dicho texto, (estas pruebas hay que hacerlas en el campo Author, ya que no tienen ningún sentido hacerlo en el ID del autor). Para buscar el resto de autores con el nuevo texto, tendrás que pulsar en Buscar para que empiece a buscar desde el principio.

Page 413: curso básico de programación en visual basic

Fíjate en el código, 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 parámetros)Esto último: lo de no especificarse o si se especifica con el valor False, es algo que hay que tener en cuenta si el parámetro fuese de tipo Variant (cosa obligatoria si usas VB4), ya que IsMissing sólo nos informa si el parámetro no se ha especificado, pero si se especifica, puede hacerse con un valor False o True, por tanto, también habría que tenerlo en cuenta... Ya sé que dije que lo iba a dejar para otra entrega... pero, que haces si estás usando VB4 ¿dejar aquí el curso?Así que vamos a ver el código 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 parámetro 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 parámetro

Dim Siguiente As Boolean

'

Dim nReg As Long

'

' Asignar correctamente el valor del parámetro 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 código es igual que antes

Page 414: curso básico de programación en visual basic

' Buscar la primera coincidencia en el recordset del Data1

'

If Option1.Value Then ' en el campo Au_ID

' Convertir el contenido de TextBox en un número

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 ' en el campo Author

'

' Si se busca el siguiente dato

If Siguiente Then

Data1.Recordset.FindNext "Author Like '" & Text4.Text & "'"

Else

Data1.Recordset.FindFirst "Author Like '" & Text4.Text & "'"

End If

End If

End Sub

Fíjate que he cambiado el nombre del parámetro 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 parámetro, la comprobación de IsMissing se cumple, por tanto aquí le indicaremos el valor que queramos que tenga por defecto, en nuestro ejemplo no es necesario asignar ningún valor, ya que False es el valor que tendrá la variable Siguiente si no le asignamos ningún valor.Por tanto en este ejemplo, podríamos haber hecho sólo 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.

Una vez visto el procedimiento de búsqueda, vamos a añadir dos nuevos botones para

Page 415: curso básico de programación en visual basic

Añadir 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.

Añade dos nuevos botones y asignales los nombres cmdAdd y cmdBorrar, así como los captions Añadir y Eliminar, el código será el siguiente:

'

Private Sub cmdAdd_Click()

' Añadir un nuevo registro

Data1.Recordset.AddNew

End Sub

Private Sub cmdBorrar_Click()

' Eliminar el registro actual

Data1.Recordset.Delete

End Sub

Tanto cuando se añade como cuando se borra, se debería mover el registro actual para que los cambios tengan efecto en la base de datos, ya que si se añade un nuevo registro y el mismo no se actualiza, se pierde.Para probarlo, ejecuta el proyecto, pulsa en Añadir y directamente sin hacer nada más cierra el ejecutable, (pulsando en la x del formulario o ventana).Ejecuta de nuevo el proyecto y si pulsas en el botón de ir al último registro del Datacontrol, verás que no hay ningún registro nuevo.Ahora vuelve a pulsar en Añadir y escribe lo que quieras en las cajas de texto del Nombre y Año de nacimiento y pulsa en el botón de ir al primer registro y después al último (ya sabes que me refiero a los botones del Datacontrol), verás que ahora si se muestra dicho registro y si pulsas en el botón de ir al anterior, verás que se salta un número... ese es el que había reservado para nosotros, pero al no actualizar los datos, se perdió en el limbo...

¿Será por estos detalles que algunos programadores prefieran usar código 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 más 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 perdería)En el caso de Eliminar, vamos a movernos al primer registro, para que el registro activo sea uno con información.Este sería el código de los eventos Click de los dos botones:

'

Page 416: curso básico de programación en visual basic

Private Sub cmdAdd_Click()

' Añadir un nuevo registro

Data1.Recordset.AddNew

' Añadimos algún 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

' (también podriamos haberlo movido al último registro)

Data1.Recordset.MoveFirst

End Sub

Toma nota de la forma en que se haría para mover al primer o al último registro del Recordset.También habría que tener en cuenta si hemos sobrepasado el principio o el final de los registros, cosa que ocurre cuando no hay más 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 sólo habría que hacerlo al eliminar registros, ya que al añadir se supone que habrá al menos el que añadimos...

'

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

'

Page 417: curso básico de programación en visual basic

' Movemos al primer registro para que los cambios se hagan permanentes

' (también podriamos haberlo movido al último registro)

Data1.Recordset.MoveFirst

End If

End Sub

Y esto es todo por hoy.En la próxima entrega veremos cómo acceder a bases de datos usando el ADO datacontrol.

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

En esta entrega veremos cómo 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 pequeños cambios, para usar el tipo de acceso a bases de datos ADO, en lugar de DAO.Me imagino que habrás leído por ahí las diferencias entre ADO y DAO, cosa que aquí no voy a hacer... porque si lo hiciera, seguramente acabaría aburriendo hasta... a mi mismo. Así que pasaré a las explicaciones básicas de cómo usarlo, que es la intención de este curso básico, aunque, cuando lo crea conveniente, profundizaré en los temas que crea conveniente, así que... vamos al tema y veamos...

Cómo 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á acompañado de la versión del control, en mi caso me indica que es 6.0 (SP4) (OLEDB)-Cuando pulses en aceptar, para cerrar el cuadro de diálogo de añadir componentes, te mostrará un nuevo objeto en la barra de herramientas. Asegúrate de que tienes abierto el formulario y haz doble-click en dicho control, de esta forma se añadirá 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 código, así que no voy a explicarte cómo hacerlo desde el IDE de Visual Basic.Añade el siguiente código al evento LOAD del formulario:

' Indicar el path correcto de la base de datos

' ¡ACUERDATE DE PONER EL PATH CORRECTO!

Page 418: curso básico de programación en visual basic

Const sPathBase As String = "C:\Program Files\Microsoft Visual Studio\VB98\BIBLIO.MDB"

'

' Crear la conexión manualmente

' Con "Provider=Microsoft.Jet.OLEDB.4.0;" se permite abrir bases de datos de Access 2000

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 están realizadas con el Visual Basic 6.0 y el Service Pack 4 (SP4). Si el código anterior te produjera un error, primero comprueba que está escrito correctamente (hay que empezar por lo más simple), si sigue sin funcionar, prueba a cambiar el 4.0 que está entre comillas por 3.51, si aún así tampoco funciona... prueba a instalar el ADO, creo que se instala automáticamente 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 también que pases de esta entrega...

Te explico un poco el código anterior.La constante es para que sea fácil 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 método .ConnectioString, según dice la ayuda del Visual Basic: Contiene la información que se utiliza para establecer una conexión a un origen de datos. Osea, es lo que le dice al ADO datacontrol de que base de datos obtener los datos y también 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 cuestión, (fíjate 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

Page 419: curso básico de programación en visual basic

también podríamos haberle indicado una consulta, pero esto último lo veremos en otra ocasión.

Ahora vamos a añadir unos cuantos controles al formulario, para poder trabajar con los datos manejados por el ADO datacontrol.Los controles que usaremos serán 3 etiquetas y 3 cajas de texto, pero ambos serán un array de controles, (en el ejemplo de las entregas 34 y 35 usamos controles independientes, pero creo que es más cómodo usar array de controles).

Cómo crear un array de controles.

A estas alturas ya deberías saber cómo hacerlo, pero te voy a refrescar la memoria.Añade una etiqueta al formulario (se creará Label1).Selecciónala, (simplemente haz click en ella)Del menú Edición, selecciona Copiar, (también puedes hacerlo con el botón derecho del ratón).Del menú Edición, selecciona Pegar, se mostrará un mensaje preguntando si quieres crear un array de Label1, contesta que si.Vuelve a pegar (Edición/Pegar)Y ya tienes tres etiquetas con índices que van desde cero a dos.Añade un TextBox y haz la misma operación que con la etiqueta: Selecciónalo; cópialo; pégalo, contesta que SI al mensaje de que quieres crear un array; vuelve a pegar. Al final tendrás tres cajas de Texto llamados Text1 con índices de cero a dos.Posiciona los controles de forma que las etiquetas estén alineadas con las cajas de texto, no te preocupes por el Caption de las etiquetas, ya que lo modificaremos desde código.

Añade unos cuantos controles más, para que podamos hacer búsqueda, etc. tal y como se hizo en las dos entregas anteriores.Al final deberíamos tener los siguientes controles:

Control Índices del array (o ninguno si no es un array)

Label1 0 a 2Text1 0 a 2Label2 Ninguno (no es un array)Text2 Ninguno (no es un array)Option1 Ninguno (no es un array)Option2 Ninguno (no es un array)cmdBuscar Ninguno (no es un array)cmdBuscarSig Ninguno (no es un array)cmdSalir Ninguno (no es un array)cmdAdd Ninguno (no es un array)cmdBorrar Ninguno (no es un array)Adodc1 Ninguno (no es un array)

Y en esta imagen tienes una idea de cómo están dispuestos:

Page 420: curso básico de programación en visual basic

El formulario de prueba de la entrega 36

Aquí te muestro el código usado para que funcione este invento, como comprobarás, es prácticamente el mismo que el usado en las entregas anteriores, con unas cuantas añadiduras para que funcione correctamente con el ADO datacontrol. Por tanto creo que no necesita más explicaciones que las que se incluyen en el propio código.El código del procedimiento Buscar es el que tiene más 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 sólo existe Find.También 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 código.

'

'------------------------------------------------------------------------------

' 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,

Page 421: curso básico de programación en visual basic

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 parámetro

Adodc1.Caption = "Registro actual: " & pRecordset.AbsolutePosition

' Si da error, indicarlo (20/Sep/99)

If Err Or pRecordset.BOF Or pRecordset.EOF Then

Adodc1.Caption = "Ningún registro activo"

' Habría que moverlo a un registro con información

' *** Dejarlo comentado ***

' para que el procedimiento de búsqueda avise si no hay datos activos

'Adodc1.Recordset.MoveFirst

End If

Err = 0

End Sub

Private Sub cmdAdd_Click()

' Añadir un nuevo registro

Adodc1.Recordset.AddNew

' Añadimos algún 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

Page 422: curso básico de programación en visual basic

Adodc1.Recordset.MoveLast

End Sub

Private Sub cmdBorrar_Click()

' Borrar el registro actual

' Se comprueba que haya algún 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 = "Ningún registro activo"

Else

' Eliminar el registro actual

Adodc1.Recordset.Delete

'

' Movemos al primer registro para que los cambios se hagan permanentes

' (también podriamos haberlo movido al último registro)

Adodc1.Recordset.MoveFirst

End If

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()

Page 423: curso básico de programación en visual basic

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 conexión 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 ejecución

Dim i As Long

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 & ":"

Page 424: curso básico de programación en visual basic

Next

End Sub

Private Sub Text2_KeyPress(KeyAscii As Integer)

' Se buscará sólo 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 asignación 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 detección de errores

On Error Resume Next

'

' Buscar la primera coincidencia en el recordset del Data1

If Option1.Value Then

' Convertir el contenido de TextBox en un número

nReg = Val(Text2)

' en el campo Au_ID

sADOBuscar = "Au_ID = " & nReg

Page 425: curso básico de programación en visual basic

End If

If Option2.Value Then

' en el campo Author

sADOBuscar = "Author Like '" & Text2.Text & "'"

End If

' Guardar la posición 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 más datos que mostrar."

' Posicionar el recordset en la posición guardada

Adodc1.Recordset.Bookmark = vBookmark

End If

End Sub

Y con esto acabamos la introducción 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 módulo de código: las clases. Así que... prepárate para lo que viene.

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

Page 426: curso básico de programación en visual basic

Con la futura versión 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 año 2001, esté en el mercado), es conveniente de que sepas de que va todo esto de las clases, no me refiero a "dar clases", (enseñar a alguien), sino a los módulos de clases, esos ficheros con la extensión .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 cómo usarlas en nuestras aplicaciones, ya que pueden aliviarnos la tarea de crear y re-usar código; pero lo más 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 código que las usan... (no voy a adelantarme, así que, tendrás que esperar un poco para que realmente puedas entender de que estoy hablando).

Nota:Como ya comenté en la entrega 30, la versión, (o versiones), de Visual Basic que hace mejor uso de las clases, sobre todo de las colecciones, es la versión 5, (y mejor aún la 6, aunque con la versión 5 será más que suficiente), por tanto, si estás usando una versión 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, confío en que todo lo que explique te quede claro, incluso si usas la versión 4, ya que con versiones anteriores, no podrás usar los módulos 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 versión 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 versión de VB: la VB5CCE (Visual Basic 5 Control Creation Edition), la puedes encontrar en la siguiente dirección: 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 dirección indicada no lo encuentres, así que tendrás que buscar en el sitio de Microsoft por "Control Creation Edition" y ¡suerte!También te puedes pasar por la página VB-Resumen de mi sitio y allí seguramente el link esté más actualizado.

Ya sin más preámbulos, vamos a empezar con el tema que nos interesa:

Los módulos de clases

Seguramente habrás oído hablar sobre Programación Orientada a Objetos, (OOP Object Oriented Programing), Herencia, Polimorfismo, Encapsulación o simplemente que existen un tipo de fichero en Visual Basic, (a partir de la

Page 427: curso básico de programación en visual basic

versión 4 inclusive), llamado módulos de clases, (esos fichero tienen la extensión .cls)

Nota:Aunque no lo sepas, ya has estado trabajando con clases, ya que los formularios son realmente módulos de clases de un tipo especial.

Hasta ahora, todo el código 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 código de la entrega anterior usábamos el procedimiento Buscar para realizar una búsqueda 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 módulo BAS y cómo podíamos acceder a unas variables y a un procedimiento declarados con el "modificador" Public. También vimos cómo, podíamos acceder a variables con el mismo nombre declaradas en el formulario y el módulo BAS, simplemente indicando el nombre del módulo delante de la variable en cuestión, 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 título recordatorio, te diré que la visibilidad es el ámbito que tienen las variables y los procedimientos, es decir en que parte del código son accesibles (visibles) y en cuales no.

Para usar los módulos de clases es muy importante tener estos conceptos claros, ya que de no ser así, vas a estar más perdido que una chiva en un garaje y no te enterarás de nada... te aviso.

Clases = Objetos (y viceversa)

Como te decía antes, sin saberlo, has estado usando objetos todo el tiempo. Un formulario es un tipo especial de clase, un botón, (CommandButton), es un objeto, (internamente es una clase). Todos los objetos tienen la característica de tener sus propios métodos, propiedades e incluso eventos.Por ejemplo, el mencionado botón, tiene propiedades, como Caption, Visible, etc, También tiene eventos como Click, MouseMove, etc. Y también tiene métodos, por ejemplo Refresh, Move, etc. Lo mismo ocurre con los formularios, también tienen métodos, propiedades y eventos. Pero todos estos objetos ya existen, simplemente los usamos y ya está.Lo que en esta entrega (y en otras más) aprenderemos es cómo poder crear nuestros propios objetos, con sus métodos, propiedades y eventos, y todo ello gracias a los módulos de clases.

Nota:En el resto de esta entrega, usaré tanto la palabra objeto como la palabra clase, estas se referirán tanto a los objetos ya existentes, (más comúnmente conocidos como controles), como a los que nosotros podamos crear, ya sean tanto objetos de código como controles propiamente dicho, (en futuras entregas aprenderemos a crear nuestros propios controles ActiveX u OCX). Sólo espero que no te confundas.

Clases/Objetos = Encapsulación:

Page 428: curso básico de programación en visual basic

La ventaja de usar clases en nuestros proyectos es que podemos encapsular todo el código y tratarlas como si de cajas negras se tratasen, es decir, sabemos que es lo que hacen, cómo usarlas, pero no es imprescindible saber el código que se usa para que todo funcione.Por tanto una de las características principales que un objeto (o clase) debe tener es que no dependa del exterior, es decir, que sea autosuficiente: todo la información que necesite se debe suministrar mediante las propiedades que dicha clase u objeto exponga al exterior, (mediante las propiedades públicas) y de igual manera, toda la información que un objeto deba mostrar al mundo exterior, se haga mediante las propiedades, métodos y eventos que dicha clase exponga al exterior, (declarados como públicos).

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 añadir un módulo de clase:Abre el menú Proyecto y pulsa en: Añadir Módulo de Clase, te mostrará un cuadro de diálogo mostrándote varias tipos de módulos de clase, pulsa en la primera que te muestra: Módulo de Clase, pulsa en Abrir y se añadirá un nuevo fichero, si te fijas en la ventana de propiedades, tendrá como nombre: Class1, (al menos así es como se llama en la versión 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, más 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:También 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" sólo 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, además de un método que nos indicará que edad tiene dicho colega.Veamos cómo añadir propiedades y métodos a una clase:

Añadir propiedades a una clase (u objeto):

Para crear las propiedades, podemos hacerlo de dos formas:

1. La forma más simple, es declarando las propiedades como variables

públicas: Cuando declaramos una variable Public en un módulo de

clase, dicha variable se convierte en una propiedad.

2. La otra forma es creando un procedimiento Property.

Page 429: curso básico de programación en visual basic

La ventaja de usar los procedimientos Property, es que podemos hacer comprobaciones extras que con las variables sería imposible hacer.Por ejemplo, al asignar un valor a la propiedad e-mail, se podría comprobar que dicho valor contenga el signo arroba (@); en el caso la fecha de nacimiento, se podría 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 más, no se comprueba nada de nada, simplemente se asigna, sin embargo al usar un procedimiento para hacer dicha asignación 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 declarándola Private, de esta forma esa variable sólo es visible dentro del código del módulo de clase. ¿Recuerdas lo que te dije del ámbito o visibilidad de las variables?

Veamos el código que se podría usar para implementar las propiedades de la clase cColega:

Para el nombre del colega, simplemente declaramos una variable pública:Public Nombre As String

Para el e-mail crearemos una variable privada para guardar internamente la dirección de e-mail:(como no podemos usar el signo - en el nombre de una variable, tendremos que usar email, sin signo de separación)Private m_email As Stringm_ es para indicar que es una variable declarada a nivel de módulo y por tanto visible en todo el módulo en el que se ha declarado.Ahora tenemos que crear el procedimiento Property. Éste podemos crearlo de dos formas, usando la opción Añadir procedimiento... del menú de Herramientas o escribiendo el código directamente... vamos a usar el primer método:Selecciona Añadir Procedimiento del menú Herramientas, te mostrará un cuadro de diálogo preguntando que tipo de procedimiento quieres crear y con que nombre. Selecciona la opción Property (Propiedad), en Ámbito (Scope) selecciona Public y escribe email en la caja de texto.Se crearán dos nuevos procedimientos:

Public Property Get email() As Variant

End Property

Public Property Let email(ByVal vNewValue As Variant)

End Property

Page 430: curso básico de programación en visual basic

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 comprobación de que se asigna un valor que representa una dirección de correo electrónico, (realmente sólo 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 dirección de correo electrónico."

.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

Page 431: curso básico de programación en visual basic

End If

End Property

En este procedimiento se comprueba que la cadena tenga el signo arroba, si no es así, la comparación 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 detectaría en la aplicación 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 código 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 lógico sería que el tipo de datos fuese Date, el problema o la ventaja, según se mire, es que si asignamos un valor erróneo 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 erróneo dentro del procedimiento o bien usar algún tipo de conversión "inteligente" de una fecha que supuestamente es errónea, (por ejemplo: permitir introducir la fecha con distintos signos de separación y asignarlo en el formato que internamente queramos), entonces lo mejor sería 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 DateEl 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 más fácil de "codificar" ese procedimiento sería 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

Page 432: curso básico de programación en visual basic

.Description = "El valor asignado a FechaNacimiento no es una fecha válida."

.Source = "cColega.FechaNacimiento = " & NewValue

End With

Else

m_FechaNacimiento = NewValue

End If

End Property

Esta otra es algo más complicada, en ella se permite aceptar fechas en diferentes formatos así como también 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

Page 433: curso básico de programación en visual basic

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

If Len(NewValue) = 5 Then

' Si es igual a 5 caracteres, es que falta el año

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)

'

Page 434: curso básico de programación en visual basic

' 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 año

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 más 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

.Description = "El valor asignado a FechaNacimiento no es una fecha válida."

.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 módulo, no solo a los de clases, (por ejemplo, en ciertas ocasiones es bastante útil usar procedimientos Property en los formularios).Ahora vamos a ver cómo podemos usar esta clase en un proyecto y poder comprobar cómo se detectan los posibles errores si la asignación 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 atención a lo que sigue... si aún asi

Page 435: curso básico de programación en visual basic

no consigues entenderlo... apúntate a algún curso de meditación trascendental y después sigue leyendo...

Cómo usar las clases, (objetos), en un proyecto.(Ahora viene el meollo de la cuestión)

Si los procedimientos que acabamos de crear estuviesen en un módulo BAS, los usaríamos sin más, pero, como dichos procedimientos están "incluidos" en un módulo de clase, o en un objeto, (según prefieras llamarlo), antes tenemos que tener una variable que sea del mismo tipo que dicho objeto y además crearlo. Ya que los procedimientos, (Sub, Function o Property), declarados en un módulo de clase sólo 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 públicas declaradas en un módulo de clase "pertenecen" a dicha clase y se convierten en propiedades y métodos de dicha clase y por tanto sólo son accesibles gracias a que dicha clase "existe".

Veamos cómo crear una variable del tipo cColega:Dim oColega As cColegaEsto simplemente le indica al Visual Basic que la variable oColega es del tipo de cColega, pero nada más.Si intentáramos usarla, nos daría 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.

Para clarificar un poco las cosas: (si es que todo esto se puede clarificar...)Cuando añadimos un control a un formulario, Visual Basic crea una variable, (con el nombre que le hemos dado a dicho control), y de forma automática, (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, después 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 más la cosa, que bastante complicada está ya...), Set oColega = New cColegaEsto 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 estaríamos asignando a dicha variable sería el contenido de la propiedad por defecto.

Por ejemplo, para guardar en una variable el control Text1, haríamos esto:

Page 436: curso básico de programación en visual basic

Dim unTextBox As TextBoxSet unTextBox = Text1Pero para guardar en una variable el contenido de la propiedad por defecto de Text1, (es decir el contenido de la propiedad Text), haríamos esto otro:Dim sUnTexto As StringsUnTexto = Text1Que es lo mismo que hacer: sUnTexto = Text1.Text

Seguramente dirás... ¿tan complicado es de entender? Si está "chupao"Me gustaría creer que no te ha parecido tan complicado y que lo has entendido al 100%, pero... ¡No te preocupes! ¡Te aseguro que acabarás entendiéndolo! ya que si no lo haces, te quedarás estancado con el Visual Basic 6 y no podrás disfrutar de lo que la nueva versión ofrece... je, je, je... así que... si no lo has entendido... ponte las pilas y aplícate al máximo.

Veamos ahora el código del formulario con el código para usar la clase.Este formulario tiene tres etiquetas, tres cajas de texto: Text1(0), Text1(1) y Text1(2) un botón 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

'

' 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

Page 437: curso básico de programación en visual basic

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 código

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 cColegaEsto lo explico, entre otras cosas, para que sepas que se puede hacer, ya que seguramente lo habrás visto en los ejemplos que acompañan 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 más lo volveré a hacer... e incluso te pediría que me dijeras en que listados, (siempre que sean míos), has visto esa forma de declarar las clases, para que pueda corregirlo...

Te recomiendo y te rogaría, (y si pudiera, te obligaría), encarecidamente de que NUNCA crees clases de esta forma.

Page 438: curso básico de programación en visual basic

¿Por qué no es recomendable crear las clases de esta forma, si es más fácil e incluso más lógico?Porque crear una clase usando New al mismo tiempo que se declara un objeto añade más código, (aunque nosotros no lo veamos), además de que no tenemos el control total sobre cuando se crea el objeto, ya que el objeto se crea cuando intentamos usarlo, no cuando "explícitamente" le indicamos que lo cree...¿Por qué añade más código?Porque Visual Basic nunca sabe si el objeto está creado o no, así que, junto a cada acceso al objeto añade una comprobación 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 duración 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.

¿Cómo podemos destruir explícitamente 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 también a los formularios, ya que, como dije al principio, los formularios también son también clases u objetos, con un tratamiento diferente, pero clases al fin y al cabo.

Para muestra un botón.Crea un nuevo proyecto Exe, añade un segundo formulario, (el Form1 se crea junto con el proyecto), añade una etiqueta al segundo formulario (Form2) que ocupe el ancho del mismo. Añade este código al Form2:

'------------------------------------------------------------------------------

' Segundo formulario para "probar" que no todo es lo que parece (14/Abr/01)

'

' ©Guillermo 'guille' Som, 2001

Page 439: curso básico de programación en visual basic

'------------------------------------------------------------------------------

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 creación

' (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

' Propiedad de sólo lectura (no existe el procedimiento Property Let)

FechaCreacion = m_FechaCreacion

End Property

En el primer formulario añade dos etiquetas (Label1 y Label2) y dos botones (Command1 y Command2) y añade este código:

'------------------------------------------------------------------------------

' Formulario para "probar" que no todo es lo que parece (14/Abr/01)

'

' ©Guillermo 'guille' Som, 2001

'------------------------------------------------------------------------------

Option Explicit

Private Sub Command1_Click()

' Mostramos el Form2

Form2.Show

' Mostrar la fecha de creación

Page 440: curso básico de programación en visual basic

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, aún existe el objeto

'Set Form2 = Nothing

'

' Una vez descargado, accedemos a la propiedad de la fecha de creación:

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.Fíjate que la fecha mostrada es la misma que cuando se creó dicho formulario.Esto prueba que el formulario, a pesar de estar descargado, aún existe, ya que conserva el valor de la variable interna y si esa variable existe es que el formulario aún está en algún 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 después en Command2 (igual que antes)Fíjate 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 asignación se hace en el evento Load y ese evento sólo se produce al cargarse el formulario de forma explícita 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 creación a la variable m_FechaCreacion...

Private Sub Form_Initialize()

Page 441: curso básico de programación en visual basic

m_FechaCreacion = Now

End Sub

¿Que valor se mostrará en Label2 después de asignar Nothing a Form2?1.- La misma que había al mostrar el formulario, (la que se muestra en Label1).2.- El valor 00:00:003.- Un valor posterior al mostrado en Label1.

Solución: 3.- Un valor posterior al mostrado en Label1, ya que al acceder a una propiedad de una clase que no existe, es decir, que aún 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 asignación que hay en el evento Form_Load del Form2 y verás que aún 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 versión de Visual Basic: VB.NET)

Para terminar, (seguramente esto hará que lo entiendas mejor... en eso confío...), vamos a ver que es lo que ocurre si nos "saltamos" la auto creación 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)

¿Cómo nos saltamos esta auto creación?Creando nosotros mismos una variable que apunte a la clase Form2, tal y como haríamos con cualquier otra clase.Por tanto modifica el código del Form1 para que sea como el que te muestro: (el código 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

Page 442: curso básico de programación en visual basic

Private Sub Form_Load()

' Creamos el objeto

Set elForm2 = New Form2

End Sub

Private Sub Command1_Click()

' Mostramos el Form2

elForm2.Show

' Mostrar la fecha de creación

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 creación:

' 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 después en Command2.En esta ocasión 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.

Page 443: curso básico de programación en visual basic

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 línea, ponla justo después de la asignación a Nothing que hacemos a la variable elForm2 en el evento Command2_Click:Label2 = "El Form2 se creó el: " & Form2.FechaCreacionAl usar la variable creada por Visual Basic, no se producirá ningún error, pero el objeto sigue estando en el limbo del VB...

Je, je, je... que me gusta liar las cosas...

Y hasta aquí hemos llegado por hoy... (mucho follón, ¿verdad?)No te preocupes si aún no tienes las cosas muy claras, ya que en las próximas 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 vemosGuillermo

Espero que la entrega anterior no te "desclasificara" y te dieran ganas de dejar esto de la programación... Se supone que sigues interesándote esto de programar con el Visual Basic, sino, no estarías aquí... Así que, sigue con la mente abierta e intenta asimilar todo esto, ya que, sólo es el principio... dentro de poco se complicará más.

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 colección de clases?Para simplificarlo, te diré que una colección 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 quisiéramos tener los datos de varios colegas, podríamos usar una colección, aunque también podríamos hacerlo con un array. La ventaja que tiene una colección frente a un array es que no tenemos que preocuparnos de redimensionarla cada vez que queramos añadir un nuevo objeto a dicha colección. Las colecciones se adaptan de forma automática a la cantidad de datos que contenga dicha colección.

¿Un ejemplo?Sí, creo que es lo mejor, ya que así lo comprenderás mejor.Y para que veas la diferencia, vamos a ver también un ejemplo usando un array.

Crea un nuevo proyecto de tipo EXE, se añade un formulario, asígnale a la propiedad Name el nombre fEntrega38.Añade cuatro etiquetas, a la última cámbiale el nombre y haz que se llame lblInfo.Añade tres cajas de texto, formando un array: Text1(0), Text1(1) y Text1(2).Añade tres commandButtons con estos nombres: cmdAsignar, cmdMostrar y cmdMostrarArray y con los siguientes captions: Asignar, Mostrar y Mostrar array.Por último añade un ListBox, deja el nombre por defecto (List1)

Page 444: curso básico de programación en visual basic

Nota:Para crear el array de tres TextBoxes, añade un TextBox, asígnale 0 (cero) a la propiedad Index. Cópialo y pégalo, cuando te pregunte si quieres crear un array, dile que si. Vuelve a pegar de nuevo para tener tres cajas de textos.

Añade la clase cColega que se usó en la entrega anterior: Proyecto/Añadir módulo de clase... y selecciona en la solapa "existente" el módulo de clase cColega.cls

Este es el aspecto del formulario en tiempo de diseño:

Añade el siguiente código y pulsa F5 para probarlo. Después te explico un poco...Escribe varios datos y pulsa en Añadir. Para mostrar los datos, pulsa en el botón Mostrar o en el botón Mostrar Array.

'------------------------------------------------------------------------------

' Entrega 38 (colecciones) (01/Jun/01)

'

' ©Guillermo 'guille' Som, 2001

'------------------------------------------------------------------------------

Option Explicit

' Colección para almacenar los datos de los colegas

Private mColegas As Collection

' Array para almacenar los datos de los colegas

Private maColegas() As cColega

Page 445: curso básico de programación en visual basic

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

'

' 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

Page 446: curso básico de programación en visual basic

Exit Sub

End If

'

' Añadimos el colega a la colección

' (no se comprueba que ya exista ese dato en la colección)

mColegas.Add oColega

' Mostrar el número de colegas que hay en la colección

lblInfo.Caption = "Hay " & mColegas.Count & " colegas en la colección"

'

' Para añadirlo en el array, debemos saber cuantos datos hay

' para poder crear un nuevo elemento en el array y añadirlo

Dim i As Long

' Averiguamos cuantos datos hay (el cero no lo tenemos en cuenta)

i = UBound(maColegas)

' incrementamos el número 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)

' Añadimos 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 colección 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 colección

For Each oColega In mColegas

List1.AddItem oColega.Nombre & " - " & oColega.FechaNacimiento

Next

Page 447: curso básico de programación en visual basic

'

' ' También se puede hacer de esta otra forma:

' Dim i As Long

' '

' 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 = "[email protected]"

Text1(2).Text = "7.6.57"

'

' Creamos el objeto colección

Set mColegas = New Collection

'

lblInfo = ""

'

' Creamos un array vacio

ReDim maColegas(0)

Page 448: curso básico de programación en visual basic

End Sub

Private Sub Text1_GotFocus(Index As Integer)

' Seleccionar el texto al entrar

With Text1(Index)

.SelStart = 0

.SelLength = Len(.Text)

End With

End Sub

La explicación:Básicamente el código es parecido en la entrega anterior, lo único que ahora hacemos es añadir cada dato de los colegas al array y después, pulsando en Mostrar o en Mostrar Array, se mostrarán los datos en el ListBox.

Veamos cada cosa por separado, en primer lugar te explicaré lo de la colección.

Private mColegas As CollectionEsta línea 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 añadir nuevos elementos a la colección se usa el método AddmColegas.Add oColega

Ahora que sabemos cómo añadir datos a la colección, vamos a ver cómo mostrar los datos de esa colección.Podemos hacerlo de dos formas, una de la forma clásica, recorriendo cada uno de los elementos de la colección mediante un bucle FOR con una variable índice, para saber cuantos elementos hay en la colección usamos la propiedad Count:For i = 1 To mColegas.Count

Para acceder a cada uno de los elementos de la colección, sabiendo el índice, usaremos la propiedad Item, ésta al ser la propiedad por defecto, no es necesario especificarla:mColegas(i).NombreSería lo mismo que:mColegas.Item(i).Nombre

La otra forma de recorrer todos los elementos de una colección es usando FOR EACH, para ello necesitamos un objeto del tipo contenido en la colección, 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

Page 449: curso básico de programación en visual basic

Ya que en cada iteración del bucle, se devuelve uno de los objetos contenidos en la colección.

Ahora veamos la explicación 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 colección, no era necesario, ya que el tipo Collection puede almacenar cualquier tipo de datos, siempre usa el tipo Variant y un Variant, como ya deberías saber, permite contener cualquier tipo de objeto):Private maColegas() As cColega

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 número en unoi = 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 cuestión:Set maColegas(i) = oColegaHay 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 clásico, 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

Fíjate que la forma de mostrar los datos de un array es casi igual que cuando se usaba FOR i = en el ejemplo de la colección.

Como habrás notado, es más fácil 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 sólo sirve para almacenar objetos, también 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 IndexSimplemente se le indica que índice queremos eliminar y ya está.Esto mismo sería un poco más complicado con un array.

Page 450: curso básico de programación en visual basic

Si no te lo crees, prueba a eliminar el elemento número x del array, (la solución te la daré en la próxima entrega).

Además de lo visto, con las colecciones podemos impedir que se añadan elementos que ya existan, para ello se puede indicar una Clave junto con el elemento que se añade, (un ejemplo lo tendremos en la próxima entrega):mColegas.Add Objeto, Clave

E incluso en que posición queremos añadirlo: Antes o detrás de un elemento dado:mColegas.Add Objeto, Clave, AntesDe, DespuesDe

Por ejemplo, si queremos añadir un objeto después del tercer elemento:mColegas.Add oColega, , 3

Te recomiendo que hagas tus propias pruebas y así verás cómo funciona.

Y esto es todo por ahora.En la próxima entrega veremos cómo crear nuestra propias colecciones a las que podremos añadir opciones de guardar los datos en el disco, poder recuperarlos y cualquier cosa que se nos ocurra... la cuestión será añadir los métodos que necesitemos... así que, estate pendiente y paciencia...

Nos vemosGuillermo

En la entrega anterior te prometí que en esta ocasión íbamos a ver cómo 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 añadir más 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 añadirle?Pues por ejemplo un método o función que permita hacer una copia del objeto en cuestión.Con esto, pretendo que comprendas que lo que estamos tratando o manejando no es una "simple" variable, sino un objeto, que como podrás comprobar, si no en estos ejemplos, si en otro código que veas por ahí... ya que me imagino que no sólo te contentarás con lo que yo te muestro, sino que te leerás la documentación del Visual Basic e incluso habrás comprado algún que otro libro sobre el tema este de la programación; en cualquier caso, debes saber que un objeto, ya sea un módulo 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

Page 451: curso básico de programación en visual basic

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 después 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 gráficas, te mostraría un dibujito con las variables y sus contenidos, pero como soy un desastre para esto de las "representaciones", sobre todo si son gráficas, 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, verás que cada una de las variables tiene un valor distinto.

¿Cómo? Que quieres ver el código completo... ¡Valeeee! ¡No insistas!, aquí lo tienes:Crea un nuevo proyecto e inserta este código 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 podrás 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 asignación.Para que sepas por dónde van los tiros, practiquemos con un ejemplo.

Supongamos, (sigo suponiendo, pero en estos casos, deberías "meterte" de lleno en el tema y convertir estas suposiciones en código), 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 más tarde te mostraré el código

Page 452: curso básico de programación en visual basic

completo, ya que en las entregas anteriores no lo hice, y sólo te mostré parte del código y he recibido algunas quejas indicándome que faltaba código... 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... dejémonos de suposiciones y vayamos a cosas concretas), te voy a mostrar el código completo para que entiendas lo que quiero explicarte... que con tanto suponer y tanta parrafada, no se si te habrás perdido o seguirás en sintonía...

Declaramos una variable para que contenga un objeto del tipo cColega y le asignamos algún valor:

Dim tColega As cColega ' La variable que contendrá un objeto del tipo cColegaSet tColega = New cColega ' Ahora está preparada para contener datostColega.Nombre = "Guille" ' Asignamos los datostColega.email = "[email protected]"

Ahora creamos otra variable en la que queremos copiar lo que tiene el objeto tColega:

Dim CopiaColega As cColegaSet CopiaColega = New cColega ' Creamos un nuevo objeto, aunque, como después verás, no es necesario

Si hacemos esta asignación: CopiaColega = tColega, recibiremos un error, ya que Visual Basic espera que se estén 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 más, ya que después lo aclaro), por tanto, lo que se debería hacer es:Set CopiaColega = tColegaPero vamos a comprobar que no es oro todo lo que reluce.Mostramos el contenido de CopiaColegaText1.Text = "Contenido de CopiaColega: " & vbCrLf & CopiaColega.Nombre & " " & CopiaColega.emailAhora cambiamos el nombre de tColegatColega.Nombre = "Guillermo"Y volvemos a mostrar el contenido de CopiaColegaText1.Text = Text1.Text & vbCrLf & _"Contenido de CopiaColega después 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 creértelo porque yo te lo diga, en el formulario que tienes creado de la prueba anterior... ¿cómo? ¿que no has probado lo anterior? entonces, tendrás que crear un nuevo proyecto... (si quieres, claro)Añade un CommandButton (Command1) y una caja de texto (Text1) con la propiedad MultiLine = True y ScrollBars = Both y pega el código, (sí, lo que está en negrita, incluso la asignación que da el error, para que compruebes que es verdad y que da un error, así vas aprendiendo de los errores...)

Page 453: curso básico de programación en visual basic

Pulsa F5 y haz click en el botón, verás que es lo que ocurre.Si todo va como debería ir, te habrá mostrado esto:

Contenido de CopiaColega: Guille [email protected] de CopiaColega después de cambiar el nombre de tColega: Guillermo [email protected]

O sea que no teníamos 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 también "aparenta" que se ha modificado... y digo aparenta, porque en realidad sólo existe un objeto del tipo cColega en la memoria, pero hay dos variables que apuntan a ese objeto.En otros lenguajes de programación sería algo así como un puntero.

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... ¿cómo 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 "pretendíamos" era "apuntar" al mismo objeto en memoria.Pruébalo quitando la línea con este código: Set CopiaColega = New cColega y verás que funciona igual (de mal)La explicación es: que al ser una variable que simplemente hace referencia a un objeto ya existente, no es necesario crear un nuevo objeto que después no se va a usar.

Veamos el código de la segunda prueba, haciendo copia de cada una de las propiedades.Añade un nuevo botón e inserta este código, después pruébalo y verás que el cambio del original no afecta a la copia, por la sencilla razón 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 = "[email protected]"

'

' Creamos otra variable en la que queremos copiar

' lo que tiene el objeto tColega:

Page 454: curso básico de programación en visual basic

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 después 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

Pues esto mismo es lo que tendríamos que hacer en un método que hiciese una copia del objeto: copiar cada una de las propiedades de dicho objeto. Este tipo de métodos suele llamarse Clone, (por lo de clonación o copia idéntica), y se podría codificar de esta forma:

Public Function Clone() As cColega

' Esta función devolverá una copia "nueva" de éste mismo objeto (12/Jul/01)

'

Dim nuevoColega As cColega ' Un objeto del tipo cColega

'

Set nuevoColega = New cColega ' Crear un nuevo objeto

'

' Asignar cada una de las propiedades de esta clase

With nuevoColega

.email = Me.email

.FechaNacimiento = Me.FechaNacimiento

.Nombre = Me.Nombre

Page 455: curso básico de programación en visual basic

End With

' Devolver el objeto "cloneado"

Set Clone = nuevoColega

End Function

Te explico un poco todo esto:

La función 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

método Clone, para ello se usa ME, aunque sólo para mayor claridad, de

ésta forma sabes que se está refiriendo al propio objeto en el que se ha

creado la función.

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 ocasión SET

Clone = nuevoColega devuelve una referencia al objeto nuevoColega

que no es ni más ni menos que el NUEVO objeto que acabamos de crear.

Para usarlo, simplemente asignaremos a la variable de destino lo que la función (o método) Clone devuelva:Set CopiaColega = tColega.Cloneque no es ni más ni menos que una "nueva" copia del objeto tColega.Como puedes comprobar en la función Clone lo único que hacemos es "encapsular" lo que antes hicimos manualmente, es decir "ocultamos" lo que la función hace para copiar el objeto, ya que al usuario lo único que le interesa es saber que ése método hace una nueva copia del objeto que lo implementa. Y si añadimos nuevas propiedades a la clase, lo único que tendríamos que hacer es añadir el código correspondiente para que las nuevas propiedades también se copien al utilizar el método Clone.

Veamos el código necesario para "probar" que todo esto que digo funciona.Añade un nuevo botón al formulario y pega el siguiente código:

Private Sub Command3_Click()

' Creamos una variable y le asignamos datos

Page 456: curso básico de programación en visual basic

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 = "[email protected]"

'

' 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 método Clone

' devuelve un nuevo objeto

'Set CopiaColega = New cColega

'

' Hacemos una clonación

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 después de cambiar el nombre de tColega: " & vbCrLf & CopiaColega.Nombre & " " & CopiaColega.email & vbCrLf

'

' 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

Page 457: curso básico de programación en visual basic

End Sub

Ejecuta el programa y pulsa en el botón que acabas de añadir, verás que te muestra esto:

Contenido de CopiaColega: Guille [email protected] de CopiaColega después de cambiar el nombre de tColega: Guille [email protected]

Contenido de tColega.Nombre: GuillermoContenido de CopiaColega.Nombre: Pepe

Es decir, que los cambios que se hagan a cualquiera de las dos variables serán independientes y no se "mezclarán", por la sencilla razón de que cada una de las variables "apunta" a un objeto diferente.

Otra forma de clonar objetosExiste 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 página que tiene el código de ejemplo, (y la página explicativa), para que sepas cómo hacerlo, por ahora no te doy más explicaciones que las que pueda haber en los comentarios y en dicha página, entre otras cosas porque se tratan temas que aún 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 página de clonación de objetos usando Visual Basic 6.0

Y hasta aquí hemos llegado.En la próxima entrega, posiblemente, veremos cómo crear nuestras propias colecciones... espero que en esa ocasión sea así... si es que antes no se me ocurre alguna cosilla nueva... ¡ya veremos!

Nos vemosGuillermoP.S.Aquí tienes el código completo de la clase y el proyecto de prueba: basico39_cod.zip 3.93 KB

¿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

ejecución, puntualizo).

Actualizado: 24/Ago/99Revisado: 12/Jul/2001 como añadido a la entrega 39 del curso básico

Page 458: curso básico de programación en visual basic

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.

¿Cómo se pueden copiar objetos en Visual Basic?

Hasta ahora, la única forma de copiar objetos es creando un método en la propia clase que devuelva un nuevo objeto con el contenido de las propiedades, (normalmente a ese método se le llama Clone), ni tan siquiera usando lenguajes como C++ se podían hacer copias de objetos...Pero ahora, con la versión 6.0 (y posteriores) de Visual Basic, las clases públicas 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 añaden tres nuevos eventos a la clase: InitProperties, ReadProperties y WriteProperties. Si has creado tus propios controles ActiveX, seguramente habrás 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 tiempo de diseño, 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 propiedades-

¿Cómo 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 sólo en los procedimientos Let o Set, Visual Basic pone a nuestra disposición la instrucción 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 más 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 pública

Page 459: curso básico de programación en visual basic

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

también esos objetos, estos deben ser también "Persistables"

En el código que sigue, veremos cómo 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 método rápido de copiar los objetos semi-automáticamente con el objeto PropertyBag.

En el código de ejemplo se muestran dos formas diferentes para hacer copias de objetos:En el método 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 código del formulario de prueba se muestra el código 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 técnica la dejo a tu imaginación...

Aquí tienes el código, el cual está lo suficientemente comentado, (al menos eso espero), como para que sea fácilmente comprensible.

Nota:Los procedimientos GuardarObjeto y LeerObjeto del formulario, muestran la forma de guardar el contenido de un objeto en un fichero y después poder recuperarlo para crear un nuevo objeto.(¿esto mismo no lo acabo de repetir un poco más arriba?)

Si quieres más información sobre el tema, consulta:Persistencia en datos de componentes, en la ayuda de Visual Basic (MSDN Library) y para saber más sobre persistencia en los controles ActiveX: Guardar las propiedades de un control, en Generar un control ActiveX.

El código

El código del formulario de prueba y una "captura" del mismo, así como un poco de explicación de los controles:(este formulario estará en un proyecto que tendrá una referencia al componente que tiene las clases mostradas más abajo)

Cuando pulses en el botón "Copiar NotPersistable usando un fichero", te mostrará un error indicando que no se pueden copiar objetos no persistente,

Page 460: curso básico de programación en visual basic

sin embargo, al pulsar en "Copiar NotPersistable", se usa el método 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..." harán que se muestren los contenidos de las clases en las cajas de texto.

El Frame superior mostrará la clase original (la Persitable o NotPersistable, según el botón pulsado).El Frame inferior mostrará el contenido de la copia realizada a la clase.

"Fecha Creación" indicará la fecha y hora de creación de la clase... lo aclaro por si piensas que es la creación de otra cosa... ¡nunca se sabe!

'------------------------------------------------------------------------------

Page 461: curso básico de programación en visual basic

' tPersistable (22/Ago/99)

' Revisado para el curso básico (12/Jul/01)

'

' Prueba de copiar objetos usando la propiedad Persitable

' Para más información ver en la MSDN de Visual Basic 6.0:

' Persistencia en datos de componentes

'

' ©Guillermo 'guille' Som, 1999-2001

'------------------------------------------------------------------------------

Option Explicit

' 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 botón de indice 0 asigna los valores a las clases básicas

If Index = 0 Then

With m_Clase1

.Nombre = Text1(0)

.email = Text1(1)

.AñoNacimiento = Text1(2)

.Comentario = Text1(3)

End With

With m_Clase3

.Nombre = Text1(0)

.email = Text1(1)

.AñoNacimiento = Text1(2)

.Comentario = Text1(3)

Page 462: curso básico de programación en visual basic

End With

Else

' El índice 1 asigna los valores a las otras clases (las copias)

With m_Clase2

.Nombre = Text1(4)

.email = Text1(5)

.AñoNacimiento = Text1(6)

.Comentario = Text1(7)

End With

With m_Clase4

.Nombre = Text1(4)

.email = Text1(5)

.AñoNacimiento = Text1(6)

.Comentario = Text1(7)

End With

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, mostraría el nombre con la palabra <COPIA>)

With m_Clase2

.Nombre = "<COPIA> " & .Nombre

End With

cmdRefresh_Click 0

End Sub

Page 463: curso básico de programación en visual basic

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, mostraría el nombre con la palabra <COPIA>)

With m_Clase4

.Nombre = "<COPIA> " & .Nombre

End With

cmdRefresh_Click 1

End Sub

Private Sub cmdCopiarF_Click()

'//////////////////////////////////////////////////////////////////////////

' El siguiente código 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

Page 464: curso básico de programación en visual basic

End Sub

Private Sub cmdCopiarF2_Click()

'//////////////////////////////////////////////////////////////////////////

' El siguiente código 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

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

Page 465: curso básico de programación en visual basic

With m_Clase1

Text1(0) = .Nombre

Text1(1) = .email

Text1(2) = .AñoNacimiento

Text1(3) = .Comentario

Label2(0) = .Copia.Nombre

lblFecha(0) = .FechaCreación

End With

With m_Clase2

Text1(4) = .Nombre

Text1(5) = .email

Text1(6) = .AñoNacimiento

Text1(7) = .Comentario

Label2(1) = .Copia.Nombre

lblFecha(1) = .FechaCreación

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) = .AñoNacimiento

Text1(3) = .Comentario

Label2(0) = .Copia.Nombre

lblFecha(0) = .FechaCreación

End With

With m_Clase4

Text1(4) = .Nombre

Text1(5) = .email

Text1(6) = .AñoNacimiento

Text1(7) = .Comentario

' Esto producirá un error si se copia mediante un fichero

Label2(1) = .Copia.Nombre

'

lblFecha(1) = .FechaCreación

Page 466: curso básico de programación en visual basic

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 podrán copiar:

Set m_Clase3 = New cNotPersistable

Set m_Clase4 = New cNotPersistable

' Unos valores de ejemplo: ¿quién es este?

Text1(0) = "Guillermo 'guille'"

Text1(1) = "[email protected]"

Text1(2) = 1957

Text1(3) = "El Guille"

' Asignar los valores a la clase

Page 467: curso básico de programación en visual basic

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 botón adecuado si se cambia el contenido de las cajas

Dim queClase As Long

queClase = 0

If Index > 3 Then

queClase = 1

End If

Page 468: curso básico de programación en visual basic

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 parámetro del tipo IPruebaPersistable ya que esa interface

' está implementada en los dos objetos del componente de prueba

'

' Esta función 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

'

' Añadir al path de la aplicación la barra de directorio

sPath = App.Path

If Right$(sPath, 1) <> "\" Then

sPath = sPath & "\"

End If

' Instanciación de un objeto PropertyBag.

Set pb = New PropertyBag

Page 469: curso básico de programación en visual basic

' 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, _

Optional ByVal sFic As String = "CopiaObjeto" _

) As Boolean

'---------------------------------------------------------------------------

' Leer el objeto del fichero y asignarlo a la clase indicada (22/Ago/99)

'

' Se usa el parámetro del tipo IPruebaPersistable ya que esa interface

' está implementada en los dos objetos del componente de prueba

'

Page 470: curso básico de programación en visual basic

' Esta función devolverá:

' False si se produjo error

' True si todo fue bien

'---------------------------------------------------------------------------

Dim varTemp As Variant

Dim byteArr() As Byte

Dim pb As PropertyBag

Dim sPath As String

On Error GoTo ErrLeer

' Añadir al path de la aplicación la barra de directorio

sPath = App.Path

If Right$(sPath, 1) <> "\" Then

sPath = sPath & "\"

End If

' Instanciación 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 & _

Page 471: curso básico de programación en visual basic

sFic & vbCrLf & Err.Number & " - " & Err.Description

Err = 0

LeerObjeto = False

End Function

El código de las clases:(todas son clases públicas y están incluidas en un componente ActiveX DLL)

'------------------------------------------------------------------------------

' IPruebaPersistable (22/Ago/99)

' Revisado para el curso básico (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 están declaradas como "variables" públicas,

' al usar Implements se crearán dos procedimientos: Get y Let

' (Get y Set en caso del objeto Copia)

Public Copia As IPruebaPersistable

Public Nombre As String

Public AñoNacimiento As Long

Public email As String

Public Comentario As String

Page 472: curso básico de programación en visual basic

' Propiedad de sólo lectura

Public Property Get FechaCreación() As String

End Property

'------------------------------------------------------------------------------

' cPersistable (22/Ago/99)

' Revisado para el curso básico (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 1-Persistable

'

' ©Guillermo 'guille' Som, 1999-2001

'------------------------------------------------------------------------------

Option Explicit

' Clase genérica 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 métodos y

' propiedades de cualquier clase que la implemente,

' por ejemplo, el método Copia devuelve un objeto de este tipo

Implements IPruebaPersistable

' Valor por defecto del año de nacimiento

Private Const cAñoNacimiento As Long = 1999

' Variables privadas para contener los valores de las propiedades

Private m_FechaCreación As String

Page 473: curso básico de programación en visual basic

Private m_Copia As IPruebaPersistable

Private m_Nombre As String

Private m_AñoNacimiento 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

Public Property Get AñoNacimiento() As Long

' Se devuelve el valor contenido en la variable privada

AñoNacimiento = m_AñoNacimiento

End Property

Public Property Let AñoNacimiento(ByVal NewValue As Long)

' Se asigna el nuevo valor en la variable privada

m_AñoNacimiento = NewValue

' y se avisa al Visual Basic de que esta propiedad ha cambiado

PropertyChanged "AñoNacimiento"

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

Page 474: curso básico de programación en visual basic

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

Public Property Let email(ByVal NewValue As String)

m_email = NewValue

PropertyChanged "email"

End Property

' Propiedad de sólo lectura

' por eso sólo está el procedimiento Get

Public Property Get FechaCreación() As String

FechaCreación = m_FechaCreación

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

Page 475: curso básico de programación en visual basic

Private Sub Class_Initialize()

m_AñoNacimiento = cAñoNacimiento

'Debug.Print "cPersistable_Initialize"

' Esto haría que se quedara sin espacio en la pila,

' ya que al crear una nueva instancia haría 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 sólo 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.

' Asignamos la fecha y hora actual a la variable privada

m_FechaCreación = Format$(Now, "dd/mmm/yyyy hh:mm:ss")

' indicamos que la propiedad ha cambiado,

' (sólo se avisará aquí, ya que esta propiedad es de sólo lectura)

PropertyChanged "FechaCreación"

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 algún error

On Local Error Resume Next

' Asignamos los valores almacenados a las variables privadas

m_Nombre = PropBag.ReadProperty("Nombre")

m_AñoNacimiento = PropBag.ReadProperty("AñoNacimiento", cAñoNacimiento)

Page 476: curso básico de programación en visual basic

m_email = PropBag.ReadProperty("email")

' Para que la propiedad Comentario sea persistente, quitar el comentario

'm_Comentario = PropBag.ReadProperty("Comentario")

m_FechaCreación = PropBag.ReadProperty("FechaCreación")

'

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)

' 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 "AñoNacimiento", m_AñoNacimiento, cAñoNacimiento

' Si no se guarda la propiedad Comentario, no se podrá "clonar"

'PropBag.WriteProperty "Comentario", m_Comentario

PropBag.WriteProperty "FechaCreación", m_FechaCreación

Page 477: curso básico de programación en visual basic

' 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 podrían asignar las variables privadas, pero entonces habría que

' "avisar" de los cambios llamando a PropertyChanged, además de que si en

' los procedimientos se hacen algunas comprobaciones, pues...

' por tanto es mejor llamar a los propios métodos de la clase.

'

Private Property Let IPruebaPersistable_AñoNacimiento(ByVal RHS As Long)

Me.AñoNacimiento = RHS

End Property

Private Property Get IPruebaPersistable_AñoNacimiento() As Long

IPruebaPersistable_AñoNacimiento = Me.AñoNacimiento

End Property

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)

Page 478: curso básico de programación en visual basic

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_FechaCreación() As String

IPruebaPersistable_FechaCreación = m_FechaCreación

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

' Devuelve una copia de esta clase (23/Ago/99)

' Se usa la técnica descrita en la ayuda de Visual Basic 6.0

' para copiar objetos usando ficheros de texto,

' aunque en este caso no sea necesario ningún fichero intermedio...

Page 479: curso básico de programación en visual basic

'

' Nota:

' Anteriormente había usado una NUEVA variable intermedia,

' pero no es necesario, incluso si la variable a la que se asigna con Clone

' no se ha creado con NEW

'

Dim pb As PropertyBag

' Instanciación 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

'------------------------------------------------------------------------------

' cNotPersistable (22/Ago/99)

' Revisado para el curso básico (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

' asignar a la propiedad Persistable de la clase el valor 1-Persistable

' (ver la clase cPersistable)

'

' ©Guillermo 'guille' Som, 1999-2001

Page 480: curso básico de programación en visual basic

'------------------------------------------------------------------------------

Option Explicit

Implements IPruebaPersistable

Private m_FechaCreación As String

Private m_Copia As IPruebaPersistable

Private m_Nombre As String

Private m_AñoNacimiento As Long

Private m_email As String

Private m_Comentario As String

Private Sub Class_Initialize()

'Set m_Copia = New IPruebaPersistable

m_FechaCreación = 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_AñoNacimiento(ByVal RHS As Long)

Me.AñoNacimiento = RHS

End Property

Private Property Get IPruebaPersistable_AñoNacimiento() As Long

IPruebaPersistable_AñoNacimiento = Me.AñoNacimiento

End Property

Page 481: curso básico de programación en visual basic

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_FechaCreación() As String

IPruebaPersistable_FechaCreación = m_FechaCreación

End Property

Private Property Let IPruebaPersistable_Nombre(ByVal RHS As String)

Me.Nombre = RHS

End Property

Page 482: curso básico de programación en visual basic

Private Property Get IPruebaPersistable_Nombre() As String

IPruebaPersistable_Nombre = Me.Nombre

End Property

Public Property Get AñoNacimiento() As Long

AñoNacimiento = m_AñoNacimiento

End Property

Public Property Let AñoNacimiento(ByVal NewValue As Long)

m_AñoNacimiento = 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

Page 483: curso básico de programación en visual basic

' Propiedad de sólo lectura

Public Property Get FechaCreación() As String

FechaCreación = m_FechaCreación

End Property

Public Property Get Nombre() As String

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 haría que se quedara sin espacio en la pila

'Set NewCopia = Me.Clone

' Así funcionaría bien

With NewCopia

.Nombre = Me.Copia.Nombre

.AñoNacimiento = Me.Copia.AñoNacimiento

.Comentario = Me.Copia.Comentario

.email = Me.Copia.email

' ¿¿¿Cómo se copiaría el objeto???

' El objeto debería tener un método Clone para copiarlo.

'.Copia = Me.Copia

End With

' Asignar cada una de las propiedades a la nueva copia.

Page 484: curso básico de programación en visual basic

' La desventaja es que, si se tienen muchas propiedades...

' pues es más trabajo y puede que por despiste se olvide algo...

With NewClase

.Nombre = m_Nombre

.AñoNacimiento = m_AñoNacimiento

.email = m_email

.Comentario = m_Comentario

Set .Copia = NewCopia

End With

' Si se devuelve este mismo objeto,

' no se creará una nueva instancia de la clase

'Set Clone = Me

Set Clone = NewClase

End Function

Si quieres bajar el código completo del ejemplo, pulsa este link. (copiar_objetos.zip 10.1 KB)

Nerja, 24 de Agosto de 1999Revisado el 12/Jul/2001 para el curso básico

Confío 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 ocasión 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 programación, habrá que hacerles caso... así que también ve preparando el cuerpo, ya que a partir del 2002 la "nueva ola" será .NET y habrá que ir "concienciándose" de la nueva tecnología... pero no te preocupes que también podrás aprenderla... para que no te quedes atrás, aunque eso será en un futuro... talvez no muy lejano...

Cómo acceder a bases de datos DAO, sin el datacontrol.

Empecemos desde el principio... para que te vayas acostumbrando... aunque ya deberías saber crear tus propios formularios y añadir los controles que te indique... aunque prácticamente no te lo haya explicado en el curso, es algo que te enseñan en prácticamente cualquier libro e incluso en los "tutoriales" que incluye el Visual Basic.Sólo decirte que usando la versión 5CCE de Visual Basic no se pueden acceder a datos, así que tendrás que usar la versión Profesional o la Empresarial... pero si de verdad te quieres dedicar a esto de la programación... esa serán las versiones de las que deberías disponer... y no me preguntes dónde conseguirlas... ya que las venden

Page 485: curso básico de programación en visual basic

en cualquier "tienda" de informática o en los distribuidores que puedes encontrar en Internet.

Vayamos al tema que nos interesa, ya que no es plan de hacer "marketing" gratuito a los distribuidores que "no sponsorizan" este curso básico... ¡hum!

Crea un nuevo proyecto, se creará un proyecto con un formulario.Añade los siguientes controles para que el formulario quede con el aspecto que te muestro a continuación, (que es parecido al usado en las entregas anteriores de acceso a datos):

Aspecto del formulario en tiempo de diseño

Los controles usados son:En la parte superior:cmdMover, un array de 0 a 3Label1, un array de 0 a 2, Text1, un array de 0 a 2cmdAdd, cmdActualizar, cmdBorrarEl segundo grupo:Label2, Text2, Option1, Option2, cmdBuscar, cmdBuscarSigElbotón de salir es: cmdSalir

En el menú Proyecto/Referencias... selecciona Microsoft DAO 3.51 Object Library -aunque también puedes seleccionar cualquier otra que empiece por Microsoft DAO... cualquiera de ellas vale... puede que también tengas la 3.6 e incluso la 2.5/3.51 Compatibility Library, todo dependerá de la versión de DAO que tengas instalada-A diferencia de cuando seleccionas un componente, después de cerrar el cuadro de diálogo no verás nada nuevo en la ventana de herramientas, ya que las referencias no añaden nuevos controles, pero si que añaden nuevas librerías que exponen objetos que podemos usar en nuestra aplicación...

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

Page 486: curso básico de programación en visual basic

permitirán, 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?Según la ayuda de Visual Basic, "Un objeto Recordset representa los registros de una tabla o los registros del resultado de ejecutar una consulta". Una consulta es... eso... una consulta... una especie de búsqueda avanzada, en la que podemos indicar varios criterios de búsqueda... aunque también, como verás pronto, es algo más 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 sección de declaraciones del formulario (General/Declaraciones), añade estas líneas:

Option Explicit

Private db As DatabasePrivate 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 tendrás 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 continuación abrimos la base de datos usando la función OpenDatabase, la cual devuelve un objeto de la base de datos recién abierta, uno de los parámetros que espera recibir dicha función 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 datosSet db = OpenDatabase(sPathBase)

A continuación 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 manipularSet rs = db.OpenRecordset("SELECT * FROM Authors", dbOpenDynaset)

La constante dbOpenDynaset le indica al método 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; además de este tipo de recordset, existen otros los cuales sólo se pueden usar para "recorrer" los datos o mostrarlos, si así lo prefieres, pero no permiten modificarlos, ese tipo de recordset también lo veremos en esta misma entrega.

Page 487: curso básico de programación en visual basic

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 tenía Data1.Recordset en los ejemplos de las entregas anteriores sobre acceso a datos, en particular las entregas 34 y 35.

Como no tenemos un control data, el cual nos permitía movernos por los diferentes registros de la tabla, en el formulario hay cuatro botones, que se usarán para esa tarea: movernos tanto al primero, anterior, siguiente o último registro, el código para hacerlo es el que te muestro a continuación:

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 según el botón pulsado

If Index = 0 Then ' Al primero

rs.MoveFirst

ElseIf Index = 1 Then ' Al anterior

rs.MovePrevious

ElseIf Index = 2 Then ' Al siguiente

rs.MoveNext

ElseIf Index = 3 Then ' Al último

rs.MoveLast

End If

'

' Si estamos antes del primero. mover al primero

If rs.BOF Then

rs.MoveFirst

' Si estamos después del último, mover al último

ElseIf rs.EOF Then

rs.MoveLast

End If

' Si no se ha producido error, mostrar los datos

Page 488: curso básico de programación en visual basic

If Err = 0 Then

MostrarRegistro

End If

'

Err = 0

End Sub

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 después del final de los registros. Cuando estas dos propiedades devuelven un valor verdadero, es que no hay datos en el recordset.

A continuación movemos el "puntero" del recordset al registro indicado, según el valor de Index sabremos que botón se ha pulsado y por tanto que "acción" tenemos que elegir. En los comentarios puedes ver qué valor corresponde con cada movimiento.

Como es posible que el usuario pulse en el botón anterior o siguiente después 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 código 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 código de las pruebas con el control Data, notarás 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 información en los controles que tenía "enlazados", pero ahora no tenemos un control que "automatice" esta tarea, así que, tenemos que hacerlo por nuestros medios... por suerte no es tan difícil.

Page 489: curso básico de programación en visual basic

Ahora vamos a ver los procedimientos de Añadir un nuevo registro, actualizar uno ya existente y eliminar el registro actual. En el propio código encontrarás la explicación de que es lo que hace cada línea usada.

El código de Añadir un nuevo registro:

Private Sub cmdAdd_Click()

' Añadir un nuevo registro

With rs

.AddNew

' Añadimos algún 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 código 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 edición

.Edit

' Este campo es autonumérico, así que no asignarlo

'.Fields("Au_ID") = Text1(0) + 0

' Añadimos una cadena vacía al final

' ya que si Text1(1) está vacío, se asignará un valor NULL y dará error

.Fields("Author") = Text1(1) & ""

' Idem con el año de nacimiento, pero como es numérico, se sumará 0

.Fields("Year Born") = Text1(2) + 0

' Actualizar los datos en el recordset

.Update

End With

End Sub

Page 490: curso básico de programación en visual basic

Y por último el código para eliminar un registro... como siempre, borrar es muy fácil...

Private Sub cmdBorrar_Click()

' Borrar el registro actual

' Se comprueba que haya algún 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

' Eliminar el registro actual

rs.Delete

'

' Movemos al primer registro

cmdMover_Click 0

End If

End Sub

Bien, básicamente estas tareas son prácticamente 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 código usado para buscar los datos, el cual es también prácticamente lo mismo que cuando se tenía el datacontrol. Fíjate 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 código de buscar y buscar siguiente, para lo cual usamos los métodos 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

Page 491: curso básico de programación en visual basic

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 detección de errores

On Error Resume Next

'

' Buscar la primera coincidencia en el recordset del Data1

If Option1.Value Then

' Convertir el contenido de TextBox en un número

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 posición 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

Page 492: curso básico de programación en visual basic

' 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 más datos que mostrar."

' Posicionar el recordset en la posición guardada

.Bookmark = sBookmark

End If

' Mostrar los datos del registro actual

MostrarRegistro

End With

End Sub

¿Cómo realizar consultas con DAO?

Ya hemos visto cómo introducir y modificar registros, también hemos visto cómo 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 búsqueda, incluso a veces nos interesa buscar más de un dato a la vez. Eso mismo se puede hacer con FindFirst y FindNext, es decir, podemos buscar en más de un campo del mismo registro el dato o datos que nos interesa a un mismo tiempo. Pero además de usar recordsets de sólo lectura, podemos acelerar esa tarea de "obtener" la información que queramos realizando consultas en lugar de ir buscando uno por uno los datos. Independientemente de cómo hagamos la consulta, siempre nos quedará el recurso de poder usar los métodos 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 cómo funciona todo esto.

Para mostrar el resultado de la "búsqueda" o consulta, vamos a usar un control ListView.Para añadir un control ListView a nuestro proyecto, primero hay que "añadir" el control a la barra de herramientas de Visual Basic, para ello, selecciona la opción Componentes del menú Proyecto y del cuadro de diálogo que te muestra, selecciona Microsoft Windows Common Controls, (pude que a continuación de este nombre te muestre la versión de Visual Basic, en mi caso, me muestra 6.0, pero también la 5.0 (SP2), ya que tengo instalado esas dos versiones de VB.Una vez que has pulsado en Aceptar, verás que se han añadido nuevos controles a la barra de herramientas (ToolBox), selecciona el que representa al Listview y añadelo al formulario, mediante código configuraremos la apariencia.Si quieres puedes crearte un nuevo proyecto para esta "segunda" prueba, ya que, además de añadir el ListView, también vamos a quitar los botones de búsqueda, porque no tiene sentido tenerlos... al menos para el propósito de este ejemplo.El aspecto del formulario sería el siguiente:

Page 493: curso básico de programación en visual basic

El aspecto, en tiempo de diseño, del segundo formulario

El código para configurar el ListView en tiempo de ejecución para mostrar la información 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 líneas de separación entre datos

.GridLines = True

' Que no se puedan modificar los datos del listview

.LabelEdit = lvwManual

' Añadimos las cabeceras

.ColumnHeaders.Add , , "Au_ID", 900

.ColumnHeaders.Add , , "Autor", 2700

.ColumnHeaders.Add , , "Año nacimiento", 1500, lvwColumnRight

End With

Page 494: curso básico de programación en visual basic

Ahora la búsqueda sólo se hará por el nombre del autor y se mostrarán en la lista todos los autores que coincidan con los datos que queremos buscar. El código del botón Buscar sería 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 "estático", el cual no es modificable

' para poder modificarlo, tendría que ser del tipo dbOpenDynamic

Set tRs = db.OpenRecordset(sBuscar, dbOpenSnapshot)

' Comprobar que hay datos en el recordset

With tRs

' Si no hay datos...

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

Page 495: curso básico de programación en visual basic

En otra ocasión veremos cómo "diseñar" un formulario para consultas con distintas opciones de búsqueda, así como para búsqueda en múltiples campos.

En la próxima entrega veremos el código que habría que usar para hacer esto mismo pero usando ADO.

Así que, paciencia y a ser buenos.

Nos vemosGuillermoP.S.Este link te permite bajar el código de los dos ejemplos. (basico40_cod.zip 7.98 KB)

Aquí tienes la entrega prometida para acceder a bases de datos ADO sin el data control.El ejemplo o código que te muestro, como verás, es prácticamente idéntico al mostrado en el último ejemplo de la entrega anterior, salvo en lo que a las referencias a las librerías que hay que usar para poder acceder a las bases de datos ADO, pero esto lo veremos a continuación, así que prepara el cuerpo y el alma y te enterarás...

Cómo acceder a bases de datos ADO, sin el datacontrol.

Empecemos desde el principio... para que te vayas acostumbrando... aunque ya deberías saber crear tus propios formularios y añadir los controles que te indique... aunque prácticamente no te lo haya explicado en el curso, es algo que te enseñan en prácticamente 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 también es válido 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 mayoría 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.Añade los siguientes controles para que el formulario quede con el aspecto que te muestro a continuación, (que es el mismo del último ejemplo de la entrega anterior):

Page 496: curso básico de programación en visual basic

Aspecto del formulario en tiempo de diseño

Los controles usados son:En la parte superior:cmdMover, un array de 0 a 3Label1, un array de 0 a 2, Text1, un array de 0 a 2cmdAdd, cmdActualizar, cmdBorrarEl segundo grupo:Label2, Text2, cmdBuscarUn Listview1 para mostrar los resultados de la búsquedaUna etiqueta (LblData) para mostrar algunos mensajes y el botón de salir es: cmdSalir

Las referencias necesarias:En el menú Proyecto/Referencias... selecciona Microsoft ActiveX Data Objects 2.6 Library -aunque también puedes seleccionar cualquiera de las otras que te muestra, (si es que te muestra alguna más); a mi me aparecen las versiones 2.0, 2.1, 2.5 y 2.6, la única que no deberías seleccionar es la 2.0 que ya está obsoleta-

Una vez añadida la referencia a los objetos ADO, puedes usar los objetos expuestos por esta librería.En este ejemplo usaremos dos de esos objetos, que serán los que en la mayoría 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 código seguramente lo entenderás y si no lo entiendes... es que deberías leerte TODAS las entregas, ¡empezando por la primera! (tendrá que excusarse el Guille de alguna forma si no entiendes lo que explica)

Page 497: curso básico de programación en visual basic

Los objetos ADO más comunes:Normalmente, el objeto Connection suele declararse de forma que sea visible en todo el formulario, salvo en el caso de que añadieses algún módulo BAS y necesitaras usarlo desde ese módulo BAS, en cuyo caso, deberías declararlo Público 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 más en las entregas de las clases)En el procedimiento Buscar, veremos cómo usar otro recordset, pero de la forma tradicional: sin eventos.

Escribe este código 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 información 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 método Open al cual hay que indicarle el "proveedor" y el nombre de la base de datos:

Private Sub Form_Load()

'

Text2 = ""

'

' 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 conexión manualmente

' Usar "Provider=Microsoft.Jet.OLEDB.3.51;" para bases de Access 97

Page 498: curso básico de programación en visual basic

' Usar "Provider=Microsoft.Jet.OLEDB.4.0;" para bases de Access 2000

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 líneas de separación entre datos

.GridLines = True

' Que no se puedan modificar los datos del listview

.LabelEdit = lvwManual

' Añadimos las cabeceras

.ColumnHeaders.Add , , "Au_ID", 900

.ColumnHeaders.Add , , "Autor", 2700

.ColumnHeaders.Add , , "Año 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, teníamos un procedimiento al que llamábamos cada vez que teníamos que actualizar la información 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

Page 499: curso básico de programación en visual basic

"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 código para mostrar los datos cada vez que se cambia el registro activo, aunque realmente no sería necesario si hubiésemos "ligado" los controles con el recordset... pero de esa forma no tendríamos 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 ningún registro activo"

.MoveFirst

Else

Text1(0) = .Fields("Au_ID")

' Por si el dato es nulo, añadirle una cadena vacia

Text1(1) = .Fields("Author") & ""

Text1(2) = .Fields("Year Born") & ""

End If

End With

End Sub

En este evento podemos usar tanto el objeto rst como el que está en el parámetro: pRecordset, los dos se refieren al mismo objeto.Hacemos una pequeña comprobación de que no nos encontremos con un recordset vacío o que esté fuera de los límites 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.Fíjate que en algunos de los campos añado una cadena vacía al contenido del campo, esto es para los casos en que esos campos contengan un valor nulo. Si se lo quitas al

Page 500: curso básico de programación en visual basic

Year Born, verás cómo 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 código para los botones de Mover, Nuevo, Actualizar y Eliminar, que es prácticamente igual al de la versión para DAO, con la salvedad de Actualizar, ya que en ADO no es necesario poner un registro (o columna que es como se debería decir), en modo edición para poder modificarlo, ya que al usar Update, se actualiza el registro.

Private Sub cmdMover_Click(Index As Integer)

' Mover según el botón pulsado

'On Error Resume Next

'

With rst

If Index = 0 Then ' Primero

.MoveFirst

ElseIf Index = 1 Then ' Anterior

.MovePrevious

ElseIf Index = 2 Then ' Siguiente

.MoveNext

ElseIf Index = 3 Then ' Último

.MoveLast

End If

'

If .BOF Or .EOF Then

.MoveFirst

lblData.Caption = " No hay datos..."

Else

lblData.Caption = " Registro actual: " & rst("Au_ID")

End If

End With

'

Err = 0

End Sub

Private Sub cmdAdd_Click()

' Añadir un nuevo registro

rst.AddNew

' Añadimos algún texto, para que no se pierda este registro

Page 501: curso básico de programación en visual basic

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 numérico, así que no asignarlo

'.Fields("Au_ID") = Text1(0) + 0

' Añadimos una cadena vacía al final

' ya que si Text1(1) está vacío, se asignará un valor NULL y dará error

.Fields("Author") = Text1(1) & ""

' Idem con el año de nacimiento, pero como es numérico, se sumará 0

.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 algún 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 (rst.EOF Or rst.BOF) Then

' Avisar de que no hay registros

lblData.Caption = "Ningún registro activo"

Else

' Eliminar el registro actual

Page 502: curso básico de programación en visual basic

rst.Delete

'

' Movemos al primer registro para que los cambios se hagan permanentes

' (también podríamos haberlo movido al último registro)

rst.MoveFirst

End If

End Sub

Creo que no necesita información adicional, en los comentarios está todo explicado.

Para terminar, vamos a ver el código 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 válidos" para ADO:

' NOTA: Replace es una función 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 "estático", el cual no es modificable

' para poder modificarlo, tendría que ser del tipo dbOpenDynamic

Set tRs = cnn.Execute(sBuscar)

' Comprobar que hay datos en el recordset

With tRs

' Si no hay datos...

Page 503: curso básico de programación en visual basic

'----------------------------------------------------------------------

' 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 podrás comprobar el procedimiento de buscar es muy parecido, por no decir casi idéntico al usado en DAO, con la salvedad de que hacemos una comprobación para cambiar los caracteres comodines que hubiese en el texto indicado para la búsqueda, ya que en ADO no son los mismos que en DAO.Según la ayuda de ADO, se usará el carácter % (tanto por ciento) en lugar del * (asterisco), cuando queramos indicar cualquier cosa que haya en el sitio en que se encuentra dicho carácter, por ejemplo: %jan% encontrará todos los registros que contenga "jan", esté en el sitio que esté: Janet, Alejandro, etc. Si pusiéramos jan%, sólo mostraría los que empezaran con jan, etc.

Por otro lado, el carácter _ (subrayado bajo) sustituye a la ? (interrogación), en este caso este comodín significa "cualquier carácter que esté en esa posición", 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.

La función Replace usada para cambiar esos caracteres es propia de la versión 6 de Visual Basic, en VB5 no existe, así que tendrás que crearte tu propia función Replace.

Bueno, vale... aquí la tienes:

Page 504: curso básico de programación en visual basic

Private Function Replace(ByVal Expresion As String, _

ByVal Encontrar As String, _

ByVal ReemplazarCon As String) As String

'---------------------------------------------------------------

' Función Replace para usar con VB5 o anteriores

'---------------------------------------------------------------

Dim i As Long, j As Long

'

j = 1

Do

' Buscar la cadena indicada en la expresión

i = InStr(j, Expresion, Encontrar)

' Si la hemos hallado, quitamos dicha cadena y ponemos la nueva

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 genérica, se usa el género masculino... como si fuese neutro... sí, ya, es que algunos que dicen ser muy masculinos, realmente son neutros... ¿a quién te refieres? no, a nadie en concreto... es por aclarar las cosas... ¡Ah! creía...)

Dejemos a los Guilles con su discusión y te espero en la próxima entrega, que aún no sé de que tratará... ¿será de clases? ¿será de acceso a datos? ¿será sobre algún tema nuevo? ya veremos...

Nos vemosGuillermo

Buenas, ya estoy de nuevo por aquí... sí, se que han pasado un montón de días... pero...

En esta entrega vamos a seguir con las clases, en particular con las colecciones personalizadas.Si quieres refrescarte la memoria, podrías volver a las entregas anteriores sobre las clases, según recuerdo son las entregas 37, 38 y 39.

Page 505: curso básico de programación en visual basic

En la entrega 38 se trató el tema de las colecciones, pero de forma genérica, en esta entrega veremos cómo crear nuestra propia clase-colección para que maneje elementos del tipo que nosotros queramos.

Es habitual que los nombres de las colecciones que contendrán 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 colección va a contener elementos del tipo cColega, se llamaría cColegas. Esto simplemente es una "recomendación", ya que el nombre que tenga la colección 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 "válido".Para seguir con la clase definida en la entrega 39, vamos a llamarla cColegas y contendrá elementos del tipo cColega.

Esta colección tendrá los métodos habituales en todas las colecciones, recordémoslos:Add, para añadir nuevos elementos.Remove, para eliminar un elemento en cuestión.Count, para saber cuantos elementos contiene.Item, para acceder a dicho elemento.Además se podrá recorrer la colección usando For Each, para ello tendremos que recurrir a un serie de asignaciones algo extrañas, pero... que al fin y al cabo nos permita darle esa funcionalidad a nuestra clase.También añadiremos otros métodos, tales como:Clone, para poder hacer copias de la colección, pero no una copia por referencia, sino una copia totalmente independiente de la clase/colección, tal como hicimos con la clase cColega.Exists, que devolverá un valor Verdadero o Falso, según un elemento esté o no en la colección.Clear, que eliminará todos los elementos de la colección y la dejará preparada para usarla desde cero.Además, vamos a añadir un método llamado Equals, que permitirá comprobar si la un objeto del tipo de la colección es igual a otro objeto de ese mismo tipo... esto es útil para poder "comparar" dos objetos. Este mismo método se lo añadiremos a la clase cColega.

Dicho todo esto y antes de ver el código de la colección personalizada, vamos a crear el método Equals de la clase cColega (la cuestión es siempre hacerse de rogar... ¡este Guille!)

Equals, un método que comprueba si dos objetos son "exactamente" iguales.

Nota:Aquí vamos a ver sólo lo que habría que añadir a la clase cColega, el código anterior sigue siendo válido y es el mismo que el que vimos en la entrega 37, con el añadido del método Clone de la entrega 39.

Para hacer las cosas bien, vamos a empezar por crear un nuevo proyecto, al cual le añadiremos una Referencia que nos será útil, sino obligatoria, para cuando vayamos a crear la colección.Abre el Visual Basic, crear un proyecto de tipo EXE, (automáticamente se añadirá un formulario llamado Form1), añade la clase cColega, (te recomendaría que hicieras una copia de la clase anterior), y del menú Proyecto, selecciona Referencias... del cuadro

Page 506: curso básico de programación en visual basic

de diálogo que te muestra, busca el elemento "OLE Automation" y pulsa en el checkBox para seleccionarlo, esta referencia yo suelo añadirla en todos los proyectos. En la siguiente imagen puedes ver el cuadro de diálogo y la referencia que tenemos que seleccionar (recuerda que el idioma de mi Visual Basic está en inglés, así que, seguramente lo que tu veas sea diferente... por el idioma, ya que el resultado es el mismo).

Añadir OLE Automation en Referencias del proyecto

Te recuerdo que esto que acabamos de hacer no tiene nada que ver con el método Equals, sino para que nuestra clase-colección se pueda usar con bucles For Each.

¡Ahora sí!Veamos el código del método Equals, (realmente una función que devolverá True o False, según los elementos comparados sea o no iguales).Primero veremos el código y después te lo explico para que no te quede duda... aunque es muy simple, como podrás comprobar.

Public Function Equals(ByVal unColega As cColega) As Boolean

' Comprueba si el objeto pasado en el parámetro 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,

Page 507: curso básico de programación en visual basic

' al estar anidadas,

' sólo serán iguales si llega a la última comparación.

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 asignación 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 función.Como esta función comparará el objeto actual con otro del mismo tipo, (en este caso del tipo cColega), el parámetro pasado debe ser del tipo cColega. Fíjate que he usado ByVal, pero lo mismo da si se usa ByRef, ya que el Visual Basic no permitirá que el parámetro sea de otro tipo... si lo intentas, recibirás un error del tipo TypeMismatch si el parámetro es un objeto, pero no del tipo cColega, o bien un error indicándote que el parámetro debe ser un objeto.

Aún así... es decir, sabiendo que a esa función sólo llegarán objetos del tipo cColega, he añadido una comparación que comprueba que realmente el objeto es del tipo cColega, esto sólo es para que sepas cómo comprobarías que el objeto sea de ese tipo... ya que es útil si nos decidiéramos a cambiar el tipo del parámetro por otro más genérico, ya sea Object o Variant, (preferiblemente Variant, si lo quieres hacer "súper" genérico); de esta forma, el Visual Basic no daría ningún error y será el código de la función el que se encargue de comprobar si son o no iguales.

Esta sería la forma de declarar la función, ya que el código contenido en ella seguiría siendo el mismo que hemos visto hace unas líneas:

Public Function Equals(ByVal unColega As Variant) As Boolean

Es tu decisión utilizar una u otra forma, todo depende de cómo quieras que el Visual Basic actúe e incluso cómo 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

Page 508: curso básico de programación en visual basic

función es del tipo correcto o no, utiliza la primera versión, 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 opinión personal... pues...)Vale, ¿cual te recomiendo que uses? Yo me inclinaría por la versión "más estricta", ya que se supone que ese método 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 Géminis 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 versión, la que tiene el parámetro del tipo Variant.De esta forma, podremos hacer algo como esto:(se supone que tenemos una variable a nivel de módulo 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

También podríamos hacer esto, y funcionaría correctamente:

' comparar un objeto del tipo cColega con otro de otro tipo diferente

Dim otro As Variant

'

Set otro = mColega

'

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 diría que son diferentes y en el segundo que son iguales.

Page 509: curso básico de programación en visual basic

Nota:En el segundo ejemplo, en el que se asigna a la variable otro el objeto del tipo cColega, también se podría usar la primera versión de la función Equals.

Bien, ya que sabemos cómo comprobar si dos objetos son iguales... (¡Eh! ¡Guille! que dijiste que ibas a explicar cómo funcionaba esto... y te has enrollao tanto con lo de los tipos... que...) ...esto... pues sí... veamos cómo funciona todo el código...Ya ha quedado claro para que usamos el TypeOf: para comprobar si el objeto pasado en el parámetro 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 comparación dentro de otra, de forma que sólo 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 función devolverá un valor falso... cosa que se hace en la última línea de la función al devolver el valor contenido en la variable iguales.Sólo decirte, que la parte Else de la comparación 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 código completo de la función Equals es este:

Public Function Equals(ByVal unColega As Variant) As Boolean

' Comprueba si el objeto pasado en el parámetro 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,

' sólo serán iguales si llega a la última comparación.

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

Page 510: curso básico de programación en visual basic

End If

Equals = iguales

End Function

Ahora sí... ¡por fin! podremos ver lo que realmente teníamos que ver...

Crear clases personalizadas

Vamos a empezar la clase-colección desde el principio, para que no te pierdas ningún detalle.Para empezar, añade una nueva clase al proyecto, en la ventana de propiedades, cámbiale el nombre para que sea cColegas.Muestra la ventana del código y añade la siguiente declaración:

Option Explicit

Private mCol As Collection

El Option Explicit se supone que ya estaría... si es que sigues mis consejos... y si no es así... yo de ti me lo pensaría "forastero" (musiquilla de spaghetti-western)A continuación declaramos un objeto del tipo Collection que será el que se encargue de contener los objetos de nuestra colección.Con esta línea, 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 colección.Por otro lado, cuando un objeto se destruye, se ejecuta el evento Class_Terminate, por tanto será en ese procedimiento donde destruiremos nuestra colección.Veamos el código 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 declaración, ya que si declaras la variable de esta otra forma:

Private mCol As New Collection

Page 511: curso básico de programación en visual basic

Consigues lo mismo, pero también añades "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 código:

i = mCol.Count

el Visual Basic realmente haría lo siguiente:

If mCol Is Nothing Then

Set mCol = New Collection

End If

i = mCol.Count

Es decir, comprueba si ya está creada la colección 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í... apúntatelo y que no se te olvide...

Una vez aclarado estoy ya que tenemos el código que declara el objeto Collection que usará nuestra clase-colección, empecemos por los métodos que suelen incluirse en todas las colecciones.

Count, nos indicará cuantos elementos hay en la colección, la codificación de este método es bien simple, ya que simplemente llamamos al método con el mismo nombre que incorpora la colección.Este método (realmente es una función), devolverá cero si no hay ningún elemento en la colección o la cantidad de elementos que tengamos almacenados.Veamos el código del método Count:

Public Function Count() As Long

Count = mCol.Count

End Function

Remove, eliminará un elemento de la colección.El código también es bastante simple, ya que utilizaremos el método Remove del objeto mCol:

Public Sub Remove(ByVal Index As Variant)

mCol.Remove Index

End Sub

El parámetro 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 numérico que será la posición del objeto que queremos borrar, el

Page 512: curso básico de programación en visual basic

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 colección, se producirá un error, por tanto podríamos añadir detección 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 "prestaría" atención y podría creer que está trabajando de forma correcta, por tanto vamos a dejar el código así, tal como está.

Si quieres crear una versión del método Remove que tenga en cuenta lo que te acabo de comentar, tendrás que declarar Remove como una función 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 versión de Remove no te lo muestro, para que lo hagas como "ejercicio".

Add, para añadir nuevos elementos a la colección.El método para añadir nuevos elementos a la colección lo podemos escribir de varias formas:1- Tal como se hace con las colecciones de Visual Basic:

mCol.Add Item, Key, before, after

2- Añadiendo un objeto del tipo que la colección contendrá y opcionalmente indicando la clave:

mCol.Add unColega

mCol.Add unColega, Key

Aunque esta forma de hacerlo sería igual que el del punto 1, ya que en esa forma de hacerlo, el único parámetro obligatorio es el primero y los demás 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

Page 513: curso básico de programación en visual basic

Set oColega = mColegas.Add("Guille", "[email protected]")

El cual también permitiría añadir elementos a la colección sin necesidad de "recibir" el objeto:

mColegas.Add "otro Guille", "[email protected]"

4- Una mezcla de los otros:-Si el primer parámetro es un objeto del tipo cColega funcionará como lo indicado en el punto 2-Si el primer parámetro NO es un objeto del tipo cColega, supondremos que es el nombre.De esta forma podríamos añadir objetos a la colección de cualquiera de estas formas:

Set oColega = New cColega

oColega.Nombre = "el Guille"

oColega.email = "[email protected]"

oColega.FechaNacimiento = "07/06/1957"

'

mColegas.Add oColega

mColegas.Add oColega, "[email protected]"

mColegas.Add oColega, "[email protected]", "07/06/1957"

mColegas.Add "el Guille", "[email protected]"

mColegas.Add "el Guille", "[email protected]", "07/06/1957"

Set oColega = mColegas.Add("el Guille")

Set oColega = mColegas.Add("el Guille", "[email protected]")

Set oColega = mColegas.Add("el Guille", "[email protected]", "07/06/1957")

Si después de estos ejemplos no te sabes mi dirección de correo... en fin.

Es decir, podemos crear nuevos objetos de formas muy variadas...Pero lo interesante es saber ¿cómo se hace esto?Y la verdad es que no se si sería conveniente mostrarte el código... (venga Guille, no te hagas de rogar)No es por hacerme de rogar ni aparentar "suspense", es que puede ser que te líes... así que, lo mejor es mostrarlos todos, para que vayas comprendiendo mejor las opciones. (y así de camino la entrega es más larga... ¡no sabes ná!)

1- Al estilo del objeto Collection de Visual Basic:

Public Sub Add(ByVal Item As cColega, _

Page 514: curso básico de programación en visual basic

Optional ByVal key As String, _

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 parámetros, los tres últimos opcionales. El primero debe ser del tipo cColega, ya que esta colección sólo admitirá objetos de ese tipo.

2- Este es como el anterior, pero sin los dos últimos parámetros, aunque el segundo será opcional.

3- En esta forma de hacerlo, los parámetros serán equivalentes a las propiedades del objeto, por tanto se usarán para crear un nuevo objeto del tipo cColega y ese nuevo objeto se añadirá a la colección y se devolverá por la función.

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 recién creado

Set Add = tColega

End Function

4- Por último, la versión "multi-uso", que se podrá usar como cualquiera de las dos anteriores:

Public Function Add(ByVal uno As Variant, _

Optional ByVal elEmail As String, _

Page 515: curso básico de programación en visual basic

Optional ByVal laFecha As String) As cColega

Dim tColega As cColega

'

' si el tipo del primer parámetro es un objeto del tipo cColega

If TypeOf uno Is cColega Then

Set tColega = uno

' si se indican los otros parámetros...

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 parámetro 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 recién creado

Set Add = tColega

End Function

Para conseguir nuestro objetivo, el primer parámetro será de tipo Variant, con idea de que permita "cualquier" tipo de dato en ese parámetro.A continuación comprobamos si dicho parámetro es un objeto del tipo cColega, de ser así, se asignará a la variable que hemos creado dentro del procedimiento y opcionalmente se asignarán el resto de los parámetros, siempre que éstos se hayan indicado con algo diferente a una cadena vacía.Si el primer parámetro 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, deberíamos especificar también el email, ya que es esa propiedad la que usamos como clave del objeto, por la sencilla razón de que no

Page 516: curso básico de programación en visual basic

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 parámetro no lo fuera, se produciría un error.

Item, nos permitirá acceder a un elemento de la colección, bien indicando el valor de la clave o una posición dentro de la colección.Por tanto podríamos acceder a un elemento de la colección de cualquiera de estas dos formas:

mColegas.Item("[email protected]").Nombre = "Guille"

mColegas.Item(2).Nombre = "Guille"

El código más simple para esta función sería 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 colección.Pero eso podemos solventarlo con algunos de los métodos que después añadiremos.

Lo que sería interesante es poder usar este método sin tener que indicar la palabra Item, tal como se hace en las colecciones propias de Visual Basic, de forma que esas dos asignaciones pudiéramos hacerlas de esta otra forma:

mColegas("[email protected]").Nombre = "Guille"

mColegas(2).Nombre = "Guille"

Para conseguirlo, tenemos que indicarle al Visual Basic que el método Item es el método por defecto de la clase.Para ello, tenemos que posicionarnos en la declaración 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:

Page 517: curso básico de programación en visual basic

Del cuadro de diálogo que se muestra, selecciona Item de la lista superior, pulsa en el botón 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:

Para que una propiedad sea la predeterminada

Y después de pulsar Intro para aceptar las selecciones, el método Item será el predeterminado y por tanto no será necesario indicarlo para usarlo.

Crear un "enumerador" para nuestra colección.Para terminar con la colección, (no te preocupes que no me he olvidado de que hay más cosas, me refiero a terminar con los métodos que se incluyen en las colecciones normales), vamos a crear un método "especial" que permita recorrer los elementos de la colección usando For Each.Para conseguir nuestro objetivo, vamos a crear un método que se llame NewEnum y que sea del tipo IUnknown, no voy a entrar en detalles del "por qué", simplemente veremos el código y la forma de "configurar" este procedimiento en el cuadro de diálogo del atributos de procedimientos.Empecemos por el código:

Public Function NewEnum() As IUnknown

' Debe ser un miembro oculto y

' el id del procedimiento debe ser -4

'

Set NewEnum = mCol.[_NewEnum]

Page 518: curso básico de programación en visual basic

End Function

Sólo aclararte que los corchetes son necesarios ya que el nombre de la propiedad contiene un carácter no válido, (empieza por un guión bajo), éste indica que es un miembro oculto...Pero sólo con esto no conseguimos nuestro objetivo, tal como se indica en los comentarios, el procedimiento NewEnum debe estar oculto y tener un ID de procedimiento con valor -4, esto lo hacemos mediante el cuadro de diálogo usado para asignar la propiedad predeterminada, tal como podemos ver en la siguiente imagen:

Para crear un enumerador que permita recorrer la colección usando For Each

Una vez hecho esto, podemos recorrer los elementos de la colección de esta forma:

' mostrar el contenido de la colección

Dim oColega As cColega

'

Text1 = ""

For Each oColega In mColegas

Text1 = Text1 & oColega.Nombre & ", " & _

oColega.email & ", " & _

Page 519: curso básico de programación en visual basic

oColega.FechaNacimiento & vbCrLf

Next

Aunque también podemos hacerlo de la forma clásica:

Dim i As Long

'

Text1 = ""

For i = 1 To mColegas.Count

Text1 = Text1 & mColegas(i).Nombre & ", " & _

mColegas(i).email & ", " & _

mColegas(i).FechaNacimiento & vbCrLf

Next

Ampliando la funcionalidad de la colección.Bien, con lo visto hasta ahora, tenemos la misma funcionalidad que la mayoría de las colecciones de Visual Basic.Vamos a añadir nuevos métodos.

Clear, eliminar el contenido de la colección.Empezaremos con uno que nos permita eliminar el contenido de la colección y así dejarla preparada para añadir nuevos elementos en una colección totalmente vacía.Si no tuviéramos este método, tendríamos que hacerlo de esta forma:

Set mColegas = Nothing

Set mColegas = New cColegas

Y eso es precisamente lo que haremos en el método Clear... es que es la forma más rápida y "limpia" de eliminar los elementos, pero por supuesto lo que borramos es el contenido de la colección privada de nuestra clase-colección.

Public Sub Clear()

Set mCol = Nothing

Set mCol = New Collection

End Sub

Exists, comprobar si un elemento está incluido en la colección.Para comprobar si un elemento está en la colección podemos hacerlo de dos formas, una sería recorriendo cada uno de los elementos de la colección y comprobar si el indicado está contenido... pero hay otra forma más fácil, y puede que más rápida, que

Page 520: curso básico de programación en visual basic

es aprovecharse de que se produce un error cuando se quiere acceder a un elemento y dicho elemento no existe... veamos el código 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

If Err.Number <> 0 Then

Exists = False

Else

Exists = True

End If

End Function

Nota:A este método también podríamos llamarlo Contains, (que es como suele llamarse en las colecciones de la nueva versión .NET de Visual Basic).

Clone, crear una copia "independiente" de la colección.Tal como hicimos con la clase cColega, vamos a crear un método que permita hacer una copia independiente de la colección.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 sólo existe un objeto en la memoria.Pero mediante este método creamos otra copia en la memoria, de forma que los cambios realizados en ella no afectarán al original.El código es bien simple, ya que vamos a aprovecharnos de que la clase cColega ya tiene un método que permite crear copias de ese objeto.Veamos el código:

Public Function Clone() As cColegas

' Hacer una copia de esta colección

'

Dim tColega As cColega

Dim tColegas As cColegas

'

Set tColegas = New cColegas

'

Page 521: curso básico de programación en visual basic

' Añadir a la nueva colección 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 colección del mismo tipo de la clase, recorremos cada uno de los elementos contenidos y añadimos una copia, con idea de que se cumpla nuestro objetivo, tener copias independientes.

Y para terminar (¡ya era hora!), nos queda por ver cómo sería el código para el método para saber si dos colecciones son iguales:

Equals, saber si dos colecciones son iguales.El código de este método es algo diferente al de la clase normal, entre otras cosas porque no sólo tenemos que comprobar si las propiedades de cada uno de los elementos son exactamente iguales, (aunque de ese detalle se encarga el método Equals de cada uno de los objetos contenidos en la colección), sino que también 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 código:

Public Function Equals(ByVal compararCon As Variant) As Boolean

Dim iguales As Boolean

Dim oColegas As cColegas

Dim oColega As cColega

'

' Sólo serán iguales si es un objeto del tipo cColegas

If TypeOf compararCon Is cColegas Then

Set oColegas = compararCon

' sólo serán iguales si tienen los mismos elementos

If oColegas.Count = mCol.Count Then

' asumimos que serán iguales

iguales = True

For Each oColega In mCol

' si no existe o no son iguales...

If oColegas.Exists(oColega.email) = False Then

iguales = False

Page 522: curso básico de programación en visual basic

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 métodos, ya que si usamos la variable pasada como parámetro, al ser del tipo Variant, no nos mostraría los miembros).A continuación comprobamos que el número 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 función 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 colección, 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 podría haber simplificado usando sólo esta comparación:

If oColega.Equals(oColegas(oColega.email)) = False Then

iguales = False

Exit For

End If

Pero esto daría error si en la colección pasada por parámetro 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 colección, se producirá un error, ya que en el método Item de la colección no se hace ningún tipo de detección 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 más claro...)Por esa razón se hace primero la comprobación de que exista el objeto en la colección.

Page 523: curso básico de programación en visual basic

Y hasta aquí hemos llegado en esta entrega que tanto se ha hecho esperar... aunque confío que con lo que me he enrollado, no te quejes y espero que tampoco te "empaches" demasiado... je, je.

Posiblemente, en la próxima entrega veremos más cosas referentes a las clases... pero no nos precipitemos que si después cambio de idea... lo mismo me regañas...

Nos vemosGuillermoP.S.Aquí tienes el código 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 según parece sólo lo dije "de pasada" en la entrega 38.

For Each, otra forma de hacer bucles.

Este tipo de bucles, a diferencia del clásico bucle For al que hay que indicarle una variable numérica, se utiliza con una variable de un tipo de objeto que debe estar contenido en una colección, de forma que se pueda recorrer cada uno de los elementos de dicha colección que sean del tipo usado junto a For Each.La forma de usarla sería:For Each elemento In laColección' lo que haya que hacer con el elementoNext...

Ha pasado algo más 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í también evitaré que me eches la bronca por olvidarme de actualizar el Curso Básico de Visual Basic... sí, que algunos me regañan y todo... snif!

En esta entrega vamos a continuar con las clases, aunque esta entrega tratará algo que puede que te vuelva un poco más "turuleta" de lo que ya puedas estar... e incluso puede que genere más tráfico de correo a mi cuenta del que a mi me gustaría, ya que puede ser que no te enteres de nada... no, no te estoy llamando ignorante... ¡ni se me ocurriría! 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 gustaría, no quiero que abandones... sólo 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 más 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 acabarás 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, sólo lo relacionado con las clases y la Programación Orientada a Objetos (o lo que más 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)

Page 524: curso básico de programación en visual basic

Visual Basic no es un lenguaje orientado a objetos, eso ya lo sabemos, (y seguro que algunos no los habrán recordado en más de una ocasión), pero hay que entender un poco de ese tema para sacarle el máximo rendimiento a esto de las clases en Visual Basic, así que, de forma resumida, vamos a ver que características 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 río de Italia que suele salir en los crucigramas...) que nuestro querido Visual Basic soporta:

Encapsulación:Esta característica de la OOP es la facultad de unificar el código y los datos que la clase u objeto contiene, así como ocultar el código que maneja dicha información. La encapsulación nos ayuda a olvidarnos de cual es la implementación realizada en los procedimientos (métodos y propiedades) de una clase, para que sólo nos preocupemos de cómo usarlos.

Polimorfismo:Polimorfismo viene del griego y significaría algo así como: muchas-formas. Todos los métodos implementados en las clases deben tener una forma de comportarse, así como ser consistente con la información que debe tratar, ya que, como te he comentado antes, la encapsulación es la característica que permite ocultar cómo están implementados (o codificados) los métodos y propiedades de las clases y el polimorfismo sería 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 "definición" queda algo más claro:Se dice que una clase es polimórfica cuando podemos usar sus métodos y propiedades sin importarnos qué objeto los implementa. Ya que Polimorfismo significa eso: múltiples formas, es decir, si un objeto implementa el método Show, podemos usar ese método 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, según se compruebe si el procedimiento (o miembro de la clase) pertenece al objeto que lo utiliza, en tiempo de diseño o en tiempo de ejecución, esto también es conocido como compilación temprana (early binding) o compilación tardía (late binding) respectivamente.

Además de estas dos características, aclaremos los siguientes puntos:

Métodos:Los métodos son las acciones que una clase puede ejecutar, normalmente son procedimientos de tipo Sub o Function.Para que sepamos cuando un procedimiento es un método, debemos pensar si dicho procedimiento realizará una acción sobre los datos que la clase maneja. Esta aclaración es porque en algunos sitios te dirán que las funciones pueden ser propiedades, pero una función siempre es un método, aunque en ocasiones estén implementadas de forma que puedan hacernos pensar lo contrario.

Propiedades:Las propiedades de una clase (u objeto) son las características de esa clase.Las propiedades podrían ser también las características de los datos que la clase maneja (o manipula).como deberías saber, las propiedades las podemos declarar de dos formas: creando un procedimiento de tipo Property o bien declarando una variable pública.

Page 525: curso básico de programación en visual basic

¿No te queda claro?Por ejemplo, si nuestra clase "maneja" a nuestros colegas, (realmente maneja o trata la información de nuestros colegas, no a nuestros colegas... aunque algunas veces sería interesante que pudiésemos manejar a un colega y "encapsularlo" dentro de una clase, para que nos deje tranquilos... je, je), las propiedades serían los datos o características que tenemos de nuestros colegas, por ejemplo: el nombre, el correo electrónico, la edad, si está loco... por la música, por ejemplo, etcétera...Mientras que los métodos podrían ser las acciones que queremos realizar sobre esos datos: que los muestre, que los borre o que los cambie, incluso podríamos hacer que esa información se almacenara en un archivo o en una base de datos, incluso enviarles de forma automática un mensaje cuando sea su cumpleaños, etc.

Ahora empecemos con los quebraderos de cabeza.

¿Cómo podemos usar la característica de la Encapsulación?

Aquí no tenemos que hacer nada especial, el mero hecho de crear un método o una propiedad en una clase ya implica que estamos usando la característica de la encapsulación.

¿Cómo podemos usar la característica del Polimorfismo?

Como te comenté antes, hay dos formas de implementar (usar) el polimorfismo, según se haga en tiempo de diseño o en tiempo de ejecución.Empecemos viendo cómo 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), podrás asignarle cualquier objeto a dicha variable y acceder a los miembros del objeto, veamos un ejemplo:Si tenemos un objeto del tipo cColega (el código lo vimos en la entrega 37 y se amplió en la entrega 39), podríamos tener algo como esto:

Private Sub cmdLate_Click()

' declaramos las variables

Dim unColega As cColega

' también se puede declarar como As Variant

Dim o As Object

'

' creamos el objeto de tipo colega

Set unColega = New cColega

' y le añadimos algunos datos

unColega.Nombre = "Guille"

unColega.email = "[email protected]"

'

' asignamos el colega (unColega) al objeto (o)

Page 526: curso básico de programación en visual basic

Set o = unColega

' mostramos el nombre del colega

MsgBox "El colega es: " & o.Nombre

'

End Sub

Esto funcionaría como se espera: mostraría el nombre correcto.Esto es Polimorfismo, aunque se use compilación en tiempo tardío (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, el código se compilará correctamente, y no será hasta que esté ejecutándose 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 ejecución.

Por ejemplo, si en lugar de acceder a la propiedad Nombre (que si la tiene), queremos mostrar los apellidos mediante este código:MsgBox "El colega es: " & o.ApellidosComo 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 línea de código:

Error en tiempo de ejecución al acceder a una propiedad que no existe

Pero hasta que no llegue a esa línea no sabremos que había algo mal.Por supuesto, podemos usar detección de errores para que ese mensaje no se muestre, pero eso sólo 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 detección de errores.2- Usar un objeto del tipo específico, 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 diseño, antes de que se compile el programa.

Page 527: curso básico de programación en visual basic

Pero, (¿recuerdas que siempre hay un pero?), ¿qué hacemos si queremos crear un procedimiento que reciba un parámetro de tipo genérico 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 también tiene la propiedad Nombre y otras de las incluidas en cColega, además 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 podríamos hacer es usar como parámetro 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 código de la clase cColega2 (versión simplificada) y el de ese procedimiento:

La clase cColega2:

'------------------------------------------------------------------------------

' 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 sólo el nombre

MsgBox "El colega es: " & elObjeto.Nombre

ElseIf TypeOf elObjeto Is cColega2 Then

Page 528: curso básico de programación en visual basic

' 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 parámetro, después comprobamos si es del tipo cColega y mostramos el nombre, si es del tipo cColega2, mostramos el nombre y los apellidos.

Pero este código, para mi gusto no sería la mejor solución, todavía 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 habrás fijado que en lugar de escribir Apellidos, he escrito Apllidos y esa no es una propiedad de la clase cColega2.

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 sólo 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 más código, evitaremos usar propiedades no existentes, ya que será el propio IDE de Visual Basic el que nos muestre las propiedades y métodos de cada objeto, tal como podemos comprobar en las siguientes imágenes:

Page 529: curso básico de programación en visual basic

Los miembros de cColega

Los miembros de cColega2

Bien, dirás... esto está muy bien, pero... ¿esto es polimorfismo?Pues... si y no... realmente no, aunque podría ser...

La verdad es que no. Al menos si entendemos polimorfismo como una de las características de la programación orientada a objetos.Pero ni el polimorfismo nos solucionaría el problema suscitado por el uso de dos clases (u objetos) diferentes en el método mostrar o mostrar2.

Otra cosa sería que tuviésemos un método Mostrar en cada una de las clases y en el procedimiento mostrar del formulario se usara dicho método para que se muestre lo que se tenga que mostrar, en el caso de cColega sólo se mostraría el Nombre y en el caso de cColega2 se mostraría el Nombre y Apellidos, veamos cómo declarar esos métodos en cada una de las clases y cómo se usaría:

El método Mostrar de la clase cColega:

Public Sub Mostrar()

MsgBox "El nombre es: " & Nombre

Page 530: curso básico de programación en visual basic

End Sub

El método Mostrar de la clase cColega2:

Public Sub Mostrar()

MsgBox "El nombre es: " & Nombre & " " & Apellidos

End Sub

El método mostrar3 del formulario de prueba:

Private Sub mostrar3(elObjeto As Object)

If TypeOf elObjeto Is cColega Then

Dim colega1 As cColega

Set colega1 = elObjeto

' llamar al método Mostrar del objeto

colega1.Mostrar

ElseIf TypeOf elObjeto Is cColega2 Then

Dim colega2 As cColega2

Set colega2 = elObjeto

' llamar al método Mostrar del objeto

colega2.Mostrar

End If

End Sub

La llamada al método 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 añadimos algunos datos

unColega.Nombre = "Guille"

unColega.email = "[email protected]"

Page 531: curso básico de programación en visual basic

'

' 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

unColega2.Nombre = "Guille"

unColega2.Apellidos = "Som Cerezo"

unColega2.email = "[email protected]"

' lo mostramos

mostrar3 unColega2

End Sub

Aquí tenemos que observar que el código 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 método Mostrar se mostrarán los datos (esto es encapsulación).Por otro lado, en el método mostrar3 usamos dichos métodos para que se muestre el mensaje que corresponda (esto podría ser polimorfismo, pero aún no lo es).

Para que realmente pudiéramos aprovecharnos de las características del polimorfismo, deberíamos poder crear, (en el formulario), un método mostrar que nos permitiera llamar al método Mostrar del objeto pasado como parámetro 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 métodos 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 tendrán una definición de dichas propiedades y métodos 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 genérica, sin necesidad de usar un objeto intermedio o genérico.Por ejemplo, supongamos que tenemos una interfaz llamada IColega, si dicha interfaz (o interface si lo prefieres), tiene un método Mostrar y tanto la clase cColega como cColega2 la implementan, podríamos mostrar los datos del colega usando el siguiente método del formulario:

Page 532: curso básico de programación en visual basic

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

Aquí comprobamos si el objeto es del tipo IColega, si es así, se llama al método Mostrar, después de hacer una asignación del objeto pasado como parámetro a una variable del tipo IColega, en caso de que no sea de ese tipo, se mostrará un mensaje de aviso.

La declaración de la interfaz (realmente es una clase) IColega sería 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 código

' sólo es necesario definirlo

End Sub

Como podemos comprobar, la declaración de una interfaz no tiene porqué contener código, sólo es necesario indicar cómo se deben declarar los miembros que dicha clase contenga.

Si tanto la clase cColega como cColega2 han firmado un contrato para utilizar el método 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:

Page 533: curso básico de programación en visual basic

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 añadimos algunos datos

unColega.Nombre = "Guille"

unColega.email = "[email protected]"

'

' asignamos el colega (unColega) al objeto (o)

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 = "[email protected]"

' lo mostramos

mostrar4 unColega2

'

' incluso podríamos llamar al método con otro objeto

mostrar4 Command1

End Sub

En este caso, el código es casi como el del tercer ejemplo (cmdMostrar3), sólo que en lugar de llamar a mostrar3, se llama a mostrar4. También hemos añadido una nueva llamada al método mostrar4, pero pasándole como parámetro cualquier otro objeto, en este caso hemos pasado un parámetro del tipo Command, que por supuesto no implementa IColega, con lo cual se mostrará un aviso indicándonos que no implementa esa interfaz.

Seguramente la pregunta que te harás es:¿Puedo usar ese código directamente y se usará el método Mostrar de esas dos clases?

Page 534: curso básico de programación en visual basic

La respuesta es: No.A pesar de que tanto la clase cColega como cColega2 tengan un método llamado Mostrar no significa que hayan firmado un contrato con la interfaz IColega.Por tanto, no podríamos usar ese código, al menos tal y como están declaradas esas dos clases.Para poder usar ese código, las dos clases deberían firmar un contrato con IColega.

Entonces, ¿cómo firmo ese contrato para las clases cColega y cColega2?

Utilizando la instrucción Implements.

Cuando usamos la instrucción 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 código de un evento de un formulario, tal como podemos ver en la siguiente captura:

Para que se pueda mostrar ese nuevo objeto, tendremos que añadir la siguiente línea a ambas clases:

Implements IColega

Después de hacer esto, tendremos el objeto IColega en esa lista desplegable y al seleccionarla, en la derecha nos mostrará los métodos que dicha interfaz contiene, en este caso sólo tendrá uno: Mostrar.Y si lo seleccionamos se mostrará el siguiente código:

Private Sub IColega_Mostrar()

End Sub

Dentro tendremos que escribir el código 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 código que ya hemos escrito, simplemente llamamos al método Mostrar escrito en nuestra clase, con lo cual, el código completo quedaría así:

Private Sub IColega_Mostrar()

Page 535: curso básico de programación en visual basic

Me.Mostrar

End Sub

Si la clase IColega tuviera más de un método, tendríamos que escribir código en todos los procedimientos que dicha clase contenga, ya que si implementamos una interfaz, debemos implementar todos los métodos 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 código completo de la clase cColega2 para que no te quede duda.

'------------------------------------------------------------------------------

' cColega2 (21/Dic/02)

' 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

Page 536: curso básico de programación en visual basic

Fíjate que el método IColega_Mostrar está declarado como Private, pero eso no debe preocuparte.

Si hacemos lo mismo con la clase cColega, podremos usar el código mostrado anteriormente sin ningún 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 sólo una parte del código de la clase representada por elObjeto, la parte que implementa dicha interfaz.Es decir, si la variable elObjeto fuese del tipo cColega, no estaríamos asignando todo el objeto, sólo una parte del mismo.Esto podemos comprobarlo si queremos acceder a la propiedad Nombre:s = elColega.NombreEsto daría error, ya que el objeto elColega, (que como sabemos es del tipo IColega), no tiene una propiedad llamada Nombre.

Sólo me queda decirte que en Visual Basic todas las clases se pueden usar con la instrucción Implements. Es decir, no tenemos porqué crear una clase específica para crear una interfaz.Pero te recomiendo que lo hagas así, para que sea más evidente y sobre todo porque así es como tendrás que hacerlo cuando "migres" a la versión .NET de Visual Basic.

Ahora sí, hasta aquí hemos llegado en esta ocasión.En la próxima entrega seguiremos viendo más cosas relacionadas con las clases y, casi con seguridad, un ejemplo más práctico sobre la ventaja de usar esto de la implementación de interfaces.

Pero eso será... casi con seguridad, el año 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 año, desear cosas buenas a la gente... je, je.

Nos vemosGuillermo

Aquí tienes el fichero zip con el código usado en esta entrega: basico43_cod.zip 5.02 KB

Algunos de los que me escriben sobre este Curso Básico de Programación 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 razón 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 extrañar, pero... es cierto, no tengo un planteamiento sobre el contenido, por eso en algunas entregas te digo que en la

Page 537: curso básico de programación en visual basic

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 también trataremos otro tema que, casi con seguridad te será de utilidad en algunas aplicaciones: cómo hacer una ventana de configuración que sea algo "inteligente", es decir, que tengamos un botón Aplicar y si modificamos el contenido de las opciones presentadas, ese botón se habilite, pero si después de hacer cambios, volvemos a dejar todo como estaba originalmente, ese botón se deshabilite.Para conseguirlo, usaremos una clase en la que estarán los valores de cada una de las opciones que la ventana de configuración va a manejar, de forma que se use esa clase para comunicarse con el formulario que llame a la ventana de configuración. También crearemos una clase/interfaz, que nos sirva para implementarla tanto en el formulario de configuración como en la clase usada para mantener esos datos.Seguramente, cuando veas el código, pensarás que no era necesario usar clases ni interfaces, y estarás en lo cierto, pero... como el tema que estamos tratando en estas últimas entregas, es precisamente las clases, el polimorfismo y las interfaces... pues eso...

Así que, sin más preámbulos, empecemos, (o mejor dicho), sigamos con esta entrega.

Antes de empezar a mostrarte el código, voy a explicarte un poco el planteamiento que vamos a hacer, para que te resulte más fácil seguir el código 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 gustaría es que "siempre" te quedes con la base de lo que te explico, no que uses el código directamente, ya que con ello no llegarás a aprender, y de lo que aquí se trata es que aprendas a hacer las cosas para que después lo apliques como más te convenga.

Como te comentaba en la entrega anterior, en la versión 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 implementación y otras de esas monerías de la OOP), se puede usar cualquier clase como una interfaz a implementar. Esa clase puede contener código, aunque cuando se implementa ese código "desaparece" y sólo se queda "la firma" o estructura de la clase, es decir los métodos y propiedades públicas que contenga. También te recomendé que en lugar de usar una clase para realizar la implementación, crearas una clase en la que sólo estén 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 fácil recomendar como hacer las cosas y después hacerla de una forma diferente.

Una cosa que debes saber, no se si ya te lo he dicho, es que cuando declaramos una variable pública en una clase, esa variable se convierte automáticamente en una propiedad. Por tanto, si sólo declaramos las variables, en lugar de crear propiedades propiamente dichas, cuando implementemos esa clase, se crearán los correspondientes procedimientos Property Let y Property Get. Te digo esto para que no te extrañes 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

Page 538: curso básico de programación en visual basic

lugar de declarar las variables, realmente sólo se declararán 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 aplicación que vamos a usar de ejemplo, tendrá un formulario principal y otro que será el que se utilice para configurar ciertos valores, además tendremos dos clases, una de ellas será la interfaz y la otra, la clase propiamente dicha, en la que se almacenarán los datos que usaremos.Seguramente, cuando veas el código y el diseño de los formularios, te parecerán iguales o muy parecidos, esto es así, para simplificar, ya que no es plan de hacer una aplicación demasiada "completa" para ver el "corazón" de lo que aquí te quiero explicar. Por supuesto, tendrás que hacer algo por tu propia cuenta si quieres que el ejemplo sea más "sofisticado", ya que, como te he comentado al principio, lo que me interesa enseñarte es cómo hacerlo, no darte el código completo para que lo copies y pegues... es que si no pones de tu parte, no llegarás a aprender... ya que considero que con todo lo que hasta ahora he explicado en este curso básico, deberías tener la "base" suficiente para que no te resulte difícil investigar por tu cuenta y riesgo, de forma que amplíes los conocimientos que, se supone, hasta ahora has ido acumulando.

Veamos el aspecto del formulario principal, así como el de configuración.Como te he comentado, el formulario principal tendrá una serie de controles, los cuales se usarán para almacenar unos datos y esos datos se podrán modificar con el de configuración, 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 imaginación e imagines que el formulario principal hace más 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í comprobarás si has aprendido algo...

Este es el aspecto de los dos formularios que usaremos en este ejemplo:

Fig. 1, el formulario principal

Page 539: curso básico de programación en visual basic

Fig. 2, el formulario de configuración

En el formulario de configuración, el botón Aplicar se habilitará o deshabilitará según se hayan o no modificado los datos, cuando se pulse en dicho botón, se asignarán los nuevos valores a una variable basada en una clase.Cuando se pulse en Aceptar, también se asignarán esos valores y se ocultará el formulario para que el formulario principal tome nuevamente el control.Cuando se pulse en Cancelar, se descartarán los cambios y se ocultará el formulario para poder volver al formulario principal.

Nota (o truco):Si a la propiedad StatUpPosition del formulario de configuración le asignas el valor 1 - CenterOwner, hará que se muestre centrado con respecto a la posición 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 configuración, el botón 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 botón 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 botón actúe como receptor de la tecla Escape, hay que asignar un valor True a la propiedad Cancel.

Después veremos el código de este formulario, aunque antes hay que saber cómo hacer que se muestre dicho formulario... y esperar a que se elijan las opciones.

Cómo llamar a un formulario desde otro

Aunque esto ya lo hemos visto, (creo), vamos a verlo nuevamente, aunque sea de pasada.

Page 540: curso básico de programación en visual basic

Para que un segundo formulario se muestre, podemos hacerlo de varias formas, aunque la forma en que dicho formulario se comportará sólo puede ser de dos formas distintas:

1- Que se muestre, pero que el formulario que lo ha llamado (o mostrado) siga ejecutándose, es decir no se espera a que el segundo formulario termine. Esto puede ser útil cuando necesitamos que ese segundo formulario actúe 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 configuración.

2- Que se muestre, pero que hasta que no se cierre u oculte, el formulario que lo ha llamado no continúe. Este caso es el que nos interesa, además de que es el que normalmente utilizan los formularios de configuración, también llamados "cuadros de diálogo".

Para el primer caso se usaría lo siguiente:NombreFormulario.Showo también de esta forma:NombreFormulario.Show vbModelessEs decir, muéstralo pero en forma no-modal (o no como cuadro de diálogo).

Para el segundo caso, habrá que indicarle al método Show que se quiere mostrar en modal, es decir como un cuadro de diálogo, 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 mostraría el formulario, pero se continuaría en la siguiente línea, por ejemplo:

Form2.Show

MsgBox "Después de mostrar el formulario"

En este caso, se mostraría el formulario indicado (Form2) y justo después de mostrarse, se ejecutaría la instrucción MsgBox y el formulario seguiría abierto.

Pero si lo mostramos de forma modal:

Form2.Show vbModal

MsgBox "Después de mostrar el formulario"

La instrucción MsgBox no se ejecutaría hasta que no se cerrara u ocultara el Form2. Es decir, esperaría a que se cierre el formulario antes de continuar con la siguiente instrucción.

En el ejemplo que veremos aquí se usa esta segunda forma.

Page 541: curso básico de programación en visual basic

Una vez hechas estas aclaraciones, veamos el código, empezando por la interfaz IConfig, la cual se implementará tanto en la clase "real" cConfig, como en el formulario de configuración, al cual llamaremos fConfig.

La clase (interfaz) IConfig:

'------------------------------------------------------------------------------

' IConfig (06/Ene/03)

' Interfaz para usar con un formulario de configuración

'

' ©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, sólo se definen los procedimientos de lectura (Property Get), pero no se utiliza ningún código "operativo", ya que esta clase sólo se usa como "plantilla".

La clase cConfig:

Page 542: curso básico de programación en visual basic

'------------------------------------------------------------------------------

' cConfig (06/Ene/03)

' Clase para usar con un formulario de configuración

'

' ©Guillermo 'guille' Som, 2003

'------------------------------------------------------------------------------

Option Explicit

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 parámetro en los valores de la clase

Me.Opcion1 = newValue.Opcion1

Page 543: curso básico de programación en visual basic

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

'

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 código está omitido para que lo hagas como ejercicio

'

End Sub

Page 544: curso básico de programación en visual basic

Public Sub Leer(ByVal fichero As String)

' leer del fichero indicado los valores de las propiedades de la clase

'

' el código está omitido para que lo hagas como ejercicio

'

End Sub

En esta clase, hemos implementado IConfig, para que al acceder a esa "parte" del código, se obtengan los datos de las propiedades.También se han implementado algunos métodos, los cuales servirán para Leer y Guardar información en un fichero de disco, (ese código es tarea tuya), por otro lado, tenemos un método que nos permitirá efectuar una copia del objeto: Clone, otro que comprobará si el contenido de esta clase es idéntico al de otra del mismo tipo: Equals y por último, un método, (CopiarIConfig), que recibe como parámetro un objeto del tipo IConfig, que se usará para asignar a las propiedades de la clase los valores indicados en el parámetro, este método 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.Fíjate que el método Clone, hace uso de ese método para realizar la copia de los datos del objeto actual en la nueva variable que devuelve.

El código del formulario principal:

'------------------------------------------------------------------------------

' fEntrega44 (06/Ene/03)

' Pruebas para la entrega 44 del Curso Básico 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"

Page 545: curso básico de programación en visual basic

'

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

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 práctica, la cual te recomiendo encarecidamente.

Dim fc As fConfig

'

Page 546: curso básico de programación en visual basic

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 configuración

' con algunos datos

Dim i As Long

'

For i = 1 To 10

fc.Combo1.AddItem "Valor de prueba número " & CStr(i)

Next

'

' Asignamos los valores a la clase

mConfig.Valor1 = Text1

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 configuración

If fc.Cancelado = False Then

' sólo asignar los valores si no se ha cancelado

'

' asignamos los nuevos valores a la copia local de este formulario

Page 547: curso básico de programación en visual basic

' si se pasa como parámetro del tipo IConfig, funcionará usando el formulario

asignarConfig fc

'

End If

'

' descargamos el formulario de configuración 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

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 código 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 guardarán los datos (sFic).En el evento Form_Load, asignamos el nombre del fichero, el cual estará en el mismo directorio que la aplicación, para ello utilizamos App.Path, a continuación creamos una nueva instancia del objeto mConfig, le asignamos unos valores "de prueba" y llamamos al método asignarConfig para que se reflejen en los controles los valores de la clase.

Cuando se pulsa en el botón Cerrar, se descarga el formulario principal, con lo cual también se termina la aplicación: Unload Me.Al pulsar en el botón Guardar, se llama al método Guardar de la clase cConfig y se

Page 548: curso básico de programación en visual basic

le pasa como parámetro el nombre del fichero en el que se guardará el contenido de esa clase. Fíjate que será el propio método Guardar de la clase el que se encargue de abrir ese fichero y guardar el contenido de las propiedades.Cuando se pulse en el botón Leer, se llama al método Leer del objeto mConfig, (que es del tipo cConfig), el cual se encargará de leer la información del fichero pasado como argumento (o parámetro), ese método (el de la clase) será el que se encargue de, además de leer la información del fichero, comprobar si ese fichero existe, etc. Una vez leídos los datos, se tendrá que reflejar esa información en los controles del formulario, esto también es algo que debes hacer como ejercicio, sólo comentarte que la solución es muy simple y ya está hecha... o casi.Cuando el usuario pulse en el botón Configurar, tendremos que mostrar el formulario de configuración, 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 información en el formulario principal, en otras ocasiones, es posible que esa información 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 método o forma de comunicación entre los formularios y los datos a configurar.

Vamos a hacer una pequeña "parada" o repaso más detallado sobre el código usado en el evento del botón 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 configuración. Realmente no es necesario ni obligatorio hacerlo de esta forma, aunque te recomendaría que te acostumbraras a hacerlo así.La forma más sencilla, hubiera sido usar directamente el nombre que le hemos asignado al formulario: fConfig, ya que el Visual Basic declara y crea una variable "global" con dicho nombre. Por tanto, si eliminas o comentas las líneas 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 actúan 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 preguntarás ¿por qué esa complicación extra?Pues, porque sí... porque así puedes comprobar que puedes usar un formulario igual que cualquier otra clase y también porque así es más evidente cual es nuestra intención, si ese código lo ve cualquier otra persona, sabrá exactamente cual es tu intención.Recuerda que quiero enseñarte "buenos modales" en esto de la programación, por tanto, siempre tendrás la libertad de hacerlo de la forma que más te guste: como el Guille te recomienda o "a las bravas", tu eres quién tiene la última palabra.

Después de esta pequeña aclaración sobre modales, sigamos examinando el código:Después 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. Fíjate que en el formulario principal estamos usando una caja de textos para el contenido de la propiedad Valor3 y en el de

Page 549: curso básico de programación en visual basic

configuración, (como comprobarás dentro de poco), se está usando un ComboBox.Una vez que la variable mConfig tiene asignado los valores de las propiedades, la pasamos con parámetro del método AsignarIConfig que hemos implementado en el formulario de configuración, esto hará, (como verás 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 configuración tendrá toda la información que necesita, por tanto lo mostramos de forma modal (como cuadro de diálogo), usando fc.Show vbModal, de esta forma, el código del formulario principal "esperará" a que el formulario de configuración 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 configuración, tal como podrás comprobar cuando veamos el código que falta.Si no se ha cancelado, asignaremos a la variable mConfig del formulario principal los valores que se han asignado en el de configuración, para ello utilizamos el método asignarConfig, al cual se le pasa como parámetro el formulario (fc), pero ese método acepta un objeto del tipo IConfig, por tanto Visual Basic filtrará el formulario para que sólo 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 mías!

Para acabar con la explicación del código del formulario principal, sólo nos queda ver el procedimiento asignarConfig.Este procedimiento (o método privado) del formulario, recibe como parámetro un objeto del tipo IConfig, por tanto cualquier objeto de ese tipo o que lo implemente, se puede usar como parámetro.Lo que se hace en ese procedimiento, es hacer una copia en la variable mConfig, (por medio del método CopiarIConfig), de los datos contenidos en el objeto pasado como parámetro, así nos aseguramos de que la variable siempre esté actualizada. También 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 código.

El código del formulario de configuración:

'------------------------------------------------------------------------------

' fConfig (06/Ene/03)

' Pruebas para la entrega 44 del Curso Básico de VB

'

' ©Guillermo 'guille' Som, 2003

'------------------------------------------------------------------------------

Page 550: curso básico de programación en visual basic

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

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

Page 551: curso básico de programación en visual basic

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)

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

Page 552: curso básico de programación en visual basic

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

'

cmdAplicar.Enabled = datosCambiados

End Sub

Public Sub AsignarIConfig(ByVal newValue As IConfig)

' esta asignación sólo funcionará si newValue es del tipo cConfig

'Set mConfig = newValue

mConfig.CopiarIConfig newValue

'

Text1 = mConfig.Valor1

Text2 = mConfig.Valor2

Page 553: curso básico de programación en visual basic

Combo1.Text = mConfig.Valor3

If mConfig.Opcion1 Then

Check1.Value = vbChecked

Else

Check1.Value = vbUnchecked

End If

comprobarCambiado

End Sub

En el formulario de configuración tenemos una variable (mConfig) del tipo cConfig, que será la que reciba los valores a "configurar"... (¡cuantas configuraciones!), también implementa la interfaz IConfig, además de tener una propiedad llamada Cancelado, (recuerda que las variables de una clase declaradas como públicas se convierten en propiedades), que nos servirá de "señal" para saber si el usuario ha cancelado o no la "configuración"...Fíjate que en este formulario el nombre de la variable con los datos, también se llama mConfig, pero esto es sólo "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. Además deberías saber que esa variable no se podrá "conectar" ni confundir con la del otro formulario, por la sencilla razón de que ambas están declaradas como "privadas", (Private), por tanto sólo visibles dentro de cada formulario.

En el evento Click de los botones Aceptar y Cancelar, se asigna el valor apropiado a la propiedad Cancelado, además 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 botón 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 botón Aplicar, ya que ese botón se encarga de actualizar los datos de la variable con el contenido de los controles.

En cuanto al código 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 botón Aplicar. De esta forma, si el usuario escribe o cambia cualquiera de esos valores, dicho botón estará habilitado (el contenido ha cambiado) o deshabilitado, para reflejar que 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 código del procedimiento comprobarCambiado no necesita ninguna explicación, (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 cómo funciona esta línea:If mConfig.Opcion1 <> (Check1.Value = vbChecked) ThenAquí 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 expresión 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 más claro en el código del evento Click del botón Aplicar, ya que allí se hace una comprobación más "específica" y detallada.

Page 554: curso básico de programación en visual basic

El método público 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 método se llama desde el formulario principal para asignar los valores iniciales de la configuración.

Para terminar, en el evento Load del formulario se crea una nueva instancia de la variable "privada" que contendrá los datos de configuración, además de deshabilitar el botón 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 líneas que hacen de separador 3D, (las líneas que están encima de los tres botones).

Espero que todo haya quedado claro y comprensible, no sólo por los comentarios que he hecho al código, sino por los propios comentarios que el código contiene.En caso de que no te hayas enterado... pues léete 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 impresión de que "realmente" no quieres esforzarte, así que... ¡no me preguntes!).

Recuerda que tienes que hacer los ejercicios que te he mencionado, la solución la veremos en la siguiente entrega, la cual aún no se de que tratará... ¿de clases? ¿de bases de datos? ¿se acabará ya el curso?Pues, tendrás que esperar, como mínimo al mes que viene o a finales de este, según me de... je, je.

Mientras tanto, busca algo que leer o repásate las 43 entregas anteriores... que no es plan de que se te olviden las cosas por mi tardanza en escribir nuevas entregas.

Nos vemosGuillermo

Aquí tienes el fichero zip con el código usado en esta entrega: basico44_cod.zip 5.50 KB

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 mínimo una entrega cada mes. Sí, ya se que deberían ser más 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, además de algo que no estaba explícitamente propuesto, pero que si no lo has hecho, no te funcionaría bien.

Page 555: curso básico de programación en visual basic

Este sería el código a usar en los métodos Guardar y Leer de la clase cConfig.Empezaremos por el método Guardar que es el más simple y después veremos el método 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 código está omitido para que lo hagas como ejercicio

' SOLUCIÓN:

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 código es de lo más 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 código de Leer.

Public Sub Leer(ByVal fichero As String)

' leer del fichero indicado los valores de las propiedades de la clase

'

' el código está omitido para que lo hagas como ejercicio

Page 556: curso básico de programación en visual basic

' SOLUCIÓN:

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 válido), se produzca un error.

' En ese caso se podría usar el siguiente código,

' o usar una función 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 línea anterior, es que no existe.

' Además 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

Page 557: curso básico de programación en visual basic

Line Input #nFic, s

Me.Valor3 = s

Line Input #nFic, s

' Nota:

' Esta asignación también 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 comprobación de que el fichero existe

' se hace desde una función, habría 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 más "largo" que el anterior, entre otras cosas porque se hace una comprobación de que el fichero indicado existe y otra de que el valor asignado a la propiedad Opcion1 es el correcto. En teoría no habría que hacer estas comparaciones, o al menos no habría 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 estarán guardados de forma correcta. Pero siempre es preferible prevenir.

Ahora veamos el código del formulario principal, tanto del evento producido al guardar como al leer:

Private Sub cmdGuardar_Click()

' guardar los datos en un fichero

'

' SOLUCIÓN:

' (aunque no estaba como ejercicio, si no se hace, no se guarda correctamente)

'

' Asignamos los valores a la clase y guardarlos

Page 558: curso básico de programación en visual basic

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

' SOLUCIÓN:

asignarConfig mConfig

'

'

End Sub

En el caso del evento producido al pulsar en el botón Guardar, antes de llamar al método correspondiente de la clase, hay que asignar los valores a las propiedades de dicha clase.Para el evento del botón Leer lo tenemos más fácil 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 confío en que lo hayas hecho bien... y si no es así... pues tampoco pasa nada, ya que por eso te doy la solución... je, je.

A la vuelta con la Encapsulación

Como has podido comprobar, al "encapsular" ciertas tareas a realizar por una clase, (en este caso las acciones de leer y guardar la información), el código de nuestros proyectos será más fácil de mantener, ya que si por alguna circunstancia necesitamos cambiar la forma de acceder a ese fichero, bien porque hayamos añadido alguna nueva propiedad o porque haya cambiado el tipo de datos, tan sólo tendremos que

Page 559: curso básico de programación en visual basic

cambiar el código de esos procedimientos, de forma que en el resto del código no tengamos que hacer ningún cambio.

Por esa razón, es importante y sobre todo aconsejable, que en la medida de lo posible, hagamos que sea el código incluido en la propia clase el que se encargue de manejar la información 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 cómo manipular los datos que contiene. Si, por ejemplo, tienes que crear una función o procedimiento para clasificar esos datos, siempre será más conveniente que el código que se encargue de realizar esa clasificación esté contenido en la propia clase que usar funciones o procedimientos externos. Haciéndolo de esta forma, sobre todo, ganamos más legibilidad en nuestro código.

Siguiendo con el tema de la encapsulación y la forma de manejar los datos o la información 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 algún 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 ratón 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 cómo 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 función o un procedimiento, existe una instrucción que nos permite indicarle a nuestro querido VB que queremos crear un evento, la instrucción que usaremos será: Event seguida del nombre del evento y opcionalmente los parámetros que deba tener.Por ejemplo:

Event Prueba(ByVal mensaje As String)

Nota:De forma predeterminada, los eventos son públicos, así que, es opcional indicar esa instrucción.

Esta línea definiría un evento llamado Prueba que recibe un parámetro por valor de tipo String.Si tuviésemos un objeto declarado con la clase que contiene ese evento, lo usaríamos

Page 560: curso básico de programación en visual basic

de la siguiente forma:Private elObjeto_Prueba(ByVal mensaje As String)' ... el códigoEnd Sub Es decir, de la misma forma que hasta ahora hemos estado usando los eventos.Fíjate que también se sigue la nomenclatura estándar de usar el nombre del objeto un guión 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.

Cómo lanzar un evento

Una vez que hemos declarado un evento en una de nuestras clases, nos queda por saber cómo hacer que dicho evento se lance, es decir cómo hacemos para "avisar" al código que usa nuestra clase de que dicho evento se ha producido.Esto se consigue usando la instrucción RaiseEvent seguida del nombre del evento y del parámetro (o parámetros) que tenga dicho evento.Por ejemplo, si queremos lanzar ese evento, haríamos algo como lo siguiente:RaiseEvent Prueba("El evento prueba")

Cuando escribimos la instrucción 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 sabrás, cuando no indicamos si el parámetro es por valor o por referencia, se supone que es por referencia, por tanto, en el caso del evento ElementoSeleccionado, el parámetro index será ByRef y en los otros dos, tal y como se indica en la declaración 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 público, pero el que esté definido como público no significa que se pueda lanzar desde cualquier sitio, ya que sólo se puede usar RaiseEvent nombreEvento() desde la propia clase en la que se ha definido el evento.

Cómo indicar que una clase debe interceptar eventos

Page 561: curso básico de programación en visual basic

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.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

Después 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 permitiría usar los eventos declarados en la clase, a pesar de que tengamos definidos los procedimientos Sub de los eventos, los cuales se definirían 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 razón de que si bien la clase produce eventos, Visual Basic no sabe que queremos usarlos, es decir, esos tres procedimientos que, al menos en teoría, deberían interceptar los eventos no los interceptarán.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 instrucción 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 intención es usar los eventos de esa clase.

Page 562: curso básico de programación en visual basic

Además, 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 código), tal como podemos ver en la siguiente imagen:

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 hipotética clase cConEventos produce:

Fig. 3, Los eventos de la clase seleccionada en la lista.

Los eventos se muestran de forma alfabética, pero el que toma el foco, (el que se crea nada más 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, sólo 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 código.

Page 563: curso básico de programación en visual basic

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, además de que al ejecutar la aplicación, el Visual Basic nos mostrará un error de que dicha clase no produce eventos, tal y como podemos comprobar en la siguiente figura:

Fig. 4, Error al declarar con WithEvents una variable que no produce eventos.

Resumen de cómo definir y usar eventos personalizados:

Resumamos un poco todo esto, para que quede más o menos claro cómo definir y usar eventos en las clases:

1- Para que la clase tenga eventos, debemos definirlos con la instrucción 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 instrucción WithEvents.4- Una vez definida la variable a partir de una clase que produce eventos, debemos escribir el código de los procedimientos de los eventos (siempre serán del tipo Sub) en la forma habitual:NombreVariable guión bajo NombreEvento, por ejemplo: Sub mConEventos_Prueba(parámetros)

Para ver en la práctica 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 añadiremos una clase llamada cConEventos que definirá dos eventos, los cuales se producirán al llamar a un método de esta misma clase.Uno de esos eventos se producirá mientras se añaden nuevos datos a un array y el otro al terminar de añadir dichos datos, devolviendo el número total de elementos.Veamos el código de esa clase:

Option Explicit

Private mDatos() As String

Page 564: curso básico de programación en visual basic

'

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 número " & CStr(i)

RaiseEvent NuevoDato(mDatos(i))

Next

RaiseEvent DatosCreados(11)

End Sub

Como podemos comprobar, los dos eventos que nuestra clase emitirá al "receptor" de utilice dicha clase, serán:NuevoDato el cual tiene un parámetro de tipo String que representará al nuevo dato que se está manipulando yDatosCreados cuyo parámetro de tipo Long nos indicará el número total de datos que la clase acaba de crear.Esos dos eventos se lanzan (o disparan) en el método CrearDatos, que será el único que podremos usar desde cualquier variable declarada con el tipo de la clase cConEventos.

El código usado en este último procedimiento es simple, pero te lo detallo para que no tengas problemas de comprensión... (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 parámetro indicamos el contenido del elemento que acabamos de asignar.-Continuamos repitiendo el bucle hasta que estén asignados los once elementos.-Por último, lanzamos el evento DatosCreados, en cuyo parámetro indicamos el valor once.

Para poder usar esta clase desde el formulario creado en el proyecto, al cual añadiremos un CommandButton al que llamaremos crearDatosCmd y un ListBox llamado List1, también definiremos la clase usando WithEvents, crearemos una nueva instancia en el evento Load del formulario y al pulsar en ese botón, llamaremos al método CrearDatos.Debido a que la variable estará definida con WithEvents, tendremos que escribir el código 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 añadir un

Page 565: curso básico de programación en visual basic

nuevo elemento al array, haremos que el parámetro indicado en el evento se añada 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 instrucción 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 resolución de tu monitor, es posible que veas el resto del código... así que intentaré dejar unas cuantas líneas en blanco para que no puedas ver el código y demás pistas...Pero me gustaría que lo intentaras antes de ver la solución...

Vale, la solución te la muestro en una página aparte... así no tendrás la excusa de que lo has visto sin querer...

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 lógica y los silogismos no fallan, podemos definir eventos en los formularios.

La forma de declarar nuevos eventos en un formulario sería usando Event y el nombre del evento, es decir, de la misma forma que lo haríamos en cualquier otra clase.Para poder usar este "formulario ampliado", tendríamos que declararlo también 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 diseño. 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 también 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 características "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 serán los que estén declarados de forma explícita.Sin embargo, si declaramos una variable de tipo genérico Form con la instrucción WithEvents, podremos usar los eventos de dicho formulario.

Comprobemos que todo esto es cierto.Para ello, crearemos un nuevo proyecto al que añadiremos un segundo formulario.El primer formulario (Form1) tendrá un botón (mostrarForm2Cmd) y una etiqueta (Label1).El segundo formulario (Form2) sólo tendrá un botón (lanzarEventoCmd).

Page 566: curso básico de programación en visual basic

Veamos primero el código del segundo formulario, ya que es algo más 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 parámetro de tipo String.Cuando se pulse en el botón de ese formulario, se lanzará el evento Prueba.

Ahora veamos el código del formulario principal (Form1).

Option Explicit

Private WithEvents mForm As Form

Private WithEvents mForm2 As Form2

Private Sub Form_Load()

' creamos una nueva instancia

Set mForm2 = New Form2

' asignamos a la variable mForm una referencia al objeto recién creado

Set mForm = mForm2

End Sub

Private Sub mForm_Click()

' Este evento se producirá al hacer una pulsación 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

Page 567: curso básico de programación en visual basic

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 específico Form2, sólo podremos acceder a los eventos que nosotros hayamos definido, por esa razón he declarado otra variable con WithEvents: mForm que permitirá acceder a los eventos "genéricos" del formulario, en este caso sólo interceptamos dos, pero igualmente podríamos acceder al resto.En el evento Load de este formulario asignamos a la variable mForm2 una nueva instancia del formulario Form2 y a continuación asignamos también el objeto apuntado por esa variable a la variable mForm de forma que tanto una como la otra variable estarán apuntando al formulario Form2.No te extrañe que se pueda realizar esa asignación, 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 producirán los dos eventos interceptados con la variable mForm, los cuales mostrarán 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 botón para mostrar el formulario Form2, éste se posicionará a la derecha del formulario principal y a continuación se mostrará.

Cuando ejecutes el proyecto, podrás 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 sólo se mostrará la primera vez que pulsemos en dicho botón, o cada vez que pulses en dicho botón y el segundo formulario no esté cargado en la memoria. Esto último puedes comprobarlo cerrando el segundo formulario y volviendo a pulsar en el botón.

Si añades este código al Form1, se mostrará un mensaje cuando se cierre el segundo formulario, así podrás comprobar mejor eso que te acabo de comentar.

Private Sub mForm_Unload(Cancel As Integer)

' Este mensaje se mostrará al cerrar el segundo formulario

Page 568: curso básico de programación en visual basic

Label1 = "El segundo formulario se ha descargado."

End Sub

Si además pulsas en el segundo formulario se producirá el evento Click y se mostrará el mensaje correspondiente, lo mismo ocurrirá cuando pulses en el botón, aunque en ese caso el evento que se producirá será el que hemos definido.

Espero que con todo esto que te he comentado tengas más claro cómo definir y lanzar eventos en las clases, además de saber cómo poder interceptar esos eventos desde otras partes del proyecto.

En la próxima entrega veremos cómo definir una clase que amplíe el funcionamiento de un control. De esa forma tendrás la posibilidad de ampliar el funcionamiento de los controles y adaptarlos a tus necesidades.

Nos vemosGuillermo

Aquí tienes el fichero zip con el código usado en esta entrega: basico45_cod.zip 4.97 KB

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 mínimo una entrega cada mes. Sí, ya se que deberían ser más 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, además de algo que no estaba explícitamente propuesto, pero que si no lo has hecho, no te funcionaría bien.

Este sería el código a usar en los métodos Guardar y Leer de la clase cConfig.Empezaremos por el método Guardar que es el más simple y después veremos el método 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 código está omitido para que lo hagas como ejercicio

' SOLUCIÓN:

Dim nFic As Long

'

On Error Resume Next

Page 569: curso básico de programación en visual basic

' 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 código es de lo más 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 código de Leer.

Public Sub Leer(ByVal fichero As String)

' leer del fichero indicado los valores de las propiedades de la clase

'

' el código está omitido para que lo hagas como ejercicio

' SOLUCIÓN:

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

Page 570: curso básico de programación en visual basic

' (no es un nombre válido), se produzca un error.

' En ese caso se podría usar el siguiente código,

' o usar una función 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 línea anterior, es que no existe.

' Además 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 asignación también 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

Page 571: curso básico de programación en visual basic

' indicarlo nuevamente, pero si la comprobación de que el fichero existe

' se hace desde una función, habría 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 más "largo" que el anterior, entre otras cosas porque se hace una comprobación de que el fichero indicado existe y otra de que el valor asignado a la propiedad Opcion1 es el correcto. En teoría no habría que hacer estas comparaciones, o al menos no habría 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 estarán guardados de forma correcta. Pero siempre es preferible prevenir.

Ahora veamos el código del formulario principal, tanto del evento producido al guardar como al leer:

Private Sub cmdGuardar_Click()

' guardar los datos en un fichero

'

' SOLUCIÓN:

' (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

Page 572: curso básico de programación en visual basic

Private Sub cmdLeer_Click()

' leer los datos del fichero indicado

mConfig.Leer sFic

'

'

'$POR HACER: actualizar los controles con los valores de mConfig

' SOLUCIÓN:

asignarConfig mConfig

'

'

End Sub

En el caso del evento producido al pulsar en el botón Guardar, antes de llamar al método correspondiente de la clase, hay que asignar los valores a las propiedades de dicha clase.Para el evento del botón Leer lo tenemos más fácil 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 confío en que lo hayas hecho bien... y si no es así... pues tampoco pasa nada, ya que por eso te doy la solución... je, je.

A la vuelta con la Encapsulación

Como has podido comprobar, al "encapsular" ciertas tareas a realizar por una clase, (en este caso las acciones de leer y guardar la información), el código de nuestros proyectos será más fácil de mantener, ya que si por alguna circunstancia necesitamos cambiar la forma de acceder a ese fichero, bien porque hayamos añadido alguna nueva propiedad o porque haya cambiado el tipo de datos, tan sólo tendremos que cambiar el código de esos procedimientos, de forma que en el resto del código no tengamos que hacer ningún cambio.

Por esa razón, es importante y sobre todo aconsejable, que en la medida de lo posible, hagamos que sea el código incluido en la propia clase el que se encargue de manejar la información 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 cómo manipular los datos que contiene. Si, por ejemplo, tienes que crear una función o procedimiento para clasificar esos datos, siempre será más conveniente que el código que se encargue de realizar esa clasificación esté contenido en la propia clase que usar funciones o procedimientos externos. Haciéndolo de esta forma, sobre todo, ganamos más legibilidad en nuestro código.

Siguiendo con el tema de la encapsulación y la forma de manejar los datos o la información 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

Page 573: curso básico de programación en visual basic

detecta algún 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 ratón 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 cómo 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 función o un procedimiento, existe una instrucción que nos permite indicarle a nuestro querido VB que queremos crear un evento, la instrucción que usaremos será: Event seguida del nombre del evento y opcionalmente los parámetros que deba tener.Por ejemplo:

Event Prueba(ByVal mensaje As String)

Nota:De forma predeterminada, los eventos son públicos, así que, es opcional indicar esa instrucción.

Esta línea definiría un evento llamado Prueba que recibe un parámetro por valor de tipo String.Si tuviésemos un objeto declarado con la clase que contiene ese evento, lo usaríamos de la siguiente forma:Private elObjeto_Prueba(ByVal mensaje As String)' ... el códigoEnd Sub Es decir, de la misma forma que hasta ahora hemos estado usando los eventos.Fíjate que también se sigue la nomenclatura estándar de usar el nombre del objeto un guión 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.

Cómo lanzar un evento

Una vez que hemos declarado un evento en una de nuestras clases, nos queda por saber cómo hacer que dicho evento se lance, es decir cómo hacemos para "avisar" al código que usa nuestra clase de que dicho evento se ha producido.Esto se consigue usando la instrucción RaiseEvent seguida del nombre del evento y del parámetro (o parámetros) que tenga dicho evento.

Page 574: curso básico de programación en visual basic

Por ejemplo, si queremos lanzar ese evento, haríamos algo como lo siguiente:RaiseEvent Prueba("El evento prueba")

Cuando escribimos la instrucción 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 sabrás, cuando no indicamos si el parámetro es por valor o por referencia, se supone que es por referencia, por tanto, en el caso del evento ElementoSeleccionado, el parámetro index será ByRef y en los otros dos, tal y como se indica en la declaración 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 público, pero el que esté definido como público no significa que se pueda lanzar desde cualquier sitio, ya que sólo se puede usar RaiseEvent nombreEvento() desde la propia clase en la que se ha definido el evento.

Cómo 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.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

Después 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.

Page 575: curso básico de programación en visual basic

Pero esto no nos permitiría usar los eventos declarados en la clase, a pesar de que tengamos definidos los procedimientos Sub de los eventos, los cuales se definirían 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 razón de que si bien la clase produce eventos, Visual Basic no sabe que queremos usarlos, es decir, esos tres procedimientos que, al menos en teoría, deberían interceptar los eventos no los interceptarán.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 instrucción 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 intención es usar los eventos de esa clase.Además, 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 código), tal como podemos ver en la siguiente imagen:

Page 576: curso básico de programación en visual basic

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 hipotética clase cConEventos produce:

Fig. 3, Los eventos de la clase seleccionada en la lista.

Los eventos se muestran de forma alfabética, pero el que toma el foco, (el que se crea nada más 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, sólo 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 código.

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, además de que al ejecutar la aplicación, el Visual Basic nos mostrará un error de que dicha clase no produce eventos, tal y como podemos comprobar en la siguiente figura:

Page 577: curso básico de programación en visual basic

Fig. 4, Error al declarar con WithEvents una variable que no produce eventos.

Resumen de cómo definir y usar eventos personalizados:

Resumamos un poco todo esto, para que quede más o menos claro cómo definir y usar eventos en las clases:

1- Para que la clase tenga eventos, debemos definirlos con la instrucción 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 instrucción WithEvents.4- Una vez definida la variable a partir de una clase que produce eventos, debemos escribir el código de los procedimientos de los eventos (siempre serán del tipo Sub) en la forma habitual:NombreVariable guión bajo NombreEvento, por ejemplo: Sub mConEventos_Prueba(parámetros)

Para ver en la práctica 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 añadiremos una clase llamada cConEventos que definirá dos eventos, los cuales se producirán al llamar a un método de esta misma clase.Uno de esos eventos se producirá mientras se añaden nuevos datos a un array y el otro al terminar de añadir dichos datos, devolviendo el número total de elementos.Veamos el código de esa clase:

Option Explicit

Private mDatos() As String

'

Event NuevoDato(ByVal elDato As String)

Event DatosCreados(ByVal total As Long)

Page 578: curso básico de programación en visual basic

Public Sub CrearDatos()

Dim i As Long

'

ReDim mDatos(10)

For i = 0 To 10

mDatos(i) = "El dato número " & CStr(i)

RaiseEvent NuevoDato(mDatos(i))

Next

RaiseEvent DatosCreados(11)

End Sub

Como podemos comprobar, los dos eventos que nuestra clase emitirá al "receptor" de utilice dicha clase, serán:NuevoDato el cual tiene un parámetro de tipo String que representará al nuevo dato que se está manipulando yDatosCreados cuyo parámetro de tipo Long nos indicará el número total de datos que la clase acaba de crear.Esos dos eventos se lanzan (o disparan) en el método CrearDatos, que será el único que podremos usar desde cualquier variable declarada con el tipo de la clase cConEventos.

El código usado en este último procedimiento es simple, pero te lo detallo para que no tengas problemas de comprensión... (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 parámetro indicamos el contenido del elemento que acabamos de asignar.-Continuamos repitiendo el bucle hasta que estén asignados los once elementos.-Por último, lanzamos el evento DatosCreados, en cuyo parámetro indicamos el valor once.

Para poder usar esta clase desde el formulario creado en el proyecto, al cual añadiremos un CommandButton al que llamaremos crearDatosCmd y un ListBox llamado List1, también definiremos la clase usando WithEvents, crearemos una nueva instancia en el evento Load del formulario y al pulsar en ese botón, llamaremos al método CrearDatos.Debido a que la variable estará definida con WithEvents, tendremos que escribir el código 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 añadir un nuevo elemento al array, haremos que el parámetro indicado en el evento se añada 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 instrucción MsgBox.

Page 579: curso básico de programación en visual basic

¿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 resolución de tu monitor, es posible que veas el resto del código... así que intentaré dejar unas cuantas líneas en blanco para que no puedas ver el código y demás pistas...Pero me gustaría que lo intentaras antes de ver la solución...

Vale, la solución te la muestro en una página aparte... así no tendrás la excusa de que lo has visto sin querer...

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 lógica y los silogismos no fallan, podemos definir eventos en los formularios.

La forma de declarar nuevos eventos en un formulario sería usando Event y el nombre del evento, es decir, de la misma forma que lo haríamos en cualquier otra clase.Para poder usar este "formulario ampliado", tendríamos que declararlo también 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 diseño. 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 también 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 características "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 serán los que estén declarados de forma explícita.Sin embargo, si declaramos una variable de tipo genérico Form con la instrucción WithEvents, podremos usar los eventos de dicho formulario.

Comprobemos que todo esto es cierto.Para ello, crearemos un nuevo proyecto al que añadiremos un segundo formulario.El primer formulario (Form1) tendrá un botón (mostrarForm2Cmd) y una etiqueta (Label1).El segundo formulario (Form2) sólo tendrá un botón (lanzarEventoCmd).

Veamos primero el código del segundo formulario, ya que es algo más simple:

Option Explicit

Page 580: curso básico de programación en visual basic

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 parámetro de tipo String.Cuando se pulse en el botón de ese formulario, se lanzará el evento Prueba.

Ahora veamos el código del formulario principal (Form1).

Option Explicit

Private WithEvents mForm As Form

Private WithEvents mForm2 As Form2

Private Sub Form_Load()

' creamos una nueva instancia

Set mForm2 = New Form2

' asignamos a la variable mForm una referencia al objeto recién creado

Set mForm = mForm2

End Sub

Private Sub mForm_Click()

' Este evento se producirá al hacer una pulsación 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

Page 581: curso básico de programación en visual basic

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 específico Form2, sólo podremos acceder a los eventos que nosotros hayamos definido, por esa razón he declarado otra variable con WithEvents: mForm que permitirá acceder a los eventos "genéricos" del formulario, en este caso sólo interceptamos dos, pero igualmente podríamos acceder al resto.En el evento Load de este formulario asignamos a la variable mForm2 una nueva instancia del formulario Form2 y a continuación asignamos también el objeto apuntado por esa variable a la variable mForm de forma que tanto una como la otra variable estarán apuntando al formulario Form2.No te extrañe que se pueda realizar esa asignación, 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 producirán los dos eventos interceptados con la variable mForm, los cuales mostrarán 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 botón para mostrar el formulario Form2, éste se posicionará a la derecha del formulario principal y a continuación se mostrará.

Cuando ejecutes el proyecto, podrás 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 sólo se mostrará la primera vez que pulsemos en dicho botón, o cada vez que pulses en dicho botón y el segundo formulario no esté cargado en la memoria. Esto último puedes comprobarlo cerrando el segundo formulario y volviendo a pulsar en el botón.

Si añades este código al Form1, se mostrará un mensaje cuando se cierre el segundo formulario, así podrás comprobar mejor eso que te acabo de comentar.

Private Sub mForm_Unload(Cancel As Integer)

' Este mensaje se mostrará al cerrar el segundo formulario

Label1 = "El segundo formulario se ha descargado."

End Sub

Page 582: curso básico de programación en visual basic

Si además pulsas en el segundo formulario se producirá el evento Click y se mostrará el mensaje correspondiente, lo mismo ocurrirá cuando pulses en el botón, aunque en ese caso el evento que se producirá será el que hemos definido.

Espero que con todo esto que te he comentado tengas más claro cómo definir y lanzar eventos en las clases, además de saber cómo poder interceptar esos eventos desde otras partes del proyecto.

En la próxima entrega veremos cómo definir una clase que amplíe el funcionamiento de un control. De esa forma tendrás la posibilidad de ampliar el funcionamiento de los controles y adaptarlos a tus necesidades.

Nos vemosGuillermo

Aquí tienes el fichero zip con el código usado en esta entrega: basico45_cod.zip 4.97 KB

Muy buenas, aquí estamos con una nueva entrega del Curso Básico de Programación con Visual Basic (el clásico) y dentro del plazo ese que me puse para que no pasara mucho más 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 veríamos cómo ampliar el funcionamiento de un control ya existente. Puede que pensaras que ¡por fin! te iba a explicar cómo crear tus propios controles, pero... no voy a tratar de ese tema, ya que voy a seguir con la creación/definición 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) debería entrar en un "curso básico", aunque la verdad es que el concepto de curso básico 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 ocasión lo que hicimos fue añadir un formulario al proyecto y definir un evento, el cual podríamos interceptar desde otro sitio. Pero en el caso de los controles, no podemos hacerlo de esa forma, por la sencilla razón de que no existe una clase específica de un control en la que podamos añadir 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 también nuevas propiedades y métodos. Esto mismo lo podemos hacer con los formularios además de con prácticamente 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 podría ser que no te gusta el funcionamiento estándar de un control y quieres que haga más cosas o que las que hace, las haga a tu gusto o a la forma que a ti te gustaría.Por ejemplo, imagínate que quieres que una caja de textos te muestre los números negativos en color rojo o que te permita indicarle que sólo quieres que acepte números o letras o... pueden ser tantas las cosas que se te podrían ocurrir, que necesitaríamos como mínimo una entrega para nombrarlas.

Page 583: curso básico de programación en visual basic

En esta entrega, (no se si me también en la siguiente), vamos a ampliar el funcionamiento de una caja de textos no multilínea, a la que añadiremos algún nuevo evento y propiedades, así como también algún que otro método... ya veremos qué le añadimos, pero sea lo que sea, te servirá para saber cómo hacerlo y después puedes ampliarlo o modificarlo a tu gusto.

Creando una clase para ampliar un TextBoxVamos a empezar haciendo poca cosa con el TextBox ampliado por lo que seguramente te preguntarás ¿para qué hacer esto?, la respuesta es bien simple, para que no te compliques con cosas nuevas sin llegar a comprender lo básico, que al fin y al cabo es de lo que se trata: aprender lo básico para que estés preparado para hacer lo que quieras... o casi...

Para probar lo que te voy a contar a continuación, necesitaremos crear un nuevo proyecto. Al form que se crea junto con el proyecto, vamos a añadirle 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 cómo usar ese control mediante la clase.Para ello, añade un módulo de clase y dale el nombre cTextBoxEx.Lo primero que tendremos que hacer es crear una variable a nivel de módulo que nos permita interceptar los eventos de un control de tipo TextBox, para ello añadiremos esta declaración:

Option Explicit

' creamos una variable para manejar el textbox

' 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 públicas, pero nunca se pueden crear arrays (o matrices) ni usarlas para instanciar directamente la clase con New.

A continuación vamos a declarar varios eventos públicos, estos eventos estarán disponible en cualquier sitio en el que se utilice esta clase declarada con WithEvents.Estos eventos, simplemente serán unos cuantos de los que ya disponen todos los TextBox, pero vamos a usarlos para que sepas cómo manipularlos y, si así lo deseas, adaptarlos a tu gusto o necesidad.

' Los eventos públicos que producirá la clase

' Aquí se ha usado la misma definición que los eventos originales

Page 584: curso básico de programación en visual basic

' pero podríamos 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 ocasión 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 lanzarán los que la clase implementa

' por tanto, podemos cambiar ese comportamiento a nuestro gusto

Private Sub mText_Change()

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

Page 585: curso básico de programación en visual basic

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 habría que hacer o si lo prefieres, es la única forma de hacerlo, al menos en este caso, ya que los eventos del objeto mText sólo se producen en la clase y no fuera de ella, ya que fuera, se producirán los que la propia clase haya implementado, esto lo comprobaremos en un momento.

Ahora vamos a ver cómo indicarle a la clase el objeto del tipo TextBox que debe manipular.Para ello, crearemos un procedimiento (método), el cual recibirá un parámetro, que será el TextBox que la clase debe manipular. A ese método lo llamaremos de la misma forma que la clase... Sí, así es como se hace en C++/C# ¿y que pasa? ¿que podría haberlo llamado New como lo hace el VB.NET? ¡pues no! además de porque no me gusta, por la sencilla razón de que no podemos tener un método que se llame igual que una palabra clave del Visual Basic.

Veamos el "constructor" extra de la clase, y digo "extra", por la sencilla razón 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 ningún parámetro, por tanto tendremos que crear un procedimiento al que "forzosamente" haya que llamar para que la clase sepa que TextBox es el que manipulará.

Page 586: curso básico de programación en visual basic

También veremos el código del destructor de la clase, (Class_Terminate), es decir, el código que se ejecutará cuando la clase ya no esté referenciada por ningún objeto.

' IMPORTANTE:

' Hay que usar este método 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 más.

' aquí liberaremos los recursos que estemos empleando.

Set mText = Nothing

End Sub

Como puedes ver en el comentario del procedimiento cTextBoxEx, es muy IMPORTANTE que se llame a ese método antes de hacer nada con el objeto creado a partir de la clase; esto es así, por la sencilla razón de que si no lo hacemos, la variable mText no estará apuntando a ningún objeto.También puedes comprobar que hago una comprobación de que el tipo del parámetro sea un TextBox, para ello he usado la siguiente línea:If TypeOf value Is TextBox ThenRealmente no sería necesario, pero... así, si decides cambiar el parámetro a un tipo más genérico, como por ejemplo Object, te asegurarás 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 produciría un error indicando ese hecho, para ello usamos el método 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 explícita... cosas mías.

Page 587: curso básico de programación en visual basic

Ahora veamos el código 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 usarán para mostrar información de que se han producido los eventos. También tendremos un botón para cerrar el formulario (cmdCerrar).Veamos el código 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

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

Page 588: curso básico de programación en visual basic

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 código no tiene ningún misterio, ya que es así como habría 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 razón 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 cTextBoxExy se asigna el textbox que debe manipular:txtBoxEx.cTextBoxEx Text2.

Añadir nuevos miembros a la claseAhora que tenemos "la base" de cómo habría que manipular un TextBox desde una clase, vamos a añadir nueva funcionalidad a esa clase, más que nada para que tenga alguna razón 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 añadiendo un método 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 más precisos).Lo que este método 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 parámetro, el cual indicará el formato que queramos que devuelva.Por ejemplo, si el contenido de la clase es una fecha, podría interesarnos que devuelva esa fecha en el formato dd/mm/yyyy o si es un número que podamos darle "formato", para ello usaremos la función Format$ del VB para que aplique el formato que le indiquemos.Veamos el código del método ToString de la clase cTextBoxEx:

Public Function ToString(Optional ByVal formato As String = "") As String

' Si se produce algún error, ignorarlo

On Error Resume Next

Page 589: curso básico de programación en visual basic

'

' devolvemos el contenido del text con el formato indicado,

' si es que se ha indicado algún formato

If formato = "" Then

ToString = mText.Text

Else

ToString = Format$(mText.Text, formato)

End If

End Function

En la declaración indicamos que el parámetro es opcional, por tanto, esta función se puede usar de dos formas:1- sin indicar el parámetro2- indicando el formato a aplicar al contenido del textboxEn caso de que no se indique el parámetro, se asignará a dicha variable el valor por defecto que se indica en la declaración, es decir: una cadena vacía. Este hecho se tiene en cuenta en la comparación que hay dentro de la función y se hará una cosa u otra, según se haya o no indicado el parámetro.En el caso de que no se haya indicado, o si se haya indicado, pero sea una cadena vacía, 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 erróneo, utilizamos una captura de errores, de forma que si se produce un error, simplemente se continúe con la siguiente línea.

Como sabemos, si declaramos una función o un procedimiento Sub de forma Public, ese procedimiento se convierte en un método de la clase y por tanto lo podemos usar desde cualquier sitio. (Sí, ya se que esto lo sabes, pero es simplemente para recordártelo y así poder tener tiempo para poner el ejemplo.)Veamos cómo usarlo desde el código del formulario, para ello, añade un botón (CommandButton) al que llamaremos cmdFormato y añade este código al evento Click:

Private Sub cmdFormato_Click()

lblTxt2 = txtBoxEx.ToString("dd/mm/yyyy")

End Sub

Bien, ya tenemos la base y el conocimiento de cómo podemos ampliar un TextBox, ahora sólo falta echarle un poco de imaginación y adaptarlo a nuestras necesidades.

Para ampliar más la clase, vamos a añadirle una propiedad que indicará el tipo de datos que manipulará la clase, esos datos podrán ser de tres tipos diferentes:-Normal, aceptará cualquier cosa, tal como lo hace el textbox de forma predeterminada

Page 590: curso básico de programación en visual basic

-Numérico, sólo aceptará números, estos a su vez podrán ser con y sin decimales-Fecha, aceptará sólo fechas o al menos lo intentará...

Para indicar estos valores, vamos a declarar una enumeración pública en la clase, de forma que la propiedad usada para el tipo de datos, utilice los valores de esa enumeración.También declararemos una variable privada para mantener el valor de esa propiedad, aunque podríamos haber declarado la propiedad como una variable pública, pero esto no nos permitiría hacer ciertas comprobaciones, como por ejemplo, comprobar que el valor asignado a esa propiedad es uno de los valores permitidos.Veamos cómo hacer todo esto y después utilizaremos esa propiedad para que realmente sólo acepte ese tipo de datos:

' enumeración 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 código lo tendrás que añadir en la parte "General" de la clase y lo que sigue, lo puedes añadir al final del código 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í podríamos comprobar que el tipo asignado sea el correcto

Select Case value

Case eTipo.Decimales, eTipo.Enteros, eTipo.Fecha, eTipo.Normal

' no hacemos nada

Case Else

value = eTipo.Normal

End Select

m_Tipo = value

Page 591: curso básico de programación en visual basic

End Property

El Property Let es el código que se utiliza cuando asignamos un valor a la propiedad, por tanto será aquí donde hagamos la comprobación de que el valor asignado sea del tipo correcto, es decir, uno de los valores de la enumeración. 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 razón de que al ser el primer valor de la enumeración, valdrá cero.Si quisiéramos que dicho valor predeterminado fuese otro, lo podríamos asignar en el constructor de la clase, tal como podemos comprobar a continuación:

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 razón 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 sólo permita que se asignen números... pero ¡cuidado! los números también permiten que se indique si es negativo y en caso de que sean números decimales, podría ser necesario que admita valores de tipo "científico", es decir que el número puede contener la letra E o D, según sea del tipo Single o Double.

La solución en la próxima entrega.

Nos vemosGuillermo

Aquí tienes el fichero zip con el código usado en esta entrega: basico46_cod.zip 4.97 KB

Aquí tienes la solución al ejercicio propuesto en la entrega 46.

Realmente lo único que había que añadir era el código necesario a la pulsación de teclas en la clase, para ello tenías 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 habría 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 atrás), los signos más (+), menos (-) y cualquiera de los dígitos de cero a nueve.

Page 592: curso básico de programación en visual basic

En el caso de que el valor de la propiedad Tipo sea Decimales se comprueba además si se ha pulsado la coma, el punto y cualquiera de las letras E o D (tanto en mayúsculas como en minúsculas), 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 parámetro 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 pulsación de esa tecla.

Y siempre se obliga a que se produzca el evento KeyPress de esta clase, para ello utilizamos la última línea: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 añadir esta producción del evento al final del procedimiento.En ese evento ya estará filtrada la tecla pulsada, de forma que si la tecla no es válida, 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 envíe al evento del objeto creado en el formulario desde el que se utilice nuestra clase.

El código 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

' Sólo admitir teclas consideradas numéricas

' El 8 es la tecla Backspace (borrar hacia atrás)

' Los códigos 43 y 45 son los signos + y - respectivamente

Case Else

' No es una tecla numérica, 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

' Sólo admitir teclas consideradas numéricas

' El código 44 es la coma y el 46 es el punto

Page 593: curso básico de programación en visual basic

' La E y D son para números con notación científica

' (68 y 100 es la E y e, 69 y 101 es D y d)

' El 8 es la tecla Backspace (borrar hacia atrás)

' Los códigos 43 y 45 son los signos + y - respectivamente

Case Else

' No es una tecla numérica, no admitirla

KeyAscii = 0

Beep

End Select

End Select

RaiseEvent KeyPress(KeyAscii)

End Sub

Espero que esto sea lo que más 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: números, tanto decimales como enteros.

Otra cosa es aceptar valores de fecha, en ese caso habría que aceptar, además de las cifras, los separadores de las fechas y también podríamos 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, verás que teníamos código para comprobar fechas... si no recuerdo mal.De todas formas, es posible que en otra ocasión te de mi solución para que tengas un punto de referencia de cómo hacerlo, pero eso no será ahora... ¡ya veremos cuando!

Nos vemosGuillermo

Seguramente pensarás que hay muchas cosas que explicar de Visual Basic, y tienes razón, pero debido a que el título de este "tutorial" es Curso Básico de Programación en Visual Basic, no pretenderás que te lo explique todo... sí, ya sé que eso es lo que "el Guille" debería 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 básico, simplemente te estoy "medio" advirtiendo que no abarcaré todos los temas, así que... intenta buscar más documentación u otros cursos por Internet y no lo dejes todo de manos del Guille, que no lo vas a tener todo... aunque también te recomiendo que eches un vistazo a las muchas cosas que hay publicadas en mi sitio sobre VB que alguna te servirá... ¡espero!

Page 594: curso básico de programación en visual basic

Bueno, después de este párrafo "liante" vamos a ver que es lo que podemos hacer... o mejor dicho, que es lo que me gustaría explicarte en las entregas que seguirán en un futuro próximo en esto que empieza ahora y que podía ser la segunda parte del Curso Básico de Programación con Visual Basic.

Introducción

Lo que me gustaría explicarte son cosas como la creación de controles ActiveX (OCX) y librerías (DLL) ActiveX, que al fin y al cabo son conceptos bastante similares, cuando las explique verás que es así.También tengo pensado, siguiendo en esta mima línea de "automatización" de Visual Basic, a explicarte cómo modularizar o "trocear" tus aplicaciones en componentes ActiveX, de forma que puedas crear componentes (controles o librerías 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 próximas entregas, para que cuando te encuentres con esos "términos" no te hagas un lío... (traduzco: no te confundas).

Componentes.

Empecemos por explicar qué significa eso de componentes.Según la documentación de la MSDN Library que se incluye con Visual Studio 6.0 (VB 6.0), un componente es:

Cualquier software compatible con Automatización, por lo que puede usarse mediante programación en una aplicación personalizada. Incluye controles ActiveX, servidores de Automatización basados en Visual Basic y servidores de Automatización basados en Visual C.

Antes de "explicarte" o aclararte que significa esto, veamos lo que dice la documentación sobre Automatización:

Una tecnología que permite que las aplicaciones proporcionen objetos de una forma coherente a otras aplicaciones, herramientas de programación y lenguajes de macros.

Ahora que tenemos definidos dos de los conceptos que aparecen en la documentación de Visual Basic, te explicaré con lenguaje lo más llano posible qué significa esto de los componentes.

Desde la versión 4.0 de Visual Basic, este lenguaje permite crear aplicaciones compatibles con lo que antes se llamaba automatización OLE, (OLE: Object Linking and Embedding, vinculación e incrustación de objetos), ese nombre ahora es más conocido como automatización COM (o simplemente como COM). Las siglas COM significan Component Object Model que traducido sería algo así como modelo de objetos componentes.

Siguiendo mi búsqueda de definiciones en las diferentes "documentaciones" de Microsoft, me he encontrado también con esta otra sobre COM:

Page 595: curso básico de programación en visual basic

COM es el "modelo de objetos" fundamental sobre el que se generan OLE (Object Linking and Embedding, Vinculación e incrustación de objetos) y los controles ActiveX. COM permite que un objeto exponga su funcionalidad a otros componentes y aloje aplicaciones. Define cómo el objeto se expone a sí mismo y cómo funciona dicha exposición en procesos y en redes. Además, 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 documentación "algunas" veces te dice y después te lo aclaro, para que lo digieras mejor... si es posible.

Lo que debe quedarte claro, es que un componente es un "trozo" de código que podemos usar en cualquier aplicación.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 código, una rutina (función o procedimiento) podría ser un componente, ya que eso no es totalmente cierto.

Los componentes COM son programas que permiten ser utilizados desde otro programas mediante automatización.Por regla general, los componentes suelen ser librerías (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 automatización (o componentes COM), ya que en el mundo de .NET también se usa el término componente y su uso no se refiere precisamente a un "objeto" de automatización.

No hace falta que te diga que no es lo mismo un EXE ActiveX que un EXE normal, por la sencilla razón de que si un EXE normal fuese lo mismo... desde hace tiempo que estaríamos 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 intervención 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 librerías que se pueden usar sólo con lenguajes que puedan trabajar con Automatización OLE (o COM).

Visual Basic está totalmente "adherido" a la automatización, es decir, entiende cómo crear objetos contenidos en componentes COM (de automatización) y, lo más importante, también puede crear componentes COM para que puedan ser usados desde otros lenguajes "adheridos" a la automatización.

Por tanto, Visual Basic podrá crear librerías (DLL) de automatización (componentes COM), pero no podrá crear librerías "normales".

Aclaro este punto, porque a pesar de que la extensión sea la misma para una librería normal y una de automatización, nuestro querido VB no podrá crear ficheros con la extensión DLL que se puedan usar de la misma forma que las librerías del API de Windows o las creadas por compiladores como C/C++ e incluso Delphi.

Page 596: curso básico de programación en visual basic

Las librerías creadas por Visual Basic siempre son librerías de automatización.

Después 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 "código" que podemos usar desde cualquier aplicación de automatización, y ese "código" normalmente estará compilado en la forma de una librería (DLL).Realmente no es "código", al menos como se entiende por código cuando se dice "código fuente", sino a código compilado o código binario, es decir, un código fuente que se ha compilado.

¿Qué ventajas tiene la creación y utilización de componentes?

Si has llegado a esta entrega número 47, (sin saltarte ninguna), ya habrás estado usando algunos componentes de automatización, por ejemplo ADO (ActiveX Data Objects) es un componente de automatización, el cual nos permite crear objetos para poder acceder a las bases de datos. También habrás usado "componentes" en la forma de librerías del API de Windows. Y por supuesto que habrás 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 aplicación, lo usamos y no tenemos que programar ni una línea para tener la funcionalidad que ese componente nos ofrece.

Por otro lado, la creación de un componente nos permite escribir un código que "posiblemente" nos servirá para usarlo en más de una aplicación, además 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 que "forzosamente" tengamos que usarlo en más de una aplicación, ya que es muy posible que simplemente lo usemos una vez y nada más, pero eso no es un impedimento para que lo "encapsulemos" en un componente (librería DLL o control OCX).

¿Cual sería la ventaja de crear un componente aunque sólo nos sirva para una aplicación?La ventaja es que si en un futuro queremos hacer cambios en el código de ese componente, sólo tendremos que distribuir esa librería y no el resto de la aplicación, incluso (aunque ese tema no lo trataremos en este curso) puede ser que ese componente se ejecute desde un servidor, con lo cual simplemente actualizándolo en el servidor, el resto de las aplicaciones "cliente" que lo utilicen estarán actualizados "a la última".

La ventaja también es evidente, ya que si no tuviésemos el código "troceado", tendríamos que compilar toda la aplicación, de esta forma sólo tendríamos que compilar el componente y distribuirlo... cuando digo distribuirlo, en la mayoría de los casos, simplemente será "copiar y pegar" esa librería en el directorio que estaba y nada más.

Sí, ya se lo que estarás pensando, y si aún no lo estás pensando, este pensamiento te "llegará" cuando sepas algo de la creación de componentes ActiveX, y como no es plan de esperar el tiempo necesario para que sepas algo de componentes, te explico cual sería ese pensamiento al que me refiero:Pregunta/pensamiento: Si tengo que compilar el componente y distribuirlo,

Page 597: curso básico de programación en visual basic

¿qué problema hay con compilar toda la aplicación 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 habría diferencia), puedes pensar que ¿para qué complicarme la vida haciendo componentes, cuando es mejor escribir todo el código junto, que con total seguridad me dará menos quebraderos de cabeza? Y si tienes este último pensamiento "pasarás" de crear y usar componentes, y si es eso lo que va a ocurrir... ¿que puñetas, (últimamente el Guille se está volviendo algo más recatado y ahora en lugar de decir coño, dice puñetas, 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, empápate de qué va todo esto de los componentes y "aprende" a usarlos aunque pienses que no te será realmente útil. A la larga lo agradecerás, 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 código, si no está indentado, parece que ni lo entiendes... (¡Este Guille no tiene remedio! tenía 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, serán cosas relacionadas con la programación 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 creación de componentes ActiveX nos permitirá entrar un "poco" en esa dinámica, por aquello de que la creación de componentes nos permitirá "granular" nuestra aplicación en trozos (que al fin y al cabo serán clases que se convertirán en objetos). Ya se que esto no será Programación Orientada a Objetos, (así en mayúsculas o en negrita), pero al menos te dará una idea de cómo podría ser... y sobre todo, como es ahora mismo si te atreves a entrar en el mundo de .NET, que al fin y al cabo debería ser el lenguaje que deberías usar, así que... no se que haces perdiendo el tiempo en este curso si ya tenías 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 estás 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, (según quien opine), en las 46 entregas anteriores te he explicado un montón de cosas, las cuales, si las has "aderezado" con alguna otra lectura, seguro que te permitirá empezar a adentrarte en el mundillo de la programación 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 práctico.

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 más o menos profundidad, espero que con todo lo dicho sobre ese tema, no tengas dudas sobre qué son las clases y cómo

Page 598: curso básico de programación en visual basic

se usan en los proyectos de Visual Basic, ya que todo esto de los componentes ActiveX está muy relacionado con las clases y la creación de objetos (instanciación) 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 automatización es fundamental el conocimiento de cómo definir una clase, además de cómo crear nuevas instancias (objetos en memoria) de una clase.

De todas formas, vamos a repasar rápidamente los conceptos más importantes.

Repaso rápido sobre las clases.

Para escribir el código de una clase, hay que usar un módulo especial, el

cual le indicará al Visual Basic que el código insertado en él es una clase.

Este tipo de módulos tendrán la extensión .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 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 código similar a este:

Set miClase = New cClase

Si bien podemos declarar la clase y crear el objeto en una sola

instrucción:

Set miClase As New cClase

Esto no es recomendable y NO DEBERÍAS HACERLO NUNCA, ya que el

rendimiento de la aplicación se vería afectado, además de que podría

Page 599: curso básico de programación en visual basic

provocar problemas "colaterales" que en ocasiones sería difícil de

descubrir.

¿Por qué se vería afectado el rendimiento? Porque cuando

declaramos e instanciamos el objeto en la misma línea, el Visual Basic

añadirá código extra cada vez que se use esa variable para comprobar si

el objeto ya existe, y en caso de que no exista lo creará

"automáticamente".

¿Qué problemas añadidos podríamos tener? Debido a esta auto-

creación de los objetos declarados con As New, puede ser que por

"accidente" creemos un objeto sin que ese fuese nuestro propósito. 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 indicaría que ya no existe), el Visual Basic

lo volverá a crear (instanciar), con lo cual nunca sabríamos 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 acostumbraríamos a

"mal usarlos", ya que, al saber que se auto-crean, podríamos escribir

código que no sería "lógico" (hablando programáticamente).

Cualquier "variable" declarada como pública dentro de una clase, se

convierte automáticamente 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

intención es asignar un objeto y no la propiedad que la clase tiene por

defecto.

El concepto de cómo crear una propiedad (o método) 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:

Page 600: curso básico de programación en visual basic

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, habría 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 después de Set también apunte al mismo

objeto que existe en la memoria, por tanto sólo existirá un objeto en

memoria, pero existirán varias variables que hagan referencia a ese

objeto.

Esto es importante, ya que cualquier cambio que hagamos en el

objeto referenciado mediante cualquiera de las variables que apuntan a

dicho objeto, se reflejará en todas las variables que apunten a ese

objeto, ya que sólo habrá un objeto creado en la memoria. En la entrega

39 y también en la 42 se explicó esto al hablar del método 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 código 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 sería 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

Page 601: curso básico de programación en visual basic

"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 públicas o mejor aún,

(recomendado), declarándolas como procedimientos Property.

Métodos, 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.

Los elementos de una clase pueden estar declarados como Private,

Friend o Public.

Private, estos elementos sólo serán visibles dentro de la propia clase.

Friend, estos elementos serán visibles dentro de la clase y por

instancias creadas desde cualquier parte de la mima aplicación

(realmente desde el mismo proyecto en el que se encuentre nuestra

clase).

Public, será visible en cualquier parte.

Para terminar, recordarte que los formularios también son clases,

especiales, pero clases al fin y al cabo, y podemos usarlas de la misma

forma como usaríamos cualquiera de las que nosotros codifiquemos.

Creo que con este repaso, más o menos, recordarás las cosas más 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 más a fondo... ya veremos.

Para terminar esta entrega, vamos a crear un componente (una librería ActiveX) y veremos cómo podemos "probarla" y usarla desde otros proyectos.

Crear un grupo de proyectos.

Page 602: curso básico de programación en visual basic

El entorno de Visual Basic (a partir de la versión 5), nos permite tener múltiples proyectos abiertos.Esto nos permite poder crear, por ejemplo, un componente, además de poder crear un ejecutable "normal" que nos permita usar ese componente.

Para no complicarte la vida con este primer intento en la creación de un componente, vamos a crear una librería Activex. Esta librería tendrá una clase (como todas las librerías ActiveX) la cual nos permitirá crear un objeto desde una aplicación normal.Esa clase tendrá dos propiedades: Nombre, Versión (esta última será de solo lectura) y un método: HoraActual.

Aquí veremos, paso a paso, cómo crear este grupo de proyectos, cómo escribir el código de la clase (que como te imaginarás no será nada del otro mundo) y cómo añadir un nuevo proyecto para que podamos probar la librería creada, además de cómo referenciar a esa librería, que como comprobarás, se hace de la misma forma que con el resto de "componentes" COM (o ActiveX).

Empecemos abriendo del Visual Basic, en este ejemplo voy a usar el VB6, si no tienes el VB6, podrás usar el VB5, pero no te servirá el VB5CCE, (ver el apéndice A), ya que el VB5CCE no permite crear librerías DLL ActiveX, lo siento. En otra ocasión veremos cómo crear un control ActiveX y en esa ocasión si podrás usar el VB5CCE.

Cuando nos pregunte el tipo de proyecto a crear, seleccionaremos ActiveX DLL, (ver la figura 1)

Page 603: curso básico de programación en visual basic

Figura 1, Diálogo para crear un nuevo proyecto

Se creará un proyecto llamado Project1 (al menos en la versión 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.

Page 604: curso básico de programación en visual basic

Figura 2, propiedades de la clase

Además de la propiedad Name (nombre) del proyecto, también podemos configurar otras cosas, pero eso lo veremos en otra ocasión.

Ahora vamos a añadirle los "miembros" que tendrá la clase.

Nota:Aquí voy a usar "las recomendaciones", aunque en este caso podría 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 código directamente.Si has leído el Apéndice A, en el que te explicaba cómo configurar el entorno del VB, sabrás que me gusta tener en la barra de herramientas un botón (o icono) para crear nuevos procedimientos. Pero si no tienes esa opción en la barra de herramientas, puedes acceder a ella mediante la opción del menú Tools>Add Procedure... (Herramientas>Añadir procedimiento...).Se mostrará un cuadro de diálogo como el mostrado en la figura 3.

Page 605: curso básico de programación en visual basic

Figura 3, Cuadro para añadir una nueva propiedad a la clase

Y se creará la propiedad con el nombre que hemos indicado, en este caso, la propiedad Nombre. Fíjate que cuando creas una propiedad de esta forma, siempre será del tipo Variant, (ver figura 4), por tanto tendrás que cambiar el tipo para que sea del adecuado, en este caso será del tipo String.

Figura 4, el código creado por Add Procedure...

El código será el siguiente:

'------------------------------------------------------------------------------

Page 606: curso básico de programación en visual basic

' Clase cEntrega47 (22/Ago/03)

' Prueba para el Curso Básico de Visual Basic

'

' ©Guillermo 'guille' Som, 2003

'------------------------------------------------------------------------------

Option Explicit

' las variables privadas que se usarán con las propiedades

Private m_Nombre As String

' La propiedad Nombre

Public Property Get Nombre() As String

Nombre = m_Nombre

End Property

Public Property Let Nombre(ByVal newValue As String)

m_Nombre = newValue

End Property

' La propiedad Version será de sólo lectura

Public Property Get Version() As String

Version = "1.00.0001"

End Property

' El método HoraActual devuelve la fecha y hora actual

Public Function HoraActual() As Date

HoraActual = Now

End Function

Ahora vamos a añadir un nuevo proyecto, en este caso será del tipo Exe normal.En el menú File (Archivo) selecciona Add project... (Añadir proyecto...) se mostrará el cuadro de diálogo mostrado en la figura 5 (que es parecido al de la figura 1), selecciona Standard EXE (recuerda que mi VB está en inglés).

Page 607: curso básico de programación en visual basic

Figura 5, Añadir un nuevo proyecto

Al pulsar en "Open" (Abrir) se creará un nuevo proyecto, el cual se añadirá al Explorador de proyectos (ver figura 6).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 también 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 estén "juntos" no quiere decir que el uno sepa del otro. Realmente actuarán como si no se conocieran, es decir, no podremos usar

Page 608: curso básico de programación en visual basic

la clase cEntrega47 desde el proyecto de prueba, ya que el proyecto de prueba (tEntrega47) no conoce la existencia de la librería Entrega47AX.

Para que nuestro proyecto de pruebas se entere de la existencia de esa librería, tendremos que añadir una referencia a la misma, (igual que haríamos para añadir una referencia a los objetos de ADO).Por tanto, tendrás que seleccionar ese proyecto en el Explorador de proyectos y en el menú Project (Proyecto), seleccionar la opción References... (Referencias...) y de las que te muestre, selecciona la que nos interesa, ver figura 7:

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 librería. Cosa que podemos comprobar si declaramos, en la ventana de código del formulario, una variable que haga referencia a la clase cEntrega47 (ver figura 8).

Page 609: curso básico de programación en visual basic

Figura 8, Intellisense nos muestra las clases que contiene la librería Entrega47AX

Con ese código 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 código, ya que antes vamos a añadir unos controles al formulario para poder probar la clase definida en el componente.Añade unas etiquetas para que nos muestre la versión y la fecha y hora actual, aunque para que muestre la fecha y hora, tendremos que pulsar en el botón.El aspecto del formulario en tiempo de diseño será el mostrado en la figura 9:

Figura 9, El formulario en tiempo de diseño

Ahora sólo queda mostrar el código del formulario y probar que todo funciona bien.

Antes de probar que todo funciona bien, veamos el código.

Page 610: curso básico de programación en visual basic

'------------------------------------------------------------------------------

' Formulario de prueba para la clase cEntrega47 (22/Ago/03)

' Prueba para el Curso Básico 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

Fíjate que la variable declarada a nivel de módulo (en el formulario), se ha declarado como del tipo Entrega47AX.cEntrega47, pero también se podía haber declarado como As cEntrega47, esto no es una característica 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 estén 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 práctica, en este caso te lo muestro para que sepas que los dos cEntrega47 son la misma clase, pero para seguir con las buenas prácticas de programación y, sobre todo, para ser consistentes, deberíamos usar la forma larga o corta, pero no mezclarlas.

Para probar que todo funciona, tendríamos que pulsar F5 o bien en el botón "play" de la barra de herramientas.Pero como resulta que el primer proyecto que hemos agregado al grupo de proyectos es la librería, se mostrará un cuadro de diálogo preguntándote que quieres ejecutar... es decir, que no se puede ejecutar una librería así por las buenas... por tanto deberíamos indicarle al Visual Basic que el proyecto que queremos probar es el ejecutable.

Page 611: curso básico de programación en visual basic

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 botón derecho o secundario del ratón) 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"), verás que se muestra la versión de la clase (la que hemos dicho que muestre), y si pulsamos en el botón, se mostrará la fecha y hora actual.

Y esto es todo por hoy.Espero que, aunque de forma orientativa, ya sepas cómo crear (o al menos probar en el IDE de Visual Basic), un componente ActiveX y lo que es mejor, cómo poder usarlo desde otro proyecto.Sólo me queda aclararte que como aún no lo hemos compilado, sólo 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 ocasión veremos cómo compilarlo y cómo configurar la utilización de ese componente, cosa que mucha gente no explica y que puede tener sus más y menos en cuanto a rendimiento... o casi...

Pero eso será en otra ocasión, que ahora me tengo que ir con la parienta, que está algo pachuchilla con un resfriadillo de estos de verano, y cuando las parientas están así, necesitan más cariñete de lo normal...

Nos vemosGuillermo

Este es el código de los dos proyectos de ejemplo: basico47_cod.zip 2.83 KB

Page 612: curso básico de programación en visual basic

Curso Básico de Programaciónen Visual Basic

El curso básico en formato CHM.Nuevo: 23/Enero/2000Si quieres, puedes bajarte el curso básico en formato CHM, puede que en un futuro esté en formato HLP para aquellos estudiantes que sólo disponen del Windows 95 y no del 98 o posterior... aún hay colegios que incluso trabajan en MS-DOS... en fin...

Pulsa este link para bajarte el fichero ZIP (guilleHHW.zip 3465 KB), con la entrega 47 incluida.

Nota del 02/Mar/2003:Debido a que el fichero en formato CHM se está haciendo cada vez más grande... es posible que no lo siga actualizando, pero, para que puedas hacerlo por tu cuenta, voy a poner los ficheros necesarios (y actualizados) para que puedas crearlo y compilarlo, para ello, necesitarás el HHW.exe

Nota del 30/Nov/2004:En vista de que no he publicado el fichero ese que comenté en la nota anterior, aquí está el link para bajarse el susodicho fichero en formato CHM (ayuda de Windows):El curso básico en formato CHM: guilleHHW.zip 3.38 MB

Las entregas en formato ZIP:

Pulsa en los links para bajarte los ficheros comprimidos con las entregas correspondientes.Si te da error al descomprimir cualquiera de ellos, intenta bajarlo de nuevo, ya que no están erróneos y puede ser que te de error por "saturación" de las líneas.De todas formas, esta "aclaración" vale para cualquier fichero zip, lo bajes de mis páginas o de cualquier otra.

Nota del 25/Abr/99: Ahora los ficheros zips se acceden directamente, sin usar la página intermedia de download, espero que de esta forma haya menos problemas al efectuar la "bajada" de los mismos, de todas formas, me reitero en que antes de "protestar" diciendo que ha dado error al bajarlo, lo intentes de nuevo ya que la mayoría de las veces es por fallo del la sobrecarga de Internet y en algunas ocasiones del servidor desde el que estás conectado... aunque en alguna rara ocasión sea un "despiste" mío o un fallo del servidor en el que están mis páginas alojadas... así que, inténtalo un par de veces antes de

Page 613: curso básico de programación en visual basic

"quejarte", no es que haya muchas quejas, pero de vez en cuando recibo alguna...

Si quieres bajarte TODAS las entregas, puedes hacerlo desde aquí:

Todas las entregas (incluida la 47) 0.97 MB

bas_todo.zip

Nota: las imágenes correspondientes, también están incluidas, salvo la del fondo y la media luna...

NOTAS aplicables desde el 24/Dic/2002:(o a partir de la entrega 43)

El contenido de este curso básico de programación NO se podrá publicar en otros sitios webs,tampoco se podrán incluir los ficheros zips con las entregas ni con el fichero en formato CHM.

En caso de que estés interesado en incluirlos, por favor consulta conmigo antes.Si no lo haces...

Seguramente te preguntarás el porqué de esta "negación".Las razones son varias, una de ellas es que hay sitios en los que no mencionan la procedencia ni el autor del curso y creo que no es ético.En otros sitios, las entregas no están actualizadas con todas las entregas del curso.Lo mismo ocurre con el contenido de el/los ficheros zips con las entregas y/o el fichero CHM.

Y creo que eso no está bien, si tienes intención de incluirlos en tu sitio web, me parece bien y te lo agradezco, pero lo único que pido es un compromiso de actualización.Sólo eso... creo que así los que lleguen a encontrar el curso en sitios diferentes al mío, tendrán la misma oportunidad de estar "a la última".

Gracias.