Curso Básico de Programación
en Visual Basic
Entrega
Quince: 23/Mar/98
por Guillermo "guille" Som
Si quieres ir
a alguna de las entregas anteriores, usa estos links:
La Primera, la Segunda, la Tercera, la Cuarta, la Quinta, la Quinta++, la Sexta, la Séptima, la Octava, la Novena, la Décima, la Undécima, la Doce, la Trece, la Catorce, Solución ejercicios entrega
Catorce
![]()
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:

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
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
sobreescribir 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 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, osea 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 (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
Integer
En 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...
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 sobreescribir 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
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
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.
| 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
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 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
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 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
![]()