I'm trying to add a list of numbers to multiple combo boxes at the same time with an easy way
I tried to use (And) but it's not working
With Me.ComboBox10 and Me.ComboBox11
.AddItem "0%"
.AddItem "10%"
.AddItem "20%"
.AddItem "30%" ' and so on.....
End With
I would use a different approach as With Me.ComboBox10 and Me.ComboBox11 is not valid.
I asssume the ComboBoxes are on a Userform otherwise you have to adjust the code. But the approach does not change.
Private Sub UserForm_Initialize()
Dim vDat As Variant
ReDim vDat(0 To 5)
vDat(0) = "0%"
vDat(1) = "10%"
vDat(2) = "20%"
vDat(3) = "30%"
vDat(4) = "40%"
vDat(5) = "50%"
Dim sngControl As MSForms.Control
For Each sngControl In Me.Controls
If TypeName(sngControl) = "ComboBox" Then
sngControl.List = vDat
End If
Next
End Sub
Try this code
Private Sub UserForm_Initialize()
Dim v, ctrl As Control
v = Array("0%", "10%", "20%", "30%")
For Each ctrl In UserForm1.Controls
If TypeName(ctrl) = "ComboBox" Then
If ctrl.Name = "ComboBox1" Or ctrl.Name = "ComboBox3" Then ctrl.List = v
End If
Next ctrl
End Sub
Related
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
I have a big row of 250 ActiveX checkboxes of which I want to change the values all back from checked to unchecked, is there a way to put that in a macro?
checkbox names are just Checkbox3 to Checkbox253
An easy way:
Private Sub UnchkAllBox()
For i = 3 to 253
Controls("Checkbox" & i).Value = False
Next i
End Sub
In this way, the names of the checkboxes are very important. Use it only you named your checkboxes orderly.
Other way:
Private Sub UnchkAllBox2()
Dim Ctrl As Control
For Each Ctrl In Me.Controls
If TypeName(Ctrl) = "CheckBox" Then Ctrl.Value = False
Next Ctrl
End Sub
In this case, you don't have to concern the names. However, it will uncheck all checkboxes in your form.
Both methods mentioned are assuming all checkboxes were placed in a userform. For checkboxes in a worksheet, excel stores them in a OLEObjects collection instead of Controls. Therefore, the code should be re-written as below.
Private Sub UnChkAllBox()
For i = 1 To 5
OLEObjects("CheckBox" & i).Object.Value = False
Next i
End Sub
And
Private Sub UnChkAllBox2()
Dim Obj As OLEObject
For Each Obj In Me.OLEObjects
If TypeOf Obj.Object Is MSForms.CheckBox Then Obj.Object.Value = False
Next Obj
End Sub
I would like to save the ComboBox1 & ComboBox2 values as variables which I could use in the module I return to once the Userform is correctly completed by the user however I am unsure how to do this.
Option Explicit
Private isCancelled As Boolean
Public Property Get Cancelled() As Boolean
Cancelled = isCancelled
End Property
Private Sub CancelButton1_Click()
isCancelled = True
Me.Hide
End Sub
Public Property Get Benefit() As String
Benefit = IIf(Me.ComboBox1.ListIndex = -1, vbNullString, Me.ComboBox1.Text)
End Property
Public Property Get Costdelivery() As String
Costdelivery = IIf(Me.ComboBox2.ListIndex = -1, vbNullString, Me.ComboBox2.Text)
End Property
Private Sub ComboBox1_Change()
ValidateForm
End Sub
Private Sub ComboBox2_Change()
ValidateForm
End Sub
Private Sub ValidateForm()
Me.Okbutton1.Enabled = (Benefit <> vbNullString And Costdelivery <> vbNullString)
End Sub
Private Sub UserForm_Activate()
ValidateForm
End Sub
Private Sub UserForm_Initialize()
'populate "Combo-Box with Boards
With Me.ComboBox1
.Clear ' clear previous items (not to have "doubles")
.AddItem "Very High"
.AddItem "High"
.AddItem "Medium"
.AddItem "Low"
End With
With Me.ComboBox2
.Clear ' clear previous items (not to have "doubles")
.AddItem "Very High"
.AddItem "High"
.AddItem "Medium"
.AddItem "Low"
End With
End Sub
Private Sub Okbutton1_Click()
Dim Ben As Long
Ben = Me.ComboBox1.Value ***ERROR
Dim Cost As Long
Cost = Me.ComboBox2.Value **** ERROR
Me.Hide
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode = vbFormControlMenu Then
Cancel = True
isCancelled = True
Me.Hide
End If
End Sub
Ideally I would like to store the Low,Medium,High or Very high as values 9,10,11,12 as I will be using these as cell references in the module I return to after the userform is closed.
I understand I would need to state a public property my attempt which does not work is below;
Public Function ConfidenceChart()
Dim ben As Long, costd As Long
ben = UserForm4.ComboBox1.Text
If ben = "Low" Then ben = 9
If ben = "Medium" Then ben = 8
If ben = "High" Then ben = 7
If ben = "Very High" Then ben = 6
costd = UserForm4.ComboBox2.Text
If costd = "Low" Then costd = 12
If costd = "Medium" Then costd = 13
If costd = "High" Then costd = 14
If costd = "Very High" Then costd = 15
End Function
Excel controls allow you to link controls to cells. In this way, you can store the values of the controls for later use.
Userform controls use the ControlSource property to establish this link. In my Demo below I create two named ranges on a setting page (named after the controls' names for easy reference) and set the controls ControlSource property to the named range.
Addendum
#Mat'sMug pointed out that the OP needed to store a value from a lookup list base on the selection from a ComboBox. This can also be achieved using named ranges.
Private Sub UserForm_Initialize()
'populate "Combo-Box with Boards
With Me.ComboBox1
.RowSource = "List1"
.ColumnCount = 2
.ColumnWidths = "0 pt;49.95 pt"
End With
With Me.ComboBox2
.RowSource = "List2"
.ColumnCount = 2
.ColumnWidths = "0 pt;49.95 pt"
End With
End Sub
You will need to change some of the properties of the ComboBoxes.
Private Sub UserForm_Initialize()
'populate "Combo-Box with Boards
With Me.ComboBox1
.RowSource = "List1"
.ColumnCount = 2
.ColumnWidths = "0 pt;49.95 pt"
.ControlSource = "ComboBox1"
End With
With Me.ComboBox2
.RowSource = "List2"
.ColumnCount = 2
.ColumnWidths = "0 pt;49.95 pt"
.ControlSource = "ComboBox2"
End With
End Sub
Ben and Cost are locals, they're only visible from within the Click handler.
It's not the forms' job to know where these values need to end up - the form is only there to collect user input.
You already have Benefit and CostDelivery properties that the caller is able to access.
Use them!
With New UserForm1 'whatever the name of that form is
.Show
If Not .Cancelled Then
Sheet1.Range("A1").Value = .Benefits
Sheet1.Range("B1").Value = .CostDelivery
End If
End With
If you need them to be numeric values, then you're not populating your comboboxes correctly.
Nowhere you're mapping "Very High" to 12, the comboboxes only know about the strings.
To do that you need to change this:
With Me.ComboBox1
.Clear ' clear previous items (not to have "doubles")
.AddItem "Very High"
.AddItem "High"
.AddItem "Medium"
.AddItem "Low"
End With
And assign Me.ComboBox1.List to a 2D array. Or a Range, if you have one that looks like this:
A B
15 Very High
14 High
13 Medium
12 Low
You could have a this helper method:
Private Sub PopulateFromRange(ByVal control As MSForms.ComboBox, ByVal source As Range, Optional ByVal valueColumn As Long = 1, Optional ByVal hasHeader As Boolean = True)
With control
.ColumnCount = source.Columns.Count
.ColumnWidths = GetColumnWidths(source)
.ListWidth = IIf(control.Width > source.Width, control.Width, source.Width)
.List = source.Range(source.Rows(IIf(hasHeader, 2, 1)).EntireRow, source.Rows(source.Rows.Count).EntireRow).Value
.BoundColumn = valueColumn
End With
End Sub
Private Function GetColumnWidths(ByVal source As Range) As String
Dim cols As Long
cols = source.Columns.Count
Dim widths()
ReDim widths(1 To cols)
Dim col As Long
For col = 1 To cols
widths(col) = source(, col).Width
Next
GetColumnWidths = Join(widths, ",")
End Function
(taken from this post specifically about populating combobox and listbox controls from ranges)
And populate your comboboxes like this:
PopulateFromRange Me.ComboBox1, DataSheet.Range("A1:B5")
PopulateFromRange Me.ComboBox2, DataSheet.Range("D1:E5")
Assuming you have a DataSheet worksheet with ranges [A1:B5] and [D1:E5] respectively containing the text and corresponding numeric value for each item.
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
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