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
Related
I am trying to check all command button in a user form for enabled state and if state is false then enabling it. I have tried this code but runs without error but changes nothing where I manually disable button. It suppose to enable them. Would you please check the code. I get the code from this page
https://stackoverflow.com/questions/49050223/vba-loop-through-buttons-on-userform
Sub form_reset()
Dim ctrl1 As Control
For Each ctrl1 In frmview.Controls
If TypeName(ctrl1) = "commandbutton" Then
Dim cmdbtn As CommandButton
Set cmdbtn = ctrl
If cmdbtn.Enabled = False Then
cmdbtn.Enabled = True
End If
End If
Next
frmview.Show
Try this code
Sub Form_Reset()
Dim ctrl As Control
For Each ctrl In frmview.Controls
If TypeName(ctrl) = "CommandButton" Then ctrl.Enabled = True
Next ctrl
frmview.Show
End Sub
Ok I have figure it out the problem with this code is it only work the button names like btn1, btn2 like. It will not checking the type of control thus making no changes. I was thinking of deleting this post but then think this might save someones time.
Sub form_reset()
Dim ctrl1 As Control
Dim cmdbtn As msforms.CommandButton
For Each ctrl1 In frmview.Controls
If TypeOf ctrl1 Is msforms.CommandButton Then
Set cmdbtn = ctrl1
If cmdbtn.Enabled = False Then
cmdbtn.Enabled = True
End If
End If
Next
frmview.Show
End Sub
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 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
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'm currently trying to call a Sub within the onclick event of a checkbox. What I want to do is grab the groupName, Name and Value parameters of the checkbox that I have clicked and feed them into a sub which uses the variables to toggle the rest of the checkboxes in the group to be hidden or unhidden. However, there are multiple group checkboxes that I want to have the same functionality and rather than create multiple functions for each onclick event I want to just be able to call the sub which processes the change in visibility each time.
Consider the image above. If I set the 'Admin' checkbox to be false the 'Old versions' checkbox within the same box should be hidden. If I check it to true, it should be shown again. I also want to do the same with the client box. I've looked online and many people recommend using ActiveControl.Value or Application.Caller but none of these seem to work. The two subs I am using so far can be found below. In it's current state, the application errors at
admin_value = ActiveControl.Value
Here is the full code snippet:
Private Sub admin_toggle_Click()
Dim admin_group As String
Dim admin_name As String
Dim admin_value As String
admin_value = ActiveControl.Value
admin_name = ActiveControl.Name
admin_group = ActiveControl.GroupName
Call toggle_options
End Sub
Private Sub toggle_options()
Dim ctl As Control
For Each ctl In Me.Controls
If TypeOf ctl Is MSForms.CheckBox Then
If ctl.GroupName = admin_group Then
If ctl.Name <> admin_name Then
If admin_value = True Then
ctl.Visible = True
Else
ctl.Visible = False
End If
End If
End If
End If
Next ctl
End Sub
Thanks in advance.
Is this what you are trying?
Private Sub admin_toggle_Click()
Dim ctl As Control
Set ctl = admin_frame.ActiveControl
ShowHideControls admin_frame, ctl
End Sub
Sub ShowHideControls(frme As Control, cb As Control)
Dim c As Control
For Each c In frme.Controls
If TypeOf c Is MSForms.CheckBox Then
If c.Name <> cb.Name Then
If cb.Value = True Then
c.Visible = True
Else
c.Visible = False
End If
End If
End If
Next
End Sub