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
Related
As the subject suggest, I have build a macro that will need a variable input
Sub GetFile(Account as String)
Currently i am setting multiple sub to call the script and assign them to different command buttons
Sub AC1_File
Call GetFile("Account1")
end sub
Sub AC2_File
Call GetFile("Account2")
end sub
And the list go on
I am trying to not make my code too long (as i got double digits accounts),
is it possible for me code to get properties of the commandbutton name and use it instead of Account1,2,3...?
Something like below?
Call GetFile(triggering-commandbutton.name)
Form Controls
In a normal module add a procedure with no arguments:
Public Sub Button_Click()
GetFile Application.Caller
End Sub
This code will pick up the button name and pass it to the GetFile procedure.
Sub GetFile(Account As String)
MsgBox "Account Name is " & Account
End Sub
So now all you need to do is name your buttons Account1, Account2, etc..
ActiveX Controls
If you're using ActiveX controls you can use a class to capture the click event.
Create a class module and name it clsButtonClick.
Add this code to the class module:
Public WithEvents AccountBtn As MSForms.CommandButton
Private Sub AccountBtn_Click()
MsgBox AccountBtn.Name & " on " & AccountBtn.Parent.Name
GetFile AccountBtn.Name
End Sub
At the very top of a normal module add this line:
Public colBtns As New Collection
And add this code to the module:
Public Sub Initialize_Buttons()
Dim wrkSht As Worksheet
Dim btnEvnt As clsButtonClick
Dim obj As OLEObject
For Each wrkSht In ThisWorkbook.Worksheets
For Each obj In wrkSht.OLEObjects
If TypeName(obj.Object) = "CommandButton" Then
Set btnEvnt = New clsButtonClick
Set btnEvnt.AccountBtn = obj.Object
colBtns.Add btnEvnt
End If
Next obj
Next wrkSht
End Sub
This will go through each sheet in your workbook and give any ActiveX command buttons the click event from the class module.
Update the Initialize_Buttons procedure if you need to limit it to a specific sheet, or specific buttons on a sheet.
Finally call the Initialize_Buttons code in the Workbook_Open event in the ThisWorkbook module.
You can create many commandbuttons as ActiveX controller, then write VBA like below:
Private Sub CommandButton1_Click()
Call GetFile("Account1")
End Sub
Private Sub CommandButton2_Click()
Call GetFile("Account2")
End Sub
This question already has answers here:
VBA: Using WithEvents on UserForms
(2 answers)
Create event handlers for multiple dynamic controls
(2 answers)
Closed 4 years ago.
Untill now I have created one event for each control in my userform.
Private Sub TextBox_Integrate_Indexes_Change()
Call LabView.textBoxChange(TextBox_Integrate_Indexes)
End Sub
Private Sub TextBox_Integrate_InputFile_Change()
Call LabView.textBoxChange(Me.TextBox_Integrate_InputFile)
End Sub
Private Sub TextBox_Integrate_OutputFile_Change()
Call LabView.textBoxChange(Me.TextBox_Integrate_OutputFile)
End Sub
As seen these events all just send its object to my method which then handles the event(check if it has changed its value, and if so store the updated value in a config.json file)
However instead of making an event for all my userform textboxes, optionbuttons listboxs,checkboxes and comboxes, I wasa wandering if there is a way to detect if any event happens to that userform, get the item that triggered the event and if this one of the above type, then send itself to my method.
Yes, you can create a custom class that holds a private textbox variable.
You can then capture the event in that class and pass it to your Labview class.
In the UserForm you can just create a collection of the custom class, and set the userform's textboxes as the private variables in the custom class.
Example code:
cTextBox Class:
Private WithEvents p_TextBox As MSForms.TextBox
Public Property Let txtBox(value As MSForms.TextBox)
Set p_TextBox = value
End Property
Public Property Get txtBox() As MSForms.TextBox
Set txtBox = p_TextBox
End Property
Private Sub p_TextBox_Change()
Call Labview.textboxchange(p_TextBox)
End Sub
Labview class:
Public Sub textboxchange(val As MSForms.TextBox)
MsgBox val.Name
End Sub
Userform code:
Private t As MSForms.Control
Private ctb As cTextBox
Private cTextBoxes As Collection
Private Sub UserForm_Initialize()
Set cTextBoxes = New Collection
For Each t In Me.Controls
If TypeName(t) = "TextBox" Then
Set ctb = New cTextBox
ctb.txtBox = t
cTextBoxes.Add ctb
End If
Next t
End Sub
And a routine to test the whole thing:
Public Labview As New Labview
Sub test()
UserForm1.Show
End Sub
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
I have a thousands of cells in an Excel worksheet which are ComboBoxes. The user will select one at random and populate it.
How do I get the selected ComboBox value? Is there a way to trigger a function (i.e. an event handler) when the ComboxBoxes has been selected?
You can use the below change event to which will trigger when the combobox value will change.
Private Sub ComboBox1_Change()
'your code here
End Sub
Also you can get the selected value using below
ComboBox1.Value
If you're dealing with Data Validation lists, you can use the Worksheet_Change event. Right click on the sheet with the data validation and choose View Code. Then type in this:
Private Sub Worksheet_Change(ByVal Target As Range)
MsgBox Target.Value
End Sub
If you're dealing with ActiveX comboboxes, it's a little more complicated. You need to create a custom class module to hook up the events. First, create a class module named CComboEvent and put this code in it.
Public WithEvents Cbx As MSForms.ComboBox
Private Sub Cbx_Change()
MsgBox Cbx.Value
End Sub
Next, create another class module named CComboEvents. This will hold all of our CComboEvent instances and keep them in scope. Put this code in CComboEvents.
Private mcolComboEvents As Collection
Private Sub Class_Initialize()
Set mcolComboEvents = New Collection
End Sub
Private Sub Class_Terminate()
Set mcolComboEvents = Nothing
End Sub
Public Sub Add(clsComboEvent As CComboEvent)
mcolComboEvents.Add clsComboEvent, clsComboEvent.Cbx.Name
End Sub
Finally, create a standard module (not a class module). You'll need code to put all of your comboboxes into the class modules. You might put this in an Auto_Open procedure so it happens whenever the workbook is opened, but that's up to you.
You'll need a Public variable to hold an instance of CComboEvents. Making it Public will kepp it, and all of its children, in scope. You need them in scope so that the events are triggered. In the procedure, loop through all of the comboboxes, creating a new CComboEvent instance for each one, and adding that to CComboEvents.
Public gclsComboEvents As CComboEvents
Public Sub AddCombox()
Dim oleo As OLEObject
Dim clsComboEvent As CComboEvent
Set gclsComboEvents = New CComboEvents
For Each oleo In Sheet1.OLEObjects
If TypeName(oleo.Object) = "ComboBox" Then
Set clsComboEvent = New CComboEvent
Set clsComboEvent.Cbx = oleo.Object
gclsComboEvents.Add clsComboEvent
End If
Next oleo
End Sub
Now, whenever a combobox is changed, the event will fire and, in this example, a message box will show.
You can see an example at https://www.dropbox.com/s/sfj4kyzolfy03qe/ComboboxEvents.xlsm
A simpler way to get the selected value from a ComboBox control is:
Private Sub myComboBox_Change()
msgbox "You selected: " + myComboBox.SelText
End Sub
Maybe you'll be able to set the event handlers programmatically, using something like (pseudocode)
sub myhandler(eventsource)
process(eventsource.value)
end sub
for each cell
cell.setEventHandler(myHandler)
But i dont know the syntax for achieving this in VB/VBA, or if is even possible.
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