Excel VBA: GroupName property from grouped ActiveX Checkbox in Worksheet - excel

I am trying to loop through a ActiveX checkboxes that are grouped together in a frame on a worksheet. I have been able to find all the checkboxes, but I am not able to get the GroupName properties through the VBA code. To figure out the script I have been just using a simple workbook that has two checkboxes grouped in a frame that are simply named Checkbox1 and Checkbox2 and they have the same GroupName. This is what I have so far
Sub test2()
Dim i As Integer
Dim cb As Object
Dim countItems As Integer
Dim checkBox As Object
For Each cb In ActiveSheet.Shapes
If cb.Name Like "Group*" Then
countItems = cb.GroupItems.Count
For i = 1 To countItems
If cb.GroupItems(i).Name Like "Check*" Then
Debug.Print cb.GroupItems(i).Name
End If
Next i
End If
Next cb
End Sub
I have been searching around the internet for solutions, but the ones that I have seen do not seem to fit because my checkboxes are grouped together.
Sub test4()
Dim ole As OLEObject
For Each ole In ActiveSheet.OLEObjects
If TypeName(ole.Object) = "CheckBox" Then
Debug.Print ole.Object.GroupName
If ole.Object.GroupName = Group And ole.Object.Value = True Then
Debug.Print ole.Object.GroupName
End If
End If
Next ole
GroupClear = True
End Sub
This seemed to work to find the checkboxes that were in the worksheet just fine but not grouped together.
Thank you for the help

The comment 1 helped getting to the right spot. The grouping seemed to require a bit of digging though the properties to get to the Object.Object.GroupName
Debug.Print cb.OLEFormat.Object.Interior.Parent.ShapeRange.GroupItems(i).Parent.Item(i).OLEFormat.Object.Object.GroupName
After the Object.Object property most of the properties were available for searching or edit that were useful in extra control of the checkbox.

Related

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 :)

When looping over shapes in a document I get only Comment types even though it has many drop down menues

I have a file that someone made and I was tasked with simply adding an autoupdater function that updates the cell next to the dropdown menu.
The way the dropdown menu is created is by going to data validation and selecting list and make list in cell. The values are read from elsewhere.
Now, what I tried was to loop over all shapes like this:
Dim dd As DropDown
Dim i As Integer
Debug.Print Sheet1.DropDowns.Count
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
Dim s As Shape
For Each s In ws.Shapes
Debug.Print CStr(s.Type)
Next
Next
End Sub
This prints the following:4 is a comment, 8 is a control form
444444444444444444444444444
8
So even though I have many drop down menus none come out when I loop over them.
I wanted to make it so that anyone can add a dropdown box and my code would attach an OnAction Sub that fills in the cell next to the dropdown box so the user can add as many boxes they want, but they have to only remember to keep the cell next to it, to the right for example, empty as it will be overridden.
Dim sh As Shape
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
For Each sh In ws.Shapes
If sh.Type = msoFormControl Then
If sh.FormControlType = xlListBox Then
sh.OLEFormat.Object.OnAction = "UpdateLBCell"
End If
End If
Next
Next
The original code above causes an object error on the innermost line.
Am I just stupid or is it not possible to loop over these dropdown boxes?
If it is impossible, can I make some other dropdown single select boxes that fit inside a cell? Combobox I tried, but they lie on top and dont match.
Any insight in alternative ways to do this is very appreciated as well.
I put a list validation on a few cells, then ran this code
Sub Test()
Dim dd As DropDown
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
Dim s As Shape
For Each s In ws.Shapes
Debug.Print CStr(s.Type), s.Top, s.Left
s.Visible = msoCTrue '<<<<
Next
Next
End Sub
Before and after (yellow cells have data validation):
So it seems as though if you have a "list" data validation set up, Excel manages a single (normally invisible and empty) drop-down which is typically positioned at the current active cell. It's only made visible when that's also one of the cells with validation set up.
EDIT: here's an example of how you could handle updates to cells with drop-down DV lists in a generic way -
Private Sub Worksheet_Change(ByVal Target As Range)
Dim c As Range
On Error GoTo haveError
Application.EnableEvents = False
For Each c In Target.Cells
If HasDVList(c) Then
c.Offset(0, 1) = Now
End If
Next c
haveError:
Application.EnableEvents = True
End Sub
'does a cell have DV list?
Function HasDVList(rng As Range)
Dim v
On Error Resume Next
v = rng.Cells(1).Validation.Type
On Error GoTo 0
HasDVList = (v = 3)
End Function
The Shape should be Visible, whether the cell is "clicked-on" or not. I put a single DV dropdown on a sheet and ran:
Sub ShapeLister()
Dim s As Shape
For Each s In ActiveSheet.Shapes
MsgBox s.Type & vbCrLf & s.Name
Next s
End Sub
and got:
Try this on a fresh worksheet and tell us what you see.

Why does grouping ActiveX checkboxes alter OLEObject visibility

I have some code that loops through the ActiveX controls on an Excel worksheet. This logs which checkboxes have been selected.
Dim obj AS OLEObject
For Each obj In ActiveSheet.OLEObjects
If TypeName(obj.Object) = "CheckBox" Then ' loop through all checkboxes to find selections
BooCheck = obj.Object
If BooCheck = True Then
MyArray(j) = obj.Name 'if checkbox selected then store the associated Name
j = j + 1
End If
End If
Next obj
This all works fine. However, as I have a number of checkboxes that I need to move around I thought I'd group them together by Shift/click in design mode, right click and select the "Group" option. However, if I do this the grouped checkboxes vanish from OLEObjects. Where do they go? Is there a way of altering my code to find them when they are grouped?
The way to reference the OLEObjects is like this:
Public Sub ReferenceTest(oSheet As Worksheet, sGroupName As String)
Dim i As Long
Dim oOle As OLEObject
With oSheet.Shapes.Range(sGroupName).GroupItems
For i = 1 To .Count
Set oOle = .Item(i).OLEFormat.Object
Debug.Print oOle.Name, oOle.Object.Value
Next i
End With
End Sub
Just specify the sheet and group name, e.g.
ReferenceTest ActiveSheet, "Group 1"

Check box general selector and macro to show next three rows when one checkbox is selected

I am new to macros so I'm not sure this is possible in VBA.
I am trying to create a document where is composed with many mini tables made of 4 rows.
One row is the title which have a checkbox and will always be shown and three rows below where contains data that I only what to see when I select the relevant checkbox.
This document will have many mini tables hence many check boxes and I was wondering if there is a generic selector for checkboxes where I can apply the same macro.
I have seen the following macro, but this will apply only to one check box and I was wondering if there was a way to apply one for all checkboxes saying that if checkbox in row 4 is selected then show row 5,6 and 7. If checkbox in row 8 is selected then show rows 9,10,and 11 and so on....
Private Sub CheckBoxRow4_Click()
Rows("5:6:7").Hidden = CheckBoxRow4.Value
End Sub
See screenshot for a better idea.
It would also be appreciated if you could indicate how can I get those three rows below hidden by default when opening the document.
I am using Excel 2011 for Mac if that makes any difference.
Thank you in advance.
I'm sure there will be several approaches to this. My first thought goes to adding checkboxes, linking them all to a single macro. When activated, you have to do several things:
find out who is calling the sub (which checkbox);
find out where that specific checkbox is located (which row);
hide / unhide the rows below it.
1:
The name of the checkbox is easy. Application Caller will give you that.
2:
Location is the real problem here. I don't see a simple solution here, other then giving the checkboxes such specific names, that it is clear which row it is in. If you add a checkbox, you can give the name in the 'named range' inputfield. If you give it names that will specify the rows it must hide, it is even better. So something like:
HIDE_4_7 would indicate the checkbox must hide / unhide rows 4 to 7.
3:
Hiding the rows is now easy.
total solution:
Sub HideRows()
Dim cbName As String
Dim cbValue As Boolean
Dim s() As String
Dim firstRow As Long
Dim lastRow As Long
On Error Resume Next
cbName = Application.Caller
If Err.Number <> 0 Then Exit Sub 'sub is not called from an application object
cbValue = (ActiveSheet.CheckBoxes(cbName) = xlOn)
If Err.Number <> 0 Then Exit Sub 'sub is not called from a checkbox
On Error GoTo 0
s = Split(cbName, "_")
If s(LBound(s)) <> "HIDE" Then Exit Sub 'name of the shape is not valid
firstRow = Val(s(LBound(s) + 1))
lastRow = Val(s(LBound(s) + 2))
Sheets(1).Rows(firstRow & ":" & lastRow).Hidden = Not cbValue
End Sub
You would have to call the checkboxes HIDE_*firstrow*_*lastrow*, and link them to this sub. That works on my side.
EDIT
To hide all rows on opening, you could use the Workbook_Open sub (in the workbook code storage thingy). Something like this:
Private Sub Workbook_Open()
Dim shp As Shape
Dim s() As String
Dim firstRow As Long
Dim lastRow As Long
Dim cbValue As Boolean
For Each shp In Sheets(1).Shapes
Debug.Print shp.Name
s = Split(shp.Name, "_")
If s(LBound(s)) <> "HIDE" Then GoTo nextShp
'set checkbox off:
Sheets(1).CheckBoxes(shp.Name) = xlOff
firstRow = Val(s(LBound(s) + 1))
lastRow = Val(s(LBound(s) + 2))
Sheets(1).Rows(firstRow & ":" & lastRow).Hidden = True
nextShp:
Next shp
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