Cómo enlazar una lista al control DataGridView
Por Enrique Martínez Montejo
[Microsoft Most Valuable Professional - Visual Basic]
Última revisión: 12/09/2010
 

Creo que son bastantes los usuarios que conocen la manera de enlazar un control DataGridView a un objeto DataTable. Basta con asignarle dicho objeto a la propiedad DataSource del control DataGridView. Pero, ¿y si no disponemos de un objeto DataTable? ¿Tendríamos que crear uno para mostrar una serie de datos?

Uno de los requisitos que debe de cumplir el objeto que se asigne a la propiedad DataSource del control DataGridView, es que su clase implemente alguna de las siguientes interfaces:

Los objetos DataSet, DataTable y BindingSource son de sobra conocidos como orígenes de datos del control DataGridView. Vamos a fijarnos en otros objetos no tan conocidos por los usuarios de Visual Basic .NET.

Enlazar el control DataGridView con una clase genérica del tipo List(Of T)

La versión 2.0 del marco de trabajo de .NET (Visual Studio 2005), introdujo una serie de clases genéricas para complementar las colecciones ya existentes en las versiones previas de la plataforma .NET. Entre ellas se encontraba la clase genérica List(Of T), que nos permite tener una lista de objetos de un determinado tipo de dato, lo que en el argot de .NET se conoce como una lista fuertemente tipificada, o tipada, como también se le denomina en ciertos medios.

Esto significa que la lista no puede contener objetos de diversos tipos (String, Integer, DateTime). El objeto List(Of T) sólo puede contener objetos de un mismo tipo, siendo la T la que define el tipo de dato que contendrá la lista: todos los elementos serán String, Integer o DateTime, obteniéndose una excepción en tiempo de diseño o compilación si algún objeto no es del tipo de dato definido al crear la lista.

Como la clase List(Of T) implementa la interfaz IList, entre otras más, cumple el requisito necesario para poder asignárselo a la propiedad DataSource del control DataGridView, tal y como muestra el siguiente ejemplo:

Dim paises As New List(Of String)

paises.Add("España")
paises.Add("Francia")
paises.Add("Italia")
paises.Add("Alemania")

' Enlazamos el control DataGridView con la lista genérica
DataGridView1.DataSource = paises

El resultado que se mostrará en el control DataGridView será el que aparece en la siguiente imagen:

Lo mismo se estará preguntando dónde están los nombres de los países que se han añadido a la lista genérica. Como podrá observar, solamente aparecen las longitudes de los nombres de los países que se han añadido a la lista, es decir, el valor devuelto por la propiedad Length de la clase System.String, que es el tipo de dato con el que se ha definido el objeto List(Of T). Como la clase System.String solamente dispone de una única propiedad que no necesita de ningún índice para obtener su valor, es por ello que únicamente se mostrarán en el control DataGridView los valores de dicha propiedad.

Es cierto que la clase System.String también dispone de la propiedad Chars, pero ésta necesita un índice para devolver el carácter (un valor Char) existente en el índice o posición indicado. Por éste motivo, los valores de ésta propiedad no se mostrarán en el control DataGridView.

Pero si en lugar de crear una lista genérica con tipos de datos String, la generamos utilizando estructuras DateTime, o cualquier otra clase o estructura definida por nosotros mismos, podremos observar los valores de muchas más propiedades:

Dim fechas As New List(Of DateTime)

fechas.Add(New DateTime(1492, 10, 12))
fechas.Add(New DateTime(1776, 7, 4))

' Enlazamos el control DataGridView con la lista genérica
DataGridView1.DataSource = fechas

' Eliminamos ciertas columnas
DataGridView1.Columns.Remove("Hour")
DataGridView1.Columns.Remove("Kind")
DataGridView1.Columns.Remove("Millisecond")
DataGridView1.Columns.Remove("Minute")
DataGridView1.Columns.Remove("Second")

Siendo el resultado el que aparece a continuación:

Esto viene a confirmar que el control DataGridView dispondrá de tantas columnas como propiedades públicas tenga el tipo de dato, o la estructura, con la que se ha definido la lista genérica List(Of T).

Todo esto está bien para mostrar un listado con los valores de la lista genérica definida, pero no podrá añadir nuevos registros al control DataGridView, ni tampoco eliminar los ya existentes, con lo cual no sabemos muy bien si nos puede ser de utilidad.

Enlazar el control DataGridView con una clase genérica del tipo BindingList(Of T)

Como he mencionado al comienzo del artículo, otro requisito para poder enlazar un control DataGridView a un origen de datos, es que éste implemente la interfaz IBindingList, tal y como así lo hace la clase BindingList(Of T), que es la clase genérica del marco de trabajo de .NET que admite el enlace de datos, por tanto, la podemos utilizar en nuestras clases para proporcionar a las mismas un enlace de datos bidireccional.

Vamos a impletar la típica clase Cliente, la cual tendrá tres únicas propiedades públicas, a parte de su constructor predeterminado y de otro constructor sobrecargado.

Public Class Cliente

    Private m_idCliente As Integer
    Private m_nombre As String
    Private m_direccion As String

    Public Sub New()
        ' Constructor por defecto. Es necesario para
        ' poder añadir nuevos registros en blanco.

    End Sub

    Public Sub New(ByVal idcliente As Integer, ByVal nombre As String, ByVal direccion As String)
        m_idCliente = idcliente
        m_direccion = direccion
        m_nombre = nombre
    End Sub

    Public Property IdCliente As Integer
        Get
            Return m_idCliente
        End Get
        Set(ByVal value As Integer)
            m_idCliente = value
        End Set
    End Property

    Public Property Nombre As String
        Get
            Return m_nombre
        End Get
        Set(ByVal value As String)
            m_nombre = value
        End Set
    End Property

    Public Property Direccion As String
        Get
            Return m_direccion
        End Get
        Set(ByVal value As String)
            m_direccion = value
        End Set
    End Property

End Class

Podríamos crear una lista genérica del tipo List(Of Cliente) tal y como he explicado en el apartado anterior. Pero estaríamos en las mismas: no se podrán añadir nuevos registros al control DataGridView.

Para solventar éste impedimento necesitamos de una clase intermedia que nos proporcione un enlace de datos, y que disponga de un evento para añadir nuevos registros, en nuestro caso, nuevos objetos del tipo Cliente. Y esta clase bien pudiera ser cualquiera que herede de la clase System.ComponentModel.BindingList(Of T):

Imports System.ComponentModel

Public Class ListaClientes

    ' La clase admitirá el enlace de datos
    Inherits BindingList(Of Cliente)

    Protected Overrides Sub OnAddingNew(ByVal e As AddingNewEventArgs)

        ' Creamos un nuevo Cliente
        '

        e.NewObject = New Cliente()

    End Sub

End Class

Esta clase derivada, a parte de la funcionalidad existente en el objeto BindingList(Of T), hará que se desencadene el evento AddingNew cada vez que se desee crear un nuevo registro en el control DataGridView, de ahí que se reemplace el método OnAddingNew para asignarle una nueva instancia de la clase Cliente a la propiedad NewObject del objeto AddingNewEventArgs.

Una vez que tenemos creadas ambas clases (Cliente y ListaClientes), ya es hora de enlazar un objeto BindingList(Of T) con un control DataGridView. Para ello, simplemente ejecutaría lo siguiente:

' Construimos una lista de clientes
Dim lst As ListaClientes = New ListaClientes()

' Añadimos clientes a la lista
lst.Add(New Cliente(43001, "José Amate", "C/. García Benítez, 23"))
lst.Add(New Cliente(43002, "Felipe Moya", "C/. Isaac Peral, 56"))
lst.Add(New Cliente(43003, "Carmen Díaz", "C/. Ruiz Romero, 85"))

' Enlazamos el control DataGridView con la lista
DataGridView1.DataSource = lst

Y aquí tiene el resultado:

Como podrá observar en la imagen, el cuarto registro está siendo añadido actualmente al control DataGridView. Asimismo, podrá editar cualquier registro existente, al igual que eliminar cualquiera de ellos. Los cambios que efectúe en el control DataGridView, automáticamente se actualizarán en la lista genérica definida, que podrá referenciarla ejecutando la siguiente asignación:

' Referenciamos el origen de datos del control DataGridView.
'
Dim lst As ListaClientes = DirectCast(DataGridView1.DataSource, ListaClientes)

' Recorremos sus elementos
'
For Each item As Cliente In lst
    Console.WriteLine("{0} {1} {2}", item.IdCliente, item.Nombre, item.Direccion)
Next

 

Otros enlaces de interés:

Índice de la colección de ejemplos de las clases del marco de trabajo de .NET


Enrique Martínez Montejo - 2010

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.