VB now allows us to addcontrols to a Form at run time. In thisarticle I will try to explain a way in which you can dynamically add a control arrayto a Form at run time and then catch the events generated by these addedcontrols.
Basic Concepts
Suppose we have aForm, named FrmTest to which we want to add a Label Control at run time. We must make our intention clear from thebeginning, giving the Label Control a name, and letting FrmTest know aboutit. Lets call this Label Control mLblTaskLength,because with it we intend to display on FrmTest the time a certainmanufacturing operation takes. Therefore, in the declaration area of FrmTest we add:
Private WithEvents mLblTaskLength As Label
We use the prefix”m” as a convention to help us identify a variable which scope is only withinFrmTest. Having declared this variableWithEvents, we now find it displayed in the left dropdown list of our codewindow, where we can write code to handle the various events the Label Controlscan generate. When the need to show thelabel arises, we use the following code to make it appear on our Form:
SetmLblTaskLaskLength = Me.Controls.Add(“VB.Label”,”mLblTaskLength”)
The firstparameter “VB.Label” or ProgID identifies the Label Control, the secondparameter is the controls name.
This code willcreate the Label control for us, although it shall not be immediately visibleand still VB is not aware of the new Label property values. Therefore we continue with our code, settingthe Property values and telling VB where to display this new control:
WithmLblTaskLaskLength
.Appearance = 1
.AutoSize = False
.BackStyle = 1 ‘Opaque
.BorderStyle = 0 ‘None
…etc
End With
We can use the”move” method to place the control on the Form.
mLblTaskLaskLength.move 500,500,100,800
Were theparameters indicate: Top, Left, Height and Width properties of the Label. Finally we make the Label visible.
mLblTaskLaskLength.Visible= True
After this codeis executed the Label will display nicely on our Form. This covers the basics.
Adding an Array of Controls
Now let usventure forward. We want to create anarray of Labels, because we have many manufacturing tasks, the duration ofwhich, we want to display with Labels. We soon discover that VB does not allow us to display an array ofcontrols in the manner we have just learnt. We need a workaround for this. One way to achieve this goal is to encapsulate a Label Control inside aClass Module. We then add to ourProject a new Class Module and name it CLabel. We set its instancing property to Private. In Clabel declaration area we add the following:
Option Explicit
PrivateWithEvents mLbl As Label
PrivatemCallingForm As Form
Let us take acloser look at what we have declared. With mLabel we are creating a variable to hold the Label Controlencapsulated in our Class Module. With mCallingForm we have a variable to hold a reference to the Form on which we wantthe Label displayed. Please note wehave declared mLbl WithEvents. As inthe previous case, you shall find in the left Dropdown list of your codeWindow, a place made available to handle the events mLbl generates. This is of course a minimum set ofdeclarations. When on your own, you candeclare other variables, one for Caption, another for BackStyle…
Now we need toadd the following property to our Class Module:
Public PropertyLet CallingForm(frm As Form)
SetmCallingForm = frm
End Property
We then need amethod to call and have it draw the Label on the Form for us. Lets call this method gDrawOnForm. Here is how to do it:
Public Sub gDrawOnForm(ByVal Name$,ByValTop%,ByVal Left%,ByVal Height%, ByVal Width%)
SetmLbl = mCallingForm.Controls.Add(“VB.Label”, fs_Name)
SetmCallingForm = Nothing ‘Important Line of Code
‘ we set the properties ‘
mLbl.Autosize= false
mLbl.BackStyle=1
…etc
‘then the position ‘
mLbl.Top= Top
mLbl.Left= Left
mLbl.Height=Height
mLbl.Width= Width
mLbl.Visible= True
End Sub
You can add otherparameters to the List, as you see fit.
Now lets go backto our Form, on which we want the array of Labels displayed. On the declaration area we declare the variableto hold the array and add a Collection to hold the array of Labels.
PrivatemLblTaskLength As Clabel
PrivatemColTaskLength As Collection
When we are readyto create the array we do it in this manner:
Dim li%
Set mColTaskLength = New Collection
For li = 0 To NumberOf Labels
SetmLblTaskLength = New Clabel
‘Add Item to Collection ‘
mColTaskLength.AddmLblTaskLength
mLblTaskLength.gDrawOnForm “LblT” & li, other parameters
SetmLblTaskLength = Nothing
Next
Notice how we havenamed our Labels. This isimportant. They are named LblT0, LblT1, andLblT2… Once this code executes you shall find theLabels displayed on the Form. By nowyou might be thinking, “Wait………. you started talking about catching events. Where are the events now? You didn’t even declare mLblTaskLenghtWithEvents. You see, I still have somebunnies in my hat, I am not through yet.
Lets get the bigblack bunny out of the hat. There is aClass Module missing here! Lets callthis missing Class Module ” CGral “. As the name suggests this module is useful for general purposes. One of the purposes may well be to raisesome events. Sort of an eventPublisher. We create this Class Module,set it’s instancing to MultiUse., and add a public Event to it:
evLabelClicked(ByValIndex As Integer)
and also a publicmethod called gLabelClicked which raises the event evLabelClicked.
Public SubgLabelCliked(ByVal Indx As Integer)
RaiseEventevLabelClicked(Indx)
EndSub
Remember we had aplace to handle the Label events in our CLabel Class Module. That is the placewe use to call the CGral method gLableClicked. Of course we need the Index identifying the Label being clicked on, andwe derive this Index from the Label Name Property. Here is one way to write this code:
Private Sub Lbl_Click()
Dim li_Len%
Dim li_Index%
li_Len = Len(mLbl.Name)
If li_Len = 6 Then li_Index =CInt(Right(mLbl.Name, 1))
If li_Len = 7 Then li_Index =CInt(Right(mLbl.Name, 2))
If li_Len = 8 Then li_Index =CInt(Right(mLbl.Name, 3))
‘raises the event ‘
gGRAL.gLabelCliked (li_Index)
End Sub
So now we haveClass Module CGral trumpeting the event within our application. But is our Form aware of it? Not yet. In the declaration area of our Form we must add
PrivateWithEvents mLabelSelected As CGral
And with thisdeclaration VB will make a space available to us where we receive and handlethe Lable_Click event. This is
Private SubmLabelSelected_LabelClicked(ByVal Index As Integer)
Codeto handle event here…
End Sub
Now thelast piece of the puzzle is in place.
Cleaning Up
Onceyou labels have appeared on your Form and you are through with clicking on themto your hearts content, its time to remove the Labels and clean up. Remember, the Labels “belong” to the Form onwhich they are drawn, that is, they are part of the Form.Controlscollection. On the other hand, the artists that have drawn the Labels on yourForm are the CLabel classes and they are independent of your Form. This code onyour Form does it:
Private Sub ClearLabels()
Dim ctl AsControl
Dim li%
‘ This removesthe Labels
For Each ctl InMe.Controls
‘ use this Filter to remove only the Labels added dymanically
‘ you may have other Labels on tour Form you dont want removed
If Left(ctl.Name, 4) = “LblT” Then Me.Controls.Removectl.Name
Next
‘ Then youdismiss the artists
Forli = mColTaskLength.Count To 1 Step -1
mColTaskLength.Remove li
Next
End Sub
You may not have yetnoticed but we have barely escaped being caught in a “circular reference”problem. The trap was nicely set for us from the beginning, because your Formhas a reference to the CLabel classes which in turn have a reference to theForm. How did we get away with this one? If you remember I highlighted one line of code as a very important line.That line read:
SetmCallingForm = Nothing
and occurred immediatelyafter the line we used to draw our Labels on the Form. Once the Label is drawnwe do not need the reference to the Form any more, so we destroy it at once,and avoid the “circular reference”.
“A stitch in time….”applies here as well.