Excel VBA Events of dynamically defined CheckBoxes not triggering - excel

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

Related

Multiple Checkboxes Conditions in VBA - No Two or more Checkboxes to be selected

I need the help with the selection of multiple checkboxes by user. I have a userform in vba that has 8 checkboxes. I want user to select only ONE checkbox at a time. What is the shortest coding possible?
Please, try the next way:
Create a Sub able to untick all the check boxes, except the active one:
Public Sub UnTickCheckBoxes() 'Public to be also called from outside if use a class for event allocation
Dim i As Long
For i = 0 To Me.Controls.count - 1
If TypeName(Me.Controls(i)) = "CheckBox" And Not ActiveControl.Name = Me.Controls(i).Name Then
Application.EnableEvents = False
Me.Controls(i).value = False
Application.EnableEvents = True
End If
Next i
End Sub
Call the above sub from the Click event of all check boxes. You can use Change event for doing something else. The call can be done in this way:
Private Sub CheckBox1_Click()
If ActiveControl.value = True Then
UnTickCheckBoxes
End If
End Sub
2 bis. Automatic event allocation to all check boxes:
2.1 Create a class named "chkBoxesClickEvent". Copy and paste in it the next code:
Option Explicit
Public WithEvents chkBEvent As MSForms.CheckBox
Private Sub chkBEvent_Click()
If chkBEvent.value = True Then
chkBEvent.Parent.UnTickCheckBoxes
End If
End Sub
2.2 Declare on top of the form (in the declarations area) the next variable:
Private chkB() As New chkBoxesClickEvent
2.3 Place the next code in the UserForm_Initialize event:
Private Sub UserForm_Initialize()
Dim C As MSForms.Control, k As Long
ReDim chkB(Me.Controls.count): k = 1
For Each C In Me.Controls
If TypeOf C Is MSForms.CheckBox Then
Set chkB(k).chkBEvent = C: k = k + 1
End If
Next
ReDim Preserve chkB(k - 1)
End Sub
Do not forget to clear all check boxes Click event, if any code inside...
Show the form a see how it works, then send some feedback.

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.

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

VBA Userforms Show the Same Userform again and again

currently i am programming a excel macro. The macro shows a Userform.
In the Userform the User can Select something. After the User has selected something i call Userform.Hide to Hide the Userform and to read the Selection from the Form. After the selection was read i call Unload Userform. Now the Code interacts with the selection. I want to do this in a loop but when the Code trys to show the Userform the second time. I get a exception that the Form is already displayed. I cant understand it, because i called Unload Userform. When i do it in debug mode everthing works as it should.
Userform Code
Private Sub Image1_Click()
SelectCard 1
End Sub
Private Sub Image2_Click()
SelectCard 2
End Sub
Private Sub SelectCard(number As Integer)
SelectedNumber = number
Me.Hide
End Sub
Public Sub CardSelector_Activate(Cards As Cards)
Dim c As card
For Each Key In Cards.CardDictionary.Keys
Set c = Cards.CardDictionary.Items(Key - 1)
If c.value = 1 And c.played Then
Image1.Enabled = False
End If
If c.value = 2 And c.played Then
Image2.Enabled = False
End If
Next Key
number = SelectedNumber
CardSelector.Show
End Sub
Code in the ClassModule i call this in a loop
Sub Costum(Spalte As Integer, Zeile As Integer, SpalteBeginn As Integer, Cards As Cards, CardsOpponent As Cards)
CardSelector.CardSelector_Activate Cards
Dim c As card
Dim number As Integer
number = CardSelector.SelectedNumber
Set c = Cards.CardDictionary.Items(CardSelector.SelectedNumber - 1)
SetCardAsPlaced c, Zeile, Spalte, SpalteBeginn
Unload CardSelector
End Sub
Can someone help me here ?
I am not sure if I fully understand your issue, but this is how I invoke a form using VBA. This is assuming you have a Cancel and OK button:
In the form:
Option Explicit
Private m_ResultCode As VbMsgBoxResult
Private Sub btnCancel_Click()
Call CloseWithResult(vbCancel)
End Sub
Private Sub btnOK_Click()
' Store form control values to member variables here. Then ...
Call CloseWithResult(vbOK)
End Sub
Private Sub CloseWithResult(Value As VbMsgBoxResult)
m_ResultCode = Value
Me.Hide
End Sub
Public Function ShowMe(Optional bNewLayerOptions As Boolean = True) As VbMsgBoxResult
' Set Default to Cancel
m_ResultCode = vbCancel
' Execution will pause here until the form is Closed or Unloaded
Call Me.Show(vbModal)
' Return Result
ShowMe = m_ResultCode
End Function
Then, to call it (please note that frmLayers is my own VBA form object - you would use yours):
Dim dlgLayers As New frmLayers
If (dlgLayers.ShowMe(False) = vbOK) Then
' Proceeed
End If
Does this help you with your issue? I am sorry if I have misunderstood, and I will remove my answer if needed.
Things like xxxxx_Activate etc. are event handlers called by the framework. So, for example, there is an event for activate and an event for initialize. You don't normally have to directly call these yourself if you set your code up correctly. See https://support.microsoft.com/en-us/kb/138819.

Resources