Determining Control X checkbox Name in VBA - excel

Can someone tell me what the syntax is to determine the controlX checkbox name?
I have approximately 4 check boxes and this may potentially grow, so I'd like a method of passing through the checkbox name dynamically rather than writing the same execution 4-9 times.
My intention is to pass through the checkbox name as a variable so I do not have to repeat the below code for each checkbox. Also, does anyone know how to reference a named range to a specific checkbox? The code I have so far is:
Sub CheckBox1_Click()
Application.ScreenUpdating = False
Dim strCheck As String
strCheck = CheckBox1.Value
If strCheck = True Then
Range("RevAssp_CCV").Select
Selection.EntireRow.Hidden = False
Else
Range("RevAssp_CCV").Select
Selection.EntireRow.Hidden = True
End If
End Sub
Thanks in advance

The easiest way would be to create a custom wrapper class, create an array of objects of said class and then hook into the event there.
You can then (for example) check the Caption and set the "hidden" of the NamedRange.EntireRow equal to the value (e.g. checked is invisible, unchecked is visible)
The most basic implementation of this would be as follows:
CustomCheckBox Class module:
Private WithEvents p_chkBox As MSForms.checkbox
Public Property Let box(value As MSForms.checkbox)
Set p_chkBox = value
End Property
Public Property Get box() As MSForms.checkbox
Set box = p_chkBox
End Property
Private Sub p_chkBox_Click()
Range(p_chkBox.Caption).EntireRow.Hidden = p_chkBox.value
End Sub
And in a regular module:
Public cCheckBox() As CustomCheckBox
Sub Test()
Dim ws As Worksheet
Dim oleObj As OLEObject
Dim i As Integer
i = 0
For Each ws In ThisWorkbook.Worksheets
For Each oleObj In ws.OLEObjects
If TypeName(oleObj.Object) = "CheckBox" Then
ReDim Preserve cCheckBox(0 To i)
Set cCheckBox(i) = New CustomCheckBox
cCheckBox(i).box = oleObj.Object
i = i + 1
End If
Next oleObj
Next ws
End Sub
The regular module puts all checkboxes into 1 array, which is a public variable so it will be available even after the code has run. You could also place this code in the Workbook Module as Private Sub Workbook_Open to ensure that the checkboxes will be initialized properly in all cases.
Keep in mind that if the Named Range for the caption of the Checkbox doesn't exist, this will throw errors.
To get back to your example, you could now just add two checkboxes on your sheets and set the caption of the first one to "RevAssp_CCV" and the second one to whatever other named range you wish to toggle.

Related

How assign the name of a check box to a cell in Excel

I have created a check box in excel and I would like that when I click on the check box name be assigned to a cell in another sheet.
how can i do that?
Here is what I tried but I get an error 424
Private Sub checkbox1_click()
Worksheets("Produits").Range("A1") = checkbox1.Caption
End Sub
Ps: I use Office2019
For the sake of simplicity I will assume that your checkbox is located in a worksheet named Test . I will also assume that when the checkbox is unchecked the corresponding cell should be emptied.
For an ActiveX type of checkbox, named CheckBox1, your code should be located in the same worksheet as the checkbox and should look like so:
Option Explicit
Sub CheckBox1_Click()
Dim sht As Worksheet
Set sht = ThisWorkbook.Worksheets("Produits")
If CheckBox1.Value Then
sht.Range("A1") = CheckBox1.Caption
Else
sht.Range("A1").ClearContents
End If
End Sub
For a Form Control type of checkbox, named Check Box 2, your code should be located in a module and should look like so:
Option Explicit
Sub CheckBox2_Click()
Dim cb As CheckBox
Dim sht1 As Worksheet
Dim sht2 As Worksheet
Set sht1 = ThisWorkbook.Worksheets("Test")
Set sht2 = ThisWorkbook.Worksheets("Produits")
Set cb = sht1.CheckBoxes("Check Box 2") 'whatever the name of your checkbox is...
If cb.Value = 1 Then
sht2.Range("A2") = cb.Caption
Else
sht2.Range("A2").ClearContents
End If
End Sub
You forgot about .Value
Try something like that:
ActiveWorkbook.Sheets("YourSheet").Range("A1").Value = CheckBox1.Caption
But I don't know what's the point about your case. Maybe you'll fined that way more helpful.
If CheckBox1.Value = True Then
ActiveWorkbook.Sheets("YourSheet").Range("A1").Value = CheckBox1.Caption
End If
If CheckBox1.Value = False Then
ActiveWorkbook.Sheets("YourSheet").Range("A1").ClearContents
End If

Excel VBA Events of dynamically defined CheckBoxes not triggering

This is my first post on this forum. So far I've managed to solve every difficulty by browsing the available answers. This time I can't.
I used this example to create a self populating userform (Userform1) with some checkboxes with events defined in the way described on the example, by Creating a class module with the code to run and assigning the class sub to the checkbox.
The UserForm1 was then replicated to several case scenarios and all works fine when these userforms are called explicitly by their names (ex: UserForm1.show) but now I call the several userforms in a "for" cicle that runs through another set of checkboxes in my worksheet to decide which userforms to initialize. Each userform is stored in an object variable (UForm) through a function based on its name, and then it is initialized, and now the events of the userforms' checkboxes do not trigger!!
Sub Test()
Dim chk As Object
Dim Uform As Object
Dim strForm as String
Dim MMarray(0 To 3, 1) As String '3 so far, more to be added
MMarray(0, 0) = "Chk1": MMarray(0, 1) = "UserForm1"
MMarray(1, 0) = "Chk2": MMarray(1, 1) = "UserForm2"
MMarray(2, 0) = "Chk3": MMarray(2, 1) = "UserForm3"
MMarray(3, 0) = "Chk4": MMarray(3, 1) = "UserForm4"
' #############################
' initializing global variables defined elsewhere
iMM = 0 '
ReDim data_ini(0, 0)
ReDim data_MM_tot(0, 1)
For i = 0 To UBound(MMarray)
Set chk = ActiveSheet.Shapes(MMarray(i, 0))
If chk.OLEFormat.Object.value = 1 Then
strForm = MMarray(i, 1)
Set Uform = GetFormObjectbyName(strForm)
Uform.Show
Call Uform.repor 'this is another sub in the userform code
End If
Next i
End Sub
I assume the issue has to do with the fact that there is an ongoing procedure when the form is shown and that's why the events can't be triggered.
Is there a way to get the events to be triggered in these circumstances?
Thanks a lot for your help.
Using your example as a base, you could have a class, like so
Private WithEvents cbCustom As MSForms.CheckBox
Private strFormName As String
Public Sub Init(cbInput As MSForms.CheckBox)
Dim ctl As Control
Set cbCustom = cbInput
Set ctl = cbInput
strFormName = ctl.Parent.Name
End Sub
Private Sub cbCustom_Click()
Select Case strFormName
Case "Userform1"
Select Case cbCustom.Name
Case "Checkbox1"
Case Else
End Select
Case "Userform2"
End Select
End Sub
I used the following in my userform
Private c As New clsCustomCheckBox
Private Sub UserForm_Initialize()
c.Init Me.CheckBox1
End Sub

VBA excel control multiple checkboxes with 1 macro

I have a quite simple macro to hide the row the checkbox is in when clicked. It works but the problem is there are A LOT of rows.
Private Sub CheckBox3_Click()
[3:3].EntireRow.Hidden = CheckBox3.Value
Range("AB3").Value = True
End Sub
I of course can make a separate macro for every single checkbox i have (all 250 of them), but i hope i can avoid a macropage that is 6 pages long.
My question is: is there a way to combine it into 1? the only thing that is different for all of them is the number (in the example its 3).
You should create those CheckBoxes programatically and create Class Module to handle events. Here is example how to achieve it:
ClassModule i.e. MyCheckBox:
Option Explicit
Dim WithEvents m_CheckBox As MSForms.CheckBox
Dim m_Row As Long
Public Sub CreateCheckBox(ByVal sh As Worksheet, ByVal rowNumber As Long)
m_Row = rowNumber
Set m_CheckBox = sh.OLEObjects.Add(ClassType:="Forms.CheckBox.1", Left:=sh.Cells(rowNumber, 1).Left, Top:=sh.Cells(rowNumber, 1).Top, Width:=108, Height:=19.5).Object
End Sub
Private Sub m_CheckBox_Change()
MsgBox "I'm in row " & m_Row & " MyValue is " & m_CheckBox.Value
End Sub
Module using this code:
Option Explicit
Dim chkBoxes As New Collection
Public Sub test()
Dim sh As Worksheet
Dim chk As MyCheckBox
Set sh = ActiveSheet
Set chk = New MyCheckBox
chk.CreateCheckBox sh, 1
chkBoxes.Add chk
Set chk = New MyCheckBox
chk.CreateCheckBox sh, 2
chkBoxes.Add chk
End Sub
Of course method CreateCheckBox has to be adjusted to your needs (my one is creating checkbox in first column). Global collection in module is required otherwise classes are destroyed automatically when sub is finished and events are never fired. If workbook is opened and closed then this code must be upgraded to either:
1) Recreate classes on Workbook event i.e. open
2) Recreate CheckBoxes every time workbook is opened

Assigning macro to Excel ActiveX ComboBox [duplicate]

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

Make vba code work for all boxes

Hello so what i want to do is make this code work for all Check Box's 1-50 I want the code to only effect the box that is clicked.
Private Sub CheckBox1_Click()
If MsgBox("Do you want to lock this box?", vbYesNo, "Warning") = vbYes Then
ActiveSheet.CheckBox2.Enabled = False
Else
End If
End Sub
I see several options (none of which are pretty since this is VBA).
Option 1: generate the code for all of your check boxes. This is probably the most maintainable. You would first choose reasonable names for all your check boxes (you can assign them by selecting them in Excel and renaming in the top left corner, or run code which will do this for you if you already have a lot of check boxes. This may be useful).
You can then generate the code and have each one of your subprocedues as follows:
'example code for one checkbox
Private Sub chkBox_1_Click()
Call lockMeUp(Sheet1.chkBox_1.Object)
End Sub
After you're done with all your code for each checkbox, you could have your lockMeUp subprocedure as follows:
Sub lockMeUp(chkBox as Object)
If MsgBox("Do you want to lock this box?", vbYesNo, "Warning") = vbYes Then
chkBox.Enabled = False
End If
End Sub
Option 2: Keep track of all your checked/unchecked statuses through either an Array or a "Settings" hidden sheet, and watch out for that triggered event. You could fire off based off of a sheet's Changed event, and match the row number to your CheckBox number so that you can go off of the Target's row number.
Other options I can think of become more convoluted... I'd be interested to see what other suggestions people have. Thanks!
EDIT You can use some code to refer to a single function as in my example, in conjunction with brettdj's example to get your optimal solution. Bam!
The easy way is to write a class module that will apply one code routine to a collection of Checkboxes
Assuming yu want to run this on all ActiveX checkboxes on the ActiveSheet, then borrowing heavily from Bob Phillip's code from VBAX
Insert a Class Module named clsActiveXEvents
Option Explicit
Public WithEvents mCheckboxes As MSForms.CheckBox
Private Sub mCheckboxes_Click()
mCheckboxes.Enabled = (MsgBox("Do you want to lock this box?", vbYesNo, "Warning") = vbNo)
End Sub
In a normal module use this code
Dim mcolEvents As Collection
Sub Test()
Dim cCBEvents As clsActiveXEvents
Dim shp As Shape
Set mcolEvents = New Collection
For Each shp In ActiveSheet.Shapes
If shp.Type = msoOLEControlObject Then
If TypeName(shp.OLEFormat.Object.Object) = "CheckBox" Then
Set cCBEvents = New clsActiveXEvents
Set cCBEvents.mCheckboxes = shp.OLEFormat.Object.Object
mcolEvents.Add cCBEvents
End If
End If
Next
End Sub
In case you do not know, all Form Controls are treated as Shapes in a Worksheet.
I have a solution that you need to create a new Module, copy-paste in code below and then from Immediate window to the same module. With some assumptions:
All Check Box Objects are named "Check Box #" where # is a number
No macro named ResetCheckBoxes() in any other modules of the workbook
No macro named CheckBox#_Click() in any other modules of the workbook
Run this ResetCheckBoxes once to enable check boxes and Assign a macro to it for you, with relevant generated codes in the immediate window (you might want to put a pause in the loop every 25 check boxes as line buffer in it are limited).
Sub ResetCheckBoxes()
Dim oWS As Worksheet, oSh As Shape, sTmp As String
Set oWS = ThisWorkbook.ActiveSheet
For Each oSh In oWS.Shapes
With oSh
If .Type = msoFormControl Then
If InStr(1, .Name, "Check Box", vbTextCompare) = 1 Then
.ControlFormat.Enabled = True
sTmp = "CheckBox" & Replace(oSh.Name, "Check Box ", "") & "_Click"
.OnAction = sTmp
Debug.Print "Sub " & sTmp & "()"
Debug.Print vbTab & "ActiveSheet.Shapes(""" & .Name & """).ControlFormat.Enabled = False"
Debug.Print "End Sub" & vbCrLf
End If
End If
End With
Next
End Sub
Example Immediate window output (2 test check boxes):
Happy New Year mate!
To build on the solution offered by #brettdj, since he is specifying ActiveX Controls, I would suggest the following in the Standard Module:
Dim mcolEvents As Collection
Sub Test()
Dim cCBEvents As clsActiveXEvents
Dim o As OLEObject
Set mcolEvents = New Collection
For Each o In ActiveSheet.OLEObjects
If TypeName(o.Object) = "CheckBox" Then
Set cCBEvents = New clsActiveXEvents
Set cCBEvents.mCheckboxes = o.Object
mcolEvents.Add cCBEvents, o.Name
End If
Next
End Sub
The differences are:
I use the OLEObjects Collection because it is more direct and doesn't waste time on non-OLE shapes.
I use TypeName instead of (the mysterious) TypeOf operator because (apparently) the later does not discriminate between OptionButton and CheckBox.
I register the Object Name as Key in the Collection to allow for efficient indexing if required.
EDIT:
I should have followed the link provided by #brettdj before posting. My solution is using the same principles as are outlined there. Hopefully, its convenient to have it documented here as well?

Resources