473,385 Members | 1,727 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,385 software developers and data experts.

Many global vars in an A97 app - good or bad? Why?

MLH
I've read some posts indicating that having tons of GV's in
an Access app is a bad idea. Personally, I love GVs and I
use them (possibly abuse them) all the time for everything
imaginable - have been for years. If the machine has memory
to spare and windows can use it - I'm thinking "Why not?"

I was wondering what some of you have to say about that,
particularly any severe "gotchas" you've had the unfortunate
experience to contend with.
Nov 13 '05 #1
33 2981
On Tue, 08 Nov 2005 11:24:15 -0500, MLH <CR**@NorthState.net> wrote:
I've read some posts indicating that having tons of GV's in
an Access app is a bad idea. Personally, I love GVs and I
use them (possibly abuse them) all the time for everything
imaginable - have been for years. If the machine has memory
to spare and windows can use it - I'm thinking "Why not?"

I was wondering what some of you have to say about that,
particularly any severe "gotchas" you've had the unfortunate
experience to contend with.


The problem is that global variables tightly couple together code that does
different things at different times, and since any code can update the
variable at any time, you can never be sure it was left in the state you think
it was. Generally, it's best if you can arrange for each piece of code to
send data directly to the thing that will use it.

One abuse of globabl vairables I've seen frequently is to keep track of the
last active control. The trouble with this is, what happens when you toggle
the focus between forms? The right place for this variable is in the form's
module, where there is no confusion as to what form's state we're talking
about.
Nov 13 '05 #2
I use GV in all my VB6 programs. I take paper schematics and put them
into electronic format and put intelligence into them using CGM
intelligence graphics. I create a history list of an index selection
that is held by GV. I say use them if you need to but don't abuse them
if you dont have to.

Nov 13 '05 #3
MLH
What about these 3: GlobalString, MySQL and GlobalVariant. I use them
really for convenience more than anything. Anytime I want to concat a
bunch of crap like

MySQL = "SELECT tblNCcounties.County, tblNCcities.CityName
FROM (tblNCcities LEFT JOIN tblNCcityCountyJunctionTable "
MySQL = MySQL & "ON tblNCcities.CityID =
tblNCcityCountyJunctionTable.CityID) LEFT JOIN tblNCcounties ON "
MySQL = MySQL & "tblNCcityCountyJunctionTable.CountyID =
tblNCcounties.CountyID WHERE (((tblNCcities.CityName) "
MySQL = MySQL & "= GetMyString())) ORDER BY
tblNCcities.CityName;"

then assign a combo box its rowsource or do a RunSQL command, this
global var is a handier than a pocket on a shirt. I don't have to
dimension it or anything - its there when I need it, so I just use it.
Its a habit. Even though I think its value is assured after assigning
it, I do realize that in some cron-based event running somewhere
else, it might possibly be changed. I guess that, in itself, is reason
enough to abandon the practice. But it sure will be a hard habit to
break. Any more comments - anybody?
The problem is that global variables tightly couple together code that does
different things at different times, and since any code can update the
variable at any time, you can never be sure it was left in the state you think
it was. Generally, it's best if you can arrange for each piece of code to
send data directly to the thing that will use it.

One abuse of globabl vairables I've seen frequently is to keep track of the
last active control. The trouble with this is, what happens when you toggle
the focus between forms? The right place for this variable is in the form's
module, where there is no confusion as to what form's state we're talking
about.


Nov 13 '05 #4
MLH wrote:
Any more comments - anybody?


Some have suggested using a hidden form to hold global variables. BTW,
has anyone used a hidden bound form holding global variables as a
persistent connection to aid A2K3 in getting data?

James A. Fortune

Cmabrigde Uinervtisy rscjeearcj"

http://www.mrc-cbu.cam.ac.uk/~mattd/Cmabrigde/

Nov 13 '05 #5
MLH wrote:
I've read some posts indicating that having tons of GV's in
an Access app is a bad idea. Personally, I love GVs and I
use them (possibly abuse them) all the time for everything
imaginable - have been for years. If the machine has memory
to spare and windows can use it - I'm thinking "Why not?"

I was wondering what some of you have to say about that,
particularly any severe "gotchas" you've had the unfortunate
experience to contend with.


I should think that its A-OK as long as no code breaks and you go into
Debug mode. Then your vars get reset.
Nov 13 '05 #6
ji********@compumarc.com wrote:
has anyone used a hidden bound form holding global variables as a
persistent connection to aid A2K3 in getting data?


That idea didn't make sense. The table for holding the global
variables has to be local rather than linked and that would defeat the
purpose.

James A. Fortune

Nov 13 '05 #7
MLH
Valid point. It has been made in this NG before. But I've gotta go now
and clean up about 5-months worth of piss-poor DLookup statement
designs. !;Da&%*!!!

I should think that its A-OK as long as no code breaks and you go into
Debug mode. Then your vars get reset.


Nov 13 '05 #8
MLH <CR**@NorthState.net> wrote in
news:uv********************************@4ax.com:
I've read some posts indicating that having tons of GV's in
an Access app is a bad idea. . . .
Yes. It's a terrible, terrible practice.
. . . Personally, I love GVs and I
use them (possibly abuse them) all the time for everything
imaginable - have been for years. If the machine has memory
to spare and windows can use it - I'm thinking "Why not?"
Because they represent poor design of your application.
I was wondering what some of you have to say about that,
particularly any severe "gotchas" you've had the unfortunate
experience to contend with.


1. Global variables should *never* be used for merely passing data
between different forms.

2. Global variables should never be used for data that is volatile
and changes during a data session. Most static global variables
should be global constants, and the only data that should be in
global variables is non-permanent values that are looked up and set
at runtime on the opening of the application.

Now, it is sometimes the case that it's important to have data
stored in memory that is used by more than one form, but for this I
always use class modules as data storage structures. A common case
of this is report filtering. I use a dialog form for collecting the
filter ctieria and store the criteria in a class module. The report
then talks to the class module to figure out what filter it should
use.

Since the reports and forms are all using a single class module as
an intermediary, this means that multiple reports can use the same
class module, and it can have as many individual instances as
necessary (something that's not possible with global variables),
and
it also means the the report doesn't need to know anything about
the
filter dialog form, nor the form anything about the report. Both
need know only about the class module that is used to store the
data.

Indeed, in general, I make the dialog forms completely stupid, and
put all the code that pulls data out of them in the subroutine that
opens the dialog. This makes it possible to use a single dialog
form
in multiple contexts without the form needing to know anything at
all about those contexts.

So, something like this (where the close button of the dialog
doesn't actually close the form, just make it invisible, and the
Cancel button sets the form's .Tag property to "Cancel":

' this line needs to be in a public module
Global clsReportCriteria As New clReportCriteria

' this code is wherever you're printing your filtered report
DoCmd.OpenForm "dlgReportCriteria", , , , , acDialog
If Forms!dlgReportCriteria.Tag <> "Cancel" Then
With Forms!dlgReportCriteria
clsReportCriteria.Criterion1 = !txtCriterion1
clsReportCriteria.Criterion2 = !txtCriterion2
End With
DoCmd.OpenReport "MyReport"
End If
DoCmd.Close acform, "dlgReportCriteria"

In the OnOpen event of MyReport, you'd check to see
clsReportCriteria has any values set and use those for filtering
the
report's recordset.

In the report's OnClose event, you'd have this:

Set clsReportCriteria = Nothing

Now, you will note that I"ve replaced a collectin of Global
variables with a Global instance of a Class Module, so you might
say
that I'm just doing a more complex version of the same thing.

But the added complexity gives you full control over the scope of
the values you store in the globally available variables.

And that's the chief problem with bare Global variables -- they are
too easy to poke new values into that could break other code that
depends on values having been put in them. With multiple class
instances, you can control exactly which instances of the variables
you want to use, and you can easily set them back to nothing.

For globals that you set at application initiation from some form
of
stored value, it's better, in my opinion, to replace them with
self-healing functions that use Static variables insternally. Say
you have an application name that you use in a lot of report or
form
headers. You could have a Global like this:

Global strAppName As String

and in your initialization routine assign a value to it like this:

strAppName=DLookup("KeyValue", "tblSettings", "[Key]='AppName'")

Now, I"d replace the global variable with a function:

Public Function strAppName () As String
Static strApplicationName As String

If Len(strApplicationName) = - Then
strApplicationName = DLookup("KeyValue", _
"tblSettings", "[Key]='AppName'")
End If
strAppName = strApplicationName
End Function

This gives you 3 advantages:

1. you don't need global variables for it, since the wrapper
function keeps the static variable hidden to itself.

2. oif converting from global variables to functions like this, you
don't have to change code that uses the global variable, if you
make
the function name the same as the former globabl variable.

3. you no longer have to initialize the values for these global
variables, as the function is self-healing -- it fhe value hasn't
been set at the first call to the function, it is looked up and set
then. This means the first time your app uses it in a session, as
well as after code resets during the development stage.

So, basically the only Globals I have in any app are globals for
data structures that are used in multiple places. This would mostly
be arrays and class modules.

Of course, I also have a lot of applications in production use that
date from the time when I didn't know any of these techniques, so I
do have to deal with global variables all the time. But it is a
real
headache and I try to engineer them out of the way weenever
practical.

--
David W. Fenton http://www.bway.net/~dfenton
dfenton at bway dot net http://www.bway.net/~dfassoc
Nov 13 '05 #9
MLH <CR**@NorthState.net> wrote in
news:19********************************@4ax.com:
What about these 3: GlobalString, MySQL and GlobalVariant. I use
them really for convenience more than anything. Anytime I want to
concat a bunch of crap like

MySQL = "SELECT tblNCcounties.County,
tblNCcities.CityName
FROM (tblNCcities LEFT JOIN tblNCcityCountyJunctionTable "
MySQL = MySQL & "ON tblNCcities.CityID =
tblNCcityCountyJunctionTable.CityID) LEFT JOIN tblNCcounties ON "
MySQL = MySQL & "tblNCcityCountyJunctionTable.CountyID =
tblNCcounties.CountyID WHERE (((tblNCcities.CityName) "
MySQL = MySQL & "= GetMyString())) ORDER BY
tblNCcities.CityName;"

then assign a combo box its rowsource or do a RunSQL command, this global var is a handier than a pocket on a shirt. I don't have to
dimension it or anything - its there when I need it, so I just use it. Its a habit. Even though I think its value is assured after
assigning it, I do realize that in some cron-based event running
somewhere else, it might possibly be changed. I guess that, in
itself, is reason enough to abandon the practice. But it sure will be a hard habit to break. Any more comments - anybody?


Been there, done that, and found that even though I thought I was
being careful to assign the value each time I used it, I ended up
with legacy values from other contexts.

This is the absolute *worst* tyhpe of usage of a glabal variable,
because the problems that can result from a breakdown are so hard
to
troubleshoot and will be dependent on the order of events that
happened before you used it.

This is the first kind of global variable that you should
eliminate.

--
David W. Fenton http://www.bway.net/~dfenton
dfenton at bway dot net http://www.bway.net/~dfassoc
Nov 13 '05 #10
MLH
OK, having said that, what would be some of the better uses
to which GV's could be put? I gather there will be very few in
the list.

xxxxxxxxxxxxxxxxxxxxxxxxxxxx

Been there, done that, and found that even though I thought I was
being careful to assign the value each time I used it, I ended up
with legacy values from other contexts.

This is the absolute *worst* tyhpe of usage of a glabal variable,
because the problems that can result from a breakdown are so hard
to
troubleshoot and will be dependent on the order of events that
happened before you used it.

This is the first kind of global variable that you should
eliminate.


Nov 13 '05 #11
MLH
David, that's a masterful answer and I for one really do
appreciate the time and effort you've taken to share it
conceptually and practically. On first pass reading, I can
certainly tell you HAVE been there done that and that
your conclusions were not arrived at without the pain
of personal experience.

God I hate to pick up 'n go a comletely different direction,
but I do see the wisdom. Let me play this thing out a bit
at a time, working toward the model you described. Are
there any particularly useful examples in the Northwind
database you would care to mention that demonstrate
how to lay the groundwork for these objectives?
Nov 13 '05 #12
MLH <CR**@NorthState.net> wrote in
news:5f********************************@4ax.com:
Are
there any particularly useful examples in the Northwind
database you would care to mention that demonstrate
how to lay the groundwork for these objectives?


In general, if you see it done in the Northwind Database, that would
be how you should *NO** do it. Same with the other sample databases
and the database wizards -- they are prime examples of terrible
practices in designing Access apps.

--
David W. Fenton http://www.bway.net/~dfenton
dfenton at bway dot net http://www.bway.net/~dfassoc
Nov 13 '05 #13
MLH <CR**@NorthState.net> wrote in
news:8q********************************@4ax.com:
OK, having said that, what would be some of the better uses
to which GV's could be put? I gather there will be very few in
the list.


Mostly, not at all.

A global variable should be used where a global variable is
*REQUIRED*, where there is no alterantive. Those circumstances are
pretty few and far between.

Global variables are decidely poorly suited to these two purposes,
if you want to avoid spaghetti code apps:

1. passing data between contexts.

2. avoiding repeatedly declaring a variable that you re-use often.

Variables should be declared according to their appropriate scope
and that scope should be as narrow as possible. If the value you're
assigning to strSQL is used only in the current subroutine, then
that variable should be declared only in the current subroutine.

If you're using global variables to pass data between forms, then
you should rewrite your code to pass the data directly between the
forms, or replace your collection of global variables with a class
module that can be instantiated in a context appropriate to how it
is used. This could be a global class module variable, or it could
class module declared as a public member of one of the form's
modules.

--
David W. Fenton http://www.bway.net/~dfenton
dfenton at bway dot net http://www.bway.net/~dfassoc
Nov 13 '05 #14
On Tue, 08 Nov 2005 22:06:53 -0600, "David W. Fenton"
<dX********@bway.net.invalid> wrote:

Amen to most, especially the past about appropriate scope. Don't
compromise on that, or it'll bite you.

Passing data between forms: why do so few people know about the
OpenArgs argument to DoCmd.OpenForm? I use it in pretty much every
serious application (a subset of what I write :-)) and I often use a
querystring-like string to pass multiple optional arguments. Not
trying to say DF doesn't know about this, but too many people I'm
interviewing for a position at KIT don't know about it.

-Tom.

MLH <CR**@NorthState.net> wrote in
news:8q********************************@4ax.com :
OK, having said that, what would be some of the better uses
to which GV's could be put? I gather there will be very few in
the list.


Mostly, not at all.

A global variable should be used where a global variable is
*REQUIRED*, where there is no alterantive. Those circumstances are
pretty few and far between.

Global variables are decidely poorly suited to these two purposes,
if you want to avoid spaghetti code apps:

1. passing data between contexts.

2. avoiding repeatedly declaring a variable that you re-use often.

Variables should be declared according to their appropriate scope
and that scope should be as narrow as possible. If the value you're
assigning to strSQL is used only in the current subroutine, then
that variable should be declared only in the current subroutine.

If you're using global variables to pass data between forms, then
you should rewrite your code to pass the data directly between the
forms, or replace your collection of global variables with a class
module that can be instantiated in a context appropriate to how it
is used. This could be a global class module variable, or it could
class module declared as a public member of one of the form's
modules.


Nov 13 '05 #15
On Tue, 08 Nov 2005 15:59:37 -0600, "David W. Fenton"
<dX********@bway.net.invalid> wrote:

Nice reply.
One thing I do slightly differently is that rather than returning a
value through the Tag property, I use a public property. This is what
I wrote today:
Public Function AllStonesOrStockOnly() As String
DoCmd.OpenForm "frmAllStonesOrStockOnly", , , , , acDialog
AllStonesOrStockOnly = Form_frmAllStonesOrStockOnly.ButtonSelected
DoCmd.Close acForm, "frmAllStonesOrStockOnly"
End Function

So here we're opening a modal form not unlike a MsgBox form to get one
of two values. When the user clicks a button, we set the variable
returned by the global property, set Form.Visible = False to fall out
of the modal loop, check with the public property "ButtonSelected" to
see which button was clicked, and proceed to clean up.

-Tom.
MLH <CR**@NorthState.net> wrote in
news:uv********************************@4ax.com :
I've read some posts indicating that having tons of GV's in
an Access app is a bad idea. . . .


Yes. It's a terrible, terrible practice.
. . . Personally, I love GVs and I
use them (possibly abuse them) all the time for everything
imaginable - have been for years. If the machine has memory
to spare and windows can use it - I'm thinking "Why not?"


Because they represent poor design of your application.
I was wondering what some of you have to say about that,
particularly any severe "gotchas" you've had the unfortunate
experience to contend with.


1. Global variables should *never* be used for merely passing data
between different forms.

2. Global variables should never be used for data that is volatile
and changes during a data session. Most static global variables
should be global constants, and the only data that should be in
global variables is non-permanent values that are looked up and set
at runtime on the opening of the application.

Now, it is sometimes the case that it's important to have data
stored in memory that is used by more than one form, but for this I
always use class modules as data storage structures. A common case
of this is report filtering. I use a dialog form for collecting the
filter ctieria and store the criteria in a class module. The report
then talks to the class module to figure out what filter it should
use.

Since the reports and forms are all using a single class module as
an intermediary, this means that multiple reports can use the same
class module, and it can have as many individual instances as
necessary (something that's not possible with global variables),
and
it also means the the report doesn't need to know anything about
the
filter dialog form, nor the form anything about the report. Both
need know only about the class module that is used to store the
data.

Indeed, in general, I make the dialog forms completely stupid, and
put all the code that pulls data out of them in the subroutine that
opens the dialog. This makes it possible to use a single dialog
form
in multiple contexts without the form needing to know anything at
all about those contexts.

So, something like this (where the close button of the dialog
doesn't actually close the form, just make it invisible, and the
Cancel button sets the form's .Tag property to "Cancel":

' this line needs to be in a public module
Global clsReportCriteria As New clReportCriteria

' this code is wherever you're printing your filtered report
DoCmd.OpenForm "dlgReportCriteria", , , , , acDialog
If Forms!dlgReportCriteria.Tag <> "Cancel" Then
With Forms!dlgReportCriteria
clsReportCriteria.Criterion1 = !txtCriterion1
clsReportCriteria.Criterion2 = !txtCriterion2
End With
DoCmd.OpenReport "MyReport"
End If
DoCmd.Close acform, "dlgReportCriteria"

In the OnOpen event of MyReport, you'd check to see
clsReportCriteria has any values set and use those for filtering
the
report's recordset.

In the report's OnClose event, you'd have this:

Set clsReportCriteria = Nothing

Now, you will note that I"ve replaced a collectin of Global
variables with a Global instance of a Class Module, so you might
say
that I'm just doing a more complex version of the same thing.

But the added complexity gives you full control over the scope of
the values you store in the globally available variables.

And that's the chief problem with bare Global variables -- they are
too easy to poke new values into that could break other code that
depends on values having been put in them. With multiple class
instances, you can control exactly which instances of the variables
you want to use, and you can easily set them back to nothing.

For globals that you set at application initiation from some form
of
stored value, it's better, in my opinion, to replace them with
self-healing functions that use Static variables insternally. Say
you have an application name that you use in a lot of report or
form
headers. You could have a Global like this:

Global strAppName As String

and in your initialization routine assign a value to it like this:

strAppName=DLookup("KeyValue", "tblSettings", "[Key]='AppName'")

Now, I"d replace the global variable with a function:

Public Function strAppName () As String
Static strApplicationName As String

If Len(strApplicationName) = - Then
strApplicationName = DLookup("KeyValue", _
"tblSettings", "[Key]='AppName'")
End If
strAppName = strApplicationName
End Function

This gives you 3 advantages:

1. you don't need global variables for it, since the wrapper
function keeps the static variable hidden to itself.

2. oif converting from global variables to functions like this, you
don't have to change code that uses the global variable, if you
make
the function name the same as the former globabl variable.

3. you no longer have to initialize the values for these global
variables, as the function is self-healing -- it fhe value hasn't
been set at the first call to the function, it is looked up and set
then. This means the first time your app uses it in a session, as
well as after code resets during the development stage.

So, basically the only Globals I have in any app are globals for
data structures that are used in multiple places. This would mostly
be arrays and class modules.

Of course, I also have a lot of applications in production use that
date from the time when I didn't know any of these techniques, so I
do have to deal with global variables all the time. But it is a
real
headache and I try to engineer them out of the way weenever
practical.


Nov 13 '05 #16
On Tue, 08 Nov 2005 15:59:37 -0600, "David W. Fenton"
<dX********@bway.net.invalid> wrote:
MLH <CR**@NorthState.net> wrote in
news:uv********************************@4ax.com :
I've read some posts indicating that having tons of GV's in
an Access app is a bad idea. . . .


Yes. It's a terrible, terrible practice.
. . . Personally, I love GVs and I
use them (possibly abuse them) all the time for everything
imaginable - have been for years. If the machine has memory
to spare and windows can use it - I'm thinking "Why not?"


Because they represent poor design of your application.
I was wondering what some of you have to say about that,
particularly any severe "gotchas" you've had the unfortunate
experience to contend with.


1. Global variables should *never* be used for merely passing data
between different forms.


Actually, I do use global storage for that in some cases, but I do it in a
very specific way to ensure safety.

1. The variable is not global, it's private and accessed indirectly through
public functions in the same module.

2. The variable is a collection, and items are stored in the collection using
the target object's name as a key to avoid collisions. Items in the
collection are object instances, often custom class objects or are themselves
collections of data items.

3. The first thing the target object does on start-up is retrieve its data
object, and remove it from shared storage.
Nov 13 '05 #17
On Tue, 08 Nov 2005 22:05:18 -0700, Tom van Stiphout <no*************@cox.net>
wrote:
On Tue, 08 Nov 2005 22:06:53 -0600, "David W. Fenton"
<dX********@bway.net.invalid> wrote:

Amen to most, especially the past about appropriate scope. Don't
compromise on that, or it'll bite you.

Passing data between forms: why do so few people know about the
OpenArgs argument to DoCmd.OpenForm? I use it in pretty much every
serious application (a subset of what I write :-)) and I often use a
querystring-like string to pass multiple optional arguments. Not
trying to say DF doesn't know about this, but too many people I'm
interviewing for a position at KIT don't know about it.

-Tom.

MLH <CR**@NorthState.net> wrote in
news:8q********************************@4ax.co m:
OK, having said that, what would be some of the better uses
to which GV's could be put? I gather there will be very few in
the list.


Mostly, not at all.

A global variable should be used where a global variable is
*REQUIRED*, where there is no alterantive. Those circumstances are
pretty few and far between.

Global variables are decidely poorly suited to these two purposes,
if you want to avoid spaghetti code apps:

1. passing data between contexts.

2. avoiding repeatedly declaring a variable that you re-use often.

Variables should be declared according to their appropriate scope
and that scope should be as narrow as possible. If the value you're
assigning to strSQL is used only in the current subroutine, then
that variable should be declared only in the current subroutine.

If you're using global variables to pass data between forms, then
you should rewrite your code to pass the data directly between the
forms, or replace your collection of global variables with a class
module that can be instantiated in a context appropriate to how it
is used. This could be a global class module variable, or it could
class module declared as a public member of one of the form's
modules.


First - to be clear, I'm agreeing with everything we're saying here, but there
are some problems that sometimes have to be worked around when passing
arguments to forms and reports.

1. In Access prior to 2002, there was no OpenArgs for reports.
2. OpenArgs is a single, string-type argument. Sometimes, you need to pass
more, and building/parsing compound argument strings can be a real mess.

When these issues come up, I usually try first to just write the arguments to
the form after it has been opened, but that often doesn't turn out to be
practical either.

Here's what I usually end up with...

I have a module with a private collection variable and at least 2 public
functions, one to add/replace an item, and one to retrieve/remove an item. In
both cases, a key string is provided.

Now, when I want to pass multiple parameters to a form or report on open, I
add an item (usually a collection of values, sometimes an instance of a custom
class), and use the name of the target obect as the key. When the target
object opens, the first thing it does is retrieves/remove its data item.

In this case, I am using a shared variable to pass data, but I'm taking the
precautions of using the target object as a key to prevent collisions, and
removing the item from shared storage immediately upon retrieval so there's no
meaningful opportunity for side effects.
Nov 13 '05 #18
On Tue, 08 Nov 2005 22:00:44 -0600, "David W. Fenton"
<dX********@bway.net.invalid> wrote:
MLH <CR**@NorthState.net> wrote in
news:5f********************************@4ax.com :
Are
there any particularly useful examples in the Northwind
database you would care to mention that demonstrate
how to lay the groundwork for these objectives?


In general, if you see it done in the Northwind Database, that would
be how you should *NO** do it. Same with the other sample databases
and the database wizards -- they are prime examples of terrible
practices in designing Access apps.


And don't even get me started on error handling in wizard-generated code...
I know - we'll trap all errors, report them to the user, then return to the
calling procedure as if nothing went wrong, letting it proceed to the next
step, which will now happily mangle your data.
Nov 13 '05 #19
On Tue, 08 Nov 2005 21:36:07 -0800, Steve Jorgensen <no****@nospam.nospam>
wrote:
On Tue, 08 Nov 2005 15:59:37 -0600, "David W. Fenton"
<dX********@bway.net.invalid> wrote:
MLH <CR**@NorthState.net> wrote in
news:uv********************************@4ax.co m:
I've read some posts indicating that having tons of GV's in
an Access app is a bad idea. . . .


Yes. It's a terrible, terrible practice.
. . . Personally, I love GVs and I
use them (possibly abuse them) all the time for everything
imaginable - have been for years. If the machine has memory
to spare and windows can use it - I'm thinking "Why not?"


Because they represent poor design of your application.
I was wondering what some of you have to say about that,
particularly any severe "gotchas" you've had the unfortunate
experience to contend with.


1. Global variables should *never* be used for merely passing data
between different forms.


Actually, I do use global storage for that in some cases, but I do it in a
very specific way to ensure safety.

1. The variable is not global, it's private and accessed indirectly through
public functions in the same module.

2. The variable is a collection, and items are stored in the collection using
the target object's name as a key to avoid collisions. Items in the
collection are object instances, often custom class objects or are themselves
collections of data items.

3. The first thing the target object does on start-up is retrieve its data
object, and remove it from shared storage.


- oops I see I repeated myself. I thought I had not posted this one when I
posted the other one like it.
Nov 13 '05 #20
On Tue, 08 Nov 2005 22:43:55 -0800, Steve Jorgensen <no****@nospam.nospam>
wrote:

....
First - to be clear, I'm agreeing with everything we're saying here, but there
are some problems that sometimes have to be worked around when passing
arguments to forms and reports.

....

- oops I see I repeated myself. Sorry about that.
Nov 13 '05 #21
Tom van Stiphout <no*************@cox.net> wrote in
news:sg********************************@4ax.com:
Passing data between forms: why do so few people know about the
OpenArgs argument to DoCmd.OpenForm? I use it in pretty much
every serious application (a subset of what I write :-)) and I
often use a querystring-like string to pass multiple optional
arguments. Not trying to say DF doesn't know about this, but too
many people I'm interviewing for a position at KIT don't know
about it.


Openargs works fine for a single value, but if you need to pass
multple values, you then have to enforce some kind of format on
that. That causes massive problems, in my opinion, since there is no
enforcement for it, and troubleshooting bad values passed to it can
be extremely difficult.

That's why when I'm passing more than one argument, I tend to store
the data in a structure outside the objects on either end of the
communication. That allows me to impose whatever structure on it I
want, and still keep the objects simple and independent of that
structure.

If you pass the values through OpenArgs, your form has to know how
to parse it. I just think that's a bad way to do it.

I use OpenArgs for only one thing -- controlling the "mode" of a
form. For instance, in my Reconnect utility (that works for
reconnecting to multiple back ends), the dialog form is normally
opened with no opening argument. In it's OnLoad, it sets itself
invisible and checsk to see if all the tablelinks are valid. If they
are, it writes a message to the Debug window and then closes.

But it takes on opening argument "Override" that allows it to stay
open even when there are no tables to be relinked.

That is the only kind of opening argument I ever use.

--
David W. Fenton http://www.bway.net/~dfenton
dfenton at bway dot net http://www.bway.net/~dfassoc
Nov 13 '05 #22
Steve Jorgensen <no****@nospam.nospam> wrote in
news:35********************************@4ax.com:
I have a module with a private collection variable and at least 2
public functions, one to add/replace an item, and one to
retrieve/remove an item. In both cases, a key string is provided.

Now, when I want to pass multiple parameters to a form or report
on open, I add an item (usually a collection of values, sometimes
an instance of a custom class), and use the name of the target
obect as the key. When the target object opens, the first thing
it does is retrieves/remove its data item.

In this case, I am using a shared variable to pass data, but I'm
taking the precautions of using the target object as a key to
prevent collisions, and removing the item from shared storage
immediately upon retrieval so there's no meaningful opportunity
for side effects.


If you're passing data back to the calling context, would you create
a new key and delete the incoming item, or change the value of the
existing item?

This is actually an interesting idea, but I prefer the class module
approach because it provides a lot more flexibility. I almost always
need to generate something that is the result of multiple variables,
such as a SQL WHERE clause. I think it's much easeir to put that in
your class module than it is to construct it in each context where
i'ts needed. With a class module, you set your properties and then
can just get the value of a WHERE property (which has code to
generate it from the values that have been set).

--
David W. Fenton http://www.bway.net/~dfenton
dfenton at bway dot net http://www.bway.net/~dfassoc
Nov 13 '05 #23
On Wed, 09 Nov 2005 22:34:07 -0600, "David W. Fenton"
<dX********@bway.net.invalid> wrote:
Steve Jorgensen <no****@nospam.nospam> wrote in
news:35********************************@4ax.com :
I have a module with a private collection variable and at least 2
public functions, one to add/replace an item, and one to
retrieve/remove an item. In both cases, a key string is provided.

Now, when I want to pass multiple parameters to a form or report
on open, I add an item (usually a collection of values, sometimes
an instance of a custom class), and use the name of the target
obect as the key. When the target object opens, the first thing
it does is retrieves/remove its data item.

In this case, I am using a shared variable to pass data, but I'm
taking the precautions of using the target object as a key to
prevent collisions, and removing the item from shared storage
immediately upon retrieval so there's no meaningful opportunity
for side effects.
If you're passing data back to the calling context, would you create
a new key and delete the incoming item, or change the value of the
existing item?


Generally, I have the calling object hold on to the object, and use the same
object to send data back, but it will no longer be in the collection by then.
This way, the called object doesn't need to know anything about the calling
object to send data back to it.

1. Calling object creates parameter object and holds a reference
2. Calling object adds paramter object to "global" collection
3. Calling object opens called object
4. Called object gets parameter object from collection, and deletes it from
the collection.

At this point, both calling and called objects reference the paramter object,
and the object is not in the global collection.

5. Called object writes back to parameter object.
6. Calling object gets data from called object by reading from parameter
object.

A variation on #6 is - the parameter object raises an event to tell the
calling object there is updated data it should respond to.
This is actually an interesting idea, but I prefer the class module
approach because it provides a lot more flexibility. I almost always
need to generate something that is the result of multiple variables,
such as a SQL WHERE clause. I think it's much easeir to put that in
your class module than it is to construct it in each context where
i'ts needed. With a class module, you set your properties and then
can just get the value of a WHERE property (which has code to
generate it from the values that have been set).


Perhaps, I'm not getting what you're saying. The technique I'm using
frequently does pass an instance of a class module to get the parameters from
the calling object to the called object and back again. I just couldn't see
how to get the paramter object to the called object except to put it in some
kind of global storage as an intermediate step.

What does your class module approach look like?
Nov 13 '05 #24

Over the years I've used UDTs and class modules (as a "smart" UDT) but find
that the easiest way to control communication between forms is to use
interfaces.

Using an interface means you tightly couple the data you are passing to the
form instance, also it allows you to create callback interfaces to
facilitate communication back to the calling form.

Simple example:-
================
A class module called iForm4
------------------------------------
Public Sub Show(Optional Modal As Boolean = False)
'
End Sub

Property Let Visible(RHS As Boolean)
'
End Property

Property Get Visible() As Boolean
'
End Property

Property Get SQL() As String
'
End Property

A form called Form4 with a command button on it
------------------------------------------------------
Option Explicit

' This couples the form instance to the interface
Implements iForm4

Private Sub Command0_Click()
Me.Visible = False
End Sub

Private Sub iForm4_Show(Optional Modal As Boolean = False)
With Me
.Visible = True
.Modal = Modal
End With
End Sub

Private Property Get iForm4_SQL() As String
iForm4_SQL = "A SQL statement"
End Property

Private Property Get iForm4_Visible() As Boolean
iForm4_Visible = Me.Visible
End Property

Private Property Let iForm4_Visible(RHS As Boolean)
Me.Visible = RHS
End Property

A test function
-----------------
Function showit()
Dim f As Form_Form4
Dim i As iForm4

Set f = New Form_Form4

If TypeOf f Is iForm4 Then
Set i = f
' Note how once the casting is carried out
' the intellisense list becomes restricted to
' the methods available through the interface
' which makes for easier and more reliable
' programming.
With i
.Show 1
Do While .Visible
DoEvents
Loop
MsgBox .SQL
End With

Set i = Nothing
End If

Set f = Nothing

End Function


--
Terry Kreft

"David W. Fenton" <dX********@bway.net.invalid> wrote in message
news:Xn**********************************@216.196. 97.142...
Steve Jorgensen <no****@nospam.nospam> wrote in
news:35********************************@4ax.com:
I have a module with a private collection variable and at least 2
public functions, one to add/replace an item, and one to
retrieve/remove an item. In both cases, a key string is provided.

Now, when I want to pass multiple parameters to a form or report
on open, I add an item (usually a collection of values, sometimes
an instance of a custom class), and use the name of the target
obect as the key. When the target object opens, the first thing
it does is retrieves/remove its data item.

In this case, I am using a shared variable to pass data, but I'm
taking the precautions of using the target object as a key to
prevent collisions, and removing the item from shared storage
immediately upon retrieval so there's no meaningful opportunity
for side effects.


If you're passing data back to the calling context, would you create
a new key and delete the incoming item, or change the value of the
existing item?

This is actually an interesting idea, but I prefer the class module
approach because it provides a lot more flexibility. I almost always
need to generate something that is the result of multiple variables,
such as a SQL WHERE clause. I think it's much easeir to put that in
your class module than it is to construct it in each context where
i'ts needed. With a class module, you set your properties and then
can just get the value of a WHERE property (which has code to
generate it from the values that have been set).

--
David W. Fenton http://www.bway.net/~dfenton
dfenton at bway dot net http://www.bway.net/~dfassoc

Nov 13 '05 #25
Steve Jorgensen <no****@nospam.nospam> wrote in
news:da********************************@4ax.com:
Perhaps, I'm not getting what you're saying. The technique I'm
using frequently does pass an instance of a class module to get
the parameters from the calling object to the called object and
back again. I just couldn't see how to get the paramter object to
the called object except to put it in some kind of global storage
as an intermediate step.
I thought you were talking about a custom collection instead of a
class module.
What does your class module approach look like?


Of course it uses a global variable, or (very occasioally) a public
member of a form (I have done that with a Query-By-Form interface).

But my "rules" for global variables explicitly pointed out that
objects like arrays and classes were appropriate uses of globals.

--
David W. Fenton http://www.bway.net/~dfenton
dfenton at bway dot net http://www.bway.net/~dfassoc
Nov 13 '05 #26
I use global variables all the time, like you, probably more than I
should. I use them for a few reasons, but not for the ones that they
seem to be going on about here.

1) I use them to get, and hold information from the user, typically
the start, and stop time of an event, (startDate and stopDate) so that
I don't have to prompt ther user over and over and OVER again for the
same piece(s) of information. Somtimes it will be an employee clock
number if we're researching that, or somethign else, maybe a part
number if I'm going to use that in a report.

2) I use them as a way to pass information to queries- almost always,
when I create a global variable - I'll usually label them glStart and
glStop for instance, or glClockno, or glPartno, I write an accompanying
"return" function. The function can be used as criteria in a the
regular QBE grid.

global glStart, glStop as date
----------------------------------------------------
function glStartReturn()
glStartReturn = glStart
end function
etc..
etc..

These are useful, because you can put the start and stop time (or other
info) into the the GV one time, and reference it multiple times in the
procedure, even if you close the form you were working with, switch
back and forth between forms, or whatnot. You're covered in all
instances.

I have never seen any program degredation because of this, but then
again, my programs are more "compartmentalized" so one program does one
function - so the programs never get that large.

That's my two cents. Good luck on this! Looks like You've stired the
caldrun up pretty good!

-BrianDP

Nov 13 '05 #27
I use global variables all the time, like you, probably more than I
should. I use them for a few reasons, but not for the ones that they
seem to be going on about here.

1) I use them to get, and hold information from the user, typically
the start, and stop time of an event, (startDate and stopDate) so that
I don't have to prompt ther user over and over and OVER again for the
same piece(s) of information. Somtimes it will be an employee clock
number if we're researching that, or somethign else, maybe a part
number if I'm going to use that in a report.

2) I use them as a way to pass information to queries- almost always,
when I create a global variable - I'll usually label them glStart and
glStop for instance, or glClockno, or glPartno, I write an accompanying
"return" function. The function can be used as criteria in a the
regular QBE grid.

global glStart, glStop as date
----------------------------------------------------
function glStartReturn()
glStartReturn = glStart
end function
etc..
etc..

These are useful, because you can put the start and stop time (or other
info) into the the GV one time, and reference it multiple times in the
procedure, even if you close the form you were working with, switch
back and forth between forms, or whatnot. You're covered in all
instances.

I have never seen any program degredation because of this, but then
again, my programs are more "compartmentalized" so one program does one
function - so the programs never get that large.

That's my two cents. Good luck on this! Looks like You've stired the
caldrun up pretty good!

-BrianDP

Nov 13 '05 #28
I use global variables all the time, like you, probably more than I
should. I use them for a few reasons, but not for the ones that they
seem to be going on about here.

1) I use them to get, and hold information from the user, typically
the start, and stop time of an event, (startDate and stopDate) so that
I don't have to prompt the user over and over and OVER again for the
same piece(s) of information. Sometimes it will be an employee clock
number if we're researching that, or something else, maybe a part
number if I'm going to use that in a report.

2) I use them as a way to pass information to queries- almost always,
when I create a global variable - I'll usually label them glStart and
glStop for instance, or glClockno, or glPartno, I write an accompanying

"return" function. The best part about this is that the function can
be
used as criteria in the regular QBE grid.

<In a public module...>
global glStart, glStop as date
----------------------------------------------------
function glStartReturn()
glStartReturn = glStart
end function
-----------------------------------------------------
function glStopReturn()
glStopReturn = glStop
end function

These are useful, because you can put the start and stop time (or other

info) into the GV one time, and reference it multiple times in the
procedure, from a report, from forms, or wherever you need to!
Even if you close the form you were working with, switch
back and forth between other forms, or whatnot. You still have
that data in all cases. It can be tricky if the code is interrupted,
sometimes the GV will lose the value, but most of the time, if the
code is not stopped, Mr. GV retains the value and runs and returns when
you ask it to.

I have never seen any speed degradation because of this, but then
again, my programs are more "compartmentalized" so one program does one
function - so the programs never get that large. That's my two cents.
Good luck on this!

-BrianDP

Nov 13 '05 #29
On Thu, 10 Nov 2005 15:16:28 -0600, "David W. Fenton"
<dX********@bway.net.invalid> wrote:
Steve Jorgensen <no****@nospam.nospam> wrote in
news:da********************************@4ax.com :
Perhaps, I'm not getting what you're saying. The technique I'm
using frequently does pass an instance of a class module to get
the parameters from the calling object to the called object and
back again. I just couldn't see how to get the paramter object to
the called object except to put it in some kind of global storage
as an intermediate step.


I thought you were talking about a custom collection instead of a
class module.


I use a mix-and-match approach. My argument passing scheme takes any kinda'
object, so I start out by using a collection, then switch to a custom object
when the requirement evolves to where that's a benefit. Actually, I'll more
often use my custom dictionary class than a raw collection, but that's
basically a collection for purposes of this discussion.

If the number of parameters gets above about 3 or if there starts to be
business logic that has more to do with the transaction than either the
calling or called object, that's when I replace the collection with a custom
class.
What does your class module approach look like?


Of course it uses a global variable, or (very occasioally) a public
member of a form (I have done that with a Query-By-Form interface).

But my "rules" for global variables explicitly pointed out that
objects like arrays and classes were appropriate uses of globals.


OK, I see where you're coming from. I had thought you were talking about an
alternative to using global variables to pass the objects.
Nov 13 '05 #30
Steve Jorgensen <no****@nospam.nospam> wrote in
news:18********************************@4ax.com:
On Thu, 10 Nov 2005 15:16:28 -0600, "David W. Fenton"
<dX********@bway.net.invalid> wrote:


[]
But my "rules" for global variables explicitly pointed out that
objects like arrays and classes were appropriate uses of globals.


OK, I see where you're coming from. I had thought you were
talking about an alternative to using global variables to pass the
objects.


Well, I *am* talking about an alternative to the usual mishmash of
global variables that beginning Access programmers often use. It
collects a group of variables and stores them in a data structure
that is itself stored in a global variable. But the organization
that comes from using an array or a collection or a class module
means that you are setting and using the values as a group, and
inthe case of class modules, you can instantiate mulsiple instances
in order to keep values segregated, while not needing to multiply
the number of global variables defined (though you may need to
define a global variable for each instance, but one alternative to
that is to store your class instances in a custom collection).

You could also make these object variables non-global, such as
defining them as a public member of a form. That's something I do
when I'm using, say, a single form for collecting criteria (this is
how I implement my QBF interfaces), then you can have multiple forms
that display results in different views that all get their criteria
from the public interface of the form. Of course, that only works
with classes and collections and not with arrays, but you can write
a wrapper around an array to get data out of it. But for arrays, it
is usually easier to just store them in blobal variables.

--
David W. Fenton http://www.bway.net/~dfenton
dfenton at bway dot net http://www.bway.net/~dfassoc
Nov 13 '05 #31
"BrianDP" <bd****@gmail.com> wrote in
news:11*********************@o13g2000cwo.googlegro ups.com:
1) I use them to get, and hold information from the user,
typically the start, and stop time of an event, (startDate and
stopDate) so that I don't have to prompt ther user over and over
and OVER again for the same piece(s) of information. Somtimes it
will be an employee clock number if we're researching that, or
somethign else, maybe a part number if I'm going to use that in a
report.

2) I use them as a way to pass information to queries- almost
always, when I create a global variable - I'll usually label them
glStart and glStop for instance, or glClockno, or glPartno, I
write an accompanying "return" function. The function can be used
as criteria in a the regular QBE grid.


These are exactly the two kinds of uses that global varaibles shoudl
*not* be used for, if you want a well-designed and easy-to-maintain
application.

--
David W. Fenton http://www.bway.net/~dfenton
dfenton at bway dot net http://www.bway.net/~dfassoc
Nov 13 '05 #32
On Fri, 11 Nov 2005 20:44:18 -0600, "David W. Fenton"
<dX********@bway.net.invalid> wrote:
Steve Jorgensen <no****@nospam.nospam> wrote in
news:18********************************@4ax.com :
On Thu, 10 Nov 2005 15:16:28 -0600, "David W. Fenton"
<dX********@bway.net.invalid> wrote:


[]
But my "rules" for global variables explicitly pointed out that
objects like arrays and classes were appropriate uses of globals.


OK, I see where you're coming from. I had thought you were
talking about an alternative to using global variables to pass the
objects.


Well, I *am* talking about an alternative to the usual mishmash of
global variables that beginning Access programmers often use. It
collects a group of variables and stores them in a data structure
that is itself stored in a global variable. But the organization
that comes from using an array or a collection or a class module
means that you are setting and using the values as a group, and
inthe case of class modules, you can instantiate mulsiple instances
in order to keep values segregated, while not needing to multiply
the number of global variables defined (though you may need to
define a global variable for each instance, but one alternative to
that is to store your class instances in a custom collection).

You could also make these object variables non-global, such as
defining them as a public member of a form. That's something I do
when I'm using, say, a single form for collecting criteria (this is
how I implement my QBF interfaces), then you can have multiple forms
that display results in different views that all get their criteria
from the public interface of the form. Of course, that only works
with classes and collections and not with arrays, but you can write
a wrapper around an array to get data out of it. But for arrays, it
is usually easier to just store them in blobal variables.


I can't tell for certain what parts of what you're saying are the same as what
I am saying and what are in contrast.

If I understand correctly, we are both talking about a global collection
through which "parameter" objects are passed from some piece of code into a
form or report that code is opening. I believe we are also both saying the
object instance being passed is created and used case-by-case, and not treated
as global, though it momentarily resides in a global collection.

In my case, the object being passed might be a custom class instance, or it
might just be a collection of the values with keys by which to pull them back
out.
Nov 13 '05 #33
Steve Jorgensen <no****@nospam.nospam> wrote in
news:qu********************************@4ax.com:
If I understand correctly, we are both talking about a global
collection through which "parameter" objects are passed from some
piece of code into a form or report that code is opening. I
believe we are also both saying the object instance being passed
is created and used case-by-case, and not treated as global,
though it momentarily resides in a global collection.

In my case, the object being passed might be a custom class
instance, or it might just be a collection of the values with keys
by which to pull them back out.


We're saying the same thing.

And I believe it's the only valid use for global variables.

--
David W. Fenton http://www.bway.net/~dfenton
dfenton at bway dot net http://www.bway.net/~dfassoc
Nov 13 '05 #34

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

14
by: lkrubner | last post by:
If I set a variable at the top of my code like this: $name = "Lawrence"; It is now a global variable. If, later on, in a function, I want to do this: function uppercaseName() { global...
0
by: Kepes Krisztian | last post by:
Hi ! How to access my module global vars ? Some applications I want to declare global vars (constants). But in the module I cannot access them: DEF_X=120
6
by: flamesrock | last post by:
ok, so to my knowledge, object oriented means splitting something into the simplest number of parts and going from there. But the question is- when is it enough? For example I have the following...
10
by: Kleenex | last post by:
Reason: I am working on an embedded project which has very limited memory (under 512 bytes, 60 or so of which is stack space), which translates into limited stack space. In order to save on stack...
6
by: rlrcstr | last post by:
The DBA team at the office controls the naming conventions for the database structure, but their naming convention are rather tedious. So typically I create a global module that I use as a mapping...
18
by: robert | last post by:
Using global variables in Python often raises chaos. Other languages use a clear prefix for globals. * you forget to declare a global * or you declare a global too much or in conflict * you...
9
by: CDMAPoster | last post by:
About a year ago there was a thread about the use of global variables in A97: http://groups.google.com/group/comp.databases.ms-access/browse_frm/thread/fedc837a5aeb6157 Best Practices by Kang...
9
by: David | last post by:
With a non-server app there is one instance of the program running and one user 'using' it at a time. With this scenario I'm pretty comfortable with variable scope and lifetime. With a server app...
4
by: pcaisse | last post by:
I'm having issues sharing global variables with Explorer. This problem probably has a simple answer (as with most newbie questions). The script.pl file: #!/usr/bin/perl -w use strict; use...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.