Professional Documents
Culture Documents
CONTENIDO
Conectar una base de datos con Visual Basic 6.0. - 8.3.1 El Modelo de datos ODBC (Open Database Connectivity). - 8.3.2 El modelo de objetos DAO (Data Access Object). - 8.3.3 El modelo de objetos RDO (Remote Data Object). - 8.3.4 El modelo de objetos ODBCDirect. - 8.3.5 El modelo de objetos OLE DB. - 8.3.6 El modelo de objetos ADO (ActiveX Data Object). reparando la conexin a la base de datos. - 8.4.1 Objeto Recordset. - 8.4.1.1 Propiedades del objeto Recordset. - 8.4.1.2 Manejo de cursores. - 8.4.1.3 Control de concurrencia. - 8.4.1.4 Lectura de los campos de un Recordset. - 8.4.1.5 Moverse por los registros de un Recordset. - 8.4.1.6 Modificacin de registros en un Recordset. - 8.4.1.7 Eliminar el registro activo del Recordset. - 8.4.1.8 Insercin de registros en el Recordset. - 8.4.1.8 Establecer y leer la posicin de un registro en el Recordset. - 8.4.1.9 Ordenacin de los registros de un Recordset. - 8.4.1.10 Bsqueda de registros. - 8.4.1.11 Verificar el estado del Recordset. - 8.4.2 Eventos del objeto Recordset. - 8.4.2.1 Sucesos de recuperacin de datos. - 8.4.2.2 Sucesos de navegacin. - 8.4.2.3 Sucesos de modificacin de datos. - 8.4.3 Generacin de reportes. Ejercicios propuestos.
CONECTAR UNA BASE DE DATOS CON VISUAL BASIC 6.0 Existen varias formas para conectar una base de datos ha una aplicacin de Visual Basic, entre las cuales cabe mencionar la utilizacin de tecnologas ODBC, DAO, RDO, ODBCDirect, OLE DB y ADO. Aunque en este libro me centrar en la tecnologa ADO (ActiveX Data Object) explicar brevemente cada una de las interfaces de conexin a bases de datos mencionadas anteriormente, debido a que cada una de estas estn ntimamente relacionadas. - 8.3.1 El modelo de datos ODBC (Open Database Connectivity) ODBC son las siglas de Conectividad de bases de datos abiertas (Open Database Connectivity) y es un conjunto de funciones que le permitir conectarse a una base de datos local o remota. Es una tecnologa que permite acceder a distintas bases de datos en diferentes formatos como Microsoft Visual FoxPro, Microsoft Access, Microsoft SQL Server, dBASE, Oracle y archivos de texto separados por comas. La mquina sobre la que se ejecuta la aplicacin se conecta en un DLL denominado ODBC Driver Manger (Gestor del controlador ODBC) que, a su vez es el encargado de mandar y recibir los datos a un controlador ODBC especfico para la base de datos particular que desee utilizar. Existen ciento de controladores ODBC para las distintas bases de datos actuales del mercado, incluso para bases de datos descontinuadas. El objetivo de ODBC es proporcionar una interfaz comn para todas las bases de datos existentes. Tericamente, podr preparar una aplicacin que utilice OBDC para hablar con una base de datos de Access y, posteriormente, adaptar el programa para una base de datos SQL Server cambiando simplemente el controlador ODBC e introduciendo unas pocas instrucciones en el cdigo fuente. ODBC tiene muchas ventajas en comparacin con otros mtodos de acceso a bases de datos, sin embargo, utilizar ODBC no resulta muy sencillo, especialmente para los programadores de Visual Basic. Trabajar con ODBC implicar manejar los conceptos API de Windows que son bastante complejos y si comete un error se suele interrumpir la ejecucin de la aplicacin con un error fatal. Por este motivo, son pocos los programadores de Visual Basic que escriben aplicaciones que llamen directamente a las funciones ODBC. Increblemente, la mayora de las otras tcnicas de acceso a datos disponibles para Visual Basic pueden utilizar controladores ODBC como capas intermedias por lo que, en ocasiones, podr potenciar las otras tcnicas con llamadas directas a los API, principalmente con aquellas basadas en RDO. Por desgracia, no podr mezclar cdigo ODBC API con ADO aun que este utiliza internamente un controlador ODBC. - 8.3.2 El modelo de objetos DAO (Data Access Object) DAO (Objeto de acceso a datos) es una tcnica de acceso a base de datos de Microsoft Access basado en el motor Microsoft Jet que es el que propulsa a Access. Los diseadores pueden disear una base de datos MDB utilizando Access y, posteriormente, utilizar DAO desde una aplicacin de Visual Basic para abrir la base de datos, agregar y recuperar registros y gestionar transacciones. Aunque DAO se diseo pensando en Access, este no limita la conexin a otras bases de datos para la que exista un controlador ODBC. Una de las desventajas de DAO es que aunque no utilice bases de datos de Access tendr que cargar completamente el motor Microsoft Jet y distribuirlo en sus aplicaciones. Otra importante desventaja es que, DAO no cuenta con muchas de las funciones que podra utilizar si trabaja directamente con funciones ODBC API. Por ejemplo, no podr realizar consultas asncronas o conexiones utilizando DAO, ni tampoco podr trabajar con conjuntos mltiples de resultados. DAO fue una de las herramientas de acceso a datos para los primeros programadores de Visual Basic 3. Actualmente este mtodo de acceso no es utilizado por los programadores de Visual Basic 6.0, debido a que su sucesor ADO es mucha ms potente que DAO y es el objeto de estudio y desarrollo de la Microsoft para acceso a datos.
Captulo 8
- 8.3.3 El modelo de objetos RDO (Remote Data Object) RDO (Objetos de Datos Remotos) es el primer intento que realiz Microsoft para combinar la sencillez del DAO con la potencia de la programacin directa del ODBC API. RDO es un modelo de objetos vagamente diseado a partir de DAO, pero deshecha el Jet Engine y el DAO DLL y trabaja directamente con los controladores ODBC subyacentes. Las aplicaciones basadas en RDO cargan slo un pequeo DLL en lugar del motor Jet que consuma gran cantidad de recursos. Lo ms importante es que RDO fue especficamente diseado para trabajar con los orgenes ODBC, por lo que cuenta con funciones que no pueden ser utilizadas desde DAO. Sin embargo, RDO es una tecnologa de 32 bits, por lo que no podr utilizarla en las versiones 2 y 3 de Visual Basic. RDO fue implementado por primera vez en la versin 4 de Visual Basic y, posteriormente fue mejorado en la versin 5. - 8.3.4 El modelo de objetos ODBCDirect Adems del RDO mejorado que inclua Visual Basic 5, este inclua otra tecnologa de acceso a datos, denominada ODBCDirect, que permita a los programadores emplear RDO utilizando una sintaxis DAO. ODBCDirect fue concebido como una tcnica de transicin que ayudara a los programadores en Visual Basic a transformar sus aplicaciones DAO/Jet a arquitecturas cliente/servidor de mayor potencia. ODBCDirect no es una tecnologa propiamente dicha. Es nicamente un conjunto de trucos que puede utilizar para ahorrarse tiempo durante la conversin de aplicaciones. - 8.3.5 El modelo de objetos OLE DB OLE DB es una tecnologa de acceso a datos de bajo nivel con la que Microsoft pretende sustituir a ODBC como el medio principal de conexin con bases de datos. Aunque OLE DB es una nueva tecnologa, podr encontrar proveedores de OLE DB para las bases de datos ms populares, y otras sern comercializadas rpidamente. A pesar de sus aparentes similitudes, las tecnologas ODBC y OLE DB son profundamente distintas. En primer lugar, OLE DB esta basada en COM, que es una arquitectura suficientemente robusta diseada para mover grandes cantidades de datos por la red. En segundo lugar, OLE DB pretende realizar la tarea de conectar a cualquier fuente de datos, no slo las bases de datos relacionales e ISAM (modo de acceso secuencial indexado), sino, que forma parte de la estrategia denominada Acceso universal de datos (UDA) de Microsoft, que le permitir leer y procesar los datos all donde se encuentren, sin necesidad de convertirlos en primer lugar y de tener que importarlos a una base de datos ms tradicional. Utilizando los proveedores OLED DB, podr procesar los datos contenidos en los mensajes de correo electrnico, pginas HTML, hojas de clculo y documentos de texto, entre otras fuentes de datos. - 8.3.6 El modelo de objetos ADO (ActiveX Data Object) ADO es una interfaz de alto nivel con OLE DB. Al igual que los API de ODBC, OLE DB es una interfaz de bajo nivel a la que no se puede acceder con facilidad utilizando lenguajes de alto nivel como Visual Basic. ADO est construido sobre un OLE DB para proporcionar funciones que no se encuentran disponibles directamente en OLE DB o que exigiran profundos conocimientos y habilidades de codificacin a los programadores. El modelo de objetos ADO es considerablemente menos complicada que los modelos DAO y RDO. ADO contiene mucho menos objetos y colecciones que DAO y RDO, pero los elementos que contiene son, con frecuencia, ms complejos que sus equivalentes DAO y RDO porque dispone muchos ms mtodos y propiedades. Las principales ventajas de ADO son su facilidad de uso, su gran velocidad, su escasa utilizacin de memoria y el poco espacio que ocupa en disco. ADO proporciona un acceso a los datos constante y de alto rendimiento para crear un cliente de base de datos para el usuario o un objeto empresarial del nivel medio con una aplicacin, una herramienta, un lenguaje o un explorador.
Captulo 8
REPARANDO LA CONEXIN A LA BASE DE DATOS Al principio de este captulo mencione que la principal herramienta tecnolgica que utilizaramos en este libro para conectar a una base de datos sera ADO. Esto es, porque considero que es la herramienta ms completa y potente de la que dispone Visual Basic para conectar a un origen de datos. El modelo de datos ADO dispone de un objeto llamado CONNECTION que permite establecer una conexin a un origen de datos que puede ser una base de datos, un origen ODBC o cualquier otro origen que disponga un proveedor OLE. El objeto Connection le permitir especificar todos los parmetros necesarios antes de abrir una base de datos. Cmo establecer la conexin Para establecer la conexin el objeto Connection dispone del mtodo Open, seguida de los parmetros necesarios para la conexin. Su sintaxis es: Open [ConnectionString], [UserID], [Password], [Options] El primer argumento ConnectionString representa una serie de parmetros necesarios para establecer la conexin a la base de datos. Estos parmetros son por ejemplo, el proveedor ODBC, el nombre de la base de datos, el nombre de usuario, la contrasea y el nombre del servidor. Ejemplo 1: Dim cn As New ADODB.Connection Linea necesaria para establecer la conexin.
cn.Open Provider=Microsoft.Jet.OLEDB.4.0; & Data Source=c:\clientes.mdb El cdigo del ejemplo anterior abre una base de datos llamada clientes.mdb que se encuentra en el disco local C, mediante el proveedor ODBC Microsoft.Jet.OLEDB.4.0 que permite conectar a bases de datos de Access 2000 y Access 97 sin ningn problema. Ejemplo 2: Dim cn As New ADODB.Connection cn.Open Provider=SQLOLEDB; & Server=ServerNT; & User ID=jose;Password=abc; _ & Data Source=empleados El cdigo del ejemplo anterior abre una base de datos llamada empleados que se encuentra en un servidor denominado ServerNT. Tambin especificamos el nombre y la contrasea de usuario. Los argumentos UserID y Password son opcionales. Estos no deben especificarse si lo escribe directamente en el argumento ConnectionString, tal y como lo vemos en el segundo ejemplo. El argumento opcional Options determina si este mtodo debe volver despus o antes de que la conexin se establezca. Este argumento puede tomar los valores -1-adConnectUnspecified (Valor por defecto), que abre una conexin sncrona con la base de datos y el valor 16-adAsyncConnect abre una conexin asncrona con la base de datos. Una conexin sncrona indica que Visual Basic no ejecutar la instruccin que sigue al mtodo Open hasta que se establezca la conexin, en consecuencia la aplicacin no responder a los eventos del usuario. Por otro lado, una conexin asncrona, permite que el usuario trabaje con el programa aun la conexin no se halla establecido. Y lo mejor de todo, permite informar el estado en que se encuentra la conexin. Este tipo de conexin ser el caso de estudio en otra seccin de este mismo capitulo, mientras tanto, trabajaremos con conexiones sncrona.
Captulo 8
Este son algunos de los argumentos que podr utilizar en el atributo ConnectionString al momento de abrir una conexin. Argumento Data Source Descripcin El nombre del servidor SQL o el nombre de la base de datos MDB a la que se desee conectar. Cuando se conecte a un origen ODBC, este argumento puede ser tambin el nombre de un origen de datos (DSN). Nombre del origen ODBC registrado en la mquina actual; este argumento puede sustituir al argumento Data Source. Archivo que contiene informacin sobre la conexin; este argumento puede ser un archivo ODBC DSN o un archivo Microsoft Data Link (UDL). Nombre de la base de datos predeterminada sobre la conexin. Cuando se conecte a un origen de datos ODBC, tambin podr utilizar el argumento Database. Contrasea del usuario. Cuando se conecte a un origen ODBC, podr utilizar el argumento PWD. No tendr que pasar su ID y contrasea de usuario si se est conectando a un servidor SQL y utiliza seguridad integrada. True si ADO almacena el ID y la contrasea de usuario en el vnculo de datos. Nombre de un proveedor OLE; el valor predeterminado es MSDASQL, el proveedor para orgenes ODBC. Nombre del usuario. Cuando se conecte a un origen ODBC, podr utilizar en su lugar el argumento UID.
DSN
FileName
Initial Catalog
Password
Provider
User ID
Hasta ahora usted esta en la capacidad de realizar una conexin a una base de datos y nada ms. Necesita ahora conocer la forma en que puede obtener los datos que se encuentran en la base de datos y mostrarlos en cajas de texto, en un DataGrid o en otro control en la que se puede mostrar datos. En la siguiente seccin veremos la forma de hacer esto y al finalizarla crearemos nuestra primera aplicacin de base de datos. - 8.4.1 Objeto Recordset Adems del objeto Connection, ADO posee un objeto denominado Recordset, que contiene todos los datos que se leer de una base de datos o que enviar a la misma. Un Recordset puede incluir varias filas y columnas de datos. Cada fila es un registro y cada columna es un campo del registro. Slo podr acceder simultneamente a una fila, la denominada fila actual o registro actual. Podr examinar un Recordset modificando el registro actual. - 8.4.1.1 Propiedades del objeto Recordset Posiblemente la propiedad ms importante del objeto Recordset es la propiedad Source que contiene el nombre de la tabla, el nombre del procedimiento almacenado o el texto de una consulta SQL utilizada para llenar el Recordset. Para cargar un Recordset usted debe realizar tres pasos que son realmente necesarios, el primero,
Abrimos la base de datos ventas.mdb. cn.Open Provider=Microsoft.Jet.OLEDB.4.0; & Data Source=c:\ventas.mdb rs.Source = clientes Especificamos la fuente de datos. En este caso la tabla clientes.
rs.Open select * from clientes, cn Abrimos el Recordset y lo llenamos con la consulta SQL. En este ejemplo creamos el objeto Connection y luego creamos el objeto Recordset. Luego abrimos la base de datos utilizando el mtodo Open del objeto Connection, tal y como vimos anteriormente. En la cuarta lnea de cdigo especificamos el origen de los datos, que en este caso es la tabla clientes de la base de datos ventas.mdb. Por ltimo, utilizamos el mtodo Open del objeto Recordset para abrirlo. El texto select * from clientes es una consulta SQL que permite seleccionar todos los registros de la tabla. En este caso el Recordset se llenar con todos los registros de la tabla clientes. Ejemplo 2: (Otra forma de abrir el Recordset) Dim cn As New ADODB.Connection Dim rs As New ADODB.Recordset Creamos el objeto Connection. Creamos el objeto Recordset.
Abrimos la base de datos ventas.mdb. cn.Open Provider=Microsoft.Jet.OLEDB.4.0; & Data Source=c:\ventas.mdb rs.Source = clientes rs.ActiveConnection = cn Especificamos la fuente de datos. En este caso la tabla clientes. Activa la conexin asocindolo a la conexin existente.
rs.Open select * from clientes Abrimos el Recordset y lo llenamos con la consulta SQL. En este ejemplo se omite el parmetro de conexin en el mtodo Open por la propiedad ActiveConnection del objeto Recordset. Ejemplo 3: (Otra forma es abrir la base de datos de forma implcita) Dim rs As New ADODB.Recordset Creamos el objeto Recordset.
Activamos y especificamos lo parmetros de la conexin en el objeto Recordset. rs.ActiveConnection = Provider=Microsoft.Jet.OLEDB.4.0; & Data Source=c:\ventas.mdb rs.Source = clientes Especificamos la fuente de datos. En este caso la tabla clientes. rs.Open select * from clientes Abrimos el Recordset y lo llenamos con la consulta SQL.
410
411
El cursor dinmico es muy parecido a cursor de conjunto de claves. La diferencia est en que cada vez que el cliente solicita otro conjunto de registros el conjunto de claves se vuelve a crear antes de devolver estos registros. Esto provoca que si abrimos un conjunto de registros dinmico y contamos el nmero de registros, luego nos desplazamos secuencialmente hasta el final y volvemos a contar el nmero de registros, no tiene por que ser el mismo ya que otros usuarios pueden haber insertado registros. No resulta sorprendente que estos cursores sean los ms exigentes desde el punto de vista del rendimiento y del trfico en la LAN porque, cada vez que se desplaza a otro registro, se necesita realizar un viaje hasta el servidor para recuperar los valores actuales. Este tipo de cursor slo se encuentra disponible del lado del servidor. Prueba de rendimiento A continuacin, se muestra una prueba de rendimiento encontrada en la Web donde se puede apreciar la potencia de cada tipo de cursor: La prueba se hizo en una maquina con las siguientes caractersticas: Pentium 4 a 2,8 GHz con 512 Mb RAM Windows 2000 PRO + SP 4 SQL Server 2000 + SP 3 VB 6.0 + SP 5 MDAC 2.7
La consulta fue la siguiente: SELECT * FROM Historico (selecciona todos los registros de la tabla Historico de 22 columnas con clave principal INT IDENTITY, con 567.430 registros). Al abrir el RecordSet y leerlo hasta el final utilizando cada cursor obtuvimos los siguientes datos: Tipo de cursor ForwardOnly Static KeySet Dynamic Static (extremo cliente) Duracin de la consulta (en segundos) 6,02 65,48 90,13 89,84 39,00
412
'Abrimos la base de datos "agenda.mdb". cn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & "Data Source=c:\agenda.mdb" rs.Source = "contactos" 'Especificamos la fuente de datos. En este caso la tabla "contactos". rs.CursorType = adOpenKeyset 'Definimos el tipo de cursor. rs.LockType = adLockOptimistic 'Definimos el tipo de bloqueo. rs.Open "select * from contactos", cn 'Abrimos el Recordset y lo llenamos con una consulta SQL.
413
List1.AddItem rs.Fields(0) & " " & rs.Fields(1) & " " & rs.Fields(2) & " " & rs.Fields(3) rs.MoveNext Loop 'Nos movemos al siguiente registro.
414
Haga clic en Base de datos en blanco. Aparecer una ventana solicitando el nombre de la base de datos. El nombre por defecto es bd1, borre ese nombre y escriba c:\agenda. Escribimos c:\ para que la base de datos se almacene en el disco local c. Aparecer una ventana con tres opciones: Crear una tabla en vista de diseo, Crear una tabla utilizando el asistente y Crear una tabla introduciendo datos. De estas tres opciones las que nos interesa es la primera. Haga clic sobre la primera opcin Crear una tabla en vista de diseo.
415
En la primera columna escriba: Nombre del campo Nombre Apellido Telefono Direccion Correo En la segunda columna seleccione los siguientes tipos de datos: Tipo de datos Texto Texto Texto Texto Texto Algunas versiones de Access agregan automticamente el tipo de dato Texto a los campos agregados a la tabla. Si la versin de Access agrega el tipo de datos Texto no tendr que especificarlo. Si no esta familiarizado con lo que estamos haciendo les recomiendo un curso bsico de base de datos. De todos modos les explico: estamos creando la tabla de la base de datos especificando los campos y el tipo de datos para cada campo.
416
Borre el texto Tabla1 y escriba Contactos. Luego haga clic en el botn Aceptar. Access les preguntar si desea crear una clave principal. Conteste que No. La tabla se agregar en la ventana de objetos:
Haga doble clic en la tabla Contactos. Access les mostrar la tabla para que usted agregue registros a la misma. En nuestro caso agregaremos cuatro registros a la tabla para que pueda ver cmo podemos visualizarlos y modificarlos desde Visual Basic. No piense que utilizaremos Access para agregar informacin a la tabla, esto lo haremos desde nuestra aplicacin en Visual Basic. En nuestro caso hemos agregado informacin (registros) para que observe como se puede navegar por la base de datos desde una aplicacin en Visual Basic. Complete la tabla con los siguientes registros:
417
Busque la referencia ActiveX Data Objects 6.0 Library y seleccinela. A continuacin, haga clic en el botn OK. Esto que estamos haciendo es necesario para poder utilizar ADO desde nuestra aplicacin. Si no tiene la versin ActiveX Data Objects 6.0 Library seleccione la versin ms actual o la mayor. Inserte una ListBox y un botn de comando en el formulario:
418
rs.Open "select * from contactos", cn 'Abrimos el Recordset y lo llenamos con una consulta SQL.
'Nos posicionamos en el primer registro del Recordset. 'Repite hasta que se lea todo el Recordset.
List1.AddItem rs.Fields("nombre") & " " & rs.Fields("apellido") & " " & rs.Fields("telefono") & " " _ & rs.Fields("direccion") & " " & rs.Fields("correo") rs.MoveNext Loop Corra la aplicacin. Haga clic en el botn Mostrar registros. Si todo esta bien, se mostrarn en la ListBox todos los registros que se agregaron en Access. Detenga la aplicacin y gurdela con los nombres FormEjercicio1-8 para el formulario y Ejercicio1-8 para el proyecto. - 8.4.1.5 Moverse por los registros de un Recordset Una forma ms elegante y profesional para visualizar los registros de un Recordset es moverse por el Recordset cada vez que sea necesario, es decir, avanzar o retroceder uno o varios registros. Para tal fin, el objeto Recordset dispone de los mtodos MoveFirst (mueve al primer registro), MovePrevious (mueve al registro anterior), MoveNext (mueve al siguiente registro) y MoveLast (mueve al ltimo registro). Para que pueda entender el correcto funcionamiento de estos mtodos crearemos una aplicacin que contendr cuatro botones de comando que permitirn desplazarse por cada uno de los registros del Recordset. Para crear nuestra segunda aplicacin haga lo siguiente: Abra un nuevo proyecto. Haga clic en el men Project (Proyecto) y ejecute la opcin References (Referencias). En el cuadro que aparece busque la referencia ActiveX Data Objects 6.0 Library y seleccinela. A continuacin, haga clic en el botn OK. Si no tiene la versin ActiveX Data Objects 6.0 Library seleccione la versin ms actual o la mayor. Inserte cuatro etiquetas y al lado de cada etiqueta una caja de texto. Tambin, inserte cinco botones de comando en la parte inferior del formulario. Tal y como se muestra en la imagen de la siguiente pgina 'Nos movemos al siguiente registro.
419
Establezca los valores necesarios para que los controles tengan la misma apariencia que la imagen anterior. Haga doble en cualquier parte del formulario y en la seccin general (no en el evento Load) escriba: Dim cn As New ADODB.Connection Private WithEvents rs As ADODB.Recordset Dentro del evento Load del formulario escriba: Set rs = New ADODB.Recordset 'Activamos el Recordset. 'Creamos el objeto Connection. 'Creamos el Recordset con soporte de eventos.
'Abrimos la base de datos "agenda.mdb" cn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & "Data Source=c:\agenda.mdb" 'Especificamos la fuente de datos. En este caso la tabla "contactos". rs.Source = "contactos" rs.CursorType = adOpenKeyset 'Definimos el tipo de cursor. rs.LockType = adLockOptimistic 'Definimos el tipo de bloqueo. 'Abrimos el Recordset y lo llenamos con una consulta SQL. rs.Open "select * from contactos", cn rs.MoveFirst 'Nos movemos al principio del Recordset.
'Cargamos los datos en las cajas de texto. Text1.Text = rs.Fields("Nombre") 'Ponemos el nombre del registro actual. Text2.Text = rs.Fields("Apellido") 'Ponemos el apellido del registro actual. Text3.Text = rs.Fields("Telefono") 'Ponemos el telfono del registro actual. Text4.Text = rs.Fields("Direccion") 'Ponemos la direccin del registro actual. Text5.Text = rs.Fields("Correo") 'Ponemos el correo del registro actual.
420
Ahora vamos a escribir el evento que ocurre cada vez que el usuario se mueve por el Recordset. Este es el evento MoveComplete (Movimiento completo). Este evento nos permite determinar si el usuario se movi a otro registro del Recordset para luego mostrarlo al usuario, ya sea en cajas de textos, en un grid o en un control Listview. Para crear nuestro evento MoveComplete haga doble clic en cualquier parte del formulario para cargar el editor de cdigo. Luego desplcese al final del editor de cdigo y haga clic en la zona blanca para colocar el punto de insercin o indicador y escriba: Private Sub rs_MoveComplete(ByVal adReason As ADODB.EventReasonEnum, ByVal pError As ADODB.Error, adStatus As ADODB.EventStatusEnum, ByVal pRecordset As ADODB.Recordset) 'Si estamos antes del principio del Recordset. If rs.BOF = True Then rs.MoveFirst 'Movemos al principio del Recordset. 'Si estamos despus del ultimo elemento del Recordset. ElseIf rs.EOF = True Then rs.MoveLast 'Movemos al final del Recordset.
421
- 8.4.1.6 Modificacin de registros en un Recordset La modificacin de un registro consiste en la actualizacin de uno o ms campos del registro que se encuentra cargado en el Recordset. Esto es posible mediante el mtodo Update del objeto Recordset que permite la actualizacin simultanea de varios registro, utilizando la siguiente sintaxis: Update [Campos], [Valores] Campos: Este argumento es un tipo Variant que contiene el nombre de un campo, un ndice de campo o un array de nombres de campo o ndices. Valores: Este argumento es un tipo Variant que contiene un valor nico o un array de valores. En este se deben especificar los nuevos valores que sern establecidos en el argumento Campos. Ambos argumentos son opcionales, pero solo uno de ellos podr omitir. Si incluye ambos argumentos deber especificar los mismos nmeros de campos y valores en ambos argumentos. Por ejemplo, si va a modificar dos campos uno llamado Nombre y otro Apellido deber especificar los valores que tendrn ambos campos en el argumento Valores. El siguiente ejemplo muestra la forma en que usted puede actualizar varios campos utilizando la siguiente sintaxis: Actualizar cinco campos en una sola operacin. rs.Update Array(Nombre, Apellido, Telefono, Direccion, Correo), _ Array(Carlos, Rodrguez, 809-598-3696, Calle #6, Alma Rosa, carlos@gmail.com) rs.move 0 Hace que el registro se actualice. Al utilizar el mtodo Update el registro no se modifica inmediatamente hasta que se desplace a otro registro del Recordset o hasta que se emplee un mtodo como el utilizado en el ejemplo anterior. ADO dispone del mtodo CancelUpdate para cancelar una operacin de actualizacin que se halla realizado sobre un registro y dejarlo como estaba al principio. Si utiliza conjuntamente el mtodo Update con el mtodo CancelUpdate podr ofrecer al usuario la posibilidad de confirmar o cancelar las modificaciones realizadas sobre el registro activo: Si el registro aun no se ha modificado. If rs.EditMode = adEditInProgress Then
422
Practiquemos como actualizar un registro de nuestra base de datos agregando un botn de comando a nuestra aplicacin anterior. Escribe en la propiedad Caption el texto Actualizar. Dentro del evento Click del nuevo botn escriba: rs.Update Array("nombre", "apellido", "telefono", "direccion", "correo"), _ Array(Text1.Text, Text2.Text, Text3.Text, Text4.Text, Text5.Text) rs.Update 'Actualizamos el registro. 'Verificamos si no ocurri ningn problema. If rs.State = 1 Or rs.State = 0 Then MsgBox ("El registro se ha actualizado con xito.") Else MsgBox ("Ha ocurrido un error al actualizar el registro.") End If Ahora podr modificar los datos de cada campo y luego actualizarlo haciendo clic sobre el botn Actualizar. - 8.4.1.7 Eliminar el registro activo del Recordset Podr eliminar el registro actual cargado en el Recordset utilizando el mtodo Delete mediante la siguiente sintaxis: rs.Delete [AffectRecords] AffectRecords (Registros afectados) es opcional, si este se omite se borrar el registro actual. El argumento AffectRecords es utilizando cuando queremos borrar varios registros que resultan de una consulta realizada sobre la base de datos. Para borra el registro actual escriba: rs.Delete rs.MoveNext Nos movemos al siguiente registro para no provocar un error. If rs.EOF Then rs.MoveLast Si es el fin del archivo nos movemos al ltimo registro. - 8.4.1.8 Insercin de registros en el Recordset Para agregar nuevos registros al Recordset utilizamos el mtodo AddNew. Su empleo es bastante sencillo, tal y como se muestra en el siguiente ejemplo: rs.AddNew rs(nombre) = Juan rs(apellido) = Rodrguez rs.Update Llamamos el mtodo. Agregamos un nombre en el campo nombre. Agregamos un apellido en el campo apellido. Actualizamos.
423
424
- 8.4.1.9 Ordenacin de los registros de un Recordset Podr ordenar los registros contenidos en un Recordset utilizando la propiedad Sort. Para esto, se debe indicar el nombre o los nombres de los campos por el cual se ordenar la columna, por ejemplo: rs.Sort = nombre Ordena el Recordset por el campo nombre.
Podemos indicar dos campos para ordenar los registros del Recordset como se muestra en el siguiente ejemplo: rs.Sort = nombre, apellido Ordena el Recordset por el campo nombre y apellido.
Los registros se ordenan automticamente de forma ascendente, pero podr elegir un orden descendente utilizando el calificador DESC: Ordena el Recordset empezando con los empleados con mayores sueldos hasta los menores. rs.Sort = Sueldo DESC - 8.4.1.10 Bsqueda de registros El objeto Recordset dispone de un mtodo muy sencillo que permite localizar un registro que cumpla un determinado criterio de seleccin. Este es el mtodo Find que tiene la siguiente sintaxis: Find CriterioBsqueda, [RegistrosOmitidos], [DireccinBsqueda], [Inicio] CriterioBsqueda es una cadena que contiene la condicin de bsqueda, que constar de un nombre de campo seguido de un operador y de un valor. Los operadores que podr utilizar son = (igual), < (menor que), > (mayor que) y LIKE (Patrn de coincidencia). El valor puede ser una cadena encerrada entre comilla, un numero, el valor de una variable, el valor de un objeto o una fecha encerrada entre caracteres #. RegistrosOmitidos es un nmero opcional que indica la cantidad de registros que se van a ignorar antes de comenzar la bsqueda. DireccinBsqueda indica la direccin en la que se procesar la bsqueda; los valores admitidos son 1adSearchForward (Bsqueda hacia delante, valor predeterminado) o 1-adSearchBackward (Bsqueda hacia atrs). Inicio es un marcador opcional que especifica el registro desde el que se debe comenzar la bsqueda, el valor predeterminado es el registro actual. Podr omitir todos los parmetros excepto el primero, en este caso la bsqueda iniciara desde el registro actual.
425
Seleccione la caja combinada (ComboBox). En la propiedad List escriba los siguientes valores:
Seleccione la primera caja de texto y en la propiedad Name escriba: txtBuscarTexto Seleccione la segunda caja de texto y en la propiedad Name escriba: txtNombre Seleccione la tercera caja de texto y en la propiedad Name escriba: txtApellido Seleccione la cuarta caja de texto y en la propiedad Name escriba: txtTelefono Seleccione la quinta caja de texto y en la propiedad Name escriba: txtDireccion Seleccione la sexta caja de texto y en la propiedad Name escriba: txtCorreo
426
Escriba dentro del evento Load del formulario el siguiente bloque de cdigo: Set rs = New ADODB.Recordset 'Abrimos la base de datos "agenda.mdb". cn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & "Data Source=c:\agenda.mdb" rs.Source = "contactos" 'Especificamos la fuente de datos. En este caso la tabla "contactos". rs.CursorType = adOpenKeyset 'Definimos el tipo de cursor. rs.LockType = adLockOptimistic 'Definimos el tipo de bloqueo. rs.Open "select * from contactos", cn 'Abrimos el Recordset y lo llenamos con una consulta SQL. 'Cargamos los datos en las cajas de texto. rs.MoveFirst 'Nos movemos al principio del Recordset.
427
Si en el tipo de bsqueda usted especificar Nombre, entonces, en la caja del texto a buscar podr escribir uno de los nombres de la primera columna. De igual manera podr hacer con el Apellido y el Telfono. Si existen ms de un registro con el mismo parmetro de bsqueda especificado, solo el primero de ellos ser el que se active. Esto es, debido a que hemos especificado el valor 1 (busca siempre desde el principio) en el argumento Inicio del mtodo Find. Si desea que la bsqueda continu a partir del registro actual cada vez que hagamos clic en el botn Buscar, entonces, deber omitir el valor del argumento Inicio y especificar el valor 1 en el argumento RegistrosOmitidos, ejemplo: rs.Find "Apellido = '" & txtBuscarTexto.Text & "'", 1 rs.Find "Nombre = '" & txtBuscarTexto.Text & "'", 1 rs.Find "Telefono = '" & txtBuscarTexto.Text & "'", 1 Detenga la aplicacin y gurdela con los nombres FormEjercicio3-8 para el formulario y Ejercicio3-8 para el proyecto. Podr utilizar un bucle para recorrer todos los registros que cumplan un determinado criterio, tal y como se muestra en el siguiente ejemplo: Buscar todos los empleados que hayan sido contratados despus del 1 de enero de 2003. rs.MoveFirst rs.Find FechaContrato > #1/1/2003# Nos colocamos al principio del Recordset. Hacemos nuestra primera bsqueda.
Do Until rs.EOF Si encontramos los registros los vamos agregando en una ListBox. List1.AddItem rs(Nombre), rs(Apellido), rs(FechaContrato) Buscar el siguiente registro que cumpla el criterio de bsqueda pero ignoramos el actual. rs.Find FechaContrato > #1/1/2003# Loop
428
- 8.4.1.11 Verificar el estado del Recordset En muchas ocasiones necesitar verificar el estado del Recordset para mostrar los resultados a los usuarios de alguna operacin, o bien, para verificar si alguna operacin tuvo o no xito. Para consultar el estado del Recordset verifique la propiedad Status (utilizando una sentencia If o Case). En esta propiedad puede tener uno de los siguientes valores: Constante adRecOK adRecNew adRecModified adRecDeleted adRecUnmodified adRecInvalid adRecMultipleChanges adRecPendingChanges adRecCanceled adRecCantRelease adRecConcurrencyViolation adRecIntegrityViolation adRecMaxChangesExceeded adRecObjectOpen Valor 0 1 2 4 8 &H10 &H40 &H80 &H100 &H400 &H800 &H1000 &H2000 &H4000 Descripcin Se ha actualizado el registro con xito. El registro es nuevo. Se ha modificado el registro. Se ha borrado el registro. El registro no se ha modificado. No se ha guardado el registro porque su marcador no es vlido. No se ha guardado el registro porque afecta a varios registros. No se ha modificado el registro porque hace referencia a una insercin pendiente. No se ha guardado el registro porque se ha cancelado la operacin. No se ha guardado el registro porque esta bloqueado. No se ha guardado el registro porque se estaba usando una concurrencia optimista. No se ha guardado el registro porque violara las restricciones de integridad. No se ha guardado el registro porque existen demasiados cambios pendientes. No se ha guardado el registro porque existe un conflicto con un objeto de almacenamiento que se encuentra abierto. No se ha guardado el registro porque se ha producido un error del tipo fuera de memoria. No se ha guardado el registro porque el usuario no cuenta con los permisos necesarios. No se ha guardado el registro porque no cumple con la estructura de la base de datos. El registro ha sido borrado de la base de datos.
429
- 8.4.2 Eventos del objeto Recordset El objeto Recordset de ADO tiene un total de 11 eventos o sucesos. Estos sucesos le permitirn tener un control total de lo que est sucediendo internamente. Al codificar estos eventos, podr potenciar las consultas asncronas, atrapar el momento en el que se modifica un campo o un registro, verificar si se ha movido de un registro a otro e, incluso, agregar datos cuando el usuario alcance el final del Recordset. Para poder apreciar mejor los sucesos del Recordset lo he clasificado en tres grandes grupos: Sucesos de recuperacin de datos, Sucesos de navegacin y Sucesos de modificacin de datos. - 8.4.2.1 Sucesos de recuperacin de datos Estos sucesos ocurren durante una operacin asncrona cuando se estn recuperando los datos. El suceso FetchProgress ocurre de forma constante durante una operacin asncrona de gran longitud. Podr utilizarlo para mostrar al usuario una barra de progreso que indique el porcentaje de los registros que han sido recuperados. Este suceso tiene la siguiente estructura: Private Sub rs_FetchProgress (ByVal Progress As Long, ByVal MaxProgress As Long, _ adStatus As ADODB.EventStatusEnum, ByVal pRecordset As ADODB.Recordset) Aqu escriba su cdigo End Sub
El parmetro Prosess contiene el nmero de registros que se han extrado hasta el momento. MaxProgress es el nmero total de registros que se esperan recuperar. adStatus es el parmetro de estado. pRecordset es una referencia al objeto Recordset que ha provocado el suceso (este parmetro no tendr que utilizarlo porque ya cuenta con una referencia al Recordset). Cuando la operacin asncrona concluya, se pondr en marcha el suceso FetchComplete. Este suceso tiene la siguiente estructura: Private Sub rs_FetchComplete(ByVal pError As ADODB.error, _ adStatus As ADODB.EventStatusEnum, ByVal pRecordset As ADODB.Recordset) Aqu escriba su cdigo. End Sub Utilice el parmetro pError para consultar el si ha ocurrido algn error durante la transaccin.
430
End Sub El parmetro adReason proporciona el motivo por el que se ha puesto en marcha este suceso. Puede ser cualquiera de los valores que se muestran en la siguiente tabla: Valor 1 2 3 4 5 6 7 8 Constante adRsnAddNew adRsnDelete adRsnUpdate adRsnUndoUpdate adRsnUndoAddNew adRsnUndoDelete adRsnRequery adRsnResynch Valor 9 10 11 12 13 14 15 Constante adRsnClose adRsnMove adRsnFirstChange adRsnMoveFirst adRsnMoveNext adRsnMovePrevious adRsnMoveLast
Despus de ocurrir el suceso WillMove le sigue el suceso MoveComplete. Este ocurre cuando la transaccin ha sido completada o si ha sido cancelada. Este suceso tiene la siguiente estructura:
Private Sub rs_MoveComplete (ByVal adReason As ADODB.EventReasonEnum, _ ByVal pError As ADODB.error, adStatus As DODB.EventStatusEnum, _ ByVal pRecordset As ADODB.Recordset) End Sub Los parmetros adReason y adStatus tienen el mismo significado en ambos sucesos. Si el parmetro adStatus devuelve el valor adStatusErrorOcurred, el objeto pError contendr informacin sobre el error ocurrido, de lo contrario pError es Nothing. Otro suceso de navegacin que debe conocer es el suceso EndOfRecordset. Este suceso ocurre cuando el puntero de registro activo se coloca despus del ltimo registro del Recordset. Este suceso puede ocurrir como resultado de un mtodo MoveNext al colocarse sobre un registro que no existe. Este suceso tiene la siguiente estructura: Private Sub rs_EndOfRecordset (fMoreData As Boolean, adStatus As ADODB.EventStatusEnum, _ ByVal pRecordset As ADODB.Recordset) End Sub Si desea aprovechar este evento, podr agregar nuevos registros cuando este suceda. Para esto tendr que ejecutar el mtodo AddNew y rellenar con datos la coleccin Fields y, a continuacin, definir el parmetro fMoreData como True para permitir que ADO sepa que ha aadido nuevos registros.
431
432
433
En el evento Load del formulario escriba: ListView1.GridLines = True 'Hacemos que aparezcan las lneas del Grid.
'Agregamos los campos al ListView. ListView1.ColumnHeaders.Add , , "Nombre", 1600 ListView1.ColumnHeaders.Add , , "Apellido", 1600 ListView1.ColumnHeaders.Add , , "Telfono", 1400 ListView1.ColumnHeaders.Add , , "Direccin", 3000 ListView1.ColumnHeaders.Add , , "Correo", 1400 Activamos los Recordset. Set rs = New ADODB.Recordset Set rs2 = New ADODB.Recordset Set rs3 = New ADODB.Recordset 'Abrimos la base de datos "agenda.mdb". cn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & "Data Source=c:\agenda.mdb" rs.Source = "contactos" Este Recordset es para el primer reporte. rs.CursorType = adOpenKeyset rs.LockType = adLockOptimistic rs2.Source = "contactos" rs2.CursorType = adOpenKeyset rs2.LockType = adLockOptimistic rs3.Source = "contactos" rs3.CursorType = adOpenKeyset rs3.LockType = adLockOptimistic Este Recordset es para el segundo reporte.
434
435
436
Trucos de la red
CONTENIDO brir la ventana de quitar o agregar programas de Windows. Obtener el directorio desde donde estemos ejecutando nuestro programa. erificar si un archivo existe o no. Capturar la pantalla entera o la ventana activa. Desplegar la lista de un ComboBox automticamente. Cambiar el fondo de Windows. Comprobar si el sistema posee una tarjeta de sonido. pagar el equipo, reiniciar Windows y reiniciar el sistema. ituar un ScrollBar horizontal en una ListBox. Centrar una ventana. Obtener el directorio de Windows y el directorio de Sistema. Crear un efecto Shade al estilo de los sistemas de instalacin. Hacer sonar un fichero WAV o una secuencia MIDI. Ocultar y mostrar el cursor.
437
Trucos de la red
En el presente apartado he decidi colocar una pequea coleccin de trucos recogidos de la red. Estos cdigos diseados por aficionados a la programacin visual le sern de mucha utilidad en sus aplicaciones, y lo mejor de todo es, que estn disponibles gratuitamente para ser usados cada vez que los necesite. Abrir la ventana de quitar o agregar programas de Windows Este cdigo le permitir abrir la ventana Agregar o quitar programas de Windows. En un botn de comando escriba: Dim X X = Shell("Rundll32.exe shell32.dll,Control_RunDLL appwiz.cpl @0") Obtener el directorio desde donde estemos ejecutando nuestro programa Este cdigo es muy utilizado para leer el directorio o path desde donde se esta ejecutando la aplicacin: Dim Directorio As String ChDir App.Path ChDrive App.Path Directorio = App.Path If Len(Directorio) > 3 Then Directorio = Directorio & "\" End If erificar si un archivo existe o no Este cdigo permite verificar si existe o no un archivo en una unidad de disco: Escriba este cdigo en cualquier parte que quiera utilizarlo. On Error GoTo Fallo x = GetAttr("C:\Autoexec.bat") Aqu se escribe el fichero. MsgBox ("El fichero existe.") Exit Sub Fallo: MsgBox ("El fichero no existe.") Capturar la pantalla entera o la ventana activa Este cdigo le permitir copiar al portapales de Windows la imagen de la pantalla completa o la ventana activa. En la seccin general del editor de cdigo escriba: Private Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, _ ByVal bScan As Byte, ByVal dwFlags As Long, _ ByVal dwExtraInfo As Long) En un botn de comando escriba: 'Captura la ventana activa. keybd_event 44, 0, 0&, 0& Esta variable almacenar el directorio actual.
438
Trucos de la red
En otro botn de comando escriba: 'Captura toda la pantalla completa. keybd_event 44, 1, 0&, 0& Desplegar la lista de un ComboBox automticamente El siguiente cdigo le permitir desplegar la lista de un ComboBox al hacer clic sobre un botn de comando: En la seccin general del editor de cdigo escriba: Private Declare Function SendMessageLong Lib "user32" Alias _ "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, _ ByVal wParam As Long, ByVal lParam As Long) As Long En el evento Load del formulario escriba: Agregamos elementos al ComboBox. Combo1.Clear Combo1.AddItem "Objeto 1" Combo1.AddItem "Objeto 2" Combo1.AddItem "Objeto 3" Combo1.AddItem "Objeto 4" Combo1.AddItem "Objeto 5" Combo1.AddItem "Objeto 6" Combo1.AddItem "Objeto 7" Combo1.Text = "Objeto 1" En un botn de comando escriba: 'Hacemos que la lista se despliegue. Dim Resp As Long Resp = SendMessageLong(Combo1.hwnd, &H14F, True, 0) Cambiar el fondo de Windows Podr cambiar el fondo del escritorio de Windows mediante el siguiente cdigo: En la seccin general escriba: Private Declare Function SystemParametersInfo Lib "user32" Alias _ "SystemParametersInfoA" (ByVal uAction As Long, ByVal uParam As _ Long, ByVal lpvParam As Any, ByVal fuWinIni As Long) As Long En un botn de comando: Dim fallo As Integer fallo = SystemParametersInfo(20, 0, "C:\WINDOWS\FONDO.BMP", 0)
Comprobar si el sistema posee una tarjeta de sonido El siguiente cdigo le permitir verificar si una computadora tiene o no instalada una tarjeta de sonido:
439
Trucos de la red
En la seccin general escriba: Private Declare Function waveOutGetNumDevs Lib "winmm.dll" () As Long En un botn de comando escriba: Dim inf As Integer inf = waveOutGetNumDevs() If inf > 0 Then MsgBox "Tarjeta de sonido soportada.", vbInformation, "Informacion: Tarjeta de sonido" Else MsgBox "Tarjeta de sonido no soportada.", vbInformation, "Informacion: Tarjeta de sonido" End If Apagar el equipo, reiniciar Windows y reiniciar el sistema Con el siguiente cdigo podr apagar y reiniciar el sistema. Tambin podr cerrar la sesin en Windows. En la seccin general escriba: Private Declare Function ExitWindowsEx& Lib "user32" (ByVal _ uFlags&, ByVal dwReserved&) En un botn de comando escriba: Dim i As Integer i = ExitWindowsEx(1, 0&) 'Apaga el equipo.
En un segundo botn de comando escriba: Dim i As Integer i = ExitWindowsEx(0, 0&) 'Reinicia Windows con nuevo usuario.
En un tercer botn de comando escriba: Dim i As Integer i = ExitWindowsEx(2, 0&) 'Reinicia el Sistema.
ituar un ScrollBar horizontal en una ListBox Los ListBox no poseen una barra de desplazamiento horizontal, podr agregar una mediante el siguiente cdigo: En la seccin general escriba: Private Declare Function ExitWindowsEx& Lib "user32" (ByVal _ uFlags&, ByVal dwReserved&) En el evento Load del formulario escriba: Dim x As Integer, i As Integer For i = 1 To 20 List1.AddItem "El nmero final de la seleccin es el " & i Next i x = SendMessage(List1.hwnd, &H194, 200, ByVal 0&)
440
Trucos de la red
Centrar una ventana En el evento Load del formulario escriba: Move (Screen.Width - Width) \ 2, (Screen.Height - Height) \ 2 Obtener el directorio de Windows y el directorio de Sistema En la seccin general escriba: Declare Function GetSystemDirectory Lib "kernel32" Alias "GetSystemDirectoryA" _ (ByVal lpBuffer As String, ByVal nSize As Long) As Long Declare Function GetWindowsDirectory Lib "kernel32" Alias _ "GetWindowsDirectoryA" (ByVal lpBuffer As String, ByVal nSize As Long) As Long Inserte dos etiquetas y un botn de comando en el formulario. En el evento Click del botn de comando escriba: Dim Car As String * 128 Dim Longitud, Es As Integer Dim Camino As String Longitud = 128 Es = GetWindowsDirectory(Car, Longitud) Camino = RTrim$(LCase$(Left$(Car, Es))) Label1.Caption = Camino Es = GetSystemDirectory(Car, Longitud) Camino = RTrim$(LCase$(Left$(Car, Es))) Label2.Caption = Camino Crear un efecto Shade al estilo de los sistemas de instalacin En el evento Load del formulario escriba: Dim i As Long Dim y As Long Form1.Cls Form1.AutoRedraw = True Form1.DrawStyle = 6 Form1.DrawMode = 13 Form1.DrawWidth = 2 Form1.ScaleMode = 3 Form1.ScaleHeight = (256 * 2) For i = 0 To 255 Form1.Line (0, y)-(Form1.Width, y + 2), RGB(0, 0, i), BF y=y+2 Next i
441
Trucos de la red
Hacer sonar un fichero WAV o una secuencia MIDI En un mdulo escriba: Declare Function mciExecute Lib "winmm.dll" (ByVal lpstrCommand As String) As Long En un botn de comando escriba: Dim Sonido Sonido = mciExecute("Play c:\windows\ringin.wav") Ocultar y mostrar el cursor En un mdulo escriba: Declare Function ShowCursor Lib "user32" (ByVal bShow As Long) As Long Agregue dos botones de comando al formulario. En el primer botn escriba: Dim result result = ShowCursor(False) En el segundo botn de comando escriba: Dim result result = ShowCursor(True)
442
Anexos