Create global variables - vba

Bit of a newbie to VBA, sorry
I need to create some variables that are available throughout my workbook, but I can't seem to figure it out. I've read in previous questions where some people have suggested create a separate dim for this?
When the workbook opens I need to set some variables equal to certain cells in a worksheet, these variables need to be called from dims in others worksheets.
So far I have tried to use
Workbook_Open()
In the 'ThisWorkbook' code area but to no avail.
Any tips?
Reagards
EDIT ----
I have tried with the following:
In 'ThisWorkbook'
Public wsDrawings As String
Public Sub Workbook_Open()
wsDrawings = "Hello"
End Sub
And in Sheet1
Private Sub CommandButton1_Click()
MsgBox wsDrawings
End Sub
I do not get an error, but the message box is empty.

Just declare the variables you need wherever they are first used (ThisWorkbook is a fine place to do it) and replace the typical Dim with Public. It will then be accessable in all your code

You can create global variable with code like this
Public testVar As String
you need to place it outside function or sub and then this variable has value till you close workbook. But i think it have scope only in current module.
So you can have something like this
Public testVar As String
Private Sub Workbook_Open()
testVar = "test"
End Sub
Sub testEcho()
MsgBox testVar
End Sub
for shared variable between multiple modules look here
edit:
So now i found, that you can use public variable from ThisWorkbook using this
Sub testSub()
MsgBox ThisWorkbook.wsDrawings
End Sub

you can use module for creating global variable.

Related

In VBA, can a workbook object reference a method written in one of its worksheets?

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

Calling a userform from a specific sheet sub

Another newbie question but I cannot find my answer anywhere so far...
I have a workbook with several sheets, lets call them S1, S2 etc., I have a userform that does an operation that can be activated from any of the sheet.
My problem here is that I have parameters passed to the userform from the sub
Public c As Integer, lf As Integer, ld As Integer
Sub Tri()
ld = 8
lf = 128
Application.ScreenUpdating = False
UsForm.Show
End Sub
Now my workbook is growing in size and differences appear from S1 to S2 etc requiring me to change parameters depending on the sheet it is launched from.
So i removed my code from "module" and put it in the "Microsoft excel object" part. But it now seems it does not have access to my public variables and as soon as I request ld or lf, it is shown as empty (even if it was implemented in the previous userform).
Please can someone tell me what I'm missing ? How can I do otherwise (I do not want to put the data in the sheets themselves)?
You need to take advantage of the fact that a userform is a class. So as an example add the following code to the "form". Let's assume you have a button with the name CommandButton1
Option Explicit
Dim mVar1 As Long
Dim mVar2 As String
Property Let Var1(nVal As Long)
mVar1 = nVal
End Property
Property Let Var2(nVal As String)
mVar2 = nVal
End Property
Private Sub CommandButton1_Click()
MsgBox mVar1 & " - " & mVar2
Me.Hide
End Sub
Then you can add in a normal Module
Sub TestForm()
Dim frm As UserForm1
Set frm = New UserForm1
Load frm
frm.Var1 = 42
frm.Var2 = "Test"
frm.Show
Unload frm
End Sub
In such a way you can pass variables to a form without using global variables.
Here is a widely accepted answer about Variable Scopes. https://stackoverflow.com/a/3815797/3961708
If you have decalred your variable inside thisworkbook, you need to access it by fully qualifying it. Like ThisWorkbook.VariableName
But with UserForms I recommend to use Properties for data flow. Thats the clean and robust way to do it. Get in the habit of using properties and you will find it highly beneficial for UserForms.
Example:
Add this code in the ThisWorkbook
Option Explicit
'/ As this variable is defined in ThisWorkBook, you need to qualify it to access anywher else.
'/ Example ThisWorkbook.x
Public x As Integer
Sub test()
Dim uf As New UserForm1
x = 10
'/ Set the propertyvalue
uf.TestSquare = 5
'/Show the form
uf.Show
'/ Get the property value
MsgBox "Square is (by property) : " & uf.TestSquare
'/Get Variable
MsgBox "Square is (by variable) : " & x
Unload uf
End Sub
Now add a UserForm, called UserForm1 and add this code
Option Explicit
Private m_lTestSquare As Long
Public Property Get TestSquare() As Long
TestSquare = m_lTestSquare
End Property
Public Property Let TestSquare(ByVal lNewValue As Long)
m_lTestSquare = lNewValue
End Property
Private Sub UserForm_Click()
'/ Accessing the Variable Defined inside ThisWorkkbook
ThisWorkbook.x = ThisWorkbook.x * ThisWorkbook.x
'/ Changing Property Value
Me.TestSquare = Me.TestSquare * Me.TestSquare
Me.Hide
End Sub
Now when you run the Test sub from ThisWorkbook you will see how you can access variables and properties across the code.

VBA: declaring/setting global and permanent variable objects and accessing in other Subs/Functions

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

How to store an object that can be used in many event calls?

I have a Worksheet_BeforeDoubleClick event that checks to see whether a cell that's clicked has data that's in a Dictionary object, like so:
Private Sub Worksheet_BeforeDoubleClick(ByVal Target as Range, Cancel as Boolean)
Dim dict as Dictionary
Dim df as New dictFactory
'returns a dictionary populated with all list items
Set dict=df.create
If dict.Exists(Target.Value) Then
MsgBox "Exists"
Else
MsgBox "Doesn't exist"
End If
End Sub
The problem is that this requires creating a new dictionary each time a cell is clicked. I thought it would be good to store the dictionary in a global variable in its own module, like this:
Global valuesDict As New Dictionary
and then populate it upon opening the workbook:
Private Sub workbook_open()
Dim df as New dictFactory
Set valuesDict=df.create
End Sub
But I've encountered a lot of problems with this during testing, because there are many conditions under which a global variable's value can be reset (as discussed here).
How can I store an object so that its value will be available as long as the workbook is open, throughout repeated calls to my BeforeDoubleClick event?
Global valuesDict As Dictionary 'EDIT - drop the "new"
Private Sub Worksheet_BeforeDoubleClick(ByVal Target as Range, Cancel as Boolean)
'populate global only when needed
if valuesDict is Nothing then CreateDict
If dict.Exists(Target.Value) Then MsgBox "Exists"
Else
MsgBox "Doesn't exist"
End If
End Sub
'
Private Sub workbook_open()
CreateDict
End Sub
'
Sub CreateDict()
Dim df as New dictFactory
Set valuesDict=df.create
End sub
It's true that data of a module level variable (a.k.a. global variable) persists until the workbook is closed but an incomplete execution of code (due to a bug or deliberate interruption) will reset the variable, wiping out that data. It happens to static variables as well which work like module level variables in terms of duration even though static variables are local in scope.
To be safe, you can write code in the worksheet module to check if the global variable (referencing a dictionary) is valid, if not, run a dedicated procedure to recreate a dictionary.
BTW, the dictionary object has no Create method.

Excel VBA - Assign value from UserForm ComboBox to Global Variable

I have a userform with a basic combobox and command button. When the user hits the command button, I want the UserForm to close, and the value of the combobox to be saved in a variable that can be accessed by a subroutine contained within "ThisWorkbook".
In the UserForm code:
Public employee_position As String
Public Sub CommandButton1_Click()
employee_position = Me.ComboBox1.Value
Unload Me
End Sub
In the "ThisWorkbook" Code
Private Sub GetUserFormValue()
Call Userform_Initialize
EmployeePosition.Show
MsgBox employee_position
End Sub
When "GetUserFormValue()" runs, the UserForm comes up, you can select a value in the combobox and press the command button, but when the MsgBox comes up, it displays "" (Nothing)
What am I doing wrong here?
When you Unload Me, I think you lose all information associated with the module (including the global variable). But if you use Me.Hide rather than Me.Unload, then you can access the value of the form after the routine returns. So try this:
-- userform code includes:
Public Sub CommandButton1_Click()
Me.Hide
End Sub
-- main module includes:
Private Sub GetUserFormValue()
Call Userform_Initialize
EmployeePosition.Show
MsgBox EmployeePosition.ComboBox1.Value
Unload EmployeePosition
End Sub
I think that should work.
I had the same problem, and this is how I resolved it:
If the main code is in a worksheet, and the variable is declared as public in that worksheet (e.g. in Microsoft Excel Objects -> Sheet1 (Sheet1)), the result from "Unload Me" cannot be passed from a UserForm to the worksheet code.
So to solve my problem, I inserted a new Module, and declared my public variable there. I didn't even have to move my code from the worksheet to the module... just the declaration of the public variable.
I hope this works for you too!
Andrew