I have a Excel Object named ThisWorkbook and a Module named Module1. In Module1, I have a function called function1. ThisWorkbook has a private sub called sub1. When user call this function, I want Excel to do sub1 first and if it has no error, perform the rest of the function. However, I am unable to process sub1 when call function1.
Excel Object - ThisWorkbook
Private WithEvents App As Application
Public Sub sub1(some parameters)
...
If (condition) Then
Msgbox ()
End If
...
End Sub
Private Sub Workbook_Open()
Set App = Application
End Sub
Excel Module - Module1
Function function1(Add As String, some parameters) As String
ThisWorkbook.sub1(some parameters)
...
End Function
** updated frequently to show the current state of code
Since Sub1 is within the scope of ThisWorkbook and not in a global module you need to specify that in your call:
Call ThisWorkbook.Sub1
When you want to refer to a sub, which is in ThisWorkbook, you should refer to the ThisWorkbook as well like this:
ThisWorkbook.sub1 instead of Call sub1.
Related
Edited to provide more information.
Base Question:
Is it possible for a workbook to have access to a method written in one of its worksheets in VBA?
Real World Reason:
I need to have a method run on workbook launch (Workbook_Open() event) however, that method has a few requirements which makes modules not viable:
Needs to maintain state
Needs to store data
Needs to be referenced from both the workbook, and the worksheets in said workbook
Psuedocode example:
myWorkbook
Private Sub Workbook_Open()
Call myWorkbook.Sheet("worksheet1").helloWorld
End Sub
worksheet1
Public Sub printHelloWorld()
'Store "Hello World" so we can use later, this method needs to maintain state
Dim helloWorld As String
Set helloWorld = "Hello World"
MsgBox(helloWorld)
End Sub
Yes, this can be done. Both of the below work fine.
Sub TestWithExplicitWorksheet()
wsTest.TestMessage
End Sub
Sub TestWithWorksheetsCollection()
ThisWorkbook.Worksheets("Sheet1").TestMessage
End Sub
wsTest Code:
Sub TestMessage()
Debug.Print "Success"
End Sub
A Worksheet is more or less a Predeclared Class Module. So if you can do it with a Class, you should be able to do it with a Worksheet. Since the Worksheets collection of the Workbook object returns (if found) a Worksheet object, you can call the method on this returned object (if it's the correct object and contains the sub being called).
I would put the code below in a module
Public Sub printHelloWorld()
MsgBox("Hello World")
End Sub
and the code in myWorkBook will look like
Private Sub Workbook_Open()
printHelloWorld
End Sub
Yes, you can use the sheet CodeName (you can also change it in the sheet object properties):
Private Sub Workbook_Open()
Sheet1.printHelloWorld() ' full name is VBAProject.Sheet1.printHelloWorld()
End Sub
I currently have a Form set in place within my workbook. This form contains a button. On the click of the button, I would like to call a Sub which is located within the "ThisWorkbook" section. How could I go along of doing this?
Button within form...
Sub CommandButton1_Click()
Call Main("Test")
End Sub
The Sub that needs to be called within "ThisWorkbook"
Sub Main(DPass As String)
msgbox Dpass
End Sub
This will give me a compile error of: Sub or Function not defined. Why does this happen?
There are essentially two types of modules:
"Standard/Procedural" modules
Class modules
"Document" modules (e.g. ThisWorkbook, Sheet1, etc.) are just special kinds of class modules. Same for "UserForm" modules, which are basically classes with a default instance and a designer.
Members of a class module don't exist at run-time; a class is nothing but a blueprint for an object - so you need to either create an object of that type (the class determines the type), or use an existing one.
ThisWorkbook is an instance of the Workbook class; Sheet1 is an instance of the Worksheet class. Chart1 is an instance of the Chart class; UserForm1 is an instance of the UserForm class. And so on.
If you make a new class module and call it Class1, and add a public procedure to it:
Public Sub DoSomething()
MsgBox "Something!"
End Sub
Then in order to call DoSomething you need an instance of Class1:
Dim foo As Class1
Set foo = New Class1
foo.DoSomething
If DoSomething is in the ThisWorkbook module, then you can call it by qualifying the method name with the object it exists on, as was mentioned in the comments and in the other answer:
ThisWorkbook.DoSomething
If DoSomething is implemented in a standard/procedural module, then there is no object, the procedure exists in global scope, and you can just do this:
DoSomething
However public members of procedural modules are also exposed as macros (Public Sub) and user-defined functions (Public Function), which you may not want to do.
If you need a procedural module with public members that you can only call from VBA code (and not by clicking a button on a worksheet, or by entering a formula in a cell), then you can specify Option Private Module at the top:
Option Private Module
Public Sub DoSomething()
' DoSomething is not exposed as a macro,
' but can be called from anywhere in the VBA project.
End Sub
Put your sub in a module and not in your ThisWorkbook unless you have to for some reason, if so use ThisWorkbook.Main "string".
I want to declare variable/objects that should be:
Global: I will be able to use them in all Subs/Functions
Permanent: some events trigger those subs and the variable should still be accessible/available to the subs.
The use of Workbook_Open() sub is not required, but it was the closest thing I found to a constructor... My simplified structure:
In ThisWorkbook:
Public HS As Worksheet <-- the global variable
Private Sub WorkbookOpen()
Set HS = ActiveWorkbook.Sheets(1)
End Sub
In Module1 :
Sub HistCheck()
HS.Activate <-- Objet required
End Sub
You need to place your global variables within a module, not the workbook or sheet*.
In Modeul1:
Global Public HS As Worksheet
Only Modules can declare variables that can be used throughout the worksheet objects, subs, and functions.
You were correct in placing this code under the workbook object
Under Workbook:
Private Sub WorkbookOpen()
Set HS = ActiveWorkbook.Sheets(1)
End Sub
With both of these items in the proper locations, you can place your code as follows in any location of the document, as long as the document is loaded in memory of course.
Sub HistCheck()
HS.Activate
End Sub
If you have any issues, please write back what the exact error messages are to allow troubleshooting.
Use a factory to initialized your global objects
Factory.bas
Public Function GetHS() as WorkSheet
GetHS = ActiveWorkBook.Sheets(1)
End Function
Module1.bas
Sub HistCheck()
Factory.GetHS().Activate
End Sub
I have all macros stored in personal.xlsm and untill now I have used a standard filter to hide/show columns. The new feature I want to implement now is that each user can have their own filter if they would like to. So basically i look in a folder for a personal filter if it exist and if it does it use that filter instead of the standard one.
But my problem is that i want to load a personal filter on workbookOpen event and reset to standard filter on the beforeClose event. My question is if I can do this from personal.xlsm in a way? Or do I have to manually go through all 250 workbooks and add in thisworkbook module onOpen and beforeClose events to call my method createFilter and resetFilter?
Here is a link to personal.xlsm for those who are not familiar with that methodology
In personal.xlsm, create a class module named "Helper". Place the following code in it:
Option Explicit
Public WithEvents xlApp As Application
Private Sub Class_Initialize()
Set xlApp = Application
End Sub
Private Sub xlApp_WorkbookOpen(ByVal Wb As Workbook)
'Your code here
End Sub
Your code for loading a filter should go in the 'Your code here bit.
Add a standard code module and add this code:
Public MyHelper As Helper
Finally, in the Workbook_Open event of personal.xlsm, place this code:
Private Sub Workbook_Open()
Set MyHelper = New Helper
End Sub
Save personal.xlsm and restart Excel.
Here is the code in helper if someones needs it:
Private Sub xlApp_WorkbookOpen(ByVal Wb As Workbook)
Dim myPath As String
On error resume next
If InStr(UCase(ActiveWorkbook.Name), "PARTSLIST") And (InStr(UCase(ActiveWorkbook.Name), "FILTERPARTSLIST") = 0) Then
myPath = Left(ThisWorkbook.Path, InStrRev(ThisWorkbook.Path, "\") - 1)
If Dir(myPath & "\filterPartsList.xlsx") <> "" Then '
Call ColumnCreater.updateFilter
Else
Call ColumnCreater.filterCreationStandard
End If
End If
End Sub
My question: How can I set value of a variable declared in other Excel Project?
Background:
I am working on calling a private sub from a diffrent Excel Project (I don't know if it matters, but the sub I am interested in is a part of Excel Add-In).
In the Add-In I have:
Public sapEEID As String
Private Sub UpdateLetterTemplate
I am able to run the sub using the:
Application.Run ("'Solutions Add-In.xlam'!UpdateLetterTemplate")
HOWEVER, variable sapEEID = ""
Is there a way to pass "17" as sapEEID when running UpdateLetterTemplate private sub?
You may need to modify your Subroutine somewhat but the following steps will work
Add Reference: You need to add a reference to your add-in (VBE -> Tools -> References)
ByRef Parameters: Also, make sure that your Sub can take a ByRef parameter. See sample code below.
Call the Subroutine: You're done, now in your code, once the reference is set, call the sub and pass your variable.
Sample Code for the Subroutine:
Public Sub ChangeToTen(ByRef a as double)
a = 10
End Sub
Calling Code in your main file:
Dim a as double
a = 1023.23
Call ChangeToTen(a)
MsgBox(a) ' It will show 10