Cómo crear un enlace de datos en tiempo de ejecución
Por Enrique Martínez Montejo
Última revisión: 10/09/2005
 

Formulario de enlace de datosEl programador aficionado a los clásicos controles Data de las versiones anteriores de Visual Basic, quizás se encuentre un poco desconcertado ante la falta del clásico control de datos en Visual Basic .NET, por lo que seguramente se esté preguntando sobre la manera de poder presentar y manipular los datos en controles enlazados. La solución en Visual Basic .NET es fácil: crear un formulario personalizado que habilite las características del clásico control Data, tal y como se muestra en la imagen.

La forma más fácil de crear un enlace con un origen de datos ADO .NET, es mediante la utilización de los componentes diseñables incluidos en el entorno de desarrollo de Visual Studio .NET, de ésta forma podrá enlazar cualquier control con un campo de una tabla de datos, sin apenas escribir código fuente alguno. Pero mediante la escritura de nuestros propios procedimientos y funciones, es cuando podemos obtener unos resultados mucho más flexibles y eficientes, al menos en líneas generales. Los pasos a seguir para configurar el enlace en tiempo de ejecución, bien se podrían adaptar al siguiente esquema.

1. Definir a nivel del formulario una variable privada que referencie a un objeto DataTable, de ésta manera crearemos un enlace de datos bidireccional, por lo que las modificaciones efectuadas en los valores de los campos enlazados, se almacenarán en el origen de datos, en éste caso, en el objeto DataTable creado, que será un objeto desconectado de la propia base de datos, lo que significa que los datos de la base no se actualizarán de manera automática, permaneciendo intactos.

Private m_dt As DataTable

2. Declarar en el módulo una variable privada del tipo BindingManagerBase, la cual contendrá todos los objetos Binding que se encuentren asociados a un mismo origen de datos, que en nuestro caso será el objeto DataTable mencionado en el punto anterior.

Private m_bmb As BindingManagerBase

El objeto BindingManagerBase dispone de la propiedad Position, que es la encargada de establecer la posición de los valores actuales de los controles. Cuando cambia el valor de la propiedad Position, el objeto BindingManagerBase notifica a todos los objetos Binding asociados, que deben de enviar al origen de datos los valores actuales existentes en los controles de datos, recibiendo asimismo los nuevos valores existentes en la nueva posición. Estos cambios de posición hacen que el objeto BindingManagerBase desencadene el evento PositionChanged, el cual podremos aprovechar para realizar cualquier otro tipo de operación que nos pueda resultar necesaria.

3. Crear el objeto DataTable declarado en el primer punto. Para ello, podemos habilitar en nuestro formulario un procedimiento genérico que sea el encargado de ejecutar todo el código necesario para la creación del objeto DataTable:

' Cadena de conexión con el origen de datos.
'
Private Const
CONNSTRING As String = _
    "Provider=Microsoft.Jet.OLEDB.4.0;" & _
    "Data Source=C:\Mis documentos\Northwind.mdb"

Private Function
GetData(ByVal sql As String, _
                         ByVal tableName As String) As DataTable

    Try
        ' Configuramos una conexión con la base de datos.
        '

        Using cnn As New OleDbConnection(CONNSTRING)

            ' Creamos un adaptador de datos.
            '

            Dim da As New OleDbDataAdapter(sql, cnn)

            ' Agregamos información sobre la clave principal de la tabla.
            '

            da.MissingSchemaAction = MissingSchemaAction.AddWithKey

            ' Rellenamos el objeto DataTable con los datos
            ' de la consulta SQL.
            '

            Dim m_dt As New DataTable(tableName)

            ' Guardamos en el objeto DataTable la consulta SQL
            ' de selección utilizada.
            '

            m_dt.ExtendedProperties.Add("CommandText", sql)

            da.Fill(m_dt)

            Return m_dt

        End Using

    Catch ex As Exception
        Throw

    End Try

End Function

4. Enlazar los controles con los campos de la base de datos, creando el correspondiente objeto Binding mediante el método Add de la colección DataBindings. Dicho método acepta el nombre de la propiedad del control enlazado (generalmente la propiedad Text), la referencia al origen de datos (el objeto DataTable), y el nombre del campo de la tabla de la base de datos, en el formato nombrecampo.

Private Sub Form1_Load(ByVal sender As System.Object, _
                       ByVal e As System.EventArgs) Handles MyBase.Load

    Try
        ' Creamos el objeto DataTable.
        '

        m_dt = GetData("SELECT EmployeeID, FirstName, LastName FROM Employees", "Employees")

        If (m_dt Is Nothing) Then Return

        ' Referenciamos el objeto BindingManagerBase del formulario,
        ' pasándole el origen de datos que se va a utilizar.
        '

        m_bmb = Me.BindingContext.Item(m_dt)

        ' Creamos el controlador para el evento PositionChanged del objeto BindingManagerBase.
        '

        AddHandler m_bmb.PositionChanged, AddressOf PositionChanged

        ' Creamos el controlador para los eventos Click de los controles Button.
        '

        AddHandler Button1.Click, AddressOf ButtonOnClick
        AddHandler Button2.Click, AddressOf ButtonOnClick
        AddHandler Button3.Click, AddressOf ButtonOnClick
        AddHandler Button4.Click, AddressOf ButtonOnClick

        ' Le asignamos un valor a la propiedad Tag de cada control Button,
        ' para posteriormente identificar el control sobre el que se ha
        ' efectuado el Click.
        '

        Button1.Tag = "F"
        Button2.Tag = "P"
        Button3.Tag = "N"
        Button4.Tag = "L"

        ' Procedemos a enlazar los controles de texto con los respectivos
        ' campos del objeto DataTable.
        '

        TextBox1.DataBindings.Add("Text", m_dt, "EmployeeID")
        TextBox2.DataBindings.Add("Text", m_dt, "FirstName")
        TextBox3.DataBindings.Add("Text", m_dt, "LastName")

        ' Actualizar la información sobre el número de registros existentes.
        '

        With m_bmb
            .Position = .Count
            .Position = 0
        End With

    Catch ex As Exception
        MessageBox.Show(ex.Message)

    End Try

End Sub

Una vez creado el conjunto de datos, y enlazados los correspondientes controles, solo nos queda escribir el código necesario para que podamos movernos de una posición a otro dentro del origen de datos (el objeto DataTable).

Private Sub PositionChanged(ByVal sender As Object, ByVal e As EventArgs)
    lblReg.Text = "Registro " & (bmb.Position + 1) & " de " & bmb.Count
End Sub

Private Sub ButtonOnClick(ByVal sender As Object, ByVal e As EventArgs)

    Try
        ' Referenciamos el control que ha desencadenado el evento
        '

        Dim btn As Button = DirectCast(sender, Button)

        If (btn.Tag Is "F") Then
            ' Nos desplazamos al primer registro
            m_bmb.Position = 0

        ElseIf (btn.Tag Is "P") Then
            ' Nos desplazamos al registro anterior
            m_bmb.Position -= 1

        ElseIf (btn.Tag Is "N") Then
            ' Nos desplazamos al siguiente registro
            m_bmb.Position += 1

        Else
            ' Nos desplazamos al último registro
            m_bmb.Position = m_bmb.Count - 1

        End If

    Catch ex As Exception
        ' Si se ha producido un error al movernos de registro,
        ' cancelamos la edición del registro actual.
        '

        m_bmb.CancelCurrentEdit()

        MessageBox.Show(ex.Message)

    End Try

End Sub

Si ejecuta el proyecto, observará que puede desplazarse por los distintos registros pulsando sobre los correspondientes botones, almacenándose en el origen de datos (el objeto DataTable) todas las modificaciones que efectúe sobre los campos. Pero al estar trabajando en un entorno desconectado de la propia base de datos, esas modificaciones realizadas no tendrán efecto en la propia base de datos: tendrá que utilizar un objeto DataAdapter apropiado para enviar las modificaciones a la base de datos. De ello se encargará el siguiente procedimiento genérico, el cual actualiza un origen de datos OleDb:

Private Function UpdateDatabase() As Integer

    ' Si el valor del objeto DataTable es Nothing, provocamos
    ' una excepción.
    '

    If (m_dt Is Nothing) Then _
        Throw New ArgumentNullException()

    Try
        ' Configuramos una conexión con la base de datos.
        '

        Using cnn As New OleDbConnection(CONNSTRING)

            ' Obtenemos la consulta SQL de selección utilizada para
            ' rellenar el objeto DataTable.
            '

            Dim sql As String = m_dt.ExtendedProperties("CommandText").ToString()

            ' Creamos el adaptador de datos.
            '

            Dim da As New OleDbDataAdapter(sql, cnn)

            ' Creamos un objeto CommandBuilder para configurar los comandos
            ' apropiados del adaptador de datos. Se requiere que la tabla
            ' de la base de datos tenga establecida su correspondiente
            ' clave principal.
            '

            Dim cb As New OleDbCommandBuilder(da)

            With da
                .InsertCommand = cb.GetInsertCommand()
                .DeleteCommand = cb.GetDeleteCommand()
                .UpdateCommand = cb.GetUpdateCommand()
            End With

            ' Procedemos a actualizar la base de datos, devolviendo
            ' el número de registros afectados.
            '

            Return da.Update(m_dt)

        End Using

    Catch ex As Exception
        Throw

    End Try

End Function

Siguiendo con nuestro ejemplo, para llamar al procedimiento UpdateDatabase utilizaríamos el siguiente código:

Try
    Me.Validate()

    ' Finalizamos la edición del registro actual.
    m_bmb.EndCurrentEdit()

    Dim n As Integer = UpdateDatabase()

    MessageBox.Show(String.Format("Nº de registros afectados: {0}", n))

Catch ex As Exception
    MessageBox.Show(ex.Message)

End Try

NOTA: Como en el ejemplo se ha utilizado la tabla Employees de la base de datos Northwind, las modificaciones efectuadas en el formulario sobre el campo EmployeeID, no surtirán efecto en la base de datos, dado que dicho campo está definido por defecto como Autonumérico.

 

Otros enlaces de interés:

Indice de Ejemplos de ADO .NET


Enrique Martínez Montejo - 2005

NOTA: La información contenida en este artículo, así como el código fuente incluido en el mismo, se proporciona COMO ESTÁ, sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo explicado, recomendado o sugerido en el presente artículo.

NOTE: The information contained in this article and source code included therein, is provided AS IS without warranty of any kind, and confers no rights. You assume any risk to implement, use or run it explained, recommended or suggested in this article.