Add click event to optionbutton - vba

I have a Outlook 2013 macro where I made a form pop up. All objects (buttons, textboxes, optionsbuttons) get added by the code behind in a form.Load() event.
The form has two option buttons with yes/no. If no is pressed then some other buttons, textboxes should be disabled. My question is how to archieve that a event handler is added to these two buttons?
My programmatically added buttons:
y = y + 30
x = 230
Set btnOppYes = Me.Controls.Add("Forms.OptionButton.1", "btnOppYes")
With btnOppYes
.Caption = "Ja"
.Left = x
.Top = y
.Width = 110
.GroupName = "OppYesNo"
End With
x = x + 110
Set btnOppNo = Me.Controls.Add("Forms.OptionButton.1", "btnOppNo")
With btnOppNo
.Caption = "Nein"
.Left = x
.Top = y
.Width = 110
.GroupName = "OppYesNo"
.Value = True
End With
I tried to add this code, but with no success. There's no msgbox like "test", when I click on the option button:
Sub btnOppYes_Click()
MsgBox ("test")
End Sub

The event isn't associated with the button when its added dynamically and Outlook doesn't allow access to the CodeModule like Excel does. Here is a workaround.
Paste this code in your userform
Dim clsbtnOpp As New Class1
Private Sub UserForm_Initialize()
Dim btnOppYes As MSForms.OptionButton
Dim btnOppNo As MSForms.OptionButton
y = y + 30
x = 230
Set btnOppYes = Me.Controls.Add("Forms.OptionButton.1", "btnOppYes")
With btnOppYes
.Caption = "Ja"
.Left = x
.Top = y
.Width = 110
.GroupName = "OppYesNo"
End With
Set clsbtnOpp.btnOppYes = btnOppYes
x = x + 110
Set btnOppNo = Me.Controls.Add("Forms.OptionButton.1", "btnOppNo")
With btnOppNo
.Caption = "Nein"
.Left = x
.Top = y
.Width = 110
.GroupName = "OppYesNo"
.Value = True
End With
Set clsbtnOpp.btnOppNo = btnOppNo
End Sub
And create a new class called Class1 and paste this code in it
Public WithEvents btnOppYes As MSForms.OptionButton
Public WithEvents btnOppNo As MSForms.OptionButton
Private Sub btnOppYes_click()
MsgBox "You clicked yes"
UserForm1.TextBox1.Enabled = True
End Sub
Private Sub btnOppNo_click()
MsgBox "You clicked no"
UserForm1.TextBox1.Enabled = False
End Sub
Run your form and test. You can play around with this and tailor it to your needs

Related

How to capture event of multiple Dynamic control in VBA

From a previous post I learned a way to populate a userform with a grid of textboxes:
Dim Grid(1 To 10, 1 To 5) As MSForms.TextBox
Private Sub UserForm_Initialize()
Dim x As Long
Dim y As Long
For x = 1 To 10
For y = 1 To 5
Set Grid(x, y) = Me.Controls.Add("Forms.Textbox.1")
With Grid(x, y)
.Name = "TextBox_" & x & "_" & y
.Width = 50
.Height = 20
.Left = y * .Width
.Top = x * .Height
.SpecialEffect = fmSpecialEffectFlat
.BorderStyle = fmBorderStyleSingle
End With
Next y
Next x
End Sub
Now, I need to run certain code when I change the contents of any textbox in columns 5 and 6. But since the textbox won't exist until after Initialize is run, their Change events don't exist either.
So I need to either:
Write the change events in advance, since I know the names of the textboxes in advance.
Use an event that will trigger whenever I click any textbox, and be able to identify the textbox in question.
If the only way to do this is by using a class module, please explain it carefully, since I've never actually used one.
EDIT: The answers from #TinMan and #Storax work a little too well. The code reacts to every keystroke in the textbox, but I really need to wait until the user is finished typing. There's no "Exit" event for the textbox when it's in the class module. Any thoughts?
You'll need to create a class to listen for the changes.
Class: TextBoxListener
Public WithEvents TextBox As MSForms.TextBox
Public UserForm As Object
Private Sub TextBox_Change()
UserForm.TextBoxGridChange TextBox
End Sub
Userform
With a few modifications you can use the Grid() to hold the TextBoxListeners references.
Option Explicit
Private Grid(1 To 10, 1 To 5) As New TextBoxListener
Public Sub TextBoxGridChange(TextBox As MSForms.TextBox)
Debug.Print TextBox.Value
End Sub
Private Sub UserForm_Initialize()
Dim x As Long
Dim y As Long
For x = 1 To 10
For y = 1 To 5
With Grid(x, y)
Set .TextBox = Me.Controls.Add("Forms.Textbox.1")
Set .UserForm = Me
With .TextBox
.Name = "TextBox_" & x & "_" & y
.Width = 50
.Height = 20
.Left = y * .Width
.Top = x * .Height
.SpecialEffect = fmSpecialEffectFlat
.BorderStyle = fmBorderStyleSingle
End With
End With
Next y
Next x
End Sub
Just a simple example how the class could look like for the textboxes. I named the class clsTextBoxes
Option Explicit
Public WithEvents tb As MSForms.TextBox
' just to keep track of the box in the grid
Public x As Long
Public y As Long
' Just a simple example for the change event.
' you could use x and y to tell the different textboxes apart
Private Sub tb_Change()
Debug.Print tb.Text, x, y
End Sub
You have to adjust your code in the userform like that
Option Explicit
Dim Grid(1 To 10, 1 To 5) As MSForms.TextBox
' Collection to save all the textboxes in the grid
Dim colTxt As New Collection
Private Sub UserForm_Initialize()
Dim x As Long
Dim y As Long
Dim cTxt As clsTextBoxes
For x = 1 To 10
For y = 1 To 5
Set Grid(x, y) = Me.Controls.Add("Forms.Textbox.1")
' create an new clsTextBoxes
Set cTxt = New clsTextBoxes
' save a pointer to the just created textbox
Set cTxt.tb = Grid(x, y)
' store the postion
cTxt.x = x
cTxt.y = y
' add it to the collection
colTxt.Add cTxt
With Grid(x, y)
.Name = "TextBox_" & x & "_" & y
.Width = 50
.Height = 20
.Left = y * .Width
.Top = x * .Height
.SpecialEffect = fmSpecialEffectFlat
.BorderStyle = fmBorderStyleSingle
End With
Next y
Next x
End Sub
Look at the comments for a short explanation

add multiple dynamic controls to userform and assign different event handlers to them

I am trying to add multiple spin button with each of them linked to different sets of cells that have some values assigned to them. i have tried adding the controls and use a class module to add the event handler procedure to them but to no avail. any help would be appreciated.
Dim spinArray() As New Class1
Private Sub UserForm_Initialize()
Dim i As Long
Dim quantspin As MSForms.SpinButton
subassy_break.Height = pnum1 * 70
subassy_break.Width = 500
With Label_Var
.Top = 15
.Left = subassy_break.Width - (Label_Var.Width + 15)
.Caption = msg
.AutoSize = True
.Font.Bold = True
End With
With UserForm
For i = 1 To pnum1
Set quantspin = Me.Controls.Add("Forms.spinbutton.1", "Quantity_Count_" & i)
With quantspin
.Min = 0
.SmallChange = 1
.Max = 1
.Left = 200
.Top = subassy_break.height- pnum1*20
End With
Next i
End With
End Sub
also the new class module that i have added is
Public WithEvents spinevents As MSForms.SpinButton
Private Sub spinevents_change()
For i = 1 To pnum1
Cells(userow + i, usecol).Value = spinevents.Value
Next i
End Sub
This should help you figure it out:
clsSpin
Public WithEvents spinevents As MSForms.SpinButton
Public TargetCell As Range '<<the cell to operate on
Private Sub spinevents_change()
TargetCell.Value = spinevents.Value
End Sub
UserForm (simplified to show the relevant parts)
Dim spinners As Collection '<<< holds your clsSpin objects
Private Sub UserForm_Initialize()
Dim i As Long, s As clsSpin, quantspin
Set spinners = New Collection
For i = 1 To 5
Set quantspin = Me.Controls.Add("Forms.spinbutton.1", "Quantity_Count_" & i)
With quantspin
.Min = 0
.SmallChange = 1
.Max = 100
.Left = 20 * i
.Top = 50
End With
'create a new instance of the class, set some properties
' and add it to the collection
Set s = New clsSpin
Set s.spinevents = quantspin
Set s.TargetCell = ThisWorkbook.Sheets(1).Cells(i, 1)
spinners.Add s
Next i
End Sub

How to pass control values between userform events?

I'm struggling with a userform (called Label_Select) that I created.
I'm initializing the userform with some text boxes and check boxes and assigning some values in them.
Then I have a OK button on the userform that was created at design mode (I can create this button at runtime if that helps).
I need to use the text boxes and check boxes values in the code of the OK_Click, refer below.
Currently I get a "Sub or Function not defined" for the OK_Click sub.
How can I pass the text boxes and check boxes values between the userform initialize code and other click events of the userform?
Thank you for your responses
Private Sub UserForm_Initialize()
Dim LotBox(500) As MSForms.TextBox
Dim SensorCheckBox(500) As MSForms.CheckBox
For i = 1 To 4
For j = 1 To 4
k = i + (4 * j)
Set LotBox(k - 4) = Label_Select.Controls.Add("Forms.TextBox.1")
Set SensorCheckBox(k - 4) = Label_Select.Controls.Add("Forms.CheckBox.1")
With LotBox(k - 4)
.Top = 250 + i * 25
.Left = (j * 80) - 50
.Width = 40
.Height = 30
.Font.Size = 14
.Font.Name = "Calibri"
.SpecialEffect = fmSpecialEffectSunken
.Value = k
.AutoSize = True
End With
With SensorCheckBox(k - 4)
.Top = 246 + i * 25
.Left = (j * 80) - 8
.Height = 30
End With
If LotBox(k - 4).Value = " " Then
Label_Select.Controls.Remove LotBox(k - 4).Name
Label_Select.Controls.Remove SensorCheckBox(k - 4).Name
End If
Next j
Next i
End Sub
Private Sub OK_Click()
Worksheets("Sheet1").Cells(1,1)=LotBox(1).Value
Worksheets("Sheet1").Cells(2,1)=SensorCheckBox(1).Value
End Sub
Try making LotBox and SensorCheckBox public variables
You've declared LotBox and SensorCheckBox within UserForm_Initialize, so as soon as that sub ends they will both go out of scope.
Move them up to the top of the module as Global variables.

Combobox Event listener for dynamic user interface

I'm basically trying to display different sets of textboxes on a userform depending on the value taken by a combobox. I have created a class module call CControlEvents in which I describe the events that should occur when I change the value of the combobox :
Private WithEvents mclsCbx As MSForms.ComboBox
Private mMyProperty As Integer
Public Property Set Cbx(ByVal clsCbx As MSForms.ComboBox): Set mclsCbx = clsCbx: End Property
Public Property Get Cbx() As MSForms.ComboBox: Set Cbx = mclsCbx: End Property
Public Property Get MyProperty() As Integer
MyProperty = mMyProperty
End Property
Public Property Let Transition(Value As Integer)
mMyProperty = Value
End Property
Private Sub mclsCbx_Change()
'Options NUM
Set Lbl4 = UserForm1.Frame1.Controls.Add("Forms.Label.1", "lbl3")
Set txtB4 = UserForm1.Frame1.Add("Forms.TextBox.1")
With txtB4
.name = "Unit" & mMyProperty
.Height = 15
.Width = 50
.Left = 500
.Top = 10 * mMyProperty * 3
.Value = "txtB4NUM"
End With
Lbl4.Caption = "Unité : "
Lbl4.Top = txtB4.Top
Lbl4.Left = 360
'Options LIST
Set Lbl3 = UserForm1.Frame1.Controls.Add("Forms.Label.1", "lbl3")
Set txtB3 = UserForm1.Frame1.Add("Forms.TextBox.1")
With txtB3
.name = "specMin" & mMyProperty
.Height = 15
.Width = 200
.Left = 410
.Top = 10 * mMyProperty * 3
.Value = "txtB3LIST"
End With
Lbl3.Caption = "Eléments : "
Lbl3.Top = txtB3.Top
Lbl3.Left = 360
If (Me.Cbx.Value = "NUM") Then
txtB3.Visible = False
txtB4.Visible = True
Else
If (Me.Cbx.Value = "LIST") Then
txtB4.Visible = False
txtB3.Visible = True
End If
End If
End Sub
In the userform's code, I dynamically add such comboboxes :
'Create the combobox
Set oleCbx = Frame1.Add("Forms.ComboBox.1") 'Bug at this line
With oleCbx
.name = "list" & i
.Height = 15
.Width = 100
.Left = 70
.Top = 10 * i * 3
.AddItem "NUM"
.AddItem "LIST"
End With
Set gclsControlEvents = New CControlEvents
Set gclsControlEvents.Cbx = oleCbx
Let gclsControlEvents.Transition = i
The problem is that when I change the value of the combobox, it displays the corresponding textboxes but it doesn't remove the others, whereas
If (Me.Cbx.Value = "LIST") Then
txtB4.Visible = False
txtB3.Visible = True
is supposed to set one of the textboxes to visible and the other one to invisible if the value of the combobox is "LIST".
EDIT : The correct solutions to this issue was given by #Rory in a comment.
Setting the txtB4.Visible = "False" should do it. Maybe try to repaint the form. Try these after the visible = "false".
UserForm1.Repaint
Or a doevents to let windows finish any updates.
DoEvents
If it is the value check that is failing, try the .Text property.
If (Me.Cbx.Text = "LIST") Then
.Text gives you what is displayed.
.Value gives you the assigned value to the list item.
Distinction between using .text and .value in VBA Access
Note the comment in that post about combobox.

VBA - Trapping events on dynamically created Textbox

I am writing a VBA application in Excel. I have a Userform that dynamically builds itself based upon the data contained in one of the worksheets.
All of the code that creates the various comboboxes, textboxes and labels is working.
I created a class module to trap OnChange events for the Comboboxes, and again this works as expected.
Now I have a need to trap OnChange events for some of the textboxes, so I created a new class module modelled on that for the comboboxes to trap the events.
Public WithEvents tbx As MSForms.TextBox
Sub SetTextBox(ctl As MSForms.TextBox)
Set tbx = ctl
End Sub
Public Sub tbx_Change()
Dim LblName As String
MsgBox "You clicked on " & tbx.Name, vbOKOnly
End Sub
The message box is just so that I can confirm it works before I go further.
The problem I'm getting is in the UserForm code module:
Dim TBox As TextBox
Dim tbx As c_TextBoxes
'[...]
Set TBox = lbl
Set tbx = New c_TextBoxes
tbx.SetTextBox lbl
pTextBoxes.Add tbx
This throws up a type mismatch error at Set TBox=lbl. It's the exact same code that works fine for the ComboBox, just with the variables given approriate names. I've stared at this for 2 hours.
Anyone got any ideas? Thanks for any pointers.
Edit - Here's the full userform module that I'm having trouble with:
Private Sub AddLines(FrameName As String, PageName As String)
Dim Counter As Integer, Column As Integer
Dim obj As Object
Dim CBox As ComboBox
Dim cbx As c_ComboBox
Dim TBox As TextBox
Dim tbx As c_TextBoxes
Dim lbl As Control
Set obj = Me.MultiPage1.Pages(PageName).Controls(FrameName)
If pComboBoxes Is Nothing Then Set pComboBoxes = New Collection
If pTextBoxes Is Nothing Then Set pTextBoxes = New Collection
For Counter = LBound(Vehicles) To UBound(Vehicles)
For Column = 1 To 8
Select Case Column
Case 1
Set lbl = obj.Add("Forms.Label.1", "LblMachine" & FrameName & Counter, True)
Case 2
Set lbl = obj.Add("Forms.Label.1", "LblFleetNo" & FrameName & Counter, True)
Case 3
Set lbl = obj.Add("Forms.Label.1", "LblRate" & FrameName & Counter, True)
Case 4
Set lbl = obj.Add("Forms.Label.1", "LblUnit" & FrameName & Counter, True)
Case 5
Set lbl = obj.Add("Forms.ComboBox.1", "CBDriver" & FrameName & Counter, True)
Case 6
Set lbl = obj.Add("Forms.Label.1", "LblDriverRate" & FrameName & Counter, True)
Case 7
Set lbltbx = obj.Add("Forms.TextBox.1", "TBBookHours" & FrameName & Counter, True)
Case 8
Set lbl = obj.Add("Forms.Label.1", "LblCost" & FrameName & Counter, True)
End Select
With lbl
Select Case Column
Case 1
.Left = 1
.Width = 60
.Top = 10 + (Counter) * 20
.Caption = Vehicles(Counter).VType
Case 2
.Left = 65
.Width = 40
.Top = 10 + (Counter) * 20
.Caption = Vehicles(Counter).VFleetNo
Case 3
.Left = 119
.Width = 50
.Top = 10 + (Counter) * 20
.Caption = Vehicles(Counter).VRate
Case 4
.Left = 163
.Width = 30
.Top = 10 + (Counter) * 20
.Caption = Vehicles(Counter).VUnit
Case 5
.Left = 197
.Width = 130
.Top = 10 + (Counter) * 20
Set CBox = lbl 'WORKS OK
Call CBDriver_Fill(Counter, CBox)
Set cbx = New c_ComboBox
cbx.SetCombobox CBox
pComboBoxes.Add cbx
Case 6
.Left = 331
.Width = 30
.Top = 10 + (Counter) * 20
Case 7
.Left = 365
.Width = 30
.Top = 10 + (Counter) * 20
Set TBox = lbl 'Results in Type Mismatch
Set tbx = New c_TextBoxes
tbx.SetTextBox TBox
pTextBoxes.Add tbx
Case 8
.Left = 400
.Width = 30
.Top = 10 + (Counter) * 20
End Select
End With
Next
Next
obj.ScrollHeight = (Counter * 20) + 20
obj.ScrollBars = 2
End Sub
And here's the c_Combobox class module:
Public WithEvents cbx As MSForms.ComboBox
Sub SetCombobox(ctl As MSForms.ComboBox)
Set cbx = ctl
End Sub
Public Sub cbx_Change()
Dim LblName As String
Dim LblDriverRate As Control
Dim i As Integer
'MsgBox "You clicked on " & cbx.Name, vbOKOnly
LblName = "LblDriverRate" & Right(cbx.Name, Len(cbx.Name) - 8)
'MsgBox "This is " & LblName, vbOKOnly
'Set obj = Me.MultiPage1.Pages(PageName).Controls(FrameName)
Set LblDriverRate = UFBookMachines.Controls(LblName)
For i = LBound(Drivers) To UBound(Drivers)
If Drivers(i).Name = cbx.Value Then LblDriverRate.Caption = Drivers(i).Rate
Next
End Sub
And finally, here's the c_TextBoxes class module:
Public WithEvents tbx As MSForms.TextBox
Sub SetTextBox(ctl As MSForms.TextBox)
Set tbx = ctl
End Sub
Public Sub tbx_Change()
Dim LblName As String
'Does nothing useful yet, message box for testing
MsgBox "You clicked on " & tbx.Name, vbOKOnly
End Sub
After some quick testing, I am able to reproduce your error if I declare TBox as TextBox. I do not get an error if I declare TBox as MSForms.TextBox. I would recommend declaring all your TextBox variables with the MSForms qualifier.
Test code is situated similar to yours. I have a MultiPage with a Frame where I am adding a Control.
Private Sub CommandButton1_Click()
Dim obj As Object
Set obj = Me.MultiPage1.Pages(0).Controls("Frame1")
Dim lbl As Control
Set lbl = obj.Add("Forms.TextBox.1", "txt", True)
If TypeOf lbl Is TextBox Then
Debug.Print "textbox found1" 'does not execute
End If
If TypeOf lbl Is MSForms.TextBox Then
Debug.Print "textbox found2"
Dim txt1 As MSForms.TextBox
Set txt1 = lbl 'no error
End If
If TypeOf lbl Is MSForms.TextBox Then
Debug.Print "textbox found3"
Dim txt As TextBox
Set txt = lbl 'throws an error
End If
End Sub
I am not sure why the qualifier is needed for TextBox and not ComboBox. As you can see above, a good test for this is the If TypeOf ... Is ... Then to test which objects are which types. I included the first block to show that lbl is not a "bare" TextBox, but, again, I have no idea why that is. Maybe there is another type of TextBox out there that overrides the default declaration?