Inside Visual Basic

May 1999

VB 5 & VB 6

Inter-Dialog Communication

by Steve Colvin

Recently, we were asked a question by an accomplished user of QBasic and VB 1.0/2.0/3.0. The question: does VB still require the use of global variables to pass data to/from a dialog box? The answer, of course, is no. This article serves to dispel that rumor and to demonstrate an OO method of passing data to/from a dialog box without the use of global or public variables. Our example application will show you how to create the code for a Text Entry dialog box, shown in Figure A, that you can then extend to meet your own needs.

Figure A: We'll design this modal dialog box that you can extend for your own use.
[ Figure A ]

That's the way it was

The method of passing data to, and returning data from, a dialog box has long dogged the VB programming community. Prior to VB4, programmers routinely used global variables in a BAS module to accomplish this communication--a practice whose roots date back to the 1970s COBOL era (yuck!). These variables had unsecured scope and were available for update by all routines. Their contents were always live, even when the processes that used them were completed. We no longer need to continue this approach in VB. In fact, it behooves us to abandon this practice and to instead embrace an objectcentric philosophy.

But there's a better way

Basic to an objectcentric philosophy is the practice of burying either generalized or specialized functionality in reusable components and exposing only what's necessary to the external process. Your PC, car, and stereo are filled with black box components. The engineer who designed these machines only needed to know the inputs, outputs, and tolerances of the building blocks to make the end product without regard to each component's internals. Developers need to take such an approach. An object, or component, exposes only those contents that are relevant to the user of that object through its public interface. With this in mind, all communication from/to the component must be done through this interface. A solid VB project architecture adheres to this guideline, eliminating the use of global variables for the passing of data from one process to another. Ultimately, this practice minimizes your debugging complexities and maximizes your potential for component reuse.

Form a la modal

Generically speaking, a dialog box is a GUI mechanism that extends the input of information beyond the confines of a single screen. It unclutters the base form and reduces overall resource consumption via segmentation. It can then be called from various places in the application without the need of process and code redundancy. More specifically, a dialog box is a form that's loaded using the vbModal parameter. It temporarily halts the calling form's thread (message loop) and leaves that thread in a wait for return status. When the modal form closes, control returns to the next line of code in the calling routine. Logically, a modal dialog box extends the thread of the calling routine.

Dialog boxes and form-level variables

The basic architecture for communication with a dialog box consists of the modal form with form-level variables, property procedures, and a VB class. The form-level variables (defined in the General Declaration section of the dialog box) are the buckets that hold the information to be returned.

The contents of form-level variables are always available, whether the form is loaded or not. They can have non-initialized values assigned to them at any time, without the form even being loaded. Also, when the form is unloaded, all its controls' properties are initialized except its form-level variables. Only when the form is set to Nothing do the form-level variables get automatically initialized. We'll demonstrate this later.

To retain a property value of a control, simply create a form-level variable in the General Declarations section and use that to store the property of the control you want to return. Then, when the button that closes the modal dialog box is pressed, capture the applicable property from the control into that variable, and unload the form. You'll want to ensure that the dialog box is set to Nothing after the values have been plucked out of the form-level variable.

Although the VB compiler accepts form-level variables that are defined as Public, you risk the variables being unintentionally altered. By changing their scope to Private and using Property Get and Property Let procedures, you control access to these Private variables.

A class(ic) excuse

In our example application, which we'll show you shortly, we've added a layer of abstraction to the process by creating a Public function to encapsulate the loading, initializing, input/output communication, and garbage collection of the dialog box. This function can't reside in the dialog box form itself since the contents of the form-level variables are destroyed during the garbage collection. It's preferable to put the function in a class that can be instantiated to launch the form as opposed to putting it in a BAS module. For instance, if you reuse the dialog box in other applications (good for you if you can!), you can simply drop the class and the dialog box form into that app without having to gut the BAS module of extraneous code that might not apply to the other VB project. Also, by hiding the function in a VB class, it's not necessary to hardcode the name of the form or any of its attributes in the application that uses it. That way, if the form or any of its components are renamed, you can just change the references to them in the class.

Our inter-dialog example

We'll begin by creating two forms, the dialog box, frmModalTextDialog, and the calling form, frmCallModalTextDialog. We'll also create one class called clsModalTextDialog. Create a project folder called ModalDialog; we'll refer to this folder later. Set frmCallModalTextDialog as the Startup object in the Project Properties dialog box in the Project menu. Figure A, on the cover, shows the structure of our modal dialog box, frmModalTextDialog. Figure B shows the structure of the calling form, frmCallModalDialog. Tables A and B show the properties frmModalTextDialog and frmCallModalDialog, respectively.

Figure B: Design your calling form to look like this (frmCallModalTextDialog).
[ Figure B ]

Table A: Property settings of frmModalTextDialog (the modal form)
Control TypeNameProperty/AttributeValue
FormfrmModalTextDialogCaptionModal Dialog Box
<filename> ModalTextDialog.frm
TextBoxText1TextText1
MaxLength0
CommandButtonCommand1MCaptionReturn

Table B: Property settings of frmCallModalTextDialog (the calling form)
Control TypeNameProperty/AttributeValue
Form FrmCallModalTextDialogCaptionReceiver of Modal Input
<filename>CallModalTextDialog.frm
TextBoxText1TextInitial Contents
MaxLength0
CommandButtonCommand1CaptionShow Modal Dialog Box

Now that you have the screens painted, let's add some code to tie this all together. Listing A defines the private form-level Property variables of the modal dialog box, as well as their corresponding Let and Get Property procedures to control access to the variables. Listing B contains the code to capture the attributes of controls (that is, the Text property) into the dialog box's Property variables so the information will persist after the dialog box is unloaded.

Listing A: Form-level variables and Property procedures for modal form (frmModalTextDialog)



Private mbSelectionMade As Boolean

Private msInputString As String
Private miMaxLength As Integer
Private msOutputString As String

Public Property Let MaxLength(theMaxLength As Integer)
	miMaxLength = theMaxLength
End Property

Public Property Let InputString _
	(theInputString As String)
	msInputString = theInputString
End Property

Public Property Get OutputString() As String
	OutputString = msOutputString
End Property

Public Property Get SelectionMade() As Boolean
	SelectionMade = mbSelectionMade
End Property

Listing B: Private routines in the modal form (frmModalTextDialog)


Private Sub Command1_Click()
	mbSelectionMade = True
	msOutputString = Text1.Text
	Unload Me
End Sub

Private Sub Form_Load()
	Text1.Text = msInputString
	Text1.MaxLength = miMaxLength
End Sub
The only code necessary for the VB class is shown in Listing C. Be sure to name the VB class file ModalTextDialog.cls. Its ShowDialog function manages the entire process of launching the modal dialog box and returning the information to the routine from which it's called. (Two of its arguments, theInputString and theOutputString, could have shared the same address space. For example, we could replace these with a single by-reference string variable, theString; however, they've been segregated for discussion purposes.)

Listing C: Exposed class function to launch modal form and return results




Public Function ShowDialog( _
	ByVal theInputString As String, _
	ByVal theMaxLength As Integer, _
	theOutputString As String) As Boolean

	Dim lfrmDialog As New frmModalTextDialog
  
	lfrmDialog.InputString = theInputString
	lfrmDialog.MaxLength = theMaxLength
	lfrmDialog.Show vbModal
	ShowDialog = lfrmDialog.SelectionMade
	theOutputString = lfrmDialog.OutputString
	Set lfrmDialog = Nothing
End Function
At this point, the dialog box needs only to be given life. Listing D activates the modal dialog box from frmCallModalTextDialog by passing it the contents of the Text property of Text1 as the initial value.

Listing D: Calling form's code (frmCallModalTextDialog) to use the reusable object


Private Sub Command1_Click()
	Dim lclsDialogClass As New clsModalTextDialog
	Dim lsReturnString As String
  
	If lclsDialogClass.ShowDialog(Text1.Text, _
		Text1.MaxLength, lsReturnString) Then
		Text1.Text = lsReturnString
	Else
		MsgBox "Cancelled"
	End If

	Set lclsDialogClass = Nothing
End Sub

ShowDialog is defined as a Boolean function, so the calling routine can recognize that the user confirmed the text to be returned. If the user clicks Return in the modal dialog box, ShowDialog returns True; otherwise, it returns False. There are many other features that could be added to the modal dialog box (MultiLine options, Search/Replace capabilities) but they go beyond the scope of this article.

How about a test drive?

To see how VB handles the initialization of control properties and form-level variables, follow these steps:

  • Ensure that frmCallModalTextDialog is your Startup form on the Project | Properties dialog box.

  • From the Tools dropdown menu, choose Options; then, in the Editor tab of the Options dialog box, check the Auto Data Tips option.

  • Put a break point in the Command1_Click subroutine of frmModalTextDialog on the Unload Me line. (Click on the line of code and press [F9] to set the break point.)

  • Put another break point in the ShowDialog function of clsModalTextDialog on the Set lfrmDialog = Nothing line.

  • Start the application.

  • Click the Show Modal Dialog Box button, alter the contents of the text box in the modal dialog box, and then click Return.

  • When VB stops on the first break point, hover the mouse pointer over the Text1.Text reference in frmModalTextDialog's Command1_Click function. You'll see the same value you entered in the text box.

  • Press [F8] to step through the Unload Me line of code. Hover your pointer again over the Text1.Text reference. You'll see it's initialized to its design-time value, in this case Text1. However, if you hover over the reference to msOutputString in frmModalTextDialog's General Declarations, you'll see it has retained the value of Text1's Text property.

  • Press [F5] to continue to the next break point. When VB halts on the Set lfrmDialog = Nothing in the VB class, hover your pointer over the reference to lfrmDialog.OutputString and you'll see it captured the Text property of the modal dialog box's TextBox.

  • Press [F8] to step through the Set lfrmDialog = Nothing line and hover over lfrmDialog.OutputString again. You'll see <Object variable ... not set>. Since we defined ShowDialog's theOutputString argument as an address (that is, by reference), it shares the memory location with the variable in the calling routine. Any change to theOutputString in ShowDialog is automatically reflected in the variable where ShowDialog is called.

  • Although the class/form we created is rather simplistic, it follows the basic object model. It has structured logical boundaries around its private interface and exposes only what's externally applicable in its public interface. Enhancements to any portion of its private interface are automatically and seamlessly incorporated into all VB projects that use it. That is to say, if you enhance our modal form to perform Find/Replace string manipulation, all VB projects that use this form realize the benefits of this enhancement automatically when they're recompiled.

Conclusion

In this article, we demonstrated how VB cleans up a form's control properties and its form-level variables when the form is unloaded and when the form is set to Nothing. We also took advantage of what we learned by creating form-level variables to keep the Property values of controls beyond the Unload statement and by launching the form with a class function. The function acted as a middleman between the base form and the modal dialog box by encapsulating the passing of data between these forms prior to setting the dialog box to Nothing. By not using global variables to pass data to a form, we can create a component that can stand alone; that is, a component that's reusable. In our companion article, "Reusability via ActiveX," we'll explore additional reusability opportunities that this methodology offers.


Copyright © 1999, ZD Inc. All rights reserved. ZD Journals and the ZD Journals logo are trademarks of ZD Inc. Reproduction in whole or in part in any form or medium without express written permission of ZD Inc. is prohibited. All other product names and logos are trademarks or registered trademarks of their respective owners.