Ejemplo propuesto
Objetivo
El objetivo es crear una agenda de teléfonos
en la que podamos consultar fácilmente las fichas, así como insertar nuevas
fichas o borrar fichas existentes.
Para ello, crearemos una nueva clase ClaAgenda:
· ColFichas:
colección de las fichas almacenadas en la agenda
· Property IntNumFichas: devuelve el número de fichas almacenadas en la agenda
· Function CrearFicha(StrNombre, StrApellidos, StrDireccion,
StrTelefono) as Integer: crea una nueva ficha y la inserta en la colección ColFichas.
Devuelve el número de la ficha.
· Function BorrarFicha(IntNumFicha): borra la ficha cuyo número se pasa como
argumento.
· Function Ficha(IntIndice) As ClaFicha: devuelve la ficha correspondiente a la
posición IntIndice en la colección de fichas ColFichas
· Sub Class_Terminate(): eliminaremos todas las fichas de la colección ColFichas, así como la
propia colección, antes de que el objeto se elimine de memoria
En el formulario podremos realizar tres acciones:
· Crear una nueva ficha: primero pulsaremos el botón Nueva
ficha, con lo que se borrarán los cuadros de texto y se incrementará el
límite superior de la barra de scroll. Una vez introducidos los datos de la
nueva ficha, pulsaremos el botón Crear
ficha, donde se llamará al método CrearFicha
de la clase ClaAgenda, obteniendo el
número de ficha resultante, que nos servirá para poder borrarla en caso
necesario.
· Consultar una ficha existente: utilizando la barra de scroll podremos
consultar todas las fichas de la agenda. Cada vez que cambie el valor de la
barra de scroll, actualizaremos el formulario para mostrar los datos de la ficha
correspondiente (por ejemplo, si el valor de la barra de scroll es 3,
mostraremos la cuarta ficha de la
colección; hay que recordar que el límite inferior de la barra de scroll es 0,
mientras que una colección empieza en 1). Cada vez que cambiemos de ficha,
actualizaremos el número de la ficha actual.
· Borrar la ficha actual: utilizaremos el número de ficha actual para llamar al método BorrarFicha de la clase ClaAgenda. Decrementaremos el límite
máximo de la barra de scroll, y actualizaremos el formulario para que muestra
los datos de la ficha siguiente o anterior a la borrada.
Desarrollo del ejemplo
1. Crearemos el siguiente
formulario:
2. Crearemos una clase Agenda con
una propiedad privada ColFichas de tipo Collection, que no tendrá métodos Property
asociados. Crearemos un método Property Get llamado IntNumFichas que nos devuelva el número
de fichas de la colección ColFichas.
3. Implementaremos un método
público CrearFicha que recibe como argumentos el nombre, apellido, dirección y
teléfono y crea una ficha nueva (utilizaremos la clase Ficha definida en el
ejemplo 3), asignándole un número (este número será una especie de “campo
autonumérico”, que generaremos incrementalmente). Además de crear la nueva
ficha, la introduce en la colección ColFichas, poniendo como clave el número de
la ficha (como la clave tiene que ser una cadena, hay que hacer la conversión a
string de dicho número).
4. Implementaremos también un
método público BorrarFicha, que recibe como argumento la clave de la ficha que
queremos eliminar. Este método elimina la ficha de memoria y también de la
colección.
5. También crearemos un método
público Ficha, que recibe como argumento el índice de la ficha dentro de la
colección ColFichas y devuelve la ficha correspondiente.
6. Como último elemento de la
clase, en el evento Terminate liberaremos todo el espacio de memoria que podamos
tener reservado para las fichas y la colección.
7. En el formulario crearemos un
objeto de la clase Agenda, y una variable IntNumFichaActual que indicará el
número de la ficha cuyos datos aparecen en el formulario. Como estas variables
van a ser utilizadas en todas las funciones del formulario, las crearemos como
variables globales del formulario.
8. Al pulsar el botón “Nueva
ficha”, debemos aumentar la propiedad Max de la barra de scroll,
posicionarnos en el último elemento y limpiar los datos (nombre, apellidos,
dirección y teléfono), preparando el formulario para insertar una nueva ficha.
(¡Ojo!: para la primera ficha, no es
necesario incrementar el límite máximo de la barra de scroll, ni limpiar el
formulario).
9. Una vez insertados los datos de
la nueva ficha, pulsaremos el botón “Crear ficha”, donde llamaremos al método
CrearFicha del objeto Agenda y actualizaremos el título del frame, donde pondrá
“Ficha N” (siendo N el número de la nueva ficha, que obtendremos como valor de
retorno del método CrearFicha).
10. Cada vez que cambie la barra de
scroll (evento Change) debemos actualizar los datos del formulario, mostrando
la información de la ficha correspondiente en la colección ColFichas del objeto
Agenda. Crearemos una función ActualizarBarraScroll que se encargará de ello (¡Ojo!: la función debe comprobar si hay
fichas en la colección, y también si estamos insertando una ficha nueva, en
cuyo caso en lugar de actualizar los datos del formulario, los limpiará.).
También tenemos que obtener el número de la ficha actual.
11. Cuando pulsemos el botón “Borrar
ficha” llamaremos al método BorrarFicha del objeto Agenda pasándole el número
de la ficha actual. Una vez borrada, decrementaremos el valor máximo de la
barra de scroll (¡Ojo!: cuidado
cuando borramos la última ficha) y actualizaremos los datos del formulario
llamando a la función ActualizarBarraScroll.
12. Para terminar, sólo nos falta
controlar cuando están habilitados los cuadros de texto, los botones y demás
elementos para que quede lo más coherente posible.
Ejemplo
Resuelto
Formulario
'_________________________________________________________________________________
' Ejemplo 4 del Curso de Visual Basic Avanzado
'' Definición del formulario, este será el interfaz del usuario.
'_________________________________________________________________________________
Dim ObjAgenda As New ClaAgenda
Dim IntNumFichaActual As Integer
Private Sub
ButBorrarFicha_Click()
If MsgBox("¿Desea eliminar la ficha
actual?", vbYesNo, "Borrado de ficha") = vbNo Then
' Borramos la ficha actual
ObjAgenda.BorrarFicha IntNumFichaActual
' Actualizamos el número de elementos de la
barra de scroll
If ObjAgenda.IntNumFichas > 0 Then
HScroll1.Max =
ObjAgenda.IntNumFichas - 1
' Actualizamos la barra de scroll
Private Sub ButCrearFicha_Click()
' Creamos una nueva ficha con los datos
introducidos por el
' usuario, y obtenemos el código de la
ficha creada
IntNumFichaActual =
ObjAgenda.CrearFicha(TxtNombre, TxtApellidos, TxtDireccion, _
' Actualizamos el título del frame
Frame1.Caption = "Ficha " &
IntNumFichaActual
' Deshabilitamos el botón de crear ficha y
el frame
ButCrearFicha.Enabled = False
'
Habilitamos los botones de nueva ficha y borrar
' ficha, así como la barra de scroll
ButNuevaFicha.Enabled = True
ButBorrarFicha.Enabled = True
Private Sub ButNuevaFicha_Click()
'
Actualizamos la barra de scroll
If ObjAgenda.IntNumFichas = 0 Then
' Con la primera ficha creada,
habilitamos
' Incrementamos el número de elementos
de
HScroll1.Max = HScroll1.Max + 1
HScroll1.Value =
HScroll1.Max
'
Habilitamos el botón de crear ficha y el frame
ButCrearFicha.Enabled = True
'
Deshabilitamos los botones de nueva ficha y borrar
' ficha, así como la barra de scroll
ButNuevaFicha.Enabled = False
ButBorrarFicha.Enabled = False
'
Deshabilitamos el frame, la barra de scroll,
' el botón de crear ficha y el botón de
borrar ficha
ButCrearFicha.Enabled = False
ButBorrarFicha.Enabled = False
'
Establecemos los límites de la barra de scroll
Private Sub Form_Unload(Cancel As Integer)
Private Sub HScroll1_Change()
'
Actualizamos la barra de scroll
Private Function
ActualizarBarraScroll()
' Comprobamos si no hay fichas o estamos
creando una ficha nueva
If HScroll1.Max = ObjAgenda.IntNumFichas Then
'
Limpiamos los datos del frame
TxtApellidos = vbNullString
TxtDireccion = vbNullString
TxtTelefono = vbNullString
' Actualizamos el título del frame
Frame1.Caption = vbNullString
' Deshabilitamos el botón de borrado
ButBorrarFicha.Enabled = False
' Obtenemos la ficha correspondiente de
la agenda
' (buscamos por índice, no por código
de ficha)
Set ObjFicha = ObjAgenda.Ficha(HScroll1.Value +
1)
'
Sacamos los datos de la ficha
TxtNombre = ObjFicha.StrNombre
TxtApellidos = ObjFicha.StrApellidos
TxtDireccion = ObjFicha.StrDireccion
TxtTelefono = ObjFicha.StrTelefono
' Actualizamos el código de la ficha
actual
IntNumFichaActual =
ObjFicha.IntNumFicha
Frame1.Caption = "Ficha " &
IntNumFichaActual
Clase Agenda
'_______________________________________________________________________
' Ejemplo 4 del Curso de Visual Basic Avanzado
'' Definición de la clase AGENDA, aquí estarán los datos de cada agenda
y los métodos
'_________________________________________________________________________________
Private ColFichas As New
Collection ' Colección de fichas de
la agenda
' Método para obtener el número
de fichas de la agenda
Public Property Get IntNumFichas() As Integer
IntNumFichas
= ColFichas.Count
' Método para crear una ficha
nueva en la agenda
Public Function CrearFicha(ByVal StrNombre As String, _
ByVal StrApellidos As String,
ByVal StrDireccion As String, _
ByVal StrTelefono As String)
As Integer
Static IntNumTotalFichas As
Integer
'
Incrementamos el "campo autonúmerico" IntNumTotalFichas
IntNumTotalFichas = IntNumTotalFichas + 1
' Creamos un nuevo objeto de la clase ficha
Set ObjFicha = New ClaFicha
' Establecemos las propiedades de la nueva
ficha
ObjFicha.StrNombre = StrNombre
ObjFicha.StrApellidos = StrApellidos
ObjFicha.StrDireccion = StrDireccion
ObjFicha.StrTelefono = StrTelefono
ObjFicha.IntNumFicha = IntNumTotalFichas
' Insertamos la nueva ficha en la colección
de fichas, poniendo
' como key el código de la ficha, para
poder buscarla después
' por código (convertimos a string el
código porque el key
' tiene que ser un string)
ColFichas.Add ObjFicha,
CStr(ObjFicha.IntNumFicha)
' Devolvemos el código de la ficha creada
CrearFicha = IntNumTotalFichas
' Método para borrar una ficha
de la agenda
Public Function
BorrarFicha(ByVal IntNumFicha As Integer)
' Obtenemos la ficha correspondiente de la
colección
' de fichas (para poder buscar por key,
tenemos que
' convertir el número a string,
pues en caso contrario
Set ObjFicha =
ColFichas.Item(CStr(IntNumFicha))
' Eliminamos la ficha de la colección
ColFichas.Remove (CStr(IntNumFicha))
' Método para eliminar todas las
fichas de una agenda cuando
Private Sub Class_Terminate()
' Eliminamos todas las fichas de la
colección
For Each ObjFicha In ColFichas
' Método para obtener una ficha
específica de la agenda (no busca
' por key sino por índice)
Public Function Ficha(ByVal IntIndice As Integer) As ClaFicha
Set Ficha = ColFichas.Item(IntIndice)
5. Tecnología ActiveX
Introducción
El objetivo de este capítulo es mostrar al
lector el proceso de creación de componentes ActiveX, y su utilización en un
proyecto. También se pretende que el lector se familiarice con el proceso de
publicación de un control ActiveX en una página Web, como muestra de la
versatilidad de este tipo de componentes.
Con este capítulo se hace una introducción al
curso Active X, que es la
continuación de los cursos Visual Basic 5.0 (básico y avanzado).
Conocimientos teóricos
Qué es un componente ActiveX
Un componente ActiveX es un archivo .EXE, .DLL u .OCX que cumple la
especificación ActiveX para proporcionar objetos. Para entendernos, un control
ActiveX es una extensión del cuadro de herramientas de Visual Basic. Los
controles ActiveX se usan como cualquiera de los controles estándar
incorporados, como el control CheckBox.
Cuando agregamos un control ActiveX a un programa, pasa a formar parte del
entorno de desarrollo y de tiempo de ejecución y proporciona nueva
funcionalidad a la aplicación.
Los controles ActiveX incrementan la capacidad del programador de
Visual Basic conservando algunos métodos, propiedades y eventos ya familiares,
como la propiedad Name, que se comportan como
cabría esperar. Pero, además, los controles ActiveX incorporan métodos y
propiedades que aumentan enormemente la flexibilidad y capacidad del
programador de Visual Basic.
La
tecnología ActiveX permite a los programadores ensamblar estos componentes
software reutilizables en sus aplicaciones.
Los controles ActiveX, que se llamaban antes
controles OLE, son elementos estándar de interfaz de usuario que nos permiten
ensamblar rápidamente formularios y cuadros de diálogo. Los controles ActiveX
también dan vida a Internet, agregando una nueva y atractiva funcionalidad a
las páginas del World Wide Web.
Visual Basic siempre ha presentado diversos
controles que podíamos utilizar en nuestras aplicaciones. Ahora podemos crear
nuestros propios controles para utilizarlos con Visual Basic y otras
herramientas de programación.
Diseñar un control ActiveX puede resultar tan fácil como diseñar un
formulario de Visual Basic: podemos utilizar los comandos gráficos de Visual
Basic con los que estamos familiarizados para dibujar el control o bien crear
un grupo de controles con los controles existentes.
Los controles ActiveX se pueden depurar en proceso, de forma que puede
pasar directamente desde el código del formulario de prueba al código del
proyecto de control ActiveX.
Visual Basic facilita crear paquetes de controles ActiveX
perfeccionados al agregar a los controles páginas de propiedades, constantes
con nombre y eventos.
Podemos compilar nuestros controles ActiveX directamente en el archivo
ejecutable de nuestra aplicación o en archivos .ocx, que se pueden utilizar con
herramientas de programación como Visual Basic y Microsoft Visual C++, con
productos para el usuario final como Microsoft Office y en Internet.
Tipos de componentes ActiveX
Existen tres tipos
de componentes ActiveX:
·
Controles ActiveX: Los controles ActiveX son elementos estándar
de interfaz de usuario que permiten construir rápidamente aplicaciones, como si
de un puzzle se tratara.
Visual
Basic proporciona un conjunto de controles predefinidos, y permite la creación
de controles de usuario. Por ejemplo, podríamos hacer una caja de texto que
controlara si los caracteres introducidos son numéricos, mostrando los números
introducidos con la coma de los decimales o de los millares, y utilizarlo como
el convencional textbox.
·
ActiveX DLL (librerías): Los componentes
ActiveX proporciona código que se puede volver a utilizar en forma de objetos.
Una aplicación (cliente) puede crear
un objeto a partir de un componente ActiveX (servidor) y utilizar sus propiedades y métodos. Los componentes
ActiveX DLL se ejecutan dentro del espacio de direcciones del programa que los
utiliza.
· ActiveX EXE: Un componente ActiveX EXE es muy similar a un componente ActiveX DLL.
La única diferencia es que un componente ActiveX EXE se ejecuta en su propio
espacio de direcciones, lo que permite que el cliente puede ejecutar un método del componente y continuar con su
ejecución mientras el componente realiza el trabajo, manteniendo el control de
la ejecución del programa principal si se quedara bloqueada la llamada a una
función del Active X. La pega es que gasta mas recursos, pues la comunicación
entre el cliente y el servidor es más compleja.
Creación dinámica de matrices
Hay ocasiones en las que no conocemos a
priori lo grande que debe ser una matriz. Puede que deseemos poder cambiar el
tamaño de la matriz en tiempo de ejecución.
Una matriz dinámica se puede cambiar de
tamaño en cualquier momento. Las matrices dinámicas son una de las
características más flexibles y cómodas de Visual Basic, y nos ayudan a
administrar de forma eficiente la memoria. Por ejemplo, podemos utilizar una
matriz grande durante un tiempo corto y liberar memoria del sistema cuando no
necesite volver a utilizar la matriz.
La alternativa consiste en declarar la matriz
con el mayor tamaño posible y pasar por alto los elementos de la matriz que no
necesitemos. Sin embargo, esta solución, si se utiliza demasiado, puede hacer
que el sistema operativo funcione con muy poca memoria.
El primer paso en la creación de una matriz
dinámica es su declaración: en un primer momento, la matriz no tendrá ninguna
dimensión. Por ejemplo, la siguiente sentencia define un array de enteros, pero no especifica cuántos elementos va a
contener:
Dim IntMatriz() As Integer
Para asignar el número real de elementos
utilizaremos la instrucción ReDim. Por ejemplo, con la
siguiente sentencia permitiremos que el array
anterior almacene 10 enteros:
La instrucción ReDim puede aparecer sola en un
procedimiento. A diferencia de las instrucciones Dim y Static,
ReDim es una instrucción ejecutable; hace que
la aplicación realice una acción en tiempo de ejecución.
La instrucción ReDim acepta la misma sintaxis que se utiliza
en las matrices fijas. Cada ReDim puede cambiar el número de elementos, así como los límites
inferior y superior de cada dimensión. Sin embargo, si la matriz ha sido
declarada con un número de elementos determinado, no podremos utilizar la
sentencia Redim para alterarlo. Por ejemplo, si definimos la matriz
anterior de esta forma:
Dim IntMatriz(5) As Integer
No podremos utilizar posteriormente la
sentencia Redim para alterar su tamaño.
Preservar el contenido de las matrices dinámicas
Cada vez que ejecutamos la instrucción ReDim se perderán todos los valores
almacenados en ese momento en la matriz. Visual Basic restablece los valores al
valor Empty (en matrices Variant), a cero (en matrices
numéricas), a una cadena de longitud cero (en matrices de cadenas) o a Nothing (en matrices de objetos).
Esto resulta muy útil cuando deseemos
preparar la matriz para contener datos nuevos o cuando deseemos reducir el
tamaño de la matriz para que ocupe menos memoria. Puede que a veces nos
interese cambiar el tamaño de la matriz sin perder los datos de la misma. Para
ello podemos utilizar ReDim con la palabra clave Preserve. Por ejemplo, podemos
ampliar una matriz en un elemento sin perder los valores de los elementos
existentes mediante la función UBound para hacer referencia al límite superior:
ReDim Preserve IntMatriz(UBound(IntMatriz) + 1)
Sólo se puede cambiar el límite superior de
la última dimensión de una matriz multidimensional cuando se utiliza la palabra
clave Preserve;
si cambiamos alguna otra dimensión o el límite inferior de la última dimensión,
se producirá un error en tiempo de ejecución. Así pues, podemos utilizar un
código como el siguiente:
ReDim Preserve IntMatriz(10,
UBound(IntMatriz, 2) + 1)
Pero no podemos utilizar este código:
ReDim Preserve IntMatriz(UBound(IntMatriz, 1) +
1, 10)
Matrices de controles
Visual Basic
permite crear matrices de controles dinámicamente y situar los nuevos controles
a voluntad en un formulario. Supongamos que tenemos un control llamado MiCtl
(por ejemplo, un CommandButton). Para
crear un nuevo control, utilizaríamos la sentencia:
donde N es el
índice del nuevo control. Una vez creado, podemos situar el nuevo control en la
posición que deseemos estableciendo apropiadamente su propiedad Top
y Left,
y hacerlo visible estableciendo a True la propiedad Visible.
Publicar un control ActiveX en un página Web
Una vez creado el control ActiveX, hay que
seguir los siguientes pasos:
1.
Generar el archivo .OCX correspondiente y guardar el proyecto después.
2.
Ejecutar el Asistente para instalar
aplicaciones.
3.
Seleccionar el proyecto correspondiente al control ActiveX, marcando
la opción Instalación para transferencia
desde Internet.
4.
Durante el proceso de creación de los archivos, especificaremos el
directorio donde se crearán los archivos, así como el lugar desde donde el
cliente que abra la página Web donde estará el control ActiveX bajará los
archivos .CAB de los componentes de nuestro control (si es necesario). También
contestaremos afirmativamente a la pregunta de si queremos incluir la DLL de la
página de propiedades (necesario si queremos ejecutar el control desde fuera de
Visual Basic, como es el caso).
El asistente nos creará principalmente dos
archivos con el nombre del control ActiveX, uno con extensión .CAB que contiene
el propio control (y otros elementos utilizados por él, si procede) y otro con
extensión .HTM en el que se muestra un ejemplo de página HTML que utiliza el
control ActiveX.
Ejemplo propuesto
Objetivo
El objetivo es crear un control ActiveX que
simule el juego de las Tres en raya.
Una vez creado, generaremos una página Web donde pegaremos el control con este
ejemplo podemos ver la potencia que tiene esta herramienta, ya que con el uso
de un control podemos tener una aplicación bastante complicada corriendo detrás,
siendo muy fácil el implantación para usos posteriores.
Desarrollo del ejemplo
1) Crearemos un control ActiveX TresEnRaya:
· Simularemos un tablero de
3x3, pero sólo crearemos el frame que
lo contiene y UN botón.
Estableceremos las propiedades del botón (color, nombre…), especialmente la
propiedad Index=0 (va a ser el
primero de una matriz de controles).
· StrTurno:
almacena el carácter correspondiente al turno actual (“O” o “X”)
· IntNumeroJugadas: recoge el número de movimientos realizados
· Sub UserControl_Initialize: a partir de un único botón creado en tiempo
de diseño en el control ActiveX, generará una matríz ordenada de 3x3 botones
(índices de 0 a
8).
· Sub InicializarFormulario: inicializará el caption de los botones, el
turno y el número de jugadas.
· Function HayTresEnRaya() As Boolean: comprobará cuando hay tres en raya, en
función del caption de los botones que es donde nos indica el jugador que lo
eligió (O o X).
· Sub ButCelda_Click(Index As Integer): realizará todo el proceso del juego
(comprobar si hay tres en raya, comprobar si el botón ya ha sido pulsado, y en
caso contrario cambiar el caption del mismo, comprobar si se han marcado ya
todas las casillas, incrementar el número de jugadas realizadas...)
2) Una vez creado el control, abriremos un proyecto nuevo (de tipo EXE
estándar) en el que añadiremos un formulario en blanco y nuestro proyecto TresEnRaya. Pegaremos el control TresEnRaya, que
aparecerá en la caja de herramientas, y depuraremos el control abierto como
proyecto, ya que si no tendríamos que depurarlo ejecutándolo directamente, y
para solucionar un posible error volvernos al VB para realizar la modificación,
recompilar y hacer el ejecutable, mientras que de esta forma con reescribir y
ejecutar tenemos el problema resuelto.
3)
Cuando el control funcione bien, abriremos el proyecto inicial,
generaremos el archivo TresEnRaya.ocx, guardaremos
el proyecto y cerraremos Visual Basic.
4)
Ejecutaremos el Asistente para instalar aplicaciones y crearemos los
ficheros necesarios para publicar nuestro control en una página Web.
Ejemplo resuelto
'________________________________________________________________________
' Ejemplo 5 del Curso de Visual Basic Avanzado
'' Código del control de TRES EN RAYA, funciones necesarias para el
control de la partida
' y control de movimientos de los jugadores.
'_________________________________________________________________________________
Dim IntNumeroJugadas As
Integer
Private Sub ButCelda_Click(Index
As Integer)
If ButCelda(Index).Caption = vbNullString
Then
' Pintamos la marca en la ButCelda
correspondiente
ButCelda(Index).Caption = StrTurno
'
Comprobamos si hay 3 en línea
MsgBox "¡Has ganado!", vbOKOnly
Or vbExclamation, "Tres en Raya"
'
Comprobamos si se han realizado todos los movimientos
If
IntNumeroJugadas = 9 Then
If
MsgBox("Ha habido un empate. ¿Quieres jugar otra vez?", vbYesNo Or
vbQuestion, "Tres en Raya") = vbYes Then
'
Incrementamos el número de movimientos realizados
IntNumeroJugadas = IntNumeroJugadas + 1
Private Function
HayTresEnRaya() As Boolean
Dim IntI, IntJ As Integer
If ButCelda(3 * IntI).Caption <>
vbNullString And _
ButCelda(3 * IntI).Caption =
ButCelda(3 * IntI + 1).Caption And _
ButCelda(3 * IntI).Caption =
ButCelda(3 * IntI + 2).Caption Then
If ButCelda(IntI).Caption <> vbNullString
And _
ButCelda(IntI).Caption = ButCelda(3
+ IntI).Caption And _
ButCelda(IntI).Caption = ButCelda(6
+ IntI).Caption Then
If ButCelda(0).Caption <>
vbNullString And _
ButCelda(0).Caption =
ButCelda(4).Caption And _
ButCelda(0).Caption =
ButCelda(8).Caption Then
If ButCelda(2).Caption <>
vbNullString And _
ButCelda(2).Caption =
ButCelda(4).Caption And _
ButCelda(2).Caption =
ButCelda(6).Caption Then
Public Sub InicializarFormulario()
ButCelda(IntI).Caption = vbNullString
'
Inicializamos el StrTurno
'
Inicializamos el número de movimientos
Private Sub
UserControl_Initialize()
Dim IntI, IntJ As Integer
' Creamos
los nuevos botones
ButCelda(IntI).Visible = True
' Colocamos el botón izquierdo de la fila
correspondiente
ButCelda(3 * IntI).Top = ButCelda(3 * IntI).Top + IntI *
ButCelda(0).Height
' Colocamos los dos botones restantes de la
fila
ButCelda(3 * IntI + IntJ).Top =
ButCelda(3 * IntI).Top
ButCelda(3 * IntI + IntJ).Left =
ButCelda(3 * IntI + IntJ - 1).Left + ButCelda(3 * IntI + IntJ - 1).Width
'
Inicializamos el tablero
6. Acceso a bases de datos (DAO y Jet)
Introducción
En Visual Basic existen múltiples métodos de
acceso a bases de datos. En este capítulo, y en los dos siguientes, se pretende
familiarizar al lector con tres de los métodos más utilizados para acceso a
bases de datos, tanto locales como remotas. Utilizar uno u otro depende de
nuestras necesidades: unos métodos son más aptos para aplicaciones sencillas
sin conexión a bases de datos externas, mientras que otros aportan un mayor
rendimiento al ser utilizados contra servidores inteligentes como SQL Server.
Visual Basic proporciona también un conjunto
de controles enlazados a datos (como
los controles DBGrid, DBCombo, DBList, RemoteData...) que facilitan el acceso
y el procesamiento de la información almacenada en una base de datos. Dichos
controles no van a ser tratados en este manual, por dos razones: la primera
razón es que estos controles no nos permiten definir su funcionamiento para
controlar cómo acceden a la base de datos, lo que en la mayoría de las
ocasiones suele ser un requisito fundamental, y la segunda razón es que una vez
asimile el lector los tres capítulos que vamos a presentar, no debería tener
ningún problema en profundizar por sí mismo, si fuera necesario, en su
funcionamiento.
Este capítulo mostrará al lector en qué
consiste y cómo se utiliza el primero de los métodos de acceso a datos, DAO
(Data Access Objects), para acceder a bases de datos locales a través del motor
Jet. De forma paralela se pretende que el lector sea consciente de la ventaja
del uso de clases para la reutilización del código.
Conocimientos teóricos
DAO y Jet
El modelo DAO es una interfaz de programación que posibilita un
control total de la base de datos. Es decir, DAO facilita una colección de
clases de objetos que proporcionan las propiedades y los métodos que nos
permitirán llevar a cabo todas las operaciones necesarias para administrar una
base de datos: funciones para crear bases de datos, definir tablas, campos e
índices, establecer relaciones entre las tablas, desplazarse por la base de
datos y crear consultas sobre ella, etc.
El motor de base de datos Jet convierte
estas operaciones, definidas sobre objetos de acceso de datos, en operaciones
físicas que se efectúan directamente sobre los propios archivos de las bases de
datos y que controlan todos los mecanismos de interfaz con las distintas bases
de datos compatibles. Hay tres categorías de bases de datos que Visual Basic
reconoce a través de DAO y del motor Jet:
· Bases de datos nativas de Visual Basic: aquellas que
utilizan el mismo formato que Microsoft Access. El motor Jet crea y manipula
directamente estas bases de datos, que proporcionan la máxima flexibilidad y
velocidad.
· Bases de datos externas: las que utilizan el método de
acceso secuencial indexado (ISAM), como dBASE III, dBASE IV, Microsoft FoxPro,
Paradox, archivos de texto, hojas de cálculo de Microsoft Excel o Lotus, etc.
·
Bases de datos ODBC: bases
de datos cliente-servidor que cumplen el estándar ODBC. Aunque es posible
trabajar contra una base de datos utilizando Jet y ODBC, existen otros métodos
más recomendables que veremos en los ejemplos 7 y 8.
Estructura de una aplicación de bases de
datos en Visual basic con DAO y Jet
Una aplicación de bases de datos en Visual Basic que utiliza DAO
y Jet consta de tres partes, como se muestra en la siguiente figura:
El interfaz de
usuario es lo que el usuario ve y con lo que interactúa con la base de datos.
Como se ve en la figura, todo acceso a la base de datos se realiza a través del
motor de base de datos (Jet) ya que es aquí donde están los métodos y funciones
de acceso a base de datos.
El motor de base de
datos es un intermediario entre el interfaz de usuario y el almacén de datos,
lo que permite abstraer a la aplicación de la base de datos utilizada,
facilitando la reutilización del código.
El modelo
de objetos DAO
El modelo de objetos de acceso a datos es la interfaz orientada a
objetos del motor de base de datos Jet. Se trata de una jerarquía de clases que
corresponde a una visión lógica de un sistema de base de datos relacional (la
base de datos propiamente dicha, las tablas definidas en ella y sus campos,
índices, etc). Estas clases se utilizan para crear objetos de acceso a datos
que se defieren a la base de datos en particular que se desea manipular.
Las clases de los objetos
de acceso a datos se organizan en una jerarquía, en la que la mayoría de las
clases pertenecen a una clase de colección, que a su vez pertenece a otra clase
en la jerarquía. Aquí se muestra la jerarquía completa de DAO.
Programación
con DAO y Jet
En las siguientes
líneas vamos a comentar cómo programar una aplicación que acceda a una base de
datos Jet a través de DAO, explicando las acciones más comunes que deberemos
implementar. Existen muchas posibilidades que no comentaremos. Sin embargo, el
ejemplo propuesto es lo suficientemente completo como para servir a los
propósitos de la mayoría.
Si programamos con
Visual Basic 5.0, es necesario incluir en el proyecto una referencia a Microsoft
DAO 3.5 Object Library.
Como ejemplo vamos
a utilizar una base de datos creada en Access y guardada en 'c:\bd\MiBD.mdb'. La base de datos
contiene una sola tabla, MiTabla, cuya estructura es la siguiente:
Ahora veremos la
estructura de un programa en el que se mantiene esta base de datos.
·
Apertura de la base de datos:
El objeto DBEngine
contiene y controla
todos los otros objetos en la jerarquía de los objetos de DAO. Es único por
aplicación (no pueden crearse otros objetos DBEngine). Utilizaremos
el Workspace
(espacio de trabajo) por defecto de este objeto para abrir la base de datos,
especificando el path completo del
archivo:
Set VarBD = DBEngine.Workspaces(0).OpenDatabase(“c:\bd\MiBD.mdb”)
·
Utilización de Recordsets:
Un objeto Recordset representa los registros de una tabla o los registros que
resultan de ejecutar una consulta. Es análogo a los
cursores utilizados en otros lenguajes. Existen varios tipos, cada uno de los
cuales tiene sus ventajas e inconvenientes:
·
dbOpenTable: tiene acceso a la base de datos directamente, es decir, que
tiene acceso a la tabla directamente. Las búsquedas son más rápidas que con
otros tipos de Recordset, pero no permite uniones ni puede ser utilizado con
otros modelos de acceso a datos como RDO.
·
dbOpenSnapshot: contiene una copia fija de los datos tal y
como existen en el momento en el que se accedió. Consume menos recursos que
otros Recordset (dbOpenTable, dbOpenDynaset), por lo
que puede ejecutar consultas y devolver datos más rápidamente, sobre todo si
utiliza ODBC. No es actualizable, por lo que no recoge los cambios que puedan
realizarse en la base de datos mientras se utiliza, es óptimo para usos de
lectura de datos.
·
dbOpenDynaset: es un conjunto de referencias a los registros de una o más tablas. Los
cambios realizados en la base de datos son automáticamente actualizados en un Recordset
de este tipo. Sin embargo, las búsquedas son más lentas ya que las acciones que
se hacen no solo se ejecutan en la memoria, si no que se reflejan a la vez en
la base de datos.
·
dbOpenForwardOnly: es un tipo de Snapshot en el que sólo se permite
el desplazamiento hacia delante sobre el conjunto de resultados obtenido en una
consulta. Es el Recordset que menos funcionalidad proporciona, con la ventaja
de que es el más rápido.
En el siguiente
cuadro se recogen los tipos de Recordset que deben utilizarse para
realizar las acciones más comunes, de forma que se maximice en la medida de lo
posible el rendimiento de la aplicación:
Siempre hay que cerrar los Recordsets
abiertos una vez dejen de ser útiles utilizando el método Close. No cerrar un Recordset
abierto puede suponer estar trabajando con datos no actualizados, no poder
abrir o cerrar transacciones, etc.
En las siguientes
líneas se exponen varios ejemplos de uso de Recordsets utilizando en
muchos casos sentencias específicas
de DAO para acceder a bases de datos Jet. Este tipo de sentencias están
pensadas para el acceso a bases de datos locales a través del motor Jet, y no deben ser utilizadas para acceder a
servidores de bases de datos no localizados en el cliente (es más, en ocasiones
no pueden ser utilizadas fuera de este contexto). Su utilización en este tipo
de entornos puede suponer una importante bajada en el rendimiento de la
aplicación, siendo preferible el uso de DAO con ODBCDirect o RDO, como veremos
en los siguientes ejemplos.
Ejemplos de utilización de Recordsets:
Para insertar un
registro nuevo (DNI=”12345678”,
Nombre=”Anastasio”, Apellidos=”Pérez Martín”) en la tabla 'MiTabla':
Dim RsetTabla as Recordset
Set RsetTabla = VarBD.OpenRecordset(“MiTabla”,dbOpenTable)
!Apellidos
= “Pérez Martín”
Para contar el
número de elementos en la tabla MiTabla utilizaremos la propiedad RecordCount
del Recordset:
Dim RsetTabla as Recordset
Dim NumElementos as integer
Set RsetTabla = VarBD.OpenRecordset(“MiTabla”,dbOpenTable)
NumElementos =
RsetTabla.RecordCount
Para mostrar en la pantalla Debug
los datos de cada registro de la tabla “MiTabla” utilizaremos el método MoveNext
del Recordset,
que obtiene el siguiente registro del conjunto. Accederemos a los campos del
registro utilizando la sintaxis Recordset_Abierto!Nombre_del_campo.
La propiedad EOF del Recordset nos indicará cuando se ha
llegado al final del conjunto de registros:
Dim RsetTabla as Recordset
Set RsetTabla = VarBD.OpenRecordset(“MiTabla”,dbOpenForwardOnly)
Debug.Print
“DNI: ” & !DNI & vbCRLF & _
“Nombre: “ & !Nombre & vbCRLF & _
“Apellidos:
“ & !Apellidos
Para buscar los
datos del registro con DNI=”12345678”
utilizaremos el método FindFirst del Recordset, que obtiene el
primer registro del conjunto de
registros del Recordset que cumple una cierta condición, una vez encontrado
el registro mostraremos los datos en el debug.
Utilizaremos la propiedad NoMatch para saber si se ha
encontrado el registro buscado, ya que será true
si se ha llegado al final del recorset sin encontrar un regostro con los datos
especificados:
Dim RsetTabla as Recordset
Set RsetTabla = VarBD.OpenRecordset(“MiTabla”,dbOpenSnapshot)
.FindFirst
“DNI=12345678”
Debug.Print “Registro no encontrado”
Debug.Print
“DNI: ” & !DNI & vbCRLF & _
“Nombre: “ & !Nombre & vbCRLF & _
“Apellidos:
“ & !Apellidos
Para borrar el
registro con DNI=”12345678”
utilizaremos el método FindFirst y el método Delete
del Recordset,
que elimina el registro actual del conjunto de registros:
Dim RsetTabla as Recordset
Set RsetTabla = VarBD.OpenRecordset(“MiTabla”,dbOpenSnapshot)
.FindFirst
“DNI=12345678”
If Not .NoMatch Then
.Delete
Hemos visto los
métodos de acceso a registros, ahora veremos los métodos de movimiento dentro
del recodset, el uso es similar a los de acceso a los registros, por eso sólo
los enunciaremos:
MoveFistr: El cursor se sitúa en
el primer registro.
MoveLast: El registro en el que
nos situamos es el último del recordset.
MoveNext: Nos vamos al siguiente
registro al que nos encontremos.
MovePrevious: Retrocedemos un
registro en el recordset.
Move #n: Avanzamos n registros a
partir de la posición actual.
Es importante
controlar la situación en la que nos encontremos en el recordset a la hora de
desplazarnos, ya que el acceso a una posición que no esté dentro del recordset
produciría un error en tiempo de ejecución.
Ejemplo propuesto
Objetivo
El objetivo de este
ejemplo es ampliar el ejemplo de la agenda de forma que los datos estén ahora
almacenados en una base de datos.
La aplicación se
diseñará en tres capas:
·
Interfaz de usuario (FrmAgenda): capa que
permite al usuario interactuar con el programa (idéntico al utilizado en la
agenda que ya tenemos).
·
Clase agenda (ClaAgenda): capa intermedia
entre el interfaz de usuario y la clase de acceso a la base de datos.
·
Clase de acceso a la base de datos (ClaDAO_Jet): capa que se comunica con el
motor de base de datos. El objetivo es que esta capa sea lo suficientemente
independiente del tipo de acceso a la base de datos elegido (DAO con Jet, DAO
con ODBCDirect o RDO) como para permitir que sea sustituido por otro sin
cambiar la capa superior.
Cada capa sólo podrá relacionarse con su capa inmediata superior
e inferior, como muestra el dibujo:
El objetivo es
aislar cada capa de la implementación de las demás, de forma que las capas
inferiores puedan ser cambiadas sin que las modificaciones repercutan en las
capas superiores. En particular, el objetivo que se pretende en este ejemplo y los dos
siguientes es crear una capa de acceso a la base de datos diferente en cada
ejemplo, pero utilizando el mismo interfaz de usuario y la misma agenda.
Desarrollo
del ejemplo
Crearemos la base
de datos para la agenda (bdAgenda.mdb) en Access, que contendrá una única tabla
Agenda cuya arquitectura se muestra
en la tabla siguiente:
El lector utilizará
los siguientes elementos:
·
ClaFicha: ya definida en anteriores ejemplos.
·
ModDefiniciones: módulo con las definiciones
utilizadas en la aplicación.
·
FrmAgenda: formulario correspondiente a la
capa de interfaz.
Desarrollaremos la
clase correspondiente a la capa de agenda (ClaAgenda). Contendrá un objeto
privado ObjAccesoBD, de tipo ClaDAO_Jet, a través del cual podrá interactuar
con la base de datos. Esta clase proporcionará los siguientes métodos:
·
Public Property Get IntNumFichas() As Integer:
Método para obtener el número de fichas de la agenda. Llamará al método
NumFichas del objeto ObjAccesoBd.
·
Public Function CrearFicha(ByVal StrNombre As String, ByVal
StrApellidos As String, ByVal StrDireccion As String, ByVal StrTelefono As
String) As Integer: Método para crear una ficha nueva
en la agenda. Creará un objeto de la clase ClaFicha y se lo pasará al método
GuardarFicha del objeto ObjAccesoBD.
·
Public Function BorrarFicha(ByVal IntNumFicha As Integer): Método para borrar una ficha de la agenda. Utilizará el método
BorrarFicha del objeto ObjAccesoBD.
·
Public Function Ficha(Optional ByVal IntIndice As Integer, Optional
ByVal IntNumFicha As Integer) As ClaFicha: Método para
obtener una ficha específica de la agenda, ya sea por índice o por número de
ficha. Utilizará el método ObtenerFicha del objeto ObjAccesoBD.
·
Private Sub Class_Terminate(): Al eliminar la
agenda es necesario cerrar la base de datos y eliminar el objeto ObjAccesoBD.
Desarrollaremos
también la clase correspondiente a la capa de acceso a la base de datos, que
utilizará DAO y Jet (ClaDAO_Jet). Esta capa proporcionará los siguientes
métodos:
·
Public Property Get NumFichas() As Integer: Método
para obtener el número de fichas de la tabla Agenda.
·
Public Function AbrirAgenda() As CodigoRetorno:
Método para abrir la base de datos.
·
Public Function CerrarAgenda() As CodigoRetorno: Método para cerrar la base de datos.