MS Access: Assign variable for Form - vba

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

Related

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

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!

Defining variables on Excel VBA

Dears,
I'm starting a new project using Excel VBA and I would like to declare some variables to be loaded during the system's initialization, but during the utilization it may be changed. How can I do it?
Example:
When the system is loaded, the variable RADIO must be equal to "OFF", but during the utilization the user will change this value to "ON", and even to "OFF" again.
This information (RADIO) will be used during the system's utilization in some other pages.
How can I do it?
Thank you so much
Bruno Lelli
Like Tim Williams said, you need a global variable. Best practice is to have it declared in the declaration block (very first lines) of a code mudule.
To have it available within the code module only:
Private booRadio as Boolean
To have it available within the whole VBA project, i.e. all modules, user forms and for the workbook and worksheet events:
Public booRadio as Boolean
When VBA starts, all Boolean variables get initialized to be False (likewise, all Integer or Long get initialized to be 0, String to be "", etc.). This can be used to have a known initial status after system start.
Or - which is better to read in code reviews and like-I-feel more robust - you use an event to initilize the startup status. E.g. like ashleedawg said, you may use the workbook_open event for this. In that case, you need your variable dacelared with the Public statement, if you want to access it outside the ThisWorkbook code.
EDIT:
Copy this code into the ThisWorkbook code module:
Private Sub Workbook_Open()
booRadio = True
End Sub
This will use the event Workbook_Open to initialize the variable upon every opening of the Excel file, because that event gets automatially raised then.

VBA Excel add in -- Initialize global variables on load -- not in workbooks

I am creating an excel add-in that connects to functions and formulas set up on an API.
To use the API functions the user needs to log in and receive a Token.
The Token is a global variable that is referenced whenever calling an API function.
This add in will be used by many different workbooks so i'm trying to make it as easy as possible for the user to connect.
At the moment i have a function that connects and stores the details in global variable so they only have to log in once. But every time they close and re open they have to log back in by calling the function and passing in the details.
Is there a way of accessing initialize workbook from an add-in?
an on-load equivalent?
Or getting add-in Functions to run on start up? without the user having to set up macros or custom functions, they can just add in and auto connect with their details.
'Is their an equivalent of
Sub New() / Sub OnLoad()
Initalize()
End sub
I don't want the user to have to worry at all about whats going on behind they just have to load up the page with their username and password in defined cells or a function that references the username and password that even when the page is set to manual calculations it will recalculate on load.
Any help would be appreciated thanks.
Rather than storing the token as a global, store it as a defined name, which is persistent.
ThisWorkbook.Names.Add "Token", sToken
Then simply evaluate the name every time you need the token.
sToken = ThisWorkbook.Sheets(1).[Token]
If anyone was curious. I found that you can initialize the global variables with something similar to OnLoad().
Private WebAddress As String
Sub Auto_Open()
WebAddress = "www.stackoverflow.com"
End Sub
Can be in a module, and is called when the workbook opens.

Storing range reference in a global variable

I'm using several named ranges located in different worksheets. I need to read from and write to those ranges in many situations throughout my VBA code.
So my question is: what is the proper way to store those range references in global variables for quick access? Or is there a better way?
I tried declaring global variables:
Public WS_BOARD As Worksheet
Public RNG_BOARD As Range
and initializing them on Workbook_Open:
Private Sub Workbook_Open()
Set WS_BOARD = Worksheets("BOARD")
Set RNG_BOARD = WS_BOARD.Range("NR_BOARD")
End Sub
This works okay except that if my code crashes those global variables are reset to Nothing and cannot be used any further (i.e. in worksheet event handlers).
Of course, I can always use something like
Worksheets("BOARD").Range("NR_BOARD")
everywhere in my code but I think it will affect performance because it obviously needs to lookup objects using string names, let alone it being not DRY.
One way is to "load" them once into memory and then pass them as arguments into other subs/functions.
Passing Variables By Reference And By Value
Second, you can declare them as module-scope variables (something like public, but only in the module you declare them).
Scope of variables in Visual Basic for Applications
Third, your way.
Scope of variables in Visual Basic for Applications
Every worksheet has a Name property (the tab caption) and a CodeName property. If you use the CodeName in your code, you don't need a variable. Like a userform, a sheet's code name is auto instantiated when you use it. Select the worksheet under your project in the Project Explorer (Ctrl+R), go to the Propeties (F4) and change the (Name) to a meaninful CodeName. The Project Explorer will then show the sheet as
wshDataEntry (Data Entry)
if you made the CodeName wshDataEntry and the tab said Data Entry.
For Ranges, I used defined names in the spreadsheet. It's like a global variable that never goes out of scope. If you have, say, and interest rate cell that you need to read in various procedure, name it Interest_Rate. Then at the top of every procedure where you need it
Set rInterest = wshDataEntry.Range(gsNMINTEREST)
where gsNMINTEREST is a global string variable holding "Interest_Rate". If the cell ever moves, you only need to move your named range for the code to work. If you change the name to "LiborPlusFour" you only need to update your global variable. Pretty DRY.
Yes there is a slight performance hit making VBA look up the named range every time. I wouldn't worry about that unless you've calculated a performance problem and identified it as the drag.
One way that I keep global variables (of course I'm very judicious using them) in scope is an Initialize procedure.
Public Sub Initialize()
If gclsApp Is Nothing Then
Set gclsApp = New CApp
'do some other setup
End If
End Sub
Then I call the Initialize procedure at the top of any module I need it. If it's Not Nothing, then it doesn't reset the variable. I'm not sure the performance implications of checking for Nothing and just looking up a named range, though.
Global variables are generally not a good idea. In some cases they're necessary, but this isn't one of those cases.
I don't think you should worry about the performance impact of looking up a few ranges by their string name. I have sheets with hundreds of named ranges, and I have never, ever observed this having a negative impact on performance.
Do you have specific evidence indicating that this is slowing down your code? If not, then don't waste time worrying about it.
I often do this to define ranges:
Type RangesType
board As Range
plank As Range
wood As Range
End Type
Function GetRanges() As RangesType
With GetRanges
Set .board = Range("board")
Set .plank = Range("plank")
Set .wood = Range("wood")
End With
End Function
Then I use them like this:
Sub something()
Dim rngs As RangesType: rngs = GetRanges
rngs.board = "My favourite board"
rngs.wood = "Chestnut"
'etc.
End Sub
Yes, ranges will get fetched by their name repeatedly if you have many subs, and no, this isn't likely to have any noticeable effect on performance.

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.