Variable in a form won't keep its value after being used in the call to another form - vba

I have a form with a variable in it called "VigilTable." This variable gets its value from the calling string OpenArgs property.
Among other things, I use this variable in the call string when opening other forms.
But it only works the first call.
MsgBox VigilTable before the call will always show "Spring2022" or whatever on the first call but always comes up blank on succeeding calls (and I get "invalid use of NULL" when the called form attempts to extract the value from OpenArgs). The variable is dimmed as String in the General section of the form's VBA code.
So what's happening here? And can I fix it?
Thanks.

Ok, so you delcared a variable at the form level (code module) for that given form.
and we assume that say on form load, you set this varible to the OpenArgs of the form on form load.
So, say like this:
Option Compare Database
Option Explicit
Public MyTest As String
Private Sub Form_Load()
MyTest = Me.OpenArgs
End Sub
Well, I can't say having a variable helps all that much, since any and all code in that form can use me.OpenArgs.
but, do keep in mind the following:
ONLY VBA code in the form can freely use that variable. It is NOT global to the applcation, but only code in the given form.
However, other VBA code outside of the form can in fact use this variable. But ONLY as long as the form is open.
So, in the forms code, you can go;
MsgBox MyTest
But, for VBA outside of the form, then you can get use of the value like this:
Msgbox forms!cityTest.MyTest
However, do keep in mind that any un-handled error will (and does) blow out all global and local variables. So, maybe you have a un-handled error.
Of course if you compile (and deploy) a compiled accDB->accDE, then any errors does NOT re-set these local and global variables.
but, for the most part, that "value" should persist ONLY as long as the form is open, and if you close that form, then of course the values and variables for that form will go out of scope (not exist).
Now, you could consider moving the variable declare to a standard code module, and then it would be really global in nature, but for the most part, such code is not recommended, since it hard to debug, and such code is not very modular, or even easy to maintain over time.
So, this suggests that some error in VBA code is occurring, and when that does occur, then all such variables are re-set (but, the noted exception is if you compile down to an accDE - and any and all variables will thus persist - and even persist their values when VBA errors are encountered.

For a string variable, a more robust solution not influenced by any error, should be writing/reading in/from Registry. You can use the, let as say, variable (the string from Registry) from any workbook/application able to read Registry.
Declare some Public constants on top of a standard module (in the declarations area):
Public Const MyApp As String = "ExcelVar"
Public Const Sett As String = "Settings"
Public Const VigilTable As String = "VT"
Then, save the variable value from any module/form:
SaveSetting MyApp, Sett, VigilTable , "Spring2022" 'Save the string in Regisgtry
It can be read in the next way:
Dim myVal as String
myVal = GetSetting(MyApp, Sett, VigilTable , "No value") 'read the Registry
If myVal = "No value" Then MsgBox "Nothing recorded in Registry, yet": Exit Sub
Debug.print myVal

Actually, this proved not to be the the answer at all.
It was suggested that I declare my variables as constants in the Standard module but I declared them as variables. It appeared at first to work, at least through one entire session, then it ceased to work and I don't know why.
If I declare as constants instead, will I still be able to change them at-will? That matters because I re-use them with different values at different times.
I didn't do constants but declaring VigilName in the Standard module and deleting all other declarations of it fixed both problems.
While I was at it I declared several other variables that are as generally used and deleted all other declarations of them as well so that at least they'll be consistently used throughout (probably save me some troubleshooting later.
Thanks to all!

Related

Passing a value from UserForm to sheet

I created a userform called Level with a button, "beginner". When it's clicked "beginner" is set as levelinput as a string to be used in coding sheet 1
--------------Sheet1(Code)------------
Public levelInput as string
Public Sub Player()
Level.show
Msgbox levelInput
Lev=levelInput
.....
end sub
--------------Below is for Userform, Levels------------------
Private Sub beginner_Click()
levelInput= "beginner"
Levels.Hide
end sub
Currently, the msgbox shows nothing and the value doesn't seem to be passed to the Sheet(Code).
Where did I go wrong?
levelInput= "beginner"
That variable isn't declared. I know, you think it is, but I'll get to it in a moment.
The thing you've fallen pray of, is called scoping.
Public levelInput as string
By declaring the levelInput public field in the code-behind of Sheet1, you're saying "any instance of Sheet1 will have a levelInput public variable".
So you can change your code to this:
Sheet1.levelInput= "beginner"
And it will work.
But the most important thing to do is this:
Option Explicit
Always turn that option on, in the declarations section of every module (i.e. at the top, before any Sub or Function or Property). Had you done that, the VBA compiler would have told you levelInput isn't declared, and would have refused to run anything until you fixed the problem - either by declaring a locally-scoped levelInput variable, or by properly qualifying the Sheet1.levelInput public field.
Without Option Explicit, VBA happily lets you assign and refer to variables that aren't declared, which leads to unexpected bug, as you've experienced.
PS - Once you get your code to work as intended, I'd recommend you post it on Code Review, where you'll learn things like why Level.Show is bad code, and how you can use functions, parameters and return values instead of global variables, for more robust, maintainable and easier-to-follow code.

MS Access: Assign variable for Form

I am trying to set up a global variable (as form) and set it = Form_MyForm
I have used the Form Load event to make the assignment and since it is a global variable I am expecting all my procedures can use this variable of mine without me retyping the assignment in different procedures
Problem is that sometimes it works while at others times it fails to recognise my variable, at which point I have to close my form and re-open it to refresh the assignment
I looked at the many Event for Access Forms but not sure what they are and how they can be helpful to my situation
Thanks for your help guys!
Define your global variable(s) in an Access module (not behind any form). This allows any object (form, report, macro etc.) to have access to them.
Initialize the variables inside a Public Function or Public Sub within the module and have the function/sub called by the database's opening form (i.e., main menu or switchboard).
Now, variables can be used in expressions, queries, VBA, and other areas.
**If the global variable needs to be redefined by various parameters, set that up in the function/sub and re-call it from a specific trigger event.
Had the same problem with user defined global variables, any time there is an error thrown or you switch to the development environment, you lose the set value in the variable.
You could try using a Session Variable, from the TempVar collection, which I find to be much more stable. It is stays set until you close the Database or you unset it.
MyFormName = me.Form.Name
'Load the data into Session variables
TempVars.Add "SessMyFormName", MyFormName
'Use the value elsewhere
SelectedForm = TempVars![SessMyFormName]
'Remove All Session Variables Set Earlier
TempVars.RemoveAll 'Destroy Session

VBA User form with ThemeColorScheme and late binding

I'd like to run a user form with status bar.
I show my form with code bolow.
How should I declare variables and assigning new values to those variables?
Very important: I have to use late binding in my project.
Sub RunMyUserForm()
With MyUserForm
.LabelProgress.Width = 0
.TextBox1 = 1
'to make the progress bar color match the workbook's current theme:
.LabelProgress.BackColor = ActiveWorkbook.Theme.ThemeColorScheme.Colors(msoThemeAccent1)
.Show vbModeless
End With
End Sub
Thank you in advance for your help!
Updated information:
When I try to run my macro with "Option Explicit", it doesn't work (Compile error: Variable not defined - part of code msoThemeAccent1 is marked as yellow color). That's why I asked for help in defining the variables.
When I try to run my macro without "Option Explicit", it dosen't work (Err.Description: "Value is out of range", Err.Number: -2147024809)
When I try to run my macro with early binding (reference to "MS Office Object Library" via Tools/References in VBE) everything works perfect with (and without) "Option Explicit".
Your compiler is seeing msoThemeAccent1 as a variable, and it is undeclared. This is why your code won't run with Option Explicit and also why your code raises an error when you disable Option Explicit. .Colors is a 1-based collection (?) so when you call:
ActiveWorkbook.Theme.ThemeColorScheme.Colors(msoThemeAccent1)
It is compiling to:
ActiveWorkbook.Theme.ThemeColorScheme.Colors(0)
Which raises an expected, albeit cryptic error.
I used some code to check the value of this constant, but in hindsight I should have just referred to the documentation:
http://office.microsoft.com/en-us/excel-help/HV080559557.aspx
This should fix it for you
ActiveWorkbook.Theme.ThemeColorScheme.Colors(5)
Alternatively, if you need to rely on this value in several places in your code, you could declare a variable (public or private, scope depends on what you need it for).
Public Const myAccentColor1 as Long = 5
And then, in your code you could:
ActiveWorkbook.Theme.ThemeColorScheme.Colors(myAccentColor1)
Revised
I understand now, without reference to MS Office Object Library this makes sense. This is one of the few libraries that I maintain a reference to in my XLSB file, so my confusion was a result of the fact that I thought I was using an appropriate late-binding, but I was really using early-binding.

Global variable loses its value

On this Access form I am working on I have a global variable that take its value from another form on its Form_Load event. For some reason unknown to me the variable "loses its value" (becomes = "") after some time elapses or some event occurs. I have not been able to notice anything in particular that triggers this behaviour. Are global variables reset after some time of "inactivity" on the form ?
Here is how I set the global variables I am talking about:
Private Sub Form_Load()
'...
Set prev_form = Form_Identification.Form
PasswordSybase = prev_form.Password.Value & vbNullString
UserSybase = prev_form.UserID.Value & vbNullString
'...
End Sub
An alternate solution (Only 2007 and onwards) I've started using is TempVars instead of globals in the odd situation I "needed" something global. It's a collection and it persists for the duration of the application unless you explicitly release it. So in some cases I feel its more useful than globals and in some cases worse.
TempVars.Add myVarName, myVarValue ' To initialize
TempVars.Item(myVarName) = newVarValue ' To reference and assign a new value
TempVars.Remove(myVarName) ' To release
Quick search should show you more lot references, but I've included link to a basic one
http://blogs.office.com/b/microsoft-access/archive/2010/09/27/power-tip-maximize-the-user-of-tempvars-in-access-2007-and-2010.aspx
I do hope that visitors see this post, as it provides an important additional piece.
Even if you declare a variable globally, it appears that - in the event that you set that variable's value in a form module - that value is lost when you UNLOAD the form.
The solution (in my case) was as simple as replacing:
Unload Me
...with...
Me.Hide
The variables (and objects) that I set in that code module then retained their values through the entire lifetime of the application instance.
This may help:
https://accessexperts.com/blog/2011/01/12/multi-session-global-variables/
Juan Soto explains how to use a local table to keep variables and how to call them when needed. It may serve your purpose in 2000 since TempVars isn't an option. You could always delete the variables "on close" of the database so that UID and PWD aren't kept.
You can create a "fake" global variable by
creating a form (e.g. named frmGlobal)
make sure the form is always open but hidden
create a TextBox for each global variable you want (e.g. tVar1)
in your code, reference as e.g. Form_frmGlobal.tVar1
The disadvantage is that an unbound text box may not give you a specific data type you want
The two ways around that are
in your code, explicitly convert the textbox to the data type when referencing the global variable
e.g Clng(Form_frmGlobal.tVar1)
another option is create a one-row table and bind your textboxes on your hidden form to the table, so your data types are enforced
A bonus of this method is you can use for persistent storage between sessions
Caveat: make sure this table is local to a single user only, in the front end database file (don't want to put it in the back end database because of multi-users over-writing each other). This assumes you are using front end + back end separated databases, with distribution of front end to each user's workstation.
I see nothing in that statement that tells me it's a global variable. You set global variables above ALL Subs/Functions and below an Options Compare statement in a module, by stating:
PUBLIC X as string
Any other variable is only good until the sub or function has completed.
Also, Global variables MUST be declared on a PROPER MODULE. You can't declare them on a form's module.

Is it possible to declare a public variable in vba and assign a default value?

I want to do this but it won't compile:
Public MyVariable as Integer = 123
What's the best way of achieving this?
.NET has spoiled us :)
Your declaration is not valid for VBA.
Only constants can be given a value upon application load. You declare them like so:
Public Const APOSTROPHE_KEYCODE = 222
Here's a sample declaration from one of my vba projects:
If you're looking for something where you declare a public variable and then want to initialize its value, you need to create a Workbook_Open sub and do your initialization there.
Example:
Private Sub Workbook_Open()
Dim iAnswer As Integer
InitializeListSheetDataColumns_S
HideAllMonths_S
If sheetSetupInfo.Range("D6").Value = "Enter Facility Name" Then
iAnswer = MsgBox("It appears you have not yet set up this workbook. Would you like to do so now?", vbYesNo)
If iAnswer = vbYes Then
sheetSetupInfo.Activate
sheetSetupInfo.Range("D6").Select
Exit Sub
End If
End If
Application.Calculation = xlCalculationAutomatic
sheetGeneralInfo.Activate
Load frmInfoSheet
frmInfoSheet.Show
End Sub
Make sure you declare the sub in the Workbook Object itself:
Just to offer you a different angle -
I find it's not a good idea to maintain public variables between function calls. Any variables you need to use should be stored in Subs and Functions and passed as parameters. Once the code is done running, you shouldn't expect the VBA Project to maintain the values of any variables.
The reason for this is that there is just a huge slew of things that can inadvertently reset the VBA Project while using the workbook. When this happens, any public variables get reset to 0.
If you need a value to be stored outside of your subs and functions, I highly recommend using a hidden worksheet with named ranges for any information that needs to persist.
Sure you know, but if its a constant then const MyVariable as Integer = 123 otherwise your out of luck; the variable must be assigned an initial value elsewhere.
You could:
public property get myIntegerThing() as integer
myIntegerThing= 123
end property
In a Class module then globally create it;
public cMyStuff as new MyStuffClass
So cMyStuff.myIntegerThing is available immediately.
Little-Known Fact: A named range can refer to a value instead of specific cells.
This could be leveraged to act like a "global variable", plus you can refer to the value from VBA and in a worksheet cell, and the assigned value will even persist after closing & re-opening the workbook!
To "declare" the name myVariable and assign it a value of 123:
ThisWorkbook.Names.Add "myVariable", 123
To retrieve the value (for example to display the value in a MsgBox):
MsgBox [myVariable]
Alternatively, you could refer to the name with a string: (identical result as square brackets)
MsgBox Evaluate("myVariable")
To use the value on a worksheet just use it's name in your formula as-is:
=myVariable
In fact, you could even store function expressions: (sort of like in JavaScript)
(Admittedly, I can't actually think of a situation where this would be beneficial - but I don't use them in JS either.)
ThisWorkbook.Names.Add "myDay", "=if(isodd(day(today())),""on day"",""off day"")"
Square brackets are just a shortcut for the Evaluate method. I've heard that using them is considered messy or "hacky", but I've had no issues and their use in Excel is supported by Microsoft.
There is probably also a way use the Range function to refer to these names, but I don't see any advantage so I didn't look very deeply into it.
More info:
Microsoft Office Dev Center: Names.Add method (Excel)
Microsoft Office Dev Center: Application.Evaluate method (Excel)
As told above, To declare global accessible variables you can do it outside functions preceded with the public keyword.
And, since the affectation is NOT PERMITTED outside the procedures, you can, for example, create a sub called InitGlobals that initializes your public variables, then you just call this subroutine at the beginning of your statements
Here is an example of it:
Public Coordinates(3) as Double
Public Heat as double
Public Weight as double
Sub InitGlobals()
Coordinates(1)=10.5
Coordinates(2)=22.54
Coordinates(3)=-100.5
Heat=25.5
Weight=70
End Sub
Sub MyWorkSGoesHere()
Call InitGlobals
'Now you can do your work using your global variables initialized as you wanted them to be.
End Sub
You can define the variable in General Declarations and then initialise it in the first event that fires in your environment.
Alternatively, you could create yourself a class with the relevant properties and initialise them in the Initialise method
This is what I do when I need Initialized Global Constants:
1. Add a module called Globals
2. Add Properties like this into the Globals module:
Property Get PSIStartRow() As Integer
PSIStartRow = Sheets("FOB Prices").Range("F1").Value
End Property
Property Get PSIStartCell() As String
PSIStartCell = "B" & PSIStartRow
End Property
there is one way to properly solve your question. i have the same concern with you for a long time. after searching and learning for a long time, finally i get a solution for this kind of question.
The solution is that no need to declare the variable and no need to set value to the variable, and even no need VBA code. Just need the "named range" in excel itself.
For example, the "A1" cell content is "hello, world". and we define the "A1" cell a name as "hello", that is, the "A1" cell have a name now, it's called "hello".
In VBA code, we just need use this method [hello], then we can get the "A1" value.
Sub test()
msgbox [hello]
end sub
the msgbox will show "Hello, word".
this way, we get a global variable without any declaration or assignment. it can be used in any Sub or Function.
we can define many named range in excel, and in VBA code we just use [] method to get the range value.
in fact, the [hello] is a abbreviation of the function Evaluate["Hell"], but it's more shorter.
It's been quite a while, but this may satisfy you :
Public MyVariable as Integer: MyVariable = 123
It's a bit ugly since you have to retype the variable name, but it's on one line.