creating user defined form controls in excel vba - vba

i have an excel user form with a comboBox for imputing no of pumps and the combo box contains drop down list from 1 to 30. how can i achieve creation of new form controls depending on the users selection? for instance if the user selects 5 i want to create 5 labels named pump1, pump2 to pump5 all with radio buttons for product selection.

You can do this in a number of ways, the simplest being to, add in all 30 buttons and hide\unhide them with Private Sub ComboBox1_Change() subroutine. If that does not meet your needs, then will you probably want to use the Microsoft Visual Basic for Applications Extensibility 5.3 reference, to add in buttons at runtime, then modify them on the same event.
Example UserForm Code
'' Corresponding UserForm has *only* a combobox called `ComboBox1` located at left=6, top=6.
'' Form-level constants
Private Const maxNumBtns As Integer = 12
Private Const defaultComBoxNum As Integer = 5
'' Update buttons
Private Sub ComboBox1_Change()
Dim comp As Boolean
'' prevent flashing
Let Application.ScreenUpdating = False
For i = 1 To maxNumBtns
With Me.Controls.Item(getBtnName(i))
Let comp = i <= CInt(Me.ComboBox1.Value)
'' ensure hidden buttons are not pressed
Let .Value = comp And .Value
Let .Enabled = comp
Let .Visible = comp
End With
Next
Let Application.ScreenUpdating = True
End Sub
'' Set up UserForm
Private Sub UserForm_Initialize()
Dim i As Integer, _
btn As String
For i = 1 To maxNumBtns
'' add line to combobox
Call Me.ComboBox1.AddItem(i)
Let btn = getBtnName(i)
'' add button
Call Me.Controls.Add( _
bstrProgID:="Forms.OptionButton.1", _
Name:=btn, _
Visible:=True)
'' Format the button
With Me.Controls(btn)
Let .Left = 12
Let .Top = 30 + 17 * i
Let .Width = 70
Let .Height = 15
Let .Caption = "Pump #" & i
End With
Next i
'note: this calls combobox1_change
Let Me.ComboBox1.Value = defaultComBoxNum
End Sub
'' helper function to ensure naming stays the same
Private Function getBtnName(ByVal index As Integer) As String
Let getBtnName = " opBtn" & index
End Function
If you are looking to have something like a combobox with a check box option, you should use a listbox. (you will not need the extensibility reference for this version)
Unfortunately, however, it is not possible to hide a listbox row, so this option requires removing all entries in the listbox when the combobox val is changed.
The methods below correspond to a useform, useform1, with a comboBox, pumpComboBox, a listBox, pumpListBox, and two commandButtons, OkButton and CancelButton.
Option Explicit
'' Calling code
'
'With New Userform1
' .Show
' If Not .IsCancelled Then
' '...
' End If
'End With
Private Const maxChecks As Integer = 12
Private Const defaultChecks As Integer = 5
Private checked() As Boolean
Private cancelled As Boolean
Private updating As Boolean
Private Sub UserForm_Initialize()
Dim i As Integer
ReDim checked(0 To maxChecks - 1) As Boolean
'' Add item to combobox
For i = 1 To maxChecks Step 1
Call Me.pumpComboBox.AddItem(i)
Let checked(i - 1) = False
Next i
'' format listbox
With Me.pumpListBox
Let .ListStyle = fmListStyleOption
Let .MultiSelect = fmMultiSelectMulti
End With
'note: this calls pumpComboBox_change
Let Me.pumpComboBox.value = defaultChecks
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode = VbQueryClose.vbFormControlMenu Then
Let cancelled = True
Call OnCancel
End If
End Sub
Private Sub pumpComboBox_Change()
Dim i As Integer
'' prevent flashing
Let Application.ScreenUpdating = False
'' make sure values are not cleared when changing number of options
Let updating = True
Call Me.pumpListBox.Clear
For i = 1 To Me.pumpComboBox.value
Call Me.pumpListBox.AddItem("Pump #" & i)
Let Me.pumpListBox.Selected(i - 1) = checked(i - 1)
Next i
Let updating = False
Let Application.ScreenUpdating = True
End Sub
Private Sub pumpListBox_Change()
If updating Then Exit Sub
Dim i As Integer
For i = 0 To maxChecks - 1
If i < UBound(Me.pumpListBox.List) Then
Let checked(i) = Me.pumpListBox.Selected(i)
Else
'' remove options when reducing number of options
Let checked(i) = False
End If
Next i
End Sub
Public Property Get IsCancelled() As Boolean
Let IsCancelled = cancelled
End Property
Private Sub OkButton_Click()
Call Hide
End Sub
Private Sub CancelButton_Click()
Call OnCancel
End Sub
Private Sub OnCancel()
Let cancelled = True
Call Hide
End Sub

Related

Code to account for all checkboxes in a userform?

I have code on a userform that contains several checkboxes and several DTPickers.
The code looks like so:
Private Sub CheckBox11_Click()
If CheckBox11.Value = True Then
DTPicker22.Enabled = True
Else
DTPicker22.Enabled = False
End If
End Sub
Private Sub CheckBox12_Click()
If CheckBox12.Value = True Then
DTPicker24.Enabled = True
Else
DTPicker24.Enabled = False
End If
End Sub
The Userform contains a lot of checkboxes that have clauses next to them. Upon their completion the DTPicker will enable entering the date of completion.
Whilst this does what I want, it only enables one DTPicker when the checkbox is ticked per private sub. There has to be some way to make this so I wouldn't need to create different private subs for every checkbox click event.
Could you also tell me where to put it, as in, what event?
A "control array" is the typical approach for something like this.
See:
http://www.siddharthrout.com/index.php/2018/01/15/vba-control-arrays/
eg:
Class module clsEvents
Option Explicit
'Handle events for a checkbox and a date control, associated with a worksheet cell
Private WithEvents m_CB As MSForms.CheckBox
Private WithEvents m_DP As DTPicker
Private m_dateCell As Range
'set up the controls and the cell
Public Sub Init(cb As MSForms.CheckBox, dp As DTPicker, rng As Range)
Set m_CB = cb
Set m_DP = dp
Set m_dateCell = rng
If rng.Value > 0 Then
cb.Value = True
m_DP.Value = rng.Value
Else
cb.Value = False
End If
m_DP.CustomFormat = "dd/MM/yyyy"
End Sub
Private Sub m_CB_Change()
m_DP.Enabled = (m_CB.Value = True)
End Sub
Private Sub m_DP_Change()
m_dateCell.Value = m_DP.Value 'update the cell
End Sub
Userform:
Option Explicit
Dim colObj As Collection 'needs to be a Global to stay in scope
Private Sub UserForm_Activate()
Dim obj As clsEvents, i As Long, ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet1")
Set colObj = New Collection
'loop over controls and create a class object for each set
' 3 pairs of controls on my test form...
For i = 1 To 3
Set obj = New clsEvents
obj.Init Me.Controls("CheckBox" & i), _
Me.Controls("DTPicker" & i), _
ws.Cells(i, "B")
colObj.Add obj
Next i
End Sub
The first thing I'd recommend is following a proper naming convention. "CheckBox11" and "DTPciker1" are really vague and once you get further into your code, you'll forget which control is which. I would recommend naming them something that relates the two control together, like "firstDate" and "firstDateDTP". My alternate answer below uses this approach.
You could make a public function that enables the DTPicker based upon the checkbox's value.
Public Function EnableDTPicker(myPicker as String, enableBool as Boolean)
UserFormName.Controls(myPicker).Enabled = enableBool
End Function
Then, you can call the function in your CheckBox123_Click() subs like this:
Private Sub CheckBox123_Click()
EnableDTPicker("thePickerName", CheckBox123.Value)
End Sub
Alternatively, you could make a timer event that runs x number of seconds that just loops through the controls and performs the checks as needed. See this page on how to set up the timer. Using the code in the link shown, You could do something along the lines of:
'Put this in Workbook events
Private Sub Workbook_Open()
alertTime = Now + TimeValue("00:00:01")
Application.OnTime alertTime, "EventMacro"
UserForm1.Show
End Sub
'Put this in a Module
Public Sub EventMacro()
With UserForm1
For each ctrl in .Controls
If TypeName(ctrl) = "CheckBox" Then
'The code below assumes the naming convention outlined above is followed
.Controls(ctrl.Name & "DTP").Enabled = ctrl.Value
End If
Next ctrl
End With
alertTime = Now + TimeValue("00:00:01")
Application.OnTime alertTime, "EventMacro"
End Sub

Userform closes after "End Sub" without ever calling "Unload Me"

I have a userform (baseUF) that has multiple pages and buttons that all do different things. I have this baseUF being modeless because I want the user to be able to play with the sheet without closing the userform and losing all of the data they input. However, I started having a problem that might be due to the modeless nature of the baseUF.
There are other userforms that can be called from the baseUF. One executes with no issue by double clicking a textbox. However, the other userform is loaded after a button click. Once that button click sub is finished, the baseUF closes after the Exit Sub OR End Sub line. I don't remember this happening in the past and it doesn't happen with any other button click subs.
Does anybody have an idea what the issue could be? I'm pretty lost because I don't have a command to close the baseUF anywhere in that sub. Below is some code to show what is happening:
This sub is connected to a button on the spreadsheet to open the baseUF (the code is in a module).
Sub Button1_Click()
' show the userform
baseUF.Show vbModeless
End Sub
And this is the sub in the baseUF that calls an additional userform (LoadBox) which seems to be the issue.
Private Sub LoadQuery_Click()
' I Dim a bunch of stuff here
' if there are no saved queries, alert the user
If saveSht.Range("B3").Value = "" Then
MsgBox "No saved queries!"
Exit Sub
' if there is only one saved query, add it to the array and pop up the userform that allows for the user to select which save to load
ElseIf saveSht.Range("B4").Value = "" Then
save_names = saveSht.Range("B3").Value
LoadBox.Show
' otherwise, add all of the save names to the array and pop up that userform
Else
save_names = saveSht.Range(saveSht.Range("B3"),saveSht.Range("B3").End(xlDown)).Value
LoadBox.Show
End If
' if the user didn't select a save to load, stop trying to make stuff happen
If load_name = "" Then
' the userform will also close here if this turns out to be true
Exit Sub
End If
' do a bunch of stuff with the selected name here
' and after this line, the userform that contains this code closes
End Sub
EDIT: here is some code showing the two other userforms
This one is the userform with no issue that is called after a textbox is double clicked
Private Sub UserForm_Initialize()
' On start up of this form, populate the listbox with the relevant column names
' Set position
Me.StartUpPosition = 0
Me.Top = baseUF.Top + 0.5 * baseUF.Height - 0.5 * Me.Height
Me.Left = baseUF.Left + 0.5 * baseUF.Width - 0.5 * Me.Width
With FilterSelectionBox
' First grab all of the column names from the main selected table
For i = 0 To baseUF.SelectionBox.ListCount - 1
.AddItem baseUF.SelectionBox.List(i)
Next i
' Then grab all of the column names from the additional tables to be joined
If Not IsVariantEmpty(join_table_cols) Then
For n = 0 To UBound(join_table_cols)
If Not IsEmpty(join_table_cols(n)) Then
For Each col_name In join_table_cols(n)
.AddItem col_name
Next
End If
Next n
End If
End With
End Sub
Private Sub OkButton_Click()
' Initialize the variables
Dim tb As MSForms.TextBox
Dim arr() As String
Dim str As String
' tb is the textbox object that the column names will be pasted in to
Set tb = baseUF.MultiPage1.Pages(baseUF.MultiPage1.Value).Controls(Me.Tag)
' sets the str according to some logic
' This is actually where it gets sent
tb.Value = str
' And close the form
Unload Me
End Sub
And this is the code in the userform with an issue
Private Sub UserForm_Initialize()
' On initialization, populate the combobox with all of the save names present in the spreadsheet
' Set position
Me.StartUpPosition = 0
Me.Top = baseUF.Top + 0.5 * baseUF.Height - 0.5 * Me.Height
Me.Left = baseUF.Left + 0.5 * baseUF.Width - 0.5 * Me.Width
With LoadComb
' If there is more than one save present, go through the array and add each one
If IsArray(save_names) Then
For Each saved_name In save_names
.AddItem saved_name
Next
' Otherwise just add the one
Else
.AddItem save_names
End If
End With
End Sub
Private Sub LoadButton_Click()
' When the user hits the load button, first check if they actually selected anything
If LoadComb.Value = "" Then
' If they didn't, yell at them
MsgBox "No saved query selected!"
Else
' Otherwise, save the name to a global variable
load_name = LoadComb.Value
End If
' Close the form
Unload Me
End Sub
Whenever something unexpected happens with forms, consider writing End in the immediate window and pressing enter. It will kill all the unkilled instances of a form and generally any variable, thus it would be like a cold restart to the VBA program.
After doing this, it is a good idea to consider a cleaner solution, concerning VBA & UserForms, using some OOP. (Disclaimer - the first article is mine):
http://www.vitoshacademy.com/vba-the-perfect-userform-in-vba/
https://rubberduckvba.wordpress.com/2017/10/25/userform1-show/
https://codereview.stackexchange.com/questions/154401/handling-dialog-closure-in-a-vba-user-form
Although it may seem that you are achieving the same results with more code, the benefits of using this approach are quite a lot in the long term.
This is a small example of the OOP model. Imagine you have a user form like this:
It has only the following controls:
btnRun
btnExit
lblInfo
frmMain (the class)
The code withing the form is the following:
Option Explicit
Public Event OnRunReport()
Public Event OnExit()
Public Property Get InformationText() As String
InformationText = lblInfo.Caption
End Property
Public Property Let InformationText(ByVal value As String)
lblInfo.Caption = value
End Property
Public Property Get InformationCaption() As String
InformationCaption = Caption
End Property
Public Property Let InformationCaption(ByVal value As String)
Caption = value
End Property
Private Sub btnRun_Click()
RaiseEvent OnRunReport
End Sub
Private Sub btnExit_Click()
RaiseEvent OnExit
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode = vbFormControlMenu Then
Cancel = True
Hide
End If
End Sub
The form is with two events, getting caught by the clsSummaryPresenter. The clsSummaryPresenter looks like this:
Option Explicit
Private WithEvents objSummaryForm As frmMain
Private Sub Class_Initialize()
Set objSummaryForm = New frmMain
End Sub
Private Sub Class_Terminate()
Set objSummaryForm = Nothing
End Sub
Public Sub Show()
If Not objSummaryForm.Visible Then
objSummaryForm.Show vbModeless
Call ChangeLabelAndCaption("Press Run to Start", "Starting")
End If
With objSummaryForm
.Top = CLng((Application.Height / 2 + Application.Top) - .Height / 2)
.Left = CLng((Application.Width / 2 + Application.Left) - .Width / 2)
End With
End Sub
Private Sub Hide()
If objSummaryForm.Visible Then objSummaryForm.Hide
End Sub
Public Sub ChangeLabelAndCaption(strLabelInfo As String, strCaption As String)
objSummaryForm.InformationText = strLabelInfo
objSummaryForm.InformationCaption = strCaption
objSummaryForm.Repaint
End Sub
Private Sub objSummaryForm_OnRunReport()
MainGenerateReport
Refresh
End Sub
Private Sub objSummaryForm_OnExit()
Hide
End Sub
Public Sub Refresh()
With objSummaryForm
.lblInfo = "Ready"
.Caption = "Task performed"
End With
End Sub
Finally, we have the modMain, which is the so-called business logic of the form:
Option Explicit
Private objPresenter As clsSummaryPresenter
Public Sub MainGenerateReport()
objPresenter.ChangeLabelAndCaption "Starting and running...", "Running..."
GenerateNumbers
End Sub
Public Sub GenerateNumbers()
Dim lngLong As Long
Dim lngLong2 As Long
tblMain.Cells.Clear
For lngLong = 1 To 10
For lngLong2 = 1 To 10
tblMain.Cells(lngLong, lngLong2) = lngLong * lngLong2
Next lngLong2
Next lngLong
End Sub
Public Sub ShowMainForm()
If (objPresenter Is Nothing) Then
Set objPresenter = New clsSummaryPresenter
End If
objPresenter.Show
End Sub

Excel VBA Userform - Execute Sub when something changes on dynamic comboBox

I created a userform where is a comboBox
Depending which result user chooses, new comboBoxes will appear with new choices to choose from.
Below is the latest test I tried.
How do I make it so that when user changes the value in the new comboBox it will execute a premade function/sub
Code in Form
Dim WB As Workbook
Dim structSheet As Worksheet
Dim tbCollection As Collection
Private Sub UserForm_Activate()
Dim ignoreList(3) As String
ignoreList(0) = "main"
ignoreList(1) = "configurator"
ignoreList(2) = "create structure"
Set WB = Excel.ActiveWorkbook
For Each sheet In WB.Worksheets
If Not isInTable(ignoreList, sheet.Name) Then
supercode_box.AddItem sheet.Name
End If
Next
End Sub
Private Sub supercode_box_Change()
If Not sheetExists(supercode_box.text) Then Exit Sub
Set structSheet = WB.Worksheets(supercode_box.text)
'Dim obj As clsControlBox
topPos = 10
leftPos = 54
ID = 1
' For ID = 1 To 2
Set ComboBox = createProductForm.Controls.add("Forms.ComboBox.1")
With ComboBox
.Name = "comboBoxName"
.Height = 16
.Width = 100
.Left = leftPos
.Top = topPos + ID * 18
.AddItem "test"
.Object.Style = 2
End With
Set tbCollection = New Collection
tbCollection.add ComboBox
'Next ID
End Sub
code in class1 module
Private WithEvents MyTextBox As MSForms.controlBox
Public Property Set Control(tb As MSForms.controlBox)
Set MyTextBox = tb
MsgBox ("did it get here?")
End Property
Public Sub comboBoxName_Change()
MsgBox ("start working ffs")
End Sub
Public Sub comboBoxName()
MsgBox ("?? maybe this?")
End Sub
Judging on your code, the easiest way is to write the value you need in a separate worksheet.
Then make a check, whether it is changed and if it is changed, write the new value and fire the procedure that you want.
In short, something like this:
Sub TestMe()
If Worksheets("Special").Cells(1, 1) = WB.Worksheets(supercode_box.Text) Then
Call TheSpecificSub
End If
Worksheets("Special").Cells(1, 1) = WB.Worksheets(supercode_box.Text)
End Sub

VBA Excel Toggle Button "Latching"

I have the following code corresponding to three ToggleButtons in a VBA Program in Excel. When one button is clicked, it is supposed to stay "pressed" and the other two buttons are supposed to "release". However, with the code I have, I have to click on one button TWICE, once to release the other buttons and the other to keep the original one pressed.
However, if I add "ToggleButtonX.Value = True" to each of the Subs, when one button is clicked, it cannot release even after clicking another button. How could one configure this so that when one clicks on one button, that button stays pressed AND the other buttons get released?
EDIT: I would like to KEEP TOGGLEBUTTONS.
Private Sub ToggleButton1_Click()
ToggleButton2.Value = False
ToggleButton3.Value = False
End Sub
Private Sub ToggleButton2_Click()
ToggleButton1.Value = False
ToggleButton3.Value = False
End Sub
Private Sub ToggleButton3_Click()
ToggleButton1.Value = False
ToggleButton2.Value = False
End Sub
In addition to the OptionButton answer, which is probably easier and cleaner to implement, there's another way to do it as well by using logic that will only change the values of the other two buttons when one button is clicked. The code for the other buttons Click event will fire, but the variable sButton will determine that the code will only fire on the button that was physically pressed by the user.
Also note the use of Not Me.ToggleButton1.Value. This will ensure that button 2 and 3 are the opposite of button 1 with each click. The way your code was written, it would always revert the other buttons to False no matter what if the clicked button was True or False.
Option Explicit
Public sButton As String
Private Sub ToggleButton1_Click()
ButtonLoad 1
End Sub
Private Sub ToggleButton2_Click()
ButtonLoad 2
End Sub
Private Sub ToggleButton3_Click()
ButtonLoad 3
End Sub
Sub ButtonLoad(iButton As Integer)
Select Case iButton
Case 1
If sButton = "" Then
sButton = "1" 'set so that other buttons don't trigger
Me.ToggleButton2.Value = Not Me.ToggleButton1.Value
Me.ToggleButton3.Value = Not Me.ToggleButton1.Value
sButton = "" 'reset for next button click
End If
Case 2
If sButton = "" Then
sButton = "2"
Me.ToggleButton1.Value = Not Me.ToggleButton2.Value
Me.ToggleButton3.Value = Not Me.ToggleButton2.Value
sButton = ""
End If
Case 3
If sButton = "" Then
sButton = "3"
Me.ToggleButton2.Value = Not Me.ToggleButton3.Value
Me.ToggleButton1.Value = Not Me.ToggleButton3.Value
sButton = ""
End If
End Select
End Sub
By calling ToggleButtonx.Value = False you are simulating a click on that button and so it own code will run, and set the value of the button you just clicked to false.
Use instead the MouseDown event:
Private Sub ToggleButton1_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
ToggleButton2.Value = False
ToggleButton3.Value = False
End Sub
Private Sub ToggleButton2_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
ToggleButton1.Value = False
ToggleButton3.Value = False
End Sub
Private Sub ToggleButton3_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
ToggleButton1.Value = False
ToggleButton2.Value = False
End Sub
This is easily done with Option Buttons (If that works with the rest of your implementation)
Private Sub OptionButton1_Click()
OptionButton1.Value = True
OptionButton2.Value = False
OptionButton3.Value = False
End Sub
Private Sub OptionButton2_Click()
OptionButton1.Value = False
OptionButton2.Value = True
OptionButton3.Value = False
End Sub
Private Sub OptionButton3_Click()
OptionButton1.Value = False
OptionButton2.Value = False
OptionButton3.Value = True
End Sub

Excel VBA ComboBox Identification

I have 4+ ComboBoxes on a user form. When they fire, they fire the same event. What I am trying to do is find out which ComboBox triggered the event. The ComboBoxes are created depending on how many components there are. The code generating the ComboBoxes is shown below:
For j = 0 To UBound(ComponentList) - 1
'Set Label
num = j + 1
Set control = UserForm1.Controls.Add("Forms.Label.1", "ComponentLabel" & CStr(num) & ":", True)
With control
.Caption = "Component " & CStr(num)
.Left = 30
.Top = Height
.Height = 20
.Width = 100
.Visible = True
End With
'set ComboBox
Set combo = UserForm1.Controls.Add("Forms.ComboBox.1", "Component" & num & ":", True)
With combo
.List = ComponentList()
.Left = 150
.Top = Height
.Height = 20
.Width = 50
.Visible = True
Set cButton = New clsButton
Set cButton.combobox = combo
coll.Add cButton
End With
Height = Height + 30
Next j
This works well and I can get the value the user selected, BUT I can not find which ComboBox has been used. This code below is the event that it fires (clsButton):
Public WithEvents btn As MSForms.CommandButton
Public WithEvents combobox As MSForms.combobox
Private combolist() As String
Private Sub btn_Click()
If btn.Caption = "Cancel" Then
MsgBox "Cancel"
Unload UserForm1
Variables.ComponentSelectionError = False
ElseIf btn.Caption = "Enter" Then
MsgBox "enter"
Unload UserForm1
Variables.ComponentSelectionError = True
End If
End Sub
Private Sub combobox_Click()
MsgBox combobox.Value
End Sub
This bit of code above was kindly worked on by Doug Glancy to get the events working with the code generated ComboBoxes.
How do I get the ComboBox that triggered the event? i.e. the name or some other form of identification.
I have managed to finally answer my own question after searching over 500 webpages (took a long time)
this is what i used and it works and fires when the certain comboboxes are clicked:
Private Sub combobox_Click()
MsgBox combobox.Value
If combobox = UserForm1.Controls("Component0") Then
MsgBox "Success1"
End If
If combobox = UserForm1.Controls("Component1") Then
MsgBox "Success2"
End If
End Sub
hopefully this can be used for other people who need it.
Within the class .Name will not appear in the intellisense list for the combobox as MSForms.ComboBox does not actually have a name property itself (take a look at it in the F2 object browser), rather that property is provided by the Control base class:
Private Sub combobox_Click()
MsgBox combobox.Value
MsgBox combobox.Name '// no hint but still works
'//cast to a Control to get the formal control interface with .Name
Dim ctrl As Control: Set ctrl = combobox
MsgBox ctrl.Name
End Sub
Maybe reference back to btn.Combobox again? Similar to how you assigned the combobox to the button in the first place, but then in reverse:
set combobox = btn.Combobox
Is there a reason you don't just add a property to your custom class and set that property when you register in the Collection?
For j = 0 To UBound(ComponentList) - 1
'Set Label
num = j + 1
Set control = UserForm1.Controls.Add("Forms.Label.1", "ComponentLabel" & CStr(num) & ":", True)
With control
.Caption = "Component " & CStr(num)
.Left = 30
.Top = Height
.Height = 20
.Width = 100
.Visible = True
End With
'set ComboBox
Set combo = UserForm1.Controls.Add("Forms.ComboBox.1", "Component" & num & ":", True)
With combo
.List = ComponentList()
.Left = 150
.Top = Height
.Height = 20
.Width = 50
.Visible = True
Set cButton = New clsButton
'*******EDIT********
with cButton
.combobox = combo
.Indx = j
end With 'cButton
'*******************
coll.Add cButton
End With
Height = Height + 30
Next j
Class Module
Public WithEvents btn As MSForms.CommandButton
Dim WithEvents mCombobox As MSForms.comboBox
Private combolist() As String
'*******EDIT********
Public Indx As Long
Property Let comboBox(cb As MSForms.comboBox)
Set mCombobox = cb
End Property
'*******************
Private Sub btn_Click()
If btn.Caption = "Cancel" Then
MsgBox "Cancel"
Unload UserForm1
Variables.ComponentSelectionError = False
ElseIf btn.Caption = "Enter" Then
MsgBox "enter"
Unload UserForm1
Variables.ComponentSelectionError = True
End If
End Sub
Private Sub mCombobox_Click()
'*******EDIT********
MsgBox "Combobox " & Indx & Chr(9) & mComboBox.Value
'*******************
End Sub
Since you require many to one mapping of the events, I assume you have a common call-back in your actual code, so you could also do this...
In a Standard Module
Public Sub cbCallBack(ocb As clsButton)
MsgBox ocb.Indx
End Sub
In clsButton (replacing the event handler)
Private Sub mCombobox_Click()
cbCallBack Me
End Sub