How to create Sub on dynamically created ComboBox in VBA? - vba

I am very new to excel programming and VBA. I am stuck at a point where I have random number of dynamically created combo boxes (ComboBox1, ComboBox2.... ComboBoxN).
I need to implement a functionality where if I select a value in the ComboBox[i] (where i can be any random number between 1 to N), then it should trigger an event that will populate values in ComboBox[i+1].
How do I write a Sub for this? Is there any other way to implement this if not in a Sub?

In order to create a group events you'll need a custom class to capture the events ( ObjectListener ), public variable to keep the class references alive (usually a collection or array - ComboListener ) and a Macro to fill the collection ( AddListeners_ComboBoxes ).
Call the AddListeners_ComboBoxes Macro from the Workbook_Open(). You will need call AddListeners_ComboBoxes again if the code breaks.
Standard Module
Public ComboListener As Collection
Sub AddListeners_ComboBoxes()
Dim ws As Worksheet
Dim obj As OLEObject
Dim listener As ObjectListener
Set ComboListener = New Collection
For Each ws In Worksheets
For Each obj In ws.OLEObjects
Select Case TypeName(obj.Object)
Case "ComboBox"
Set listener = New ObjectListener
Set listener.Combo = obj.Object
ComboListener.Add listener
End Select
Next
Next
End Sub
Class ObjectListener
Option Explicit
Public WithEvents Combo As MSForms.ComboBox
Private Sub Combo_Change()
MsgBox Combo.Name
Select Case Combo.Name
Case "ComboBox2"
ActiveSheet.OLEObjects("ComboBox3").Object.ListIndex = 1
End Select
End Sub

As an alternative to the "Class" approach shown by Thomas Inzina here's a "less structured" approach:
Private Sub ComboBox1_Change()
PopulateCombo 2
End Sub
Private Sub ComboBox2_Change()
PopulateCombo 3
End Sub
Private Sub ComboBox3_Change()
PopulateCombo 4
End Sub
Private Sub ComboBox4_Change()
PopulateCombo 1 '<--| will "last" combobox populate the "first" one?
End Sub
Private Sub PopulateCombo(cbNr As Long)
With ActiveSheet.OLEObjects("ComboBox" & cbNr) '<--| reference the combobox as per the passed number
.ListFillRange = "Sheet1!J1:J10" '<--| populate it with "Sheet1" worksheet range "A1:A10"
.Object.ListIndex = 1 '<--| select its first item
End With
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

change data based on vlookup result using vba userform

Afternoon guys,
I cannot find a way to code the following and I do not have any code to show.
I have a small and simple userform that displays a permit expiry date based on a combo box selection using vlookup.
Screen grab of userform
What I need is to be able to change the date and write that new date back to appropriate cell in the database when hitting the command button on the left. The command button on the right just unloads the form. The form will be used to change the permit expiry date, which is required in a different database to control competition points allocation.
The code that I do have for this userform is as follows (This code is working fine):
Private Sub CmdChangedate_Click()
'This is where the code will be
End Sub
Private Sub CommandButton2_Click()
Unload Me
End Sub
'This is where the name is selected from a combobox and the date is located
Private Sub Hengelaar_Change()
With Me
On Error Resume Next
.Nuwepermitdatum = Application.WorksheetFunction.VLookup(CStr(Me.Hengelaar), Worksheets("Lede Lys").Range("A:J"), 10, 0)
On Error GoTo 0
End With
End Sub
Private Sub Nuwepermitdatum_CallbackKeyDown(ByVal KeyCode As Integer, ByVal Shift As Integer, ByVal CallbackField As String, CallbackDate As Date)
End Sub
Private Sub UserForm_Click()
End Sub
Private Sub UserForm_Initialize()
Dim lastrow As Integer
lastrow = ActiveSheet.Cells(Rows.Count, 1).End(xlUp).Row
Me.Nuwepermitdatum.Value = Date
Hengelaar.List = Worksheets("Lede Lys").Range("a3:a" & lastrow).Value
End Sub
Maybe this is what you’re after:
Private Sub CmdChangedate_Click()
With Worksheets("Lede Lys")
.Cells(Application.Match(Me.Hengelaar, .Range("A:A"),0), "J") = Me.Nuwepermitdatum
End With
End Sub

Userforms with a listbox in VBA

This is my first post on this forum. I have a quick question regarding VBA, Userforms with a listbox. My goal is to select two options and return a list of names in a listbox. I have attached the example userform and the example table that I would be choosing from. Any help would be appreciated.
Worksheet
Current VBA for Userform
Private Sub ListBox1_Click()
Sheets("Trainers1").Range("I2") = ListBox1
End Sub
Private Sub ListBox2_Click()
Sheets("Trainers1").Range("I2") = ListBox2
End Sub
Private Sub ListBox3_Click()
Sheets("Trainers1").Range("I3") = ListBox3
End Sub
Private Sub ListBox4_Change()
.ListBox4 = Sheets("Trainers1").Range("K2:K10")
End Sub
Private Sub UserForm_Initialize()
Dim cnt
Dim cntr As Integer
cntr = Application.WorksheetFunction.CountA(Sheets("Shift Pattern Key").Range("A:A"))
cnt = Application.WorksheetFunction.CountA(Sheets("Training Ratio").Range("A:A"))
For i = 2 To cntr
ListBox2.AddItem Sheets("Shift Pattern Key").Cells(i, 1)
Next i
For i2 = 2 To cnt
ListBox3.AddItem Sheets("Training Ratio").Cells(i2, 1)
Next i2
End Sub
You could iterate through the rows of your table and compare the values in each row to the values selected. If both values in a row match the values selected by the user you can then use the .AddItem method to add the name of the employee to the list.

Assigning a variant to a combobox and vice-versa

I am trying to assign a variant the "value" of a combobox in a userform in order to store the list so that I can re-assign the "Value" of the variant to the combobox when initializing the userform.
Here is the code I am using to assign my variant the List value of the combobox before the userform is closed:
Dim S()
Private Sub ExitButton_Click()
ReDim S(1 To NewRecordUserForm.RepCombo.ListCount)
S = NewRecordUserForm.RepCombo.List
Unload Me
End Sub
I then use this set of code in a separate sub to initialize the combobox upon opening the userform:
Private Sub UserForm_Initialize()
NewRecordUserForm.RepCombo.List = S
End Sub
Note that Dim S() has been declared as a global variable. I get the following error message:
"Run-error '380':
Could not set the list property. Invalid property value."
Dim ArrRep() As Variant
Public Sub PopulateCombos()
NewRecordUserForm.RepCombo.List = ArrRep
End Sub
Public Sub SaveCombos()
ReDim ArrRep(1 To NewRecordUserForm.RepCombo.ListCount)
ArrRep = NewRecordUserForm.RepCombo.List
End Sub
I called the first sub upon opening the userform and the second just before it closes. Worked!

Why VBA global variables loses values when closing UserForm?

I have a macro code behind Worksheet. When button is clicked on the sheet, new user form is initialised and showed to user. If user closes the windows with red X, or form is closed with "hide" function/method, all global variables that are behind Worksheet loses their values. Is it possible to preserve this values?
Worksheet code behind:
Private MeasurementCollection As Collection
Dim CurrentMeasurement As measurement
Dim NewMeasurement As measurement
Private Sub Worksheet_Activate()
Initialize
End Sub
Public Sub Initialize()
Set NewMeasurement = New measurement
Dim DropDownDataQueries As Collection
Set DropDownDataQueries = DBQueries.GetAllUpdateQueries
For i = 1 To DropDownDataQueries.Count
Dim Values As Collection
Set Values = DataBase.GetData(DropDownDataQueries(i))
With Me.OLEObjects("Combo" & i).Object
For Each value In Values
.AddItem value
Next value
End With
Next i
End Sub
Private Sub UpdateDB_Click()
UpdateGeneralData
If (CurrentMeasurement Is Nothing) Then
MsgBox ("Message text")
Else
Dim form As UpdateComentForm
Set form = New UpdateComentForm
form.Show
End If
End Sub
Private Sub Combo1_Change()
If Application.EnableEvents = True Then
If (Combo1.value <> "") Then
NewMeasurement.DN = Combo1.value
Else
NewMeasurement.DN = 0
End If
End If
End Sub
UserForm code
Private Sub UpdateDBData_Click()
If (Komentar.value <> "") Then
Me.Hide
Else
MsgBox ("Prosimo napišite vzrok za spremembe podatkov v belo polje!")
End If
End Sub
Private Sub UserForm_Terminate()
Me.Hide
End Sub
Experiments show that the module-level variables are cleared upon exiting a procedure that involves calling = New Form, provided that the form designer window is opened somewhere in the IDE.
Close all user forms designer windows you might have open in the VBA IDE and try again.
NewMeasurement as been declared but never assigned.
You could do something like Dim NewMeasurement As New measurement to create an instance of the object.