Adding item in combobox when combobox is created on click event - excel

I am trying to add items in comboBox. I know how to do this, with myBox.AddItem "words" However I have an issue to implement this to my code as I create the comboBox on click event :
Private Sub CommandButton2_Click()
Dim editBox As MSForms.Control
Dim testBox As ComboBox
Static i
Set editBox = Me.Controls.Add("Forms.ComboBox.1")
i = i + 1
With editBox
.Name = "cmBox" & i
.Top = i * editBox.Height + 10
.Left = 130
End With
End Sub
Do you have any suggestions ?
Thank you

Assuming the items are known at the time you're creating the controls, ...you can do that in the handler directly. Just set testBox to your editBox reference:
Set testBox = editBox 'basically cast from MSForms.Control to MSForms.ComboBox
testBox.Add "test1"
testBox.Add "test2"
On the other hand, if the items are not known at the time you're creating the controls, you have a problem:
Dim testBox As ComboBox
The object is locally-scoped, and goes out of scope as soon as the click handler exits.
You need to hold on to it. Move that declaration to module-level (and qualify it, for consistency):
Private testBox As MSForms.ComboBox
Then you can invoke testBox.Add.... the problem is that you're going to be adding more than one control, so you can't just have one field like this. Have a Collection instead:
Private dynamicControls As Collection
Private Sub UserForm_Initialize()
Set dynamicControls = New Collection
End Sub
Now when you create a dynamic control, add it to the collection with a key:
Set editBox = Me.Controls.Add("Forms.ComboBox.1")
'...
dynamicControls.Add editBox, editBox.Name
If you need to handle events for these dynamic controls, you'll need a different setup though, with an instance of a custom class for each dynamic control:
'Class1
Option Explicit
Private WithEvents box As MSForms.ComboBox
Private Sub box_Change()
'...
End Sub
Public Property Get Control() As MSForms.ComboBox
Set Control = box
End Property
Public Property Set Control(ByVal value As MSForms.ComboBox)
Set box = value
End Property
Then when you create a dynamic control, you add it to a new instance of that class instead:
Set editBox = Me.Controls.Add("Forms.ComboBox.1")
'...
Dim dynamicHandler As Class1
Set dynamicHandler = New Class1 'todo rename that class
Set dynamicHandler.Control = editBox
dynamicControls.Add dynamicHandler, editBox.Name
Now when you're ready to add items to a given box, retrieve the control from the dynamicControls collection:
With dynamicControls("cmBox1")
.Add "test1"
.Add "test2"
End With

Related

Store a class module as a variable in VBA

I wrote some code in a class module and userform_initialize_event. All are okay. But when I Dim the new class module and applies in the initialize event it says that variable not defined. Here is my code -
Dim Buttons() As New BtnClass
Private Sub UserForm_Initialize()
Dim ButtonCount As Integer
Dim ctl As Control
' Create the Button objects
ButtonCount = 0
For Each ctl In fmHover.Controls
If TypeName(ctl) = "Label" Then
ButtonCount = ButtonCount + 1
ReDim Preserve Buttons(1 To ButtonCount)
Set Buttons(ButtonCount).ButtonGroup = ctl
End If
Next ctl
End Sub
If I Dim the Buttons inside the event it does not throws any error and the code also don't works. I searched in a lot of place. Every one Dim the new class before the Initialize event. So, why my one doesn't work? Kindly suggest where is my mistake.
Here is my class module code -
Public WithEvents ButtonGroup As MSForms.Label
Private Sub ButtonGroup_Click()
Msg = "You clicked " & ButtonGroup.Name
MsgBox Msg
ButtonGroup.Name
End Sub
Thanks in advance.
Each label on your form should have it's own instance of the class. The form needs a way to remember each instance - stored in a collection at the form level.
Not sure what the ButtonCount is doing, so have removed it.
Your form code:
Option Explicit '!!!Add option explicit to the top of each module!!!
'It helps avoid so many errors that I don't think three
'exclamations marks is enough!!!!
'>Tools>Options>tick Require variable declaration
'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
'Each instance of the class is stored here.
Private colLabels As Collection
Private Sub UserForm_Initialize()
Dim BtnEvents As BtnClass
'Initialise the collection to hold the class instances.
Set colLabels = New Collection
Dim ctrl As Control
For Each ctrl In Me.fmHover.Controls
If TypeName(ctrl) = "Label" Then
Set BtnEvents = New BtnClass 'New instance of the class.
Set BtnEvents.AssignButton = ctrl 'Assign the label to it.
colLabels.Add BtnEvents 'Add it to the collection so it's remembered.
End If
Next ctrl
End Sub
Your class module will look like:
Option Explicit 'More exclamation marks!!!!!!!!!
Public WithEvents ButtonGroup As MSForms.Label
'Let the class know what control it's assigned to.
Public Property Set AssignButton(ctrl As Control)
Set ButtonGroup = ctrl
End Property
Private Sub ButtonGroup_Click()
Dim Msg As String
Msg = "You clicked " & ButtonGroup.Name
MsgBox Msg
End Sub

Reading/returning variable values to/from custom classes to/from username - VBA Excel

I have a custom created class btnClass based on CommandButton class.
Public WithEvents ButtonEvent As MsForms.CommandButton
Private Sub ButtonEvent_Click()
End sub
I have a UserForm1 that have one ListBox, one Label, and hundreds of dynamically created CommandButtons. I assigned btnClass to Buttons. When clicked on buttons, I want the Click event has the following result:
If number of buttons selected (selQty) less than the Label.Caption (totalQty), and this button hasn't been selected before, add value to listBox and change BackColor.
if this button selected previously, change color and decrease number of buttons selected (selQty) by 1.
I tried creating Public variables, but cannot get the result I want. Is this doable?
P.S. when the UserForm1 is activated, it means no button selected; as I click button I change the color of the button and accept it as selected.
Seems like you want to do something like this
clsButtonClick:
Option Explicit
Public WithEvents ButtonEvent As MSForms.CommandButton
Private Sub ButtonEvent_Click()
'pass the button to the procedure in the userform
ButtonEvent.Parent.HandleClick ButtonEvent
End Sub
Userform code:
Option Explicit
Const CLR_SEL As Long = vbRed 'selected color
Const CLR_NOT_SEL As Long = vbGreen 'unselected color
Dim btnCol As Collection
Dim maxQty As Long 'max number selectable
Dim currQty As Long 'number currently selected
'perform some setup
Private Sub UserForm_Activate()
Const NUM_BUTTONS As Long = 10
Dim i As Long, btn As MSForms.CommandButton
Dim o As clsButtonClick
currQty = 0 'number selected
maxQty = 5 'max selectable
Set btnCol = New Collection
'add some buttons
For i = 1 To NUM_BUTTONS
Set btn = Me.Controls.Add("Forms.CommandButton.1", "btn" & i, True)
btn.BackColor = CLR_NOT_SEL
btn.Height = 18
btn.Left = 20
btn.Top = 20 * i
btn.Caption = "Button " & i
Set o = New clsButtonClick
Set o.ButtonEvent = btn
btnCol.Add o
Next i
End Sub
'handle a button click event (button is passed in)
Sub HandleClick(btn As MSForms.CommandButton)
If btn.BackColor = CLR_SEL Then
btn.BackColor = CLR_NOT_SEL
currQty = currQty - 1
Else
If currQty = maxQty Then
MsgBox "no more selections available"
Else
btn.BackColor = CLR_SEL
currQty = currQty + 1
End If
End If
End Sub
Here's something to get you started.
Create a wrapper class which wraps each button and handles its click event. Then when the form loads, loop through the controls and wrap the buttons.
A module level collection is required to hold the references of wrapped buttons (wrapper classes).
The ButtonWrapper class:
Option Explicit
Private WithEvents objButton As MsForms.CommandButton
'Wrap button
Public Function WrapCommandButton(btn As MsForms.CommandButton) As ButtonWrapper
Set objButton = btn
Set WrapCommandButton = Me
End Function
'Button's event handler
Private Sub objButton_Click()
MsgBox objButton.Caption & " was clicked."
End Sub
'Clean up
Private Sub Class_Terminate()
Set objButton = Nothing
End Sub
The code behind the Form:
Option Explicit
Private m_handlers As Collection
'Initialize
Private Sub UserForm_Initialize()
Set m_handlers = New Collection
Dim ctl As Control
For Each ctl In Me.Controls
If TypeName(ctl) = "CommandButton" Then
With New ButtonWrapper
m_handlers.Add .WrapCommandButton(ctl)
End With
End If
Next ctl
End Sub
'Clean up
Private Sub UserForm_Terminate()
Set m_handlers = Nothing
End Sub
Hope this helps.

Class for Custom Button Event Handling

1) I have a Form with some buttons (its in Access, but I guess it applies for Excel as well).
2) I have a custom class that helps me debug that form (and future forms that I may add).
The class simply logs when form events fire, such as loaded, unloaded, dirty, exited.
I'd like that class to have the capability to log when buttons are clicked.
I know this can be done by using a standard module, and loading a public collection there. Or by directly using the form's events. Or by storing in a collection behind the form.
But I would like, if possible, to encapsulate it all in my debugging class. Then its a simple two lines added to the Form_Load event of each new form I add.
My simplified attempt below is only capturing the event for the last button that gets added in the class collection, ie. Button3.
TestButtons (A Form with Button1, Button2, & Button3)
Private Buttons As CButtons
Private Sub Form_Load()
Set Buttons = New CButtons
Buttons.LoadButtons Me
End Sub
CButtons (Class):
Public WithEvents btn As Access.CommandButton
Private AllButtons As Collection
Const MODE_DEBUG As Boolean = True
Public Sub LoadButtons(ByRef TheForm As Access.Form)
Dim ctl As Control
Set AllButtons = New Collection
For Each ctl In TheForm.Controls
If ctl.ControlType = acCommandButton Then
Set btn = ctl
btn.OnClick = "[Event Procedure]"
AllButtons.Add btn
End If
Next ctl
End Sub
Private Sub btn_Click()
If MODE_DEBUG Then debug.print btn.Name & "_Click"
End Sub
Wondering if anyone's got any advice, thanks!
You can't handle events from a collection. The easiest solution is to use a separate class to handle the button events, make a collection of those classes in your multiple buttons handler, and pass the button from the class handling the single button to the class handling multiple ones on an event.
Class CSingleButton
Public buttonsHandler As CButtons
Public WithEvents btn As Access.CommandButton
Private Sub btn_Click()
buttonsHandler.HandleClick btn
End Sub
Class CButtons
Private ButtonHandlers As Collection
Const MODE_DEBUG As Boolean = True
Public Sub LoadButtons(ByRef TheForm As Access.Form)
Dim ctl As Control
Dim btnHandler As CSingleButton
Set ButtonHandlers = New Collection
For Each ctl In TheForm.Controls
If ctl.ControlType = acCommandButton Then
Set btnHandler = New CSingleButton
Set btnHandler.btn = ctl
Set btnHandler.buttonsHandler = Me
ctl.OnClick = "[Event Procedure]"
ButtonHandlers.Add btnHandler
End If
Next ctl
End Sub
Public Sub HandleClick(btn As Access.CommandButton)
If MODE_DEBUG Then debug.print btn.Name & "_Click"
End Sub

Click Event for buttons inside Frames for VBA in Excel

In Excel I insert an ActiveX Frame into a worksheet. Right clicking this frame allows me to select:
Frame Object>Edit
Now I am able to add a button to this frame. Great.
How do I add a _Click event to this button so that it will run a macro?
Basically, what you need to do is to create you own class, for instance, "XButton". Inside this 'XButton' there will be an event handler for the button object that is inside the frame.
So you can handle all of the events that are sent by 'btn' and forward it further. Then you will have to create a custom interface (empty class) IXButtonEventHandler, that will look something like this:
Option Explicit
Public Sub Click(Sender as XButton)
End Sub
So, your custom class XButton will look like this:
Private WithEvents btn as MSForms.CommandButton
Private mEventHandler as IXButtonEventHandler
Public Sub CreateObject(EventHandlerOf as MSForms.CommandButton, EventHandler as IXButtonEventHandler)
Set btn = EventHandlerOf
Set mEventHandler = EventHandler
End Sub
Private Sub btn_Click()
If not mEventHandler is Nothing then mEventHandler.Click(Me)
End Sub
Let's say, your Workbook will be the event handler and will need to implement the IXButtonEventHandler interface, for instance:
Implements IXButtonEventHandler
Private Sub IXButtonEventHandler_Click(Sender as XButton)
'your code
End Sub
On Workbook_Load or whatnot you will need to create a collection of XButtons and attach them to your frame controls:
Dim xbtn as Collection
Private Sub AttachButtons()
Set xbtn = New Collection
Dim i as Long
For i = 0 to 3
Dim xb as New XButton
xb.CreateObject <YourFrame>.Controls("CommandButton" & Cstr(i)), Me
xbtn.Add xb
Next i
End Sub

How do you control a button inside a multipage in excel VBA

I have a multipage, I was successfully able to copy elements of the first page which is my reference page to new pages which is created dynamically.
My question is, how do I set a commandbutton's actions inside a page in a multipage control?
My goal is to click on the button from any page then pops up another form.
How do I do this?
It's pretty hard to adjust from Android to VB. I really appreciate any help from you guys.
This is my code in cloning pages.
i = 0
MultiPage1.Pages.Add
MultiPage1.Pages(i).Controls.Copy
i = i + 1
MultiPage1.Pages(i).Paste
For Each ctl In Me.MultiPage1.Pages(i).Controls
If TypeOf ctl Is MSForms.Label Then
'~~~ code omitted
Select Case ctl.Tag
Case "startTime"
ctl.Caption = "4:00pm"
End Select
End If
Next
this is how it's going to look like.
the button will concatenate all strings inside the page. the concatenated string will be shown on another userform.
You probably would be better off creating a button in the ribbon so that it is available on all pages:
http://chandoo.org/wp/2012/02/27/how-to-add-your-own-macros-to-excel-ribbon/
EDIT:
My bad, I thought you meant a worksheet instead of a VBA MultiPage in a userform.
Check this out. I was able to make this work for me:
Assign code to a button created dynamically
Class1:
Option Explicit
Public WithEvents CmdEvents As MSForms.CommandButton
Private Sub CmdEvents_Click()
MsgBox "yo"
End Sub
Userform with MultiPage object:
Option Explicit
Dim cmdArray() As New Class1
Private Sub CommandButton1_Click()
Dim newControl As Control
Set newControl = Me.MultiPage1.Pages(0).Controls.Add("Forms.CommandButton.1", "NewCommand", True)
newControl.Object.Caption = "hello"
newControl.Left = 50
newControl.Top = 50
ReDim Preserve cmdArray(1 To 1)
Set cmdArray(1).CmdEvents = newControl
Set newControl = Nothing
End Sub
You can do this with a custom class. The class basically has one member Public WithEvents b as CommandButton.
The trick is the WithEvents keyword. You can now insert some code to generically handle the click of a button that is assigned to this class:
Private Sub b_Click()
MsgBox "You clicked " & b.Name 'Modify this event so that different code is executed base on the page/name/etc.
End Sub
In order to make this work, you need to assign the button you create in your code to an object of this new class:
Private objButtonHandler as New MyClass 'this should be scope a UserForm wide
Sub YourSub
Dim objYourButton as CommandButton
Set objYourButton = ... `your code here
Set objButtonHandler.b = objYourButton
End Sub

Resources