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
Related
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
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
I am trying to create a dynamic User_form, where all the Controls are created at Run-Time.
I have 2 array of Combo-Boxes, first array of Combo-Boxes is "Catgeory" (CatCBArr) , and the second array of Combo-Boxes is "Item" (ItemCBArr).
I want, that once I select a value from the first Combo-Box of "Category", let's say CatCBArr(0), that only the related Items in ItemCBArr(0) will be displayed.
Issue: I can't figure out how to modify the second Combo-box (ItemCBArr(0)) according to the value selected in the first Combo-box (CatCBArr(0))
User_Form Code (relevant section)
Option Explicit
Dim ItemsNumofRows As Long
Dim QtyTB As MSForms.TextBox
Dim CatCB As MSForms.ComboBox
Dim ItemCB As MSForms.ComboBox
Dim Key As Variant
' dynamic Form controls (related to new Classes)
Dim CatCBArr() As New cComboBox
Dim ItemCBArr() As New cComboBox
Dim QtyTBArr() As New cTextBox
Private Sub UserForm_Initialize()
' reset flags
ItemsNumofRows = 5
TasksNamesUpd = False
TasksColUpd = False
ItemsRows_ControlsInit '<-- upload all Controls at run-time
Check_FormHeight
End Sub
'======================================================
Private Sub ItemsRows_ControlsInit()
For ItemRow = 0 To ItemsNumofRows
' add Category Combo-boxes
Set CatCB = Me.Controls.Add("Forms.ComboBox.1", "Cb" & ItemRow, True)
With CatCB
' loop through Dictionay items (view category)
For Each Key In Dict.Keys
.AddItem Key
Next Key
.SpecialEffect = fmSpecialEffectSunken
.Left = 40
.Width = 100
.Height = 18
.Top = 54 + 20 * ItemRow
ReDim Preserve CatCBArr(0 To ItemRow)
Set CatCBArr(ItemRow).ComboBoxEvents = CatCB
End With
' add Item Combo-boxes
Set ItemCB = Me.Controls.Add("Forms.ComboBox.1", "Cb_" & ItemRow, True)
With ItemCB
.SpecialEffect = fmSpecialEffectSunken
.Left = 160
.Width = 100
.Height = 18
.Top = 54 + 20 * ItemRow
ReDim Preserve ItemCBArr(0 To ItemRow)
Set ItemCBArr(ItemRow).ComboBoxEvents = ItemCB
End With
Next ItemRow
End Sub
cComboBox Class Code
Public WithEvents ComboBoxEvents As MSForms.ComboBox
Private Sub ComboBoxEvents_Change()
Dim CBIndex As Long
' get for ID number (row number), from third character in String Name.
' e.g "Cb1" will result 1)
CBIndex = CInt(Mid(ComboBoxEvents.Name, 3))
' ??? How do I get the Value, and update the second combo-box Items
Select Case ComboBoxEvents.Value
End Select
End Sub
GUI User_Form screen-shot
Alright, here's the basics.
Your class cCombobox I replicated as follows:
Private WithEvents ComboBoxEvents As MsForms.ComboBox
Private Sub ComboBoxEvents_Change()
Select Case ComboBoxEvents.value
Case "1":
UserForm1.DependentBox.Clear
UserForm1.DependentBox.AddItem "3"
UserForm1.DependentBox.AddItem "4"
Case "2":
UserForm1.DependentBox.Clear
UserForm1.DependentBox.AddItem "5"
UserForm1.DependentBox.AddItem "6"
Case Default:
'Do Nothing
End Select
End Sub
Public Property Let box(value As MsForms.ComboBox)
Set ComboBoxEvents = value
End Property
Public Property Get box() As MsForms.ComboBox
Set box = ComboBoxEvents
End Property
Next, I created a UserForm1 that adds 2 comboboxes, one of which I add to a local variable of type cComboBox.
Public DependentBox As MsForms.ComboBox
Private InitialBox As cComboBox
Private Sub UserForm_Initialize()
Dim cBox As MsForms.ComboBox
Set InitialBox = New cComboBox
Set cBox = Me.Controls.Add("Forms.ComboBox.1", "initial", True)
With cBox
.Left = 6
.Width = 100
.Height = 25
.Top = 6
.AddItem "1"
.AddItem "2"
End With
InitialBox.box = cBox
Set DependentBox = Me.Controls.Add("Forms.ComboBox.1", "dependent", True)
With DependentBox
.Top = 6
.Left = 126
.Height = 25
.Width = 100
End With
End Sub
Even though this works, the above approach is not very clean, since your class is not self-contained - It has to be aware of the UserForm. A better way would be to link the boxes in the class already and then just pass them from the Userform when you initialize your arrays of controls.
Then it would be:
cComboBox class:
Private WithEvents p_ComboBoxEvents As MSForms.ComboBox
Private p_DependBox As MSForms.ComboBox
Private Sub p_ComboBoxEvents_Change()
Select Case p_ComboBoxEvents.value
Case "1":
p_DependBox.Clear
p_DependBox.AddItem "3"
p_DependBox.AddItem "4"
Case "2":
p_DependBox.Clear
p_DependBox.AddItem "5"
p_DependBox.AddItem "6"
Case Default:
'Do Nothing
End Select
End Sub
Public Property Let TriggerBox(value As MSForms.ComboBox)
Set p_ComboBoxEvents = value
End Property
Public Property Get TriggerBox() As MSForms.ComboBox
Set TriggerBox = p_ComboBoxEvents
End Property
Public Property Let DependBox(value As MSForms.ComboBox)
Set p_DependBox = value
End Property
Public Property Get DependBox() As MSForms.ComboBox
Set DependBox = p_DependBox
End Property
Here you see you already link the boxes in a self-contained class.
In the event handler you could create a lookup for the values, etc.
Then in the UserForm1 code you initialize them as follows:
Option Explicit
Private LinkedComboBox As cComboBox
Private Sub UserForm_Initialize()
Dim cBox As MSForms.ComboBox
Set LinkedComboBox = New cComboBox
Set cBox = Me.Controls.Add("Forms.ComboBox.1", "initial", True)
With cBox
.Left = 6
.Width = 100
.Height = 25
.Top = 6
.AddItem "1"
.AddItem "2"
End With
LinkedComboBox.TriggerBox = cBox
Set cBox = Me.Controls.Add("Forms.ComboBox.1", "dependent", True)
With cBox
.Top = 6
.Left = 126
.Height = 25
.Width = 100
End With
LinkedComboBox.DependBox = cBox
End Sub
EDIT:
Based on the fact that it needs to be an array, you can amend the userform as follows:
Option Explicit
Private LinkedComboBox(0 To 4) As cComboBOx
Private Sub UserForm_Initialize()
Dim cBox As MSForms.ComboBox
Dim i As Integer
For i = 0 To 4
Set LinkedComboBox(i) = New cComboBOx
Set cBox = Me.Controls.Add("Forms.ComboBox.1", "initial", True)
With cBox
.Left = 6
.Width = 100
.Height = 25
.Top = 6 + (i * 25)
.AddItem "1"
.AddItem "2"
End With
LinkedComboBox(i).TriggerBox = cBox
Set cBox = Me.Controls.Add("Forms.ComboBox.1", "dependent", True)
With cBox
.Top = 6 + (i * 25)
.Left = 126
.Height = 25
.Width = 100
End With
LinkedComboBox(i).DependBox = cBox
Next i
End Sub
In the array you can access each box as LinkedComboBox(i).DependBox and LinkedComboBox(i).TriggerBox. You won't need the two seperate arrays anymore, since everything is already contained in this LinkedComboBox array
I have a userform which is dynamically filled by checkboxes. To improve usability I've added a checkbox "Select all" which either selects or deselects all checkboxes. This works perfectly. Now I want the "Select all" checkbox to be unchecked (value=False) automatically once one of the other checkboxes is unchecked.
In order to achieve this I've created a class module. This class module also does what it's supposed to. But, and here's my problem, once the value of "Select all" is changed, the "Select all" checkbox_click event is triggered, which means all other checkboxes are being unchecked as well, and that's obviously not what I want! I've already tried to use the MouseDown and MouseUp events instead, but these events behave irregular and often lead to the wrong results.
Question: How do I stop the Click event from running everytime the value of this checkbox is changed without the checkbox actually being clicked?
Here's my code:
Private colTickBoxes As Collection
Private Sub UserForm_Initialize()
Dim ChkBoxes As cls_RIRI
Dim ctrl As Control
'Controls are created on run time here
'Some events to change the height of the userform and the top value of several buttons
'Make sure click events work
Set colTickBoxes = New Collection
For Each ctrl In Me.Controls
If TypeName(ctrl) = "CheckBox" And Left(ctrl.Name, 1) = "M" Then
Set ChkBoxes = New cls_RIRI
ChkBoxes.AssignClicks ctrl
colTickBoxes.Add ChkBoxes
End If
Next ctrl
Set ctrlCHK = Nothing
Set ChkBoxes = Nothing
Set ctrl = Nothing
End Sub
And the class module:
Private WithEvents chkBox As MSForms.CheckBox
Public Sub AssignClicks(ctrl As Control)
Set chkBox = ctrl
End Sub
Private Sub chkBox_Click()
If chkBox.value = False Then UserForms(0).Controls("chkSelAll").value = False
End Sub
And the chkSelAll_Click sub:
Private Sub chkSelAll_Click()
Dim ctrl As Control
For Each ctrl In Me.Controls
If TypeName(ctrl) = "CheckBox" And Left(ctrl.Name, 1) = "M" Then
If chkSelAll.value = True Then
ctrl.value = True
Else
ctrl.value = False
End If
End If
Next
Set ctrl = Nothing
End Sub
Add a public variable, say blnBypassNonUIClick and set this, then when coming from the UI, by a human click set it to false, and when setting the select all the same. Your select all, shouldnt activate click if you're setting the .value by code. Something like this....
Public blnNonUI As Boolean
Private Sub CheckBox1_Click()
If Not blnNonUI Then
MsgBox "hello"
End If
End Sub
Private Sub UserForm_Click()
blnNonUI = True
Me.CheckBox1.Value = 1
blnNonUI = False
End Sub
Nathan_Sav gave me this idea and it works:
Public blnHumanClick As Boolean
Private Sub chkSelAll_Click()
If blnHumanClick = True Then
Dim ctrl As Control
For Each ctrl In Me.Controls
If TypeName(ctrl) = "CheckBox" And Left(ctrl.Name, 1) = "M" Then
If chkSelAll.value = True Then
ctrl.value = True
Else
ctrl.value = False
End If
End If
Next
Set ctrl = Nothing
blnHumanClick = False
End If
End Sub
Private Sub chkSelAll_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
blnHumanClick = True
End Sub
In Excel 2010, I am trying to create a userform dynamically in my vba code. However, after running the code, the list does not show when opening the dropdown. If I put a breakpoint before ".show", I can inspect the form in the design window and I see that the list is populated. Does the .show method clear the list or what is happening? The solutions I have found focus on populating from cell ranges or placing the ".additem"s in the initialization code of the userform. I don't want to do anything that requires me to create a second file. Everything should be in this vba code if possible. Any help appreciated. My code follows.
Sub make_combobox_form()
' Makes a form with only a simple combobox
Dim myForm As Object
Dim cb As MSForms.ComboBox
'Create the User Form
Set myForm = ThisWorkbook.VBProject.VBComponents.Add(vbext_ct_MSForm)
With myForm
.Properties("Width") = 400
.Properties("Height") = 300
End With
Set cb = myForm.Designer.Controls.Add("Forms.ComboBox.1")
With cb
.Top = 100
.Left = 100
.Height = 20
.Width = 200
.AddItem "Item_1"
.AddItem "Item_2"
.AddItem "Item_3"
.value = "Item_1"
End With
VBA.UserForms.Add(myForm.name).Show
ThisWorkbook.VBProject.VBComponents.Remove myForm
End Sub
Sub make_combobox_form()
'Create the User Form as component first
Dim myFormComponent As VBComponent
Set myFormComponent = ThisWorkbook.VBProject.VBComponents.Add(vbext_ct_MSForm)
' Get reference to user form from the UserForms collection then
Dim cb As MSForms.ComboBox
Dim myForm As Variant
Set myForm = VBA.UserForms.Add(myFormComponent.Name)
With myForm
.Width = 400
.Height = 300
End With
Set cb = myForm.Controls.Add("Forms.ComboBox.1")
With cb
.Top = 100
.Left = 100
.Height = 20
.Width = 200
.AddItem "Item_1"
.AddItem "Item_2"
.AddItem "Item_3"
.Value = "Item_1"
End With
myForm.Show
ThisWorkbook.VBProject.VBComponents.Remove myFormComponent
End Sub