I am trying to create a button in an excel spreadsheet that activates every other button in the workbook in a particular order. These buttons are located in 4 different sheets. I attempted to simply create a button in one sheet, and call the other buttons from this button as below:
Public Sub CommandButton1_Click()
Button2_Click
Button3_Click
Button4_Click
Button5_Click
Button6_Click
End Sub
This did not work. I was thinking that maybe I need some sort of way to reference the sheet that each button is in?
Event handlers are Private for a reason. Making them Public will work, but isn't what you should be doing.
Move the code out of these handlers and into their own public procedures; then invoke these procedures from the respective handlers - and invoke the same procedures in the appropriate order from this commandbutton handler.
For example you take this:
Private Sub Button2_Click()
'do stuff
End Sub
Turn it into this:
Private Sub Button2_Click()
DoStuff
End Sub
Public Sub DoStuff()
'do stuff
End Sub
Then you can invoke DoStuff from wherever you need to.
Event handlers should never be invoked directly, and never need to be.
Even Private event handlers within sheet modules can be started by following way.
Assuming you have a worksheet named "ActiveXButtons" (its caption) with Button2 on it and following button event code within that worksheet:
Private Sub Button2_Click()
' ...
End Sub
Then you can trigger above button's event by addressing the worksheet's codename indirectly:
Application.Run Worksheets("ActiveXButtons").CodeName & ".Button2_Click"
or shortened:
Run “Sheet2.Button2_Click“
Related
I made a module that I want to be a plugin. My issue is I want it to rerun every time I open the sheet and only the sheet I activated it on.
I found a solution on how to do it by using the
Private Sub Worksheet_Activate()
Call Func
End sub
inside the sheet, I applied the macro to. How can I make it apply this code snipped to the currently active sheet when automatically when I activate the macro.
Basically, when I use my plugin while I am on sheet x I want it to apply
Private Sub Worksheet_Activate()
Call Func
End sub
this function to that specific sheet and that specific sheet only
Just to clarify better.
I want to sit on the sheet that has NO VBA code associated,
activate my add-in
and have a predefined code-block run in the context of the activated sheet.
You need to add the following codes to your Add-In, it is important to add them to the right module (as indicated by the comments in the code).
' ThisWorkbook
Option Explicit
Private Sub Workbook_Open()
InitializeMyEventHandler ThisWorkbook.Application
End Sub
' modMyEventHandler
Option Explicit
Public g_mehHandler As clsMyEventHandler
Public Sub InitializeMyEventHandler(eapApplication As Application)
Set g_mehHandler = New clsMyEventHandler
Set g_mehHandler.eapApplication = eapApplication
End Sub
' clsMyEventHandler
Option Explicit
Public WithEvents eapApplication As Application
Private Sub eapApplication_SheetActivate(ByVal Sh As Object)
If TypeName(Sh) = "Worksheet" Then
Dim ewsSheet As Worksheet: Set ewsSheet = Sh
Debug.Print "Worksheet activated. Workbook: " & ewsSheet.Parent.Name & ", Worksheet: " & ewsSheet.Name
End If
End Sub
The first part in the Add-In's ThisWorkbook module makes sure that our event handler class will be initialized each time the Add-In is loaded (when a new Excel Application is opened).
The second part is a normal public module, which is capable of holding global public objects, in our case an object of class clsMyEventHandler. At initialization g_mehHandler will be set to a new instance of clsMyEventHandler, and its member, eapApplication will be assigned to the Application object received from the Add-In's Workbook_Open function.
The third part must be added as a class module. It has a variable that is declared with WithEvents, this means that any time an event (e.g. activation of a new sheet) happens, the appropriate functions of this class module will be called. If a SheetActivate event is fired in eapApplication, then eapApplication_SheetActivate function of this class will be called. The function is selected based on its name (object name + underscore + event name). After you declared eapApplication (Public WithEvents eapApplication As Application), you will be able to select eapApplication from the ComboBox above the VBA code, which usually contains the word (General). If you selected eapApplication there, you will be able to select events to which you want to react from the ComboBox next to it.
I've got an extensive project.
Amongst other things, it contains an
UserForm
Worksheet (Data) with a button called import_button
Either way, I've got one procedure inside the UserForm vba code, which acts something akin to mine.
After it is executed, I wanted to Call the import_button_Click procedure, from the Data Worksheet, however obviously, the reference is unknown, given the import_button_Click procedure is Private inside the Data Worksheet module.
Is there perhaps a way, I could invoke the import_button_Click procedure outside of the Sheet1 (Data) module?
EDIT:
Should probably note, this is the structure of the import_button_Click procedure.
Public Sub import_button_Click()
Dim prva As Range: Set prva = Sheets("Zdrojove").Range("A1")
Call import_data(get_delimeter(prva, ";", ",", ".", "-", "/"), False)
Call validate_format
End Sub
I can't recreate the issue once changing the Sub to Public. Inside CommandButton1:
Inside Module1:
Running Test:
An option would be to replace the ActiveX button on the Worksheet by a Form Control button. This would allow you to get the exact procedure, now hosted in a Module instead of in the Worksheet... Then you can get the button to execute whatever macro (or sub) you want, while being able to call this procedure from other piece of code at the same time.
There're several ways to make it work:
You declare import_button_Click Public
Public Sub import_button_Click()
'Your code
End Sub
You create another Public Sub (inside Sheet1 (Data)) to call the Private one.
Private Sub import_button_Click()
'Your code
End Sub
'Use this outside the sheet
Public Sub public_import_button_Click()
Call import_button_Click
End Sub
Hope this helps.
I have a user-form which is made up of many subs, this is assigned as a macro to a button on the worksheet. When the user is finished with this user-form they can press a button on it which causes its visibility to become false and when entered again everything appears how it was left resulting in a save like feature.
I now need to apply this to multiple buttons on the worksheet and each user form needs to have the exact same code and same buttons but be a separate form as each individual button requires it's own save like feature. The way I was planning on doing this was to copy the existing user form and paste it many times with different names however, if a modification is required it will take a long time to carry out therefore, is there a method such as "include" which could use a base module from which all the code is accessed so that if I ever need to change anything I just do it on that one module and everything else updates via the include?
EDIT:
I now have a public function called costing() and am getting an error when I used:
Private Sub material_Change()
Call costing
End Sub
You can have multiple instances of the same form. You can use this to retain multiple sets of form values
Try this:
Create your form, as usual. Let's call it MyForm
Create several buttons on your sheet. My example uses ActiveX buttons, but Form Control buttons can be used too. Let's call them CommandButton1 and CommandButton2
In your form module, include a Terminate Sub, which includes this code
Private Sub UserForm_Terminate()
' any other code you may need...
Unload Me
End Sub
The Form buton to save/Hide the form needs to be
Private Sub btnSaveAndHide_Click()
Me.Hide
End Sub
The Sheet Button code is as follows
The code is identical for each button (and calls a common Sub), and each button has its own Static form variable.)
The Error handler is needed to deal with the case a form is not properly closed. In this case the instance no longer exists, but the local Static variable is also not Nothing
Example shows form shown as Modeless, you can change this to Modal if you want.
Private Sub CommandButton1_Click()
Static frm As MyForm
ShowMyForm frm
End Sub
Private Sub CommandButton2_Click()
Static frm As MyForm
ShowMyForm frm
End Sub
Private Sub ShowMyForm(frm As MyForm)
If frm Is Nothing Then Set frm = New MyForm
On Error GoTo EH
frm.Show vbModeless
Exit Sub
EH:
If Err.Number = -2147418105 Then
On Error GoTo 0
Set frm = Nothing
Set frm = New MyForm
frm.Show
End If
On Error GoTo 0
End Sub
End result: multiple copies of the same form, each with their own values
In responce to comment How would I access the variables inside of each user form externally
In the example above the Form instances are only accessable in the Command Button Click Handler routines, or within the Form module itself. If you can write your code in the form module, then no change is needed.
To make the Form instances available elsewhere, consider moving their declaration to Module Scope of a standard Module. You could declare them as, eg individual variables, an array (either static or dynamic), a Collection, a Dictionary. Which structure is best will depend on how you want to manage and access your form instances.
For example, a Static Array: Code in a standard Module
Option Explicit
Global MyForms(1 To 2) As MyForm
Update the CommandButton code to
Private Sub CommandButton1_Click()
ShowMyForm Module1.MyForms(1)
End Sub
Private Sub CommandButton2_Click()
ShowMyForm Module1.MyForms(2)
End Sub
Private Sub ShowMyForm(frm As MyForm) no change, same as before
The code works the same as before, but you can now access the Global variable in a standard Module
Sub Demo()
Dim i As Long
For i = LBound(MyForms) To UBound(MyForms)
If Not MyForms(i) Is Nothing Then
MsgBox "Form " & i & " Value = " & MyForms(i).TextBox1.Value
End If
Next
End Sub
You don't need an "Include" (none exists in VBA); all you need to do is create a module and make the common methods public.
For example, if you create a module and have a function like this:
Public Function Add(first As Integer, second As Integer) As Integer
Add = first + second
End Function
Then you can access it like this from another module/form/class module:
Sub test()
MsgBox Add(3, 6)
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.
Is there any way to insert a user form directly on the excel sheet?
We can add the build-in controls as well as active x controls. I don't see why we cannot add user forms within the same workbook.
Thanks
No, I don't think it's possible.
Userforms are merely containers to hold your ActiveX controls. Spreadsheets are also ActiveX control containers, so I'm not sure what the benefit of having a container in a container would be.
You could easily color a group of cells to look like a userform and place ActiveX controls within that range. That would simulate a userform embedded on a spreadsheet. You'd be missing userform level events and probably a few other things. But if you wanted those things, you'd probably just use a userform.
If there's something you want to do that I'm missing, let me know.
Place this in the WorkSheet module
VB:
Private Sub Worksheet_Activate()
UserForm1.Show
End Sub
I realize it's an old post and this solution might not have been available in the past but you can insert an ActiveX control element and have "Microsoft Forms 2.0 Frame" to control the information.
You will also need to create a custom class to handle the button presses since you can't attach macros directly onto them like a normal button.
Here is an example of mine:
buttonEventHandler
Option Explicit
Public Sub Click(Sender As Integer)
End Sub
xButton
Private WithEvents btn As MSForms.commandButton
Private bEventHandler As buttonEventHandler
Private b As Integer
Public Sub createObject(EventHandlerOf As MSForms.commandButton, EventHandler As buttonEventHandler, xB As Integer)
Set btn = EventHandlerOf
Set bEventHandler = EventHandler
b = xB
End Sub
Private Sub btn_Click()
If Not bEventHandler Is Nothing Then bEventHandler.Click (b)
End Sub
in Microsoft Excel Objects: thisWorkbook
Public Sub buttonEventHandler_Click(Sender As Integer)
Select Case Sender
Case 1
'Do Stuff
End Select
End Sub
Private Sub workbook_open()
Sheet1.Frame1.Activate
Set xbtn = New Collection
Dim o As Object
Dim xB As New XButton
xB.createObject Sheet1.Frame1.Controls("excelCommandButton"), ThisWorkbook, 1
xbtn.Add xB
Set xB = Nothing
End Sub
You could place MyForm.Show code on the Workbook.Open event to launch the form whenever you open the file... that I've done. You can even choose where to place the form.
Your wish, if possible (I'm pretty positive it's not), could trigger concurrent VB code: e.g. auto-calculate of cells, and the form code. That's an issue because XL is single VBA thread, hence it's impossible to have two things happening. Either the form is showing and it's code is running, or XL application is running and doing cells/graphs/etc stuff.