|
Objects
in Visual Basic
By Guillermo Som (Translated by Joe LeVasseur [Any mistakes, etc are probably by translator].) |
Despite the fact that it has been more than two and a half years since the release of Visual Basic 4.0, there is an element that hasn't been sufficiently implemented, perhaps from lack of understanding. I'm referring to class modules- the VB gateway to the world of object oriented programming.
Don't get me wrong, I'm not saying that these "new" modules aren't used, I'm saying that perhaps they're not as well understood as they should be. Luckily version five of Visual Basic improved classes in general, placing object oriented programming within our reach. Well, almost object oriented, I must say- I don't need a bunch purist zealots to fill my mailbox up with speeches about true object oriented programming languages.
Perhaps some of my readers will say "I've been using Visual Basic classes for ages", and that is fine, although some of us "abuse" classes- sometimes for snobbish reasons: since they're the latest and greatest we must use them ... I've seen more than one application that uses classes (objects) for situations that didn't really require them. I've also seen code that the use of an object could have saved a lot of headaches. The truth is that I didn't have to look very far for examples of both situations- I found them in my own code.
I believe that the most prevalent reason that classes are not used more extensively in Visual Basic projects is that they are not fully understood, it's not understood when to use them or when their use could be of benefit to our projects. This is quite strange, because the moment that we create a new project, we are working, directly or indirectly, consciously or unconsciously, with objects: assigning values to their properties and using their exposed methods. Knowing that, we should change our focus when starting a new project and plan it like a puzzle, with each piece being an object. The more independent each piece of the puzzle is, the better.
Let's look at a practical use of an object, (In the form of a class module) which can solve some problems we didn't even plan on. The following code snippet will allow us to show in the text portion of a combobox, while we're writing, the item in the listbox that most closely corresponds to what we're typing. At first, I implemented the procedure as public methods of a standard .BAS module.
Here's the code:
' Code in a standard BAS module
Option ExplicitDim ComboBackspace As Boolean
Public Sub SomeCombo_KeyDown(KeyCode As Integer)
If KeyCode = vbKeyDelete Then
ComboBackspace = True
Else
ComboBackspace = False
End If
End SubPublic Sub SomeCombo_KeyPress(KeyAscii As Integer)
'If BackSpace pressed, ignore that key
If KeyAscii = vbKeyBack Then
ComboBackspace = True
Else
ComboBackspace = False
End If
End SubPublic Sub SomeCombo_Change(ByVal sText As String, TheCombo As ComboBox)
Dim i As Integer, L As IntegerIf Not ComboBackspace Then
L = Len(sText)
With TheCombo
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 SubTo implement, simply do this:
Option Explicit
'Code for the ComboBox
Private Sub Combo1_Change()
Static BeenHere As Boolean
On Local Error Resume Next
If Not BeenHere Then
BeenHere = True
SomeCombo_Change Combo1.Text, Combo1
BeenHere = False
End If
Err = 0
End SubPrivate Sub Combo1_KeyDown(KeyCode As Integer, Shift As Integer)
SomeCombo_KeyDown KeyCode
End SubPrivate Sub Combo1_KeyPress(KeyAscii As Integer)
SomeCombo_KeyPress KeyAscii
End SubIf you have more than one ComboBox control, use the routine the same way, simply changing the name of the control you're referring to. There's no problem with this method ... except it would be better if we could call the routine for all of them at the same time. Anyway, using a class which would take charge of each combo would free us from a lot of conflicts, for the simple reason that each of these objects created for each ComboBox would independently manage it's own variables. which is what we call "encapsulation". Well, on to the code:
'Code from the class
Option ExplicitDim theCombo As ComboBox
Dim ComboErase As Boolean
Public Property Set Combo(ByVal NewCombo As ComboBox)
Set theCombo = NewCombo
End PropertyPublic Sub KeyDown(KeyCode As Integer)
If KeyCode = vbKeyDelete Then
ComboErase = True
Else
ComboErase = False
End If
End SubPublic Sub KeyPress(KeyAscii As Integer)
'If Backspace, ignore
If KeyAscii = vbKeyBack Then
ComboErase = True
Else
ComboErase = False
End If
End SubPublic Sub Change(ByVal sText As String)
Dim i As Integer, L As IntegerIf Not ComboErase Then
L = Len(sText)
With theCombo
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
To use it, we create a reference to this type of object. Here's the implementation code.
'Code for the form
Option ExplicitDim cCombo1 As cSearchCbo
Private Sub Form_Load()
Set cCombo1 = New cSearchCbo
Set cCombo1.Combo = Combo1
End SubPrivate Sub Combo1_Change()
Static BeenHere As Boolean
If Not BeenHere Then
BeenHere = True
cCombo1.Change Combo1.Text
BeenHere = False
End If
End SubPrivate Sub Combo1_KeyDown(KeyCode As Integer, Shift As Integer)
cCombo1.KeyDown KeyCode
End SubPrivate Sub Combo1_KeyPress(KeyAscii As Integer)
cCombo1.KeyPress KeyAscii
End SubPrivate Sub Form_Unload(Cancel As Integer)
Set cCombo1 = Nothing
End SubUsing the class this way, each object takes care of it's own combo and if we want to expand it's functionality, we would feel secure that any variable values we want to protect. (For example: for example a copy of the items in the list.), will remain "inside" the object. But using the class this way, basically we are using the code as we did with the .BAS module, except each ComboBox will be assigned in a new instance of the class, maintaining its own copy of variables. Although it's not a good way to do it, it would provide us with "privacy" for our variables to if we created a new instance of the class with a different name for each ComboBox. For example:
Dim cCombo2 As cSearchCbo
Set cCombo2 = New cSearchCbo
Set cCombo2.Combo = Combo2Private Sub Combo2_Change()
Static BeenHere As Boolean
If Not BeenHere Then
BeenHere = True
cCombo2.Change Combo2.Text
BeenHere = False
End If
End SubIt would be more practical to create a collection of this type of object and simply indicate the name of the ComboBox we want to use, in other words the class itself would take care of looking for it's appropriate object (ComboBox), if it exists, if not it will generate an error. Let's look at the code for the class/collection and how it would be used in the form. One thing: the type of object in the collection will be the same as we previously wrote. That way we can use it as a stand-alone or collection.
' Code from the Collection class ...
Option ExplicitDim colCombos As New Collection
Public Sub AddCombo(ByVal NewCombo As ComboBox)
Dim tCombo As New cSearchCbo
Dim theIndex As String
On Local Error Resume Next
' Add a new ComboBox
Set tCombo.Combo = NewCombo
With NewCombo
theIndex = Format$(.Index, "000")
' If it's not part of an array, you'll
' get an error
If Err Then
'If not, assign a default
theIndex = "000"
End If
Err = 0
colCombos.Add tCombo, .Name & theIndex
End With
' If we get an error, it's already in there
Set tCombo = Nothing
Err = 0
End SubPublic Function Item(ByVal Index As ComboBox) As cSearchCbo
' This procedure must be marked as Procedure ID "Default" in the
'Object browser (VB5 Only)
Dim theIndex As String
On Local Error Resume Next
With Index
theIndex = Format$(.Index, "000")
' If it's not part of an array, you'll
' get an error
If Err Then
'If not, assign a default
theIndex = "000"
End If
Err = 0
Set Item = colCombos(.Name & theIndex)
End With
If Err Then
' Doesn't exist
Err.Raise Number:=vbObjectError + 1000, _
Source:="cCombos", _
Description:="Element not found"
End If
End FunctionCode from the Form:
Option Explicit
'The Class/Collection
Dim Combos As cCombosPrivate Sub Form_Load()
Set Combos = New cCombos
Combos.AddCombo Combo1
Combos.AddCombo Combo2
End SubPrivate Sub Form_Unload(Cancel As Integer)
Set Combos = Nothing
Set Form1 = Nothing
End Sub
Private Sub Combo2_Change()
Static BeenHere As Boolean
If Not BeenHere Then
BeenHere = True
With Combo2
Combos(Combo2).Change .Text
End With
BeenHere = False
End If
End Sub
Private Sub Combo1_Change()
Static BeenHere As Boolean
If Not BeenHere Then
BeenHere = True
With Combo1
Combos(Combo1).Change .Text
End With
BeenHere = False
End If
End SubPrivate Sub Combo2_KeyDown(KeyCode As Integer, Shift As Integer)
Combos(Combo2).KeyDown KeyCode
End Sub
Private Sub Combo1_KeyDown(KeyCode As Integer, Shift As Integer)
Combos(Combo1).KeyDown KeyCode
End SubPrivate Sub Combo2_KeyPress(KeyAscii As Integer)
Combos(Combo2).KeyPress KeyAscii
End Sub
Private Sub Combo1_KeyPress(KeyAscii As Integer)
Combos(Combo1).KeyPress KeyAscii
End Sub
As you can see, it's easier to use the code in the second example, although if you only have one ComboBox which needs this functionality, it's not necessary to use the collection class.
I hope that despite the fact that this article might be a little too "tutorialistic" in style, that it helped you to understand Visual Basic classes in general. If there is sufficient reader interest I could write more practical articles on how to create simple classes and collections. I'm awaiting your comments.
I'll see you around.
Guillermo
P.S.
If you're interested, here's the code for the examples. (In Spanish) objects.zip
About the Author
Guillermo Som
Microsoft Developer MVP
Nerja, Spain
guille@costasol.net
|