Un Gran Proyecto, Paso a Paso

 

Primera Entrega (Iniciado el 23/Feb/97)


El título puede parecerte exagerado, pero es para llamar la atención, je, je. 8-)

La intención es hacer una aplicación, (programa, proyecto, como prefieras), desde cero. Partiendo desde conceptos sobre cómo debe funcionar y cómo debemos programarla (la aplicación)

La versión de Visual Basic será la 4.0 o superior. Ya que se usarán las clases para crear los objetos que necesitemos. En el caso de usar controles, intentaré que sean compatibles con 16/32 bits. Aunque prefiero usar los controles de Windows 95, haré un "esfuerzo" e intentaré "adaptarme" a los de 16 bits.

La idea que se me ha ocurrido para este proyecto, es la siguiente:

Una base de datos al estilo de una agenda u organizador, con tareas pendientes de hacer, además de un registro de la información que vamos "recogiendo" de distintas fuentes y que nos puede servir para "catalogar" esa información.
Por ejemplo: artículos aparecidos en revistas, mensajes sobre ciertos temas que vamos "encontrando" por ahí dispersos y que incluiremos en nuestra base de datos para posteriormente poder localizarlos de una forma rápida. Incluso lo podemos usar para catalogar cosas: los CDs, tanto de música como de programación, etc.

Para ello, crearemos una base de datos con varias tablas:

  • La primera será una al estilo "To-Do" (con las tareas pendientes de hacer y/o realizadas)
  • La segunda tabla, llevará un registro de esos artículos, libros, mensajes, colecciones, etc. que queremos "catalogar"

Si después necesitamos más, podremos ampliar tanto la base de datos como el programa.

Para la Tabla de las tareas pendientes/realizadas, se me ocurren los siguientes campos:

Campo Tipo Descripción
     
ID Contador Un contador, me gusta usarlo en todas las tablas.
Fecha Fecha/Hora La fecha actual o de cuando se nos ha ocurrido la idea.
Asunto Texto (255) Una breve descripción de lo que hay que hacer.
Descripción Memo Detalle ampliado de la tarea a realizar.
Fecha Inicio Fecha/Hora Cuando empezamos a ponernos "mano a la obra".
Fecha Término Fecha/Hora Fecha de cuando terminamos la tarea o fecha tope para acabarla, según el valor del siguiente campo.
Terminada Entero Si ya hemos terminado o no la tarea. (valor booleano)

En asunto podríamos crear una lista de conceptos que usaríamos en ocasiones posteriores, de esta forma, nos organizaremos mejor. Por ejemplo: contestar el correo electrónico, probar una rutina, etc.

La tabla del catálogo de información, podría ser:

Campo Tipo Descripción
     
ID Contador Insisto en el contador
Fecha Fecha/Hora Me gusta saber cuando me ocurren las cosas
Tema Texto (50) Programación, música, amigos, libros, artículos, etc.
Asunto Texto (255) Como en la tabla anterior, un concepto básico del tema en cuestión. En el caso de un artículo de una revista, podría ser el titular del artículo.
Medio Texto (255) Pues, el sitio donde hemos visto u oido el asunto. Por ejemplo el nombre de la revista.
Localización Texto (255) El número y página donde encontrar el artículo.
Descripción Memo La descripción del asunto, copia del artículo, etc.
Detalle Binario En este campo podremos "pegar" la información extra que queramos, desde un mensaje, hasta un gráfico y cualquier documento OLE.

Por supuesto, las longitudes de los textos, puedes "acortalas", pero a mi me gusta que sobre, en lugar de que me falte sitio. Debes tener en cuenta que al almacenarse en la base de datos, no se desperdicia el espacio no usado.

Ahora que ya tenemos "la base" de nuestra idea, vamos a empezar a crear el proyecto:

Para ello debemos considerar, que o bien creamos "manualmente" la base de datos o bien será el programa el que lo haga. Lo más fácil es la primera opción, eso nos evitaría "un montón" de código; pero la intención de este artículo/proyecto es aprender; así pues, vamos a tomarnos "la molestia" de hacerlo por medio de código, desde el programa.

Así las cosas, cuando el programa empiece, deberá comprobar que base de datos abrir y que tablas usar, aunque ésto último, no será configurable.
Esta información la tomará de un archivo de configuración (INI). Para ello podemos usar las funciones incorporadas en VB4: GetSetting y SaveSetting. Pero el problema es que según trabajemos en 16 ó 32 bits, usará archivos INI o el registro del sistema. Por una parte, usar el registro del sistema, está bien si sólo trabajamos en un ordenador, pero ¿que ocurre si el proyecto se usa en una red? Por tanto, creo que es preferible usar archivos INI. El problema con estos archivos es que se suelen guardar en el directorio local de Windows, así que también tendríamos problemas al trabajar en red.
La solución: crear el archivo de configuración en el directorio de la aplicación. Con esta solución, el único requisito es que el programa siempre se ejecute desde el mismo directorio. Cosa que es habitual cuando se trabaja en red.

Para poder crear archivos de configuración, vamos a usar las funciones incluidas en el API de Windows: GetPrivateProfileString y WritePrivateProfileString.
Aunque las llamadas a estas funciones se filtrarán por unas rutinas de creación propia, con los mismos parámetros que las incorporadas en Visual Basic 4. Así, si cambias de idea y prefieres usar las que VB trae de fábrica, sólo tendrás que efectuar una operación de búsqueda/reemplazo.

Manos a la obra: Vamos a crear un proyecto nuevo.
Si aún no tienes cargado el Visual Basic, ya puedes hacerlo.
Crea un nuevo proyecto y comprueba que esté seleccionada la opción de "declarar las variables", así si cometes algún error al teclear, podrás detectar mejor dónde está el fallo.
El nombre del proyecto será: gsNotas, (que se note que soy yo el que se está pegando el curro de escribir esto, jé).
El form que automáticamente se crea, se llamará también gsNotas, pero con la extensión .frm

Consejo: Yo tengo por costumbre guardar inmediatamente las cosas, son muchas las sorpresas desagradables que he tenido por culpa de no seguir este consejo.

Así que pulsa en guardar proyecto y dale los nombres que te he indicado. Por supuesto que puedes usar otros, pero estos son los que usaré al referirme a ellos.

Ahora vamos a crear un módulo basic para guardar las rutinas y declaraciones del API que antes he comentado:
Selecciona Insertar nuevo módulo y lo guardas como: Profile.bas
Escribe el siguiente código en la sección de las declaraciones:

'--------------------------------------------------
' 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 xxxProfileString
'--------------------------------------------------
Option Explicit

#If Win32 Then
    'Declaraciones para 32 bits
    Private Declare Function GetPrivateProfileString Lib "Kernel32" 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" Alias "WritePrivateProfileStringA" _
		(ByVal lpApplicationName As String, ByVal lpKeyName As Any, _
		 ByVal lpString As Any, ByVal lpFileName As String) As Long
#Else
    'Declaraciones para 16 bits
    Private Declare Function GetPrivateProfileString Lib "Kernel" _
		(ByVal lpApplicationName As String, ByVal lpKeyName As Any, _
		 ByVal lpDefault As String, ByVal lpReturnedString As String, _
		 ByVal nSize As Integer, ByVal lpFileName As String) As Integer
    Private Declare Function WritePrivateProfileString Lib "Kernel" _
		(ByVal lpApplicationName As String, ByVal lpKeyName As Any, _
		 ByVal lpString As Any, ByVal lplFileName As String) As Integer
#End If

Bien, ya tenemos las declaraciones del API, fijate que se han declarado de forma Privada, así no las usaremos desde otro módulo por equivocación, a no ser que se declaren de nuevo.

Ahora vamos a crear las dos rutinas que se encargarán de leer y escribir la información del archivo INI.
La primera, para seguir el mismo orden que las declaraciones del API, será LeerIni:

'
Public Function LeerIni(lpFileName As String, lpAppName As String, lpKeyName As String, Optional 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
    If IsMissing(vDefault) Then
        lpString = ""
    Else
        lpString = vDefault
    End If

    sRetVal = String$(255, 0)

    LTmp = GetPrivateProfileString(lpAppName, lpKeyName, lpString, sRetVal, Len(sRetVal), lpFileName)
    If LTmp = 0 Then
        LeerIni = lpString
    Else
        LeerIni = Left(sRetVal, LTmp)
    End If
End Function

La segunda, será el equivalente a SaveSetting:

Sub GuardarIni(lpFileName As String, lpAppName As String, lpKeyName As String, 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

Una vez creadas las dos rutinas, podemos grabar el módulo.

El siguiente paso será leer la información del archivo de configuración y si la base de datos no existe, cosa que ocurrirá la primera vez que ejecutemos el programa, nos debe preguntar por el nombre y la localización (path), acto seguido pasará a crearla.
Si ya existiera, cosa que ocurrirá en las siguientes ocasiones, simplemente la usará.

Antes de seguir, (tranquilo, no te impacientes, de lo que se trata es de hacer las cosas bien, o al menos intentarlo y para ello debemos tener las cosas claras), deberíamos pensar en la estructura que tendrá el archivo de configuración.
Aunque en este proyecto no es importante manejar configuraciones generales y particulares, nos puede servir para otros, en los que si sea necesario hacer la distinción.

Por ejemplo, el nombre de la base de datos si puede ser General, pero no su localización o la posición y tamaño de la ventana de trabajo.
Y la localización, ¿por qué no? Pues, porque si trabajamos en red, en cada puesto puede tener un nombre unidad distinto. Aunque yo recomendaría el uso del nombre "largo"
\\Nombre_Equipo\Unidad_Disco\Directorio
para que de esta forma no hubiese problemas de localización.
Bueno, vamos al tema, el archivo tendrá un apartado General con el nombre de la base de datos.
También un apartado para cada usuario, con entradas (claves), para la Localización, (el path de la base de datos), El tamaño y la posición de la ventana y otras cosas que puedan ir surgiendo.

También puede ocurrir que deseemos manejar varias bases de datos a la vez. Aunque sólo una en cada ejecución del programa. Esto será un problema si trabajamos en red, (otro más), porque al modificar un archivo común de configuración, alterará al resto de los usuarios, al menos los que quieran trabajar después de nosotros.

Solución: Poder usar varios nombres de bases de datos.
Para hacer esto, debemos tener un contador con el número de las bases creadas, así como el nombre de cada una de ellas.
En el apartado general, en lugar de tener sólo el nombre de la base de datos, tendremos una entrada que nos indicará cuantas bases hemos creado, para de esta forma, saber "cuantas veces" deberá comprobar los nombres...
Ya verás esto explicado más adelante, ahora no debe preocuparte este tema, sólo saber que existirán entradas del tipo:
Basexx= Donde xx será un número de dos cifras, (
creo que con 99 bases de datos diferentes, es suficiente!)
También existirá una entrada con el número de bases diferentes:
NumeroBases=x Este será el contador y se incrementará cada vez que se cree una nueva base de datos.
El programa leerá las bases creadas y nos mostrará un ComboBox del cual seleccionaremos la que queramos usar.

¿Por qué complicarnos tanto? Porque, como he dicho antes, de eso se trata, de hacer cosas complicadas para que en un momento dado, cuando se presente la ocasión, saber por donde cogerla. Además, puede ser interesante llevar varias bases, por si no queremos "mezclar" los asuntos.

Bueno, sigamos. Como he comentado, pueden existir varios usuarios, así que el programa deberá preguntar el nombre, para poder "cargar" la configuración personal.
Por tanto tenemos que el programa debe saber el nombre del usuario y que base de datos quiere usar, o crear, en caso de que no exista.

Manos a la obra: Crea un nuevo form, (el Form1 lo vamos a dejar para más adelante), y guardalo como: Entrada.frm. Los controles que vamos a usar serán: un TextBox, un ComboBox y tres botones: Aceptar, Cancelar y Examinar, además de dos etiquetas.
Este sería el aspecto del formulario Entrada.frm:

Las propiedades del form serán: Name: frmEntrada, BorderStile: 3-Fixed Dialog
Combo1 Style: 0-DropDown Combo
Los botones se llamarán: cmdExaminar, cmdAceptar y cmdSalir.
CommonDialog1.Filter: Bases de datos (*.mdb)|*.mdb|Todos los archivos (*.*)|*.*

Vamos a ver el código de forma genérica:

Form_Load:
	'Leer el número de bases creadas
	'Comprobar y leer los nombres
	'Mostrar la información con los datos procesados.

cmdAceptar_Click:
	'Comprobar si la base existe en el combo
	'	Si no existe, añadirlo al combo
	'Comprobar si existe "fisicamente" la base
	'	Si no existe crearla
	'Guardar los datos de configuración

cmdCancelar_Click:
	'Terminar el programa!!!

cmdExaminar_Click:
	'Abrir el control de diálogos comunes y "localizar"
	'los archivos con extensión MDB
	'Añadirlo al Combo y seleccionarlo

Veamos ahora los detalles:
(
lo voy a dejar por hoy, mañana será otro día... son las 06:20, hora local)


Nota: Los cambios realizados (el 1/Mar) son:

  • "El look" del Form de Entrada.
  • Y que el ComboBox es del tipo 0- DropDown Combo
    (sino, no se puede escribir!)

ir al índice principal