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:

Formulario de la entrega 15

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 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 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 (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 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

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.

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


 
entrega anterior ir al índice siguiente entrega

Ir al índice principal del Guille