Objetos en Visual Basic

 

Autor: Guillermo 'guille' Som
Traducido por: Joe LeVasseur.
Publicado originalmente en inglés en VB Online, edición USA de Mayo'98


A pesar de que han pasado más de dos años y medio desde que la versión 4 del Visual Basic hiciera su aparición, hay un elemento que aún no está lo suficientemente extendido como debiera, quizás sea por "incomprendido", me refiero a los módulos de clases, esa puerta al mundo de los objetos a la programación orientada a objetos.
No quiero decir que no se use este "nuevo" tipo de módulo, me refiero a que si la forma de usarlo está lo suficientemente entendida como debiera.
Por suerte la quinta versión de Visual Basic ha mejorado ese tipo de módulos, poniendo a nuestro alcance un sistema mejorado para la programación orientada a objetos. Bueno, casi orientada a objetos, no es plan de que los puristas empiecen a bombardearme el buzón de correo.

Puede que muchos de los lectores digan: "yo hace tiempo que trabajo con las clases en Visual Basic", y me parece estupendo, aunque algunos "abusamos" de las clases y en ocasiones las usamos por "snobismo": como es nuevo hay que usarlo...
He visto alguna que otra aplicación que usa las clases (objetos) para situaciones en las que realmente no son necesarias, también he visto código en los que el uso de un objeto (clase) solucionaría algunos conflictos; la verdad es que no he tenido que buscar mucho para encontrar los listados a los que hago alusión... mirando mi propio código tengo ejemplos de ambos casos...

Creo que el principal inconveniente con un uso más extendido de los módulos de clases en las aplicaciones de Visual Basic, básicamente es su incomprensión... el no saber cuándo usarlos y cuando nos puede beneficiar su uso en nuestros proyectos.
Es curioso, porque desde que empezamos a crear un proyecto, estamos trabajando, directa o indirectamente, consciente o inconscientemente, con objetos: asignamos valores a sus propiedades y usamos los métodos expuestos.
Por tanto deberíamos "cambiar" nuestro enfoque a la hora de crear un nuevo proyecto y planearlo como un puzzle en el que cada pieza pueda ser un objeto... y si estos objetos son lo más independiente posible: mejor.

Veamos un caso práctico en el que el uso de un objeto, (en forma de un módulo de clase), puede solucionarnos algún que otro problema no desado.
Este trozo de código nos permite mostrar en la caja de texto de un combobox, mientras vamos escribiendo, el item que más se parece de los que están en la lista de items.
En un principio, la implementación la hice usando unos procedimientos públicos que estaban en un módulo BAS.
Este es el código:

'Código en Módulo BAS
Option Explicit

Dim Combo1Borrado As Boolean

Public Sub unCombo_KeyDown(KeyCode As Integer)
    If KeyCode = vbKeyDelete Then
        Combo1Borrado = True
    Else
        Combo1Borrado = False
    End If
End Sub

Public Sub unCombo_KeyPress(KeyAscii As Integer)
    'si se pulsa Borrar... ignorar la búsqueda al cambiar
    If KeyAscii = vbKeyBack Then
        Combo1Borrado = True
    Else
        Combo1Borrado = False
    End If
End Sub

Public Sub unCombo_Change(ByVal sText As String, elCombo As ComboBox)
    Dim i As Integer, L As Integer
    
    If Not Combo1Borrado Then
        L = Len(sText)
        With elCombo
            For i = 0 To .ListCount - 1
                If StrComp(sText, Left$(.List(i), L), 1) = 0 Then
                    .ListIndex = i
                    .Text = .List(.ListIndex)
                    .SelStart = L
                    .SelLength = Len(.Text) - .SelStart
                    Exit For
                End If
            Next
        End With
    End If
End Sub

Para usarlo, simplemente hay que hacer esto:

'Código en los eventos del Combo
Private Sub Combo1_Change()
    Static YaEstoy As Boolean
    
    On Local Error Resume Next
    
    If Not YaEstoy Then
        YaEstoy = True
        unCombo_Change Combo1.Text, Combo1
        YaEstoy = False
    End If
    
    Err = 0
End Sub

Private Sub Combo1_KeyDown(KeyCode As Integer, Shift As Integer)
    unCombo_KeyDown KeyCode
End Sub

Private Sub Combo1_KeyPress(KeyAscii As Integer)
    unCombo_KeyPress KeyAscii
End Sub

En caso de tener varios controles combo, llamaríamos a estos procedimientos de igual forma, sólo cambiando el nombre del combo al que hacemos referencia.
Realmente no existe ningún problema... salvo si pudiesemos escribir en cada uno de ellos al mismo tiempo...
De cualquier forma, usando una clase que se encargara del control de cada combo, nos libraría de cualquier conflicto, por la sencilla razón de que cada uno de esos objetos creados para "tratar" cada combo mantiene de forma independiente las variables que necesita, a esto es lo que se llama "encapsulamiento"... Pero veamos el código de la clase y cómo usarlo:

'Código de la clase y cómo usarlo
Option Explicit

Dim elCombo As ComboBox

Dim Combo1Borrado As Boolean


Public Sub KeyDown(KeyCode As Integer)
    If KeyCode = vbKeyDelete Then
        Combo1Borrado = True
    Else
        Combo1Borrado = False
    End If
End Sub

Public Sub KeyPress(KeyAscii As Integer)
    'si se pulsa Borrar... ignorar la búsqueda al cambiar
    If KeyAscii = vbKeyBack Then
        Combo1Borrado = True
    Else
        Combo1Borrado = False
    End If
End Sub

Public Sub Change(ByVal sText As String)
    Dim i As Integer, L As Integer
    
    If Not Combo1Borrado Then
        L = Len(sText)
        With elCombo
            For i = 0 To .ListCount - 1
                If StrComp(sText, Left$(.List(i), L), 1) = 0 Then
                    .ListIndex = i
                    .Text = .List(.ListIndex)
                    .SelStart = L
                    .SelLength = Len(.Text) - .SelStart
                    Exit For
                End If
            Next
        End With
    End If
End Sub

Para usarlo, hay que crear una referencia a este tipo de objeto y después poder usarlo, veamos parte del código para implementar esta clase:

'En las declaraciones del form:
Option Explicit

Dim cCombo1 As cBuscCbo

Private Sub Form_Load()
    
    Set cCombo1 = New cBuscCbo
    Set cCombo1.Combo = Combo1
'...


Private Sub Combo1_Change()
    Static YaEstoy As Boolean
    
    If Not YaEstoy Then
        YaEstoy = True
        cCombo1.Change Combo1.Text
        YaEstoy = False
    End If

End Sub

Private Sub Combo1_KeyDown(KeyCode As Integer, Shift As Integer)
    cCombo1.KeyDown KeyCode
End Sub

Private Sub Combo1_KeyPress(KeyAscii As Integer)
    cCombo1.KeyPress KeyAscii
End Sub


Private Sub Form_Unload(Cancel As Integer)
    Set cCombo1 = Nothing
'...

Usándolo de esta forma, cada objeto se encarga de un combo y si quisieramos ampliar su funcionalidad, tendríamos la seguridad de que cualquier valor que deba conservar, (por ejemplo: una copia del contenido de los items de la lista), permanecerían "dentro" de cada objeto.
Pero al usarlo de esta forma, básicamente lo estamos usando como se hacía al tener los procedimientos en el módulo BAS, salvo que cada combo se guardaría en una nueva instancia de la clase, manteniendo su propia copia de las variables.
Aunque no es una buena forma de usarlo... cierto es que nos da "privacidad" en los datos, pero si tuviesemos otro combo en el que aplicar esto mismo habría que crear una nueva clase con un nombre diferente, por ejemplo:

'
Dim cCombo2 As cBuscCbo

    Set cCombo2 = New cBuscCbo
    Set cCombo2.Combo = Combo2

Private Sub Combo2_Change()
    Static YaEstoy As Boolean
    
    If Not YaEstoy Then
        YaEstoy = True
        cCombo2.Change Combo2.Text
        YaEstoy = False
    End If

End Sub

Sería más práctico crear una colección de este tipo de objeto y simplemente indicándole el nombre del combo que queremos usar, que sea la propia clase/colección la que se encargue de buscar el objeto apropiado, si es que existe, en otro caso nos devolvería un error.
Veamos el código de la clase/colección y cómo quedaría el código en el form en el que se emplee.
Un detalle: El tipo de objeto "almacenado" en esta colección sería un objeto de la clase usada anteriormente, por tanto, podríamos usarlo de cualquiera de las dos formas: con o sin colección...

'La clase / colección:
Option Explicit

Dim colCombos As New Collection

Public Sub AddCombo(ByVal NewCombo As ComboBox)
    Dim tCombo As New cBuscCbo
    Dim elIndex As String
    
    'Añadir un nuevo combo...
    On Local Error Resume Next
    
    Set tCombo.Combo = NewCombo
    With NewCombo
        elIndex = Format$(.Index, "000")
        If Err Then
            Err = 0
            elIndex = "000"
        End If
        colCombos.Add tCombo, .Name & elIndex
    End With
    'Si se produce un error es que ya existe
    
    Set tCombo = Nothing
    Err = 0
End Sub

Public Function Item(ByVal Index As ComboBox) As cBuscCbo
    'Este procedimiento hay que "marcarlo" como predeterminado
    'esto sólo es posible con el VB5
    Dim elIndex As String
    
    On Local Error Resume Next
    
    With Index
        elIndex = Format$(.Index, "000")
        If Err Then
            elIndex = "000"
        End If
        Err = 0
        Set Item = colCombos(.Name & elIndex)
    End With
    
    If Err Then
        'No existe
        Err.Raise Number:=vbObjectError + 1000, _
                  Source:="cCombos", _
                  Description:="Elemento no hallado"
    End If
    
End Function


'El código a usar en el form:
Option Explicit

Dim Combos As cCombos

Private Sub Form_Load()
    
    Set Combos = New cCombos
    Combos.AddCombo Combo1
    Combos.AddCombo Combo2
'...

Private Sub Form_Unload(Cancel As Integer)
    Set Combos = Nothing
'...

Private Sub Combo1_Change()
    Static YaEstoy As Boolean
    
    If Not YaEstoy Then
        YaEstoy = True
        With Combo1
            Combos(Combo1).Change .Text
        End With
        YaEstoy = False
    End If

End Sub

Private Sub Combo1_KeyDown(KeyCode As Integer, Shift As Integer)
    Combos(Combo1).KeyDown KeyCode
End Sub

Private Sub Combo1_KeyPress(KeyAscii As Integer)
    Combos(Combo1).KeyPress KeyAscii
End Sub

'En el Combo2 se usaría el mismo código,
'simplemente cambiando el nombre del Combo
Private Sub Combo2_Change()
    Static YaEstoy As Boolean
    
    If Not YaEstoy Then
        YaEstoy = True
        With Combo2
            Combos(Combo2).Change .Text
        End With
        YaEstoy = False
    End If
End Sub

Como comprobarás, resulta más fácil de usar de esta segunda forma, aunque si sólo tienes un combo en el que quieres aplicar esta funcionalidad, no es necesario usar la clase/colección...

Espero que, a pesar de no haber sido demasiado "tutorialista", te haya servido para "plantearte" el uso de las clases en Visual Basic y si quieres en próximos artículos, veriamos de una forma más "didáctica" cómo crear unas clases sencillas así como crear nuestras propias colecciones.
Todo depende del interés que suscite este artículo.
Espero tus comentarios.

 

Nos vemos.
Guillermo
P.S.
Si quieres, puedes ver
el código de ejemplo de los tres métodos usados. (cBuscCbo.zip 8.58 KB)


El segundo artículo publicado en VB Online USA

la Luna del Guille o... el Guille que está en la Luna... tanto monta...