Anatomy of a Function

noi_max
02-16-2005, 12:46 PM
This is a tutorial/reference for working with functions in Visual Basic 6.

Special thanks to:
OnErr0r
herilane
Deadalus
Dennis DVR
MikeJ
NateO
loquin
Iceplug
Mike_R
lebb
and others for their support and feedback in the making of this tutorial. :)

-----------------------------------------------------------------------
This tutorial is split up into different sections with some quick links to each one.
-----------------------------------------------------------------------

Introduction (http://www.xtremevbtalk.com/showpost.php?p=944964&postcount=2)
The difference between a Sub and a Function (http://www.xtremevbtalk.com/showpost.php?p=944966&postcount=3)
Calling a Function (http://www.xtremevbtalk.com/showpost.php?p=944969&postcount=4)
Arguments/Parameters - the basics (http://www.xtremevbtalk.com/showpost.php?p=944971&postcount=5)
ByVal vs. ByRef (http://www.xtremevbtalk.com/showpost.php?p=944973&postcount=6)
Optional Parameters (http://www.xtremevbtalk.com/showpost.php?p=944977&postcount=7)
Passing Controls and Objects (http://www.xtremevbtalk.com/showpost.php?p=944984&postcount=8)
Passing/returning Arrays - by herilane (http://www.xtremevbtalk.com/showpost.php?p=944986&postcount=9)
Enums and User Defined Types (http://www.xtremevbtalk.com/showpost.php?p=944991&postcount=10)
ParamArray (http://www.xtremevbtalk.com/showpost.php?p=944997&postcount=11)
Scope (http://www.xtremevbtalk.com/showpost.php?p=945001&postcount=12)

noi_max
02-16-2005, 12:49 PM
Coming from a purely Visual Basic background, I got used to the fact that everything I was doing at first was related to events. Click a button and something happens. While that's still true, it isn't very practical to have all your code in your controls' event handlers (such as cmdButton_Click).
Also, it became clear to me that some bits of code could be re-used by different events or procedures.
Take one of the built in conversions for example:

Dim lngNumber As Long
LngNumber = CLng(Text1.Text)

This is an example of a function. You don’t need to write the code for the CLng() function, its already done. You just need to use it when it’s needed. This comes in real handy when it’s time to write your own functions. You write the code to do something once, and then just call it when it’s needed. How convenient is that?

After this small tutorial is through, you should be able to create your own functions without any problems and hopefully make your programming life easier. I’ll try to cover some things that can or will trip you up if you’re in unfamiliar territory. I’ll also try to make this interesting for those just looking for a refresher.

noi_max
02-16-2005, 12:51 PM
There is one major difference between a sub procedure and a function. A function returns a value. Some programming languages use functions exclusively, always with a return value unless specified as “void”. Visual Basic, however has the sub procedure, which suggests no return value at all.

Private Sub myProcedure()

'Code for myProcedure

End Sub

This will basically run whatever code is inside the procedure, producing no return value. Here’s an example of a function that does return a value.

Private Function GetCircumferenceFeet(ByVal DiameterInInches As Single) As Single

Const PI = 3.14159
Const IPF As Integer = 12 'twelve inches per foot

GetCircumferenceFeet = (DiameterInInches * PI) / IPF

End Function

In that example I’ve included a parameter in the function (ByVal DiameterInInches As Single) and passed an argument, which I will cover a little later on. Notice the data type declaration after the parenthesis. The return is a Single data type. This becomes important later when calling the function.

Private Function GetCircumferenceFeet(ByVal DiameterInInches As Single) As Single

You return the value by assigning it to the function name.

GetCircumferenceFeet = (DiameterInInches * PI) / IPF

This will be covered in more detail in the next article when we talk about the different ways a function can be called.
Stay tuned. :)

noi_max
02-16-2005, 12:55 PM
Calling a function can be done in a few different ways. When you're storing the return value of the function the format looks a little like this:

CircumferenceFeet = GetCircumferenceFeet(myDiameter)

In this example, we’re assigning the return value of the function to a variable, passing to it the one required argument. Here’s the whole thing at a glance.

Private Sub Command1_Click()

Dim CircumferenceFeet As Single, myDiameter As Single
myDiameter = 48 '48 inches

CircumferenceFeet = GetCircumferenceFeet(myDiameter)

MsgBox CircumferenceFeet

End Sub

Private Function GetCircumferenceFeet(ByVal DiameterInInches As Single) As Single

Const PI = 3.14159
Const IPF As Integer = 12 'twelve inches per foot

GetCircumferenceFeet = (DiameterInInches * PI) / IPF

End Function

There may be cases where you’re not assigning the return value. In this instance, you would simply call the function, passing the argument like so:

GetCircumferenceFeet myDiameter

This is the same as it’s done in a regular sub procedure. You’ll notice that when you are getting a return value, it’s the same syntax as using VB’s built in functions. Remember the CLng() conversion function from earlier on? Same thing.

lngNumber = CLng(Text1.Text)

What if you have no parameters in your function?

Private Function GetCircumferenceFeet() As Single

Const PI = 3.14159
Const IPF As Integer = 12 'twelve inches per foot
Dim DiameterInInches As Single
DiameterInInches = 48

GetCircumferenceFeet = (DiameterInInches * PI) / IPF

End Function

Notice the parenthesis are empty. This means there are no required arguments to pass to the function, so calling it can be done in either of these two ways:

SurfaceFeet = GetCircumferenceFeet()
SurfaceFeet = GetCircumferenceFeet

The parenthesis when calling the function are not required if there are no arguments, so either choice will do, however the first method shown is clearer. The parenthesis suggest that the variable is being assigned to a function rather than to another variable.

For functions that have no return value, calling them can be done with the Call statement, or by simply using the function name.

Here’s an example of a function that has two parameters but doesn't use a return value.

Private Function ChangeBackColor(ByVal lColor As Long, ByRef obj As Object)

obj.BackColor = lColor

End Function

And here’s an example of calling the function:

Call ChangeBackColor(vbRed, Me)
ChangeBackColor vbRed, Me

Notice the required parenthesis when using the Call statement.

More on using brackets here:
http://www.xtremevbtalk.com/showthread.php?t=15728

Parameters are a very important part of a function as are the arguments you pass. The next few articles will be devoted to function parameters and the passing of arguments to a function. Hope you’re still with me! ;)

noi_max
02-16-2005, 12:59 PM
When you’re creating a function it’s important to keep in mind the type of arguments it will receive. You can build your function with some parameters that will receive these arguments when the function is called.

Let’s say for example you needed a function to quickly tell you if two numbers added together were more than one hundred. It’s possible. ;) Now were working with two possible parameters. Number1 and Number2. How about the return type? Well, this kind of fits in a Yes/No, True False category so I’ll use a Boolean return value.
Here’s the prototype:

Private Function MoreThan100(ByVal Num1 As Integer, ByVal Num2 As Integer) As Boolean

Probably the most important things to point out here are the explicit declarations of data types (ByVal Num1 As Integer, ByVal Num2 As Integer) and the commas between the parameters. If the data types are not called out in this fashion, VB will assume a Variant data type, which could get you into trouble.
Here’s the entire function…

Private Function MoreThan100(ByVal num1 As Integer, ByVal num2 As Integer) As Boolean

If num1 + num2 > 100 Then
MoreThan100 = True
Else
MoreThan100 = False
End If

End Function

…and here’s an example of calling the function

Private Sub Command1_Click()

Dim myNum1 As Integer, myNum2 As Integer, blnReturn As Boolean

myNum1 = 50
myNum2 = 45

blnReturn = MoreThan100(myNum1, myNum2)

MsgBox blnReturn

End Sub

When you’re evaluating the return value of a function, you would put the arguments passed in parenthesis, just as you would in assigning the return value.

Here’s an alternate example of calling the same function.

If MoreThan100(myNum1, myNum2) = True Then
MsgBox "More than 100!"
Else
MsgBox "Less than 100"
End If

Using parameters in a function is very handy and can help keep the amount of global variables you have down. Do the variables passed to the function as arguments change at all? We’ll find out in the next topic when we discuss the ByVal and ByRef keywords. :)

noi_max
02-16-2005, 01:02 PM
Anatomy of a Function - Parameters – ByVal and ByRef

When you pass a variable as an argument to a sub or function you can pass it in two different ways. One way uses the address of the variable in memory, thus allowing us to work with the variable directly. This method is known as ByRef.
Consider a small function to increment a number:

Private Function IncrementNumber(num As Integer) As Integer

num = num + 1
IncrementNumber = num

End Function

ByRef is assumed as a default. But, to be more explicit, you should use the keyword in your parameter. Here’s what it would look like:

Private Function IncrementNumber(ByRef num As Integer) As Integer

The ByVal keyword by contrast will use the actual value of the variable. This allows us to manipulate a copy of it, without altering the original variable. In short, we'll use just the value and leave the variable unchanged. This is better illustrated by using these functions. The first one passes the variable ByRef, thus changing the actual variable:

Private Sub Command1_Click()

Dim myNumber As Integer, myResult As Integer

myNumber = 2 'Initialize variables.
myResult = 0

myResult = IncrementNumber(myNumber)

'Display results.
MsgBox "myNumber is now " & myNumber & vbCrLf & _
"myResult is now " & myResult

End Sub

Private Function IncrementNumber(ByRef num As Integer) As Integer

num = num + 1
IncrementNumber = num

End Function

If you run this, you'll notice that the original variable myNumber has been changed to 3. Now let’s look at the same function using the ByVal keyword.

Private Sub Command1_Click()

Dim myNumber As Integer, myResult As Integer

myNumber = 2 'Initialize variables.
myResult = 0

myResult = IncrementNumber(myNumber)

'Display results.
MsgBox "myNumber is now " & myNumber & vbCrLf & _
"myResult is now " & myResult

End Sub

Private Function IncrementNumber(ByVal num As Integer) As Integer

num = num + 1
IncrementNumber = num

End Function

This is the exact same function but with the ByVal keyword used. If you run this, you’ll see that the variable myNumber remains at 2. This is because a copy of that variable's address was used and not the original.

I hope that demonstration helped to sort it all out.

noi_max
02-16-2005, 01:08 PM
It's sometimes useful to have paramters that are not required; meaning that if an argument isn't passed, a default value is assumed.

Optional parameters come with a few rules:

• You need to declare a default value.

• The default value must be a Constant expression. (not a variable)

• Optional parameters have to come at the end of your parameter list. In other words you cannot have any required parameters listed after your optional ones.

• You cannot use optional parameters if you're using ParamArray

An example would be useful here. Here's a simple function that changes the .BackColor property of a form:

Private Function ChangeFormBackColor(ByRef frm As Form, Optional ByVal lCOlor As Long = 0) As Boolean

frm.BackColor = lCOlor
ChangeFormBackColor = True

End Function

And here we'll call the function:

Dim blnReturn As Boolean
blnReturn = ChangeFormBackColor(Me, vbRed)

In the second example, I've added a conditional statement based on the optional parameter.

Here, we're drawing a simple blue box using the .Line method. The optional boolean parameter tells the function whether or not the box we draw will be filled with color:

Private Function DrawBlueBox(ByRef ctrl As Control, ByVal X As Long, ByVal Y As Long, _
ByVal lWidth As Long, ByVal lHeight As Long, _
Optional ByVal Fill As Boolean = False) As Boolean

On Error GoTo ErrHandler

If Fill Then
ctrl.Line (X, Y)-(lWidth, lHeight), vbBlue, BF
Else
ctrl.Line (X, Y)-(lWidth, lHeight), vbBlue, B
End If

DrawBlueBox = True

Exit Function
ErrHandler:

DrawBlueBox = False

End Function

I've added an error handler, which will give the function a False return value if there's an error.

Here's an example of calling this function:

Dim blnReturn As Boolean
lReturn = DrawBlueBox(Me, 100, 100, 1000, 1000, True)

If blnReturn = False Then
MsgBox "Unable to render box"
End If

The error handling in the previous function was provided in case the control passed to it did not support the .Line method.

The next topic will cover using controls and objects like this as parameters in a function, and what to watch out for.

noi_max
02-16-2005, 01:14 PM
This is something that you'll need to do from time to time. Create a function that uses objects or controls for parameters.

Early/Late binding and Intellisense
For purposes of this tutorial, we will consider parameters that are explicitly declared as Early Bound. VB knows in advance what we're working with and Intellisense for the properties and methods of the control or object is available.
For example:

Private Function ChangeFormBackColor(ByVal frm As Form) As Boolean


By contrast, parameters declared as Control or Object will be considered Late Bound. VB does not know in advance what we're working with and will have to determine what it's getting at run-time.

Private Function ChangeBackColor(ByVal Ctrl As Control) As Boolean

Controls and objects by nature are one in the same, but for sake of simplicity, I will begin with specific controls first, then move on to control arrays and then finally objects. :)

Controls

Let's begin with a simple function to get the width of a textbox.

Private Function GetWidthProperty(ByVal txt As TextBox) As Single

GetWidthProperty = txt.Width

End Function

Now we'll call the function and pass to it a regular text box

Private Sub Command1_Click()

Dim sWidth As Single
sWidth = GetWidthProperty(Text1)
Debug.Print sWidth

End Sub

We've used early binding in this example by declaring txt As TextBox. Using the dot notation allowed us to see the properties and methods of the text box via Intellisense.

Now, let's try re-writing the function to get the width property of any control that supports it.

Private Function GetWidthProperty(ByVal ctrl As Control) As Single

On Error GoTo errhandler

GetWidthProperty = ctrl.Width

Exit Function
errhandler:
Debug.Print "Control does not support the .Width property"

End Function

Now we're late binding. If you write this function in VB, you'll notice that the Intellisense feature does not work in this case. This is because VB has no idea what control we'll be using in the function until run-time. I've also added error handling in case the control we pass to it does not support the .Width property. Calling the function remains the same as before:

Private Sub Command1_Click()

Dim sWidth As Long
sWidth = GetWidthProperty(Text1)
Debug.Print sWidth

End Sub

However, since our new function takes it's argument As Control, we can pass a picturebox, or command button.. etc. If the control has a width property, the function will return the appropriate value.

Control Arrays

If you need to pass a control array as an argument to a function, you need to define your parameters As Object (Late binding). It is reccomended that error handling be used like in the previous examples in case a control array you're passing doesn't support the properties or methods you're working with.

Here's an example of a function that is meant to work with text boxes specifically

Private Function ClearTextBox(ByVal ctrl As Object) As Boolean

On Error GoTo errHandler

Dim i As Integer

'Loop through the control array and clear each text box.
For i = ctrl.Lbound To ctrl.Ubound
ctrl(i).Text = vbNullString
Next i

Exit Function
errHandler:

'If a single textbox is passed (NOT a control array),
'the .Lbound and .Ubound properties are not supported.
If TypeOf ctrl Is TextBox Then
ctrl.Text = vbNullString
Else
Debug.Print "Control passed is not a text box"
'Return true if function failed
ClearTextBox = True
End If

End Function

Again, I had no Intellisense to guide me here due to declaring the parameter As Object (Late binding). Here's an example of calling the function

Private Sub Command1_Click()

Dim blnResult As Boolean
blnResult = ClearTextBox(Text1)

End Sub



Objects

Once you understand how passing controls to a function works you'll find that objects are very similar. Here I've made up a class module (clsObject) and a method called EndProcess. If you refer to your object explicitly (using early binding as shown here) the dot notation will show any exposed properties or methods.

Private Function DestroyObject(ByVal obj As clsObject) As Boolean

obj.EndProcess
Set obj = Nothing
DestroyObject = True

End Function

Here's an example of returning an object

Private Function InitObject(ByVal strProperty1 As String, _
ByVal strProperty2 As String) As clsObject

Dim NewInstance As New clsObject

NewInstance.strProperty1 = strProperty1
NewInstance.strProperty2 = strProperty2

'Return the new object
Set InitObject = NewInstance

Set NewInstance = Nothing

End Function

Private Sub Command1_Click()

Dim myObject As clsObject
Set myObject = InitObject("Hello", "World")

Debug.Print myObject.strProperty1
Debug.Print myObject.strProperty2

End Sub

There are some nuances when it comes to passing object variables ByVal vs ByRef that you may want to know about. At first glance their behavior is identical, but when it comes to setting object variables to new instances of an object there is a difference. More on ByVal vs ByRef in regards to objects here:
http://www.windowsdevcenter.com/pub/a/oreilly/windows/ron/objects.html
(Thanks to Deadalus for providing the link)

Creating your own objects with their own properties and methods is beyond the scope of this tutorial, however I felt that using them as parameters in a function needed mentioning.

Now that we've moved variables and objects around it's time to move on to arrays :)

noi_max
02-16-2005, 01:16 PM
This is where I could put my feet up and relax as our own herilane has already done a terrific job explaining how to pass and return arrays to/from a function.

She was kind enough to offer a link to this article and save me some work. Very much appreciated!

http://www.thecodenet.com/articles.php?id=6

:D

noi_max
02-16-2005, 01:22 PM
Enums:
This is a personal favorite. One thing I enjoy about working in the Visual Basic IDE is the Intellisense feature, and one place where it really shines is when using Enums as parameters in a function.

Let's take a look at the ChangeFormBackColor function once again; this time with an Enum in the parameter list.

Option Explicit

Private Enum eColors
RED = vbRed
BLUE = vbBlue
GREEN = vbGreen
BLACK = vbBlack
End Enum

Private Sub Command1_Click()

Dim blnReturn As Boolean
blnReturn = ChangeFormBackColor(Me, BLUE)

End Sub

Private Function ChangeFormBackColor(ByRef frm As Form, Optional lColor As eColors = BLACK) As Boolean

frm.BackColor = lColor
ChangeFormBackColor = True

End Function

One thing that's difficult to illustrate is how using an Enum in your parameter list really helps you. Of course the main objective is to enumerate the argument choices, in this case the four different color choices with black as a default. But it's better seen when it comes time to call the function. This pic attached will provide a better illustration:
(see below for attachment)

Enums are very useful if your function parameter expects a certain constant expression as an argument form a list of possible values, and as you can see, when it comes time to call the function the list is conveiniently displayed.

This works for returning an Enum as well. Here's an example of reversing a direction based on four values

Option Explicit

Private Enum eDirection
NORTH = 0
SOUTH = 1
EAST = 2
WEST = 3
End Enum

Private Sub Command1_Click()

Dim myDirection As Integer
myDirection = ReverseDirection(NORTH)

End Sub

Private Function ReverseDirection(Direction As eDirection) As eDirection

Select Case Direction

Case NORTH
ReverseDirection = SOUTH
Case SOUTH
ReverseDirection = NORTH
Case EAST
ReverseDirection = WEST
Case WEST
ReverseDirection = EAST

End Select

End Function

When assigning the return value to the function name, Intellisense once again provides us with the list of choices from the Enum.

User Defined Types

User defined types used as parameters is very similar to using objects or controls. Like objects and controls, user defined types are passed ByRef. Intellisense will provide you with a list of 'members' using dot notation.

Here's an example of a sub routine that initializes a baseball score.

Option Explicit

Private Type tBaseballScore
Runs As Integer
Innings As Integer
Outs As Integer
End Type

Private Sub Command1_Click()

Dim BaseballScore As tBaseballScore
InitScore BaseballScore
Debug.Print BaseballScore.Innings

End Sub

Private Sub InitScore(ByRef Score As tBaseballScore)

With Score
.Runs = 0
.Outs = 0
.Innings = 1
End With

End Sub

Here the data type in the parameter is our own custom type we've created.

Here's the same InitScore routine except now we're returning the user type in a function.

Private Sub Command2_Click()

Dim BaseballScore As tBaseballScore
BaseballScore = InitScore2(0, 1, 0)
Debug.Print BaseballScore.Innings

End Sub

Private Function InitScore2(ByVal intRuns As Integer, ByVal intInnings As Integer, _
ByVal intOuts As Integer) As tBaseballScore

'Return user type using the function name.
'Intellisense will work here to list your members.
With InitScore2
.Runs = intRuns
.Innings = intInnings
.Outs = intOuts
End With

End Function

As you can see, this isn't much different than working with
controls and objects. :)

noi_max
02-16-2005, 01:26 PM
ParamArray is a strange one. It is basically an array of optional parameters. The parameters are a Variant data type.

Here are some rules to ParamArray

• Cannot be used with other Optional parameters.
• Cannot specify ByVal or Byref; arguments are passed ByRef only.
• Must be put at the end of your parameter list.

Paramarray is useful for an arbitrary number of arguments. Say for example you had a function that added numbers, but you weren’t sure how many numbers you needed to add together in advance.

Private Function AddNumbers(ParamArray NumbersToAdd()) As Integer

Dim i As Integer 'Loop variable
Dim Count As Integer

'Loop through the ParamArray and add to the 'Count'
For i = LBound(NumbersToAdd) To UBound(NumbersToAdd)
Count = Count + NumbersToAdd(i)
Next i

'Return the final result
AddNumbers = Count

End Function

Here's an example of calling the function:

Private Sub Command1_Click()

Dim intResult As Integer

intResult = AddNumbers(5, 5, 6, 5)
Debug.Print intResult '21

End Sub

In this example, I could have passed any number of arguments(numbers) to add together and the function would have returned the correct result.

Of course you should consider what the function is going to do before using ParamArray, as it uses the Variant data type. Had I replaced one of the arguments with a String, I would have a type mismatch error. Also if I had provided a precision number, it would have rounded the number. :whoops:

noi_max
02-16-2005, 01:30 PM
This is a quick reference for function scope. It is reccomended that your function's scope is explicitly declared.

Private Function ChangeFormBackColor(ByRef frm As Form, ByVal lColor As Long) As Boolean

Here are the different ways in which a function or sub can be declared:

Private
When your function or sub is declared as Private, it is accessible only to the module in which it was declared, be it a form, module or class module.

Public
When your function or sub is declared as Public, it is accessible from all modules in your project.

Friend
Used in class modules. Declares the function or sub as public to all modules in your project, but not as a public interface to an object. This is useful when making your own ActiveX components. Perhaps a little beyond the scope of this tutorial. ;)

Static
When your function or sub is declared as Static, all of the variables declared in the function or sub will retain their values between calls.

Going back to the AddNumbers example:

Static Function AddNumbers(ParamArray NumbersToAdd()) As Integer

Dim i As Integer 'Local variables
Dim Count As Integer

'Loop through the ParamArray and add to the 'Count'
For i = LBound(NumbersToAdd) To UBound(NumbersToAdd)
Count = Count + NumbersToAdd(i)
Next i

'Return the final result
AddNumbers = Count

End Function

Since the vaiable 'Count' is not initialized within the function, it's value is preserved after every call.

By contrast, the variable 'i' is initialized in the For/Next loop.

For i = LBound(NumbersToAdd) To UBound(NumbersToAdd)

That pretty much wraps up this little reference on scope. For more on scope, refer to the .. :)

EZ Archive Ads Plugin for vBulletin Copyright 2006 Computer Help Forum