Agenda telefónica 2 en Visual Basic .NET Fecha: 12/Sep/2003 (14/Sep/2003) |
. |
Recordando mi anterior ejercicio "Agenda telefónica en VBasic .NET", publicado en esta sección, se proponía una manera muy sencilla (posiblemente la que más) de conectar con una base de datos de Access, usando el Asistente para Formularios de Datos del propio IDE de Visual Studio. Esta manera de trabajar tiene ventajas evidentes para el principiante:
Pero, junto a esas ventajas, aparecen algunos inconvenientes, de los que, al menos 3, son para tener en cuenta:
- el asistente realiza la mayor parte del trabajo, de manera que, con pocos conocimientos y en poco tiempo, se tiene una aplicación que funciona.
- el código creado automáticamente es bastante complejo, con buena protección de errores, y suele funcionar bien casi desde el principio.
- la aplicación realizada de esta manera entra de lleno en ADO .NET (ActiveX Data Objects .NET), que es la tecnología de acceso a datos más avanzada de la plataforma .NET, proporcionando pleno acceso desconectado a los datos.
De todo lo anterior nace la idea del ejercicio actual, esta "Agenda telefónica 2", con 2 premisas fundamentales:
- el código creado automáticamente reside en gran parte en la sección
#Region " Código generado por el Diseñador de Windows Forms "
lo que lo hace menos manipulable por el programador.- el código creado automáticamente, como ya se ha dicho antes, es bastante complejo, y puede darse el caso de que el usuario principiante ponga a punto una aplicación de este tipo con rapidez y, sin embargo, no comprenda grandes fragmentos del código de su propio programa (como me pasó a mí mismo ;-).
- el código creado automáticamente recurre a un mecanismo de la plataforma .NET llamado DataBinding, que consiste en crear enlaces entre los objetos contenedores de los datos y los controles del formulario, lo que permite que las operaciones de navegación y edición de registros se automaticen en grado elevado. Este mecanismo de enlace automático de datos a controles usa numerosos elementos del conjunto de tipos de .NET Framework: clases, colecciones, enumeraciones... Todo ello pasa desapercibido al realizar este tipo de aplicaciones con el Asistente para Formularios de Datos.
- debe usar un mecanismo similar al de la primera Agenda telefónica, realizando otro ejercicio en el que sea el propio usuario el que construya el código "a mano", recurriendo también al empleo de DataBinding.
- debe seguir siendo un ejercicio proyectado para principiantes, por lo que el código debe ser sencillo en todo lo que se pueda y debe estar explicado al máximo en cualquiera de sus pasos.
Introducción simple a ADO .NET y DataBinding
Acceso desconectado a datos: En la arquitectura tradicional de tipo cliente / servidor, cada cliente se conectaba a la base de datos, manteniendo la conexión el tiempo necesario para realizar operaciones directamente contra el servidor de base de datos al que está conectado. Obligaba a mantener conexiones de base de datos para todos y cada uno de los usuarios.
Al crecer el tamaño de las bases de datos y el número de clientes (usuarios) potenciales, sobre todo en relación con el fenómeno de Internet, se vio la necesidad de establecer conexiones lo más breves posibles, simplemente para las operaciones inicial (lectura o recuperación de datos) y final (escritura o actualización de datos) sobre el origen de los datos (la base de datos) y trabajar el resto del tiempo sobre una copia desconectada local del origen de los datos (DataSet).
En versiones anteriores de Visual Basic ya existía ADO (ActiveX Data Objects) que permite acceder a todo tipo de bases de datos desde Visual Basic y trabajar en modo desconectado. Visual Basic .NET puede usar ADO, pero ADO .NET es el método preferido por Visual Studio .NET para conectar a bases de datos.
ADO .NET usa clases específicas para efectuar operaciones en bases de datos. Una de las diferencias con respecto a ADO es el uso de comandos específicos de conexión con SQL Server: varias de las clases principales de ADO .NET tienen 2 versiones diferentes, una para conectar a una base de datos SQL Server y otra para proveedores OLE-DB. Como ejemplo, tenemos SQLCommand y OLEDBCommand, SQLConnection y OLEDBConnection, etc. ODBC está desaconsejado, es conveniente usar OLE-DB en su lugar.
DataBinding: es el mecanismo de la plataforma .NET que, en aplicaciones con interfaz WindowsForms, permite crear enlaces entre los objetos que contienen los datos y los controles del formulario. Como ejemplo, se puede enlazar la propiedad Text de un control TextBox con una columna de una tabla del DataSet (la base de datos desconectada), lo que simplifica enormemente las operaciones de navegación por los registros de la tabla.
Objetos importantes del DataBinding:
- Binding: clase que permite crear el enlace, necesita:
- propiedad del control que muestra los datos
- DataSet que proporciona la información
- tabla y columna cuyos datos pasan a la propiedad del control
- DataBinding: colección de los enlaces a datos que tienen los controles
- BindingContext: propiedad de la clase Form con información de los enlaces establecidos entre controles y datos. Devuelve un objeto BindingManagerBase
- BindingManagerBase: encargado de administrar un conjunto de enlaces de un formulario, obtenidos desde la propiedad BindingContext del formulario.
A continuación sigue código en Visual Basic:
'Agenda telefonica 2 --- Miliuco 07/09/2003 'Ejercicio de acceso a datos: '- base de datos de Microsoft Access '- usando DataBinding (enlace de datos a controles del formulario) Imports System.Data Imports System.Data.OleDb Public Class Inicio Inherits System.Windows.Forms.Form 'Variables declaradas a nivel de clase para poder usarlas desde 'cualquier procedimiento o función Private conn As OleDbConnection = New OleDbConnection 'conexión de tipo OleDb Private adaptador As OleDbDataAdapter 'DataAdapter: adaptador de datos Private datos As DataSet 'DataSet: conjunto de datos desconectados Private enlaceBase As BindingManagerBase 'administrador de los objetos de enlace a datos Private comando As OleDbCommandBuilder 'constructor de comandos para el DataAdapter Private ruta As String 'almacena la ruta a la base de datos Private abierto As Boolean = False 'para saber si ya está abierta la base de datos Private cambios As Boolean = False 'para saber si hay cambios pendientes de guardar #Region " Código generado por el Diseñador de Windows Forms " #End Region 'Al cargar el formulario Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load 'Crear tooltip de 2 líneas en el control Icono Me.ToolTip1.SetToolTip(Icono, "Emilio Pérez Egido (Miliuco)" & ControlChars.CrLf _ & Space(2) & "8 de Septiembre de 2003") 'Texto inicial de las etiquetas lbPrimera.Text = "Pulsa en ""Cargar datos"" para conectarte a la base de datos." & _ vbCrLf & "Elige la base de datos en el cuadro de diálogo." lbSegunda.Text = "Desconectado. Conjunto de datos vacío." 'Altura inicial del separador Panel1 Panel1.Height = 5 End Sub Private Sub btCargar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btCargar.Click 'Si la base de datos está abierta, simplemente informar If abierto = True Then Dim aviso As String = "La base de datos ya está cargada en el DataSet" lbPrimera.Text = aviso.ToUpper End If 'Si la base de datos NO está abierta If abierto = False Then Try 'Cuadro de diálogo para elegir la base de datos dlgFile = New OpenFileDialog dlgFile.Filter = "Base de datos Agenda (*.mdb)|*.mdb" dlgFile.Title = "Selecciona la base de datos Agenda" 'Si el diálogo devuelve OK If dlgFile.ShowDialog() = DialogResult.OK Then 'Nombre del archivo elegido, con su ruta completa, 'equivale a la base de datos ruta = dlgFile.FileName 'Cadena de conexión típica de base de datos de Microsoft Access conn.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" _ & ruta & ";User Id=admin;Password=;" 'DataAdapter: objeto que hace de puente entre la base de datos 'y el DataSet o conjunto de datos adaptador = New OleDbDataAdapter("SELECT * FROM Agenda ORDER BY Nombre", conn) 'DataAdapter puede contener sentencias SQL y objetos Command comando = New OleDbCommandBuilder(adaptador) 'DataSet: contiene una copia de la base de datos, en esquema XML, 'independiente del proveedor, con sus elementos tablas y relaciones. 'Es el verdadero almacén de datos desconectados: actuamos sobre 'el DataSet desconectado y el DataAdapter se conecta para volcar 'los datos entre base de datos y DataSet, en ambos sentidos datos = New DataSet 'Abrir la conexión, el DataAdapter llena el DataSet, 'cerrar la conexión conn.Open() adaptador.Fill(datos, "Agenda") conn.Close() 'Para enlazar controles del formulario con el DataSet usamos 'DataBinding; al objeto Binding le pasamos la propiedad del 'control que se debe enlazar, el DataSet y la tabla-columna. 'Después, el objeto Binding se añade a la colección de enlaces 'de datos (DataBinding) del control elegido, con el método Add() Dim enlace As Binding enlace = New Binding("Text", datos, "Agenda.Nombre") txNombre.DataBindings.Add(enlace) enlace = Nothing enlace = New Binding("Text", datos, "Agenda.Teléfono1") txTel1.DataBindings.Add(enlace) enlace = Nothing enlace = New Binding("Text", datos, "Agenda.Teléfono2") txTel2.DataBindings.Add(enlace) enlace = Nothing 'Al objeto BindingManagerBase le pasamos el DataSet y la tabla, 'desde el contexto de enlace del formulario (BindingContext) Me.enlaceBase = Me.BindingContext(datos, "Agenda") lbPrimera.Text = "Base de datos cargada en el conjunto de datos," & _ vbCrLf & "usando DataBinding: Enlace de datos a controles." & _ vbCrLf & "Para insertar un registro nuevo, pulsa ""Limpiar campos""," & _ vbCrLf & "escribe los datos y pulsa ""Insertar""" lbSegunda.Text = "Archivo de datos: " & ruta 'Procedimiento descrito más abajo Call verCuenta() 'Para saber que la base de datos ya está cargada abierto = True End If Catch pollo As Exception lbPrimera.Text = pollo.Message.ToUpper End Try End If End Sub 'Mostrar el registro actual y el total de registros de la tabla del DataSet Private Sub verCuenta() lbRegistro.Text = "Registro " & Me.enlaceBase.Position + 1 & " de " _ & Me.enlaceBase.Count End Sub 'Gracias al enlace automático entre controles y datos (DataBinding) 'es muy sencillo navegar entre los registros y se necesita muy poco 'código para ello, el objeto enlaceBase (BindingManagerBase) se encarga 'de actualizar de manera automática el control con los datos de la fila 'de la tabla en que estamos posicionados Private Sub btAvanzar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btAvanzar.Click 'Si la base de datos está abierta If abierto = True Then Try 'Si ya estamos en el último registro If Me.enlaceBase.Position = Me.enlaceBase.Count - 1 Then Dim aviso1 As String = "Ya estás en el último registro de la tabla" lbPrimera.Text = aviso1.ToUpper Else lbPrimera.Text = "Base de datos cargada en el conjunto de datos." & _ vbCrLf & "Usando DataBinding: Enlace de datos a controles." 'avanzar un registro Me.enlaceBase.Position += 1 verCuenta() End If Catch pollo As Exception lbPrimera.Text = pollo.Message.ToUpper End Try 'Si la base de datos NO está abierta Else Dim aviso As String = "Primero has de cargar la base de datos" lbPrimera.Text = aviso.ToUpper End If End Sub Private Sub btRetroceder_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btRetroceder.Click 'Si la base de datos está abierta If abierto = True Then Try 'Si ya estamos en el primer registro If Me.enlaceBase.Position = 0 Then Dim aviso2 As String = "Ya estás en el primer registro de la tabla" lbPrimera.Text = aviso2.ToUpper Else lbPrimera.Text = "Base de datos cargada en el conjunto de datos." & _ vbCrLf & "Usando DataBinding: Enlace de datos a controles." 'retroceder un registro Me.enlaceBase.Position -= 1 verCuenta() End If Catch pollo As Exception lbPrimera.Text = pollo.Message.ToUpper End Try 'Si la base de datos NO está abierta Else Dim aviso As String = "Primero has de cargar la base de datos" lbPrimera.Text = aviso.ToUpper End If End Sub Private Sub btPrimero_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btPrimero.Click 'Si la base de datos está abierta If abierto = True Then Try 'Si ya estamos en el primer registro If Me.enlaceBase.Position = 0 Then Dim aviso2 As String = "Ya estás en el primer registro de la tabla" lbPrimera.Text = aviso2.ToUpper Else lbPrimera.Text = "Base de datos cargada en el conjunto de datos." & _ vbCrLf & "Usando DataBinding: Enlace de datos a controles." 'ir al primer registro Me.enlaceBase.Position = 0 verCuenta() End If Catch pollo As Exception lbPrimera.Text = pollo.Message.ToUpper End Try 'Si la base de datos NO está abierta Else Dim aviso As String = "Primero has de cargar la base de datos" lbPrimera.Text = aviso.ToUpper End If End Sub Private Sub btUltimo_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btUltimo.Click 'Si la base de datos está abierta If abierto = True Then Try 'Si ya estamos en el último registro If Me.enlaceBase.Position = Me.enlaceBase.Count - 1 Then Dim aviso1 As String = "Ya estás en el último registro de la tabla" lbPrimera.Text = aviso1.ToUpper Else : lbPrimera.Text = "Base de datos cargada en el conjunto de datos." & _ vbCrLf & "Usando DataBinding: Enlace de datos a controles." 'ir al último registro Me.enlaceBase.Position = Me.enlaceBase.Count - 1 verCuenta() End If Catch pollo As Exception lbPrimera.Text = pollo.Message.ToUpper End Try 'Si la base de datos NO está abierta Else Dim aviso As String = "Primero has de cargar la base de datos" lbPrimera.Text = aviso.ToUpper End If End Sub 'Para borrar un registro Private Sub btBorrar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btBorrar.Click 'Si la base de datos está abierta If abierto = True Then Try 'Borrar el registro que se corresponde con la posición actual Me.enlaceBase.RemoveAt(Me.enlaceBase.Position) lbPrimera.Text = "Se ha eliminado un registro." & vbCrLf _ & "Pulsa actualizar si deseas guardar los cambios." cambios = True Catch pollo As Exception lbPrimera.Text = pollo.Message.ToUpper End Try 'Si la base de datos NO está abierta Else Dim aviso As String = "Primero has de cargar la base de datos" lbPrimera.Text = aviso.ToUpper End If End Sub 'Para vaciar los campos de texto Private Sub btLimpiar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btLimpiar.Click 'Si la base de datos está abierta If abierto = True Then txNombre.Text = "" txTel1.Text = "" txTel2.Text = "" txNombre.Focus() 'Si la base de datos NO está abierta Else Dim aviso As String = "Primero has de cargar la base de datos" lbPrimera.Text = aviso.ToUpper End If End Sub 'Para añadir un registro Private Sub btInsertar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btInsertar.Click 'Si la base de datos está abierta If abierto = True Then ''crear una nueva fila en la tabla Dim fila As DataRow fila = Me.datos.Tables("Agenda").NewRow() Try 'rellenar los campos de la fila nueva fila("Nombre") = txNombre.Text fila("Teléfono1") = txTel1.Text fila("Teléfono2") = txTel2.Text lbPrimera.Text = "Se ha insertado un registro nuevo." & vbCrLf _ & "Pulsa actualizar si deseas guardar los cambios." 'añadir la nueva fila a la tabla Me.datos.Tables("Agenda").Rows.Add(fila) cambios = True Catch pollo As Exception lbPrimera.Text = pollo.Message.ToUpper End Try 'Si la base de datos NO está abierta Else Dim aviso As String = "Primero has de cargar la base de datos" lbPrimera.Text = aviso.ToUpper End If End Sub 'Para actualizar el adaptador de datos (DataAdapter) con los cambios hechos en el DataSet Private Sub btActualizar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btActualizar.Click 'Si la base de datos está abierta If abierto = True Then 'Si ha habido cambios en el DataSet desde la última actualización If (cambios = True) Then Try conn.Open() 'Actualizar DataAdapter, se pueden pasar parámetros: ' - (datos as dataset, "tabla" as string) ' - (datos.tables("tabla") as dataset) --> usado aquí Me.adaptador.Update(Me.datos.Tables("Agenda")) 'Vaciar el DataSet: conserva los datos de tablas y registros y si no 'se limpia acumula las operaciones sucesivas de modificación de filas Me.datos.Clear() 'Volver a llenar el DataSet desde el DataAdapter ya modificado Me.adaptador.Fill(datos, "Agenda") conn.Close() 'Actualizar la etiqueta lbRegistro Call verCuenta() 'Indicador de cambios pendientes a false cambios = False Dim aviso As String = "Base de datos actualizada con los cambios realizados" lbPrimera.Text = aviso.ToUpper Catch pollo As Exception lbPrimera.Text = pollo.Message.ToUpper End Try Else ' 'Si NO ha habido cambios en el DataSet desde la última actualización Dim aviso2 As String = "No hay cambios pendientes de actualizar" lbPrimera.Text = aviso2.ToUpper End If 'Si la base de datos NO está abierta Else Dim aviso3 As String = "Primero has de cargar la base de datos" lbPrimera.Text = aviso3.ToUpper End If End Sub Private Sub btSalir_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btSalir.Click 'Si la base de datos NO está abierta If abierto = False Then 'Cerrar el formulario Me.Close() End If If abierto = True Then 'si NO ha habido cambios en el DataSet desde la última actualización If cambios = False Then 'Cerrar el formulario Me.Close() End If 'Si la base de datos está abierta y si además 'ha habido cambios en el DataSet desde la última actualización If cambios = True Then If MessageBox.Show("Hay cambios pendientes de guardar." & vbCrLf _ & "Pulsa en ""Cancelar"" para volver al programa." & _ vbCrLf & "Pulsa en ""Aceptar"" para salir sin guardar", _ "Cambios sin guardar", MessageBoxButtons.OKCancel, _ MessageBoxIcon.Exclamation) = DialogResult.OK Then Me.Close() End If End If End If End Sub End ClassImagen del programa en funcionamiento
Fichero con el código de ejemplo en Visual Studio .NET 2003 (miliuco_agenda2.zip - 90 KB)