Yet Another Bad Hair Day: Avoiding Global Variables

Dear Dr. GUI:

Could I get an awesome, free Dr. GUI T-shirt if I ask you to re-answer a question for which you gave away a free T-shirt when you didn't seem to hear the question correctly? Take those things out of your ears, man. Boris wasn't asking how to pass parameters to subroutines, but rather between *modules* (Visual Basic synonym for .BAS files)! Although you gave a very nice answer for the former. [See "Is Dr. GUI Qualified?" under "Ask Dr. GUI #19" in the Development Library. —Dr. G]

A minor problem with Visual Basic 3.0 is that you are encouraged to declare global variables in a .BAS file that contains a function for displaying a dialog box in a corresponding .FRM file. In other words: X.FRM wants to call a function in Y.BAS that will run Y.FRM. But Y.BAS can't talk to Y.FRM without declaring globals, which are then visible to everyone.

The question is: What is your favorite way to avoid global variables in that scenario? Thanks in advance, and keep printing the very interesting developer information. I wear X-large.

Scott Zimmerman
San Diego, California

Dr. GUI replies:

I am just not making any headway! Let's revisit yet another "oldie moldy." My favorite way of handling the problems you are talking about is to implement pseudo-object design, which is sometimes called data structures. Most Visual Basic developers use data structures only when it is required for an API call. I tend to use it all the time.

The typical scenario is to create what I call a drop-in component. To create a drop-in component, you define a data structure, say tagFooBar, which will be declared in a module called FooBar.Bas. This FooBar.Bas module will contain only public routines that take tagFooBar as one of the arguments. The other arguments for these public routines must be elementary Visual Basic objects—for example, strings, integers, forms, controls, and objects. There should be no global declaration in this module. If this data structure needs dialog boxes, they are handled by this module exclusively.

If these rules are followed, the code may be dropped into different projects without requiring any code changes to the module. The application declares the number of instances of the data structure needed, and then calls the public routines in FooBar.Bas as needed. The diagram below shows how this was implemented in a forthcoming article in the Microsoft Development Library that explains how you add spell checking to a Visual Basic application.

Now to return to your specific questions on passing data between modules. Consider two arrays of structures, foobarA in BasA and foobarB in BasB. If you wish to pass this data between BasA and BasB, simply call routines in BasB such as the ones shown below:

Sub PutBasA(Arrfoobar() as tagFooBar)
  Redim foobarB(lbound(Arrfoobar) to ubound(Arrfoobar))
  for i%=lbound(Arrfoobar) to ubound(Arrfoobar)
    foobarB(i%)=ArrFoobar(i%)
  next I%
End Sub

Sub GetBasA(Arrfoobar() as tagFooBar)
  Redim ArrfoobarB(lbound(foobarB) to ubound(foobarB))
  for i%=lbound(foobarB) to ubound(foobarB)
    ArrFoobar(i%)=foobarB(i%)
  next I%
End Sub

The second part of your question has both a complex and a simple solution. I will present the simple solution only (because I am a simpleton). If Y.BAS changes a visible or invisible label box, the change event occurs. This change event can then call a function such as GetBasA shown above to obtain the current values in the data structure (I assumed you created a data structure that has all the data in the form). The Y.BAS module could have a public routine called UpdateFrmWithCurrent that changes this label box. The process in the X.BAS module would then be:

GetFrmYData  foobar
foobar.name="Dr.Z"
etc
SetFrmYData foobar
UpdateFrmWithCurrent foobar

If you have multiple instances of a form, you simply add the form as a parameter to the routines. I leave you to add the rest of the code.