Agenda telefónica en Visual Basic .NET
Usando el Asistente para Formulario de Datos para crear una conexión con una base de datos de Microsoft Access

Fecha: 23 de Agosto de 2003 (23/Ago/2003)
Revisado: 24/Ago/2003
Autor: Emilio Pérez Egido (Miliuco) - emi@miliuco.com

 
.

Explicación del ejercicio:

La base de este ejercicio está en crear una conexión entre una base de datos de Microsoft Access y un formulario de datos (DataForm) de Visual Basic .NET. De esta manera se consigue mostrar en el programa los registros almacenados en la base de datos de Access.
En este sitio web ya existe un tutorial en la sección de ADO.NET llamado "Ejemplo de acceso a datos con una base de Access" (autor: Guille) que propone realizar todo ésto mediante código escrito "a mano", pero exige un nivel de conocimientos avanzado, por ello propongo este otro ejercicio en el que es muy poco el código que debe escribir el usuario, aunque sí conviene entenderlo para poder retocarlo a nuestro gusto.
Los pasos que debemos seguir son:

- crear una base de datos de Microsoft Access que tenga al menos una tabla (no es imprescindible nada más en la base de datos) con los campos que nos interesen. En el ejercicio son ID, Nombre, Dirección, Teléfono 1 y Teléfono 2. Podemos dejar la tabla (aquí llamada tbAgenda) en blanco, sin registros, y agregarlos directamente desde la aplicación de Visual Basic, pero he comprobado que es más práctico rellenar desde Access unos pocos registros (bastan 3 o 4) para que, al probar la aplicación, sea más ilustrativo cómo se cargan los datos en el formulario de datos.

- comenzar un proyecto nuevo de Visual Basic .NET, del tipo Aplicación de Windows.

- nos olvidamos por ahora del formulario creado por defecto Form1.vb.

- agregar un elemento nuevo, eligiendo el "Asistente para formulario de datos", lo que inicia un asistente.

- elegir el conjunto de datos que deseas utilizar: creamos un nuevo conjunto de datos al que ponemos nombre.

- elegir una conexión de datos: desde el botón "Nueva conexión" abrimos las "Propiedades el vínculo de datos" con 4 pestañas:

- proveedor: elegimos Microsoft Jet 4.0 OLEDB Provider.
- conexión: buscamos la base de datos de Access para que aparezca en el cuadro "Seleccione el nombre de una base de datos". Mediante el botón "Probar conexión" sabremos si es factible realizarla.
- el resto de opciones las dejamos como están.
- elegir tablas de la base de datos: elegimos tbAgenda (o el nombre que le hayamos dado).

- elegir tablas y columnas para mostrar en el formulario: marcamos las deseadas.

- elegir el estilo de presentación: Registro único en controles individuales, y marcamos aquellos controles que deseamos que aparezcan como botones de comando en el formulario.

- finalizar, y podemos incluir la contraseña en el cuadro de diálogo que nos pregunta sobre ello (por el momento nos despreocupamos de las medidas de seguridad).

- ahora sólo queda modificar el diseño del formulario de datos a nuestro gusto, recordando que es imprescindible cambiar el objeto inicial del proyecto que, por defecto, estará configurado en Form1.vb y debe ser el formulario de datos (aquí DataForm1.vb). Incluso podemos eliminar el formulario Form1.vb después de ello.

NOTA acerca de ordenar los datos y los comandos SQL

Si deseamos que los registros aparezcan ordenados por algún campo, podemos modificar fácilmente la instrucción SQL que se encarga de seleccionar los datos de la tabla que son mostrados en el formulario.

En este ejercicio usamos la clase OleDb del espacio de nombres System.Data que permite el acceso a proveedores de datos que trabajan directamente contra los controladores basados en ActiveX de Microsoft. Si en vez de ello conectásemos con proveedores SQL Server usaríamos la clase SqlClient del mismo espacio de nombres System.Data. La clase OleDb tiene subclases específicas de cada proveedor de datos. De estas subclases, nos interesa la clase OleDbCommand, que representa un comando SQL enviado contra el gestor de datos. En el ejercicio, dentro de la región "Código generado por el Diseñador de Windows Forms", tenemos un objeto del tipo OleDbCommand llamado OleDbSelectCommand1 que se crea en 2 líneas no consecutivas:


    Friend WithEvents OleDbSelectCommand1 As System.Data.OleDb.OleDbCommand

        Me.OleDbSelectCommand1 = New System.Data.OleDb.OleDbCommand

Este objeto OleDbSelectCommand1 tiene una propiedad de tipo String, CommandText, que contiene la cadena con la sentencia SQL de selección para cargar la tabla en el formulario. Por defecto el comando SQL es:


  Me.OleDbSelectCommand1.CommandText = "SELECT Dirección, ID, Nombre, [Teléfono 1], [Teléfono 2] FROM tbAgenda"

Esa instrucción SQL simplemente selecciona todos los registros de esos campos de la tabla tbAgenda y los muestra. Pero si queremos ordenarlos por algún criterio, podemos modificar la instrucción en esa línea de código. Por ejemplo, si queremos que los datos aparezcan ordenados por el campo Nombre en sentido ascendente, tendríamos que escribir:


  Me.OleDbSelectCommand1.CommandText = "SELECT Dirección, ID, Nombre, [Teléfono 1], [Teléfono 2] FROM tbAgenda ORDER BY Nombre ASC"

De esta manera comprobaremos cómo los registros mostrados en el formulario de datos están ordenados por el campo Nombre, lo que puede resultarnos más útil a la hora de desplazarnos por ellos.


A continuación sigue código en Visual Basic:


    'Cargar la base de datos
    Private Sub btnLoad_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnLoad.Click
        Try
            'Intenta cargar el conjunto de datos.
            Me.LoadDataSet()
            'Avisa de que se ha cargado la base de datos predeterminada
            MessageBox.Show("La base de datos se ha cargado en el DataSet", "Datos cargados", MessageBoxButtons.OK, MessageBoxIcon.Information)
            'Informar en la barra de título del formulario
            Me.Text = "Agenda telefónica - DATOS CARGADOS"
        Catch eLoad As System.Exception
            'Agregar aquí el código de control de errores.
            'Mostrar mensaje de error, si hay alguno.
            System.Windows.Forms.MessageBox.Show(eLoad.Message)
        End Try
        Me.objemiData_PositionChanged()
    End Sub

    'Añadir un registro
    Private Sub btnAdd_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAdd.Click
        Try
            'Borrar las ediciones actuales
            Me.BindingContext(objemiData, "tbAgenda").EndCurrentEdit()
            Me.BindingContext(objemiData, "tbAgenda").AddNew()
        Catch eEndEdit As System.Exception
            System.Windows.Forms.MessageBox.Show(eEndEdit.Message)
        End Try
        Me.objemiData_PositionChanged()
    End Sub

    'Borrar un registro
    Private Sub btnDelete_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDelete.Click
        If (Me.BindingContext(objemiData, "tbAgenda").Count > 0) Then
            Me.BindingContext(objemiData, "tbAgenda").RemoveAt(Me.BindingContext(objemiData, "tbAgenda").Position)
            Me.objemiData_PositionChanged()
        End If
    End Sub

    Private Sub btnUpdate_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnUpdate.Click
        Try
            'Intenta actualizar el origen de datos.
            Me.UpdateDataSet()
        Catch eUpdate As System.Exception
            'Agregar aquí el código de control de errores.
            'Mostrar mensaje de error, si hay alguno.
            System.Windows.Forms.MessageBox.Show(eUpdate.Message)
        End Try
        Me.objemiData_PositionChanged()
    End Sub

    'Ir al primer registro
    Private Sub btnNavFirst_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnNavFirst.Click
        Me.BindingContext(objemiData, "tbAgenda").Position = 0
        Me.objemiData_PositionChanged()
    End Sub

    'Ir al último registro
    Private Sub btnLast_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnLast.Click
        Me.BindingContext(objemiData, "tbAgenda").Position = (Me.objemiData.Tables("tbAgenda").Rows.Count - 1)
        Me.objemiData_PositionChanged()
    End Sub

    'Retroceder un registro
    Private Sub btnNavPrev_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnNavPrev.Click
        Me.BindingContext(objemiData, "tbAgenda").Position = (Me.BindingContext(objemiData, "tbAgenda").Position - 1)
        Me.objemiData_PositionChanged()
    End Sub

    'Avanzar un registro
    Private Sub btnNavNext_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnNavNext.Click
        Me.BindingContext(objemiData, "tbAgenda").Position = (Me.BindingContext(objemiData, "tbAgenda").Position + 1)
        Me.objemiData_PositionChanged()
    End Sub

    'Rellenar la etiqueta del panel de navegación
    Private Sub objemiData_PositionChanged()
        Me.lblNavLocation.Text = (((Me.BindingContext(objemiData, "tbAgenda").Position + 1).ToString + "  de  ") _
                    + Me.BindingContext(objemiData, "tbAgenda").Count.ToString)
    End Sub

    'Actualizar el origen de datos
    Public Sub UpdateDataSet()
        'Crear un conjunto de datos para alojar los cambios realizados en el conjunto de datos principal.
        Dim objDataSetChanges As Agenda_telefónica.emiData = New Agenda_telefónica.emiData
        'Detener las ediciones actuales.
        Me.BindingContext(objemiData, "tbAgenda").EndCurrentEdit()
        'Obtener los cambios realizados en el conjunto de datos principal.
        objDataSetChanges = CType(objemiData.GetChanges, Agenda_telefónica.emiData)
        'Comprobar si se han realizado cambios.
        If (Not (objDataSetChanges) Is Nothing) Then
            Try
                'Hay cambios que necesitan aplicarse, por tanto, intente actualizar el origen de datos
                'llamando al método de actualización y pasando el conjunto de datos y los parámetros.
                Me.UpdateDataSource(objDataSetChanges)
                objemiData.Merge(objDataSetChanges)
                objemiData.AcceptChanges()
            Catch eUpdate As System.Exception
                'Agregar aquí el código de control de errores.
                Throw eUpdate
            End Try
            'Agregar código para comprobar en el conjunto de datos devuelto los errores que se puedan haber
            'introducido en el error del objeto de fila.
        End If
    End Sub

    'Cargar la base de datos
    Public Sub LoadDataSet()
        'Crear un conjunto de datos para alojar los registros devueltos de la llamada a FillDataSet.
        'Se utiliza un conjunto de datos temporal porque el relleno del conjunto de datos existente
        'requeriría que se volvieran a enlazar los enlaces de datos.
        Dim objDataSetTemp As Agenda_telefónica.emiData
        objDataSetTemp = New Agenda_telefónica.emiData
        Try
            'Intenta rellenar el conjunto de datos temporal.
            Me.FillDataSet(objDataSetTemp)
        Catch eFillDataSet As System.Exception
            'Agregar aquí el código de control de errores.
            Throw eFillDataSet
        End Try
        Try
            'Vaciar los registros obsoletos del conjunto de datos.
            objemiData.Clear()
            'Combinar los registros en el conjunto de datos principal.
            objemiData.Merge(objDataSetTemp)
        Catch eLoadMerge As System.Exception
            'Agregar aquí el código de control de errores.
            Throw eLoadMerge
        End Try
    End Sub

    'Actualizar el origen de datos
    Public Sub UpdateDataSource(ByVal ChangedRows As Agenda_telefónica.emiData)
        Try
            'Sólo es necesario actualizar el origen de datos si hay cambios pendientes.
            If (Not (ChangedRows) Is Nothing) Then
                'Abra la conexión.
                Me.OleDbConnection1.Open()
                'Intenta actualizar el origen de datos.
                OleDbDataAdapter1.Update(ChangedRows)
            End If
        Catch updateException As System.Exception
            'Agregar aquí el código de control de errores.
            Throw updateException
        Finally
            'Cerrar la conexión independientemente de si se inició una excepción o no.
            Me.OleDbConnection1.Close()
        End Try
    End Sub

    'Rellenar el DataSet con los datos
    Public Sub FillDataSet(ByVal dataSet As Agenda_telefónica.emiData)
        'Desactiva la comprobación de restricciones antes de rellenar el conjunto de datos.
        'De esta forma los adaptadores pueden rellenar el conjunto de datos sin preocuparse
        'de las dependencias entre las tablas.
        dataSet.EnforceConstraints = False
        Try
            'Abrir la conexión.
            Me.OleDbConnection1.Open()
            'Intenta rellenar el conjunto de datos a través de OleDbDataAdapter1.
            Me.OleDbDataAdapter1.Fill(dataSet)
        Catch fillException As System.Exception
            'Agregar aquí el código de control de errores.
            Throw fillException
        Finally
            'Volver a activar la comprobación de restricciones.
            dataSet.EnforceConstraints = True
            'Cerrar la conexión independientemente de si se inició una excepción o no.
            Me.OleDbConnection1.Close()
        End Try
    End Sub

    Private Sub btSalir_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btSalir.Click
        Me.Close() 'cerrar el formulario
    End Sub

End Class

Comentarios finales y propuestas de mejora

Como resumen, hemos visto que, mediante el asistente de formularios de datos, el propio entorno de Visual Studio .NET escribe gran cantidad de código útil por nosotros, lo que facilita la construcción de programas relativamente complejos sin tener conocimientos avanzados de programación.
El programa se mejoraría añadiendo funciones de filtrado o de búsqueda de registros concretos, impresión de informes con listados, etc. Por mi parte, por ahora se queda como está ;-)


Imagen del programa en funcionamiento


Agenda telefónica

(El botón "Abrir datos" se conecta a la base de datos predeterminada)

 

Fichero con el código de ejemplo en Visual Studio .NET 2003 (miliuco_agenda.zip - 116 KB)


ir al índice