|
Curso básico
de programación en Visual Basic
Lección
15
Un
pequeño editor de textos
|
Realizamos
programas para la gestión de empresas. Empresas medianas y
pequeñas. Programas de contabilidad, cartera de pedidos
clientes proveedores, facturación control de albaranes,
tesorería cartera de cobros y pagos y estadísticas.
Nuestro
agradecimiento a todos los que por unas causas o por otras
visitan nuestra web. Gestión de empresas PYMES. Curso de
programación de Visual Basic.
|
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
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.
|