Calling a Private Sub from another module - vba

I have two modules, Module1 and Module2.
In Module1:
Private Function myCheck() As Boolean
if [Operation] = [Something] then
myCheck = True
Else
myCheck = False
End if
End Sub
In Module2, I would like to run myCheck sub in Module 1 then do another operation:
Private Sub Execute()
[Operation 1]
If myCheck = True Then
[Operation 2]
Else
[Operation 3]
End If
End Sub
It does not work. If I place Private Function myCheck within the same module then it works. Is there a special method to call a sub or function from another module?

Use Option Private Module For Module1, then in Module 2:
for a Sub; qualify what you're calling with the Module it's in, like so:
Module1.myCheck()
for a Private Sub; use Application.Run and qualify what you're calling with the Module it's in, like so:
Application.Run ("Module1.myCheck")
Using Private Module hides it's contained sub/s in the Developer > Macros list.
Further Reading :-)
Read through the comments in the code below to see what does and doesn't work.
To confirm the behaviours yourself:
Create a new Excel, open Developer > Visual Basic, insert 3 Modules.
Copy the below code blocks into the relevant modules.
'In Module1
Option Explicit
Sub ScopeTrials()
'NOTES:
' Only NormalSubIn_NormalModule shows in Developer > Macros.
' As the default without a keyword is Public I have called
' these "Normal". I.e. you can use Public or Nothing wherever
' Normal is.
' A line commented out shows what doesn't work.
NormalSubIn_NormalModule
Call NormalSubIn_NormalModule
Application.Run ("NormalSubIn_NormalModule") 'Not recommended!
NormalSubIn_PrivateModule
Call NormalSubIn_PrivateModule
Application.Run ("NormalSubIn_PrivateModule") 'Not recommended!
'PrivateSubIn_NormalModule
'Call PrivateSubIn_NormalModule
'Module2.PrivateSubIn_NormalModule
'Call Module2.PrivateSubIn_NormalModule
Application.Run ("PrivateSubIn_NormalModule") 'Fails with duplicates! See Explanation
Application.Run ("Module2.PrivateSubIn_NormalModule")
'PrivateSubIn_PrivateModule
'Call PrivateSubIn_PrivateModule
'Module3.PrivateSubIn_PrivateModule
'Call Module3.PrivateSubIn_PrivateModule
Application.Run ("PrivateSubIn_PrivateModule") 'Fails with duplicates! See Explanation
Application.Run ("Module3.PrivateSubIn_PrivateModule")
'Explanation: if there is an identical sub in another Private Module, then this fails
'with Runtime Error 1004 (Macro not available or Macros Disabled), which is Misleading
'as the duplication and/or nonspecified module is the problem.
'I.e. always specify module!
'Also, this only fails when the line is actually run. I.e. Compile check doesn't find this
'when starting to Run the code.
End Sub
'In Module2
Option Explicit
Sub NormalSubIn_NormalModule() 'only THIS sub shows in Developer > Macros
MsgBox "NormalSubIn_NormalModule"
End Sub
Private Sub PrivateSubIn_NormalModule()
MsgBox "PrivateSubIn_NormalModule"
End Sub
'In Module3
Option Explicit
Option Private Module
Sub NormalSubIn_PrivateModule()
MsgBox "NormalSubIn_PrivateModule"
End Sub
Private Sub PrivateSubIn_PrivateModule()
MsgBox "PrivateSubIn_PrivateModule"
End Sub

You can use Application.Run to do this:
Application.Run "Module1.myCheck"
The macro will stay "invisible" for the users if they display the Macros Dialog Box (Alt+F8), since it's still private.
Edit #1
A second option is to introduce a dummy variable as an optional parameter in the Sub, like this:
Public Sub notVisible(Optional dummyVal As Byte)
MsgBox "Im not visible because I take a parameter, but I can be called normally."
End Sub
This too, will hide the macro in the Macros Dialog Box (Alt+F8), but it can now be invoked the usual way.

Related

Calling set of event functions of objects in other sub function

I have an UserForm in Excel VBA with different Objects like CheckBox, RadioButtons, TextEdit Box etc. Each Object has different event functions, but i want to call the some event functions in user defined sub function like UserForm_Init() function in order to re-utilize the same code written in them.
Example:
Private Sub CheckBox1_Click() [Later changed to public also]
If CheckBox1.Value Then
--- Code
End If
End Sub
Private Sub CheckBox2_Click()
If CheckBox2.Value Then
--- Code
End If
End Sub
Private Sub RadioButton1_Click()
If RadioButton1.Value Then
--- Code
End If
End Sub
' User Defined Function
Sub UserForm_Init()
CheckBox1_Click()
CheckBox2_Click()
RadioButton1_Click()
End Sub
But when i am trying to compile it is showing compile error, how can i execute these object event functions within user-defined sub function.
I know I can write individual functions within Event functions and call these individual functions for user-defined sub function. But in this case, it will be too-difficult to re-change entire code. I have only option of calling these event-functions in an sub-function for execution.
How can i perform these actions, call event functions with in the user defined sub function [UserForm_Init()]?
VBA doesn't use curly braces. This code won't compile with those. VBA requires Sub and End Sub to define the procedures.
Sub CheckBox1_Click()
if CheckBox1.Value Then
--- Code
end if
End Sub
Sub CheckBox2_Click()
if CheckBox2.Value Then
--- Code
end if
End Sub
Sub RadioButton1_Click()
if RadioButton1.Value Then
--- Code
End If
End Sub
Sub UserForm_Init()
CheckBox1_Click()
CheckBox2_Click()
RadioButton1_Click()
End Sub
NOTE: If your custom function UserForm_Init is not in the Form's code module, you'll need to qualify the procedure names, e.g.:
Sub UserForm_Init()
UserForm1.CheckBox1_Click()
UserForm1.CheckBox2_Click()
UserForm1.RadioButton1_Click()
End Sub
Additional complexity may arise based on your implementation, but I hope this will get you started.
You're missing parts of code in your example; it's hard to determine what you're trying to do without a clear picture. It is possible to call an event procedure from another, it's a bad idea for a number of reasons.
Instead of calling a private procedure like you're trying to, sort of like this:
Private Sub myEvent()
'event code here
End Sub
Sub mySub()
myEvent()
End Sub
...you're better off to move the code that you need to be shared to a shared module, kind of like this:
Public Sub mySubInSharedModule()
'event code here
End Sub
Private Sub myEvent()
Call mySubInSharedModule()
End Sub
Public Sub mySub()
Call mySubInSharedModule()
End Sub
There are numerous articles explaining this further, on this site and others. To get you started, see this search as well as this Stack Overflow question.

EXCEL VBA procedure ambigous name

My VBA code is too large and I'm trying to make smaller SUBS so the error won't come up, but then the error "Ambigoius name" pops up. I've tried to rename my subs...
Ex.
Private Sub worksheet_calculate()
Range("I9").Interior.Color=Range("AK9").Display.Format.Interior.Color
end sub
Private Sub worksheet_calculate2()
Range("J9").Interior.Color=Range("AQ9").Display.Format.Interior.Color
end sub
...when I rename the other subs as shown in the example it doesn't do anything, only the original work properly. How do I rename them so they can work properly?
In my understanding, worksheet_calculate is the predefined name of the subroutine, triggered by the event when the worksheet is recalculated.
You can define and call other private subs from it.
like
Private Sub worksheet_calculate()
rem sub body
CalculateSub1 pars 'variant one
Call CalculateSub1(pars) 'variant two
rem sub body
End Sub
Sub CalculateSub1(pars)
Rem Sub body
End Sub
Just insert the below line of codes at the end of your main sub which is "worksheet_calculate"
Call worksheet_calculate2
Call worksheet_calculate3
Call worksheet_calculate4
Call worksheet_calculate4
.
.
.
Call worksheet_calculaten

VBA - UserForm Button (Macro) to Continue running the sub

I have a sub called as sub1() which activates the userform1 through the command userform1.show()
This userform1 has a button called as continue. On clicking that button continue - A Macro called as Private Sub continuebutton() gets activated.
I want to program in such a way that it redirects to the line after userform1.show() in sub1().
Is it something that can be done?
Theoretically, what you want is possible, if you do it like this:
In the UserForm:
Private Sub btnContinue_Click()
flag = True
Debug.Print "I continue ..."
sub1
End Sub
In a module:
Public flag As Boolean
Public Sub sub1()
If flag Then
Debug.Print "sub 1 continues here..."
Else
flag = False
UserForm1.Show
Exit Sub
End If
End Sub
It will work exactly as intended, BUT it is not a good practice to work this way. (Some people may throw stones at you for using public variables at all in VBA.) Here are two articles, that give better ideas:
https://rubberduckvba.wordpress.com/2017/10/25/userform1-show/
Disclaimer - this one is mine:
http://www.vitoshacademy.com/vba-the-perfect-userform-in-vba/
On the form properties for userform1, set its "Modal" property to true.
When the form opens, it will have exclusive focus, and the code in sub1 will not continue running until after it closes. This may be the solution you need.
In the code below, the msgbox will only appear once userform1 closes:
sub sub1()
userform1.show()
msgbox "Now continuing with sub1"
end sub
No way as long as you show the form.
If you show the form modal, the calling routine continues if (and only if) the form is closed.
If you show the form non-modal, the code continues to run directly after the show - so it's already done.
So either you have to close the form when the user clicks the "continue..." button to let the calling macro continue or you have to split your code into two routines and call the second on button-click.
You can change your Sub1 as follows:
Sub sub1(Optional Continue As Boolean)
If Continue = True Then
DoSomeStuff
Exit Sub
End If
userform1.show
End sub
And then, you can call your sub1 using:
Private Sub continuebutton()
Call sub1(True)
End Sub
Hope it helps
If you don't want to go with the 'Modal Form' solution, you could add a subroutine to your main module, and call it when required. So, in userform1, you have:
sub sub1()
userform1.show()
end sub
public sub sub2()
msgbox "Now continuing..."
end sub
And then in userform1, set some code on its onClose event:
Private Sub continuebutton()
Call sub2()
end sub

How to schedule an async macro from WORD

I'm facing an annoying issue where I simply want to schedule an asynchronous macro from another instance of Word, which happens to be the same .doc file.
Meaning, in the ThisDocument namespace I have the following code snippet:
Public Sub Document_Open()
Set Obj = New Word.Application
Obj.OnTime Now + TimeValue("00:00:01"), "Module1.Test"
End Sub
I've declared a new object of Word due to the following reasons:
My macro may block user's I\O
The user may close the document before the macro finishes its task
And declared a module named Module1 with a simple MsgBox
Public Sub Test()
MsgBox "hhh"
End Sub
Needless to say, nothing happened, and I'm unable to check what OnTime function returns.
I've also tried the following combinations:
"!Module1.Test"
"c:\\....\\file.doc!Module1.Test"
What am I missing here?
Your newly created Word application, represented by a Word.Application object, doesn't have the document open. Thus there's no "Module1" as far as he's aware. The fact that you want to run code from the same document is immaterial. It's a different instance of Word.
Something like this works:
'''''''' ThisDocument
Option Explicit
Private Sub Document_Open()
Dim res As VbMsgBoxResult
Dim Obj As Word.Application
If Application.Visible Then
res = MsgBox("Hello", vbOKCancel, "Hi!")
Else
Exit Sub
End If
If res = vbOK Then
Set Obj = New Word.Application
Obj.Documents.Open "C:\Users\conio\Desktop\Hello.docm", , True
Obj.OnTime Now + TimeValue("00:00:01"), "Module1.Foo"
End If
End Sub
'''''''' Module1
Public Sub Foo()
If Not Application.Visible Then
MsgBox "Foo"
Application.Quit
End If
End Sub
Maybe you'd want to use a different check to differentiate between the interactive run and the unattended run.

Calling a private VBA subroutine with Parameter

I have a VBA Add-In which is supposed to add functionality to several worksheets. I have a class to listen for worksheet events and trigger macros. This works but I can't seem to call private macros with parameters from it.
My event listener is in a class module and is this:
Option Explicit
Private WithEvents App As Application
Private Sub Class_Initialize()
Set App = Application
End Sub
Private Sub App_SheetChange(ByVal Sh As Object, ByVal Source As Range)
On Error GoTo Finish
App.EnableEvents = False
Call Application.Run("Worksheet_Change", "Source")
Finish:
App.EnableEvents = True
End Sub
It is initialized on opening the workbook in a module like so:
Sub Auto_Open()
Set clsAppEvents = New clsApplicationEvents
End Sub
And the macro I'm trying to call is in a separate module again and takes the form:
Private Sub Worksheet_Change(ByVal Target As Range)
'Do some work on range
End Sub
I've tried the following ways of calling the macro and none have worked so far:
Call Application.Run("Worksheet_Change", "Source")
Application.Run "Worksheet_Change", "Source"
Application.Run "Worksheet_Change" "Source"
The arguments to Run don't all have to be strings, so
Application.Run "Worksheet_Change", "Source"
Should be
Application.Run "Worksheet_Change", Source