How make VBA run on clicking any checkbox in a userform? - vba

I have a userform with a multiple frames, all filled with multiple checkboxes. I've named the checkboxes to their corresponding Excel cells. Now I want to make VBA run on clicking any of these checkboxes on run time. I know I can do this by creating a click-sub for every individual checkbox, but there must be a cleaner way to do this.
So far I've tried to put this code in the userform_Click and userform_Mousedown events, but they don't run when I click the checkboxes. Does anyone have an idea how to do this?
Dim iControl As Control
For Each iControl In Me.Controls
If TypeName(iControl) = "CheckBox" Then
If iControl.Value = True And Range(iControl.Name).Value = "" Then
Range(iControl.Name).Value = Format(Now, "dd.mm.yyyy")
ElseIf iControl.Value = True And Range(iControl.Name).Font.Color = vbWhite Then
Range(iControl.Name).Font.Color = vbBlack
ElseIf iControl.Value = False And Range(iControl.Name).Value <> "" Then
Range(iControl.Name).Font.Color = vbWhite
End If
End If
Next

As SilentRevolution said - you need an event to fire when you click the button. You're just after a single procedure to fire all check box click events.
So:
Create a class module called cls_ChkBox.
In the class module you'll add the Click event code:
Option Explicit
Private WithEvents chkBox As MSForms.CheckBox
Public Sub AssignClicks(ctrl As Control)
Set chkBox = ctrl
End Sub
Private Sub chkBox_Click()
ThisWorkbook.Worksheets("Sheet1").Range(chkBox.Name).Value = Format(Now, "dd.mm.yyyy")
End Sub
Now you just need to attach the chkBox_Click event to each check box on your form. In your user form add this code:
Option Explicit
Private colTickBoxes As Collection
Private Sub UserForm_Initialize()
Dim ChkBoxes As cls_ChkBox
Dim ctrl As Control
Set colTickBoxes = New Collection
For Each ctrl In Me.Controls
If TypeName(ctrl) = "CheckBox" Then
Set ChkBoxes = New cls_ChkBox
ChkBoxes.AssignClicks ctrl
colTickBoxes.Add ChkBoxes
End If
Next ctrl
End Sub
Each check box is given its own instance of the class, which is stored in the colTickBoxes collection.
Open the form and the cell in Sheet1 will update to show the date depending on the name of the check box.

You need an event to run code, if there is no event, the macro cannot start. I don't know if there is a single event that triggers for any button or checkbox that is clicked.
If the code you want to execute is the same every time except the control, you could write a private sub in the userform module which is called for each event, the private sub can take an input between the () for example.
Private Sub CheckBox1_Click()
Call ExecuteCode(Me.CheckBox1)
End Sub
Private Sub CheckBox2_Click()
Call ExecuteCode(Me.CheckBox2)
End Sub
Private Sub CheckBox3_Click()
Call ExecuteCode(Me.CheckBox2)
End Sub
Private Sub ExecuteCode(IControl As Control)
If TypeName(IControl) = "CheckBox" Then
If IControl.Value = True And Range(IControl.Name).Value = "" Then
Range(IControl.Name).Value = Format(Now, "dd.mm.yyyy")
ElseIf IControl.Value = True And Range(IControl.Name).Font.Color = vbWhite Then
Range(IControl.Name).Font.Color = vbBlack
ElseIf IControl.Value = False And Range(IControl.Name).Value <> "" Then
Range(IControl.Name).Font.Color = vbWhite
End If
End If
End Sub

I just learned this today and thought it might help? Most of the questions, and responses for that matter, to questions regarding checkboxes, listboxes, etc. seem to not distinguish between those forms are inserted directly on the worksheet or are imbedded in a UserForm.
This may work for a checkbox - it works for a ListBox on a UserForm. If you want code to run after a selection, the code you must write has to be in module of the UserForm. I had no clue how to access. Once you have inserted a UserForm in your VBE, add the ListBoxes or whatever to the UserForm. Right click on the UserForm in the upper left project window and select "view code." Here is where you'll place the code, in my case a "change event" such that after a selection is made from the ListBox, other code is automatically run. Her for example:
Sub lstBoxDates_Change()
Dim inputString1 As String
inputString1 = Format(UserForm1.lstBoxDates.Value, "mm-dd-yyyy")
Call EnterDates(inputString1)
Unload Me
End Sub
To explain: Again this code is in the UserForm Module. I named my ListBox,
lstBoxDates. In my main code that I call - EnterDates, I use the variable name = inputString1. The value or date that I have selected from the ListBox is captured from the UserForm1 by UserForm1.lstBoxDates.Value - and I format that to a date, otherwise you see just a number. This is for only one selection.
Because I Dim here, no need to Dim in your main code. The sub for the main code
needs to accept the variable you are passing to it:
Sub EnterDates(inputString1)
This is very generalized but maybe something will click so you can get what you are after. I hope so, for I've worked on this a full two days!!

Related

Textbox Exit Event for unknown Textbox

I am working on a userform which contains numerous (an unknown number) of textbox controls. The number of textboxes on the form is based on certain parameters being met elsewhere within the project.
The number of textboxes on the form could get up to 100 or so and my users are expected to fill in each of them. However, in many cases the value for one textbox will be the same as the textbox positioned directly above it.
I therefore wish to implement functionality to allow the user to enter a "." followed by tabbing out of the field in order to copy the value from the textbox above it.
There are a couple of ways I could go about this initial requirement:
Private Sub TextBox2_Exit(ByVal Cancel As MSForms.ReturnBoolean)
If TextBox2.Value = "." Then
TextBox2.Value = TextBox1.Value
End If
End Sub
This works absolutely fine.. however given that there is no indication here that the textbox2 is below textbox1 (Other than me knowing where I put it) we could do something like:
Private Sub TextBox2_Exit(ByVal Cancel As MSForms.ReturnBoolean)
If TextBox2.Left = TextBox1.Left And TextBox2.Value = "." Then
TextBox2.Value = TextBox1.Value
End If
End Sub
And again this works absolutely fine.
My problem is this, with up to 100 textbox controls I do not particularly want to write and maintain an Exit event for each possible textbox control.
I will probably phrase this incorrectly, but is there a way to trigger the exit event dynamically by passing the name of the textbox the user has tabbed out of?
Or am I lumbered within dozens of identical subs handling the exit of each textbox specifically?
EDIT:
Reading the post linked from Word Nerd, I have the following included with the userform initialize event
Dim tbCollection As Collection
Private Sub UserForm_Initialize()
Dim Ctrl As MSForms.Control
Dim obj As clsTextBox
Set tbCollection = New Collection
For Each Ctrl In usfEnterTime.Controls
If TypeOf Ctrl Is MSForms.TextBox Then
Set obj = New clsTextBox
Set obj.Control = Ctrl
tbCollection.Add obj
End If
Next Ctrl
Set obj = Nothing
Then we have the class module clsTextBox
Private WithEvents MyTextBox As MSForms.TextBox
Public Property Set Control(tb As MSForms.TextBox)
Set MyTextBox = tb
End Property
Private Sub MyTextBox_Change()
If MyTextBox.Value = ". " Then
If MyTextBox.Top = 24 Then
MsgBox "Nothing to copy from"
MyTextBox.Value = ""
Exit Sub
Else
For Each Ctrl In usfEnterTime.Controls
If Ctrl.Left = MyTextBox.Left And Ctrl.Top = MyTextBox.Top - 18 Then
MyTextBox.Value = Ctrl.Value
Application.SendKeys "{TAB}"
Exit Sub
End If
Next Ctrl
End If
Else
Exit Sub
End If
End Sub
My initial requirement was to use the EXIT event to implement a "." + TAB out of textbox to copy from above, however it appears the TextBox control does not have the EXIT event available. So I am using the change event, application.sendkeys and expecting my users to use ". " (Note blank space) to mimic the same functionality and tab to the next textbox in the tab order index using the spacebar instead. As you can see I am simply looping through all controls and checking the relative TOP and LEFT positions to determine which textbox to copy from. 24 is the position of the top most textbox control and so I am capturing this and displaying a msgbox to advise the user there will be nothing to copy from. -18 represents the gap between the tops of two textbox controls.
Working perfectly, thanks to Word Nerd for linking through to the other post.

VBA check if all userform comboboxes have an option selected

I have a userform with many comboboxes and an "Ok" button. What I need is that when pressing that "Ok" button, VBA to check if there's no empty comboboxes. If all comboboxes have some value selected - close the userform otherwise return a message box and clicking "Ok" on that messagebox returns me to the userform with no filled values lost.
I've tried all the methods I could think of:
If PackageOuterRadius = null Then
if PackageOuterRadius is nothing Then
If PackageOuterRadius.value = 0 Then
If IsNull(PackageOuterRadius) = True Then
If IsNull(PackageOuterRadius.value) Then
What I've been trying to do is:
Private Sub Rigid_Filme_Ok_Button_Click()
If PackageOuterRadius.ListCount = 0 Then
MsgBox "Select a ""Package Outer Radius!"
End If
And absolutelly nothing actually checks if the combobox is empty and keeps returning a positive (That there's a value selected)
What could be the solution to this problem? Could someone, please, help me?
If you have few ComboBoxes only, you may insert lines underneath the OK button to check if all the ComboBoxes are filled but if you have too many ComboBoxes on UserForm, you may achieve this with the help of a Class Module and to do so, follow these steps...
Insert a Class Module and rename it to clsUserForm and the place the following code on Class Module...
Code for Class Module:
Public WithEvents mCMB As msforms.ComboBox
Public Sub CheckComboBoxes()
Dim cCtl As msforms.Control
Dim cntCBX As Long
Dim cnt As Long
For Each cCtl In frmMyUserForm.Controls
If TypeName(cCtl) = "ComboBox" Then
cntCBX = cntCBX + 1
If cCtl.Value <> "" Then
cnt = cnt + 1
End If
End If
Next cCtl
If cnt = cntCBX Then
Unload frmMyUserForm
Else
MsgBox "All the ComboBoxes are mandatory.", vbExclamation
End If
End Sub
Then place the following code on UserForm Module. The code assumes that the name of the userForm is frmMyUserForm and the name of the CommandButton is cmdOK.
Code for UserForm Module:
Dim GroupCBX() As New clsUserForm
Dim frm As New clsUserForm
Private Sub cmdOK_Click()
frm.CheckComboBoxes
End Sub
Private Sub UserForm_Initialize()
Dim i As Long
Dim ctl As Control
For Each ctl In Me.Controls
If TypeName(ctl) = "ComboBox" Then
i = i + 1
ReDim Preserve GroupCBX(1 To i)
Set GroupCBX(i).mCMB = ctl
End If
Next ctl
End Sub
And you will be good to go.

VBA loop through buttons on UserForm

I have not used VBA so quite new - but all searches have not given me the answer
its a simple question really. I have a group of buttons in an Excel Form.
The code is very similar when each one is pressed, and for each pressed button, I would like the colour of the button to change. So in reality, I have something like this for each button
UserForm2.CommandButton17.BackColor = RGB(255,255,0)
I would like to go through each button. Check if it is pressed, and then set the colour accordingly.
I actually want to say something like
for counter in 1 to 100
if (ispressed((CommandButton & counter )) then
I have found the following construct:
Dim ctrl as Control
For Each ctrl in UserForm1.Controls
ctrl.BackColor = RGB(255,0,0)
end for
this construct works - but I cant figure out how to check if the button is pressed.
Some of the answers show the above construct, with ctrl.Value = True
but those are for checkboxes and radio buttons. I don't even get the ctrl.Value option with buttons, so I can't use it anyway
Every example of code I have found glosses over this requirement.
Can someone help
Use a ToggleButton instead of a CommandButton if you want it to represent a state.
To initialize a state for each toggle button you can loop through the control.
Dim ctrl As Control
For Each ctrl In Me.Controls
If TypeName(ctrl) = "ToggleButton" Then
ctrl.Value = True 'set button state to pressed
End If
Next ctrl
This sets the state as pressed for every toggle button on the form.
Note that the .Value does not show up in the IntelliSense box because ctrl is of type Control which doesn't have a .Value. If you need IntelliSense then you could workaround like that:
Dim ctrl As Control
For Each ctrl In Me.Controls
If TypeName(ctrl) = "ToggleButton" Then
Dim tggl As ToggleButton
Set tggl = ctrl
tggl.Value = True
End If
Next ctrl
// Edit
Everytime a toggle button gets clicked it triggers a _Click event for that button. So you will need such an event for each button.
Private Sub ToggleButton1_Click()
With Me.ToggleButton1
If .Value = True Then
.BackColor = RGB(255, 0, 0)
Else
.BackColor = -2147483633 'switch to original color
End If
End With
End Sub
Or if you have many buttons, do it more efficiently
'this procedure handles all buttons
Private Sub ToggleButtonClick(ByRef tggl As ToggleButton)
With tggl
If .Value = True Then
.BackColor = RGB(255, 0, 0)
Else
.BackColor = -2147483633 'switch to original color
End If
End With
End Sub
'so you just need to call that function on every _Click event
Private Sub ToggleButton1_Click()
ToggleButtonClick Me.ToggleButton1
End Sub
Private Sub ToggleButton2_Click()
ToggleButtonClick Me.ToggleButton2
End Sub
But you still need a _Click() event for every button to call that procedure.
You can also evaluate the .Value state of each button in the _Click() event to set/unset your asterisk.
I think that the best thing is to work with event and to intercept Press Button
in defining Click() event for all buttons like this
'Form variable to define at begin on code
Dim pbPressed as Control
Dim pbLastPressed as Control
Private Sub pbButton(X)_Click()
'restore previous color only to last pressed
Set pbLastPressed.BackColor = RGB(0,0,155)
Set pbPressed = pbButton(X)
'assign color-pressed to button pressed
pbPressed = RGB(255,0,0)
End sub
where (X) must be replaced by a number as 1 or 2 or 10 !
You can make a fonction, and you obtain
Private Sub pbButton1_Click()
Call ChangeButtonsColor(pbButton1)
End Sub
Private Sub pbButton2_Click()
Call ChangeButtonsColor(pbButton2)
End Sub
Private Sub pbButton3_Click()
Call ChangeButtonsColor(pbButton3)
End Sub
Private Sub ChangeButtonsColor(pb as Button)
'restore previous color only to last pressed
Set pbLastPressed.BackColor = RGB(0,0,155)
Set pbPressed = pb
'assign color-pressed to button pressed
pbPressed = RGB(255,0,0)
End sub
Don't forget to add other event as KeyPress() that can make same action than clicking the Button.
If you have more than 10 buttons, you can perhaps create the buttons dynamically.
I would suggest to implement a class named TglBtn like that
Option Explicit
Private WithEvents m_ToggleButton As MSForms.ToggleButton
Private Sub m_ToggleButton_Click()
With m_ToggleButton
If .Value Then
.BackColor = RGB(255, 255, 0)
Else
.BackColor = &H8000000F
End If
End With
End Sub
Public Property Set Btn(tb As MSForms.ToggleButton)
Set m_ToggleButton = tb
End Property
In the Userform you can use the following code
Option Explicit
Dim mTgBtns As Collection
Private Sub UserForm_Initialize()
Dim sngControl As MSForms.Control
Dim mTglBtn As tglBtn
Set mTgBtns = New Collection
For Each sngControl In Me.Controls
If TypeName(sngControl) = "ToggleButton" Then
Set mTglBtn = New tglBtn
Set mTglBtn.Btn = sngControl
mTgBtns.Add mTglBtn
End If
Next sngControl
End Sub
When you click on one of togglebuttons on your userform the class will take care of the background color.
EDIT
If you want to access the caption of the Togglebutton you could add the following property to the class
Public Property Get Caption() As String
Caption = m_ToggleButton.Caption
End Property
EDIT2
Just as an example of using the property, you could change the Click event as below and everytime you click a MsgBox with the caption of the button will appear
Private Sub m_ToggleButton_Click()
With m_ToggleButton
If .Value Then
.BackColor = RGB(255, 255, 0)
Else
.BackColor = &H8000000F
End If
End With
MsgBox "You pressed the button with the caption " & Me.Caption
End Sub

VBA excel change value of multiple checkboxes

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

Excel VBA activate objects if checkbox ticked

I have got a piece of code that works which changes a input field from disabled to enabled and changes the colour from gray to white if the corresponsding checkbox is ticked.
Is there a way to loop this or call it for all checkboxes & input fields without having a seperate piece of code for each pair?
The code I have is:
Private Sub CheckBox1_Click()
If CheckBox1.Value = True Then
tb01.Enabled = True
tb01.BackColor = vbWhite
Else: tb01.Enabled = False
tb01.BackColor = vb3DLight
End If
End Sub
Private Sub CheckBox2_Click()
If CheckBox2.Value = True Then
tb02.Enabled = True
tb02.BackColor = vbWhite
Else: tb02.Enabled = False
tb02.BackColor = vb3DLight
End If
End Sub
edit: This code is in a UserForm
Checkboxes are a collection in Excel, you can do something like this:
Sub SelectCheckboxes()
Dim CB As CheckBox
For Each CB In Sheet1
If CB.Name <> Sheet1.CheckBoxes("SkipThisCheckbox").Name Then
CB.Value = 2
End If
Next CB
End Sub
You change the value to all checkboxes, except the "SkipThisCheckbox" checkbox.
Edit: The idea is that the checkboxes are a collection and I have translated your question as "show me how to select/check checkboxes together and not one by one".
On a form it should be something like this:
Private Sub CommandButton1_Click()
Dim cCont As Control
For Each cCont In Me.Controls
If TypeName(cCont) = "Checkbox" Then
Debug.Print cCont
'True or False if checked
'Write your logic here.
End If
Next cCont
End Sub
the more versatile approach is a module class one
here follows a possible application to your case, assuming that TextBoxes and CheckBoxes in your UserForm are paired on a "Name" property basis like "CheckBox1"-"TextBox1", "CheckBox2"-"TextBox2", ...
in the UserForm code pane place the following code
Option Explicit
Dim chkBoxes(1 To 4) As ChkBox_TxtBox_Class 'array of type "ChkBoxClass" which you define in a Class Module
Private Sub UserForm_Initialize()
Dim nControls As Integer, i As Integer
nControls = 4 '<== set here the number of CheckBox-TextBox pairs you have in the Form
For i = 1 To nControls
Set chkBoxes(i) = New ChkBox_TxtBox_Class 'initialize a new instance of 'ChkBoxClass' class and store it in the array i-th position
With chkBoxes(i)
Set .ChkBox = Me.Controls("CheckBox" & i) 'assign the correct CheckBox control to its ChkBox property
Set .TxtBox = Me.Controls("TextBox" & i) 'assign the correct TextBox control to its TxtBox property
.ChkBox.value = False 'set "unchecked" as checkbox initial value
.SetTexBox ' call the class method that will have the "paired" textbox adjust its state accordingly to checkbox value
End With
Next i
End Sub
add a "Class Module" to your project
either clicking Insert-> Class Module in the VBA IDE main Ribbon menu
or right-clicking anywhere in the VBA IDE Project Window and selecting Insert -> Class Module in subsequent sub-menus
expand the "Class Module" node in the Project Window
if you don't see the Project Window you can open it by clicking View-> Project Window in the main ribbon menu, or press "Ctrl+R"
select the new Class you added (it should be some "Class1" or the likes) and change its name to "ChkBox_TxtBox_Class" in the Property Window "Name" textbox
if you don't see the Property Window you can open it by clicking View-> Property Window in the main ribbon menu or press "F4"
in the Class Module code pane place the following
Option Explicit
'declare class properties of CheckBox and TextBox type to be "paired" in every instance of this class. the CheckBox one will be associated to events
Public WithEvents ChkBox As MSForms.CheckBox ' ChkBox is a property of the class of type CheckBox. it's associated to events
Public TxtBox As MSForms.TextBox ' TxtBox is a property of the class of type TextBox
' events associated to ChkBox class property
Sub ChkBox_Change()
Call SetTexBox 'call the "method" (i.e. a Sub attached to the class) associated to ChkBox property state change
End Sub
Sub SetTexBox()
'sub that sets the state of the associated TextBox accordingly to the state of the associated CheckBox
With Me.ChkBox
If .value Then
TxtBox.Enabled = True
TxtBox.BackColor = vbWhite
Else
TxtBox.Enabled = False
TxtBox.BackColor = vb3DLight
End If
End With
End Sub
run your routine