Excel VBA add item to a programatic ComboBox when other ComboBox Change - excel

I add a new page in Multipage1 programmatically
Private Sub CommandButton3_Click()
Dim i As Integer
i = MultiPage1.Pages.Count
MultiPage1.Pages.Add.Caption = "Guarantee " & i
Then I would like my new page containing 3 ComboBoxes and the ComboBox1 will list the item in the table on my worksheet name "LeftTB", here is the code.
For r = 1 To 3
Set myCB = MultiPage1.Pages(i).Controls.Add("Forms.ComboBox.1", "ComboBox" & r, 1)
With myCB
.Width = 150
.Height = 18
Select Case r
Case Is = 1
.Left = 54
.Top = 156
'add item to combobox1
Dim rng, cl As Range
'LeftTB is the name of Table contain Data
Set rng = Range("LeftTB")
For Each cl In rng
.AddItem cl.Value
Next cl
Case Is = 2
.Left = 252
.Top = 156
Case Is = 3
.Left = 54
.Top = 180
End Select
End With
Next r
End Sub
It works fine to add value in ComboBox1 by this code. For the item in ComboBox 2, it depends on the value of the ComboBox1 as the code below.
Private Sub ComboBox1_Change()
Dim rng, cl As Range
'The CenterTB is the table in my worksheet with two columns. The first column (SubD) contains the same data as table "LeftTB" and the next column is the item I would like to add to ComboBox2
Set rng = Range("CenterTB[SubD]")
For i = 1 to me.MultiPage1.Pages.Count
me.MultiPage1.Pages(i).Controls("ComboBox2").Clear
For Each cl In rng
If cl.Value = me.MultiPage1.Pages(i).Controls("ComboBox1").Text Then
me.MultiPage1.Pages(i).Controls("ComboBox2").AddItem cl.Offset(0, 1).Value
End If
Next cl
Next i
End Sub
However, it does not work when ComboBox1 is programmatic. I have no idea to detect the change procedure of Combobox1 when it is programmatic.
Can someone provide me the solution?

When creating controls on the fly, VBA does not automatically create their Events!
There are two ways to do that. To create a events wrapper class, or simpler, in case of a known number of controls to be added (your case) to previously declare the controls in a specific way:
Put the next declaration on top of the form code module (in the declarations area):
Option Explicit
Private WithEvents ComboBox1 As MSForms.ComboBox
Private WithEvents ComboBox2 As MSForms.ComboBox 'possible to use its events, too
Private WithEvents ComboBox3 As MSForms.ComboBox 'possible to use its events, too
Then you should adapt your code, in a way to set the above declared variables as what you want/need them to be. So, please adapt your code in the next way:
Private Sub btCreateCmb_Click()
Dim myCB As MSForms.ComboBox, r As Long
For r = 1 To 3
Set myCB = MultiPage1.Pages(i).Controls.Add("Forms.ComboBox.1", "ComboBox" & r, 1)
With myCB
.Width = 150
.Height = 18
Select Case r
Case Is = 1
Set ComboBox1 = myCB 'added to Set your first combo
.Left = 54
.Top = 156
'add item to combobox1
Dim rng, cl As Range
'LeftTB is the name of Table contain Data
Set rng = Range("LeftTB")
For Each cl In rng
.AddItem cl.Value
Next cl
Case Is = 2
Set ComboBox2 = myCB 'added to Set your second combo
.Left = 252
.Top = 156
Case Is = 3
Set ComboBox3 = myCB 'added to Set your third combo
.Left = 54
.Top = 180
End Select
End With
Next r
End Sub
Only now, the combo events will be triggered (if their code exists in the form code module). Please, firstly use the next short code example:
Private Sub ComboBox1_Change()
MsgBox "Changed 1..."
End Sub
If you exactly followed the above suggestions, It should be surely triggered!
Now, you can put in its event code whatever you need...
Please, test it and send some feedback.

Related

Refer to ComboBox that is just created after user input

i am trying to have VBA create a user form and based off of what selection you choose populate more selections.
the following code is my initial user form and i have it adding Comboboxes Based on whatever selection is chosen.
Private Sub UserForm_Initialize()
ComboBox1.AddItem "Selection1"
ComboBox1.AddItem "Selection2"
ComboBox1.AddItem "Selection3"
ComboBox1.FontSize = 13
End Sub
But i have it going to comboboxchange to do so
Private Sub ComboBox1_Change()
'Here i have some Working Code That Adds another ComboBox
Dim MsgType As Control
Set MsgType = UserForm2.Controls.Add("Forms.ComboBox.1")
With MsgType
.Height = 25
.Width = 300
.Top = 75
.Left = 20
.FontSize = 13
.Name = "vmtype"
.AddItem "Selection1"
.AddItem "Selection2"
Debug.Print .Value
End With
EndSub
I now need to have a private sub refer to this combo box just as "Private Sub ComboBox1_Change()" had. that way i can add more items depending on that selection.
What i have so far is the following.
Private Sub ComboBox2_Change()
Dim Notetype As String
Notetype = ComboBox1.Value
Debug.Print Notetype
End Sub
But it is not actually refering to the newly created Combo Box any idea how i can Fix this?
Use WithEvents to listen for dynamically added controls.
Here is a simple example where you are just adding a single control:
Private WithEvents vmtype As MSForms.ComboBox
Private Sub vmtype_Change()
Debug.Print MsgType
End Sub
Private Sub ComboBox1_Change()
Set vmtype = UserForm2.Controls.Add("Forms.ComboBox.1")
With vmtype
.Height = 25
.Width = 300
.Top = 75
.Left = 20
.FontSize = 13
.Name = "vmtype"
.AddItem "Selection1"
.AddItem "Selection2"
End With
End Sub
Consider using a custom class when adding many control. See my post: Responding to Events of Dynamically added Controls

Event trigger when any checkbox is checked

I have a worksheet where there is a list of car parts pending delivery from the factory. I need to populate column I with a checkbox in each cell.
I created a button called "CREATE CHECKBOXES" that looks at how many rows of data exists then populates each cell of column I with ActiveX checkboxes from CheckBox1 up to CheckboxN (n = number of rows containing data). That part is already figured out.
Next when the user checks any of the checkboxes, it must pop up a userform with 2 data field inputs that will be inserted on column J and K in the same row of the checked checkbox. Where I got stuck in the code is the event that triggers the userform to pop when any of the checkboxes is checked.
I saw Event triggered by ANY checkbox click), but now due to the code counting the checkboxes as shapes, I can't add any sort of button to add a macro to it.
I had to delete the "CREATE CHECKBOXES" button, otherwise the code from the linked post won't work.
How can I make this userform trigger event happen when any of the checkboxes are checked and maintain the shape buttons?
A few things must happen after that, but I think I can handle it.
I created a class module, named ChkClass, with this code:
' put all this code in class a module and name the class module "ChkClass"
Option Explicit
Public WithEvents ChkBoxGroup As MSForms.CheckBox
Private Sub ChkBoxGroup_Change()
Debug.Print "ChkBoxGroup_Change"
End Sub
Then pasted this in the sheet code:
' this goes into sheet code
Private Sub Worksheet_Activate()
activateCheckBoxes
End Sub
After that, I created a module and it was slightly adapted from the linked post:
' this code goes into a module
Option Explicit
Dim CheckBoxes() As New ChkClass
Sub doCheckBoxes()
makeCheckBoxes
activateCheckBoxes
End Sub
Sub makeCheckBoxes()
Sheets(2).Select
Dim c As Range
Dim ultlinha As Integer
ultlinha = Range("A2").End(xlDown).Row
Range(Cells(2, 9), Cells(ultlinha, 9)).Select
For Each c In Selection
With ActiveSheet.OLEObjects.Add(ClassType:="Forms.CheckBox.1", Left:=c.Left, Top:=c.Top, Width:=c.Width, Height:=c.Height)
DoEvents
.Object.Caption = "FATURADO"
.LinkedCell = c.Offset(0, 3).Address
.Object.Value = 0 'sets checkbox to false
.Object.Font.Name = "Calibri"
.Object.Font.Size = 9
.Object.Font.Italic = True
.Object.BackStyle = fmBackStyleTransparent
End With
Next
Range("a1").Select
End Sub
Sub activateCheckBoxes() ' assigns all checkBoxes on worksheet to ChkClass.ChkBoxGroup
Dim sht As Worksheet
Set sht = ActiveSheet
ReDim CheckBoxes(1 To 1)
Dim i As Integer
For i = 1 To sht.Shapes.Count
ReDim Preserve CheckBoxes(1 To i)
Set CheckBoxes(i).ChkBoxGroup = sht.Shapes(i).OLEFormat.Object.Object
Next i
End Sub
The problem lies in this line:
Set CheckBoxes(i).ChkBoxGroup = sht.Shapes(i).OLEFormat.Object.Object
If there is no other button or shape in the sheet, it runs correctly. If I add a single button or form to add the macro to it, it doesn't work.
If you only want to "activate" the checkboxes then you can loop over the sheet's OLEObjects collection and only capture the checkboxes.
Sub activateCheckBoxes()
Dim sht As Worksheet, obj As OLEObject, n As Long
Set sht = ActiveSheet
ReDim CheckBoxes(1 To 1)
Dim i As Integer
For Each obj In sht.OLEObjects
If TypeName(obj.Object) = "CheckBox" Then 'is a checkbox?
n = n + 1
If n > 1 Then ReDim Preserve CheckBoxes(1 To n)
Set CheckBoxes(n).ChkBoxGroup = obj.Object
End If
Next obj
End Sub

Create and Add Controls and Attach Events to them at Runtime in Excel UserForms

recently I found a very useful article about “How to add Events to dynamically created Controls in Excel using VBA class module”. I tried it in my project and it worked perfectly for just one textbox at a time. Actually I have to create multiple textboxes dynamically using VBA "For Next Loop" and attach the same event to all of them explicitly. but only the last created textbox gets the event.
Would you please help?
userform:
Private Sub UserForm_Initialize()
' Create and add the textboxes.
Dim i As Integer
For i = 0 To 4
Set tbPin = Me.Controls.Add("Forms.TextBox.1")
With tbPin
.Top = 8 + (i * 20)
.Left = 10
.Width = 130
.Name = "thePin"
End With
Set objMyEventClass.tbEvents = tbPin ' Attach an event to the textbox.
Next
' Create and add the button control.
Dim btEx As MSForms.CommandButton
Set btEx = Me.Controls.Add("Forms.CommandButton.1")
With btEx
.Top = Me.Height - 60
.Left = 10
.Width = 130
.Height = 25
.Caption = "Submit"
End With
Set objMyEventClass.btEvents = btEx ' Attach at event to the button.
End Sub
class module:
Option Explicit
Public WithEvents tbEvents As MSForms.TextBox
Public WithEvents btEvents As MSForms.CommandButton
Private Sub tbEvents_Change()
If Len(UserForm1.tbPin) > 6 Then
MsgBox "Max 6 digits only"
With UserForm1.tbPin
.Text = ""
.SetFocus
End With
End If
End Sub
Private Sub btEvents_click()
If Trim(UserForm1.tbPin) <> "" Then
Call showMyPin(UserForm1.tbPin)
End If
End Sub
Private Sub showMyPin(pin As String)
MsgBox "You have entered " & pin
End Sub

Add event listeners to procedurally generated controls without using a user form

I have a spreadsheet and create ListBox controls in every cell of a column. I'm trying to capture their selected contents but the examples of capturing events on runtime generated controls all involve using a user form and I'm not using one. I'm new to VBA so how can I reproduce the code below from
How to add events to Controls created at runtime in Excel with VBA
Option Explicit
Dim ButArray() As New Class2
Private Sub UserForm_Initialize()
Dim ctlbut As MSForms.CommandButton
Dim butTop As Long, i As Long
'~~> Decide on the .Top for the 1st TextBox
butTop = 30
For i = 1 To 10
Set ctlbut = Me.Controls.Add("Forms.CommandButton.1", "butTest" & i)
'~~> Define the TextBox .Top and the .Left property here
ctlbut.Top = butTop: ctlbut.Left = 50
ctlbut.Caption = Cells(i, 7).Value
'~~> Increment the .Top for the next TextBox
butTop = butTop + 20
ReDim Preserve ButArray(1 To i)
Set ButArray(i).butEvents = ctlbut
Next
End Sub
My code for generating my controls is
Public Sub CreateListbox()
Dim rCell As Range
Dim rRng As Range
Set rRng = ActiveSheet.Range("AA3:AA45")
For Each rCell In rRng.Cells
Set oLISTBOX = ActiveSheet.OLEObjects.Add(classtype:="Forms.ListBox.1")
With oLISTBOX
.Object.IntegralHeight = False
.Object.Font.Size = 11
.Top = rCell.Top
.Left = rCell.Left
.Width = rCell.Width
.Height = rCell.Height
.LinkedCell = rCell.Address
.ListFillRange = "ValSocDeterm."
.Object.ColumnCount = 3
.MultiSelect = 1
End With
Next rCell
End Sub
I basically want to take the example code for creating buttons on a form to creating ListBoxes on a Sheet.
Something like a class module, called clsCustomListBox containing the following code
Option Explicit
Private WithEvents custom As MSForms.ListBox
Public Function initialise(cbConvert As MSForms.ListBox) As Boolean
Set custom = cbConvert
End Function
Private Sub custom_Click()
MsgBox "Clicked"
End Sub
and then a standard module to go through the sheet and get all the listboxes, or you could just add to the collection, when your code adds them.
Option Explicit
Private cls_CustomListBox As clsCustomListbox
Public colCustomListboxCollection As Collection
Public Sub GetListBoxes()
Dim c As OLEObject
Set colCustomListboxCollection = New Collection
For Each c In Worksheets("Sheet1").OLEObjects
If TypeOf c.Object Is MSForms.ListBox Then
Set cls_CustomListBox = New clsCustomListbox
cls_CustomCombo.initialise c.Object
colCustomListboxCollection.Add c
End If
Next c
End Sub
I havent fully tested as at work, but that's where id start.
Hope it helps.

Creating a Userform that changes depending on the elements it should contain

Below is part of the code used to create a chart in Excel:
elements = 0
For j = 1 To x - 1
ans = MsgBox(Activity(j), vbYesNo, "Do you wish for this Element to appear in the Gantt Chart?")
If ans = vbYes Then
elements = elements + 1
ActivityNew(elements) = Activity(j)
End If
Next j
The idea is that I have a list of x-1 activities in the array Activity() only some of which need to be displayed on the chart. These are stored in ActivityNew() and the variable elements counts the population of this array.
At the moment I use a VbYesNo message box to loop through all the activities in Activity() and ask the user to decide which should be shown in the chart.
I would like to show all the activities on a Userform each with a tickbox to either include in ActivityNew() or not but I have no idea how to go about that.
Do you know how to manipulate UserForms ?
You can use this code to create an element :
Set TheTickBox = UserForm.Controls.Add("Forms.TickBox.1", Visible = True)
With TheTickBox
.Left 'Beginning of the tickbox compared to the left side of the UserForm
.Width
.Top 'Beginning of the tickbox compared to the top of the UserForm
.Height
.Caption 'Change the displayed text
End With
So you could use something like this :
For j = 0 to x - 1
Set TheTickBox = UserForm.Controls.Add("Forms.TickBox.1", Visible = True)
With TheTickBox
.Left = 10
.Width = The_Width_You_Want
.Top = 10 + j*The_Height_You_Want
.Height = The_Height_You_Want
.Caption = activity(j)
End With
Next j
At the end of your UserForm you could add a Button 'Validate' that goes through all the tickboxes, and checks the value you've given them:
Sub ButtonValidate_Click()
elements = 0
For each Ctrl in UserForm.Controls
If Ctrl.Value = True Then
ActivityNew(elements) = Ctrl.Caption
elements = elements + 1
End If
Next Ctrl
End Sub
Edit :
To create the UserForm, just click on 'Add UserForm' when right clicking on your project (in the VBA editor).
The code lines I have given you at the beginning will have to be written in the UserForm code box (right click on your UserForm -> Code) and the following in the normal code area :
Sub UserForm()
UserForm.Show 'Here I suppose UserForm is the name of your UserForm
End Sub
And in the UserForm code box make sure that your sub has this name :
Sub UserForm_Initialize()
ACTIONS
End Sub
Please refer to the simplified output below:
To have the output:
1. You need to have user form with its property of list box MulitSelect set to 1.
The following are the codes:
In Userform module:
Private Sub ButtonOK_Click()
Dim Msg As String
Dim i As LoadPictureConstants
Msg = ""
For i = 0 To ListBox1.ListCount - 1
If ListBox1.Selected(i) Then _
Msg = Msg & ListBox1.List(i) & vbCrLf
Next i
MsgBox "You selected: " & vbCrLf & Msg
Unload Me
End Sub
Private Sub UserForm_Initialize()
Dim i As Long
Me.Caption = "Select Activities"
Call BuildActivityArray
For i = 1 To 5
Me.ListBox1.AddItem Activity(i)
Next
End Sub
In standard code module:
Sub ShowForm()
UserForm1.Show
End Sub

Resources