Make vba code work for all boxes - excel

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?

Related

Can VBA interact with check boxes that were placed without VBA

I am trying to write a macro that will scrape data from a huge set of already existing excel files. These files have the same format just with different info. I am able to pull any data written in a cell, even if it's in the form of a drop down. However, since checkboxes are not tied to a cell range, I'm not sure how to interact with them in VBA.
I've tried using
? Activesheet.checkbox1.value
? Activesheet.checkbox(1).value
but it's not giving me anything.
Is there a way to reference these? I'm guessing they were placed using "developer tab -> insert objects" so there is no userform area in the project tree in VBA
Thanks,
Try this code to find checkboxes on the worksheet:
Sub InspectCheckBoxes()
Dim sh As Shape
For Each sh In ActiveSheet.Shapes
Select Case sh.Type ' see MsoShapeType enumeration
Case msoFormControl
If sh.FormControlType = xlCheckBox Then ' see XlFormControl enumeration
Debug.Print "Found CheckBox as FormControl named '" & sh.Name & "' at left "; sh.Left & "; top " & sh.Top
End If
Case msoOLEControlObject
If InStr(1, sh.OLEFormat.progID, "CheckBox", vbTextCompare) > 0 Then
Debug.Print "Found CheckBox as OLEControlObject named '" & sh.Name & "' at left "; sh.Left & "; top " & sh.Top
End If
End Select
Next
End Sub
Prints:
Found CheckBox as FormControl named 'Check Box 2' at left 207; top 78
Found CheckBox as OLEControlObject named 'CheckBox1' at left 244,5; top 142,5
You didn't specify which type of checkboxes...
Dim cbx As Excel.CheckBox
Dim msfCbx As msforms.CheckBox
Dim ole As OLEObject
Debug.Print "Forms type checkboxes"
For Each cbx In ActiveSheet.CheckBoxes
Debug.Print cbx.Value, cbx.Name, cbx.Caption, cbx.TopLeftCell.Address
Next
Debug.Print "ActiveX checkboxes"
For Each ole In ActiveSheet.OLEObjects
If ole.progID = "Forms.CheckBox.1" Then
Set msfCbx = ole.Object
Debug.Print msfCbx.Value, ole.Name, msfCbx.Caption, ole.TopLeftCell.Address
End If
Next
Both previous answers here assumed your checkboxes were on worksheets. Both checkbox types include a 'linked-cell' property which changes a cell's value.
Checkboxes on a userform are can't be directly linked to cells, as you say. Code in a checkbox event can perform some action, or other code might read the checkbox state later when required,
Unless I'm missing something the only way you might retrieve any useful data, just by reading checkbox values on a loaded userform, is if you can deduce some linked cell range simply from the checkbox's name. It would also seem likely code in the userform's initialize event, or perhaps a 'get data' type button, reads cells and updates checkboxes at runtime.
If that's the case try this - add a button to your form
Private Sub CommandButton1_Click()
Dim ctl As MSForms.Control
For Each ctl In Me.Controls
If TypeName(ctl) = "CheckBox" Then
Debug.Print ctl.Value, ctl.Name
End If
Next
End Sub

Set Listbox selection as variable

I want to create something like a "Patch" file which I can send to users and they use it to patch their existing templates.
What I'm trying to do is to have a userform with a listbox that shows all currently opened Excel files, users then select the file they want to patch and click a button to run the patch script.
Am very new to userforms and vba as a whole, and am having difficulty trying to set the 'Listbox1.Selection' as a variable that the subsequent patch code can refer to. My code currently for the userform/listbox is as below (It just allows selection of item:
Private Sub UserForm_Activate()
Dim wb As Workbook
For Each wb In Workbooks
If Windows(wb.Name).Visible Then _
ListBox1.AddItem wb.Name
Next
End Sub
Once users select the file, how do I go about setting that as a variable?
How do I go about setting that as a variable?
Private Sub doPatch()
With Me.ListBox1
Dim currIndex&
currIndex = .ListIndex ' assign zerobased index number to variable
' how do I go about setting that as a variable?
Dim currWB
currWB = .List(currIndex, 0) ' get chosen list element in column zero based on current index
' 'or simply:
' currWB = .Value ' sufficient in your case as only one column listed
' display both variables in immediate window of your VB Editor
Debug.Print "zerobased Listindex#: " & currIndex & " ~> " & currWB
' do patch stuff...
End With
End Sub
Eventually ou could call the above procedure either by a command button and/or by doubleclick, e.g. via
Private Sub CommandButton1_Click()
doPatch
End Sub
Private Sub ListBox1_DblClick(ByVal Cancel As MSForms.ReturnBoolean)
doPatch
End Sub
Add a Command button to your UserForm and add the following code:
Private Sub CommandButton1_Click()
ActiveSheet.Range("A1").Value = ListBox1.Text
End Sub
That will print the Selected option to the A1. You can save it to variable to anything further you want.
Basically ListBox1.Text will give you the selected option.

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

Determining Control X checkbox Name in VBA

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.

VBA - Value of an option button in a frame (within an Excel sheet)

I'm having problems with shapes, frames and option buttons... I'm a total newbie, I've never used them. I just put several option buttons on an Excel sheet (Using the FORM toolbox).
I'm trying to check whether my optionbutton is filled or not. So far, I've done the following :
Sub SX_EXTERNE()
Dim Ws As Worksheet
Dim ConBut As Shape
Dim Answer As String
Set Ws = ThisWorkbook.Sheets("Externe")
For Each ConBut In Ws.Shapes
If ConBut.Type = msoFormControl Then
If ConBut.FormControlType = xlOptionButton Then
If ConBut.ControlFormat.Value = xlOn Then
Answer = ConBut.Name
End If
End If
End If
Next ConBut
MsgBox Answer
End Sub
My problem is I do not know how to check only in a selected frame (i.e. "Conges_generaux" for my example):
Could you please give me a hint? I've seen many subjects about that but many of them treat of ActiveXControls... I don't even know the difference.
Thanks
Here is a quick way
Sub Sample()
Dim optBtn As OptionButton
For Each optBtn In ActiveSheet.OptionButtons
If optBtn.Value = 1 Then
Debug.Print optBtn.Name
Debug.Print optBtn.GroupBox.Name
End If
Next
End Sub
So in your code change Dim ConBut As Shape to Dim ConBut As OptionButton. Feel free to put relevant checks and store it in the relevant answer variable :)

Resources