Procedures
are named blocks of code that perform a task. I've shown a lot of
procedures already and I feel a little bad about waiting this long to
define that term. Procedures can have arguments
, which let you pass values in to the procedure from somewhere, and they may return values
through their name.
Visual Basic has four kinds of procedures:
- Sub procedures
Perform a task but don't have return values.
- Function procedures
Perform a task and return a value as their result.

Property procedures
Get or set a value in an object or module.
Event procedures
A special kind of Sub procedure that respond to events that occur in Excel. Only classes can contain event procedures.
The following sections explain these different types of procedures and how you use them.
1. Arguments and Results
I've heard some people say "Sub procedures don't return a value; Function procedures do." I may have said that myself once or twice, but it's not exactly true. Actually Sub and Function procedures can both return values
through their arguments. Only Function procedures return a value as their result. In other words, only Function procedures can be used on the righthand side of the equals sign (=).
For example, the CubeRoot procedure in Figure 2-5 can return a result and store that result in a variable as shown here:
x = CubeRoot(42)
You couldn't do that if it were a Sub procedure. But what if it were? Here's what CubeRoot might look like if it were rewritten as a Sub (changes are in bold):
Public Sub CubeRoot2(x As Double, result As Double)
result = x ^ (1 / 3)
End Sub
This Sub just returns the result as an argument rather than through the function name. Using the CubeRoot2 procedure is a lot more awkward than using CubeRoot, however:
' Use the CubeRoot2 Sub
Sub TestCubeRoot2( )
Dim res As Double
CubeRoot2 42, res
Debug.Print res
End Sub
One problem is that it
isn't always clear which argument you are passing in and which argument
returns the result—I named the second argument result to make that clearer. It's more common to use Subs to change arguments when you want the input argument to change to the result, like this:
' Change the passed-in argument
Public Sub GetCubeRoot(x As Double)
x = x ^ (1 / 3)
End Sub
Now the Sub changes the value of whatever argument you pass in:
Sub TestGetCubeRoot( )
Dim x As Double
x = 42
GetCubeRoot x
Debug.Print x
End Sub
This works because Visual Basic passes arguments by reference. That means the argument x
is not really 42; it's actually an address in memory that contains the
value 42. You can change this by declaring the argument as ByVal:
' Doesn't change the passed-in argument
Public Sub GetCubeRoot2(ByVal x As Double)
x = x ^ (1 / 3)
End Sub
The preceding code doesn't change the argument since it is passed by value. To confirm that it doesn't change, try this:
Sub TestGetCubeRoot2( )
Dim x As Double
x = 42
GetCubeRoot2 x
Debug.Print x
End Sub
The preceding code
displays 42, not the result you probably want. The default is to pass
arguments by reference, and you can include the optional ByRef keyword if you want to be absolutely clear what you are doing:
Public Sub CubeRoot2(ByVal x As Double, ByRef result As Double)
result = x ^ (1 / 3)
End Sub
Now, it is clearer which argument is for input (x) and which is for output (result).
2. Optional Arguments
Sometimes you can avoid having an argument. The Optional keyword tells Visual Basic than an argument can be omitted.
Sub ChangeSheets2(Optional index As Integer = 1)
Select Case TypeName(ActiveSheet)
Case "Worksheet"
If ActiveSheet.index < Worksheets.Count Then
Worksheets(ActiveSheet.index + index).Activate
Else
Worksheets(1).Activate
End If
Case "Chart"
If ActiveSheet.index < Charts.Count Then
Charts(ActiveSheet.index + index).Activate
Else
Charts(1).Activate
End If
Case Else
Debug.Print TypeName(ActiveSheet), ActiveSheet.Name
End Select
End Sub
Now, you can call the procedure with or without an index argument:
Sub TestChangeSheets2( )
' Activates the sheet three sheets away.
ChangeSheets2 3
' Activates the next sheet (omits argument)
ChangeSheets2
End Sub
Visual Basic illustrates the optional argument and its default as you type, using the autocompletion feature as shown in Figure 2.

In some cases, you
might want to fill in the default value of an optional argument with a
value that is available only while the code is running instead of using a
fixed setting. To do that, omit the default setting and test to see if
the argument is Nothing in code. For example, the following procedure automatically formats the active worksheet if the ws argument is omitted:
Public Sub Reformat(Optional ws As Worksheet)
' Check if argument was omitted.
If TypeName(ws) = "Nothing" Then
' Check the type of the active sheet.
If TypeName(ActiveSheet) = "Worksheet" Then
' Format the active worksheet.
Set ws = ActiveSheet
Else
' You can't reformat nonworksheets.
MsgBox "Select a worksheet and try again."
Exit Sub
End If
End If
Dim rng As Range
' Get the cells with data in them.
Set rng = ws.UsedRange
' Apply AutoFormat
rng.AutoFormat xlRangeAutoFormatSimple
End Sub
Most of the preceding
code is devoted to checking whether the argument is missing and whether
the active sheet is a worksheet. That is usually the case in this
situation; you need to be careful to make sure the selected item will
work with the rest of your code when filling in a default value this
way.
In a few rare cases, you
might want to write a procedure that takes any number of similar
arguments. In that situation, declare the argument as a ParamArray as shown here:
Public Sub Reformat2(ParamArray sheets( ) As Variant)
' If argument is ommitted, call Reformat
If IsMissing(sheets) Then
Reformat
Exit Sub
End If
' Otherwise, go through each argument in the array.
Dim var As Variant, ws As Worksheet
For Each var In sheets
If TypeName(var) = "Worksheet" Then
' Convert the type to Worksheet.
Set ws = var
' Call Reformat.
Reformat ws
End If
Next
End Sub
The Reformat2 procedure can have any number of arguments, including none: the IsMissing function checks for that case. ParamArray arguments can only be Variants, so you need to check each argument as shown in the For Each loop to make sure it's the right type. Reformat2 simply reuses the Reformat procedure I created earlier to do the real work. Reusing existing code is always a good idea.
The keyword ParamArray points up a terminology detail I'd rather ignore: the names used between parentheses in a procedure definition are called arguments; the variables passed in when the procedure is called are referred to as parameters. Confused? That's why I just call them all arguments. |
|
To see how ParamArray works, call Reformat2 as shown here:
Sub TestReformat2( )
' Format two worksheets
Reformat2 Worksheets("2002"), Worksheets("2003")
' Format the active worksheet
Reformat2
End Sub