Adding Run-Time Event Handlers Dynamically
"Erick" had a problem figuring out how to record the sequence of clicks in a program. The program displayed a matix of CheckBox controls but the key information that was needed was the sequence that they were checked.
Piece of cake, right? Just add an event subroutine that handles the Click event for all of the Checkboxes and capture the sequence.
Private Sub SequenceCapture( _ ByVal sender As Object, - ByVal e As System.EventArgS) _ Handles CheckBox01.CheckChanged, _ Checkbox02.CheckChanged, _
... and so forth followed by ...
CheckBoxSequence(CheckSeq) = sender.name CheckSeq += 1
Not so fast there, sailor. There's more to the problem.
The matrix of CheckBox controls is variable. The number of rows and columns is entered into textboxes and the CheckBox matrix is created in a double loop:
For RowCount = 1 To RowSize
For ColCount = 1 To ColSize
Dim ChkBox As New CheckBox
ChkBox.Name = "Checkbox" & i.ToString + 1
ChkBox.Location = _
New Point(20 * ColCount, 20 * RowCount)
ChkBox.Size = New Size(20, 20)
Me.Controls.Add(ChkBox)
i += 1
Next
Next
Adding handlers, a "compile-time" solution, doesn't work now. For one thing, you don't even know how many CheckBox controls there will be at run-time. We need a run-time solution instead.
The AddHandler statement fills the bill just fine. Use AddressOf to point to the right subroutine to handle the CheckedChanged event for each CheckBox component.
Dim ChkBox As New CheckBox AddHandler ChkBox.CheckedChanged, AddressOf SequenceCapture
Here's the source for a complete example program:
Public Class Form1
Dim CheckBoxSequence() As String
Dim CheckSeq As Integer = 0
Private Sub Form1_Load( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles MyBase.Load
Dim CheckBoxMatrix() As String
Dim MatrixSize, i, RowCount, ColCount As Integer
Dim RSize, CSize As Integer
RSize = CInt(RowSize.Text)
CSize = CInt(ColSize.Text)
MatrixSize = RSize * CSize
ReDim CheckBoxMatrix(MatrixSize - 1)
ReDim CheckBoxSequence(MatrixSize - 1)
i = 0
For RowCount = 1 To RSize
For ColCount = 1 To CSize
CheckBoxMatrix(i) = i.ToString
Dim ChkBox As New CheckBox
AddHandler ChkBox.CheckedChanged, _
AddressOf SequenceCapture
ChkBox.Name = "Checkbox" & i.ToString + 1
ChkBox.Location = _
New Point(20 * ColCount, 20 * RowCount)
ChkBox.Text = ""
ChkBox.Size = New Size(20, 20)
Me.Controls.Add(ChkBox)
i += 1
Next
Next
End Sub
Private Sub SequenceCapture( _
ByVal sender As Object, _
ByVal e As System.EventArgs)
CheckBoxSequence(CheckSeq) = sender.name
CheckSeq += 1
End Sub
Private Sub Button1_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles Button1.Click
For i As Int16 = 0 To CheckSeq
Label1.Text = _
Label1.Text & CheckBoxSequence(i) & vbCrLf
Next
End Sub
End Class


Excellent. Great use of OO and good event model. I would even go a few steps further. Allow row/column count to vary, create a custom checkbox class with published events the form can subscribe.
I didn’t see anything for handling if someone ‘unchecks’ a box.
For me, the form should do 2 things – get data from the user, show data to the user and provide navigation.
I copied and pasted the complete code, on the form I placed a Button, a Label and two text boxes one called RowSize and one ColSize, but when I try to run the program I get an error :-
Conversion from String “”to type’Integer’ is not valid.
Mark,
Great comments. The example was designed to illustrate a technical point and is a long way from being a production application.
Graham,
Ummmm … Yeah …. The textboxs have to be initialized with a numeric value since the processing takes place in the Form1.Load event. When I’m trying to be both entertaining and informative, I sometimes leave details like this out.
By the way … how are you coming on checking out the flowchart I sent to you?
Thanks for that, the flow chart is fine the only thing I would add is the ability to search, so that I do not need to go through the complete list to find the site/program I’m after.
I did reply to your email, must be floating around cyberspace somewhere?
Ok, it’s a great example, but not to a novice.
First I needed to initialise the text boxes, after doing so the program ran, now I change the value of the textbox and get an error, so I alter the code and put the part that generates the checkboxes in to it’s own sub which will be called when the form loads and again on ColSize_TextChanged and again on RowSize_TextChanged. Novice ‘There that should work’ Expert ‘No it won’t’
Use step through and find that after dim statements we jump to ColSize_TextChanged then to Private Sub MakeBoxes() Problem ColSize = 4 but RowSize = “” so we get an error.
Below is my code and to me it should work (Rememeber Novice) so why doesn’t it and why does it jump from dim to TextChanged when it hasn’t yet.
Public Class Form1
Dim CheckBoxSequence() As String
Dim CheckSeq As Integer = 0
Dim CheckBoxMatrix() As String
Dim MatrixSize, i, RowCount, ColCount As Integer
Dim RSize, CSize As Integer
Private Sub Form1_Load( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles MyBase.Load
MakeBoxes()
End Sub
Private Sub SequenceCapture( _
ByVal sender As Object, _
ByVal e As System.EventArgs)
CheckBoxSequence(CheckSeq) = sender.name
CheckSeq += 1
End Sub
Private Sub Button1_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles Button1.Click
For i As Int16 = 0 To CheckSeq
Label1.Text = _
Label1.Text & CheckBoxSequence(i) & vbCrLf
Next
End Sub
Private Sub MakeBoxes()
RSize = CInt(RowSize.Text)
CSize = CInt(ColSize.Text)
MatrixSize = RSize * CSize
ReDim CheckBoxMatrix(MatrixSize – 1)
ReDim CheckBoxSequence(MatrixSize – 1)
i = 0
For RowCount = 1 To RSize
For ColCount = 1 To CSize
CheckBoxMatrix(i) = i.ToString
Dim ChkBox As New CheckBox
AddHandler ChkBox.CheckedChanged, _
AddressOf SequenceCapture
ChkBox.Name = “Checkbox” & i.ToString + 1
ChkBox.Location = _
New Point(20 * ColCount, 20 * RowCount)
ChkBox.Text = “”
ChkBox.Size = New Size(20, 20)
Me.Controls.Add(ChkBox)
i += 1
Next
Next
End Sub
Private Sub ColSize_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ColSize.TextChanged
MakeBoxes()
End Sub
Private Sub RowSize_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles RowSize.TextChanged
MakeBoxes()
End Sub
End Class
“First I needed to initialise the text boxes, after doing so the program ran, now I change the value of the textbox and get an error, so I alter the code …”
Ummmm … There we part company. I tried it again and I don’t get an error when I change the value of the text box. The size of the Checkbox matrix doesn’t change. (It’s created in the Form Load event.) But I don’t get an error. So I don’t know what kind of error you might be seeing here.
Anyway …
I checked out your code and I have to admit that you created a very sneaky bug!! I had to scratch my head twice before I found it.
In fact, this is SUCH a good bug, I’m a little reluctant to just spill the answer. I’m thinking that I might make another blog out of it. I need to go to a meeting and I’ll think about it. In the meantime, you might take another look and see if you can figure it out.
Sorry, you’re right, there is no error it’s just that the matrix does not change, what tricked me was the line in your article;
The matrix of CheckBox controls is variable. The number of rows and columns is entered into textboxes and the CheckBox matrix is created in a double loop:
I read it as entering a number in the textboxes, changes the Matrix, which is what I’m trying to achieve now. Will look for the very sneaky bug.
I’m writing a new blog (with illustrations) about it right now.