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 SubPara 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 SubEn 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 SubPara 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 SubSerí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 SubComo 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)