Create Button Dynamically and add OnClick Event - vba

In Outlook 2010 I have a userform called UserForm1.
Here I create a button dynamically and show the form.
How can I add an event (the sub btnLoad_OnClick) when the button was clicked?
Here is my code:
Dim btnLoad As MSForms.CommandButton
Sub btnLoad_OnClick()
MsgBox ("Button Clicked")
End Sub
Sub SaveAttachment()
Set btnLoad = UserForm1.Controls.Add("Forms.CommandButton.1", "btnLoad", True)
With btnLoad
.Caption = "Click Me"
.Left = 30
.Top = 30
.Height = 30
.Width = 60
End With
With UserForm1
.Width = 850
.Show
End With
End Sub

In your UserForm1 module add
Private Sub btnLoad_Click()
MsgBox "Button Clicked"
End Sub
and save it.
Whenever you dynamically add the button btnLoad and click it the message should pop up.

Related

VBA loop through buttons on UserForm

I have not used VBA so quite new - but all searches have not given me the answer
its a simple question really. I have a group of buttons in an Excel Form.
The code is very similar when each one is pressed, and for each pressed button, I would like the colour of the button to change. So in reality, I have something like this for each button
UserForm2.CommandButton17.BackColor = RGB(255,255,0)
I would like to go through each button. Check if it is pressed, and then set the colour accordingly.
I actually want to say something like
for counter in 1 to 100
if (ispressed((CommandButton & counter )) then
I have found the following construct:
Dim ctrl as Control
For Each ctrl in UserForm1.Controls
ctrl.BackColor = RGB(255,0,0)
end for
this construct works - but I cant figure out how to check if the button is pressed.
Some of the answers show the above construct, with ctrl.Value = True
but those are for checkboxes and radio buttons. I don't even get the ctrl.Value option with buttons, so I can't use it anyway
Every example of code I have found glosses over this requirement.
Can someone help
Use a ToggleButton instead of a CommandButton if you want it to represent a state.
To initialize a state for each toggle button you can loop through the control.
Dim ctrl As Control
For Each ctrl In Me.Controls
If TypeName(ctrl) = "ToggleButton" Then
ctrl.Value = True 'set button state to pressed
End If
Next ctrl
This sets the state as pressed for every toggle button on the form.
Note that the .Value does not show up in the IntelliSense box because ctrl is of type Control which doesn't have a .Value. If you need IntelliSense then you could workaround like that:
Dim ctrl As Control
For Each ctrl In Me.Controls
If TypeName(ctrl) = "ToggleButton" Then
Dim tggl As ToggleButton
Set tggl = ctrl
tggl.Value = True
End If
Next ctrl
// Edit
Everytime a toggle button gets clicked it triggers a _Click event for that button. So you will need such an event for each button.
Private Sub ToggleButton1_Click()
With Me.ToggleButton1
If .Value = True Then
.BackColor = RGB(255, 0, 0)
Else
.BackColor = -2147483633 'switch to original color
End If
End With
End Sub
Or if you have many buttons, do it more efficiently
'this procedure handles all buttons
Private Sub ToggleButtonClick(ByRef tggl As ToggleButton)
With tggl
If .Value = True Then
.BackColor = RGB(255, 0, 0)
Else
.BackColor = -2147483633 'switch to original color
End If
End With
End Sub
'so you just need to call that function on every _Click event
Private Sub ToggleButton1_Click()
ToggleButtonClick Me.ToggleButton1
End Sub
Private Sub ToggleButton2_Click()
ToggleButtonClick Me.ToggleButton2
End Sub
But you still need a _Click() event for every button to call that procedure.
You can also evaluate the .Value state of each button in the _Click() event to set/unset your asterisk.
I think that the best thing is to work with event and to intercept Press Button
in defining Click() event for all buttons like this
'Form variable to define at begin on code
Dim pbPressed as Control
Dim pbLastPressed as Control
Private Sub pbButton(X)_Click()
'restore previous color only to last pressed
Set pbLastPressed.BackColor = RGB(0,0,155)
Set pbPressed = pbButton(X)
'assign color-pressed to button pressed
pbPressed = RGB(255,0,0)
End sub
where (X) must be replaced by a number as 1 or 2 or 10 !
You can make a fonction, and you obtain
Private Sub pbButton1_Click()
Call ChangeButtonsColor(pbButton1)
End Sub
Private Sub pbButton2_Click()
Call ChangeButtonsColor(pbButton2)
End Sub
Private Sub pbButton3_Click()
Call ChangeButtonsColor(pbButton3)
End Sub
Private Sub ChangeButtonsColor(pb as Button)
'restore previous color only to last pressed
Set pbLastPressed.BackColor = RGB(0,0,155)
Set pbPressed = pb
'assign color-pressed to button pressed
pbPressed = RGB(255,0,0)
End sub
Don't forget to add other event as KeyPress() that can make same action than clicking the Button.
If you have more than 10 buttons, you can perhaps create the buttons dynamically.
I would suggest to implement a class named TglBtn like that
Option Explicit
Private WithEvents m_ToggleButton As MSForms.ToggleButton
Private Sub m_ToggleButton_Click()
With m_ToggleButton
If .Value Then
.BackColor = RGB(255, 255, 0)
Else
.BackColor = &H8000000F
End If
End With
End Sub
Public Property Set Btn(tb As MSForms.ToggleButton)
Set m_ToggleButton = tb
End Property
In the Userform you can use the following code
Option Explicit
Dim mTgBtns As Collection
Private Sub UserForm_Initialize()
Dim sngControl As MSForms.Control
Dim mTglBtn As tglBtn
Set mTgBtns = New Collection
For Each sngControl In Me.Controls
If TypeName(sngControl) = "ToggleButton" Then
Set mTglBtn = New tglBtn
Set mTglBtn.Btn = sngControl
mTgBtns.Add mTglBtn
End If
Next sngControl
End Sub
When you click on one of togglebuttons on your userform the class will take care of the background color.
EDIT
If you want to access the caption of the Togglebutton you could add the following property to the class
Public Property Get Caption() As String
Caption = m_ToggleButton.Caption
End Property
EDIT2
Just as an example of using the property, you could change the Click event as below and everytime you click a MsgBox with the caption of the button will appear
Private Sub m_ToggleButton_Click()
With m_ToggleButton
If .Value Then
.BackColor = RGB(255, 255, 0)
Else
.BackColor = &H8000000F
End If
End With
MsgBox "You pressed the button with the caption " & Me.Caption
End Sub

Creating and populating combobox from vba module

In Excel 2010, I am trying to create a userform dynamically in my vba code. However, after running the code, the list does not show when opening the dropdown. If I put a breakpoint before ".show", I can inspect the form in the design window and I see that the list is populated. Does the .show method clear the list or what is happening? The solutions I have found focus on populating from cell ranges or placing the ".additem"s in the initialization code of the userform. I don't want to do anything that requires me to create a second file. Everything should be in this vba code if possible. Any help appreciated. My code follows.
Sub make_combobox_form()
' Makes a form with only a simple combobox
Dim myForm As Object
Dim cb As MSForms.ComboBox
'Create the User Form
Set myForm = ThisWorkbook.VBProject.VBComponents.Add(vbext_ct_MSForm)
With myForm
.Properties("Width") = 400
.Properties("Height") = 300
End With
Set cb = myForm.Designer.Controls.Add("Forms.ComboBox.1")
With cb
.Top = 100
.Left = 100
.Height = 20
.Width = 200
.AddItem "Item_1"
.AddItem "Item_2"
.AddItem "Item_3"
.value = "Item_1"
End With
VBA.UserForms.Add(myForm.name).Show
ThisWorkbook.VBProject.VBComponents.Remove myForm
End Sub
Sub make_combobox_form()
'Create the User Form as component first
Dim myFormComponent As VBComponent
Set myFormComponent = ThisWorkbook.VBProject.VBComponents.Add(vbext_ct_MSForm)
' Get reference to user form from the UserForms collection then
Dim cb As MSForms.ComboBox
Dim myForm As Variant
Set myForm = VBA.UserForms.Add(myFormComponent.Name)
With myForm
.Width = 400
.Height = 300
End With
Set cb = myForm.Controls.Add("Forms.ComboBox.1")
With cb
.Top = 100
.Left = 100
.Height = 20
.Width = 200
.AddItem "Item_1"
.AddItem "Item_2"
.AddItem "Item_3"
.Value = "Item_1"
End With
myForm.Show
ThisWorkbook.VBProject.VBComponents.Remove myFormComponent
End Sub

Add custom menu option in mouse right click event

When some words is selected in the document and right click, macro function should be called. Or something like, custom right click menu option should be added and when I click that menu option, macro should be called.
For example:
In the document I have four digit numbers like 2001 and 2010. When I select four digit number and hit right click and press my custom menu option, macro should be called and selected four digit no. should be parsed to the macro function which is called.
I got some code but its for Excel, I dont't know how to customize it for Microsoft Word.
Code for excel:
Const strMacro = "YourCode"
Sub CreateMacro()
Dim cBut
Call KillMacro
Set cBut = Application.CommandBars("Cell").Controls.Add(Temporary:=True)
With cBut
.Caption = strMacro
.Style = msoButtonCaption
.OnAction = "Test_Macro"
End With
End Sub
Sub Test_Macro()
MsgBox "I work"
End Sub
Sub KillMacro()
On Error Resume Next
Application.CommandBars("Cell").Controls(strMacro).Delete
End Sub
For Microsoft Word 2007, I have pasted the macro code to add a custom menu option in the right click mouse event and getting the selected text into the function.
Macro:
Option Explicit
Sub CreateMacro()
Dim MenuButton As CommandBarButton
With CommandBars("Text")
Set MenuButton = .Controls.Add(msoControlButton)
With MenuButton
.Caption = "Press Me"
.Style = msoButtonCaption
.OnAction = "Test_Macro"
End With
End With
End Sub
Sub Test_Macro()
MsgBox Selection.text
End Sub
Sub ResetRightClick()
Application.CommandBars("Text").Reset
End Sub

applying event handler to dynamic control

I have a userform that dynamically places a commandButton onto the user form but I can't seem to get the dynamic event handler set up properly: Below shows the code for how I have set up the dynamic button:
Set cButton = Me.Controls.Add("Forms.CommandButton.1")
With cButton
.Left = 150
.Top = 0
.Width = 300
.Height = 140
End With
I have also defined dim WithEvents cButton as Commandbutton outside of any sub or procedure and then finally I have the event handler for the dynamic button, which I simply want to output a message for now:
Private Sub cButton_Click()
MsgBox "Dynamic Handler functioning correctly"
End Sub
Now that I am able to create event handlers for dynamic events for individual controls, however I am creating multiple controls and they all have the same name cButton so how would I be able to create individual event handlers for each of them. Below shows the code that is used to create the multiple controls:
If TextBox1 <> vbNullString Then
For i = 1 To TextBox1.Value
Set cButton = Me.Controls.Add("Forms.CommandButton.1")
With cButton
.Left = 150
.Top = 0
.Width = 300
.Height = 140
End With
Next i
End IF
cButton should only be declared once:
Dim WithEvents cButton As CommandButton
Sub UserForm_Initialize()
Set cButton = Me.Controls.Add("Forms.CommandButton.1")
With cButton
.Left = 150
.Top = 0
.Width = 300
.Height = 140
End With
End Sub
Private Sub cButton_Click()
MsgBox "Dynamic Handler functioning correctly"
End Sub

Excel VBA ComboBox Identification

I have 4+ ComboBoxes on a user form. When they fire, they fire the same event. What I am trying to do is find out which ComboBox triggered the event. The ComboBoxes are created depending on how many components there are. The code generating the ComboBoxes is shown below:
For j = 0 To UBound(ComponentList) - 1
'Set Label
num = j + 1
Set control = UserForm1.Controls.Add("Forms.Label.1", "ComponentLabel" & CStr(num) & ":", True)
With control
.Caption = "Component " & CStr(num)
.Left = 30
.Top = Height
.Height = 20
.Width = 100
.Visible = True
End With
'set ComboBox
Set combo = UserForm1.Controls.Add("Forms.ComboBox.1", "Component" & num & ":", True)
With combo
.List = ComponentList()
.Left = 150
.Top = Height
.Height = 20
.Width = 50
.Visible = True
Set cButton = New clsButton
Set cButton.combobox = combo
coll.Add cButton
End With
Height = Height + 30
Next j
This works well and I can get the value the user selected, BUT I can not find which ComboBox has been used. This code below is the event that it fires (clsButton):
Public WithEvents btn As MSForms.CommandButton
Public WithEvents combobox As MSForms.combobox
Private combolist() As String
Private Sub btn_Click()
If btn.Caption = "Cancel" Then
MsgBox "Cancel"
Unload UserForm1
Variables.ComponentSelectionError = False
ElseIf btn.Caption = "Enter" Then
MsgBox "enter"
Unload UserForm1
Variables.ComponentSelectionError = True
End If
End Sub
Private Sub combobox_Click()
MsgBox combobox.Value
End Sub
This bit of code above was kindly worked on by Doug Glancy to get the events working with the code generated ComboBoxes.
How do I get the ComboBox that triggered the event? i.e. the name or some other form of identification.
I have managed to finally answer my own question after searching over 500 webpages (took a long time)
this is what i used and it works and fires when the certain comboboxes are clicked:
Private Sub combobox_Click()
MsgBox combobox.Value
If combobox = UserForm1.Controls("Component0") Then
MsgBox "Success1"
End If
If combobox = UserForm1.Controls("Component1") Then
MsgBox "Success2"
End If
End Sub
hopefully this can be used for other people who need it.
Within the class .Name will not appear in the intellisense list for the combobox as MSForms.ComboBox does not actually have a name property itself (take a look at it in the F2 object browser), rather that property is provided by the Control base class:
Private Sub combobox_Click()
MsgBox combobox.Value
MsgBox combobox.Name '// no hint but still works
'//cast to a Control to get the formal control interface with .Name
Dim ctrl As Control: Set ctrl = combobox
MsgBox ctrl.Name
End Sub
Maybe reference back to btn.Combobox again? Similar to how you assigned the combobox to the button in the first place, but then in reverse:
set combobox = btn.Combobox
Is there a reason you don't just add a property to your custom class and set that property when you register in the Collection?
For j = 0 To UBound(ComponentList) - 1
'Set Label
num = j + 1
Set control = UserForm1.Controls.Add("Forms.Label.1", "ComponentLabel" & CStr(num) & ":", True)
With control
.Caption = "Component " & CStr(num)
.Left = 30
.Top = Height
.Height = 20
.Width = 100
.Visible = True
End With
'set ComboBox
Set combo = UserForm1.Controls.Add("Forms.ComboBox.1", "Component" & num & ":", True)
With combo
.List = ComponentList()
.Left = 150
.Top = Height
.Height = 20
.Width = 50
.Visible = True
Set cButton = New clsButton
'*******EDIT********
with cButton
.combobox = combo
.Indx = j
end With 'cButton
'*******************
coll.Add cButton
End With
Height = Height + 30
Next j
Class Module
Public WithEvents btn As MSForms.CommandButton
Dim WithEvents mCombobox As MSForms.comboBox
Private combolist() As String
'*******EDIT********
Public Indx As Long
Property Let comboBox(cb As MSForms.comboBox)
Set mCombobox = cb
End Property
'*******************
Private Sub btn_Click()
If btn.Caption = "Cancel" Then
MsgBox "Cancel"
Unload UserForm1
Variables.ComponentSelectionError = False
ElseIf btn.Caption = "Enter" Then
MsgBox "enter"
Unload UserForm1
Variables.ComponentSelectionError = True
End If
End Sub
Private Sub mCombobox_Click()
'*******EDIT********
MsgBox "Combobox " & Indx & Chr(9) & mComboBox.Value
'*******************
End Sub
Since you require many to one mapping of the events, I assume you have a common call-back in your actual code, so you could also do this...
In a Standard Module
Public Sub cbCallBack(ocb As clsButton)
MsgBox ocb.Indx
End Sub
In clsButton (replacing the event handler)
Private Sub mCombobox_Click()
cbCallBack Me
End Sub