Gestionar un Array con ADO


Usando Clases de Reconocimiento de Datos y un DataGrid

Por Harvey Triana, Abril 1999

Disponer una interfaz de usuario para editar un Array suele ser bastante laborioso. En VB5 podemos usar un DBGrid no enlazado y escribir un extenso código para dar la funcionalidad requerida. Ahora con VB6, podemos usar una combinación de ADO, clases de reconocimientos de datos y un DataGrid, para obtener un código sencillo, una solución eficiente, y lo mejor: reutilizable a través de objetos.

Perspectiva

Mi propósito no es explicar como construir una clase de reconocimiento de datos (en la documentación de MSDN encuentra lo necesario), es más bien dar una utilidad muy interesante. Este pequeño articulo presenta un ejemplo puntual para gestionar una Array con ADO, y tiene el propósito de dar una guía de solución a casos más generales.

El objetivo de las clases de reconocimientos de datos es encapsular código y datos, permitiendo hacer datos persistentes entre sesiones, sin importar su origen. Personalmente pienso que es uno de los aciertos más relevantes que se introdujeron con Visul Basic 6.0.

Ejemplo

Deseamos editar un array bidimensional de valores Double en una grilla de datos. Para la solución, creamos un Origen de Datos que gestione el Array, y  luego lo enlazamos a un control DataGrid, el cual suministra la interfaz de edición.

1. Crea un proyecto EXE Estándar. Selecciona Referencias del menú proyecto para agregar una referencia a la Biblioteca Microsoft ActiveX Data Objetos 2.1 (ó 2.0). También agrega la referencia al control Microsoft DataGrid Control 6.0 (OLEDB).

2. Agrega un módulo de clase. Desde la ventana propiedades: Name = cls_Array, DataSourceBehavior = vbDataSource.

3. Agrega este código al módulo de Clase:

'//CLASE DE RECONOCIMIENTO DE DATOS
'//Ejemplo: Harvey T., 1999
Option Explicit

Private WithEvents rsArray As ADODB.Recordset

Private Sub Class_GetDataMember(DataMember As String, Data As Object)
    Set Data = rsArray
End Sub

Public Sub Init(a() As Double)
    Dim i As Long
    
    DataMembers.Add "Array Sample"
    
    Set rsArray = New ADODB.Recordset
    
    With rsArray
       .Fields.Append "Column1", adDouble
       .Fields.Append "Column2", adDouble
       .CursorType = adOpenStatic
       .LockType = adLockOptimistic
       .Open
        '//data
        For i = LBound(a) To UBound(a)
           .AddNew
           ![Column1] = a(i, 1)
           ![Column2] = a(i, 2)
           .Update
        Next
    End With
End Sub

4. Para usar la anterior clase de origen, dibujá en el formulario un control DataGrid con Name = dg. Por último, agregua el siguiente código al módulo del formulario:

'//FORM: ARRAY SAMPLE
'//Harvey T., 1999
Option Explicit

Private aSample As cls_Array

Private Sub Form_Load()
    Dim i As Long
    Dim j As Long
    
    '//Algunos datos de prueba:
    ReDim a(1 To 10, 1 To 2) As Double
    For i = 1 To 10
        For j = 1 To 2
            a(i, j) = FormatNumber(10 * Rnd(), 1)
        Next
    Next
    
    '//Se crea el objeto de reconocimiento de datos
    Set aSample = New cls_Array
    '//Se inicia explicitamente:
    aSample.Init a()
    
    '//Enlazar al DataGrid
    dg.Caption = "ARRAY SAMPLE"
    dg.DataMember = "Array Sample"
    Set dg.DataSource = aSample
End Sub

Private Sub Form_Unload(Cancel As Integer)
    Set aSample = Nothing
End Sub

5. Ejecuta el proyecto. Debe presentase la grilla con 2 columnas y 10 filas de datos.


Algunas Explicaciones

El código anterior es lo básico, todo lo que se necesita. Después, puedes ampliar el código para que cumpla con requerimientos particulares. por ejemplo podríamos crear una propiedad para obtener el número de registros (filas del Array) de la siguiente manera.

Public Property Get RecordCount() As Long
    RecordCount = rsArray.RecordCount
End Property

Por defecto DataGrid solo permite editar y actualizar datos. Puedes modificar las propiedades del DataGrid para que Elimine o Agregue filas.

Nota que el Array ya no es la fuente de datos física, la cual es la clase (formalmente hablando, el Recordset de ADO). Esto quiere decir que para usar posteriormente el Array debe revertir los cambios hechos en la edición, lo cual se puede hacer simple con un método en la clase como: GetArray, veamos:

Public Sub GetArray(a() As Double)
    Dim i As Long
    Dim j As Long
    With rsArray
        ReDim a(1 To .RecordCount, 1 To .Fields.Count)
        For i = 1 To .RecordCount
            For j = 1 To .Fields.Count
                a(i, j) = .Fields(j)
            Next
        Next
    End With
End Sub

Podemos hacer persistente el Array si lo escribimos en el disco, como también se lo puede leer posteriormente. Podríamos escribir una par de métodos en la clase para esto.

Sugerencia: Un Array se escribe y lee fácil y rápidamente desde un archivo Binary. Escribimos (despues de abrir el archivo como binario) con Put #Channel, , miArray() y recuperamos con Get #Channel, , miArray().

Dado que las Clases de Reconocimiento de Datos se soportan en ADO, se puede escribir cualquier capacidad del objeto Recordset de ADO. Es asi como podemos gestionar eventos, en virtud de la cláusula WithEvents. Note que la variable objeto rsArray, de la clase, suministra varios  eventos.


Extensión del Ejemplo

El ejemplo que publique suministra una clase reutilizable para cualquier Array bidimensional de tipo Double. ¿Desea más columnas?, modifiquemos el método Init de la clase para especificar:

Public Sub Init(a() As Double)
    Dim i As Long
    Dim j As Long
    
    DataMembers.Add "Array Sample"
    
    Set rsArray = New ADODB.Recordset
    
    With rsArray
        For j = LBound(a, 2) To UBound(a, 2)
           .Fields.Append "Column" & j, adDouble
        Next
       .CursorType = adOpenStatic
       .LockType = adLockOptimistic
       .Open
        '//data
        For i = LBound(a) To UBound(a)
           .AddNew
            For j = LBound(a, 2) To UBound(a, 2)
                rsArray("Column" & j) = a(i, j)
            Next
           .Update
        Next
    End With
End Sub

Ahora la clase soporta un número variable de columnas. Por ejemplo: ReDim a(1 To n, 1 To m) As Double.

Si deseo que la clase me sirva para usar un Array de otro tipo, p.e. String, ¿Que hacer?. En este caso recomiendo crear una clase exclusiva para Strings. Esto no representa un esfuerzo considerable y se optimizará el rendimiento. Tratar de escribir una clase general para todos los tipos de Array puede ser posible (Visual Basic es sorprendente), pero puede ser una misión de mucho esfuerzo y una solución no tan eficiente.

Este es un buen ejemplo, pero ¿que hay si deseo un Array heterogéneo? , ¿Es decir una matriz Variant?. Este caso le agregua complejidad a la solución, pero es viable. Empezariamos por analizar la configuración de tipos con VarType, etc. Sin embargo en vez de una matriz Variant, podría ser más elegante usar un UDT y una clase exclusiva para el UDT.