By the very nature of the material, this unit presents more theory than many of this book's other units have done. Before you can move up to the next level of Visual Basic programming, you must master certain programming techniques needed for writing
large-scale applications.
Until now, each unit has added to your Visual Basic language vocabulary. So far, the more you learned, the larger your event procedure got. This unit reduces the size of your event procedures! Starting in this
unit, you'll see how to break up
your programs into smaller and more numerous procedures than you've seen so far.
Concept: Break your programs into as many small but logical sections as possible. The smaller routines make your programming and subsequent maintenance easier.
In many traditional programming languages such as COBOL and FORTRAN, a program is like a long book without chapters: The code goes on and on and the program's length is exceeded only by the boredom programmers face trying to wade through the code
hunting down errors. You've learned enough about Visual Basic so far to know that a Visual Basic program consists of a lot more than a long program listing. A Visual Basic program consists of the following:
Note: The (general) procedure in any program's Code window is often called the declarations section.
This unit will expand on the purpose of the (general) procedure and will explain how to add different kinds of procedures to your
programs.
You now know two kinds of procedures: event procedures, which execute as events occur, and the (general) procedure. The (general) procedure is really not an executable procedure in the way that event procedures execute. The only
statements that you can
place in the (general) procedure are data definition statements such as Dim statements and option statements such as these:
Option Base 1
and
Open Explicit
Many Visual Basic programmers don't worry about using the Option Base 1 statement, which, as you learned in Lesson 6, specifies that all array subscripts will begin at 1 rather than 0 (the default). Rather
than use Option Base 1, most programmers just
ignore the first subscript of 0 and act as if the subscripts in all arrays begin at 1.
The Option Explicit statement is a helpful statement that you can place in the (general) procedure to tell
Visual Basic to look for any undefined variables and to issue an error if Visual Basic finds any. By requiring that you explicitly define all
variables before you use them, misspelled variable names almost never cause the problem they would otherwise.
Assume that your program does not include the Option Explicit statement and you type the following in an event procedure:
Dim Sales As Currency Sale = 294.43 lblOut.Caption = Sales ' Outputs zero
Visual Basic outputs zero in the label because Sales contains zero, and the variable that you thought was named Sales, which you accidentally named Sale, holds the value of 294.43, which the user won't see.
If you were to
add the Option Explicit statement in the (general) procedure (the only place you can put Option statements), Visual Basic would display the error message box shown in Figure 15.1, because Visual Basic correctly deduces that you have yet
to define Sale but
you're trying to store a value in Sale.
Figure 15.1. Define all variables before using them or you'll get this error.
Definition: Variable declaration, in Visual Basic, means the same thing as variable definition.
Note: The Options Environment menu displays a list of options that contain a Require Variable Declaration option that you can set to True. If a program's (general) procedure includes the Option Explicit statement, the program requires that all variables be defined before their use no matter how the Options Environment menu is set.
There are additional procedures that you can place in your programs. The general name for these procedures is subprogram. These
procedures, just like the event procedures, are like small versions of a Visual Basic program. In a way, all
procedures are the building blocks of the overall application code because all the procedures work together to comprise the complete application.
There are two kinds of Visual Basic subprograms: subroutine procedures and function procedures. Often, we abbreviate subroutine procedures to just subroutines and we abbreviate function procedures to
functions.
This may cause confusion due to the built-in functions such as Int() that you read about in the previous lesson. This book reserves the term function procedure, or just function, for the function procedures that you write,
and this book
refers to the functions supplied by Visual Basic as the built-in functions, as has been the case throughout the earlier lessons.
Note: Event procedures are actually specialized subroutine procedures. It's important, however, to distinguish between event procedures and the general-purpose subroutines you'll write that aren't tied to events. Therefore, this book will continue to refer to event procedures by that name, and this book will refer to subroutine procedures either subroutine procedures or just subroutines.
Review: This section, as
well as most of this unit, is concerned with getting the terminology straight that you'll read about and use throughout the rest of this book. Visual Basic programs are actually just
small sections of code named subprograms that you write and that
appear along with a (general) definitions section. There are two kinds of subprograms: subroutine procedures (which include event procedures) and function procedures. The rest of this
unit explains more about these Visual Basic program divisions and also
explains how to store subprograms in external files that more than one Visual Basic application can share.
Concept: A subroutine procedure always begins with the Sub statement and always ends with the End Sub statement. A subroutine procedure may or may not be an event procedure. Non-event procedures are
general-purpose subroutines that you can write and add to any program.
Listing 15.1 contains an event procedure that you've seen in almost every program in this book.
Listing 15.1. A common event
procedure.
1: Sub cmdExit_Click() 2: End 3: End Sub
Definition: Wrapper lines are lines of code that start and terminate procedure.
Lines 1 and 3, the wrapper lines, confirm that the Exit command button's Click event procedure is a subroutine procedure, as mentioned previously.
Event procedures are
specific subroutines tied directly to control events. There will be times, many times in fact, when you'll write subroutines that aren't tied to any events whatsoever. When you write a section of code that your application will have
to execute more than
once, that section of code is a great candidate for a general-purpose, non-event subroutine.
Definition: To call a subroutine means to execute the subroutine from elsewhere in the program.
Suppose that your company has a specialized routine for calculating a cost of sales value. In writing a sales-computation data entry program, you find that you must execute this
code several different places in the program. In other words, you never
execute the routine over and over in a loop; instead, you may find that three or four different event procedures need to include this same calculation. Rather than type the exact code
in three or four different places, type the code just once in its
own subroutine and call that subroutine from each event procedure that needs the calculations.
Follow these steps to create a non-event subroutine procedure:
Figure 15.2. Visual Basic gives your subroutine its own
place in the Code window.
Note: Notice that Visual Basic always adds parentheses after all subroutine names. Nothing you do will get rid of those parentheses. You'll use those parentheses in the next unit.
Listing 15.2. The cost of sales
subroutine.
1: Sub CostOfSales () 2: ' Computes a cost of sales and displays 3: ' that cost in the appropriate label 4: Dim GrossSales As Currency 5: Dim CostSales As Currency 6: Dim OverHead As Single 7: Dim InventoryFctr As Single 8: Dim PilferFctr As Single 9: 10: ' Store initial values from the form 11: GrossSales = txtGross.Text 12: InventoryFctr = txtTotalInv.Text * .38 13: PilferFctr = txtPilfer.Text 14: OverHead = .21 ' Fixed overhead 15: 16: CostSales = GrossSales - (InventoryFctr * GrossSales) 17: CostSales = CostSales - (PilferFctr * GrossSales) 18: CostSales = CostSales - (OverHead * GrossSales) 19: lblCost.Caption = Format$(CostSales, "Currency") 20: End Sub
Figure 15.3. You can use the New Procedure dialog box to open a new procedure.
Although the body of the cost of sales subroutine is only 18 lines long, you certainly don't want to type
those same 18 lines in every event procedure that needs to execute those 18 lines. If you've typed the code in its own subroutine as shown in
Listing 15.2, you never have to type that code again. Instead, you'll just call that subroutine from every place
in the program that needs the code executed.
Tip: You can take a shortcut when you want to open a new procedure. Select the View New Procedure from the menu bar. Visual Basic displays the small New Procedure dialog box shown in Figure 15.3. Clicking Sub opens a new subroutine procedure, and clicking Function opens a new function procedure (described later in this unit). Type the new procedure name in the Name text box and click OK. Visual Basic will open the new procedure and type the wrapper lines for you.
The Call statement executes subroutines. Here is the format of Call:
[Call] subName [(argumentList)]
Unit 16 describes the use of the argumentList. Not all procedures will contain an argument list. Call is optional, but if you do use the Call keyword when you
call subroutines, you also must include the parentheses. Just
remember: If no Call, no parentheses; if Call, use parentheses. Either way, the argument list may or may not be required.
Tip: You can move from procedure to procedure inside the Code window using the PgUp and PgDn keys. If you press F2, the shortcut access key for the Window Procedures command, Visual Basic displays a list of procedures; select a procedure and Visual Basic takes you to that procedure in the Code window.
You'll learn about the argument list in the next unit. For now, concentrate on the use of Call. Whenever any procedure in your
program needs to trigger the execution of the CostOfSales subroutine, you'll only need to code the following Call statement:
Call CostOfSales () ' Executes all of the subroutine
The following
statement is equivalent because, without Call, you don't need the parentheses:
CostOfSales ' Executes all of the subroutine
If three different procedures need the cost of sales routine calculated,
isn't it easier (and less error-prone) to type the code once in its own subroutine and then simply call that subroutine from every place in the program that needs it? You
can call subroutine procedures from event procedures as well as from other
non-event subroutine procedures. Figure 15.4 shows the program flow when several event and subroutine procedures call the CostOfSales procedure. When one of the calling procedures
calls CostOfSales, the CostOfSales takes over, executes, and the program's
execution flow continues where it left off before taking time off.
Figure 15.4. The subroutines work like detours inside your program.
The Call statement also executes event procedures. You can, even if
the user didn't trigger an event, trigger any event from within the code by using Call to execute the event procedure. Suppose that the Exit command button performs several
end-of-program tasks such as erasing the form, asking the user's permission to
exit inside a message box, writing the last of data files to disk, and printing a final report on the printer. If you find that you need to perform a program exit even if the
user has yet to click Exit, you can call the cmdExit_Click() event procedure
yourself.
Note: If you ever need to exit a subroutine procedure before the procedure's normal terminationfor example if the user cancels a subroutine input boxuse the Exit Sub statement.
Review: When the same set of code lines needs to appear in more than one location in your program, consider putting those lines in
their own subroutine procedure. You'll need to start a new procedure only if
you use the Sub" or select the New Procedure command from the View menu. Place a Call to the subroutine procedure in all other program locations where you want the
subroutine code to execute. You can call either a subroutine procedure or an event
procedure using Call. When Visual Basic completely executes the procedure, Visual Basic returns execution to the code that performed the Call, and execution resumes at the
statement following the Call.
Concept: Over time, you'll
write several routines, such as the previous section's CostOfSales subroutine procedure, that you will need in more than one application. Rather than write the same subroutine
procedure in every application, you can store the subroutine procedure in a
module file, along with other common procedures that you write, and add that file to any application that uses the file's procedures.
Definition: A module contains a form and the procedures used by that form.
Every program that you've seen so far in this book has consisted of single modules. A module is a collection of all event procedures, subroutine procedures,
and function procedures, along with a form holding the controls. With the Visual Basic Primer
system, you can add additional code modules to your form's module. A code module is an external file, stored separately on the disk, that contains the code of
procedures that you write. These procedures are general-purpose procedures that perform
calculations on data that you'll soon learn to pass to and from those procedures.
The CONSTANT.TXT file is a specialized kind of module that contains only a
(general) procedure. Although you could add additional procedures to CONSTANT.TXT, you should neither add nor take away from the contents of CONSTANT.TXT. With each version of
Visual Basic that Microsoft releases, Microsoft updates the contents of
CONSTANT.TXT, and you'll want to replace the old CONSTANT.TXT file with the new one when and if you upgrade to a different version of Visual Basic.
You can add additional code modules to your program by selecting File New Module (or by clicking
the New Module toolbar button, the second toolbar button from the left). As soon as you request a new module, Visual Basic opens a new Code window for that
module. Visual Basic automatically names the first module that you add MODULE1.BAS (adding that
file to the Project window's file list for that application). If you were to add a second module, Visual Basic would name that module MODULE2.BAS, and so on.
Figure 15.5 shows the new Code window that opens when you add a new module to an application.
Figure 15.5. The new Code window from the MODULE1.BAS external module file.
Once you add code to a module file, you should save the file under a name that's different from the default name. Keep
the .BAS file extension, however. The .BAS filename extension is usually reserved in Visual Basic for the general purpose external
module files that you'll write and use. Even though CONSTANT.TXT is an external module file that breaks the .BAS file-naming
rules, CONSTANT.TXT is an external module file.
The Project window for each application reflects which form file and module files (as well as CONSTANT.TXT if you use it) that each application requires. When you create a module, you're creating a
stand-alone file on the disk that more than one
application can use as long as you add that module to the applications' Project windows.
Note: Remember that the Project window doesn't hold files. The Project window holds only an application's list of needed files. You'll have to ensure that you distribute your Visual Basic applications with all files listed in each application's Project window (described in the .MAK file) if you write and distribute your Visual Basic applications to others.
Suppose that you wrote several subroutine procedures that printed your company's name and address, calculated city sales
taxes for sales in all of your division's 20 regions, and made a backup of data files if the date hits the first of the month. You
may find that, although you originally wrote these procedures for a general ledger system that you were writing, you need
those same procedures in other programs. You can open a new module from within the general ledger application's Code window, and cut
and paste those procedures from the general ledger application to the new module. If you then saved the module under the
name MYPROC.BAS, the general ledger application would be able to call that module's procedures (owing to the fact that Visual Basic
added the module to the Project window when you first created the module), as well as any other application to which you
selected File Add and added the MYPROC.BAS module to.
All programs automatically contain a form module. The module that you work in, using the Code window, that always displays when you open a form's or control's event procedure, is the form
module. Additional modules that you add to the application, as
well as the module supplied in CONSTANT.TXT, are sometimes called non-form modules.
Review: Over time, you'll create module files
when you run across useful procedures that you might need elsewhere. You can have as many modules on your disk as you want and add any or all of them to subsequent
applications that you write. In a way, you're then building new applications faster by
reusing code (through the Call statement) that you wrote for other applications and stored in the general-purpose modules.
Concept: As with the built-in functions, you can write your own general-purpose function procedures, often just called functions, that aren't tied to specific
events. You can call these functions from
your Visual Basic application. You can place these functions in their own external code modules or add them to an application's Code window. Function procedures work a lot like subroutine procedures; you can call
them from elsewhere in the program. Unlike
subroutine procedures, however, function procedures return values.
You can write your own function procedures to augment the built-in procedures supplied by Visual Basic. If you run across a needed
calculation that converts a needed measurement, and you feel that you'll need that same conversion several places in your
program, add that code to a function procedure. The function will be able to make the calculation and return the answer for the
calculation to the calling routine.
As explained in the previous lesson, "Functions and Dates," when you call built-in functions, you must do something with the return value. You can't code the Int() function on a line by itself like
this:
Int(Amount) ' Invalid!
Int(Amount) will return the amount converted to an integer, and you must do something with the returned integer. Therefore, you'll usually assign the return value like
this:
lblAmt.Caption = Int(Amount)
or you'll use the function in a calculation that needs the value like this:
WholePart = Int(Amount) + Estimate
You can write the code for your own numeric and string function procedures and add them to Visual Basic's repertoire. The functions that you write aren't quite as built in as Visual Basic's built-in functions because your
functions don't become part of
Visual Basic's language. Nevertheless, as with subroutine procedures, you can code the function procedures inside your application's Code window as well as store function procedures by themselves or along with subroutine
procedures in external modules so
that more than one application has access to the functions.
To write a new function procedure within a Code window, whether that Code window is your application form's Code window or an external .BAS module that
you've opened, you can select the View New Procedure from the menu and click the Function option
button on the New Procedure dialog box. (In the previous section, you clicked Sub to open a new subroutine.) After you type a new name for the function in the
Name text box, Visual Basic adds the function wrapper lines like the ones shown here for a new
function procedure named MultiplyIt:
Function MultiplyIt () End Function
A function procedure always
begins with the Function statement and ends with the End Function statement. You'll add code to the body of the function between the two wrapper lines.
Tip: If you add a dollar sign after the function name, such as Reverse$(), Visual Basic assumes that you want to open a string function that will return a string value. If you omit the dollar sign, Visual Basic assumes that your function will return the Variant data type in which you can return numbers or string values. To keep things clear, always use the dollar sign for string functions.
A function is the same in every way as a subroutine except that the function returns
a value. To return the function's value, assign the return value to the name of the function. Don't use Call to call a function. All you do to call a function is
to use the function procedure's name inside an expression or statement.
Note: If you ever need to exit a function procedure before the function's normal terminationfor example, if the user cancels an input boxuse the Exit Function statement.
Stop and Type: Listing 15.3 contains a function that computes the postage for a letter or package as follows:
Listing 15.3 assumes that the letter or package weight appears in a text box
control named txtWeight.Text. In addition, the weight must appear as ounces. Therefore, any application that uses this function must make sure that a text box named txtWeight
exists and holds the total package weight before calling the function.
Review: When you want special routines that calculate or manipulate string values, and Visual Basic doesn't supply those routines through internal built-in functions such as CInt() and Mid$(), write your own
function procedures. Before the function procedure terminates, assign the function's return value to the name of the function. Visual Basic uses this mechanism (the assigned name) to return the value assigned to the name back to the calling code that needs
the result.
Listing 15.3. A function that calculates postage and returns the cost.
1: Function Postage () 2: ' Calculate postage based on the 3: ' weight of a letter or package 4: Dim PostHold As Currency 5: Dim weight As Integer 6: 7: ' Grab the weight value from the text box 8: ' and convert to number for comparison 9: weight = Val(txtWeight.Text) 10: 11: Select Case weight 12: Case Is <= 8: 13: PostHold = .32 14: Case Is <= 12: 15: PostHold = .47 16: Case Is <= 16: 17: PostHold = .62 18: Case Is <= 20: 19: PostHold = .77 20: Case Is < 24: 21: PostHold = .92 22: Case Is >= 24: 23: MsgBox "Weight is more than 24 ounces!", MB_ICONEXCLAMATION, "Error" 24: PostHold = 0 25: End Select 26: 27: Postage = PostHold ' Return the value 28: End Function
Analysis: Line 3 defines a variable that will hold an
interim postage amount throughout the Select Case. Actually, the extra PostHold variable is not strictly needed because each Case could assign directly to
the function name. Nevertheless, assigning to the function name at the bottom of the function makes
for better self-documentation of the code. When the function finishes, line 26 completes the function's task by assigning the calculated value to the
function name.
Without Sharing, Your Code Is Limited: This unit's discussion of subroutines and functions can only be complete once you master the next unit's material on passing of data from procedure to procedure. Until now, a procedure could only work with data defined inside that procedure. The only exception was the function procedure that you just learned about, in which one procedure can use a value returned from another.
The next unit shows you how you can share variables between functions. The controls that you've been using have been available to all procedures in your program but not the variables. Only when you learn how to pass data from one procedure to another can you see the real power of subroutine and function procedures.
Write a string-reversal function named Reverse$() that takes the string stored in the text box named txtAString and reverses the string, returning the string in the function name of
Reverse.